You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ab...@apache.org on 2015/03/17 15:47:45 UTC

ambari git commit: AMBARI-10104 Enhanced Configs: create models and mappers for tab sections and subsections. (ababiichuk)

Repository: ambari
Updated Branches:
  refs/heads/trunk 9fe2741dc -> ea1d531be


AMBARI-10104 Enhanced Configs: create models and mappers for tab sections and subsections. (ababiichuk)


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

Branch: refs/heads/trunk
Commit: ea1d531beb002af36e320550873594acae5147d4
Parents: 9fe2741
Author: aBabiichuk <ab...@cybervisiontech.com>
Authored: Tue Mar 17 16:14:03 2015 +0200
Committer: aBabiichuk <ab...@cybervisiontech.com>
Committed: Tue Mar 17 16:47:37 2015 +0200

----------------------------------------------------------------------
 .../app/assets/data/configurations/theme.json   | 174 ++++++++++++++
 ambari-web/app/assets/test/tests.js             |   1 +
 ambari-web/app/mappers.js                       |   1 +
 .../mappers/configs/config_versions_mapper.js   |  14 +-
 ambari-web/app/mappers/configs/themes_mapper.js | 177 +++++++++++++++
 ambari-web/app/models.js                        |   5 +-
 .../app/models/configs/config_property.js       |  46 +++-
 ambari-web/app/models/configs/config_version.js |  10 +-
 ambari-web/app/models/configs/section.js        |  37 +++
 .../app/models/configs/stack_config_property.js |   8 +-
 ambari-web/app/models/configs/sub_section.js    |  36 +++
 ambari-web/app/models/configs/tab.js            |  34 +++
 ambari-web/app/utils/ajax/ajax.js               |   6 +
 ambari-web/app/utils/config.js                  |  59 +++--
 .../views/common/configs/service_config_view.js |  32 +--
 .../test/mappers/configs/themes_mapper_test.js  | 225 +++++++++++++++++++
 16 files changed, 800 insertions(+), 65 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/assets/data/configurations/theme.json
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/data/configurations/theme.json b/ambari-web/app/assets/data/configurations/theme.json
new file mode 100644
index 0000000..b756230
--- /dev/null
+++ b/ambari-web/app/assets/data/configurations/theme.json
@@ -0,0 +1,174 @@
+{
+  "Theme": {
+    "name": "default",
+    "service_name": "YARN",
+    "description": "Default theme for YARN service",
+    "configuration": {
+      "layouts": [
+        {
+          "name": "default",
+          "tabs": [
+            {
+              "name": "Settings",
+              "display-name": "Settings",
+              "layout": {
+                "tab-columns": "1",
+                "tab-rows": "2",
+                "sections": [
+                  {
+                    "name": "section-nm-sizing",
+                    "display-name": "NM Sizing",
+                    "row-index": "0",
+                    "column-span": "1",
+                    "column-index": "0",
+                    "row-span": "1",
+                    "section-columns": "1",
+                    "section-rows": "1",
+                    "subsections": []
+                  },
+                  {
+                    "name": "section-scheduler-defaults",
+                    "display-name": "Scheduler Defaults",
+                    "row-index": "1",
+                    "column-index": "0",
+                    "row-span": "1",
+                    "section-columns": "1",
+                    "section-rows": "1",
+                    "subsections": []
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      ]
+    },
+    "placement": {
+      "configuration-layout": "default",
+      "configs": [
+        {
+          "config": "yarn.nodemanager.resource.memory-mb",
+          "section-name": "section-nm-sizing"
+        },
+        {
+          "config": "yarn.nodemanager.vmem-pmem-ratio",
+          "section-name": "section-nm-sizing"
+        },
+        {
+          "config": "yarn.nodemanager.resource.percentage-physical-cpu-limit",
+          "section-name": "section-nm-sizing"
+        },
+        {
+          "config": "yarn.nodemanager.resource.cpu-vcores",
+          "section-name": "section-nm-sizing"
+        },
+        {
+          "config": "yarn.scheduler.minimum-allocation-mb",
+          "section-name": "section-scheduler-defaults"
+        },
+        {
+          "config": "yarn.scheduler.maximum-allocation-mb",
+          "section-name": "section-scheduler-defaults"
+        },
+        {
+          "config": "yarn.scheduler.minimum-allocation-vcores",
+          "section-name": "section-scheduler-defaults"
+        },
+        {
+          "config": "yarn.scheduler.maximum-allocation-vcores",
+          "section-name": "section-scheduler-defaults"
+        }
+      ]
+    },
+    "widgets":[
+      {
+        "config":"yarn.nodemanager.resource.memory-mb",
+        "widget":{
+          "type":"slider",
+          "units":[
+            {
+              "unit-name":"MB"
+            }
+          ]
+        }
+      },
+      {
+        "config": "yarn.nodemanager.vmem-pmem-ratio",
+        "widget": {
+          "type": "slider",
+          "units": [
+            {
+              "unit-name": "float"
+            }
+          ]
+        }
+      },
+      {
+        "config": "yarn.nodemanager.resource.percentage-physical-cpu-limit",
+        "widget": {
+          "type": "slider",
+          "units": [
+            {
+              "unit-name": "percent"
+            }
+          ]
+        }
+      },
+      {
+        "config": "yarn.nodemanager.resource.cpu-vcores",
+        "widget": {
+          "type": "slider",
+          "units": [
+            {
+              "unit-name": "int"
+            }
+          ]
+        }
+      },
+      {
+        "config":"yarn.scheduler.minimum-allocation-mb",
+        "widget":{
+          "type":"slider",
+          "units":[
+            {
+              "unit-name":"MB"
+            }
+          ]
+        }
+      },
+      {
+        "config":"yarn.scheduler.maximum-allocation-mb",
+        "widget":{
+          "type":"slider",
+          "units":[
+            {
+              "unit-name":"MB"
+            }
+          ]
+        }
+      },
+      {
+        "config":"yarn.scheduler.minimum-allocation-vcores",
+        "widget":{
+          "type":"slider",
+          "units":[
+            {
+              "unit-name":"int"
+            }
+          ]
+        }
+      },
+      {
+        "config":"yarn.scheduler.maximum-allocation-vcores",
+        "widget":{
+          "type":"slider",
+          "units":[
+            {
+              "unit-name":"int"
+            }
+          ]
+        }
+      }
+    ]
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/assets/test/tests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js
index 12a4d07..ee6d81c 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -133,6 +133,7 @@ var files = ['test/init_model_test',
   'test/mappers/configs/stack_config_properties_mapper_test',
   'test/mappers/configs/config_groups_mapper_test',
   'test/mappers/configs/config_versions_mapper_test',
+  'test/mappers/configs/themes_mapper_test',
   'test/mixins/common/chart/storm_linear_time_test',
   'test/mixins/common/localStorage_test',
   'test/mixins/common/serverValidator_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/mappers.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers.js b/ambari-web/app/mappers.js
index 01c49eb..04086af 100644
--- a/ambari-web/app/mappers.js
+++ b/ambari-web/app/mappers.js
@@ -21,6 +21,7 @@ require('mappers/server_data_mapper');
 require('mappers/stack_service_mapper');
 require('mappers/stack_mapper');
 require('mappers/stack_version_mapper');
+require('mappers/configs/themes_mapper');
 require('mappers/configs/stack_config_properties_mapper');
 //require('mappers/configs/config_groups_mapper');
 require('mappers/configs/config_versions_mapper');

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/mappers/configs/config_versions_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/configs/config_versions_mapper.js b/ambari-web/app/mappers/configs/config_versions_mapper.js
index f268e13..1ae43dc 100644
--- a/ambari-web/app/mappers/configs/config_versions_mapper.js
+++ b/ambari-web/app/mappers/configs/config_versions_mapper.js
@@ -32,10 +32,11 @@ App.configVersionsMapper = App.QuickDataMapper.create({
     author: 'user',
     notes: 'service_config_version_note',
     is_current: 'is_current',
-    index: 'index'
+    index: 'index',
+    is_for_compare: 'is_for_compare'
   },
 
-  map: function (json) {
+  map: function (json, isForCompare) {
     var configVersions = [];
     var itemIds = {};
     var serviceToHostMap = {};
@@ -48,6 +49,7 @@ App.configVersionsMapper = App.QuickDataMapper.create({
         itemIds[parsedItem.id] = true;
         parsedItem.index = index;
         parsedItem.config_properties = [];
+        parsedItem.is_for_compare = Boolean(isForCompare);
 
         if (serviceToHostMap[item.service_name]) {
           serviceToHostMap[item.service_name] = serviceToHostMap[item.service_name].concat(item.hosts);
@@ -66,12 +68,14 @@ App.configVersionsMapper = App.QuickDataMapper.create({
             var properties_array = Object.keys(config.properties);
             for (var i = 0; i < properties_array.length; i++) {
               var key = properties_array[i];
-              var property = App.StackConfigProperty.find(key  + '_' + type).toJSON();
 
-              property.id = key  + '_' + type + '_' + item.service_config_version;
+              var property = {
+                id: key  + '_' + type + '_' + item.service_config_version,
+                config_version_id: parsedItem.id,
+                stack_config_property_id: key  + '_' + type
+              };
               property.value = property.default_value = config.properties[key];
               property.is_final = property.default_is_final = !!item.properties_attributes && item.properties_attributes.final[key] === "true";
-              property.config_version_id = parsedItem.id;
 
               properties.push(property);
             }

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/mappers/configs/themes_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/configs/themes_mapper.js b/ambari-web/app/mappers/configs/themes_mapper.js
new file mode 100644
index 0000000..62f4d31
--- /dev/null
+++ b/ambari-web/app/mappers/configs/themes_mapper.js
@@ -0,0 +1,177 @@
+/**
+ * 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.themesMapper = App.QuickDataMapper.create({
+  tabModel: App.Tab,
+  sectionModel: App.Section,
+  subSectionModel: App.SubSection,
+
+  tabConfig: {
+    "id": "name",
+    "name": "name",
+    "display_name": "display-name",
+    "columns": "layout.tab-columns",
+    "rows": "layout.tab-rows",
+    "service_name": "service_name",
+    "sections_key": "sections"
+  },
+
+  sectionConfig: {
+    "id": "name",
+    "name": "name",
+    "display_name": "display-name",
+    "row_index": "row-index",
+    "column_index": "column-index",
+    "row_span": "row-span",
+    "column_span": "column-span",
+    "section_columns": "section-columns",
+    "section_rows": "section-rows",
+    "tab_id": "tab_id"
+  },
+
+  subSectionConfig: {
+    "id": "name",
+    "name": "name",
+    "display_name": "display-name",
+    "border": "border",
+    "row_index": "row-index",
+    "column_index": "column-index",
+    "column_span": "column-span",
+    "row_span": "row-span",
+    "configProperties": "config_properties",
+    "section_id": "section_id"
+  },
+
+  map: function (json) {
+    var tabs = [];
+    if (Em.get(json, "Theme.configuration.layouts")) {
+      var serviceName = Em.get(json, "Theme.service_name");
+      Em.get(json, "Theme.configuration.layouts").forEach(function(layout) {
+
+        if (layout.tabs) {
+          layout.tabs.forEach(function(tab) {
+            var parsedTab = this.parseIt(tab, this.get("tabConfig"));
+            parsedTab.id = serviceName + "_" + tab.name;
+            parsedTab.service_name = serviceName;
+
+            if (Em.get(tab, "layout.sections")) {
+              var sections = [];
+              Em.get(tab, "layout.sections").forEach(function(section) {
+                var parsedSection = this.parseIt(section, this.get("sectionConfig"));
+                parsedSection.tab_id = parsedTab.id;
+
+                if (section.subsections) {
+                  var subSections = [];
+                  section.subsections.forEach(function(subSection) {
+                    var parsedSubSection = this.parseIt(subSection, this.get("subSectionConfig"));
+                    parsedSubSection.section_id = parsedSection.id;
+
+                    subSections.push(parsedSubSection);
+                  }, this);
+                  App.store.loadMany(this.get("subSectionModel"), subSections);
+                  parsedSection.sub_sections = subSections.mapProperty("id");
+                }
+
+                sections.push(parsedSection);
+              }, this);
+
+              App.store.loadMany(this.get("sectionModel"), sections);
+              parsedTab.sections = sections.mapProperty("id");
+            }
+
+            tabs.push(parsedTab);
+          }, this)
+        }
+
+      }, this);
+    }
+
+    /**
+     * create tie between <code>stackConfigProperty<code> and <code>subSection<code>
+     */
+    if (Em.get(json, "Theme.configuration.placement.configs")) {
+      Em.get(json, "Theme.configuration.placement.configs").forEach(function(configLink) {
+
+        var configId = this.getConfigId(configLink);
+        var subSectionId = configLink["subsection-name"];
+
+        var subSection = App.SubSection.find(subSectionId);
+        var configProperty = App.StackConfigProperty.find(configId);
+        if (configProperty && subSection) {
+          subSection.get('configProperties').pushObject(configProperty);
+          configProperty.set('subSection', subSection);
+        } else {
+          console.warn('there is no such property: ' + configId + '. Or subsection: ' + subSectionId);
+        }
+      }, this);
+    }
+
+    /**
+     * add widget object to <code>stackConfigProperty<code>
+     */
+    if (Em.get(json, "Theme.configuration.widgets")) {
+      Em.get(json, "Theme.configuration.widgets").forEach(function(widget) {
+        var configId = this.getConfigId(widget);
+
+        var configProperty = App.StackConfigProperty.find(configId);
+        if (configProperty) {
+          configProperty.set('widget', widget.widget);
+        } else {
+          console.warn('there is no such property: ' + configId);
+        }
+      }, this);
+    }
+    App.store.loadMany(this.get("tabModel"), tabs);
+  },
+
+  /**
+   * transform info from json to config id
+   * @param {Object} json
+   * @returns {string|null}
+   */
+  getConfigId: function(json) {
+    if (json && json.config && typeof json.config === "string") {
+      var split = json.config.split("/");
+      return split[1] + "_" + split[0];
+    } else {
+      console.warn('getConfigId: invalid input data');
+      return null;
+    }
+  },
+
+  /**
+   * generates Advanced tabs for selected or all services
+   * @param {[string]} [serviceNames=null]
+   */
+  generateAdvancedTabs: function(serviceNames) {
+    serviceNames = Em.isArray(serviceNames) ? serviceNames : App.StackService.find().mapProperty('serviceName');
+    var advancedTabs = [];
+    serviceNames.forEach(function(serviceName) {
+      advancedTabs.pushObject({
+        id: serviceName + '_advanced',
+        name: 'advanced',
+        display_name: 'Advanced',
+        is_advanced: true,
+        service_name: serviceName
+      });
+    });
+    App.store.loadMany(this.get("tabModel"), advancedTabs);
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/models.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models.js b/ambari-web/app/models.js
index 361b099..c762a7f 100644
--- a/ambari-web/app/models.js
+++ b/ambari-web/app/models.js
@@ -64,4 +64,7 @@ require('models/configs/service_config_version');
 require('models/configs/stack_config_property');
 //require('models/configs/config_group');
 require('models/configs/config_version');
-require('models/configs/config_property');
\ No newline at end of file
+require('models/configs/config_property');
+require('models/configs/tab');
+require('models/configs/section');
+require('models/configs/sub_section');
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/models/configs/config_property.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/configs/config_property.js b/ambari-web/app/models/configs/config_property.js
index 49cf3a2..6be05d8 100644
--- a/ambari-web/app/models/configs/config_property.js
+++ b/ambari-web/app/models/configs/config_property.js
@@ -18,7 +18,7 @@
 
 var App = require('app');
 
-App.ConfigProperty = App.StackConfigProperty.extend({
+App.ConfigProperty = DS.Model.extend({
 
   /**
    * id is consist of property <code>name<code>+<code>fileName<code>+<code>configVersion.version<code>
@@ -33,10 +33,22 @@ App.ConfigProperty = App.StackConfigProperty.extend({
   value: DS.attr('string'),
 
   /**
+   * default value of property
+   * @property {string}
+   */
+  defaultValue: DS.attr('string'),
+
+  /**
    * defines if property is final
    * @property {boolean}
    */
-  isFinal:  DS.attr('boolean', {defaultValue: false}),
+  isFinal: DS.attr('boolean', {defaultValue: false}),
+
+  /**
+   * defines if property is final by default
+   * @property {boolean}
+   */
+  defaultIsFinal: DS.attr('boolean', {defaultValue: false}),
 
   /**
    * link to config version
@@ -45,6 +57,13 @@ App.ConfigProperty = App.StackConfigProperty.extend({
   configVersion: DS.belongsTo('App.ConfigVersion'),
 
   /**
+   * link to config version
+   * from this model we can get all static info about property
+   * @property {App.ConfigVersion}
+   */
+  stackConfigProperty: DS.belongsTo('App.StackConfigProperty'),
+
+  /**
    * defines if property should be visible for user
    * all properties that has <code>isVisible<code> false will be present in model
    * and saved but will be hidden from user
@@ -84,12 +103,6 @@ App.ConfigProperty = App.StackConfigProperty.extend({
   isSecureConfig: DS.attr('boolean', {defaultValue: false}),
 
   /**
-   * defines if property is added by user
-   * @property {boolean}
-   */
-  isUserProperty: DS.attr('boolean', {defaultValue: false}),
-
-  /**
    * if true - don't show property
    * @property {boolean}
    */
@@ -144,6 +157,23 @@ App.ConfigProperty = App.StackConfigProperty.extend({
   }.property('configVersion.isDefault'),
 
   /**
+   * defines if property is added by user
+   * @property {boolean}
+   */
+  isUserProperty: function() {
+    return Em.isNone(this.get('stackConfigProperty'));
+  }.property('stackConfigProperty'),
+
+  /**
+   * defines if this property is belongs to version
+   * with which we make comparison
+   * @property {boolean}
+   */
+  isForCompare: function() {
+    return this.get('configVersion.isForCompare');
+  }.property('configVersion.isForCompare'),
+
+  /**
    * Indicates when value is not the default value.
    * Returns false when there is no default value.
    * @type {boolean}

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/models/configs/config_version.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/configs/config_version.js b/ambari-web/app/models/configs/config_version.js
index 29bc52b..4acb664 100644
--- a/ambari-web/app/models/configs/config_version.js
+++ b/ambari-web/app/models/configs/config_version.js
@@ -20,7 +20,15 @@
 var App = require('app');
 
 App.ConfigVersion = App.ServiceConfigVersion.extend({
-  configProperties: DS.hasMany('App.ConfigProperty')
+  configProperties: DS.hasMany('App.ConfigProperty'),
+
+  /**
+   * this flag is true when we compare some version with
+   * this config version
+   * this flag make influence on displaying properties
+   * @property {boolean} [isForCompare=false]
+   */
+  isForCompare: DS.attr('boolean', {defaultValue: false})
 });
 
 App.ConfigVersion.FIXTURES = [];

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/models/configs/section.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/configs/section.js b/ambari-web/app/models/configs/section.js
new file mode 100644
index 0000000..d78cfea
--- /dev/null
+++ b/ambari-web/app/models/configs/section.js
@@ -0,0 +1,37 @@
+/**
+ * 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.Section = DS.Model.extend({
+  id: DS.attr('string'),
+  name: DS.attr('string'),
+  displayName: DS.attr('string'),
+  rowIndex: DS.attr('number', {defaultValue: 1}),
+  rowSpan: DS.attr('number', {defaultValue: 1}),
+  columnIndex: DS.attr('number', {defaultValue: 1}),
+  columnSpan: DS.attr('number', {defaultValue: 1}),
+  sectionColumns: DS.attr('number', {defaultValue: 1}),
+  sectionRows: DS.attr('number', {defaultValue: 1}),
+  subSections: DS.hasMany('App.SubSection'),
+  tab: DS.belongsTo('App.Tab')
+});
+
+
+App.Section.FIXTURES = [];
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/models/configs/stack_config_property.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/configs/stack_config_property.js b/ambari-web/app/models/configs/stack_config_property.js
index 7a29fac..4a87b36 100644
--- a/ambari-web/app/models/configs/stack_config_property.js
+++ b/ambari-web/app/models/configs/stack_config_property.js
@@ -147,7 +147,13 @@ App.StackConfigProperty = DS.Model.extend({
    * }
    * @property {object}
    */
-  valueAttributes: DS.attr('object', {defaultValue: null})
+  valueAttributes: DS.attr('object', {defaultValue: null}),
+
+  /**
+   * sub section to which belongs this property
+   * @property {App.SubSection}
+   */
+  subSection: DS.belongsTo('App.SubSection')
 
 });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/models/configs/sub_section.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/configs/sub_section.js b/ambari-web/app/models/configs/sub_section.js
new file mode 100644
index 0000000..648aae3
--- /dev/null
+++ b/ambari-web/app/models/configs/sub_section.js
@@ -0,0 +1,36 @@
+/**
+ * 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.SubSection = DS.Model.extend({
+  id: DS.attr('string'),
+  name: DS.attr('string'),
+  displayName: DS.attr('string'),
+  border: DS.attr('boolean', {defaultValue: true}),
+  rowIndex: DS.attr('number', {defaultValue: 1}),
+  columnIndex: DS.attr('number', {defaultValue: 1}),
+  rowSpan: DS.attr('number', {defaultValue: 1}),
+  columnSpan: DS.attr('number', {defaultValue: 1}),
+  section: DS.belongsTo('App.Section'),
+  configProperties: DS.hasMany('App.StackConfigProperty')
+});
+
+
+App.SubSection.FIXTURES = [];
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/models/configs/tab.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/configs/tab.js b/ambari-web/app/models/configs/tab.js
new file mode 100644
index 0000000..0365ffb
--- /dev/null
+++ b/ambari-web/app/models/configs/tab.js
@@ -0,0 +1,34 @@
+/**
+ * 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.Tab = DS.Model.extend({
+  id: DS.attr('string'),
+  name: DS.attr('string'),
+  displayName: DS.attr('string'),
+  columns: DS.attr('number', {defaultValue: 1}),
+  rows: DS.attr('number', {defaultValue: 1}),
+  isAdvanced: DS.attr('boolean', {defaultValue: false}),
+  serviceName: DS.attr('string'),
+  sections: DS.hasMany('App.Section')
+});
+
+
+App.Tab.FIXTURES = [];
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/utils/ajax/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index 66136de..66353fb 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -603,7 +603,13 @@ var urls = {
       }
     }
   },
+  /*************************CONFIG THEME****************************************/
 
+  'configs.theme': {
+    'real': '{stackVersionUrl}/services/{serviceName}/artifacts/theme',
+    'mock': '/data/configurations/theme.json'
+  },
+  
   /*************************CONFIG GROUPS***************************************/
 
   'configs.config_groups.load.all': {

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/utils/config.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/config.js b/ambari-web/app/utils/config.js
index 1e96bb6..91ae72d 100644
--- a/ambari-web/app/utils/config.js
+++ b/ambari-web/app/utils/config.js
@@ -1910,38 +1910,37 @@ App.config = Em.Object.create({
 
   /**
    * load config groups
-   * @param {String} serviceName
-   * @param {Number} configGroupId
+   * @param {string} [serviceName=null]
+   * @param {number} [configGroupId=null]
+   * @param {number} [configVersion=null]
+   * @param {boolean} [isForCompare=false]
    * @returns {$.ajax}
    * @method loadConfigGroups
    */
-  loadConfigVersions: function (serviceName, configGroupId, configVersion) {
-    var info = this.generateAjaxDataForVersions(serviceName, configGroupId, configVersion);
-    return App.ajax.send({
-      name: info.name,
-      sender: this,
-      data: info.data,
-      success: 'saveConfigVersionsToModel'
-    });
+  loadConfigVersions: function (serviceName, configGroupId, configVersion, isForCompare) {
+    var info = this.generateAjaxDataForVersions(serviceName, configGroupId, configVersion, isForCompare);
+    return App.ajax.send($.extend({sender: this, success: 'saveConfigVersionsToModel'}, info));
   },
 
   /**
    * generate ajax info
-   * @param serviceName
-   * @param configGroupId
-   * @param configVersion
+   * @param {string} [serviceName=null]
+   * @param {number} [configGroupId=null]
+   * @param {number} [configVersion=null]
+   * @param {boolean} [isForCompare=false]
    * @returns {{name: string, data: {}}}
    */
-  generateAjaxDataForVersions: function (serviceName, configGroupId, configVersion) {
+  generateAjaxDataForVersions: function (serviceName, configGroupId, configVersion, isForCompare) {
     var result = {
       name: 'configs.config_versions.load.all.min',
       data: {}
-    }
+    };
     if (serviceName) {
       result.data.serviceName = serviceName;
       if (configVersion) {
         result.name = 'configs.config_versions.load';
-        result.data.configVersion = configVersion
+        result.data.configVersion = configVersion;
+        result.data.isForCompare = isForCompare;
       } else if (configGroupId) {
         result.name = 'configs.config_versions.load.group';
         result.data.configGroupId = configGroupId;
@@ -1958,7 +1957,31 @@ App.config = Em.Object.create({
    * @param opt
    * @param params
    */
-  saveConfigVersionsToModel: function (data) {
-    App.configVersionsMapper.map(data);
+  saveConfigVersionsToModel: function (data, opt, params) {
+    App.configVersionsMapper.map(data, params.isForCompare);
+  },
+
+  /**
+   * load config themes
+   * @param {string} serviceName
+   * @returns {$.ajax}
+   */
+  loadConfigTheme: function(serviceName) {
+    return App.ajax.send({
+      name: 'configs.theme',
+      sender: this,
+      data: {
+        serviceName: serviceName
+      },
+      success: 'saveThemeToModel'
+    });
+  },
+
+  /**
+   * runs <code>themeMapper<code>
+   * @param data
+   */
+  saveThemeToModel: function(data) {
+    App.themesMapper.map(data);
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/app/views/common/configs/service_config_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/configs/service_config_view.js b/ambari-web/app/views/common/configs/service_config_view.js
index ec11fe5..fc58b48 100644
--- a/ambari-web/app/views/common/configs/service_config_view.js
+++ b/ambari-web/app/views/common/configs/service_config_view.js
@@ -85,43 +85,13 @@ App.ServiceConfigView = Em.View.extend({
       'App.router.wizardStep7Controller.selectedConfigGroup.name'
   ),
 
-  // TODO remove workaround for `Advanced` tab after Tab model implementation
-  /**
-   * Create `Advanced` tab which is not related to fancy tab section.
-   * This tab contains properties without widgets.
-   *
-   * @returns {Ember.Object}
-   */
-  getAdvancedTab: function() {
-    var tabId = 'advanced-' + this.get('controller.selectedService.serviceName').toLowerCase() + '-tab';
-    return this.createTabObject(Em.Object.create({
-      id: tabId,
-      name: Em.I18n.t('common.advanced')
-    }));
-  },
-
-  /**
-   * @param {Ember.Object} model
-   * @returns {Ember.Object}
-   */
-  createTabObject: function(model) {
-    return Em.Object.create({
-      id: model.get('id'),
-      displayName: model.get('name'),
-      tabClassName: '.' + model.get('id'),
-      isAdvanced: model.get('name') == Em.I18n.t('common.advanced')
-    });
-  },
-
   /**
    * Object that used for Twitter Bootstrap tabs markup.
    *
    * @returns {Ember.A}
    */
   tabs: function() {
-    var tabs = Em.A([]);
-    // filter Tab models by service name and create object here
-    tabs.pushObject(this.getAdvancedTab());
+    var tabs = App.Tab.find();
     // make first tab active
     tabs.get('firstObject').set('isActive', true);
     return tabs;

http://git-wip-us.apache.org/repos/asf/ambari/blob/ea1d531b/ambari-web/test/mappers/configs/themes_mapper_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/mappers/configs/themes_mapper_test.js b/ambari-web/test/mappers/configs/themes_mapper_test.js
new file mode 100644
index 0000000..195a62f
--- /dev/null
+++ b/ambari-web/test/mappers/configs/themes_mapper_test.js
@@ -0,0 +1,225 @@
+/**
+ * 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');
+require('mappers/configs/themes_mapper');
+require('models/configs/tab');
+require('models/configs/section');
+require('models/configs/sub_section');
+require('models/configs/stack_config_property');
+
+describe('App.themeMapper', function () {
+
+  beforeEach(function () {
+    App.resetDsStoreTypeMap(App.Tab);
+    App.resetDsStoreTypeMap(App.Section);
+    App.resetDsStoreTypeMap(App.SubSection);
+    App.resetDsStoreTypeMap(App.StackConfigProperty);
+    sinon.stub(App.store, 'commit', Em.K);
+  });
+  afterEach(function () {
+    App.store.commit.restore();
+  });
+
+  describe("#map", function () {
+
+    var json = {
+      "Theme": {
+        "service_name": "HDFS",
+        "name": "default",
+        "description": "Default theme for HDFS service",
+        "configuration": {
+          "layouts": [
+            {
+              "name": "default",
+              "tabs": [
+                {
+                  "name": "settings",
+                  "display-name": "Settings",
+                  "layout": {
+                    "tab-columns": "2",
+                    "tab-rows": "1",
+                    "sections": [
+                      {
+                        "name": "Section-1",
+                        "display-name": "Section One",
+                        "row-index": "0",
+                        "column-index": "0",
+                        "row-span": "1",
+                        "column-span": "1",
+                        "section-columns": "2",
+                        "section-rows": "3",
+                        "subsections": [
+                          {
+                            "name": "subsection1",
+                            "display-name": "Storage",
+                            "border": "false",
+                            "row-index": "0",
+                            "column-index": "0",
+                            "column-span": "1",
+                            "row-span": "1"
+                          }
+                        ]
+                      },
+                      {
+                        "name": "Section-2",
+                        "display-name": "Section Two",
+                        "row-index": "0",
+                        "column-index": "1"
+                      }
+                    ]
+                  }
+                }
+              ]
+            }
+          ],
+          "placement": {
+            "configuration-layout": "default",
+            "configs": [
+              {
+                "config": "c1/p1",
+                "subsection-name": "subsection1"
+              },
+              {
+                "config": "c1/p2",
+                "subsection-name": "subsection1"
+              }
+            ]
+          },
+          "widgets": [
+            {
+              "config": "c1/p1",
+              "widget": {
+                "type": "slider",
+                "units": [
+                  {
+                    "unit-name": "MB"
+                  },
+                  {
+                    "unit-name": "GB"
+                  }
+                ]
+              }
+            },
+            {
+              "config": "c1/p2",
+              "widget": {
+                "type": "slider",
+                "units": [
+                  {
+                    "unit-name": "percent"
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      }
+    };
+
+    it('should map theme data', function () {
+
+      App.StackConfigProperty.createRecord({id: 'p1_c1'});
+      App.StackConfigProperty.createRecord({id: 'p2_c1'});
+
+      App.themesMapper.map(json);
+
+      expect(App.Tab.find().get('length')).to.equal(1);
+      expect(App.Section.find().get('length')).to.equal(2);
+      expect(App.SubSection.find().get('length')).to.equal(1);
+
+      //checking tab
+      expect(App.Tab.find('HDFS_settings').toJSON()).to.eql({
+        id: 'HDFS_settings',
+        name: 'settings',
+        display_name: 'Settings',
+        columns: "2",
+        rows: "1",
+        is_advanced: false,
+        service_name: 'HDFS'
+      });
+
+      //checking section
+      expect(App.Tab.find('HDFS_settings').get('sections').objectAt(0).toJSON()).to.eql({
+        "id": "Section-1",
+        "name": "Section-1",
+        "display_name": "Section One",
+        "row_index": "0",
+        "row_span": "1",
+        "column_index": "0",
+        "column_span": "1",
+        "section_columns": "2",
+        "section_rows": "3",
+        "tab_id": "HDFS_settings"
+      });
+
+      //checking subsection
+      expect(App.Tab.find('HDFS_settings').get('sections').objectAt(0).get('subSections').objectAt(0).toJSON()).to.eql({
+        "id": "subsection1",
+        "name": "subsection1",
+        "display_name": "Storage",
+        "border": "false",
+        "row_index": "0",
+        "row_span": "1",
+        "column_index": "0",
+        "column_span": "1",
+        "section_id": "Section-1"
+      });
+
+      //checking stack config object
+      var config = App.Tab.find('HDFS_settings').get('sections').objectAt(0).get('subSections').objectAt(0).get('configProperties').objectAt(0);
+      expect(config.get('id')).to.eql("p1_c1");
+      expect(config.get('subSection.id')).to.eql("subsection1");
+      expect(config.get('widget')).to.eql({
+        "type": "slider",
+        "units": [
+          {
+            "unit-name": "MB"
+          },
+          {
+            "unit-name": "GB"
+          }
+        ]
+      });
+    });
+  });
+
+  describe('generateAdvancedTabs', function () {
+    it('generates advanced tabs', function () {
+      App.themesMapper.generateAdvancedTabs(['HDFS']);
+      expect(App.Tab.find('HDFS_advanced').toJSON()).to.eql({
+        "id": "HDFS_advanced",
+        "name": "advanced",
+        "display_name": "Advanced",
+        "columns": 1,
+        "rows": 1,
+        "is_advanced": true,
+        "service_name": "HDFS"
+      });
+    });
+  });
+
+  describe('getConfigId', function () {
+    it('gets configs id from json', function () {
+      expect(App.themesMapper.getConfigId({config: "c1/p1"})).to.equal("p1_c1");
+    });
+    it('returns null as data is invalid', function () {
+      expect(App.themesMapper.getConfigId({configs: "c1/p1"})).to.equal(null);
+    });
+  });
+});