You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by ji...@apache.org on 2017/07/21 18:39:22 UTC

[20/52] [abbrv] hadoop git commit: YARN-6419. Support to launch new native-service from new YARN UI. Contributed by Akhil PB.

YARN-6419. Support to launch new native-service from new YARN UI. Contributed by Akhil PB.


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

Branch: refs/heads/yarn-native-services
Commit: 7f037ee30156256ce199ff2559f70961e9f28a70
Parents: e3dc7b0
Author: Sunil G <su...@apache.org>
Authored: Wed May 3 12:30:55 2017 +0530
Committer: Jian He <ji...@apache.org>
Committed: Fri Jul 21 11:38:13 2017 -0700

----------------------------------------------------------------------
 .../main/webapp/app/adapters/restabstract.js    |  50 ++++
 .../main/webapp/app/adapters/yarn-servicedef.js |  31 +++
 .../webapp/app/components/breadcrumb-bar.js     |   1 +
 .../webapp/app/components/deploy-service.js     | 167 +++++++++++
 .../app/components/fileconfig-viewer-dialog.js  |  36 +++
 .../main/webapp/app/components/info-tooltip.js  |  44 +++
 .../app/components/service-component-table.js   |  56 ++++
 .../app/components/service-config-table.js      |  89 ++++++
 .../app/components/service-fileconfig-table.js  | 112 ++++++++
 .../main/webapp/app/components/upload-config.js |  54 ++++
 .../app/controllers/yarn-deploy-service.js      |  69 +++++
 .../main/webapp/app/models/yarn-servicedef.js   | 278 +++++++++++++++++++
 .../src/main/webapp/app/router.js               |   1 +
 .../webapp/app/routes/yarn-deploy-service.js    |  27 ++
 .../src/main/webapp/app/services/hosts.js       |   4 +
 .../src/main/webapp/app/styles/app.css          | 164 +++++++++++
 .../main/webapp/app/templates/application.hbs   |   2 +
 .../app/templates/components/breadcrumb-bar.hbs |   4 +-
 .../app/templates/components/deploy-service.hbs | 157 +++++++++++
 .../components/fileconfig-viewer-dialog.hbs     |  53 ++++
 .../app/templates/components/info-tooltip.hbs   |  20 ++
 .../components/service-component-table.hbs      | 113 ++++++++
 .../components/service-config-table.hbs         | 130 +++++++++
 .../components/service-fileconfig-table.hbs     | 152 ++++++++++
 .../app/templates/components/upload-config.hbs  |  44 +++
 .../app/templates/yarn-deploy-service.hbs       |  33 +++
 .../main/webapp/app/templates/yarn-services.hbs |   4 +
 .../src/main/webapp/app/utils/info-seeder.js    |  26 ++
 .../src/main/webapp/config/configs.env          |   7 +
 .../src/main/webapp/config/default-config.js    |   7 +-
 .../components/deploy-service-test.js           |  43 +++
 .../components/fileconfig-viewer-dialog-test.js |  43 +++
 .../integration/components/info-tooltip-test.js |  43 +++
 .../components/service-component-table-test.js  |  43 +++
 .../components/service-config-table-test.js     |  43 +++
 .../components/service-fileconfig-table-test.js |  43 +++
 .../components/upload-config-test.js            |  43 +++
 .../tests/unit/adapters/yarn-servicedef-test.js |  30 ++
 .../controllers/yarn-deploy-service-test.js     |  30 ++
 .../tests/unit/models/yarn-servicedef-test.js   |  30 ++
 .../unit/routes/yarn-deploy-service-test.js     |  29 ++
 41 files changed, 2352 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/restabstract.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/restabstract.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/restabstract.js
new file mode 100644
index 0000000..df409d6
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/restabstract.js
@@ -0,0 +1,50 @@
+/**
+ * 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.
+ */
+
+import DS from 'ember-data';
+import Ember from 'ember';
+
+export default DS.RESTAdapter.extend({
+  address: null, //Must be set by inheriting classes
+  restNameSpace: null, //Must be set by inheriting classes
+  serverName: null, //Must be set by inheriting classes
+
+  headers: {
+    Accept: 'application/json'
+  },
+
+  host: Ember.computed("address", function() {
+    var address = this.get("address");
+    return this.get(`hosts.${address}`);
+  }),
+
+  namespace: Ember.computed("restNameSpace", function() {
+    var nameSpace = this.get("restNameSpace");
+    return this.get(`env.app.namespaces.${nameSpace}`);
+  }),
+
+  ajax(url, method, options) {
+    options = options || {};
+    options.crossDomain = true;
+    options.xhrFields = {
+      withCredentials: true
+    };
+    options.targetServer = this.get('serverName');
+    return this._super(url, method, options);
+  }
+});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-servicedef.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-servicedef.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-servicedef.js
new file mode 100644
index 0000000..c362f5e
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-servicedef.js
@@ -0,0 +1,31 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import RESTAbstractAdapter from './restabstract';
+
+export default RESTAbstractAdapter.extend({
+  address: "dashWebAddress",
+  restNameSpace: "dashService",
+  serverName: "DASH",
+
+  deployService(request) {
+    var url = this.buildURL();
+    return this.ajax(url, "POST", {data: request});
+  }
+});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/breadcrumb-bar.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/breadcrumb-bar.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/breadcrumb-bar.js
index 44edb8e..b8d974a 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/breadcrumb-bar.js
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/breadcrumb-bar.js
@@ -21,6 +21,7 @@ import Ember from 'ember';
 export default Ember.Component.extend({
 
   breadcrumbs: null,
+  hideRefresh: false,
 
   actions:{
     refresh: function () {

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/deploy-service.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/deploy-service.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/deploy-service.js
new file mode 100644
index 0000000..90e10e5
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/deploy-service.js
@@ -0,0 +1,167 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  viewType: 'standard',
+  savedStandardTemplates: null,
+  savedJsonTemplates: null,
+  savedTemplateName: '',
+  serviceDef: null,
+  customServiceDef: '',
+  serviceResp: null,
+  isLoading: false,
+
+  actions: {
+    showSaveTemplateModal() {
+      this.$('#saveListModal').modal('show');
+    },
+
+    deployService() {
+      this.set('serviceResp', null);
+      if (this.get('isStandardViewType')) {
+        this.sendAction("deployServiceDef", this.get('serviceDef'));
+      } else {
+        try {
+          var parsed = JSON.parse(this.get('customServiceDef'));
+          this.sendAction("deployServiceJson", parsed);
+        } catch (err) {
+          this.set('serviceResp', {type: 'error', message: 'Invalid JSON: ' + err.message});
+          throw err;
+        }
+      }
+    },
+
+    updateViewType(type) {
+      this.set('viewType', type);
+    },
+
+    addToSavedList() {
+      this.unselectAllSavedList();
+      if (this.get('isStandardViewType')) {
+        this.get('savedStandardTemplates').addObject({
+          name: this.get('savedTemplateName'),
+          defId: this.get('serviceDef.id'),
+          active: true
+        });
+        this.set('serviceDef.isCached', true);
+      } else {
+        this.get('savedJsonTemplates').addObject({
+          name: this.get('savedTemplateName'),
+          custom: this.get('customServiceDef'),
+          active: true
+        });
+      }
+      this.$('#saveListModal').modal('hide');
+      this.set('savedTemplateName', '');
+    },
+
+    updateServiceDef(def) {
+      this.selectActiveListItem(def);
+      if (this.get('isStandardViewType')) {
+        this.set('serviceDef', this.getStore().peekRecord('yarn-servicedef', def.defId));
+      } else {
+        this.set('customServiceDef', def.custom);
+      }
+    },
+
+    clearConfigs() {
+      this.unselectAllSavedList();
+      this.set('serviceResp', null);
+      if (this.get('isStandardViewType')) {
+        var oldDef = this.get('serviceDef');
+        var def = oldDef.createNewServiceDef();
+        this.set('serviceDef', def);
+        if (!oldDef.get('isCached')) {
+          oldDef.deleteRecord();
+        }
+      } else {
+        this.set('customServiceDef', '');
+      }
+    },
+
+    removeFromSavedList(list) {
+      if (list.active) {
+        this.send('clearConfigs');
+      }
+      if (this.get('isStandardViewType')) {
+        this.get('savedStandardTemplates').removeObject(list);
+      } else {
+        this.get('savedJsonTemplates').removeObject(list);
+      }
+    },
+
+    clearServiceResponse() {
+      this.set('serviceResp', null);
+    }
+  },
+
+  didInsertElement() {
+    var self = this;
+    self.$().find('.modal').on('shown.bs.modal', function() {
+      self.$().find('.modal.in').find('input.form-control:first').focus();
+    });
+  },
+
+  selectActiveListItem(item) {
+    this.unselectAllSavedList();
+    Ember.set(item, 'active', true);
+  },
+
+  unselectAllSavedList() {
+    this.get('getSavedList').forEach(function(item) {
+      Ember.set(item, 'active', false);
+    });
+  },
+
+  getSavedList: Ember.computed('viewType', function() {
+    if (this.get('isStandardViewType')) {
+      return this.get('savedStandardTemplates');
+    } else {
+      return this.get('savedJsonTemplates');
+    }
+  }),
+
+  getStore: function() {
+    return this.get('serviceDef.store');
+  },
+
+  isStandardViewType: Ember.computed.equal('viewType', 'standard'),
+
+  isCustomViewType: Ember.computed.equal('viewType', 'custom'),
+
+  isValidTemplateName: Ember.computed.notEmpty('savedTemplateName'),
+
+  isValidServiceDef: Ember.computed('serviceDef.name', 'serviceDef.queue', 'serviceDef.serviceComponents.[]', function () {
+    return this.get('serviceDef').isValidServiceDef();
+  }),
+
+  isValidCustomServiceDef: Ember.computed.notEmpty('customServiceDef'),
+
+  enableSaveOrDeployBtn: Ember.computed('isValidServiceDef', 'isValidCustomServiceDef', 'viewType', 'isLoading', function() {
+    if (this.get('isLoading')) {
+      return false;
+    }
+    if (this.get('isStandardViewType')) {
+      return this.get('isValidServiceDef');
+    } else {
+      return this.get('isValidCustomServiceDef');
+    }
+  })
+});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/fileconfig-viewer-dialog.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/fileconfig-viewer-dialog.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/fileconfig-viewer-dialog.js
new file mode 100644
index 0000000..d4912768
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/fileconfig-viewer-dialog.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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  dialogId: "fileconfig_viewer_dialog",
+  title: "File Configuration Properties",
+  props: null,
+  customProps: Ember.computed('props', function() {
+    var custom = [];
+    var props = this.get('props');
+    for (var pro in props) {
+      custom.push({
+        name: pro,
+        value: props[pro]
+      });
+    }
+    return custom;
+  })
+});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/info-tooltip.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/info-tooltip.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/info-tooltip.js
new file mode 100644
index 0000000..605b611
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/info-tooltip.js
@@ -0,0 +1,44 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import InfoSeeder from 'yarn-ui/utils/info-seeder';
+
+export default Ember.Component.extend({
+  classNames: ['tooltip', 'info-tooltip'],
+  elementId: 'info_tooltip_wrapper',
+
+  didInsertElement() {
+    var $tooltip = Ember.$('#info_tooltip_wrapper');
+    Ember.$('body').on('mouseenter', '.info-icon', function() {
+      var $elem = Ember.$(this);
+      var info = InfoSeeder[$elem.data('info')];
+      var offset = $elem.offset();
+      $tooltip.show();
+      $tooltip.find("#tooltip_content").text(info);
+      $tooltip.offset({top: offset.top + 20, left: offset.left - 10});
+    }).on('mouseleave', '.info-icon', function() {
+      $tooltip.find("#tooltip_content").text('');
+      $tooltip.hide();
+    });
+  },
+
+  WillDestroyElement() {
+    Ember.$('body').off('hover');
+  }
+});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/service-component-table.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/service-component-table.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/service-component-table.js
new file mode 100644
index 0000000..5a9ae30
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/service-component-table.js
@@ -0,0 +1,56 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  serviceDef: null,
+  currentComponent: null,
+  duplicateNameError: false,
+
+  actions: {
+    showAddComponentModal() {
+      var newComp = this.get('serviceDef').createNewServiceComponent();
+      this.set('currentComponent', newComp);
+      this.set('duplicateNameError', false);
+      this.$('#addComponentModal').modal('show');
+    },
+
+    addNewComponent() {
+      this.set('duplicateNameError', false);
+      if (this.isCurrentNameDuplicate()) {
+        this.set('duplicateNameError', true);
+        return;
+      }
+      this.get('serviceDef.serviceComponents').addObject(this.get('currentComponent'));
+      this.$('#addComponentModal').modal('hide');
+    },
+
+    removeComponent(component) {
+      this.get('serviceDef.serviceComponents').removeObject(component);
+    }
+  },
+
+  isCurrentNameDuplicate() {
+    var currName = this.get('currentComponent.name');
+    var item = this.get('serviceDef.serviceComponents').findBy('name', currName);
+    return !Ember.isNone(item);
+  },
+
+  isValidCurrentComponent: Ember.computed.and('currentComponent', 'currentComponent.name', 'currentComponent.cpus', 'currentComponent.memory', 'currentComponent.numOfContainers', 'currentComponent.artifactId', 'currentComponent.launchCommand')
+});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/service-config-table.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/service-config-table.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/service-config-table.js
new file mode 100644
index 0000000..b0a78dd
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/service-config-table.js
@@ -0,0 +1,89 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  serviceDef: null,
+  currentConfig: null,
+  serviceConfigJson: '',
+
+  actions: {
+    showNewConfigurationModal() {
+      var newConfig = this.get('serviceDef').createNewServiceConfig();
+      this.set('currentConfig', newConfig);
+      this.$('#addConfigurationModal').modal('show');
+      if (this.get('isNonEmptyComponents') && this.get('currentConfig.componentName') === '') {
+        this.set('currentConfig.componentName', this.get('componentNames.firstObject'));
+      }
+    },
+
+    removeConfiguration(config) {
+      this.get('serviceDef.serviceConfigs').removeObject(config);
+    },
+
+    configTypeChanged(type) {
+      this.set('currentConfig.type', type);
+      if (type === 'quicklink') {
+        this.set('currentConfig.scope', 'service');
+        this.set('currentConfig.componentName', '');
+      }
+    },
+
+    addNewConfiguration() {
+      this.get('serviceDef.serviceConfigs').addObject(this.get('currentConfig'));
+      this.$('#addConfigurationModal').modal('hide');
+    },
+
+    showServiceConfigUploadModal() {
+      this.set('serviceConfigJson', '');
+      this.$("#service_config_upload_modal").modal('show');
+    },
+
+    uploadServiceConfig(json) {
+      this.get('serviceDef').convertJsonServiceConfigs(json);
+      this.$("#service_config_upload_modal").modal('hide');
+    },
+
+    configScopeChanged(scope) {
+      this.set('currentConfig.scope', scope);
+    },
+
+    scopeComponentChanged(name) {
+      this.set('currentConfig.componentName', name);
+    }
+  },
+
+  isNonEmptyComponents: Ember.computed('serviceDef.serviceComponents.length', function() {
+    return this.get('serviceDef.serviceComponents.length') > 0;
+  }),
+
+  isNotQuicklink: Ember.computed('currentConfig.type', function() {
+    return this.get('currentConfig.type') !== "quicklink";
+  }),
+
+  componentNames: Ember.computed('serviceDef.serviceComponents.[]', function() {
+    var names = [];
+    this.get('serviceDef.serviceComponents').forEach(function(cmp) {
+      names.push(cmp.get('name'));
+    });
+    return names;
+  }),
+
+  isValidCurrentConfig: Ember.computed.and('currentConfig', 'currentConfig.name', 'currentConfig.value')
+});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/service-fileconfig-table.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/service-fileconfig-table.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/service-fileconfig-table.js
new file mode 100644
index 0000000..7c06152
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/service-fileconfig-table.js
@@ -0,0 +1,112 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  serviceDef: null,
+  currentFileConfig: null,
+  fileConfigJson: '',
+  fileConfigProps: '',
+  propertyViewer: null,
+  parseError: '',
+
+  actions: {
+    showNewConfigFileModal() {
+      var newFile = this.get('serviceDef').createNewFileConfig();
+      this.set('currentFileConfig', newFile);
+      this.set('fileConfigProps', '');
+      this.set('parseError', '');
+      this.$('#addFileConfigModal').modal('show');
+      if (this.get('isNonEmptyComponents') && this.get('currentFileConfig.componentName') === '') {
+        this.set('currentFileConfig.componentName', this.get('componentNames.firstObject'));
+      }
+    },
+
+    removeFileConfiguration(file) {
+      this.get('serviceDef.fileConfigs').removeObject(file);
+    },
+
+    addNewFileConfig() {
+      this.set('parseError', '');
+      var props = this.get('fileConfigProps');
+      if (props) {
+        try {
+          var parsed = JSON.parse(props);
+          this.set('currentFileConfig.props', parsed);
+        } catch (err) {
+          this.set('parseError', `Invalid JSON: ${err.message}`);
+          throw err;
+        }
+      }
+      this.get('serviceDef.fileConfigs').addObject(this.get('currentFileConfig'));
+      this.$('#addFileConfigModal').modal('hide');
+    },
+
+    showFileConfigUploadModal() {
+      this.set('fileConfigJson', '');
+      this.$("#service_file_config_upload_modal").modal('show');
+    },
+
+    uploadFileConfig(json) {
+      this.get('serviceDef').convertJsonFileConfigs(json);
+      this.$("#service_file_config_upload_modal").modal('hide');
+    },
+
+    configScopeChanged(scope) {
+      this.set('currentFileConfig.scope', scope);
+    },
+
+    scopeComponentChanged(name) {
+      this.set('currentFileConfig.componentName', name);
+    },
+
+    configTypeChanged(type) {
+      this.set('currentFileConfig.type', type);
+      if (type === "TEMPLATE") {
+        this.set('currentFileConfig.props', null);
+        this.set('fileConfigProps', '');
+      }
+    },
+
+    showFileConfigPropertyViewer(props) {
+      this.set('propertyViewer', props);
+      this.$("#file_config_properties_viewer").modal('show');
+    }
+  },
+
+  isNonEmptyComponents: Ember.computed('serviceDef.serviceComponents.length', function() {
+    return this.get('serviceDef.serviceComponents.length') > 0;
+  }),
+
+  componentNames: Ember.computed('serviceDef.serviceComponents.[]', function() {
+    var names = [];
+    this.get('serviceDef.serviceComponents').forEach(function(cmp) {
+      names.push(cmp.get('name'));
+    });
+    return names;
+  }),
+
+  isValidCurrentFileConfig: Ember.computed('currentFileConfig', 'currentFileConfig.srcFile', 'currentFileConfig.destFile', 'fileConfigProps', function() {
+    return this.get('currentFileConfig') && this.get('currentFileConfig.destFile') && (this.get('currentFileConfig.srcFile') || this.get('fileConfigProps'));
+  }),
+
+  isConfigTypeHadoopXml: Ember.computed('currentFileConfig.type', function() {
+    return this.get('currentFileConfig.type') === 'HADOOP_XML';
+  })
+});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/upload-config.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/upload-config.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/upload-config.js
new file mode 100644
index 0000000..2f9dc9c
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/upload-config.js
@@ -0,0 +1,54 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  dialogId: "config_upload_modal",
+  title: "Upload Configuration",
+  configJson: '',
+  parseErrorMsg: '',
+
+  actions: {
+    uploadConfig() {
+      var json = this.get('configJson');
+      try {
+        JSON.parse(json);
+        this.upateParseResults("");
+      } catch (ex) {
+        this.upateParseResults("Invalid JSON: " + ex.message);
+        throw ex;
+      }
+      if (!this.get('parseErrorMsg')) {
+        this.sendAction("uploadConfig", json);
+      }
+    }
+  },
+
+  didInsertElement() {
+    this.$('#' + this.get('dialogId')).on('shown.bs.modal', function() {
+      this.upateParseResults("");
+    }.bind(this));
+  },
+
+  isValidConfigJson: Ember.computed.notEmpty('configJson'),
+
+  upateParseResults(message) {
+    this.set('parseErrorMsg', message);
+  }
+});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-deploy-service.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-deploy-service.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-deploy-service.js
new file mode 100644
index 0000000..25d575f
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/controllers/yarn-deploy-service.js
@@ -0,0 +1,69 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+  breadcrumbs: [{
+    text: "Home",
+    routeName: 'application'
+  }, {
+    text: "Services",
+    routeName: 'yarn-services',
+  }, {
+    text: "New Service",
+    routeName: 'yarn-deploy-service',
+  }],
+
+  savedStandardTemplates: [],
+  savedJsonTemplates: [],
+  serviceResponse: null,
+  isLoading: false,
+
+  actions: {
+    deployServiceDef(serviceDef) {
+      var defjson = serviceDef.getServiceJSON();
+      this.deployServiceApp(defjson);
+    },
+
+    deployServiceJson(json) {
+      this.deployServiceApp(json);
+    }
+  },
+
+  gotoServices() {
+    Ember.run.later(this, function() {
+      this.set('serviceResponse', null);
+      this.transitionToRoute('yarn-services');
+    }, 1000);
+  },
+
+  deployServiceApp(requestJson) {
+    var self = this;
+    var adapter = this.store.adapterFor('yarn-servicedef');
+    this.set('isLoading', true);
+    adapter.deployService(requestJson).then(function() {
+      self.set('serviceResponse', {message: 'Service has been accepted successfully. Redirecting to services in a second.', type: 'success'});
+      self.gotoServices();
+    }, function(errmsg) {
+      self.set('serviceResponse', {message: errmsg, type: 'error'});
+    }).finally(function() {
+      self.set('isLoading', false);
+    });
+  }
+});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-servicedef.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-servicedef.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-servicedef.js
new file mode 100644
index 0000000..0439fb4
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-servicedef.js
@@ -0,0 +1,278 @@
+/**
+ * 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.
+ */
+
+import DS from 'ember-data';
+import Ember from 'ember';
+
+export default DS.Model.extend({
+  name: DS.attr('string', {defaultValue: ''}),
+  queue: DS.attr('string', {defaultValue: ''}),
+  lifetime: DS.attr('string', {defaultValue: ''}),
+  isCached: DS.attr('boolean', {defaultValue: false}),
+
+  serviceComponents: DS.attr({defaultValue: function() {
+    return Ember.A();
+  }}),
+
+  serviceConfigs: DS.attr({defaultValue: function() {
+    return Ember.A();
+  }}),
+
+  fileConfigs: DS.attr({defaultValue: function() {
+    return Ember.A();
+  }}),
+
+  quicklinks: DS.attr({defaultValue: function() {
+    return {};
+  }}),
+
+  clear() {
+    this.set('name', '');
+    this.set('queue', '');
+    this.set('lifetime', '');
+    this.get('serviceComponents').clear();
+    this.get('serviceConfigs').clear();
+    this.get('fileConfigs').clear();
+    this.set('quicklinks', {});
+  },
+
+  isValidServiceDef() {
+    return this.get('name') !== '' && this.get('queue') !== '' &&  this.get('serviceComponents.length') > 0;
+  },
+
+  createNewServiceComponent() {
+    return Ember.Object.create({
+      name: '',
+      numOfContainers: '',
+      cpus: '',
+      memory: '',
+      artifactId: '',
+      artifactType: 'DOCKER',
+      launchCommand: '',
+      dependencies: [],
+      uniqueComponentSupport: false,
+      configuration: null
+    });
+  },
+
+  createNewServiceConfig(name, value) {
+    var Config = Ember.Object.extend({
+      name: name || '',
+      value: value || '',
+      type: 'property', // property OR env OR quicklink
+      scope: 'service', // service OR component
+      componentName: '',
+      capitalizedType: Ember.computed('type', function() {
+        return Ember.String.capitalize(this.get('type'));
+      }),
+      formattedScope: Ember.computed('scope', 'componentName', function() {
+        if (this.get('scope') !== 'service') {
+          return this.get('componentName') + ' [Component]';
+        }
+        return Ember.String.capitalize(this.get('scope'));
+      })
+    });
+    return Config.create();
+  },
+
+  createNewFileConfig(src, dest) {
+    var FileConfig = Ember.Object.extend({
+      type: 'TEMPLATE', // HADOOP_XML OR TEMPLATE
+      srcFile: src || '',
+      destFile: dest || '',
+      scope: 'service', // service OR component
+      componentName: '',
+      props: null,
+      formattedScope: Ember.computed('scope', 'componentName', function() {
+        if (this.get('scope') !== 'service') {
+          return this.get('componentName') + ' [Component]';
+        }
+        return Ember.String.capitalize(this.get('scope'));
+      })
+    });
+    return FileConfig.create();
+  },
+
+  getServiceJSON() {
+    return this.serializeServiceDef();
+  },
+
+  serializeServiceDef() {
+    var json = {
+      name: "",
+      queue: "",
+      lifetime: "-1",
+      components: [],
+      configuration: {
+        properties: {},
+        env: {},
+        files: []
+      },
+      quicklinks: {}
+    };
+
+    var components = this.get('serviceComponents');
+    var configs = this.get('serviceConfigs');
+    var fileConfigs = this.get('fileConfigs');
+
+    json['name'] = this.get('name');
+    json['queue'] = this.get('queue');
+
+    if (this.get('lifetime')) {
+      json['lifetime'] = this.get('lifetime');
+    }
+
+    components.forEach(function(component) {
+      json.components.push(this.serializeComponent(component));
+    }.bind(this));
+
+    configs.forEach(function(config) {
+      let conf = this.serializeConfiguration(config);
+      if (conf.scope === "service") {
+        if (conf.type === "property") {
+          json.configuration.properties[conf.name] = conf.value;
+        } else if (conf.type === "env") {
+          json.configuration.env[conf.name] = conf.value;
+        } else if (conf.type === "quicklink") {
+          json.quicklinks[conf.name] = conf.value;
+        }
+      } else if (conf.scope === "component") {
+        let requiredCmp = json.components.findBy('name', conf.componentName);
+        if (requiredCmp) {
+          requiredCmp.configuration = requiredCmp.configuration || {};
+          requiredCmp.configuration.properties = requiredCmp.configuration.properties || {};
+          requiredCmp.configuration.env = requiredCmp.configuration.env || {};
+          if (conf.type === "property") {
+            requiredCmp.configuration.properties[conf.name] = conf.value;
+          } else if (conf.type === "env") {
+            requiredCmp.configuration.env[conf.name] = conf.value;
+          }
+        }
+      }
+    }.bind(this));
+
+    fileConfigs.forEach(function(file) {
+      let scope = file.get('scope');
+      if (scope === "service") {
+        json.configuration.files.push(this.serializeFileConfig(file));
+      } else if (scope === "component") {
+        let requiredCmp = json.components.findBy('name', file.get('componentName'));
+        if (requiredCmp) {
+          requiredCmp.configuration = requiredCmp.configuration || {};
+          requiredCmp.configuration.files = requiredCmp.configuration.files || [];
+          requiredCmp.configuration.files.push(this.serializeFileConfig(file));
+        }
+      }
+    }.bind(this));
+
+    return json;
+  },
+
+  serializeComponent(record) {
+    var json = {};
+    json['name'] = record.get('name');
+    json['number_of_containers'] = record.get('numOfContainers');
+    json['launch_command'] = record.get('launchCommand');
+    json['dependencies'] = [];
+    json['artifact'] = {
+      id: record.get('artifactId'),
+      type: record.get('artifactType')
+    };
+    json['resource'] = {
+      cpus: record.get('cpus'),
+      memory: record.get('memory')
+    };
+    if (record.get('uniqueComponentSupport')) {
+      json['unique_component_support'] = "true";
+    }
+    if (record.get('configuration')) {
+      json['configuration'] = record.get('configuration');
+    }
+    return json;
+  },
+
+  serializeConfiguration(config) {
+    var json = {};
+    json["type"] = config.get('type');
+    json["scope"] = config.get('scope');
+    json["componentName"] = config.get('componentName');
+    json["name"] = config.get('name');
+    json["value"] = config.get('value');
+    return json;
+  },
+
+  serializeFileConfig(file) {
+    var json = {};
+    json["type"] = file.get('type');
+    json["dest_file"] = file.get('destFile');
+    json["src_file"] = file.get('srcFile');
+    if (file.get('type') === "HADOOP_XML" && file.get('props')) {
+      json["props"] = file.get('props');
+    }
+    return json;
+  },
+
+  createNewServiceDef() {
+    return this.get('store').createRecord('yarn-servicedef', {
+      id: 'yarn_servicedef_' + Date.now()
+    });
+  },
+
+  convertJsonServiceConfigs(json) {
+    var parsedJson = JSON.parse(json);
+    if (parsedJson.properties) {
+      for (let prop in parsedJson.properties) {
+        if (parsedJson.properties.hasOwnProperty(prop)) {
+          let newPropObj = this.createNewServiceConfig(prop, parsedJson.properties[prop]);
+          this.get('serviceConfigs').addObject(newPropObj);
+        }
+      }
+    }
+    if (parsedJson.env) {
+      for (let envprop in parsedJson.env) {
+        if (parsedJson.env.hasOwnProperty(envprop)) {
+          let newEnvObj = this.createNewServiceConfig(envprop, parsedJson.env[envprop]);
+          newEnvObj.set('type', 'env');
+          this.get('serviceConfigs').addObject(newEnvObj);
+        }
+      }
+    }
+  },
+
+  convertJsonFileConfigs(json) {
+    var parsedJson = JSON.parse(json);
+    if (parsedJson.files) {
+      parsedJson.files.forEach(function(file) {
+        let newFileObj = this.createNewFileConfig(file.src_file, file.dest_file);
+        this.get('fileConfigs').addObject(newFileObj);
+      }.bind(this));
+    }
+  },
+
+  cloneServiceDef() {
+    var clone = this.createNewServiceDef();
+    clone.set('name', this.get('name'));
+    clone.set('queue', this.get('queue'));
+    clone.set('lifetime', this.get('lifetime'));
+    clone.get('serviceComponents', this.get('serviceComponents'));
+    clone.get('serviceConfigs', this.get('serviceConfigs'));
+    clone.get('fileConfigs', this.get('fileConfigs'));
+    clone.set('quicklinks', this.get('quicklinks'));
+    return clone;
+  }
+});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/router.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/router.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/router.js
index 9013142..9b3424b 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/router.js
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/router.js
@@ -48,6 +48,7 @@ Router.map(function() {
   this.route('yarn-container-log', { path:
       '/yarn-container-log/:node_id/:node_addr/:container_id/:filename' });
 
+  this.route('yarn-deploy-service');
   this.route('cluster-overview');
   this.route('yarn-app', function() {
     this.route('info', {path: '/:app_id/info'});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-deploy-service.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-deploy-service.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-deploy-service.js
new file mode 100644
index 0000000..05ef600
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-deploy-service.js
@@ -0,0 +1,27 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Route.extend({
+  model() {
+    return this.store.createRecord('yarn-servicedef', {
+      id: 'yarn_servicedef_' + Date.now()
+    });
+  }
+});

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/services/hosts.js
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/services/hosts.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/services/hosts.js
index 807844e..9359530 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/services/hosts.js
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/services/hosts.js
@@ -75,4 +75,8 @@ export default Ember.Service.extend({
   rmWebAddress: Ember.computed(function () {
     return this.normalizeURL(this.get("env.app.hosts.rmWebAddress"));
   }),
+
+  dashWebAddress: Ember.computed(function () {
+    return this.normalizeURL(this.get("env.app.hosts.dashWebAddress"));
+  })
 });

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/styles/app.css
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/styles/app.css b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/styles/app.css
index 8b8ea56..d246f2d 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/styles/app.css
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/styles/app.css
@@ -397,3 +397,167 @@ div.attempt-info-panel table > tbody > tr > td:last-of-type {
   width: 14px;
   display: inline-block;
 }
+
+.deploy-service textarea {
+  border-radius: 5px !important;
+  resize: none;
+  word-wrap: break-word;
+}
+
+.deploy-service .loading-state {
+  opacity: 0.5;
+}
+
+.deploy-service .loading-state img {
+  width: 80px;
+  height: 80px;
+  margin: 40px auto;
+  left: 50% !important;
+  position: absolute;
+  z-index: 9999;
+}
+
+.align-center {
+  text-align: center !important;
+}
+
+.bold-text {
+  font-weight: bold !important;
+}
+
+.deploy-service .saved-list {
+  min-height: 600px;
+}
+
+.deploy-service .glyphicon {
+  cursor: pointer;
+}
+
+.deploy-service .remove-icon:hover {
+  color: #d9534f;
+}
+
+.deploy-service .savedlist-column {
+  padding-top: 10px;
+}
+
+.deploy-service .definition-column {
+  padding-top: 10px;
+  border-left: 1px solid #ddd;
+}
+
+.deploy-service .content-area {
+  padding: 15px 0px;
+  border-top: 1px solid #ddd;
+}
+
+.deploy-service .custom-json-area {
+  padding: 10px 0;
+  margin-top: -26px;
+}
+
+.deploy-service-modal .modal-dialog {
+  width: 400px;
+}
+
+.deploy-service-modal .form-group {
+  margin-bottom: 5px;
+}
+
+.deploy-service .action-btns {
+  text-align: right;
+  padding-bottom: 15px;
+  padding-right: 0;
+}
+
+table.table-custom-action > thead > tr > th:last-of-type, table.table-custom-action > tbody > tr > td:last-of-type {
+  width: 50px !important;
+}
+
+.deploy-service .toggle-btn.active {
+  color: #fff;
+  background-color: #337ab7;
+  border-color: #337ab7;
+  text-shadow: none;
+}
+
+.deploy-service .service-resp {
+  word-wrap: break-word;
+}
+
+table.table-custom-bordered {
+  border: 1px solid #ddd !important;
+  border-radius: 3px !important;
+}
+
+table.table-custom-bordered > thead > tr > th, table.table-custom-bordered > tbody > tr > td {
+  border-bottom: 1px solid #ddd !important;
+  border-right: 1px solid #ddd !important;
+}
+
+table.table-custom-striped > thead > tr, .table-custom-striped > tbody > tr:nth-of-type(even) {
+  background-color: #f9f9f9 !important;
+}
+
+.deploy-service label.required:after, .deploy-service-modal label.required:after {
+  content: '*';
+  color: #d9534f;
+}
+
+.deploy-service .form-group.shrink-height {
+  margin-bottom: -8px;
+}
+
+table.fix-table-overflow {
+  table-layout: fixed;
+}
+
+table.fix-table-overflow > tbody > tr > td:last-of-type {
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+div.tooltip.info-tooltip {
+  font: 14px sans-serif !important;
+  background: lightsteelblue;
+  word-wrap: break-word;
+  position: absolute;
+  text-align: justify;
+  border-radius: 3px;
+  z-index: 9999;
+  padding: 10px;
+  display: none;
+  min-width: 200px;
+  max-width: 500px;
+  opacity: 1;
+  top: 10px;
+  left: 0;
+}
+
+div.tooltip.info-tooltip > span.top-arrow {
+  color: lightsteelblue;
+  position: absolute;
+  top: -10px;
+  left: 10px;
+}
+
+span.info-icon {
+  color: #337ab7 !important;
+}
+
+div.service-action-mask {
+  position: absolute;
+  opacity: 0.5;
+  z-index: 9999;
+  width: 100%;
+  height: 100%;
+}
+
+div.service-action-mask img {
+  position: absolute;
+  width: 80px;
+  height: 80px;
+  margin: 40px auto;
+  left: 45% !important;
+  z-index: 9999;
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/application.hbs
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/application.hbs b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/application.hbs
index e988e0c..a08ff72 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/application.hbs
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/application.hbs
@@ -92,3 +92,5 @@
     </div>
   </div>
 </div>
+
+{{info-tooltip}}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/breadcrumb-bar.hbs
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/breadcrumb-bar.hbs b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/breadcrumb-bar.hbs
index 24acbd9..54229cc 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/breadcrumb-bar.hbs
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/breadcrumb-bar.hbs
@@ -18,5 +18,7 @@
 
 <div class="col-md-12 container-fluid breadcrumb-bar">
   {{em-breadcrumbs items=breadcrumbs}}
-  <button type="button" class="btn btn-sm btn-primary refresh" {{action "refresh"}}>Refresh</button>
+  {{#unless hideRefresh}}
+    <button type="button" class="btn btn-sm btn-primary refresh" {{action "refresh"}}>Refresh</button>
+  {{/unless}}
 </div>

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/deploy-service.hbs
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/deploy-service.hbs b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/deploy-service.hbs
new file mode 100644
index 0000000..a098ec3
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/deploy-service.hbs
@@ -0,0 +1,157 @@
+{{!
+ * 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="container-fluid deploy-service">
+  {{#if serviceResp}}
+    <div class="row">
+      <div class="col-md-12">
+        <div class="panel panel-default service-resp">
+          <div class="panel-body {{if (eq serviceResp.type 'error') 'bg-danger' 'bg-success'}}">
+            <span class="glyphicon glyphicon-remove pull-right remove-icon" {{action "clearServiceResponse"}}></span>
+            <strong class="{{if (eq serviceResp.type 'error') 'text-danger' 'text-success'}}">{{serviceResp.message}}</strong>
+          </div>
+        </div>
+      </div>
+    </div>
+  {{/if}}
+  <div class="panel panel-default {{if isLoading 'loading-state'}}">
+    {{#if isLoading}}
+      <img src="assets/images/spinner.gif" alt="Loading...">
+    {{/if}}
+    <div class="row">
+      <div class="col-md-12">
+        <div class="col-md-2 savedlist-column">
+          <label>Saved Templates</label>
+          <div class="panel panel-default saved-list">
+            <ul class="list-group">
+              {{#each getSavedList as |list|}}
+                <a href="#" class="list-group-item {{if list.active 'active'}}" {{action "updateServiceDef" list}}>
+                  {{list.name}}
+                  <span class="glyphicon glyphicon-remove pull-right remove-icon" {{action "removeFromSavedList" list}}></span>
+                </a>
+              {{else}}
+                <li class="list-group-item align-center">No saved templates</li>
+              {{/each}}
+            </ul>
+          </div>
+        </div>
+
+        <div class="col-md-10 definition-column">
+          <label>Service Definition</label>
+          <div class="btn-group pull-right" data-toggle="buttons">
+            <label class="btn btn-default btn-sm toggle-btn active" {{action "updateViewType" "standard"}}>
+              <input type="radio" name="custom" checked><b>Standard</b>
+            </label>
+            <label class="btn btn-default btn-sm toggle-btn" {{action "updateViewType" "custom"}}>
+              <input type="radio" name="custom"><b>Custom</b>
+            </label>
+          </div>
+
+          <div class="col-md-12 content-area">
+            {{#if isStandardViewType}}
+
+              <div class="row">
+                <div class="col-md-4">
+                  <div class="form-group shrink-height">
+                    <label class="required">Service Name</label>
+                    <span class="glyphicon glyphicon-info-sign info-icon" data-info="serviceName"></span>
+                    {{input type="text" class="form-control" placeholder="Service Name" value=serviceDef.name}}
+                  </div>
+                  <br>
+                </div>
+              </div>
+
+              <div class="row">
+                <div class="col-md-4">
+                  <div class="form-group shrink-height">
+                    <label class="required">Queue Name</label>
+                    <span class="glyphicon glyphicon-info-sign info-icon" data-info="queueName"></span>
+                    {{input type="text" class="form-control" placeholder="Queue Name" value=serviceDef.queue}}
+                  </div>
+                  <br>
+                </div>
+              </div>
+
+              <div class="row">
+                <div class="col-md-4">
+                  <div class="form-group">
+                    <label>Service Lifetime</label>
+                    <span class="glyphicon glyphicon-info-sign info-icon" data-info="lifetime"></span>
+                    {{input type="number" min="0" class="form-control" placeholder="Service Lifetime (Seconds)" value=serviceDef.lifetime}}
+                  </div>
+                  <br>
+                </div>
+              </div>
+
+              <div class="row">
+                {{service-component-table serviceDef=serviceDef applicationCtrl=applicationCtrl}}
+              </div>
+
+              <div class="row">
+                {{service-config-table serviceDef=serviceDef}}
+              </div>
+
+              <div class="row">
+                {{service-fileconfig-table serviceDef=serviceDef}}
+              </div>
+            {{/if}}
+
+            {{#if isCustomViewType}}
+            <div class="form-group custom-json-area">
+              {{textarea class="form-control" rows="29" cols="120" value=customServiceDef placeholder="Service JSON configuration here..."}}
+            </div>
+            {{/if}}
+          </div>
+
+          <div class="col-md-12 action-btns">
+            <button class="btn btn-default btn-sm" {{action "clearConfigs"}} disabled={{if isLoading "disabled"}}>
+              Reset
+            </button>
+            <button class="btn btn-primary btn-sm" disabled={{unless enableSaveOrDeployBtn "disabled"}} {{action "showSaveTemplateModal"}}>
+              Save
+            </button>
+            <button class="btn btn-success btn-sm" disabled={{unless enableSaveOrDeployBtn "disabled"}} {{action "deployService"}}>
+              Deploy
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<div class="modal fade deploy-service-modal" tabindex="-1" role="dialog" id="saveListModal">
+  <div class="modal-dialog" role="document">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+        <h4 class="modal-title">Save Template As</h4>
+      </div>
+      <div class="modal-body">
+        <div class="form-group">
+          <label>Template Name</label>
+          {{input type="text" class="form-control" id="templateNameInput" value=savedTemplateName}}
+        </div>
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+        <button type="button" class="btn btn-primary" {{action "addToSavedList"}} disabled={{unless isValidTemplateName "disabled"}}>Add</button>
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/fileconfig-viewer-dialog.hbs
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/fileconfig-viewer-dialog.hbs b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/fileconfig-viewer-dialog.hbs
new file mode 100644
index 0000000..1420340
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/fileconfig-viewer-dialog.hbs
@@ -0,0 +1,53 @@
+{{!
+ * 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="modal fade" tabindex="-1" role="dialog" id="{{dialogId}}">
+  <div class="modal-dialog" role="document" style="width: 700px;">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+        <h4 class="modal-title bold-text">{{title}}</h4>
+      </div>
+      <div class="modal-body" style="padding: 0;">
+        <table class="table table-hover table-custom-bordered table-custom-striped fix-table-overflow" style="max-width: 700px;">
+          <thead>
+            <tr>
+              <th>Name</th>
+              <th>Value</th>
+            </tr>
+          </thead>
+          <tbody>
+            {{#each customProps as |prop|}}
+              <tr>
+                <td>{{prop.name}}</td>
+                <td title="{{prop.value}}">{{prop.value}}</td>
+              </tr>
+            {{else}}
+              <tr>
+                <td colspan="2">No data available</td>
+              </tr>
+            {{/each}}
+          </tbody>
+        </table>
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/info-tooltip.hbs
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/info-tooltip.hbs b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/info-tooltip.hbs
new file mode 100644
index 0000000..faba135
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/info-tooltip.hbs
@@ -0,0 +1,20 @@
+{{!
+ * 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.
+}}
+
+<span class="glyphicon glyphicon-triangle-top top-arrow"></span>
+<div id="tooltip_content"></div>

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/service-component-table.hbs
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/service-component-table.hbs b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/service-component-table.hbs
new file mode 100644
index 0000000..8f3904d
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/service-component-table.hbs
@@ -0,0 +1,113 @@
+{{!
+ * 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="col-md-12">
+  <div class="form-group">
+    <label>Service Components</label>
+    <span class="glyphicon glyphicon-info-sign info-icon" data-info="components"></span>
+    <button class="btn btn-primary btn-xs pull-right" {{action "showAddComponentModal"}}>
+      <span class="glyphicon glyphicon-plus"></span>
+    </button>
+    <div class="panel panel-default">
+      <table class="table table-hover table-custom-bordered table-custom-striped table-custom-action">
+        <thead>
+          <tr>
+            <th>Component Name</th>
+            <th>CPU</th>
+            <th>Memory</th>
+            <th># Containers</th>
+            <th>Artifact Id</th>
+            <th>Launch Command</th>
+            <th></th>
+          </tr>
+        </thead>
+        <tbody>
+          {{#each serviceDef.serviceComponents as |component|}}
+            <tr>
+              <td>{{component.name}}</td>
+              <td>{{component.cpus}}</td>
+              <td>{{component.memory}}</td>
+              <td>{{component.numOfContainers}}</td>
+              <td>{{component.artifactId}}</td>
+              <td>{{component.launchCommand}}</td>
+              <td class="align-center">
+                <span class="glyphicon glyphicon-remove remove-icon" {{action "removeComponent" component}}></span>
+              </td>
+            </tr>
+          {{else}}
+            <tr class="align-center">
+              <td colspan="7">No data available</td>
+            </tr>
+          {{/each}}
+        </tbody>
+      </table>
+    </div>
+  </div>
+</div>
+
+<div class="modal fade deploy-service-modal" tabindex="-1" role="dialog" id="addComponentModal">
+  <div class="modal-dialog" role="document">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+        <h4 class="modal-title">Add Component</h4>
+      </div>
+      <div class="modal-body">
+        {{#if duplicateNameError}}
+          <div class="alert alert-danger alert-dismissible" role="alert">
+            <strong>Component name already exists</strong>
+          </div>
+        {{/if}}
+        <div class="form-group">
+          <label class="required">Component Name</label>
+          {{input type="text" class="form-control" value=currentComponent.name}}
+        </div>
+        <div class="form-group">
+          <label class="required">CPU</label>
+          {{input type="number" min="0" class="form-control" value=currentComponent.cpus}}
+        </div>
+        <div class="form-group">
+          <label class="required">Memory</label>
+          {{input type="number" min="0" class="form-control" value=currentComponent.memory}}
+        </div>
+        <div class="form-group">
+          <label class="required"># Containers</label>
+          {{input type="number" min="0" class="form-control" value=currentComponent.numOfContainers}}
+        </div>
+        <div class="form-group">
+          <label class="required">Artifact Id</label>
+          {{input type="text" class="form-control" value=currentComponent.artifactId}}
+        </div>
+        <div class="form-group">
+          <label class="required">Launch Command</label>
+          {{input type="text" class="form-control" value=currentComponent.launchCommand}}
+        </div>
+        <div class="form-group">
+          <label class="checkbox-inline">
+            {{input type="checkbox" checked=currentComponent.uniqueComponentSupport}}
+            Unique Component Support
+          </label>
+        </div>
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+        <button type="button" class="btn btn-primary" {{action "addNewComponent"}} disabled={{unless isValidCurrentComponent "disabled"}}>Add</button>
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/service-config-table.hbs
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/service-config-table.hbs b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/service-config-table.hbs
new file mode 100644
index 0000000..46a66ee
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/service-config-table.hbs
@@ -0,0 +1,130 @@
+{{!
+ * 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="col-md-12">
+  <div class="form-group">
+    <label>Service Configurations</label>
+    <span class="glyphicon glyphicon-info-sign info-icon" data-info="configurations"></span>
+    <button class="btn btn-primary btn-xs pull-right" {{action "showNewConfigurationModal"}}>
+      <span class="glyphicon glyphicon-plus"></span>
+    </button>
+    <button class="btn btn-primary btn-xs pull-right" style="margin-right: 5px;" {{action "showServiceConfigUploadModal"}}>
+      <span class="glyphicon glyphicon-open"></span>
+    </button>
+    <div class="panel panel-default">
+      <table class="table table-hover table-custom-bordered table-custom-striped table-custom-action">
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Value</th>
+            <th>Type</th>
+            <th>Scope</th>
+            <th></th>
+          </tr>
+        </thead>
+        <tbody>
+          {{#each serviceDef.serviceConfigs as |config|}}
+            <tr>
+              <td>{{config.name}}</td>
+              <td>{{config.value}}</td>
+              <td>{{config.capitalizedType}}</td>
+              <td>{{config.formattedScope}}</td>
+              <td>
+                <span class="glyphicon glyphicon-remove remove-icon" {{action "removeConfiguration" config}}></span>
+              </td>
+            </tr>
+          {{else}}
+            <tr class="align-center">
+              <td colspan="5">No data available</td>
+            </tr>
+          {{/each}}
+        </tbody>
+      </table>
+    </div>
+  </div>
+</div>
+
+<div class="modal fade deploy-service-modal" tabindex="-1" role="dialog" id="addConfigurationModal">
+  <div class="modal-dialog" role="document">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+        <h4 class="modal-title bold-text">Add Configuration</h4>
+      </div>
+      <div class="modal-body">
+        <div class="form-group">
+          <label class="required">Name</label>
+          {{input type="text" class="form-control" value=currentConfig.name}}
+        </div>
+        <div class="form-group">
+          <label class="required">Value</label>
+          {{input type="text" class="form-control" value=currentConfig.value}}
+        </div>
+        <div class="form-group">
+          <label class="required">Type</label>
+          <div>
+            <label class="radio-inline">
+              <input type="radio" name="type" value="property" checked={{eq currentConfig.type "property"}} onchange={{action "configTypeChanged" "property"}}>Property
+            </label>
+            <label class="radio-inline">
+              <input type="radio" name="type" value="env" checked={{eq currentConfig.type "env"}} onchange={{action "configTypeChanged" "env"}}>Env
+            </label>
+            <label class="radio-inline">
+              <input type="radio" name="type" value="quicklink" checked={{eq currentConfig.type "quicklink"}} onchange={{action "configTypeChanged" "quicklink"}}>Quicklink
+            </label>
+          </div>
+        </div>
+        {{#if isNotQuicklink}}
+          <div class="form-group">
+            <label class="required">Scope</label>
+            <div>
+              <label class="radio-inline">
+                <input type="radio" name="scope" value="service" checked={{eq currentConfig.scope "service"}} onchange={{action "configScopeChanged" "service"}}>Service
+              </label>
+              {{#if isNonEmptyComponents}}
+                <label class="radio-inline">
+                  <input type="radio" name="scope" value="component" checked={{eq currentConfig.scope "component"}} onchange={{action "configScopeChanged" "component"}}>Component
+                </label>
+              {{/if}}
+            </div>
+          </div>
+          {{#if (eq currentConfig.scope "component")}}
+            <div class="form-group">
+              <select class="form-control" onchange={{action "scopeComponentChanged" value="target.value"}}>
+                {{#each componentNames as |name|}}
+                  <option value="{{name}}" selected={{eq currentConfig.componentName name}}>{{name}}</option>
+                {{/each}}
+              </select>
+            </div>
+          {{/if}}
+        {{/if}}
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+        <button type="button" class="btn btn-primary" {{action "addNewConfiguration"}} disabled={{unless isValidCurrentConfig "disabled"}}>Add</button>
+      </div>
+    </div>
+  </div>
+</div>
+
+{{upload-config
+  dialogId="service_config_upload_modal"
+  title="Upload Service Configurations"
+  configJson=serviceConfigJson
+  uploadConfig="uploadServiceConfig"
+}}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/service-fileconfig-table.hbs
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/service-fileconfig-table.hbs b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/service-fileconfig-table.hbs
new file mode 100644
index 0000000..97442c6
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/service-fileconfig-table.hbs
@@ -0,0 +1,152 @@
+{{!
+ * 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="col-md-12">
+  <div class="form-group">
+    <label>File Configurations</label>
+    <span class="glyphicon glyphicon-info-sign info-icon" data-info="fileConfigs"></span>
+    <button class="btn btn-primary btn-xs pull-right" {{action "showNewConfigFileModal"}}>
+      <span class="glyphicon glyphicon-plus"></span>
+    </button>
+    <button class="btn btn-primary btn-xs pull-right" style="margin-right: 5px;" {{action "showFileConfigUploadModal"}}>
+      <span class="glyphicon glyphicon-open"></span>
+    </button>
+    <div class="panel panel-default">
+      <table class="table table-hover table-custom-bordered table-custom-striped table-custom-action">
+        <thead>
+          <tr>
+            <th>Source File</th>
+            <th>Properties</th>
+            <th>Destination File</th>
+            <th>Type</th>
+            <th>Scope</th>
+            <th></th>
+          </tr>
+        </thead>
+        <tbody>
+          {{#each serviceDef.fileConfigs as |file|}}
+            <tr>
+              <td>
+                {{#if file.srcFile}}
+                  {{file.srcFile}}
+                {{else}}
+                  <span>N/A</span>
+                {{/if}}
+              </td>
+              <td>
+                {{#if file.props}}
+                  <a href="#" {{action "showFileConfigPropertyViewer" file.props}}>View Properties</a>
+                {{else}}
+                  <span>N/A</span>
+                {{/if}}
+              </td>
+              <td>{{file.destFile}}</td>
+              <td>{{file.type}}</td>
+              <td>{{file.formattedScope}}</td>
+              <td class="align-center">
+                <span class="glyphicon glyphicon-remove remove-icon" {{action "removeFileConfiguration" file}}></span>
+              </td>
+            </tr>
+          {{else}}
+            <tr class="align-center">
+              <td colspan="6">No data available</td>
+            </tr>
+          {{/each}}
+        </tbody>
+      </table>
+    </div>
+  </div>
+</div>
+
+<div class="modal fade deploy-service-modal" tabindex="-1" role="dialog" id="addFileConfigModal">
+  <div class="modal-dialog" role="document" style="width: 500px;">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+        <h4 class="modal-title bold-text">Add File Configuration</h4>
+      </div>
+      <div class="modal-body">
+        {{#if parseError}}
+          <div class="alert alert-danger alert-dismissible" role="alert">
+            <strong>{{parseError}}</strong>
+          </div>
+        {{/if}}
+        <div class="form-group">
+          <label class="required">Type</label>
+          <div>
+            <label class="radio-inline">
+              <input type="radio" name="type" value="TEMPLATE" checked={{eq currentFileConfig.type "TEMPLATE"}} onchange={{action "configTypeChanged" "TEMPLATE"}}>TEMPLATE
+            </label>
+            <label class="radio-inline">
+              <input type="radio" name="type" value="HADOOP_XML" checked={{eq currentFileConfig.type "HADOOP_XML"}} onchange={{action "configTypeChanged" "HADOOP_XML"}}>HADOOP_XML
+            </label>
+          </div>
+        </div>
+        <div class="form-group">
+          <label class={{unless isConfigTypeHadoopXml "required"}}>Source File</label>
+          {{input type="text" class="form-control" value=currentFileConfig.srcFile}}
+        </div>
+        {{#if isConfigTypeHadoopXml}}
+          <div class="form-group">
+            <label>Properties</label> <span>(Source File and/or Properties are required)</span>
+            {{textarea class="form-control" rows="15" value=fileConfigProps placeholder="Configuration file properties here..."}}
+          </div>
+        {{/if}}
+        <div class="form-group">
+          <label class="required">Destination File</label>
+          {{input type="text" class="form-control" value=currentFileConfig.destFile}}
+        </div>
+        <div class="form-group">
+          <label class="required">Scope</label>
+          <div>
+            <label class="radio-inline">
+              <input type="radio" name="scope" value="service" checked={{eq currentFileConfig.scope "service"}} onchange={{action "configScopeChanged" "service"}}>Service
+            </label>
+            {{#if isNonEmptyComponents}}
+              <label class="radio-inline">
+                <input type="radio" name="scope" value="component" checked={{eq currentFileConfig.scope "component"}} onchange={{action "configScopeChanged" "component"}}>Component
+              </label>
+            {{/if}}
+          </div>
+        </div>
+        {{#if (eq currentFileConfig.scope "component")}}
+          <div class="form-group">
+            <select class="form-control" onchange={{action "scopeComponentChanged" value="target.value"}}>
+              {{#each componentNames as |name|}}
+                <option value="{{name}}" selected={{eq currentFileConfig.componentName name}}>{{name}}</option>
+              {{/each}}
+            </select>
+          </div>
+        {{/if}}
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+        <button type="button" class="btn btn-primary" {{action "addNewFileConfig"}} disabled={{unless isValidCurrentFileConfig "disabled"}}>Add</button>
+      </div>
+    </div>
+  </div>
+</div>
+
+{{upload-config
+  dialogId="service_file_config_upload_modal"
+  title="Upload File Configurations"
+  configJson=fileConfigJson
+  uploadConfig="uploadFileConfig"
+}}
+
+{{fileconfig-viewer-dialog dialogId="file_config_properties_viewer" props=propertyViewer}}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/upload-config.hbs
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/upload-config.hbs b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/upload-config.hbs
new file mode 100644
index 0000000..045fb0f
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/components/upload-config.hbs
@@ -0,0 +1,44 @@
+{{!
+ * 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="modal fade" tabindex="-1" role="dialog" id="{{dialogId}}">
+  <div class="modal-dialog" role="document">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+        <h4 class="modal-title bold-text">{{title}}</h4>
+      </div>
+      <div class="modal-body">
+        <div class="form-group">
+          {{textarea class="form-control" rows="20" cols="100" value=configJson placeholder="JSON Configuration Here..."}}
+        </div>
+        {{#if isParseError}}
+          <div class="panel panel-default">
+            <div class="panel-body bg-danger">
+              <strong>{{parseErrorMsg}}</strong>
+            </div>
+          </div>
+        {{/if}}
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+        <button type="button" class="btn btn-primary" {{action "uploadConfig"}} disabled={{unless isValidConfigJson "disabled"}}>Upload</button>
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/hadoop/blob/7f037ee3/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-deploy-service.hbs
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-deploy-service.hbs b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-deploy-service.hbs
new file mode 100644
index 0000000..98bc917
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-deploy-service.hbs
@@ -0,0 +1,33 @@
+{{!
+ * 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.
+}}
+
+{{breadcrumb-bar breadcrumbs=breadcrumbs hideRefresh=true}}
+
+<div class="col-md-12 container-fluid">
+  <div class="row">
+    {{deploy-service
+      savedStandardTemplates=savedStandardTemplates
+      savedJsonTemplates=savedJsonTemplates
+      serviceDef=model
+      serviceResp=serviceResponse
+      isLoading=isLoading
+      deployServiceDef="deployServiceDef"
+      deployServiceJson="deployServiceJson"
+    }}
+  </div>
+</div>


---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org