/** * 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'); /** * Regexp used to determine if mount point is windows-like * * @type {RegExp} */ var winRegex = /^([a-z]):\\?$/; App.MountPointsBasedInitializerMixin = Em.Mixin.create({ /** * Map for methods used as value-modifiers for configProperties with values as mount point(s) * Used if mount point is win-like (@see winRegex) * Key: id * Value: method-name * * @type {{default: string, file: string, slashes: string}} */ winReplacersMap: { default: '_defaultWinReplace', file: '_winReplaceWithFile', slashes: '_defaultWinReplaceWithAdditionalSlashes' }, /** * Initializer for configs with value as one of the possible mount points * Only hosts that contains on the components from initializer.components are processed * Hosts with Windows needs additional processing (@see winReplacersMap) * Value example: '/', '/some/cool/dir' * * @param {configProperty} configProperty * @param {topologyLocalDB} localDB * @param {object} dependencies * @param {object} initializer * @return {Object} */ _initAsSingleMountPoint: function (configProperty, localDB, dependencies, initializer) { var hostsInfo = this._updateHostInfo(localDB.hosts); var setOfHostNames = this._getSetOfHostNames(localDB, initializer); var winReplacersMap = this.get('winReplacersMap'); // In Add Host Wizard, if we did not select this slave component for any host, then we don't process any further. if (!setOfHostNames.length) { return configProperty; } var allMountPoints = this._getAllMountPoints(setOfHostNames, hostsInfo, localDB); var mPoint = allMountPoints[0].mountpoint; if (mPoint === "/") { mPoint = Em.get(configProperty, 'recommendedValue'); } else { var mp = mPoint.toLowerCase(); if (winRegex.test(mp)) { var methodName = winReplacersMap[initializer.winReplacer]; mPoint = this[methodName].call(this, configProperty, mp); } else { mPoint = mPoint + Em.get(configProperty, 'recommendedValue'); } } Em.setProperties(configProperty, { value: mPoint, recommendedValue: mPoint }); return configProperty; }, /** * Initializer for configs with value as all of the possible mount points * Only hosts that contains on the components from initializer.components are processed * Hosts with Windows needs additional processing (@see winReplacersMap) * Value example: '/\n/some/cool/dir' (`\n` - is divider) * * @param {Object} configProperty * @param {topologyLocalDB} localDB * @param {object} dependencies * @param {object} initializer * @return {Object} */ _initAsMultipleMountPoints: function (configProperty, localDB, dependencies, initializer) { var hostsInfo = this._updateHostInfo(localDB.hosts); var self = this; var setOfHostNames = this._getSetOfHostNames(localDB, initializer); var winReplacersMap = this.get('winReplacersMap'); // In Add Host Wizard, if we did not select this slave component for any host, then we don't process any further. if (!setOfHostNames.length) { return configProperty; } var allMountPoints = this._getAllMountPoints(setOfHostNames, hostsInfo, localDB); var mPoint = ''; allMountPoints.forEach(function (eachDrive) { if (eachDrive.mountpoint === '/') { mPoint += Em.get(configProperty, 'recommendedValue') + "\n"; } else { var mp = eachDrive.mountpoint.toLowerCase(); if (winRegex.test(mp)) { var methodName = winReplacersMap[initializer.winReplacer]; mPoint += self[methodName].call(this, configProperty, mp); } else { mPoint += eachDrive.mountpoint + Em.get(configProperty, 'recommendedValue') + "\n"; } } }, this); Em.setProperties(configProperty, { value: mPoint, recommendedValue: mPoint }); return configProperty; }, /** * Replace drive-based windows-path with 'file:///' * * @param {configProperty} configProperty * @param {string} mountPoint * @returns {string} * @private */ _winReplaceWithFile: function (configProperty, mountPoint) { var winDriveUrl = mountPoint.toLowerCase().replace(winRegex, 'file:///$1:'); return winDriveUrl + Em.get(configProperty, 'recommendedValue') + '\n'; }, /** * Replace drive-based windows-path * * @param {configProperty} configProperty * @param {string} mountPoint * @returns {string} * @private */ _defaultWinReplace: function (configProperty, mountPoint) { var winDrive = mountPoint.toLowerCase().replace(winRegex, '$1:'); var winDir = Em.get(configProperty, 'recommendedValue').replace(/\//g, '\\'); return winDrive + winDir + '\n'; }, /** * Same to _defaultWinReplace, but with extra-slash in the end * * @param {configProperty} configProperty * @param {string} mountPoint * @returns {string} * @private */ _defaultWinReplaceWithAdditionalSlashes: function (configProperty, mountPoint) { var winDrive = mountPoint.toLowerCase().replace(winRegex, '$1:'); var winDir = Em.get(configProperty, 'recommendedValue').replace(/\//g, '\\\\'); return winDrive + winDir + '\n'; }, /** * Update information from localDB using App.Host-model * * @param {object} hostsInfo * @returns {object} * @private */ _updateHostInfo: function (hostsInfo) { App.Host.find().forEach(function (item) { if (!hostsInfo[item.get('id')]) { hostsInfo[item.get('id')] = { name: item.get('id'), cpu: item.get('cpu'), memory: item.get('memory'), disk_info: item.get('diskInfo'), bootStatus: "REGISTERED", isInstalled: true }; } }); return hostsInfo; }, /** * Determines if mount point is valid * Criterias: * * * @param {{mountpoint: string, available: number}} mPoint * @returns {function} true - valid, false - invalid * @private */ _filterMountPoint: function (localDB) { var stackVersionNumber = [Em.getWithDefault(localDB.selectedStack || {}, 'repository_version', null)].compact(); return function(mPoint) { var isAvailable = mPoint.available !== 0; if (!isAvailable) { return false; } var stackRoot = '/usr/hdp'; var notHome = !['/', '/home'].contains(mPoint.mountpoint); var notDocker = !['/etc/resolv.conf', '/etc/hostname', '/etc/hosts'].contains(mPoint.mountpoint); var notBoot = mPoint.mountpoint && !(mPoint.mountpoint.startsWith('/boot') || mPoint.mountpoint.startsWith('/mnt') || mPoint.mountpoint.startsWith('/tmp')); var notDev = !(['devtmpfs', 'tmpfs', 'vboxsf', 'CDFS'].contains(mPoint.type)); var validStackRootMount = !(mPoint.mountpoint.startsWith(stackRoot) && !['current'].concat(stackVersionNumber).filter(function(i) { return mPoint.mountpoint === stackRoot + '/' + i; }).length); return notHome && notDocker && notBoot && notDev && validStackRootMount; }; }, /** * Get list of hostNames from localDB which contains needed components * * @param {topologyLocalDB} localDB * @param {object} initializer * @returns {string[]} * @private */ _getSetOfHostNames: function (localDB, initializer) { var masterComponentHostsInDB = Em.getWithDefault(localDB, 'masterComponentHosts', []); var slaveComponentHostsInDB = Em.getWithDefault(localDB, 'slaveComponentHosts', []); var hosts = masterComponentHostsInDB.filter(function (master) { return initializer.components.contains(master.component); }).mapProperty('hostName'); var sHosts = slaveComponentHostsInDB.find(function (slave) { return initializer.components.contains(slave.componentName); }); if (sHosts) { hosts = hosts.concat(sHosts.hosts.mapProperty('hostName')); } return hosts; }, /** * Get list of all unique valid mount points for hosts * * @param {string[]} setOfHostNames * @param {object} hostsInfo * @param {topologyLocalDB} localDB * @returns {string[]} * @private */ _getAllMountPoints: function (setOfHostNames, hostsInfo, localDB) { var allMountPoints = [], mountPointFilter = this._filterMountPoint(localDB); for (var i = 0; i < setOfHostNames.length; i++) { var hostname = setOfHostNames[i]; var mountPointsPerHost = hostsInfo[hostname].disk_info; var mountPointAsRoot = mountPointsPerHost.findProperty('mountpoint', '/'); // If Server does not send any host details information then atleast one mountpoint should be presumed as root // This happens in a single container Linux Docker environment. if (!mountPointAsRoot) { mountPointAsRoot = { mountpoint: '/' }; } mountPointsPerHost.filter(mountPointFilter).forEach(function (mPoint) { if( !allMountPoints.findProperty("mountpoint", mPoint.mountpoint)) { allMountPoints.push(mPoint); } }, this); } if (!allMountPoints.length) { allMountPoints.push(mountPointAsRoot); } return allMountPoints; }, /** * Settings for single_mountpoint-initializer * Used for configs with value as one of the possible mount points * * @see _initAsSingleMountPoint * @param {string|string[]} components * @param {string} winReplacer * @returns {{components: string[], winReplacer: string, type: string}} */ getSingleMountPointConfig: function (components, winReplacer) { winReplacer = winReplacer || 'default'; return { components: Em.makeArray(components), winReplacer: winReplacer, type: 'single_mountpoint' }; }, /** * Settings for multiple_mountpoints-initializer * Used for configs with value as all of the possible mount points * * @see _initAsMultipleMountPoints * @param {string|string[]} components * @param {string} winReplacer * @returns {{components: string[], winReplacer: string, type: string}} */ getMultipleMountPointsConfig: function (components, winReplacer) { winReplacer = winReplacer || 'default'; return { components: Em.makeArray(components), winReplacer: winReplacer, type: 'multiple_mountpoints' }; } });