ambari-web/app/views/common/quick_view_link_view.js (669 lines of code) (raw):

/** * 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 {boolean} */ showNoLinks: false, /** * @type {string} */ quickLinksErrorMessage: '', /** * Updated quick links. Here we put correct hostname to url * * @type {object[]} */ quickLinks: [], /** * @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"], masterGroups: [], tooltipAttribute: 'quick-links-title-tooltip', shouldSetGroupedLinks: false, /** * @type {object} */ ambariProperties: function () { return App.router.get('clusterController.ambariProperties'); }.property().volatile(), didInsertElement: function () { this.loadQuickLinksConfigurations(); }, willDestroyElement: function () { this.get('configProperties').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.setConfigProperties().done((configProperties) => { this.get('configProperties').pushObjects(configProperties); this.getQuickLinksHosts().fail(() => { this.set('showNoLinks', true); }); }); } }.observes( 'App.currentStackVersionNumber', 'App.router.clusterController.isServiceMetricsLoaded', 'App.router.clusterController.isHostComponentMetricsLoaded', 'App.router.clusterController.quickLinksUpdateCounter' ), /** * 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', 'admin-properties']; 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) { this.addSite(link, 'host', sites); this.addSite(link, 'port', sites); } }, this); this.set('requiredSiteNames', this.get('requiredSiteNames').pushObjects(sites).uniq()); this.setQuickLinks(); } } else { this.set('showNoLinks', true); } }, addSite: function(link, linkPropertyName, sites) { var config = Em.get(link, linkPropertyName); if (config) { var siteName = Em.get(config, 'site'); if (!sites.contains(siteName)) { sites.push(siteName); } } }, /** * call for public host names * * @returns {$.ajax} * @method getQuickLinksHosts */ getQuickLinksHosts: function () { const links = App.QuickLinksConfig.find().mapProperty('links'), components = links.reduce((componentsArray, currentLinksArray) => { const componentNames = currentLinksArray.mapProperty('component_name').uniq(); return [...componentsArray, ...componentNames]; }, []), hosts = App.HostComponent.find().filter(component => { return components.contains(component.get('componentName')); }).mapProperty('hostName').uniq(); if (hosts.length === 0) { return $.Deferred().reject().promise(); } return App.ajax.send({ name: 'hosts.for_quick_links', sender: this, data: { clusterName: App.get('clusterName'), hosts: hosts.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 component = App.MasterComponent.find().findProperty('componentName', _componentName) || App.SlaveComponent.find().findProperty('componentName', _componentName); if (component) { hasHosts = hasHosts || !!component.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 and has no overridden hosts if(hasQuickLinks && (hasHosts || this.hasOverriddenHost())) { this.set('showQuickLinks', true); } else { this.set('showNoLinks', true); } var isMultipleComponentsInLinks = componentNames.uniq().length > 1; if (hosts.length === 0 && !this.hasOverriddenHost()) { this.setEmptyLinks(); } else if (hosts.length === 1 || isMultipleComponentsInLinks || this.hasOverriddenHost()) { this.setSingleHostLinks(hosts, response); } else if (this.get('masterGroups.length') > 1 || this.get('shouldSetGroupedLinks')) { this.setMultipleGroupLinks(hosts); } else { this.setMultipleHostLinks(hosts); } }, hasOverriddenHost: function() { var links = Em.get(this.getQuickLinksConfiguration(), 'links'); return links && links.some(function (each) { return each.host; }); }, /** * 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(); return App.router.get('configurationController').getCurrentConfigsBySites(this.get('requiredSiteNames')); }, /** * 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'); const 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; } } } else if (serviceName === 'RANGER') { const siteConfigs = this.get('configProperties').findProperty('type', 'admin-properties').properties; if (siteConfigs['policymgr_external_url']) { return { label: link.label, url: siteConfigs['policymgr_external_url'] } } } 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; newItem.url = this.resolvePlaceholders(newItem.url); return newItem; } return null; }, /** * Replace placeholders like ${config-type/property-name} in the given URL */ resolvePlaceholders: function(url) { return url.replace(/\$\{(\S+)\/(\S+)\}/g, function(match, configType, propertyName) { var config = this.get('configProperties').findProperty('type', configType); if (config) { return config.properties[propertyName] ? config.properties[propertyName] : match; } else { return match; } }.bind(this)); }, /** * 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 links = Em.get(quickLinksConfig, 'links'); links.forEach(function (link) { var isRelatedComponentInstalled = this.isRelatedComponentInstalled(link); if (isRelatedComponentInstalled) { var publicHostName = this.publicHostName(link, hosts, protocol); if (publicHostName) { if (link.protocol) { protocol = this.setProtocol(configProperties, link.protocol); } 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); } }, publicHostName: function(link, hosts, protocol) { if (link.host) { // if quicklink overrides hostcomponent host name, get host from config var configProperties = this.get('configProperties'); var hostProperty = Em.get(link.host, protocol + '_property'); var site = configProperties.findProperty('type', Em.get(link.host, 'site')); return site && site.properties ? this.parseHostFromUri(site.properties[hostProperty]) : null; } else { var hostNameForComponent = hosts.findProperty('componentName', link.component_name); return hostNameForComponent ? hostNameForComponent.publicHostName : null; } }, /** * @param {string} uri */ parseHostFromUri: function(uri) { if (uri) { var match = uri.match(/:\/\/([^/:]+)/i); return match != null && match.length == 2 ? match[1] : uri; } else { return null; } }, getMultipleHostLinks: function (hosts) { var quickLinksConfig = this.getQuickLinksConfiguration(); if (Em.isNone(quickLinksConfig)) { 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'); var isRelatedComponentInstalled = this.isRelatedComponentInstalled(link); if (url && !linkRemoved && isRelatedComponentInstalled) { 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 nnKeyRegex = new RegExp('^dfs\.namenode\.' + protocol + '-address\.'); var nnProperties = Object.keys(properties).filter(key => nnKeyRegex.test(key)); 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); return quickLinksArray; }, /** * set links that contain multiple hosts * * @param {hostForQuickLink[]} hosts * @method setMultipleHostLinks */ setMultipleHostLinks: function (hosts) { const quickLinks = this.getMultipleHostLinks(hosts); this.setProperties({ quickLinksArray: [ { links: quickLinks || [] } ], isLoaded: !!quickLinks }); }, /** * set links that contain for multiple grouped hosts * * @param {hostForQuickLink[]} hosts * @method setMultipleGroupLinks */ setMultipleGroupLinks: function (hosts) { let isLoaded = true; const quickLinksArray = this.get('masterGroups').map(group => { const groupHosts = hosts.filter(host => group.hosts.contains(host.hostName)), links = this.getMultipleHostLinks(groupHosts); if (!links) { isLoaded = false; } return { title: group.title, links: links || [] }; }); this.setProperties({ quickLinksArray, isLoaded }); }, /** * 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(host => { const {hostName} = host, isActiveNameNode = Em.get(this, 'content.activeNameNodes').someProperty('hostName', hostName), isStandbyNameNode = Em.get(this, 'content.standbyNameNodes').someProperty('hostName', hostName); if (isActiveNameNode) { host.status = Em.I18n.t('quick.links.label.active'); } if (isStandbyNameNode) { host.status = Em.I18n.t('quick.links.label.standby'); } return host; }); }, /** * 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); break; } }, this); } return hosts; }, /** * find host public names * * @param {string} componentName * @param {object} response * @returns {hostForQuickLink[]} */ findHosts: function (componentName, response) { var hosts = []; var component = App.MasterComponent.find().findProperty('componentName', componentName) || App.SlaveComponent.find().findProperty('componentName', componentName); if (component) { var hostComponents = component.get('hostNames') || []; hostComponents.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) { if (!portConfigs) { return ''; } 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'); if (protocol === 'https') { var httpsRegex = Em.get(portConfigs, 'https_regex'); if (httpsRegex) { regexValue = httpsRegex; } } 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; } }, setTooltip: function () { Em.run.next(() => { if (this.get('showQuickLinks') && this.get('isLoaded') && this.get('quickLinksArray.length')) { App.tooltip($(`[rel="${this.get('tooltipAttribute')}"]`)); } }); }.observes('showQuickLinks', 'isLoaded', 'quickLinksArray.length'), /** * Check if host component related to quickLink is installed * @param quickLink * @returns {Boolean} */ isRelatedComponentInstalled: function (quickLink) { return App.HostComponent.find().someProperty('componentName', quickLink.component_name); } });