Prechádzať zdrojové kódy

AMBARI-1090. Restrict user to apply service configuration when custom box properties are already exposed on the management config page. (Jaimin Jetly via yusaku)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/trunk@1431650 13f79535-47bb-0310-9956-ffa450edef68
Yusaku Sako 12 rokov pred
rodič
commit
0eddcbd936

+ 4 - 0
CHANGES.txt

@@ -654,6 +654,10 @@ AMBARI-666 branch (unreleased changes)
 
   BUG FIXES
 
+  AMBARI-1090. Restrict user to apply service configuration when custom box
+  properties are already exposed on the management config page.
+  (Jaimin Jetly via yusaku)
+
   AMBARI-1149. HIVE_METASTORE needs to be started as a Hive component.
   (yusaku)
 

+ 197 - 90
ambari-web/app/controllers/main/service/info/configs.js

@@ -57,6 +57,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
   loadStep: function () {
     console.log("TRACE: Loading configure for service");
     this.clearStep();
+    // this.set('serviceConfigs',require('data/service_configs'));
     //STEP 1: set the present state of the service Properties. State depends on array of: unique combination of type(ex. core-site) and tag (ex. version01) derived from serviceInfo desired_state
     this.loadMasterComponents();
     //this.loadSlaveComponentVersion();
@@ -139,27 +140,19 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
    *  Loads the advanced configs fetched from the server metadata libarary
    */
   loadAdvancedConfig: function (serviceConfigs, advancedConfig) {
-    var service = this.get('serviceConfigs').findProperty('serviceName', this.get('content.serviceName'));
     advancedConfig.forEach(function (_config) {
-      if (service) {
-        if (!this.get('configMapping').someProperty('name', _config.name)) {
-          if (service.configs.someProperty('name', _config.name)) {
-            service.configs.findProperty('name', _config.name).description = _config.description;
-          } else {
-            _config.id = "site property";
-            _config.category = 'Advanced';
-            _config.displayName = _config.name;
-            _config.defaultValue = _config.value;
-          /*  if (/\${.*}/.test(_config.value) || (service.serviceName !== 'OOZIE' && service.serviceName !== 'HBASE')) {
-              _config.isRequired = false;
-              _config.value = '';
-            } else if (/^\s+$/.test(_config.value)) {
-              _config.isRequired = false;
-            }   */
-            _config.isVisible = true;
-            _config.displayType = 'advanced';
-            service.configs.pushObject(_config);
-          }
+      if (_config) {
+        if (this.get('configMapping').someProperty('name', _config.name)) {
+        } else if (!(serviceConfigs.someProperty('name', _config.name))) {
+          _config.id = "site property";
+          _config.category = 'Advanced';
+          _config.displayName = _config.name;
+          _config.defaultValue = _config.value;
+          _config.isRequired = false;
+          _config.isVisible = true;
+          _config.displayType = 'advanced';
+          _config.serviceName = this.get('content.serviceName');
+          serviceConfigs.pushObject(_config);
         }
       }
     }, this);
@@ -186,13 +179,13 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
         //STEP 3: Set globalConfigs and Get an array of serviceProperty objects
         var serviceConfigs = self.getSitesConfigProperties();
         //STEP 5: Add the advanced configs to the serviceConfigs property
-
         var advancedConfig = App.router.get('installerController').loadAdvancedConfig(self.get('content.serviceName')) || [];
         self.loadAdvancedConfig(serviceConfigs, advancedConfig);
+        self.loadCustomConfig(serviceConfigs);
+        var serviceConfig = self.get('serviceConfigs').findProperty('serviceName', self.get('content.serviceName'));
+        serviceConfig.configs = self.get('globalConfigs').concat(serviceConfigs);
 
-        self.loadCustomConfig();
-
-        self.renderServiceConfigs(self.get('serviceConfigs'));
+        self.renderServiceConfigs(serviceConfig);
       },
 
       error: function (request, ajaxOptions, error) {
@@ -229,10 +222,9 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
   /**
    * Render a custom conf-site box for entering properties that will be written in *-site.xml files of the services
    */
-  loadCustomConfig: function () {
-    var serviceConfig = this.get('serviceConfigs').findProperty('serviceName', this.get('content.serviceName'));
+  loadCustomConfig: function (serviceConfigs) {
     var customConfig = this.get('customConfigs').findProperty('serviceName', this.get('content.serviceName'));
-    serviceConfig.configs.pushObject(customConfig);
+    serviceConfigs.pushObject(customConfig);
   },
 
   /**
@@ -251,38 +243,52 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
           name: index,
           value: properties[index],
           defaultValue: properties[index],
-          filename: _tag.siteName + ".xml",
-          isVisible: true,
-          isRequired: true
+          filename: _tag.siteName + ".xml"
         };
+        if (this.get('configs').someProperty('name', index)) {
+          var configProperty = this.get('configs').findProperty('name', index);
+          if (this.get('configs').findProperty('name', index).isReconfigurable === false) {
+          }
+          serviceConfigObj.displayType = configProperty.displayType;
+          serviceConfigObj.isRequired = configProperty.isRequired ? configProperty.isRequired : true;
+          serviceConfigObj.isReconfigurable = (configProperty.isReconfigurable !== undefined) ? configProperty.isReconfigurable : true;
+          serviceConfigObj.isVisible = (configProperty.isVisible !== undefined) ? configProperty.isVisible : true;
+
+        }
+        serviceConfigObj.displayType = this.get('configs').someProperty('name', index) ? this.get('configs').findProperty('name', index).displayType : null;
+
+        serviceConfigObj.isRequired = this.get('configs').someProperty('name', index) ? this.get('configs').findProperty('name', index).isRequired : null;
+
         if (_tag.siteName === 'global') {
-          if (localServiceConfigs.configs.someProperty('name', index)) {
-            var item = localServiceConfigs.configs.findProperty('name', index);
-            item.value = properties[index];
-            item.defaultValue = properties[index];
+          if (this.get('configs').someProperty('name', index)) {
+            var item = this.get('configs').findProperty('name', index);
             if (item.displayType === 'int') {
-              if (/\d+m$/.test(item.value)) {
-                item.value = item.value.slice(0, item.value.length - 1);
-                item.defaultValue = item.value;
+              if (/\d+m$/.test(properties[index])) {
+
+                serviceConfigObj.value = properties[index].slice(0, properties[index].length - 1);
+                serviceConfigObj.defaultValue = serviceConfigObj.value;
               }
             }
             if (item.displayType === 'checkbox') {
-              switch (item.value) {
+              switch (properties[index]) {
                 case 'true' :
-                  item.value = true;
+                  serviceConfigObj.value = true;
+                  serviceConfigObj.defaultValue = true;
                   break;
                 case 'false' :
-                  item.value = false;
+                  serviceConfigObj.value = false;
+                  serviceConfigObj.defaultValue = false;
                   break;
               }
             }
           }
           serviceConfigObj.id = 'puppet var';
           serviceConfigObj.serviceName = this.get('configs').someProperty('name', index) ? this.get('configs').findProperty('name', index).serviceName : null;
+          serviceConfigObj.displayName = this.get('configs').someProperty('name', index) ? this.get('configs').findProperty('name', index).displayName : null;
           serviceConfigObj.category = this.get('configs').someProperty('name', index) ? this.get('configs').findProperty('name', index).category : null;
           globalConfigs.pushObject(serviceConfigObj);
         } else if (!this.get('configMapping').someProperty('name', index)) {
-          if (_tag.siteName !== localServiceConfigs.filename) {
+          if (_tag.siteName !== this.get('serviceConfigs').findProperty('serviceName', this.get('content.serviceName')).filename) {
             serviceConfigObj.isVisible = false;
           }
           serviceConfigObj.id = 'site property';
@@ -290,9 +296,10 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
           serviceConfigObj.category = 'Advanced';
           serviceConfigObj.displayName = index;
           serviceConfigObj.displayType = 'advanced';
-          localServiceConfigs.configs.pushObject(serviceConfigObj);
+          // localServiceConfigs.configs.pushObject(serviceConfigObj);
+          serviceConfigs.pushObject(serviceConfigObj);
         }
-        serviceConfigs.pushObject(serviceConfigObj);
+
       }
     }, this);
     this.set('globalConfigs', globalConfigs);
@@ -335,26 +342,24 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
    */
   renderServiceConfigs: function (serviceConfigs) {
 
-    serviceConfigs.forEach(function (_serviceConfig) {
-      var serviceConfig = App.ServiceConfig.create({
-        filename: _serviceConfig.filename,
-        serviceName: _serviceConfig.serviceName,
-        displayName: _serviceConfig.displayName,
-        configCategories: _serviceConfig.configCategories,
-        configs: []
-      });
+    var serviceConfig = App.ServiceConfig.create({
+      filename: serviceConfigs.filename,
+      serviceName: serviceConfigs.serviceName,
+      displayName: serviceConfigs.displayName,
+      configCategories: serviceConfigs.configCategories,
+      configs: []
+    });
 
-      if ((this.get('content.serviceName') && this.get('content.serviceName').toUpperCase() === serviceConfig.serviceName) || serviceConfig.serviceName === 'MISC') {
+    if ((this.get('content.serviceName') && this.get('content.serviceName').toUpperCase() === serviceConfig.serviceName) || serviceConfig.serviceName === 'MISC') {
 
-        this.loadComponentConfigs(_serviceConfig, serviceConfig);
+      this.loadComponentConfigs(serviceConfigs, serviceConfig);
 
-        console.log('pushing ' + serviceConfig.serviceName);
-        this.get('stepConfigs').pushObject(serviceConfig);
+      console.log('pushing ' + serviceConfig.serviceName);
+      this.get('stepConfigs').pushObject(serviceConfig);
 
-      } else {
-        console.log('skipping ' + serviceConfig.serviceName);
-      }
-    }, this);
+    } else {
+      console.log('skipping ' + serviceConfig.serviceName);
+    }
 
     this.set('selectedService', this.get('stepConfigs').objectAt(0));
   },
@@ -368,31 +373,39 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
     _componentConfig.configs.forEach(function (_serviceConfigProperty) {
       console.log("config", _serviceConfigProperty);
       if (!_serviceConfigProperty) return;
-      var serviceConfigProperty = App.ServiceConfigProperty.create(_serviceConfigProperty);
-     // serviceConfigProperty.serviceConfig = componentConfig;
-      serviceConfigProperty.set('isEditable',serviceConfigProperty.get('isReconfigurable'));
-      this.initialValue(serviceConfigProperty);
-      componentConfig.configs.pushObject(serviceConfigProperty);
-      serviceConfigProperty.validate();
-      console.log("config result", serviceConfigProperty);
+      if (_serviceConfigProperty.serviceName === this.get('content.serviceName')) {
+        var serviceConfigProperty = App.ServiceConfigProperty.create(_serviceConfigProperty);
+        // serviceConfigProperty.serviceConfig = componentConfig;
+        if (App.db.getUser().admin) {
+          serviceConfigProperty.set('isEditable', serviceConfigProperty.get('isReconfigurable'));
+        } else {
+          serviceConfigProperty.set('isEditable', false);
+        }
+        this.initialValue(serviceConfigProperty);
+        componentConfig.configs.pushObject(serviceConfigProperty);
+        serviceConfigProperty.validate();
+        console.log("config result", serviceConfigProperty);
+      }
     }, this);
   },
 
   restartServicePopup: function (event) {
     console.log("Enered the entry pointttt");
     var self = this;
-    var result;
     var header;
     var message;
-    console.log('I am over hererererere: ' + this.get('content.healthStatus'));
+    var value;
+    var flag;
     if (this.get('content.isStopped') === true) {
       var result = this.saveServiceConfigProperties();
-      if (result === true) {
+      flag = result.flag;
+      if (flag === true) {
         header = 'Start Service';
         message = 'Service configuration applied successfully';
       } else {
         header = 'Faliure';
-        message = 'Faliure in applying service configuration'
+        message = result.message;
+        value = result.value;
       }
 
     } else {
@@ -407,8 +420,57 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
         this.hide();
       },
       bodyClass: Ember.View.extend({
+        flag: flag,
         message: message,
-        template: Ember.Handlebars.compile(['<p>{{view.message}}</p>'].join('\n'))
+        siteProperties: value,
+        getDisplayMessage: function () {
+          var displayMsg = [];
+          var siteProperties = this.get('siteProperties');
+          if (siteProperties) {
+            siteProperties.forEach(function (_siteProperty) {
+              var displayProperty = _siteProperty.siteProperty;
+              var displayNames = _siteProperty.displayNames;
+              /////////
+              if (displayNames && displayNames.length) {
+                if (displayNames.length === 1) {
+                  displayMsg.push(displayProperty + ' as ' + displayNames[0]);
+                } else {
+                  var name;
+                  displayNames.forEach(function (_name, index) {
+                    if (index === 0) {
+                      name = _name;
+                    } else if (index = siteProperties.length - 1) {
+                      name = name + ' and ' + _name;
+                    } else {
+                      name = name + ', ' + _name;
+                    }
+                  }, this);
+                  displayMsg.push(displayProperty + ' as ' + name);
+
+                }
+              } else {
+                displayMsg.push(displayProperty);
+              }
+            }, this);
+          }
+          return displayMsg;
+
+        }.property('siteProperties'),
+        template: Ember.Handlebars.compile([
+          '<h5>{{view.message}}</h5>',
+          '{{#unless view.flag}}',
+          '<br/>',
+          '<div class="pre-scrollable" style="max-height: 250px;">',
+          '<ul>',
+          '{{#each val in view.getDisplayMessage}}',
+          '<li>',
+          '{{val}}',
+          '</li>',
+          '{{/each}}',
+          '</ul>',
+          '</div>',
+          '{{/unless}}'
+        ].join('\n'))
       })
     });
   },
@@ -417,14 +479,26 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
    * Save config properties
    */
   saveServiceConfigProperties: function () {
-    var result = false;
+    var result = {
+      flag: false,
+      message: null,
+      value: null
+    };
     var configs = this.get('stepConfigs').findProperty('serviceName', this.get('content.serviceName')).get('configs');
     this.saveGlobalConfigs(configs);
     this.saveSiteConfigs(configs);
-    this.setCustomConfigs();
-    var result = this.createConfigurations();
-    if (result === true) {
-      result = this.applyCreatedConfToService('new');
+    var customConfigResult = this.setCustomConfigs();
+    result.flag = customConfigResult.flag;
+    result.value = customConfigResult.value;
+    if (result.flag !== true) {
+      result.message = 'Error in custom HDFS configuration. Some properties entered in the box are already exposed on this page';
+      return result;
+    }
+    result.flag = result.flag && this.createConfigurations();
+    if (result.flag === true) {
+      result.flag = this.applyCreatedConfToService('new');
+    } else {
+      result.message = 'Faliure in applying service configuration';
     }
     console.log("The result from applyCreatdConfToService is: " + result);
     return result;
@@ -560,7 +634,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
     var serviceConfigTags = this.get('serviceConfigTags');
     serviceConfigTags.forEach(function (_serviceTags) {
       if (_serviceTags.siteName === 'global') {
-        console.log("TRACE: Inside globalssss");
+        console.log("TRACE: Inside global");
         result = result && this.createConfigSite(this.createGlobalSiteObj(_serviceTags.newTagName));
       } else if (_serviceTags.siteName === 'core-site') {
         console.log("TRACE: Inside core-site");
@@ -641,11 +715,14 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
   },
 
   createSiteObj: function (siteName, tagName) {
-    var siteObj = this.get('uiConfigs').filterProperty('filename', siteName + ".xml");
+    var siteObj = this.get('uiConfigs').filterProperty('filename', siteName + '.xml');
     var siteProperties = {};
     siteObj.forEach(function (_siteObj) {
       siteProperties[_siteObj.name] = _siteObj.value;
     }, this);
+    if (siteName === 'hdfs-site') {
+      debugger;
+    }
     return {"type": siteName, "tag": tagName, "properties": siteProperties};
   },
 
@@ -696,7 +773,9 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
   },
 
   setCustomConfigs: function () {
-    var site = this.get('stepConfigs').filterProperty('id', 'conf-site');
+    var site = this.get('stepConfigs').findProperty('serviceName', this.get('content.serviceName')).get('configs').filterProperty('id', 'conf-site');
+    var siteProperties = [];
+    var flag = true;
     site.forEach(function (_site) {
       var keyValue = _site.value.split(/\n+/);
       if (keyValue) {
@@ -706,28 +785,56 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
           var key = _keyValue.match(/(.+)=/);
           var value = _keyValue.match(/=(.*)/);
           if (key) {
-            this.setSiteProperty(key[1], value[1], _site.filename);
+            // Check dat entered config is allowed to reconfigure
+            if (this.get('uiConfigs').someProperty('name', key[1])) {
+              var property = {
+                siteProperty: key[1],
+                displayNames: []
+              };
+              if (this.get('configMapping').someProperty('name', key[1])) {
+                this.setPropertyDisplayNames(property.displayNames, this.get('configMapping').findProperty('name', key[1]).templateName);
+              }
+              siteProperties.push(property);
+              flag = false;
+            } else if (flag) {
+              this.setSiteProperty(key[1], value[1], _site.name + '.xml');
+            }
           }
 
         }, this);
       }
     }, this);
+    var result = {
+      flag: flag,
+      value: siteProperties
+    }
+    return result;
+  },
+
+  /**
+   * Set display names of the property tfrom he puppet/global names
+   * @param: displayNames: a field to be set with displayNames
+   * @param names: array of property puppet/global names
+   */
+  setPropertyDisplayNames: function (displayNames, names) {
+    var stepConfigs = this.get('stepConfigs').findProperty('serviceName', this.get('content.serviceName')).configs;
+    names.forEach(function (_name, index) {
+      if (stepConfigs.someProperty('name', _name)) {
+        displayNames.push(stepConfigs.findProperty('name', _name).displayName);
+      }
+    }, this);
   },
 
   /**
    * Set property of the site variable
    */
   setSiteProperty: function (key, value, filename) {
-    if (this.get('uiConfigs').someProperty('name', key)) {
-      this.get('uiConfigs').findProperty('name', key).value = value;
-    } else {
-      this.get('uiConfigs').pushObject({
-        "id": "site property",
-        "name": key,
-        "value": value,
-        "filename": filename
-      });
-    }
+    this.get('uiConfigs').pushObject({
+      "id": "site property",
+      "name": key,
+      "value": value,
+      "filename": filename
+    });
   },
 
   getUrl: function (testUrl, url) {

+ 0 - 8
ambari-web/app/data/config_properties.js

@@ -483,7 +483,6 @@ module.exports =
       "displayName": "Hadoop Log Dir Prefix",
       "description": "The parent directory for Hadoop log files.  The HDFS log directory will be ${hadoop_log_dir_prefix} / ${hdfs_user} and the MapReduce log directory will be ${hadoop_log_dir_prefix} / ${mapred_user}.",
       "defaultValue": "/var/log/hadoop",
-      "isReconfigurable": false,
       "displayType": "directory",
       "isVisible": true,
       "domain": "global",
@@ -496,7 +495,6 @@ module.exports =
       "displayName": "Hadoop PID Dir Prefix",
       "description": "The parent directory in which the PID files for Hadoop processes will be created.  The HDFS PID directory will be ${hadoop_pid_dir_prefix} / ${hdfs_user} and the MapReduce PID directory will be ${hadoop_pid_dir_prefix} / ${mapred_user}.",
       "defaultValue": "/var/run/hadoop",
-      "isReconfigurable": false,
       "displayType": "directory",
       "isVisible": true,
       "domain": "global",
@@ -619,7 +617,6 @@ module.exports =
       "displayName": "HDFS Maximum Checkpoint Delay",
       "description": "Maximum delay between two consecutive checkpoints for HDFS",
       "defaultValue": "21600",
-      "isReconfigurable": false,
       "displayType": "int",
       "unit": "seconds",
       "isVisible": true,
@@ -634,7 +631,6 @@ module.exports =
       "displayName": "HDFS Maximum Edit Log Size for Checkpointing",
       "description": "Maximum size of the edits log file that forces an urgent checkpoint even if the maximum checkpoint delay is not reached",
       "defaultValue": "0.5",
-      "isReconfigurable": false,
       "displayType": "float",
       "unit": "GB",
       "isVisible": true,
@@ -662,7 +658,6 @@ module.exports =
       "displayName": "Exclude hosts",
       "description": "Names a file that contains a list of hosts that are not permitted to connect to the namenode.  This file will be placed inside the Hadoop conf directory.",
       "defaultValue": "dfs.exclude",
-      "isReconfigurable": false,
       "displayType": "advanced",
       "isVisible": true,
       "filename": "hdfs-site.xml",
@@ -676,7 +671,6 @@ module.exports =
       "displayName": "Include hosts",
       "description": "Names a file that contains a list of hosts that are permitted to connect to the namenode.  This file will be placed inside the Hadoop conf directory.",
       "defaultValue": "dfs.include",
-      "isReconfigurable": false,
       "displayType": "advanced",
       "isVisible": true,
       "filename": "hdfs-site.xml",
@@ -691,7 +685,6 @@ module.exports =
       "description": "Default block replication.",
       "displayType": "int",
       "defaultValue": "3",
-      "isReconfigurable": false,
       "isVisible": true,
       "filename": "hdfs-site.xml",
       "domain": "global",
@@ -705,7 +698,6 @@ module.exports =
       "description": "the user who is allowed to perform short circuit reads",
       "displayType": "advanced",
       "defaultValue": "hbase",
-      "isReconfigurable": false,
       "isVisible": true,
       "filename": "hdfs-site.xml",
       "domain": "global",

+ 35 - 201
ambari-web/app/templates/main/service/info/configs.hbs

@@ -16,212 +16,46 @@
 * limitations under the License.
 }}
 
-<!--<div>-->
-    <!--<fieldset class="fieldset">-->
-     <!--<legend>General</legend>-->
-        <!--<div class="row">-->
-           <!--<div class="span2"> <b>Some HDFS config</b> </div>-->
-           <!--<div class="span2"><input type="text"></div>-->
-        <!--</div>-->
-    <!--</fieldset>-->
-
-<!--</div>-->
-
-<!--<br>-->
-
-<!--<div>-->
-       <!--<fieldset class="fieldset">-->
-        <!--<legend>NameNode</legend>-->
-          <!--<div class="row">-->
-            <!--<div class="span2"><b>NameNodeSet</b> </div>-->
-            <!--<div class="span2">host0001.com.com</div>-->
-
-        <!--</div>-->
-<!--<br>-->
-        <!--<div class="row">-->
-            <!--<div class="span2"><b>NameNode directories</b> dfs.name.dir </div>-->
-            <!--<div class="span2"><textarea placeholder="/grid/hadoop/namenode"></textarea></div>-->
-
-        <!--</div>-->
-<!--<br>-->
-        <!--<div class="row">-->
-            <!--<div class="span2"><b>Some NameNode config</b> dfs.name.some </div>-->
-            <!--<div class="span2 input-append"><input class="span2" id="appendedInput" type="text" placeholder="2048"><span class="add-on">MB</span></div>-->
-            <!--<div class="span2"></div>-->
-
-        <!--</div>-->
-    <!--</fieldset>-->
-<!--</div>-->
-<!--<br>-->
-<!--<div>-->
-    <!--<fieldset class="fieldset">-->
-        <!--<legend>SNameNode</legend>-->
-        <!--<div class="row">-->
-            <!--<div class="span2"><b>Some NameNode host</b> </div>-->
-            <!--<div class="span2">host0002.com.com</div>-->
-        <!--</div>-->
-    <!--</fieldset>-->
-<!--</div>-->
-<!--<br>-->
-<!--<div>-->
-    <!--<fieldset class="fieldset">-->
-        <!--<legend>DataNode (Default)</legend>-->
-        <!--<div class="row">-->
-            <!--<div class="span2"><b>DataNode hosts</b> </div>-->
-            <!--<div class="span3"><a href="#">host003.com.com and 89 more</a> </div>-->
-
-        <!--</div>-->
-        <!--<br>-->
-        <!--<div class="row">-->
-            <!--<div class="span2"><b>DataNode directories</b> dfs.data.dir</div>-->
-             <!--<div class="span2"><textarea placeholder="/grid/0/hadoop/datanode-->
-                                                      <!--/grid/1/hadoop/datanode-->
-                                                      <!--/grid/2/hadoop/datanode"></textarea></div>-->
-
-        <!--</div>-->
-        <!--<br>-->
-        <!--<div class="row">-->
-            <!--<div class="span2"><b>Some DataNode config</b> dfs.data.some </div>-->
-            <!--<div class="span2 input-append"><input class="span2" id="appendedInput" type="text" placeholder="1024"><span class="add-on">MB</span></div>-->
-            <!--<div class="span2"></div>-->
-
-        <!--</div>-->
-        <!--<br>-->
-        <!--<div class="row">-->
-            <!--<div class="span2"><b>Another DataNode config</b> dfs.data.another</div>-->
-            <!--<div class="span2 input-append"><input class="span2" id="appendedInput" type="text" placeholder="512"><span class="add-on">MB</span></div>-->
-
-        <!--</div>-->
-   <!--</fieldset>-->
-<!--</div>-->
-<!--<br>-->
-<!--<div>-->
-    <!--<fieldset class="fieldset">-->
-        <!--<legend>DataNode Group A</legend>-->
-        <!--<div class="row">-->
-            <!--<div class="span2"><b>DataNode hosts</b> </div>-->
-            <!--<div class="span3"><a href="#">host0090.com.com and 90 more</a> </div>-->
-
-        <!--</div>-->
-        <!--<br>-->
-        <!--<div class="row">-->
-            <!--<div class="span2"><b>DataNode directories</b> dfs.data.dir</div>-->
-            <!--<div class="span2"><textarea placeholder="/grid/0/hadoop/datanode-->
-                                                      <!--/grid/1/hadoop/datanode-->
-                                                      <!--/grid/2/hadoop/datanode-->
-                                                      <!--/grid/3/hadoop/datanode"></textarea></div>-->
-
-        <!--</div>-->
-        <!--<br>-->
-        <!--<div class="row">-->
-            <!--<div class="span2"><b>Some DataNode config</b> dfs.data.another</div>-->
-            <!--<div class="span2 input-append"><input class="span2" id="appendedInput" type="text" placeholder="2048"><span class="add-on">MB</span></div>-->
-            <!--<div class="span2"></div>-->
-        <!--</div>-->
-        <!--<br>-->
-        <!--<div class="row">-->
-            <!--<div class="span2"><b>Another DataNode config</b> dfs.data.another</div>-->
-            <!--<div class="span2 input-append"><input class="span2" id="appendedInput" type="text" placeholder="128"><span class="add-on">MB</span></div>-->
-        <!--</div>-->
-    <!--</fieldset>-->
-<!--</div>-->
-<!--<br>-->
-      <!--<p align="right"> Need a different settings on certain DataNodes? <a href="#">Add a DataNode group</a>-->
-      <!--<br>-->
-      <!--<br>-->
-      <!--<input class="btn btn-primary" type="button" value="Save and apply changes">-->
-    <!--</p>-->
-     <!--</div>-->
-{{!outlet}}
 <div id="serviceConfig">
-  <div class="accordion">
-    {{#each category in selectedService.configCategories}}
-
-      <div class="accordion-group">
-        <div class="accordion-heading">
-          {{#if category.isCollapsed}}
-          <i class='icon-caret-right pull-left accordion-toggle'></i>
-          {{else}}
-          <i class='icon-caret-down pull-left accordion-toggle'></i>
-          {{/if}}
-          <a class="accordion-toggle" {{action "onToggleBlock" category target="view"}}>
-            {{category.name}}
-          </a>
-        </div>
-
-        {{#unless category.isForSlaveComponent}}
-          {{#view App.ServiceConfigsByCategoryView categoryBinding="category" serviceConfigsBinding="selectedService.configs"}}
-            <form class="form-horizontal">
-
-              {{#each view.categoryConfigs}}
-                {{#if isVisible}}
-                  <div {{bindAttr class="errorMessage:error: :control-group"}}>
-                    <label class="control-label">{{displayName}}</label>
-
-                    <div class="controls">
-                      {{view viewClass serviceConfigBinding="this" categoryConfigsBinding="view.categoryConfigs"}}
-                      <span class="help-inline">{{errorMessage}}</span>
-                    </div>
-                  </div>
+    <div class="accordion">
+      {{#each category in selectedService.configCategories}}
+          <div class="accordion-group {{unbound category.name}}">
+              <div class="accordion-heading" {{action "onToggleBlock" category target="view"}}>
+                {{#if category.isCollapsed}}
+                    <i class='icon-caret-right pull-left accordion-toggle'></i>
+                {{else}}
+                    <i class='icon-caret-down pull-left accordion-toggle'></i>
                 {{/if}}
-              {{/each}}
-
-            </form>
-          {{/view}}
-        {{/unless}}
-
-        {{#if category.isForSlaveComponent}}
-          {{#view App.ServiceConfigsByCategoryView categoryBinding="category" serviceConfigsBinding="selectedService.configs" controllerBinding="App.router.mainServiceSlaveComponentGroupsController"}}
-            <div class="slave-component-group-menu">
-              {{view App.SlaveComponentGroupsMenu}}
-            </div>
-
-            {{#view App.AddSlaveComponentGroupButton slaveComponentNameBinding="category.name"}}
-              <a class="btn add-slave-component-group btn-large" {{action addSlaveComponentGroup target="controller"}}><i
-                class="icon-plus"></i></a>
-            {{/view}}
-            <div class="remove-group-error control-group warning">
-              <span class="help-inline">You cannot delete this group since there are hosts assigned to it. You must assign them to another group before you can delete this group.</span>
-            </div>
-
-            <form class="form-horizontal">
-
-              {{#view App.SlaveComponentChangeGroupNameView}}
-                <label class="control-label">Group name</label>
+                  <a class="accordion-toggle">
+                    {{category.name}}
+                  </a>
+              </div>
 
-                <div class="controls">
-                  <div class="span6">
-                    <input class="span9" type="text" {{bindAttr value="view.content.name"}}>
-                    <button class="btn" {{action changeGroupName target="view"}}>Save</button>
-                  </div>
-                  <span class="help-inline">{{view.errorMessage}}</span>
-                </div>
-              {{/view}}
 
+            {{#view App.ServiceConfigsByCategoryView categoryBinding="category" serviceConfigsBinding="selectedService.configs"}}
+                <form class="form-horizontal">
 
-              {{#each view.categoryConfigs}}
-                {{#if isVisible}}
-                  <div {{bindAttr class="errorMessage:error: :control-group"}}>
-                    <label class="control-label">{{displayName}}</label>
+                  {{#each view.categoryConfigs}}
+                    {{#if isVisible}}
+                        <div {{bindAttr class="errorMessage:error: :control-group"}}>
+                            <label class="control-label">{{displayName}}</label>
 
-                    <div class="controls">
-                      {{view viewClass serviceConfigBinding="this" categoryConfigsBinding="view.categoryConfigs"}}
-                      <span class="help-inline">{{errorMessage}}</span>
-                    </div>
-                  </div>
-                {{/if}}
-              {{/each}}
-
-            </form>
+                            <div class="controls">
+                              {{view viewClass serviceConfigBinding="this" categoryConfigsBinding="view.categoryConfigs"}}
+                                <span class="help-inline">{{errorMessage}}</span>
+                            </div>
+                        </div>
+                    {{/if}}
+                  {{/each}}
 
-          {{/view}}
-        {{/if}}
-      </div>
-    {{/each}}
-  </div>
-  <p class="pull-right">
-    <!--<input class="btn btn-primary" type="button" value="Save and apply changes" {{!bindAttr disabled="isSubmitDisabled"}} />-->
-    <a class="btn btn-primary" {{bindAttr disabled="isSubmitDisabled"}}
-      {{action restartServicePopup target="controller"}}>Save and apply changes</a>
-  </p>
+                </form>
+            {{/view}}
+          </div>
+      {{/each}}
+    </div>
+    <p class="pull-right">
+        <!--<input class="btn btn-primary" type="button" value="Save and apply changes" {{!bindAttr disabled="isSubmitDisabled"}} />-->
+        <a class="btn btn-primary" {{bindAttr disabled="isSubmitDisabled"}}
+          {{action restartServicePopup target="controller"}}>Save and apply changes</a>
+    </p>
 </div>