Procházet zdrojové kódy

AMBARI-3628. Restart indicators for services and hosts needed. (onechiporenko)

Oleg Nechiporenko před 11 roky
rodič
revize
2b6dd0427a

+ 5 - 0
ambari-web/app/assets/data/dashboard/HDP2/services.json

@@ -30,6 +30,7 @@
                 "service_name" : "GANGLIA",
                 "stack_id" : "HDP-2.0.5",
                 "state" : "STARTED",
+                "stale_configs": "true",
                 "actual_configs" : {
                   "capacity-scheduler" : {
                     "tag" : "version1"
@@ -93,6 +94,7 @@
                 "service_name" : "GANGLIA",
                 "stack_id" : "HDP-2.0.5",
                 "state" : "STARTED",
+                "stale_configs": "true",
                 "actual_configs" : {
                   "capacity-scheduler" : {
                     "tag" : "version1"
@@ -199,6 +201,7 @@
                 "desired_stack_id" : "HDP-2.0.5",
                 "desired_state" : "STARTED",
                 "ha_status" : "passive",
+                "stale_configs": "true",
                 "host_name" : "dev01.hortonworks.com",
                 "service_name" : "HBASE",
                 "stack_id" : "HDP-2.0.5",
@@ -884,6 +887,7 @@
                 "service_name" : "MAPREDUCE2",
                 "stack_id" : "HDP-2.0.5",
                 "state" : "STARTED",
+                "stale_configs": "true",
                 "actual_configs" : {
                   "capacity-scheduler" : {
                     "tag" : "version1"
@@ -953,6 +957,7 @@
                 "service_name" : "MAPREDUCE2",
                 "stack_id" : "HDP-2.0.5",
                 "state" : "INSTALLED",
+                "stale_configs": "true",
                 "actual_configs" : { },
                 "configs" : { },
                 "desired_configs" : { }

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

@@ -801,5 +801,12 @@ App.MainHostDetailsController = Em.Controller.extend({
         this.hide();
       }
     })
+  },
+
+  restartComponents: function() {
+    App.showConfirmationPopup(function() {
+
+    });
   }
+
 });

+ 49 - 0
ambari-web/app/controllers/main/service/info/configs.js

@@ -1545,5 +1545,54 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
 
   doCancel: function () {
     this.loadStep();
+  },
+  restartComponents: function() {
+    App.showConfirmationPopup(function() {
+
+    });
+  },
+  showHostsShouldBeRestarted: function() {
+    var hosts = [];
+    for(var hostName in this.get('content.restartRequiredHostsAndComponents')) {
+      hosts.push(hostName);
+    }
+    hosts = hosts.join(', ');
+    this.showItemsShouldBeRestarted(hosts, Em.I18n.t('service.service.config.restartService.hostsShouldBeRestarted'));
+  },
+  showComponentsShouldBeRestarted: function() {
+    var rhc = this.get('content.restartRequiredHostsAndComponents');
+    var hostsComponets = [];
+    for(var hostName in rhc) {
+      rhc[hostName].forEach(function(hostComponent) {
+        hostsComponets.push(hostComponent);
+      })
+    }
+    hostsComponets = hostsComponets.join(', ');
+    this.showItemsShouldBeRestarted(hostsComponets, Em.I18n.t('service.service.config.restartService.componentsShouldBeRestarted'));
+  },
+
+  showItemsShouldBeRestarted: function(content, header) {
+    App.ModalPopup.show({
+      content: content,
+      header: header,
+      bodyClass: Em.View.extend({
+        templateName: require('templates/common/selectable_popup'),
+        textareaVisible: false,
+        textTrigger: function() {
+          this.set('textareaVisible', !this.get('textareaVisible'));
+        },
+        putContentToTextarea: function() {
+          var content = this.get('parentView.content');
+          if (this.get('textareaVisible')) {
+            var wrapper = $(".task-detail-log-maintext");
+            $('.task-detail-log-clipboard').html(content).width(wrapper.width()).height(wrapper.height());
+            Em.run.next(function() {
+              $('.task-detail-log-clipboard').select();
+            });
+          }
+        }.observes('textareaVisible')
+      }),
+      secondary: null
+    });
   }
 });

+ 1 - 0
ambari-web/app/mappers/service_mapper.js

@@ -166,6 +166,7 @@ App.servicesMapper = App.QuickDataMapper.create({
     component_name: 'HostRoles.component_name',
     ha_status: 'HostRoles.ha_status',
     host_id: 'HostRoles.host_name',
+    stale_configs: 'HostRoles.stale_configs',
     $service_id: 'none' /* will be set outside of parse function */
   },
 

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

@@ -35,6 +35,7 @@ Em.I18n.translations = {
   'to':'To',
   'ok':'OK',
   'as':'as',
+  'on':'on',
   'any': 'Any',
   'more':'more',
   'yes':'Yes',
@@ -1065,6 +1066,10 @@ Em.I18n.translations = {
   'services.service.config.saved':'Saved Configuration Changes',
   'services.service.config.notSaved':'Unable to Save Configuration Changes',
   'services.service.config.restartService.TooltipMessage':'<b>Restart Service</b><br>Stale configuration used by {0} components on {1} hosts:{2}',
+  'services.service.config.restartService.needToRestart':'<strong>Restart Service</strong> ',
+  'services.service.config.restartService.needToRestartEnd':'should be restated',
+  'service.service.config.restartService.hostsShouldBeRestarted':'Hosts should be restarted',
+  'service.service.config.restartService.componentsShouldBeRestarted':'Components should be restarted',
   'services.service.config.saved.message':'Service configuration changes saved successfully.',
   'services.service.config.msgServiceStop':'Could not save configuration changes.  Please stop the service first. You will be able to save configuration changes after all of its components are stopped.',
   'services.service.config.msgHDFSMapRServiceStop':'Could not save configuration changes.  Please stop both HDFS and MapReduce first.  You will be able to save configuration changes after all HDFS and MapReduce components are stopped.',
@@ -1263,6 +1268,8 @@ Em.I18n.translations = {
   'hosts.host.summary.agentHeartbeat':'Agent <br/> Heartbeat',
   'hosts.host.summary.hostMetrics':'Host Metrics',
 
+  'hosts.host.details.needToRestart':'Host needs {0} components restarted',
+  'hosts.host.details.needToRestart.button':'Restart components',
   'hosts.host.details.deleteHost':'Delete Host',
   'hosts.host.details.startAllComponents':'Start All Components',
   'hosts.host.details.stopAllComponents':'Stop All Components',

+ 1 - 0
ambari-web/app/models/host_component.js

@@ -22,6 +22,7 @@ App.HostComponent = DS.Model.extend({
   workStatus: DS.attr('string'),
   componentName: DS.attr('string'),
   haStatus: DS.attr('string'),
+  staleConfigs: DS.attr('boolean'),
   host: DS.belongsTo('App.Host'),
   service: DS.belongsTo('App.Service'),
   isClient:function () {

+ 12 - 4
ambari-web/app/models/service.js

@@ -107,10 +107,18 @@ App.Service = DS.Model.extend({
    * properties, which need to be checked with map.
    */
   isRestartRequired: function () {
-    var restartRequired = false;
-    var restartRequiredHostsAndComponents = {};
-    this.set('restartRequiredHostsAndComponents', restartRequiredHostsAndComponents);
-    return restartRequired;
+    var rhc = this.get('hostComponents').filterProperty('staleConfigs', true);
+    var hc = {};
+    rhc.forEach(function(_rhc) {
+      var hostName = _rhc.get('host.publicHostName');
+      if (!hc[hostName]) {
+        hc[hostName] = [];
+      }
+      hc[hostName].push(_rhc.get('displayName'));
+    });
+    this.set('restartRequiredHostsAndComponents', hc);
+    return (rhc.length>0);
+
   }.property('serviceName', 'hostComponents'),
   
   /**

+ 35 - 0
ambari-web/app/templates/common/selectable_popup.hbs

@@ -0,0 +1,35 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<div>
+  <div class="task-top-wrap">
+    <div class="task-detail-ico-wrap">
+      <a href="#" title="Click to Copy" {{action "textTrigger"}} class="task-detail-copy"><i class="icon-copy"></i> {{t common.copy}}</a>
+    </div>
+  </div>
+  <div class="task-detail-log-info">
+    <div class="content-area" >
+      <div {{bindAttr class="view.textareaVisible::hidden :task-detail-log-clipboard-wrap"}}>
+        <textarea class="task-detail-log-clipboard"></textarea>
+      </div>
+      <div {{bindAttr class="view.textareaVisible:hidden :task-detail-log-maintext"}}>
+        {{content}}
+      </div>
+    </div>
+  </div>
+</div>

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

@@ -26,6 +26,16 @@
         </div>
         <div class="host-components">
         {{#if view.sortedComponents.length}}
+
+          {{#if view.needToRestartComponentsCount}}
+              <div class="alert alert-warning clearfix">
+                <i class="icon-refresh"></i> {{view.needToRestartMessage}}
+                <button class="btn btn-warning restart-components pull-right" {{action restartComponents target="controller"}}>
+                  {{t hosts.host.details.needToRestart.button}}
+                </button>
+              </div>
+          {{/if}}
+
           {{#each component in view.sortedComponents}}
           <div class="row-fluid">
           {{#view view.ComponentView contentBinding="component" decommissionDataNodeHostNamesBinding="view.decommissionDataNodeHostNames"}}
@@ -46,6 +56,10 @@
                 {{/if}}
                 &nbsp;/&nbsp;
               <a href="#" {{action routeToService component.service target="controller"  }}>{{component.service.displayName}}</a>
+                &nbsp;
+              {{#if component.staleConfigs}}
+                <i class="text-warning icon-refresh"></i>
+              {{/if}}
             </div>
             <div class="span5">
               {{#if App.isAdmin}}

+ 13 - 3
ambari-web/app/templates/main/service/info/configs.hbs

@@ -19,9 +19,19 @@
 <div id="serviceConfig">
   {{#if dataIsLoaded}}
     {{#if App.supports.hostOverrides}}
-	    <div class="pull-right">
-	      {{view App.FilterComboboxView filterBinding="controller.filter" columnsBinding="controller.filterColumns" }}
-	    </div>
+      {{#if controller.content.isRestartRequired}}
+        <div>
+          <div class="alert alert-warning clearfix">
+            <i class="icon-refresh"></i> {{{view.needToRestartMessage}}} <a href="#" {{action showComponentsShouldBeRestarted target="controller"}}>{{view.componentsCount}} {{t common.components}}</a> {{t on}} <a href="#" {{action showHostsShouldBeRestarted target="controller"}}>{{view.hostsCount}} {{t dashboard.services.hosts}}</a> {{t services.service.config.restartService.needToRestartEnd}}
+            <button class="btn btn-warning restart-components pull-right" {{action restartComponents target="controller"}}>
+              {{t hosts.host.details.needToRestart.button}}
+            </button>
+          </div>
+        </div>
+      {{/if}}
+    <div class="pull-right">
+      {{view App.FilterComboboxView filterBinding="controller.filter" columnsBinding="controller.filterColumns" }}
+    </div>
 	  {{/if}}
     <div class="clearfix"></div>
     {{view App.ServiceConfigView filterBinding="controller.filter" columnsBinding="controller.filterColumns"}}

+ 8 - 0
ambari-web/app/views/main/host/summary.js

@@ -32,6 +32,14 @@ App.MainHostSummaryView = Em.View.extend({
     window.open(gangliaMobileUrl);
   },
 
+  needToRestartComponentsCount: function() {
+    return this.get('sortedComponents').filterProperty('staleConfigs', true).length;
+  }.property('sortedComponents'),
+
+  needToRestartMessage: function() {
+    return Em.I18n.t('hosts.host.details.needToRestart').format(this.get('needToRestartComponentsCount'));
+  }.property(),
+
   /**
    * @type: [{String}]
    */

+ 16 - 1
ambari-web/app/views/main/service/info/configs.js

@@ -23,5 +23,20 @@ App.MainServiceInfoConfigsView = Em.View.extend({
   didInsertElement: function () {
     var controller = this.get('controller');
     controller.loadStep();
-  }
+  },
+
+  componentsCount: null,
+  hostsCount: null,
+
+  calculateCounts: function() {
+    var hc = this.get('controller.content.restartRequiredHostsAndComponents');
+    var hostsCount = 0;
+    var componentsCount = 0;
+    for (var host in hc) {
+      hostsCount++;
+      componentsCount += hc[host].length;
+    }
+    this.set('componentsCount', componentsCount);
+    this.set('hostsCount', hostsCount);
+  }.observes('controller.content.restartRequiredHostsAndComponents')
 });

+ 5 - 0
ambari-web/app/views/main/service/menu.js

@@ -86,6 +86,11 @@ App.MainServiceMenuView = Em.CollectionView.extend({
   classNames:["nav", "nav-list", "nav-services"],
 
   itemViewClass:Em.View.extend({
+
+    shouldBeRestarted: function() {
+      return this.get('content.hostComponents').someProperty('staleConfigs', true);
+    }.property('content.hostComponents.@each.staleConfigs'),
+
     classNameBindings:["active", "clients"],
     active:function () {
       return this.get('content.id') == this.get('parentView.activeServiceId') ? 'active' : '';