blob: dfaf95371ac6f020ad0ad7b92dc882239d90f72a [file] [log] [blame]
/**
* 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');
/**
* @typedef {object} hostForQuickLink
* @property {string} hostName
* @property {string} publicHostName
* @property {string} componentName
* @property {?string} status
*/
App.QuickLinksView = Em.View.extend({
/**
* @type {boolean}
*/
isLoaded: false,
/**
* @type {boolean}
*/
showQuickLinks: false,
/**
* @type {string}
*/
quickLinksErrorMessage: '',
/**
* Updated quick links. Here we put correct hostname to url
*
* @type {object[]}
*/
quickLinks: [],
/**
* @type {string[]}
*/
actualTags: [],
/**
* @type {object[]}
*/
configProperties: [],
/**
* list of files that contains properties for enabling/disabling ssl
*
* @type {string[]}
*/
requiredSiteNames: [],
/**
* services that supports security. this array is used to find out protocol.
* besides YARN, MAPREDUCE2, ACCUMULO. These services use
* their properties to know protocol
*/
servicesSupportsHttps: ["HDFS", "HBASE"],
/**
* @type {object}
*/
ambariProperties: function () {
return App.router.get('clusterController.ambariProperties');
}.property().volatile(),
didInsertElement: function () {
this.loadQuickLinksConfigurations();
},
willDestroyElement: function () {
this.get('configProperties').clear();
this.get('actualTags').clear();
this.get('quickLinks').clear();
this.get('requiredSiteNames').clear();
},
/**
* The flags responsible for data to build quick links:
* - App.router.clusterController.isServiceMetricsLoaded
*
* The flags responsible for correct, up-to-date state of quick links:
* - App.currentStackVersionNumber
* - App.router.clusterController.isHostComponentMetricsLoaded
*/
setQuickLinks: function () {
if (App.get('router.clusterController.isServiceMetricsLoaded')) {
this.loadTags();
}
}.observes(
'App.currentStackVersionNumber',
'App.router.clusterController.isServiceMetricsLoaded',
'App.router.clusterController.isHostComponentMetricsLoaded',
'App.router.clusterController.quickLinksUpdateCounter'
),
/**
* call for configuration tags
*
* @returns {$.ajax}
*/
loadTags: function () {
return App.ajax.send({
name: 'config.tags',
sender: this,
success: 'loadTagsSuccess',
error: 'loadTagsError'
});
},
/**
* Success-callback for load-tags request
*
* @param {object} data
* @method loadTagsSuccess
*/
loadTagsSuccess: function (data) {
this.get('actualTags').clear();
var self = this;
var tags = Object.keys(data.Clusters.desired_configs).map(function (prop) {
return Em.Object.create({
siteName: prop,
tagName: data.Clusters.desired_configs[prop].tag
});
});
this.get('actualTags').pushObjects(tags);
this.setConfigProperties().done(function (configProperties) {
self.get('configProperties').pushObjects(configProperties);
self.getQuickLinksHosts();
});
},
/**
* Error-callback for load-tags request
*
* @method loadTagsError
*/
loadTagsError: function () {
this.getQuickLinksHosts();
},
/**
* Request for quick-links config
*
* @returns {$.ajax}
* @method loadQuickLinksConfigurations
*/
loadQuickLinksConfigurations: function () {
var serviceName = this.get('content.serviceName');
return App.ajax.send({
name: 'configs.quicklinksconfig',
sender: this,
data: {
serviceName: serviceName,
stackVersionUrl: App.get('stackVersionURL')
},
success: 'loadQuickLinksConfigSuccessCallback'
});
},
/**
* Sucess-callback for quick-links config request
*
* @param {object} data
* @method loadQuickLinksConfigSuccessCallback
*/
loadQuickLinksConfigSuccessCallback: function (data) {
App.quicklinksMapper.map(data);
var quickLinksConfig = this.getQuickLinksConfiguration();
if (!Em.isNone(quickLinksConfig)) {
var protocolConfig = Em.get(quickLinksConfig, 'protocol');
var checks = Em.get(protocolConfig, 'checks');
var sites = ['core-site', 'hdfs-site'];
if (checks) {
checks.forEach(function (check) {
var protocolConfigSiteProp = Em.get(check, 'site');
if (sites.indexOf(protocolConfigSiteProp) < 0) {
sites.push(protocolConfigSiteProp);
}
}, this);
}
var links = Em.get(quickLinksConfig, 'links');
if (!Em.isEmpty(links)) {
links.forEach(function (link) {
if (!link.remove) {
var portConfig = Em.get(link, 'port');
var portConfigSiteProp = Em.get(portConfig, 'site');
if (!sites.contains(portConfigSiteProp)) {
sites.push(portConfigSiteProp);
}
}
}, this);
this.set('requiredSiteNames', this.get('requiredSiteNames').pushObjects(sites).uniq());
this.setQuickLinks();
}
}
},
/**
* call for public host names
*
* @returns {$.ajax}
* @method getQuickLinksHosts
*/
getQuickLinksHosts: function () {
var masterHosts = App.HostComponent.find().filterProperty('isMaster').mapProperty('hostName').uniq();
if (masterHosts.length === 0) {
return $.Deferred().reject().promise();
}
return App.ajax.send({
name: 'hosts.for_quick_links',
sender: this,
data: {
clusterName: App.get('clusterName'),
masterHosts: masterHosts.join(','),
urlParams: this.get('content.serviceName') === 'HBASE' ? ',host_components/metrics/hbase/master/IsActiveMaster' : ''
},
success: 'setQuickLinksSuccessCallback'
});
},
/**
* Success-callback for quick-links hosts request
*
* @param {object} response
* @method setQuickLinksSuccessCallback
*/
setQuickLinksSuccessCallback: function (response) {
var serviceName = this.get('content.serviceName');
var hosts = this.getHosts(response, serviceName);
var hasQuickLinks = this.hasQuickLinksConfig(serviceName, hosts);
var hasHosts = false;
var componentNames = hosts.mapProperty('componentName');
componentNames.forEach(function(_componentName){
var masterComponent = App.MasterComponent.find().findProperty('componentName', _componentName);
if (masterComponent) {
hasHosts = hasHosts || !!masterComponent.get('totalCount');
}
});
// no need to set quicklinks if
// 1)current service does not have quick links configured
// 2)No host component present for the configured quicklinks
this.set('showQuickLinks', hasQuickLinks && hasHosts);
var isMultipleComponentsInLinks = componentNames.uniq().length > 1;
if (hosts.length === 0) {
this.setEmptyLinks();
} else if (hosts.length === 1 || isMultipleComponentsInLinks) {
this.setSingleHostLinks(hosts, response);
} else {
this.setMultipleHostLinks(hosts);
}
},
/**
* Get public host name by its host name.
*
* @method getPublicHostName
* @param {Object[]} hosts - list of hosts from response
* @param {string} hostName
* @return {?string}
**/
getPublicHostName: function (hosts, hostName) {
var host = hosts.findProperty('Hosts.host_name', hostName);
return host ? Em.get(host, 'Hosts.public_host_name') : null;
},
/**
* Get configs from `configurationController` for provided list of the tags
*
* @returns {$.Deferred}
* @method setConfigProperties
*/
setConfigProperties: function () {
this.get('configProperties').clear();
var requiredSiteNames = this.get('requiredSiteNames');
var tags = this.get('actualTags').filter(function (tag) {
return requiredSiteNames.contains(tag.siteName);
});
return App.router.get('configurationController').getConfigsByTags(tags);
},
/**
* Get quick links config for <code>content.serviceName</code>
*
* @returns {?App.QuickLinksConfig}
* @method getQuickLinksConfiguration
*/
getQuickLinksConfiguration: function () {
var serviceName = this.get('content.serviceName');
var self = this;
var quicklinks = {};
if (self.hasQuickLinksConfig(serviceName)) {
quicklinks = App.QuickLinksConfig.find().findProperty('id', serviceName);
Em.set(quicklinks, 'links', Em.get(quicklinks, 'links').filterProperty('visible', true));
return quicklinks;
}
return null;
},
/**
* Check if <code>serviceName</code> has quick-links config
*
* @param {string} serviceName
* @returns {boolean}
* @method hasQuickLinksConfig
*/
hasQuickLinksConfig: function (serviceName) {
var result = App.QuickLinksConfig.find().findProperty('id', serviceName);
if (!result) {
return false;
}
var links = result.get('links');
return Em.isEmpty(links) ? false : links.length !== links.filterProperty('remove').length;
},
/**
*
* @param {object} link
* @param {string} host
* @param {string} protocol
* @param {object[]} configProperties
* @param {object} response
* @returns {?object}
* @method getHostLink
*/
getHostLink: function (link, host, protocol, configProperties, response) {
var serviceName = this.get('content.serviceName');
if (serviceName === 'MAPREDUCE2' && response) {
var portConfig = Em.get(link, 'port');
var siteName = Em.get(portConfig, 'site');
var siteConfigs = this.get('configProperties').findProperty('type', siteName).properties;
var hostPortConfigValue = siteConfigs[Em.get(portConfig, protocol + '_config')];
if (!Em.isNone(hostPortConfigValue)) {
var hostPortValue = hostPortConfigValue.match(new RegExp('([\\w\\d.-]*):(\\d+)'));
var hostObj = response.items.findProperty('Hosts.host_name', hostPortValue[1]);
if (!Em.isNone(hostObj)) {
host = hostObj.Hosts.public_host_name;
}
}
}
var linkPort = this.setPort(Em.get(link, 'port'), protocol, configProperties);
if (Em.get(link, 'url') && !Em.get(link, 'removed')) {
var newItem = {};
var requiresUserName = Em.get(link, 'requires_user_name');
var template = Em.get(link, 'url');
if ('true' === requiresUserName) {
newItem.url = template.fmt(protocol, host, linkPort, App.router.get('loginName'));
} else {
newItem.url = template.fmt(protocol, host, linkPort);
}
newItem.label = link.label;
return newItem;
}
return null;
},
/**
* set empty links
*
* @method setEmptyLinks
*/
setEmptyLinks: function () {
//display an error message
var quickLinks = [{
label: this.get('quickLinksErrorMessage')
}];
this.set('quickLinks', quickLinks);
this.set('isLoaded', true);
},
/**
* set links that contain only one host
*
* @param {hostForQuickLink[]} hosts
* @param {object} response
* @method setSingleHostLinks
*/
setSingleHostLinks: function (hosts, response) {
var quickLinksConfig = this.getQuickLinksConfiguration();
if (!Em.isNone(quickLinksConfig)) {
var quickLinks = [];
var configProperties = this.get('configProperties');
var protocol = this.setProtocol(configProperties, quickLinksConfig.get('protocol'));
var publicHostName = hosts[0].publicHostName;
var links = Em.get(quickLinksConfig, 'links');
links.forEach(function (link) {
var componentName = link.component_name;
var hostNameForComponent = hosts.findProperty('componentName',componentName);
if (hostNameForComponent) {
publicHostName = hostNameForComponent.publicHostName;
if (link.protocol) {
protocol = this.setProtocol(configProperties, link.protocol);
}
}
if (componentName && !hostNameForComponent) {
return;
}
var newItem = this.getHostLink(link, publicHostName, protocol, configProperties, response); //quicklink generated for the hbs template
if (!Em.isNone(newItem)) {
quickLinks.push(newItem);
}
}, this);
this.set('quickLinks', quickLinks);
this.set('isLoaded', true);
}
else {
this.set('quickLinks', []);
this.set('isLoaded', false);
}
},
/**
* set links that contain multiple hosts
*
* @param {hostForQuickLink[]} hosts
* @method setMultipleHostLinks
*/
setMultipleHostLinks: function (hosts) {
var quickLinksConfig = this.getQuickLinksConfiguration();
if (Em.isNone(quickLinksConfig)) {
this.set('quickLinksArray', []);
this.set('isLoaded', false);
return;
}
var quickLinksArray = [];
hosts.forEach(function (host) {
var publicHostName = host.publicHostName;
var quickLinks = [];
var configProperties = this.get('configProperties');
var protocol = this.setProtocol(configProperties, quickLinksConfig.get('protocol'));
var serviceName = Em.get(quickLinksConfig, 'serviceName');
var links = Em.get(quickLinksConfig, 'links');
links.forEach(function (link) {
var linkRemoved = Em.get(link, 'removed');
var url = Em.get(link, 'url');
if (url && !linkRemoved) {
var hostNameRegExp = new RegExp('([\\w\\W]*):\\d+');
if (serviceName === 'HDFS') {
var config;
var configPropertiesObject = configProperties.findProperty('type', 'hdfs-site');
if (configPropertiesObject && configPropertiesObject.properties) {
var properties = configPropertiesObject.properties;
var nameServiceId = properties['dfs.nameservices'];
var nnProperties = ['dfs.namenode.{0}-address.{1}.nn1', 'dfs.namenode.{0}-address.{1}.nn2'].invoke('format', protocol, nameServiceId);
var nnPropertiesLength = nnProperties.length;
for (var i = nnPropertiesLength; i--;) {
var propertyName = nnProperties[i];
var hostNameMatch = properties[propertyName] && properties[propertyName].match(hostNameRegExp);
if (hostNameMatch && hostNameMatch[1] === host.publicHostName) {
config = propertyName;
break;
}
}
}
var portConfig = Em.get(link, 'port');
Em.set(portConfig, protocol + '_property', config);
Em.set(link, 'port', portConfig)
}
var newItem = this.getHostLink(link, publicHostName, protocol, configProperties); //quicklink generated for the hbs template
if (!Em.isNone(newItem)) {
quickLinks.push(newItem);
}
}
}, this);
if (host.status) {
quickLinks.set('publicHostNameLabel', Em.I18n.t('quick.links.publicHostName').format(host.publicHostName, host.status));
} else {
quickLinks.set('publicHostNameLabel', host.publicHostName);
}
quickLinksArray.push(quickLinks);
}, this);
this.set('quickLinksArray', quickLinksArray);
this.set('isLoaded', true);
},
/**
* set status to hosts with OOZIE_SERVER
*
* @param {object[]} hosts
* @returns {object[]}
* @method processOozieHosts
*/
processOozieHosts: function (hosts) {
var activeOozieServers = this.get('content.hostComponents')
.filterProperty('componentName', 'OOZIE_SERVER')
.filterProperty('workStatus', 'STARTED')
.mapProperty('hostName');
var oozieHostsArray = hosts.filter(function (host) {
host.status = Em.I18n.t('quick.links.label.active');
return activeOozieServers.contains(host.hostName);
}, this);
if (!oozieHostsArray.length) {
this.set('quickLinksErrorMessage', Em.I18n.t('quick.links.error.oozie.label'));
}
return oozieHostsArray;
},
/**
* set status to hosts with NAMENODE
*
* @param {hostForQuickLink[]} hosts
* @returns {object[]}
* @method hostForQuickLink
*/
processHdfsHosts: function (hosts) {
return hosts.map(function (host) {
if (host.hostName === Em.get(this, 'content.activeNameNode.hostName')) {
host.status = Em.I18n.t('quick.links.label.active');
}
else
if (host.hostName === Em.get(this, 'content.standbyNameNode.hostName')) {
host.status = Em.I18n.t('quick.links.label.standby');
}
else
if (host.hostName === Em.get(this, 'content.standbyNameNode2.hostName')) {
host.status = Em.I18n.t('quick.links.label.standby');
}
return host;
}, this);
},
/**
* set status to hosts with HBASE_MASTER
*
* @param {hostForQuickLink[]} hosts
* @param {object} response
* @returns {hostForQuickLink[]}
* @method processHbaseHosts
*/
processHbaseHosts: function (hosts, response) {
return hosts.map(function (host) {
var isActiveMaster;
response.items.filterProperty('Hosts.host_name', host.hostName).filter(function (item) {
var hbaseMaster = item.host_components.findProperty('HostRoles.component_name', 'HBASE_MASTER');
isActiveMaster = hbaseMaster && Em.get(hbaseMaster, 'metrics.hbase.master.IsActiveMaster');
});
if (isActiveMaster === 'true') {
host.status = Em.I18n.t('quick.links.label.active');
}
else
if (isActiveMaster === 'false') {
host.status = Em.I18n.t('quick.links.label.standby');
}
return host;
}, this);
},
/**
* set status to hosts with RESOURCEMANAGER
*
* @param {hostForQuickLink[]} hosts
* @returns {hostForQuickLink[]}
* @method processYarnHosts
*/
processYarnHosts: function (hosts) {
return hosts.map(function (host) {
var resourceManager = this.get('content.hostComponents')
.filterProperty('componentName', 'RESOURCEMANAGER')
.findProperty('hostName', host.hostName);
var haStatus = resourceManager && resourceManager.get('haStatus');
if (haStatus === 'ACTIVE') {
host.status = Em.I18n.t('quick.links.label.active');
}
else
if (haStatus === 'STANDBY') {
host.status = Em.I18n.t('quick.links.label.standby');
}
return host;
}, this);
},
/**
* sets public host names for required masters of current service
*
* @param {object} response
* @param {string} serviceName - selected serviceName
* @returns {hostForQuickLink[]} containing hostName(s)
* @method getHosts
*/
getHosts: function (response, serviceName) {
//The default error message when we cannot obtain the host information for the given service
this.set('quickLinksErrorMessage', Em.I18n.t('quick.links.error.nohosts.label').format(serviceName));
var hosts = [];
var quickLinkConfigs = App.QuickLinksConfig.find().findProperty("id", serviceName);
if (quickLinkConfigs) {
var links = quickLinkConfigs.get('links');
var componentNames = links.mapProperty('component_name').uniq();
componentNames.forEach(function (_componentName) {
var componentHosts = this.findHosts(_componentName, response);
switch (serviceName) {
case 'OOZIE':
hosts = hosts.concat(this.processOozieHosts(componentHosts));
break;
case "HDFS":
hosts = hosts.concat(this.processHdfsHosts(componentHosts));
break;
case "HBASE":
hosts = hosts.concat(this.processHbaseHosts(componentHosts, response));
break;
case "YARN":
hosts = hosts.concat(this.processYarnHosts(componentHosts));
break;
case "SMARTSENSE":
if(!App.MasterComponent.find().findProperty('componentName', _componentName)) {
hosts = [];
break;
}
default:
hosts = hosts.concat(componentHosts);
if(hosts.length < 1 && this.getWithDefault('content.hostComponents', []).someProperty('isMaster')) {
hosts = this.findHosts(this.get('content.hostComponents').findProperty('isMaster').get('componentName'), response);
}
break;
}
}, this);
}
return hosts;
},
/**
* find host public names
*
* @param {string} componentName
* @param {object} response
* @returns {hostForQuickLink[]}
*/
findHosts: function (componentName, response) {
var hosts = [];
var masterComponent = App.MasterComponent.find().findProperty('componentName', componentName);
if (masterComponent) {
var masterHostComponents = masterComponent.get('hostNames') || [];
masterHostComponents.forEach(function (_hostName) {
var host = this.getPublicHostName(response.items, _hostName);
if (host) {
hosts.push({
hostName: _hostName,
publicHostName: host,
componentName: componentName
});
}
}, this);
}
return hosts;
},
/**
* 'http' for 'https'
* 'https' for 'https'
* empty string otherwise
*
* @param {string} type
* @returns {string}
* @method reverseType
*/
reverseType: function (type) {
if ('https' === type) {
return 'http';
}
if ('http' === type) {
return 'https';
}
return '';
},
/**
*
* @param {object[]} configProperties
* @param {string} configType
* @param {string} property
* @param {string} desiredState
* @returns {boolean}
* @method meetDesired
*/
meetDesired: function (configProperties, configType, property, desiredState) {
var currentConfig = configProperties.findProperty('type', configType);
if (!currentConfig) {
return false;
}
var currentPropertyValue = currentConfig.properties[property];
if ('NOT_EXIST' === desiredState) {
return Em.isNone(currentPropertyValue);
}
if ('EXIST' === desiredState) {
return !Em.isNone(currentPropertyValue);
}
return desiredState === currentPropertyValue;
},
/**
* setProtocol - if cluster is secure for some services (GANGLIA, MAPREDUCE2, YARN and servicesSupportsHttps)
* protocol becomes "https" otherwise "http" (by default)
*
* @param {Object} configProperties
* @param {Object} protocolConfig
* @returns {string} "https" or "http" only!
* @method setProtocol
*/
setProtocol: function (configProperties, protocolConfig) {
var hadoopSslEnabled = false;
if (!Em.isEmpty(configProperties)) {
var hdfsSite = configProperties.findProperty('type', 'hdfs-site');
hadoopSslEnabled = hdfsSite && Em.get(hdfsSite, 'properties') && hdfsSite.properties['dfs.http.policy'] === 'HTTPS_ONLY';
}
if (!protocolConfig) {
return hadoopSslEnabled ? 'https' : 'http';
}
var protocolType = Em.get(protocolConfig, 'type');
if ('HTTPS_ONLY' === protocolType) {
return 'https';
}
if ('HTTP_ONLY' === protocolType) {
return 'http';
}
protocolType = protocolType.toLowerCase();
var count = 0;
var checks = Em.get(protocolConfig, 'checks');
if (!checks) {
return hadoopSslEnabled ? 'https' : 'http';
}
checks.forEach(function (check) {
var configType = Em.get(check, 'site');
var property = Em.get(check, 'property');
var desiredState = Em.get(check, 'desired');
var checkMeet = this.meetDesired(configProperties, configType, property, desiredState);
if (!checkMeet) {
count++;
}
}, this);
return count ? this.reverseType(protocolType) : protocolType;
},
/**
* sets the port of quick link
*
* @param {object} portConfigs
* @param {string} protocol
* @param {object[]} configProperties
* @returns {string}
* @method setPort
*/
setPort: function (portConfigs, protocol, configProperties) {
var defaultPort = Em.get(portConfigs, protocol + '_default_port');
var portProperty = Em.get(portConfigs, protocol + '_property');
var site = configProperties.findProperty('type', Em.get(portConfigs, 'site'));
var propertyValue = site && site.properties && site.properties[portProperty];
if (!propertyValue) {
return defaultPort;
}
var regexValue = Em.get(portConfigs, 'regex');
regexValue = regexValue.trim();
if (regexValue) {
var re = new RegExp(regexValue);
var portValue = propertyValue.match(re);
try {
return portValue[1];
} catch (err) {
return defaultPort;
}
} else {
return propertyValue;
}
}
});