You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by on...@apache.org on 2014/02/07 14:52:33 UTC
git commit: AMBARI-4556. Refactor and Unit tests for host summary.
(onechiporenko)
Updated Branches:
refs/heads/trunk 556102801 -> e6e6a5ef7
AMBARI-4556. Refactor and Unit tests for host summary. (onechiporenko)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/e6e6a5ef
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/e6e6a5ef
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/e6e6a5ef
Branch: refs/heads/trunk
Commit: e6e6a5ef71858d9746745dbd8cbe3b09992418fe
Parents: 5561028
Author: Oleg Nechiporenko <on...@apache.org>
Authored: Fri Feb 7 15:47:56 2014 +0200
Committer: Oleg Nechiporenko <on...@apache.org>
Committed: Fri Feb 7 15:47:56 2014 +0200
----------------------------------------------------------------------
ambari-web/app/app.js | 9 +
ambari-web/app/assets/test/tests.js | 2 +
ambari-web/app/templates/main/host/summary.hbs | 4 +-
.../main/host/details/host_component_view.js | 237 +++++++-----
ambari-web/app/views/main/host/summary.js | 192 ++++-----
ambari-web/test/app_test.js | 10 +
.../host/details/host_component_view_test.js | 315 +++++++++++++++
ambari-web/test/views/main/host/summary_test.js | 386 +++++++++++++++++++
8 files changed, 956 insertions(+), 199 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/e6e6a5ef/ambari-web/app/app.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/app.js b/ambari-web/app/app.js
index 06797f2..252edd5 100644
--- a/ambari-web/app/app.js
+++ b/ambari-web/app/app.js
@@ -73,11 +73,17 @@ module.exports = Em.Application.create({
return !this.HostComponent.find().someProperty('componentName', 'SECONDARY_NAMENODE');
}.property('router.clusterController.isLoaded'),
+ /**
+ * List of components with allowed action for them
+ * @type {Em.Object}
+ */
components: Ember.Object.create({
reassignable: ['NAMENODE', 'SECONDARY_NAMENODE', 'JOBTRACKER', 'RESOURCEMANAGER'],
restartable: ['APP_TIMELINE_SERVER'],
deletable: ['SUPERVISOR', 'HBASE_MASTER', 'DATANODE', 'TASKTRACKER', 'NODEMANAGER', 'HBASE_REGIONSERVER'],
rollinRestartAllowed: ["DATANODE", "TASKTRACKER", "NODEMANAGER", "HBASE_REGIONSERVER", "SUPERVISOR"],
+ decommissionAllowed: ["DATANODE", "TASKTRACKER", "NODEMANAGER", "HBASE_REGIONSERVER"],
+ addableToHost: ["DATANODE", "TASKTRACKER", "NODEMANAGER", "HBASE_REGIONSERVER", "HBASE_MASTER", "ZOOKEEPER_SERVER", "SUPERVISOR"],
slaves: function() {
return require('data/service_components').filter(function(component){
return !component.isClient && !component.isMaster
@@ -86,6 +92,9 @@ module.exports = Em.Application.create({
masters: function() {
return require('data/service_components').filterProperty('isMaster', true).mapProperty('component_name').uniq();
+ }.property().cacheable(),
+ clients: function() {
+ return require('data/service_components').filterProperty('isClient', true).mapProperty('component_name').uniq();
}.property().cacheable()
})
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/e6e6a5ef/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 1550d0b..fd7cb29 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -98,6 +98,8 @@ require('test/views/main/dashboard/widgets/resource_manager_uptime_test');
require('test/views/main/dashboard/widgets/links_widget_test');
require('test/views/main/dashboard/widgets/pie_chart_widget_test');
require('test/views/main/dashboard/widgets/namenode_cpu_test');
+require('test/views/main/host/summary_test');
+require('test/views/main/host/details/host_component_view_test');
require('test/views/common/configs/services_config_test');
require('test/views/wizard/step9_view_test');
require('test/models/host_test');
http://git-wip-us.apache.org/repos/asf/ambari/blob/e6e6a5ef/ambari-web/app/templates/main/host/summary.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/host/summary.hbs b/ambari-web/app/templates/main/host/summary.hbs
index 4866c7f..de4fd8b 100644
--- a/ambari-web/app/templates/main/host/summary.hbs
+++ b/ambari-web/app/templates/main/host/summary.hbs
@@ -58,7 +58,7 @@
{{#each component in view.sortedComponents}}
<div class="row-fluid">
- {{view App.HostComponentView contentBinding="component" decommissionDataNodeHostNamesBinding="view.decommissionDataNodeHostNames"}}
+ {{view App.HostComponentView contentBinding="component"}}
</div>
{{/each}}
{{/if}}
@@ -77,7 +77,7 @@
<div class="span5 row">
{{#if App.isAdmin}}
<div class="btn-group pull-right">
- <button id="add_component" data-toggle="dropdown" {{bindAttr class="view.addComponentDisabled:disabled :btn :btn-info :dropdown-toggle"}}>
+ <button id="add_component" data-toggle="dropdown" {{bindAttr class=":btn :btn-info :dropdown-toggle"}}>
{{t common.installed}}
<span class="caret pull-right"></span>
</button>
http://git-wip-us.apache.org/repos/asf/ambari/blob/e6e6a5ef/ambari-web/app/views/main/host/details/host_component_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/host/details/host_component_view.js b/ambari-web/app/views/main/host/details/host_component_view.js
index b515b18..20ae8de 100644
--- a/ambari-web/app/views/main/host/details/host_component_view.js
+++ b/ambari-web/app/views/main/host/details/host_component_view.js
@@ -20,30 +20,14 @@ var App = require('app');
var uiEffects = require('utils/ui_effects');
App.HostComponentView = Em.View.extend({
+
templateName: require('templates/main/host/details/host_component'),
+
/**
* @type {App.HostComponent}
*/
content: null,
- didInsertElement: function () {
- App.tooltip($('[rel=componentHealthTooltip]'));
- App.tooltip($('[rel=passiveTooltip]'));
- if (this.get('isInProgress')) {
- this.doBlinking();
- }
- if (this.get('isDataNode')){
- this.loadDataNodeDecommissionStatus();
- }
- if (this.get('isNodeManager')){
- this.loadNodeManagerDecommissionStatus();
- }
- if (this.get('isTaskTracker')){
- this.loadTaskTrackerDecommissionStatus();
- }
- if (this.get('isRegionServer')){
- this.loadRegionServerDecommissionStatus();
- }
- },
+
/**
* @type {App.HostComponent}
*/
@@ -128,6 +112,10 @@ App.HostComponentView = Em.View.extend({
return '';
}.property('content.passiveState','parentView.content.passiveState'),
+ /**
+ * CSS-class for host component status
+ * @type {String}
+ */
statusClass: function () {
//If the component is DataNode
if (this.get('isDataNode')) {
@@ -178,67 +166,57 @@ App.HostComponentView = Em.View.extend({
}.property('content.passiveState','workStatus', 'isDataNodeRecommissionAvailable', 'isNodeManagerRecommissionAvailable', 'isTaskTrackerRecommissionAvailable', 'isRegionServerRecommissionAvailable'),
/**
+ * CSS-class for disabling drop-down menu with list of host component actions
+ * Disabled if host's <code>healthClass</code> is health-status-DEAD-YELLOW (lost heartbeat)
* @type {String}
*/
disabled: function () {
return (this.get('parentView.content.healthClass') === "health-status-DEAD-YELLOW") ? 'disabled' : '';
}.property('parentView.content.healthClass'),
+
/**
* For Upgrade failed state
+ * @type {bool}
*/
isUpgradeFailed: function () {
return App.HostComponentStatus.getKeyName(this.get('workStatus')) === "upgrade_failed";
}.property("workStatus"),
+
/**
* For Install failed state
+ * @type {bool}
*/
isInstallFailed: function () {
return App.HostComponentStatus.getKeyName(this.get('workStatus')) === "install_failed";
}.property("workStatus"),
+
/**
- * Do blinking for 1 minute
- */
- doBlinking: function () {
- var workStatus = this.get('workStatus');
- var self = this;
- var pulsate = [ App.HostComponentStatus.starting, App.HostComponentStatus.stopping, App.HostComponentStatus.installing].contains(workStatus);
- if (!pulsate && (this.get('isDataNode') || this.get('isRegionServer') || this.get('isNodeManager') || this.get('isTaskTracker'))) {
- var component = this.get('content');
- if (component && workStatus != "INSTALLED") {
- pulsate = this.get('isDecommissioning');
- }
- }
- if (pulsate && !self.get('isBlinking')) {
- self.set('isBlinking', true);
- uiEffects.pulsate(self.$('.components-health'), 1000, function () {
- self.set('isBlinking', false);
- self.doBlinking();
- });
- }
- },
- /**
- * Start blinking when host component is starting/stopping/decommissioning
+ * For Started and Starting states
+ * @type {bool}
*/
- startBlinking: function () {
- this.$('.components-health').stop(true, true);
- this.$('.components-health').css({opacity: 1.0});
- this.doBlinking();
- }.observes('workStatus','isDataNodeRecommissionAvailable', 'isDecommissioning', 'isRegionServerRecommissionAvailable',
- 'isNodeManagerRecommissionAvailable', 'isTaskTrackerRecommissionAvailable'),
-
isStart: function () {
return (this.get('workStatus') == App.HostComponentStatus.started || this.get('workStatus') == App.HostComponentStatus.starting);
}.property('workStatus'),
+ /**
+ * For Installed state
+ * @type {bool}
+ */
isStop: function () {
return (this.get('workStatus') == App.HostComponentStatus.stopped);
}.property('workStatus'),
+ /**
+ * For Installing state
+ * @type {bool}
+ */
isInstalling: function () {
return (this.get('workStatus') == App.HostComponentStatus.installing);
}.property('workStatus'),
+
/**
* No action available while component is starting/stopping/unknown
+ * @type {String}
*/
noActionAvailable: function () {
var workStatus = this.get('workStatus');
@@ -249,38 +227,148 @@ App.HostComponentView = Em.View.extend({
}
}.property('workStatus'),
+ /**
+ * For Stopping or Starting states, also for decommissioning
+ * @type {bool}
+ */
isInProgress: function () {
return (this.get('workStatus') === App.HostComponentStatus.stopping ||
this.get('workStatus') === App.HostComponentStatus.starting) ||
this.get('isDecommissioning');
}.property('workStatus', 'isDecommissioning'),
+ /**
+ * For ACTIVE <code>passiveState</code> of host component
+ * @type {bool}
+ */
+ isActive: function () {
+ return (this.get('content.passiveState') == "ACTIVE");
+ }.property('content.passiveState'),
+
+ /**
+ * For PASSIVE <code>passiveState</code> of host or service
+ * @type {bool}
+ */
+ isImplied: function() {
+ return (this.get('parentView.content.passiveState') === 'PASSIVE' || this.get('content.service.passiveState') === 'PASSIVE');
+ }.property('parentView.content.passiveState', 'content.service.passiveState'),
+
+ /**
+ *
+ * @type {bool}
+ */
+ isDecommissioning: function () {
+ return ( (this.get('isDataNode') && this.get("isDataNodeDecommissioning")) || (this.get('isRegionServer') && this.get("isRegionServerDecommissioning"))
+ || (this.get('isNodeManager') && this.get("isNodeManagerDecommissioning")) || (this.get('isTaskTracker') && this.get('isTaskTrackerDecommissioning')));
+ }.property("workStatus", "isDataNodeDecommissioning", "isRegionServerDecommissioning", "isNodeManagerDecommissioning", "isTaskTrackerDecommissioning"),
+
+ /**
+ * Shows whether we need to show Delete button
+ * @type {bool}
+ */
+ isDeletableComponent: function () {
+ return App.get('components.deletable').contains(this.get('content.componentName'));
+ }.property('content'),
+
+ /**
+ * Host component with some <code>workStatus</code> can't be deleted (so, disable such action in the dropdown list)
+ * @type {bool}
+ */
+ isDeleteComponentDisabled: function () {
+ return ![App.HostComponentStatus.stopped, App.HostComponentStatus.unknown, App.HostComponentStatus.install_failed, App.HostComponentStatus.upgrade_failed].contains(this.get('workStatus'));
+ }.property('workStatus'),
+
+ /**
+ * Check if component may be reassinged to another host
+ * @type {bool}
+ */
+ isReassignable: function () {
+ return App.supports.reassignMaster && App.get('components.reassignable').contains(this.get('content.componentName')) && App.Host.find().content.length > 1;
+ }.property('content.componentName'),
+
+ /**
+ * Check if component is restartable
+ * @type {bool}
+ */
+ isRestartableComponent: function() {
+ return App.get('components.restartable').contains(this.get('content.componentName'));
+ }.property('content'),
+
+ /**
+ * Host component with some <code>workStatus</code> can't be restarted (so, disable such action in the dropdown list)
+ * @type {bool}
+ */
+ isRestartComponentDisabled: function() {
+ return ![App.HostComponentStatus.started].contains(this.get('workStatus'));
+ }.property('workStatus'),
+
+ didInsertElement: function () {
+ App.tooltip($('[rel=componentHealthTooltip]'));
+ App.tooltip($('[rel=passiveTooltip]'));
+ if (this.get('isInProgress')) {
+ this.doBlinking();
+ }
+ if (this.get('isDataNode')){
+ this.loadDataNodeDecommissionStatus();
+ }
+ if (this.get('isNodeManager')){
+ this.loadNodeManagerDecommissionStatus();
+ }
+ if (this.get('isTaskTracker')){
+ this.loadTaskTrackerDecommissionStatus();
+ }
+ if (this.get('isRegionServer')){
+ this.loadRegionServerDecommissionStatus();
+ }
+ },
+
+ /**
+ * Do blinking for 1 minute
+ */
+ doBlinking: function () {
+ var workStatus = this.get('workStatus');
+ var self = this;
+ var pulsate = [ App.HostComponentStatus.starting, App.HostComponentStatus.stopping, App.HostComponentStatus.installing].contains(workStatus);
+ if (!pulsate && (this.get('isDataNode') || this.get('isRegionServer') || this.get('isNodeManager') || this.get('isTaskTracker'))) {
+ var component = this.get('content');
+ if (component && workStatus != "INSTALLED") {
+ pulsate = this.get('isDecommissioning');
+ }
+ }
+ if (pulsate && !self.get('isBlinking')) {
+ self.set('isBlinking', true);
+ uiEffects.pulsate(self.$('.components-health'), 1000, function () {
+ self.set('isBlinking', false);
+ self.doBlinking();
+ });
+ }
+ },
+ /**
+ * Start blinking when host component is starting/stopping/decommissioning
+ */
+ startBlinking: function () {
+ this.$('.components-health').stop(true, true);
+ this.$('.components-health').css({opacity: 1.0});
+ this.doBlinking();
+ }.observes('workStatus','isDataNodeRecommissionAvailable', 'isDecommissioning', 'isRegionServerRecommissionAvailable',
+ 'isNodeManagerRecommissionAvailable', 'isTaskTrackerRecommissionAvailable'),
+
isDataNode: function () {
return this.get('content.componentName') === 'DATANODE';
}.property('content'),
+
isNodeManager: function () {
return this.get('content.componentName') === 'NODEMANAGER';
}.property('content'),
+
isTaskTracker: function () {
return this.get('content.componentName') === 'TASKTRACKER';
}.property('content'),
+
isRegionServer: function () {
return this.get('content.componentName') === 'HBASE_REGIONSERVER';
}.property('content'),
- isActive: function () {
- return (this.get('content.passiveState') == "ACTIVE");
- }.property('content.passiveState'),
-
- isImplied: function() {
- return (this.get('parentView.content.passiveState') === 'PASSIVE' || this.get('content.service.passiveState') === 'PASSIVE');
- }.property('content.passiveState'),
-
- isDecommissioning: function () {
- return ( (this.get('isDataNode') && this.get("isDataNodeDecommissioning")) || (this.get('isRegionServer') && this.get("isRegionServerDecommissioning"))
- || (this.get('isNodeManager') && this.get("isNodeManagerDecommissioning")) || (this.get('isTaskTracker') && this.get('isTaskTrackerDecommissioning')));
- }.property("workStatus", "isDataNodeDecommissioning", "isRegionServerDecommissioning", "isNodeManagerDecommissioning", "isTaskTrackerDecommissioning"),
-
isDataNodeDecommissioning: null,
isDataNodeDecommissionAvailable: null,
isDataNodeRecommissionAvailable: null,
@@ -620,31 +708,6 @@ App.HostComponentView = Em.View.extend({
deferred.resolve(desired_admin_state);
});
return deferred.promise();
- }.observes('App.router.mainHostDetailsController.content'),
-
- /**
- * Shows whether we need to show Delete button
- */
- isDeletableComponent: function () {
- return App.get('components.deletable').contains(this.get('content.componentName'));
- }.property('content'),
-
- isDeleteComponentDisabled: function () {
- return !(this.get('workStatus') == App.HostComponentStatus.stopped || this.get('workStatus') == App.HostComponentStatus.unknown ||
- this.get('workStatus') == App.HostComponentStatus.install_failed || this.get('workStatus') == App.HostComponentStatus.upgrade_failed);
- }.property('workStatus'),
-
- isReassignable: function () {
- return App.supports.reassignMaster && App.get('components.reassignable').contains(this.get('content.componentName')) && App.Host.find().content.length > 1;
- }.property('content.componentName'),
-
- isRestartableComponent: function() {
- return App.get('components.restartable').contains(this.get('content.componentName'));
- }.property('content'),
-
- isRestartComponentDisabled: function() {
- var allowableStates = [App.HostComponentStatus.started];
- return !allowableStates.contains(this.get('workStatus'));
- }.property('workStatus')
+ }.observes('App.router.mainHostDetailsController.content')
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/e6e6a5ef/ambari-web/app/views/main/host/summary.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/host/summary.js b/ambari-web/app/views/main/host/summary.js
index 4c8903f..520b4c7 100644
--- a/ambari-web/app/views/main/host/summary.js
+++ b/ambari-web/app/views/main/host/summary.js
@@ -17,13 +17,19 @@
*/
var App = require('app');
-var uiEffects = require('utils/ui_effects');
App.MainHostSummaryView = Em.View.extend({
+
templateName: require('templates/main/host/summary'),
+ /**
+ * @type {bool}
+ */
isStopCommand:true,
+ /**
+ * @type {App.Host}
+ */
content: function () {
return App.router.get('mainHostDetailsController.content');
}.property('App.router.mainHostDetailsController.content'),
@@ -34,6 +40,10 @@ App.MainHostSummaryView = Em.View.extend({
window.open(gangliaMobileUrl);
},
+ /**
+ * Message for "restart" block
+ * @type {String}
+ */
needToRestartMessage: function() {
var componentsCount, word;
componentsCount = this.get('content.componentsWithStaleConfigsCount');
@@ -48,11 +58,29 @@ App.MainHostSummaryView = Em.View.extend({
didInsertElement: function () {
this.addToolTip();
},
+
+ /**
+ * Create tooltip for "Add" button if nothing to add to the current host
+ */
addToolTip: function() {
if (this.get('addComponentDisabled')) {
App.tooltip($('#add_component'), {title: Em.I18n.t('services.nothingToAdd')});
}
}.observes('addComponentDisabled'),
+
+ /**
+ * List of installed services
+ * @type {String[]}
+ */
+ installedServices: function() {
+ return App.Service.find().mapProperty('serviceName');
+ }.property('App.Service.@each'),
+
+ /**
+ * List of installed masters and slaves
+ * Masters first, then slaves
+ * @type {DS.Model[]}
+ */
sortedComponents: function () {
var slaveComponents = [];
var masterComponents = [];
@@ -64,7 +92,12 @@ App.MainHostSummaryView = Em.View.extend({
}
}, this);
return masterComponents.concat(slaveComponents);
- }.property('content', 'content.hostComponents.length'),
+ }.property('content.hostComponents.length'),
+
+ /**
+ * List of installed clients
+ * @type {DS.Model[]}
+ */
clients: function () {
var clients = [];
this.get('content.hostComponents').forEach(function (component) {
@@ -93,7 +126,10 @@ App.MainHostSummaryView = Em.View.extend({
}).length;
}.property('clients.@each.staleConfigs'),
-
+ /**
+ * Template for addable component
+ * @type {Em.Object}
+ */
addableComponentObject: Em.Object.extend({
componentName: '',
subComponentNames: null,
@@ -104,134 +140,70 @@ App.MainHostSummaryView = Em.View.extend({
return App.format.role(this.get('componentName'));
}.property('componentName')
}),
+
+ /**
+ * If host lost heartbeat, components can't be added on it
+ * @type {bool}
+ */
isAddComponent: function () {
return this.get('content.healthClass') !== 'health-status-DEAD-YELLOW';
}.property('content.healthClass'),
+ /**
+ * Disable "Add" button if components can't be added to the current host
+ * @type {bool}
+ */
addComponentDisabled: function() {
return (!this.get('isAddComponent')) || (this.get('addableComponents.length') == 0);
}.property('isAddComponent', 'addableComponents.length'),
+ /**
+ * List of client's that may be installed to the current host
+ * @type {String[]}
+ */
installableClientComponents: function() {
- var installableClients = [];
if (!App.supports.deleteHost) {
- return installableClients;
+ return [];
}
- App.Service.find().forEach(function(svc){
- switch(svc.get('serviceName')){
- case 'PIG':
- installableClients.push('PIG');
- break;
- case 'SQOOP':
- installableClients.push('SQOOP');
- break;
- case 'HCATALOG':
- installableClients.push('HCAT');
- break;
- case 'HDFS':
- installableClients.push('HDFS_CLIENT');
- break;
- case 'OOZIE':
- installableClients.push('OOZIE_CLIENT');
- break;
- case 'ZOOKEEPER':
- installableClients.push('ZOOKEEPER_CLIENT');
- break;
- case 'HIVE':
- installableClients.push('HIVE_CLIENT');
- break;
- case 'HBASE':
- installableClients.push('HBASE_CLIENT');
- break;
- case 'YARN':
- installableClients.push('YARN_CLIENT');
- break;
- case 'MAPREDUCE':
- installableClients.push('MAPREDUCE_CLIENT');
- break;
- case 'MAPREDUCE2':
- installableClients.push('MAPREDUCE2_CLIENT');
- break;
- case 'TEZ':
- installableClients.push('TEZ_CLIENT');
- break;
- }
+ var componentServiceMap = App.QuickDataMapper.componentServiceMap();
+ var allClients = App.get('components.clients');
+ var installedServices = this.get('installedServices');
+ var installedClients = this.get('clients').mapProperty('componentName');
+ return allClients.filter(function(componentName) {
+ // service for current client is installed but client isn't installed on current host
+ return installedServices.contains(componentServiceMap[componentName]) && !installedClients.contains(componentName);
});
- this.get('content.hostComponents').forEach(function (component) {
- var index = installableClients.indexOf(component.get('componentName'));
- if (index > -1) {
- installableClients.splice(index, 1);
- }
- }, this);
- return installableClients;
- }.property('content', 'content.hostComponents.length', 'App.Service', 'App.supports.deleteHost'),
-
+ }.property('content.hostComponents.length', 'installedServices.length', 'App.supports.deleteHost'),
+
+ /**
+ * List of components that may be added to the current host
+ * @type {Em.Object[]}
+ */
addableComponents: function () {
var components = [];
- var services = App.Service.find();
- var dataNodeExists = false;
- var taskTrackerExists = false;
- var regionServerExists = false;
- var zookeeperServerExists = false;
- var nodeManagerExists = false;
- var hbaseMasterExists = false;
- var supervisorExists = false;
-
+ var self = this;
var installableClients = this.get('installableClientComponents');
-
- this.get('content.hostComponents').forEach(function (component) {
- switch (component.get('componentName')) {
- case 'DATANODE':
- dataNodeExists = true;
- break;
- case 'TASKTRACKER':
- taskTrackerExists = true;
- break;
- case 'HBASE_REGIONSERVER':
- regionServerExists = true;
- break;
- case 'ZOOKEEPER_SERVER':
- zookeeperServerExists = true;
- break;
- case 'NODEMANAGER':
- nodeManagerExists = true;
- break;
- case 'HBASE_MASTER':
- hbaseMasterExists = true;
- break;
- case 'SUPERVISOR':
- supervisorExists = true;
- break;
- }
- }, this);
+ var installedComponents = this.get('content.hostComponents').mapProperty('componentName');
+ var addableToHostComponents = App.get('components.addableToHost');
+ var installedServices = this.get('installedServices');
+ var componentServiceMap = App.QuickDataMapper.componentServiceMap();
- if (!dataNodeExists) {
- components.pushObject(this.addableComponentObject.create({ 'componentName': 'DATANODE' }));
- }
- if (!taskTrackerExists && services.findProperty('serviceName', 'MAPREDUCE')) {
- components.pushObject(this.addableComponentObject.create({ 'componentName': 'TASKTRACKER' }));
- }
- if (!regionServerExists && services.findProperty('serviceName', 'HBASE')) {
- components.pushObject(this.addableComponentObject.create({ 'componentName': 'HBASE_REGIONSERVER' }));
- }
- if (!hbaseMasterExists && services.findProperty('serviceName', 'HBASE')) {
- components.pushObject(this.addableComponentObject.create({ 'componentName': 'HBASE_MASTER' }));
- }
- if (!zookeeperServerExists && services.findProperty('serviceName', 'ZOOKEEPER')) {
- components.pushObject(this.addableComponentObject.create({ 'componentName': 'ZOOKEEPER_SERVER' }));
- }
- if (!nodeManagerExists && services.findProperty('serviceName', 'YARN')) {
- components.pushObject(this.addableComponentObject.create({ 'componentName': 'NODEMANAGER' }));
- }
- if (!supervisorExists && services.findProperty('serviceName', 'STORM')) {
- components.pushObject(this.addableComponentObject.create({ 'componentName': 'SUPERVISOR' }));
- }
+ addableToHostComponents.forEach(function(addableComponent) {
+ if(installedServices.contains(componentServiceMap[addableComponent]) && !installedComponents.contains(addableComponent)) {
+ components.pushObject(self.addableComponentObject.create({'componentName': addableComponent}));
+ }
+ });
if (installableClients.length > 0) {
components.pushObject(this.addableComponentObject.create({ 'componentName': 'CLIENTS', subComponentNames: installableClients }));
}
+
return components;
- }.property('content', 'content.hostComponents.length', 'installableClientComponents'),
+ }.property('content.hostComponents.length', 'installableClientComponents'),
+ /**
+ * Formatted with <code>$.timeago</code> value of host's last heartbeat
+ * @type {String}
+ */
timeSinceHeartBeat: function () {
var d = this.get('content.lastHeartBeatTime');
if (d) {
http://git-wip-us.apache.org/repos/asf/ambari/blob/e6e6a5ef/ambari-web/test/app_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/app_test.js b/ambari-web/test/app_test.js
index 59ec825..cc3155c 100644
--- a/ambari-web/test/app_test.js
+++ b/ambari-web/test/app_test.js
@@ -19,10 +19,20 @@
var App = require('app');
describe('#App.components', function() {
+
it('slaves and masters should not intersect', function() {
var intersected = App.get('components.slaves').filter(function(item){
return App.get('components.masters').contains(item);
});
expect(intersected).to.eql([]);
});
+
+ it('decommissionAllowed', function() {
+ expect(App.get('components.decommissionAllowed')).to.eql(["DATANODE", "TASKTRACKER", "NODEMANAGER", "HBASE_REGIONSERVER"]);
+ });
+
+ it('addableToHost', function() {
+ expect(App.get('components.addableToHost')).to.eql(["DATANODE", "TASKTRACKER", "NODEMANAGER", "HBASE_REGIONSERVER", "HBASE_MASTER", "ZOOKEEPER_SERVER", "SUPERVISOR"]);
+ });
+
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/e6e6a5ef/ambari-web/test/views/main/host/details/host_component_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/host/details/host_component_view_test.js b/ambari-web/test/views/main/host/details/host_component_view_test.js
new file mode 100644
index 0000000..bd30686
--- /dev/null
+++ b/ambari-web/test/views/main/host/details/host_component_view_test.js
@@ -0,0 +1,315 @@
+/**
+ * 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('models/host_component');
+require('views/main/host/details/host_component_view');
+
+var hostComponentView;
+
+describe('App.HostComponentView', function() {
+
+ beforeEach(function() {
+ hostComponentView = App.HostComponentView.create({
+ startBlinking: function(){},
+ doBlinking: function(){}
+ });
+ });
+
+ describe('#componentTextStatus', function() {
+
+ var tests = Em.A([
+ {
+ content: Em.Object.create({passiveState: 'PASSIVE'}),
+ m: 'PASSIVE state',
+ e: Em.I18n.t('hosts.component.passive.short.mode')
+ },
+ {
+ content: Em.Object.create({passiveState: 'IMPLIED'}),
+ m: 'IMPLIED state',
+ e: Em.I18n.t('hosts.component.passive.short.mode')
+ }
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.m, function() {
+ hostComponentView.set('content', test.content);
+ expect(hostComponentView.get('componentTextStatus')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#passiveImpliedTextStatus', function() {
+
+ var tests = Em.A([
+ {
+ content: {service: {passiveState: 'PASSIVE'}},
+ parentView: {content: {passiveState: 'PASSIVE'}},
+ m: 'service in PASSIVE, host in PASSIVE',
+ e: Em.I18n.t('hosts.component.passive.implied.host.mode.tooltip')
+ },
+ {
+ content: {service: {passiveState: 'PASSIVE', serviceName:'SERVICE_NAME'}},
+ parentView: {content: {passiveState: 'ACTIVE'}},
+ m: 'service in PASSIVE, host in ACTIVE',
+ e: Em.I18n.t('hosts.component.passive.implied.service.mode.tooltip').format('SERVICE_NAME')
+ },
+ {
+ content: {service: {passiveState: 'ACTIVE'}},
+ parentView: {content: {passiveState: 'ACTIVE'}},
+ m: 'service in ACTIVE, host in ACTIVE',
+ e: ''
+ }
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.m, function() {
+ hostComponentView = App.HostComponentView.create({
+ startBlinking: function(){},
+ doBlinking: function(){},
+ parentView: test.parentView,
+ content: test.content
+ });
+ expect(hostComponentView.get('passiveImpliedTextStatus')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#disabled', function() {
+
+ var tests = Em.A([
+ {
+ parentView: {content: {healthClass: 'health-status-DEAD-YELLOW'}},
+ e: 'disabled'
+ },
+ {
+ parentView: {content: {healthClass: 'another-class'}},
+ e: ''
+ }
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.m, function() {
+ hostComponentView = App.HostComponentView.create({
+ startBlinking: function(){},
+ doBlinking: function(){},
+ parentView: test.parentView
+ });
+ expect(hostComponentView.get('disabled')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isUpgradeFailed', function() {
+
+ var tests = Em.A([
+ {workStatus: 'UPGRADE_FAILED', e: true},
+ {workStatus: 'OTHER_STATUS', e: false}
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.workStatus, function() {
+ hostComponentView.set('content', {workStatus: test.workStatus});
+ expect(hostComponentView.get('isUpgradeFailed')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isInstallFailed', function() {
+
+ var tests = Em.A([
+ {workStatus: 'INSTALL_FAILED', e: true},
+ {workStatus: 'OTHER_STATUS', e: false}
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.workStatus, function() {
+ hostComponentView.set('content', {workStatus: test.workStatus});
+ expect(hostComponentView.get('isInstallFailed')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isStart', function() {
+
+ var tests = Em.A([
+ {workStatus: 'STARTED', e: true},
+ {workStatus: 'STARTING', e: true},
+ {workStatus: 'OTHER_STATUS', e: false}
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.workStatus, function() {
+ hostComponentView.set('content', {workStatus: test.workStatus});
+ expect(hostComponentView.get('isStart')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isStop', function() {
+
+ var tests = Em.A([
+ {workStatus: 'INSTALLED', e: true},
+ {workStatus: 'OTHER_STATUS', e: false}
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.workStatus, function() {
+ hostComponentView.set('content', {workStatus: test.workStatus});
+ expect(hostComponentView.get('isStop')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isInstalling', function() {
+
+ var tests = Em.A([
+ {workStatus: 'INSTALLING', e: true},
+ {workStatus: 'OTHER_STATUS', e: false}
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.workStatus, function() {
+ hostComponentView.set('content', {workStatus: test.workStatus});
+ expect(hostComponentView.get('isInstalling')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#noActionAvailable', function() {
+
+ var tests = Em.A([
+ {workStatus: 'STARTING', e: 'hidden'},
+ {workStatus: 'STOPPING', e: 'hidden'},
+ {workStatus: 'UNKNOWN', e: 'hidden'},
+ {workStatus: 'OTHER_STATUS', e: ''}
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.workStatus, function() {
+ hostComponentView.set('content', {workStatus: test.workStatus});
+ expect(hostComponentView.get('noActionAvailable')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isActive', function() {
+
+ var tests = Em.A([
+ {passiveState: 'ACTIVE', e: true},
+ {passiveState: 'PASSIVE', e: false},
+ {passiveState: 'IMPLIED', e: false}
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.workStatus, function() {
+ hostComponentView.set('content', {passiveState: test.passiveState});
+ expect(hostComponentView.get('isActive')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isImplied', function() {
+
+ var tests = Em.A([
+ {
+ content: {service: {passiveState: 'PASSIVE'}},
+ parentView: {content: {passiveState: 'PASSIVE'}},
+ m: 'service in PASSIVE, host in PASSIVE',
+ e: true
+ },
+ {
+ content: {service: {passiveState: 'PASSIVE', serviceName:'SERVICE_NAME'}},
+ parentView: {content: {passiveState: 'ACTIVE'}},
+ m: 'service in PASSIVE, host in ACTIVE',
+ e: true
+ },
+ {
+ content: {service: {passiveState: 'ACTIVE', serviceName:'SERVICE_NAME'}},
+ parentView: {content: {passiveState: 'PASSIVE'}},
+ m: 'service in ACTIVE, host in PASSIVE',
+ e: true
+ },
+ {
+ content: {service: {passiveState: 'ACTIVE'}},
+ parentView: {content: {passiveState: 'ACTIVE'}},
+ m: 'service in ACTIVE, host in ACTIVE',
+ e: false
+ }
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.m, function() {
+ hostComponentView = App.HostComponentView.create({
+ startBlinking: function(){},
+ doBlinking: function(){},
+ parentView: test.parentView,
+ content: test.content
+ });
+ expect(hostComponentView.get('isImplied')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isRestartComponentDisabled', function() {
+
+ var tests = Em.A([
+ {workStatus: 'STARTED', e: false},
+ {workStatus: 'OTHER_STATUS', e: true}
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.workStatus, function() {
+ hostComponentView.set('content', {workStatus: test.workStatus});
+ expect(hostComponentView.get('isRestartComponentDisabled')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isDeleteComponentDisabled', function() {
+
+ var tests = Em.A([
+ {workStatus: 'INSTALLED', e: false},
+ {workStatus: 'UNKNOWN', e: false},
+ {workStatus: 'INSTALL_FAILED', e: false},
+ {workStatus: 'UPGRADE_FAILED', e: false},
+ {workStatus: 'OTHER_STATUS', e: true}
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.workStatus, function() {
+ hostComponentView.set('content', {workStatus: test.workStatus});
+ expect(hostComponentView.get('isDeleteComponentDisabled')).to.equal(test.e);
+ });
+ });
+
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/e6e6a5ef/ambari-web/test/views/main/host/summary_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/host/summary_test.js b/ambari-web/test/views/main/host/summary_test.js
new file mode 100644
index 0000000..588a2b6
--- /dev/null
+++ b/ambari-web/test/views/main/host/summary_test.js
@@ -0,0 +1,386 @@
+/**
+ * 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('models/host');
+require('models/service');
+require('models/host_component');
+require('mappers/server_data_mapper');
+require('views/main/host/summary');
+
+var mainHostSummaryView;
+var extendedMainHostSummaryView = App.MainHostSummaryView.extend({content: {}, addToolTip: function(){}, installedServices: []});
+
+describe('App.MainHostSummaryView', function() {
+
+ beforeEach(function() {
+ mainHostSummaryView = extendedMainHostSummaryView.create({});
+ });
+
+ describe('#sortedComponents', function() {
+
+ var tests = Em.A([
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({isMaster: false, isSlave: true, componentName: 'B'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'A'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'C'}),
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'D'})
+ ])
+ }),
+ m: 'List of masters, slaves and clients',
+ e: ['A', 'C', 'B']
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({isMaster: false, isSlave: true, componentName: 'B'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'A'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'C'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'D'})
+ ])
+ }),
+ m: 'List of masters and slaves',
+ e: ['A', 'C', 'D', 'B']
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'B'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'A'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'C'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'D'})
+ ])
+ }),
+ m: 'List of masters',
+ e: ['B', 'A', 'C', 'D']
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({isMaster: false, isSlave: true, componentName: 'B'}),
+ Em.Object.create({isMaster: false, isSlave: true, componentName: 'A'}),
+ Em.Object.create({isMaster: false, isSlave: true, componentName: 'C'}),
+ Em.Object.create({isMaster: false, isSlave: true, componentName: 'D'})
+ ])
+ }),
+ m: 'List of slaves',
+ e: ['B', 'A', 'C', 'D']
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([])
+ }),
+ m: 'Empty list',
+ e: []
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'B'}),
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'A'}),
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'C'}),
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'D'})
+ ])
+ }),
+ m: 'List of clients',
+ e: []
+ }
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.m, function() {
+ mainHostSummaryView.set('content', test.content);
+ expect(mainHostSummaryView.get('sortedComponents').mapProperty('componentName')).to.eql(test.e);
+ });
+ });
+
+ });
+
+ describe('#clients', function() {
+
+ var tests = Em.A([
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({isMaster: false, isSlave: true, componentName: 'B'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'A'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'C'}),
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'D'})
+ ])
+ }),
+ m: 'List of masters, slaves and clients',
+ e: ['D']
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({isMaster: false, isSlave: true, componentName: 'B'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'A'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'C'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'D'})
+ ])
+ }),
+ m: 'List of masters and slaves',
+ e: []
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'B'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'A'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'C'}),
+ Em.Object.create({isMaster: true, isSlave: false, componentName: 'D'})
+ ])
+ }),
+ m: 'List of masters',
+ e: []
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({isMaster: false, isSlave: true, componentName: 'B'}),
+ Em.Object.create({isMaster: false, isSlave: true, componentName: 'A'}),
+ Em.Object.create({isMaster: false, isSlave: true, componentName: 'C'}),
+ Em.Object.create({isMaster: false, isSlave: true, componentName: 'D'})
+ ])
+ }),
+ m: 'List of slaves',
+ e: []
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([])
+ }),
+ m: 'Empty list',
+ e: []
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'B'}),
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'A'}),
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'C'}),
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'D'})
+ ])
+ }),
+ m: 'List of clients',
+ e: ['B', 'A', 'C', 'D']
+ }
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.m, function() {
+ mainHostSummaryView.set('content', test.content);
+ expect(mainHostSummaryView.get('clients').mapProperty('componentName')).to.eql(test.e);
+ });
+ });
+
+ });
+
+ describe('#areClientWithStaleConfigs', function() {
+
+ var tests = Em.A([
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'D', staleConfigs: true}),
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'C', staleConfigs: false})
+ ])
+ }),
+ m: 'Some clients with stale configs',
+ e: true
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'D', staleConfigs: false}),
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'C', staleConfigs: false})
+ ])
+ }),
+ m: 'No clients with stale configs',
+ e: false
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'D', staleConfigs: true}),
+ Em.Object.create({isMaster: false, isSlave: false, componentName: 'C', staleConfigs: true})
+ ])
+ }),
+ m: 'All clients with stale configs',
+ e: true
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([])
+ }),
+ m: 'Empty list',
+ e: false
+ }
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.m, function() {
+ mainHostSummaryView.set('content', test.content);
+ expect(mainHostSummaryView.get('areClientWithStaleConfigs')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isAddComponent', function() {
+
+ var tests = Em.A([
+ {content: {healthClass: 'health-status-DEAD-YELLOW'}, e: false},
+ {content: {healthClass: 'OTHER_VALUE'}, e: true}
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.content.healthClass, function() {
+ mainHostSummaryView.set('content', test.content);
+ expect(mainHostSummaryView.get('isAddComponent')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#installableClientComponents', function() {
+
+ it('delete host not supported', function() {
+ App.set('supports.deleteHost', false);
+ expect(mainHostSummaryView.get('installableClientComponents')).to.eql([]);
+ App.set('supports.deleteHost', true);
+ });
+
+ var tests = Em.A([
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([])
+ }),
+ services: ['HDFS', 'YARN', 'MAPREDUCE2'],
+ e: ['HDFS_CLIENT', 'YARN_CLIENT', 'MAPREDUCE2_CLIENT'],
+ m: 'no one client installed'
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({
+ componentName: 'HDFS_CLIENT'
+ })
+ ])
+ }),
+ services: ['HDFS', 'YARN', 'MAPREDUCE2'],
+ e: ['YARN_CLIENT', 'MAPREDUCE2_CLIENT'],
+ m: 'some clients are already installed'
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({
+ componentName: 'HDFS_CLIENT'
+ }),
+ Em.Object.create({
+ componentName: 'YARN_CLIENT'
+ }),
+ Em.Object.create({
+ componentName: 'MAPREDUCE2_CLIENT'
+ })
+ ])
+ }),
+ services: ['HDFS', 'YARN', 'MAPREDUCE2'],
+ e: [],
+ m: 'all clients are already installed'
+ }
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.m, function() {
+ mainHostSummaryView.set('content', test.content);
+ mainHostSummaryView.set('installedServices', test.services);
+ expect(mainHostSummaryView.get('installableClientComponents')).to.include.members(test.e);
+ expect(test.e).to.include.members(mainHostSummaryView.get('installableClientComponents'));
+ });
+ });
+
+ });
+
+ describe('#addableComponents', function() {
+
+ var tests = Em.A([
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([])
+ }),
+ services: ['HDFS', 'YARN', 'MAPREDUCE2'],
+ e: ['DATANODE', 'NODEMANAGER', 'CLIENTS'],
+ m: 'no components on host (impossible IRL, but should be tested)'
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({
+ componentName: 'HDFS_CLIENT'
+ }),
+ Em.Object.create({
+ componentName: 'DATANODE'
+ })
+ ])
+ }),
+ services: ['HDFS', 'YARN', 'MAPREDUCE2'],
+ e: ['NODEMANAGER', 'CLIENTS'],
+ m: 'some components are already installed'
+ },
+ {
+ content: Em.Object.create({
+ hostComponents: Em.A([
+ Em.Object.create({
+ componentName: 'HDFS_CLIENT'
+ }),
+ Em.Object.create({
+ componentName: 'YARN_CLIENT'
+ }),
+ Em.Object.create({
+ componentName: 'MAPREDUCE2_CLIENT'
+ }),
+ Em.Object.create({
+ componentName: 'NODEMANAGER'
+ })
+ ])
+ }),
+ services: ['HDFS', 'YARN', 'MAPREDUCE2'],
+ e: ['DATANODE'],
+ m: 'all clients and some other components are already installed'
+ }
+ ]);
+
+ tests.forEach(function(test) {
+ it(test.m, function() {
+ mainHostSummaryView.set('content', test.content);
+ mainHostSummaryView.set('installedServices', test.services);
+ expect(mainHostSummaryView.get('addableComponents').mapProperty('componentName')).to.include.members(test.e);
+ expect(test.e).to.include.members(mainHostSummaryView.get('addableComponents').mapProperty('componentName'));
+ });
+ });
+
+ });
+
+
+
+});