瀏覽代碼

AMBARI-7653. TESTSRV client should be installable via Ambari

Srimanth Gunturi 10 年之前
父節點
當前提交
7627eecfad

+ 43 - 0
ambari-web/app/controllers/main/host/details.js

@@ -1659,5 +1659,48 @@ App.MainHostDetailsController = Em.Controller.extend({
     });
     if (!clientsToInstall.length) return;
     this.sendComponentCommand(clientsToInstall, Em.I18n.t('host.host.details.installClients'), 'INSTALLED');
+  },
+
+  /**
+   * On click handler for custom command from items menu
+   * @param context
+   */
+  executeCustomCommand: function(event) {
+    var controller = this;
+    var context = event.context;
+    return App.showConfirmationPopup(function() {
+      App.ajax.send({
+        name : 'service.item.executeCustomCommand',
+        sender: controller,
+        data : {
+          command : context.command,
+          context : Em.I18n.t('services.service.actions.run.executeCustomCommand.context').format(context.command),
+          hosts : context.hosts,
+          serviceName : context.service,
+          componentName : context.component
+        },
+        success : 'executeCustomCommandSuccessCallback',
+        error : 'executeCustomCommandErrorCallback'
+      });
+    });
+  },
+
+  executeCustomCommandSuccessCallback  : function(data, ajaxOptions, params) {
+    if (data.Requests.id) {
+      App.router.get('backgroundOperationsController').showPopup();
+    } else {
+      console.warn('Error during execution of ' + params.command + ' custom command on' + params.componentName);
+    }
+  },
+  executeCustomCommandErrorCallback : function(data) {
+    var error = Em.I18n.t('services.service.actions.run.executeCustomCommand.error');
+    if(data && data.responseText){
+      try {
+        var json = $.parseJSON(data.responseText);
+        error += json.message;
+      } catch (err) {}
+    }
+    App.showAlertPopup(Em.I18n.t('services.service.actions.run.executeCustomCommand.error'), error);
+    console.warn('Error during executing custom command');
   }
 });

+ 3 - 0
ambari-web/app/messages.js

@@ -1216,6 +1216,9 @@ Em.I18n.translations = {
   'services.service.actions.run.yarnRefreshQueues.menu':'Refresh YARN Capacity Scheduler',
   'services.service.actions.run.yarnRefreshQueues.context':'Refresh YARN Capacity Scheduler',
   'services.service.actions.run.yarnRefreshQueues.error':'Error during remote command: ',
+  'services.service.actions.run.executeCustomCommand.menu':'{0}',
+  'services.service.actions.run.executeCustomCommand.context':'Execute {0}',
+  'services.service.actions.run.executeCustomCommand.error':'Error during remote command: ',
   'services.service.actions.run.compaction':'Run Compaction',
   'services.service.actions.run.smoke':'Run Service Check',
   'services.service.actions.reassign.master':'Move {0}',

+ 1 - 0
ambari-web/app/styles/application.less

@@ -2885,6 +2885,7 @@ width:100%;
   margin-bottom: 5px;
   margin-top: -55px;
   margin-left: 10px;
+  min-height: 30px;
   ul.dropdown-menu {
     li {
       text-align: left;

+ 1 - 1
ambari-web/app/templates/main/host/details.hbs

@@ -53,7 +53,7 @@
                                 <a><i class="icon-download-alt"></i> {{t services.service.actions.downloadClientConfigs}}</a>
                                 <div class="dropdown-menu-wrap">
                                   <ul class="dropdown-menu">
-                                    {{#each client in view.clients}}
+                                    {{#each client in view.clientsWithConfigs}}
                                       <li>
                                         <a {{action "downloadClientConfigs" client target="controller" href=true}}>{{client.displayName}}</a>
                                       </li>

+ 17 - 0
ambari-web/app/templates/main/host/summary.hbs

@@ -105,6 +105,23 @@
                           {{t host.host.details.installClients}}
                         </a>
                       </li>
+                      {{#each option in view.clientsWithCustomCommands}}
+                        <li class="dropdown-submenu submenu-left">
+                          <a href="javascript:void(null)" >
+                            <i class="icon-play-circle"></i>
+                            {{option.label}}
+                          </a>
+                            <div class="dropdown-menu-wrap">
+                              <ul class="dropdown-menu">
+                                {{#each command in option.commands}}
+                                  <li>
+                                    <a href="javascript:void(null)" {{action "executeCustomCommand" command target="controller" href=true}}>{{command.label}}</a>
+                                  </li>
+                                {{/each}}
+                              </ul>
+                            </div>
+                        </li>
+                      {{/each}}
                     </ul>
                   </div>
                 {{/if}}

+ 17 - 0
ambari-web/app/utils/ajax/ajax.js

@@ -405,6 +405,23 @@ var urls = {
       }
   },
 
+  'service.item.executeCustomCommand':{
+    'real': '/clusters/{clusterName}/requests',
+    'mock': '',
+    'format' : function(data) {
+      return {
+        type : 'POST',
+        data : JSON.stringify({
+          RequestInfo: {
+            'context': data.context,
+            'command': data.command,
+          },
+          "Requests/resource_filters": [{"service_name" : data.serviceName, "component_name" : data.componentName, 'hosts': data.hosts}]
+        })
+      }
+    }
+  },
+
   'service.load_config_groups': {
     'real': '/clusters/{clusterName}/config_groups?ConfigGroup/tag={serviceName}&fields=*',
     'mock': '/data/configurations/config_group.json'

+ 6 - 0
ambari-web/app/views/main/host/details.js

@@ -35,6 +35,12 @@ App.MainHostDetailsView = Em.View.extend({
     return this.get('content.hostComponents').filterProperty('isClient');
   }.property('content.hostComponents.@each'),
 
+  clientsWithConfigs: function() {
+    return this.get('content.hostComponents').filterProperty('isClient').filter(function(client) {
+      return !App.get('services.noConfigTypes').contains(client.get('service.serviceName'));
+    });
+  }.property('content.hostComponents.@each'),
+
   isActive: function() {
     return this.get('controller.content.passiveState') === "OFF";
   }.property('controller.content.passiveState'),

+ 34 - 1
ambari-web/app/views/main/host/summary.js

@@ -292,5 +292,38 @@ App.MainHostSummaryView = Em.View.extend({
       return $.timeago(d);
     }
     return "";
-  }.property('content.lastHeartBeatTime')
+  }.property('content.lastHeartBeatTime'),
+
+  /**
+   * Get clients with custom commands
+   */
+  clientsWithCustomCommands: function() {
+    var clients = this.get('clients');
+    var options = [];
+    var clientWithCommands;
+    clients.forEach(function(client) {
+      var componentName = client.get('componentName');
+      var customCommands = App.StackServiceComponent.find(componentName).get('customCommands');
+
+      if (customCommands.length) {
+        clientWithCommands = {
+          label: client.get('displayName'),
+          commands: []
+        };
+        customCommands.forEach(function(command) {
+          clientWithCommands.commands.push({
+            label: Em.I18n.t('services.service.actions.run.executeCustomCommand.menu').format(command),
+            service: client.get('service.serviceName'),
+            hosts: client.get('hostName'),
+            component: componentName,
+            command: command
+          });
+        });
+
+        options.push(clientWithCommands);
+      }
+    });
+
+    return options;
+  }.property('controller')
 });

+ 9 - 2
ambari-web/app/views/main/service/item.js

@@ -153,12 +153,15 @@ App.MainServiceItemView = Em.View.extend({
     var allSlaves = service.get('hostComponents').filterProperty('isSlave').mapProperty('componentName').uniq();
     var actionMap = this.actionMap();
     var serviceCheckSupported = App.get('services.supportsServiceCheck').contains(service.get('serviceName'));
+    var hasConfigTab = this.get('hasConfigTab');
 
     if (this.get('controller.isClientsOnlyService')) {
       if (serviceCheckSupported) {
         options.push(actionMap.RUN_SMOKE_TEST);
       }
-      options.push(actionMap.REFRESH_CONFIGS);
+      if (hasConfigTab) {
+	      options.push(actionMap.REFRESH_CONFIGS);
+	    }
     } else {
       if (this.get('serviceName') === 'FLUME') {
         options.push(actionMap.REFRESH_CONFIGS);
@@ -211,7 +214,11 @@ App.MainServiceItemView = Em.View.extend({
         options.push(item);
       });
     }
-    options.push(actionMap.DOWNLOAD_CLIENT_CONFIGS);
+
+    if (hasConfigTab) {
+      options.push(actionMap.DOWNLOAD_CLIENT_CONFIGS);
+    }
+
     return options;
   }.property('controller.content', 'controller.isStopDisabled','controller.isClientsOnlyService', 'controller.content.isRestartRequired', 'isPassive'),
 

+ 18 - 0
ambari-web/test/controllers/main/host/details_test.js

@@ -1445,4 +1445,22 @@ describe('App.MainHostDetailsController', function () {
     });
   });
 
+  describe('#executeCustomCommands', function() {
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      App.ajax.send.restore();
+    });
+
+    it('confirm popup should be displayed', function () {
+      var popup = controller.executeCustomCommand({context: Em.Object.create()});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
 });

+ 61 - 4
ambari-web/test/views/main/host/details_test.js

@@ -36,12 +36,16 @@ var view,
 describe('App.MainHostDetailsView', function () {
 
   beforeEach(function () {
-    view = App.MainHostDetailsView.create();
+    view = App.MainHostDetailsView.create({
+       content: Em.Object.create({
+          hostComponents: []
+       })
+    });
   });
 
   describe('#content', function () {
     it('should take content from controller', function () {
-      App.router.set('mainHostDetailsController.content', {
+      view.set('content', {
         property: 'value'
       });
       expect(view.get('content.property')).to.equal('value');
@@ -50,7 +54,7 @@ describe('App.MainHostDetailsView', function () {
 
   describe('#clients', function () {
     it('should take clients from content', function () {
-      App.router.set('mainHostDetailsController.content', {
+      view.set('content', {
         hostComponents: [
           {
             isClient: true
@@ -94,6 +98,60 @@ describe('App.MainHostDetailsView', function () {
     });
   });
 
+  describe('#clientsWithConfigs', function() {
+    beforeEach(function () {
+      view.set('content', {
+        hostComponents: [
+          Em.Object.create({
+            isClient: true,
+            service: Em.Object.create({
+              serviceName: 'WITH_CONFIGS'
+            })
+          }),
+          Em.Object.create({
+            isClient: true,
+            service: Em.Object.create({
+              serviceName: 'WITHOUT_CONFIGS'
+            })
+          }),
+          Em.Object.create({
+            isClient: false,
+            service: Em.Object.create({
+              serviceName: 'SAMPLE_SERVICE'
+            })
+          })
+        ]
+      });
+
+      App.set('services', {
+        noConfigTypes: ['WITHOUT_CONFIGS', 'WITHOUT_CONFIGS_2']
+      });
+    });
+
+    afterEach(function () {
+      App.set('services', Em.K);
+    });
+
+    it('should get only clients with configs', function() {
+      expect(view.get('clientsWithConfigs')).to.have.length(1);
+      console.log(view.get('content.hostComponents'));
+      view.get('content.hostComponents').pushObject(Em.Object.create({
+        isClient: true,
+        service: Em.Object.create({
+          serviceName: 'WITHOUT_CONFIGS_2'
+        })
+      }));
+      expect(view.get('clientsWithConfigs')).to.have.length(1);
+      view.get('content.hostComponents').pushObject(Em.Object.create({
+        isClient: true,
+        service: Em.Object.create({
+          serviceName: 'WITH_CONFIGS_2'
+        })
+      }));
+      expect(view.get('clientsWithConfigs')).to.have.length(2);
+    });
+  });
+
   describe('#didInsertElement()', function () {
     it('isLoaded should be set as true', function () {
       view.didInsertElement();
@@ -107,5 +165,4 @@ describe('App.MainHostDetailsView', function () {
       expect(App.router.get('currentState.name')).to.equal('index');
     });
   });
-
 });

+ 52 - 1
ambari-web/test/views/main/host/summary_test.js

@@ -272,7 +272,7 @@ describe('App.MainHostSummaryView', function() {
 
   });
 
- describe('#addableComponents', function() {
+  describe('#addableComponents', function() {
 
     var tests = Em.A([
       {
@@ -326,4 +326,55 @@ describe('App.MainHostSummaryView', function() {
 
   });
 
+  describe("#clientsWithCustomCommands", function() {
+    before(function() {
+      sinon.stub(App.StackServiceComponent, 'find', function(component) {
+        var customCommands = [];
+
+        if (component == 'WITH_CUSTOM_COMMANDS') {
+          customCommands = ['CUSTOMCOMMAND'];
+        }
+
+        var obj = Em.Object.create({
+          customCommands: customCommands,
+          filterProperty: function () {
+            return {
+              mapProperty: Em.K
+            };
+          }
+        });
+        return obj;
+      });
+    });
+
+    after(function() {
+      App.StackServiceComponent.find.restore();
+    });
+    var content = Em.Object.create({
+      hostComponents: Em.A([
+        Em.Object.create({
+          componentName: 'WITH_CUSTOM_COMMANDS',
+          displayName: 'WITH_CUSTOM_COMMANDS',
+          hostName: 'c6401',
+          service: Em.Object.create({
+            serviceName: 'TESTSRV'
+          })
+        }),
+        Em.Object.create({
+          componentName: 'WITHOUT_CUSTOM_COMMANDS',
+          displayName: 'WITHOUT_CUSTOM_COMMANDS',
+          hostName: 'c6401',
+          service: Em.Object.create({
+            serviceName: 'TESTSRV'
+          })
+        })
+      ])
+    });
+
+    it("Clients with custom commands only", function() {
+      mainHostSummaryView.set('content', content);
+      expect(mainHostSummaryView.get('clientsWithCustomCommands').length).to.eql(1);
+      expect(mainHostSummaryView.get('clientsWithCustomCommands')).to.have.deep.property('[0].commands[0].command', 'CUSTOMCOMMAND');
+    });
+  });
 });