You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ak...@apache.org on 2013/11/14 17:22:02 UTC

git commit: AMBARI-3767. Provide basic config-group management dialog. (akovalenko)

Updated Branches:
  refs/heads/trunk a948d6b77 -> ef55a79c2


AMBARI-3767. Provide basic config-group management dialog. (akovalenko)


Project: http://git-wip-us.apache.org/repos/asf/incubator-ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ambari/commit/ef55a79c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ambari/tree/ef55a79c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ambari/diff/ef55a79c

Branch: refs/heads/trunk
Commit: ef55a79c264fdc14c99dca62fa83da9fc050a038
Parents: a948d6b
Author: Aleksandr Kovalenko <ol...@ukr.net>
Authored: Thu Nov 14 18:20:32 2013 +0200
Committer: Aleksandr Kovalenko <ol...@ukr.net>
Committed: Thu Nov 14 18:20:32 2013 +0200

----------------------------------------------------------------------
 ambari-web/app/controllers.js                   |   1 +
 ambari-web/app/controllers/main/service/item.js |  19 +++
 .../service/manage_config_groups_controller.js  | 144 +++++++++++++++++++
 ambari-web/app/messages.js                      |  10 +-
 ambari-web/app/models/config_group.js           |  84 ++++++-----
 ambari-web/app/styles/application.less          |   6 +
 .../manage_configuration_groups_popup.hbs       |  63 ++++++++
 ambari-web/app/utils/ajax.js                    |   3 +
 ambari-web/app/views.js                         |   1 +
 ambari-web/app/views/main/service/item.js       |   1 +
 .../main/service/manage_config_groups_view.js   |  48 +++++++
 11 files changed, 344 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/ef55a79c/ambari-web/app/controllers.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers.js b/ambari-web/app/controllers.js
index 80bfac9..37610be 100644
--- a/ambari-web/app/controllers.js
+++ b/ambari-web/app/controllers.js
@@ -72,6 +72,7 @@ require('controllers/main/service/reassign/step3_controller');
 require('controllers/main/service/reassign/step4_controller');
 require('controllers/main/service/reassign/step5_controller');
 require('controllers/main/service/reassign/step6_controller');
+require('controllers/main/service/manage_config_groups_controller');
 require('controllers/main/host');
 require('controllers/main/host/details');
 require('controllers/main/host/configs_service');

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/ef55a79c/ambari-web/app/controllers/main/service/item.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/item.js b/ambari-web/app/controllers/main/service/item.js
index c24c4f9..ca9ca35 100644
--- a/ambari-web/app/controllers/main/service/item.js
+++ b/ambari-web/app/controllers/main/service/item.js
@@ -215,6 +215,25 @@ App.MainServiceItemController = Em.Controller.extend({
     App.router.transitionTo('reassign');
   },
 
+  manageConfigurationGroups: function () {
+    var serviceName = this.get('content.serviceName');
+    var displayName = this.get('content.displayName');
+    App.ModalPopup.show({
+      header: Em.I18n.t('services.service.config_groups_popup.header').format(displayName),
+      bodyClass: App.MainServiceManageConfigGroupView.extend({
+        serviceName: serviceName,
+        controllerBinding: 'App.router.manageConfigGroupsController'
+      }),
+      classNames: ['sixty-percent-width-modal', 'manage-configuration-group-popup'],
+      primary: Em.I18n.t('common.save'),
+      onPrimary: function() {
+        this.hide();
+      },
+      secondary : Em.I18n.t('common.cancel'),
+      didInsertElement: function () {}
+    });
+  },
+
   /**
    * On click callback for <code>action</code> dropdown menu
    * Calls runSmokeTest, runRebalancer, runCompaction or reassignMaster depending on context

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/ef55a79c/ambari-web/app/controllers/main/service/manage_config_groups_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/manage_config_groups_controller.js b/ambari-web/app/controllers/main/service/manage_config_groups_controller.js
new file mode 100644
index 0000000..549a1cb
--- /dev/null
+++ b/ambari-web/app/controllers/main/service/manage_config_groups_controller.js
@@ -0,0 +1,144 @@
+/**
+ * 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.
+ */
+
+
+var App = require('app');
+App.ManageConfigGroupsController = App.WizardController.extend({
+
+  name: 'manageConfigGroupsController',
+
+  isLoaded: false,
+
+  serviceName: null,
+
+  configGroups: [],
+
+  selectedConfigGroup: null,
+
+  loadConfigGroups: function (serviceName) {
+    this.set('serviceName', serviceName);
+    App.ajax.send({
+      name: 'service.load_config_groups',
+      sender: this,
+      data: {
+        serviceName: serviceName
+      },
+      success: 'onLoadConfigGroupsSuccess',
+      error: 'onLoadConfigGroupsError'
+    });
+  },
+
+  onLoadConfigGroupsSuccess: function (data) {
+    var usedHosts = [];
+    var unusedHosts = [];
+    var defaultConfigGroup = App.ConfigGroup.create({
+      name: "Default",
+      description: "Default cluster level " + this.get('serviceName') + " configuration",
+      isDefault: true,
+      parentConfigGroup: null,
+      service: this.get('content'),
+      configSiteTags: []
+    });
+    if (data && data.items) {
+      var groupToTypeToTagMap = {};
+      var configGroups = [];
+      data.items.forEach(function (configGroup) {
+        configGroup = configGroup.ConfigGroup;
+        var newConfigGroup = App.ConfigGroup.create({
+          id: configGroup.id,
+          name: configGroup.group_name,
+          description: configGroup.description,
+          isDefault: false,
+          parentConfigGroup: defaultConfigGroup,
+          service: App.Service.find().findProperty('serviceName', configGroup.tag),
+          hosts: configGroup.hosts.mapProperty('host_name'),
+          configSiteTags: [],
+          properties: []
+        });
+        usedHosts = usedHosts.concat(newConfigGroup.get('hosts'));
+        configGroups.push(newConfigGroup);
+        configGroup.desired_configs.forEach(function (config) {
+          if (!groupToTypeToTagMap[configGroup.group_name]) {
+            groupToTypeToTagMap[configGroup.group_name] = {}
+          }
+          groupToTypeToTagMap[configGroup.group_name][config.type] = config.tag;
+        });
+      }, this);
+      unusedHosts = App.Host.find().mapProperty('hostName');
+      usedHosts.uniq().forEach(function (host) {
+        unusedHosts = unusedHosts.without(host);
+      }, this);
+      defaultConfigGroup.set('childConfigGroups', configGroups);
+      defaultConfigGroup.set('hosts', unusedHosts);
+      this.set('configGroups', [defaultConfigGroup].concat(configGroups));
+      this.loadProperties(groupToTypeToTagMap);
+      this.set('isLoaded', true);
+    }
+  },
+
+  onLoadConfigGroupsError: function () {
+    console.error('Unable to load config groups for service.');
+  },
+
+  loadProperties: function (groupToTypeToTagMap) {
+    var typeTagToGroupMap = {};
+    var urlParams = [];
+    for (var group in groupToTypeToTagMap) {
+      var overrideTypeTags = groupToTypeToTagMap[group];
+      for (var type in overrideTypeTags) {
+        var tag = overrideTypeTags[type];
+        typeTagToGroupMap[type + "///" + tag] = group;
+        urlParams.push('(type=' + type + '&tag=' + tag + ')');
+      }
+    }
+    var params = urlParams.join('|');
+    if (urlParams.length) {
+      App.ajax.send({
+        name: 'config.host_overrides',
+        sender: this,
+        data: {
+          params: params,
+          typeTagToGroupMap: typeTagToGroupMap
+        },
+        success: 'onLoadPropertiesSuccess'
+      });
+    }
+  },
+
+  onLoadPropertiesSuccess: function (data, opt, params) {
+    data.items.forEach(function (configs) {
+      var typeTagConfigs = [];
+      App.config.loadedConfigurationsCache[configs.type + "_" + configs.tag] = configs.properties;
+      var group = params.typeTagToGroupMap[configs.type + "///" + configs.tag];
+      for (var config in configs.properties) {
+        typeTagConfigs.push({
+          name: config,
+          value: configs.properties[config]
+        });
+      }
+      this.get('configGroups').findProperty('name', group).get('properties').pushObjects(typeTagConfigs);
+    }, this);
+  },
+
+  showProperties: function () {
+    var properies = this.get('selectedConfigGroup.propertiesList');
+    if (properies) {
+      App.showAlertPopup(Em.I18n.t('services.service.config_groups_popup.properties'), properies);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/ef55a79c/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index ead756f..1e2f49d 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -156,6 +156,8 @@ Em.I18n.translations = {
   'common.allServices':'All Services',
   'common.move':'Move',
   'common.change': 'Change',
+  'common.overrides': 'Overrides',
+  'common.properties': 'properties',
 
   'requestInfo.installComponents':'Install Components',
   'requestInfo.installServices':'Install Services',
@@ -953,6 +955,7 @@ Em.I18n.translations = {
   'services.service.actions.run.smoke':'Run Smoke Test',
   'services.service.actions.reassign.master':'Reassign {0}',
   'services.service.actions.reassign.master.hive':'Reassign HiveServer2, WebHCat Server, MySQL Server',
+  'services.service.actions.manage_configuration_groups':'Manage Configuration Groups...',
   'services.service.actions.serviceActions':'Service Actions...',
   'services.service.summary.unknown':'unknown',
   'services.service.summary.notRunning':'Not Running',
@@ -1111,7 +1114,12 @@ Em.I18n.translations = {
   'services.service.add':'Add Service',
   'services.service.startAll':'Start All',
   'services.service.stopAll':'Stop All',
-
+  'services.service.config_groups_popup.header':'Manage {0} Configuration Groups',
+  'services.service.config_groups_popup.notice':'Manage configuration groups of this service. Groups can be created, renamed and deleted and from here. Host memberships can also be changed. A host can belong to only one configuration group.',
+  'services.service.config_groups_popup.config_groups':'Configuration Groups',
+  'services.service.config_groups_popup.rename':'Rename',
+  'services.service.config_groups_popup.duplicate':'Duplicate',
+  'services.service.config_groups_popup.properties':'Properties',
   'services.reassign.closePopup':'Reassign {0} wizard is in progress. It\'s necessary to complete the wizard for Ambari to be in usable state. If you choose to quit, you must follow manual instructions to complete or revert reassign {0} wizard as documented in the Ambari User Guide. Are you sure you want to exit the wizard ?',
 
   'services.reassign.step1.header':'Get Started',

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/ef55a79c/ambari-web/app/models/config_group.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/config_group.js b/ambari-web/app/models/config_group.js
index d789793..2a70ce4 100644
--- a/ambari-web/app/models/config_group.js
+++ b/ambari-web/app/models/config_group.js
@@ -19,80 +19,94 @@
 var App = require('app');
 
 /**
- * Represents a configuration-group on the cluster. 
+ * Represents a configuration-group on the cluster.
  * A configuration-group is a collection of hosts
  * on which a collection of configurations are applied.
- * 
- * Configuration group hierarchy is at 2 levels. For 
+ *
+ * Configuration group hierarchy is at 2 levels. For
  * each service there is a 'Default' configuration group
  * containing all hosts not belonging to any group of that
- * service. 
- * 
+ * service.
+ *
  * A default configuration group has child configuration
  * groups which contain configuration overrides (deltas)
- * for a bunch of hosts. This allows different configurations 
+ * for a bunch of hosts. This allows different configurations
  * for different hosts in a heterogeneous cluster environment.
  */
 App.ConfigGroup = Ember.Object.extend({
-  id: DS.attr('number'),
-  name: DS.attr('string'),
-  description: DS.attr('string'),
-  isDefault: DS.attr('boolean'),
-  
+  id: null,
+  name: null,
+  description: null,
+  isDefault: null,
+
   /**
    * Parent configuration group for this group.
    * When {@link #isDefault} is true, this value is <code>null</code>
    * When {@link #isDefault} is false, this represents the configuration
    * deltas that are applied on the default.
    */
-  parentConfigGroup: DS.belongsTo('App.ConfigGroup'),
-  
+  parentConfigGroup: null,
+
   /**
    * Children configuration groups for this group.
    * When {@link #isDefault} is false, this value is <code>null</code>
    * When {@link #isDefault} is true, this represents the various
    * configuration groups that override the default.
    */
-  childConfigGroups: DS.hasMany('App.ConfigGroup'),
-  
+  childConfigGroups: [],
+
   /**
-   * Service for which this configuration-group 
+   * Service for which this configuration-group
    * is applicable.
    */
-  service: DS.belongsTo('App.Service'),
-  
+  service: null,
+
   /**
-   * Hosts on which this configuration-group 
-   * is to be applied. For a service, a host can 
+   * Hosts on which this configuration-group
+   * is to be applied. For a service, a host can
    * belong to only one non-default configuration-group.
-   * 
+   *
    * When {#isDefault} is false, this contains hosts
    * for which the overrides will apply.
-   * 
-   * When {#isDefault} is true, this value is empty, as 
+   *
+   * When {#isDefault} is true, this value is empty, as
    * it dynamically reflects hosts not belonging to other
    * non-default groups.
-   * 
+   *
    */
-  hosts: DS.hasMany('App.Host'),
-  
+  hosts: [],
+
+  displayName: function () {
+    return this.get('name') + ' (' + this.get('hosts.length') + ')';
+  }.property('name', 'hosts.length'),
+
   /**
-   * Provides hosts which are available for inclusion in 
-   * non-default configuration groups. 
+   * Provides hosts which are available for inclusion in
+   * non-default configuration groups.
    */
-  availableHosts: function() {
-    
+  availableHosts: function () {
+
   }.property('isDefault', 'parentConfigGroup', 'childConfigGroups'),
-  
+
   /**
    * Collection of (site, tag) pairs representing properties.
-   * 
-   * When {#isDefault} is true, this represents the 
+   *
+   * When {#isDefault} is true, this represents the
    * default cluster configurations for that service.
-   * 
+   *
    * When {#isDefault} is false, this represents the
    * configuration overrides on top of the cluster default for the
    * hosts identified by 'hosts'.
    */
-  configSiteTags: DS.hasMany('App.ConfigSiteTag')
+  configSiteTags: [],
+
+  properties: [],
+
+  propertiesList: function () {
+    var result = '';
+    this.get('properties').forEach(function (item) {
+      result += item.name + " : " + item.value + '\n';
+    }, this);
+    return result;
+  }.property('properties.length')
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/ef55a79c/ambari-web/app/styles/application.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less
index 8b00c90..a8be260 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -4870,3 +4870,9 @@ i.icon-asterisks {
     }
   }
 }
+
+.manage-configuration-group-popup {
+  .group-select {
+    width: 100%
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/ef55a79c/ambari-web/app/templates/main/service/manage_configuration_groups_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/manage_configuration_groups_popup.hbs b/ambari-web/app/templates/main/service/manage_configuration_groups_popup.hbs
new file mode 100644
index 0000000..073a8d1
--- /dev/null
+++ b/ambari-web/app/templates/main/service/manage_configuration_groups_popup.hbs
@@ -0,0 +1,63 @@
+{{!
+* 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 class="alert alert-info">{{t services.service.config_groups_popup.notice}}</div>
+<div class="row-fluid">
+  <div class="span12">
+    {{t services.service.config_groups_popup.config_groups}}
+    <div class="row-fluid">
+      <div class="span6">
+        {{view Em.Select
+          contentBinding="configGroups"
+          optionLabelPath="content.displayName"
+          selectionBinding="view.selectedConfigGroup"
+          multiple="multiple"
+          class="group-select"
+        }}
+        <div class="button-group pull-right">
+          <a class="btn">+</a>
+          <a class="btn">-</a>
+          <a class="btn">{{t services.service.config_groups_popup.rename}}</a>
+          <a class="btn">{{t services.service.config_groups_popup.duplicate}}</a>
+        </div>
+      </div>
+      <div class="span6">
+        <div class="row-fluid">
+          <div class="span2">{{t common.hosts}}</div>
+          <div class="span10">
+            {{view Em.Select
+              contentBinding="selectedConfigGroup.hosts"
+              multiple="multiple"
+              class="group-select"
+            }}
+          </div>
+          <div class="button-group pull-right">
+            <a class="btn">+</a>
+            <a class="btn">-</a>
+          </div>
+        </div>
+        <div class="row-fluid">
+          <div class="span2">{{t common.overrides}}</div>
+          <div class="span10">
+            <a href="" class="properties-link" {{action showProperties target="controller"}}
+               rel="HealthTooltip" {{bindAttr data-original-title="selectedConfigGroup.propertiesList" }}>{{selectedConfigGroup.properties.length}} {{t common.properties}}</a>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/ef55a79c/ambari-web/app/utils/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax.js b/ambari-web/app/utils/ajax.js
index 91654d3..45da260 100644
--- a/ambari-web/app/utils/ajax.js
+++ b/ambari-web/app/utils/ajax.js
@@ -90,6 +90,9 @@ var urls = {
       };
     }
   },
+  'service.load_config_groups': {
+    'real': '/clusters/{clusterName}/config_groups?ConfigGroup/tag={serviceName}&fields=*'
+  },
   'reassign.stop_services': {
     'real': '/clusters/{clusterName}/services',
     'mock': '',

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/ef55a79c/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index c7980cb..2e53b86 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -196,6 +196,7 @@ require('views/main/service/reassign/step3_view');
 require('views/main/service/reassign/step4_view');
 require('views/main/service/reassign/step5_view');
 require('views/main/service/reassign/step6_view');
+require('views/main/service/manage_config_groups_view');
 require('views/main/charts/menu');
 require('views/main/charts/heatmap');
 require('views/main/charts/heatmap/heatmap_rack');

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/ef55a79c/ambari-web/app/views/main/service/item.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/item.js b/ambari-web/app/views/main/service/item.js
index 76d0712..3352e62 100644
--- a/ambari-web/app/views/main/service/item.js
+++ b/ambari-web/app/views/main/service/item.js
@@ -43,6 +43,7 @@ App.MainServiceItemView = Em.View.extend({
         }
       default:
         options.push({action: 'runSmokeTest', 'label': Em.I18n.t('services.service.actions.run.smoke'), disabled:disabled});
+        options.push({action: 'manageConfigurationGroups', 'label': Em.I18n.t('services.service.actions.manage_configuration_groups'), disabled:false});
     }
     return options;
   }.property('controller.content', 'controller.isStopDisabled'),

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/ef55a79c/ambari-web/app/views/main/service/manage_config_groups_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/manage_config_groups_view.js b/ambari-web/app/views/main/service/manage_config_groups_view.js
new file mode 100644
index 0000000..9a50d10
--- /dev/null
+++ b/ambari-web/app/views/main/service/manage_config_groups_view.js
@@ -0,0 +1,48 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.MainServiceManageConfigGroupView = Em.View.extend({
+
+  templateName: require('templates/main/service/manage_configuration_groups_popup'),
+
+  selectedConfigGroup: null,
+
+  onGroupSelect: function () {
+    var selectedConfigGroup = this.get('selectedConfigGroup');
+    // to unable user select more than one config group at a time
+    if (selectedConfigGroup.length) {
+      this.set('controller.selectedConfigGroup', selectedConfigGroup[selectedConfigGroup.length - 1]);
+    }
+    if (selectedConfigGroup.length > 1) {
+      this.set('selectedConfigGroup', selectedConfigGroup[selectedConfigGroup.length - 1]);
+    }
+  }.observes('selectedConfigGroup'),
+
+  onLoad: function () {
+    if (this.get('controller.isLoaded')) {
+      this.set('selectedConfigGroup', this.get('controller.configGroups')[0])
+    }
+  }.observes('controller.isLoaded', 'controller.configGroups'),
+
+  didInsertElement: function () {
+    this.get('controller').loadConfigGroups(this.get('serviceName'));
+    $('.properties-link').tooltip();
+  }
+});