mount_points_based_initializer_mixin.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. var App = require('app');
  19. /**
  20. * Regexp used to determine if mount point is windows-like
  21. *
  22. * @type {RegExp}
  23. */
  24. var winRegex = /^([a-z]):\\?$/;
  25. App.MountPointsBasedInitializerMixin = Em.Mixin.create({
  26. /**
  27. * Map for methods used as value-modifiers for configProperties with values as mount point(s)
  28. * Used if mount point is win-like (@see winRegex)
  29. * Key: id
  30. * Value: method-name
  31. *
  32. * @type {{default: string, file: string, slashes: string}}
  33. */
  34. winReplacersMap: {
  35. default: '_defaultWinReplace',
  36. file: '_winReplaceWithFile',
  37. slashes: '_defaultWinReplaceWithAdditionalSlashes'
  38. },
  39. /**
  40. * Initializer for configs with value as one of the possible mount points
  41. * Only hosts that contains on the components from <code>initializer.components</code> are processed
  42. * Hosts with Windows needs additional processing (@see winReplacersMap)
  43. * Value example: '/', '/some/cool/dir'
  44. *
  45. * @param {configProperty} configProperty
  46. * @param {topologyLocalDB} localDB
  47. * @param {object} dependencies
  48. * @param {object} initializer
  49. * @return {Object}
  50. */
  51. _initAsSingleMountPoint: function (configProperty, localDB, dependencies, initializer) {
  52. var hostsInfo = this._updateHostInfo(localDB.hosts);
  53. var setOfHostNames = this._getSetOfHostNames(localDB, initializer);
  54. var winReplacersMap = this.get('winReplacersMap');
  55. // In Add Host Wizard, if we did not select this slave component for any host, then we don't process any further.
  56. if (!setOfHostNames.length) {
  57. return configProperty;
  58. }
  59. var allMountPoints = this._getAllMountPoints(setOfHostNames, hostsInfo, localDB);
  60. var mPoint = allMountPoints[0].mountpoint;
  61. if (mPoint === "/") {
  62. mPoint = Em.get(configProperty, 'recommendedValue');
  63. }
  64. else {
  65. var mp = mPoint.toLowerCase();
  66. if (winRegex.test(mp)) {
  67. var methodName = winReplacersMap[initializer.winReplacer];
  68. mPoint = this[methodName].call(this, configProperty, mp);
  69. }
  70. else {
  71. mPoint = mPoint + Em.get(configProperty, 'recommendedValue');
  72. }
  73. }
  74. Em.setProperties(configProperty, {
  75. value: mPoint,
  76. recommendedValue: mPoint
  77. });
  78. return configProperty;
  79. },
  80. /**
  81. * Initializer for configs with value as all of the possible mount points
  82. * Only hosts that contains on the components from <code>initializer.components</code> are processed
  83. * Hosts with Windows needs additional processing (@see winReplacersMap)
  84. * Value example: '/\n/some/cool/dir' (`\n` - is divider)
  85. *
  86. * @param {Object} configProperty
  87. * @param {topologyLocalDB} localDB
  88. * @param {object} dependencies
  89. * @param {object} initializer
  90. * @return {Object}
  91. */
  92. _initAsMultipleMountPoints: function (configProperty, localDB, dependencies, initializer) {
  93. var hostsInfo = this._updateHostInfo(localDB.hosts);
  94. var self = this;
  95. var setOfHostNames = this._getSetOfHostNames(localDB, initializer);
  96. var winReplacersMap = this.get('winReplacersMap');
  97. // In Add Host Wizard, if we did not select this slave component for any host, then we don't process any further.
  98. if (!setOfHostNames.length) {
  99. return configProperty;
  100. }
  101. var allMountPoints = this._getAllMountPoints(setOfHostNames, hostsInfo, localDB);
  102. var mPoint = '';
  103. allMountPoints.forEach(function (eachDrive) {
  104. if (eachDrive.mountpoint === '/') {
  105. mPoint += Em.get(configProperty, 'recommendedValue') + "\n";
  106. }
  107. else {
  108. var mp = eachDrive.mountpoint.toLowerCase();
  109. if (winRegex.test(mp)) {
  110. var methodName = winReplacersMap[initializer.winReplacer];
  111. mPoint += self[methodName].call(this, configProperty, mp);
  112. }
  113. else {
  114. mPoint += eachDrive.mountpoint + Em.get(configProperty, 'recommendedValue') + "\n";
  115. }
  116. }
  117. }, this);
  118. Em.setProperties(configProperty, {
  119. value: mPoint,
  120. recommendedValue: mPoint
  121. });
  122. return configProperty;
  123. },
  124. /**
  125. * Replace drive-based windows-path with 'file:///'
  126. *
  127. * @param {configProperty} configProperty
  128. * @param {string} mountPoint
  129. * @returns {string}
  130. * @private
  131. */
  132. _winReplaceWithFile: function (configProperty, mountPoint) {
  133. var winDriveUrl = mountPoint.toLowerCase().replace(winRegex, 'file:///$1:');
  134. return winDriveUrl + Em.get(configProperty, 'recommendedValue') + '\n';
  135. },
  136. /**
  137. * Replace drive-based windows-path
  138. *
  139. * @param {configProperty} configProperty
  140. * @param {string} mountPoint
  141. * @returns {string}
  142. * @private
  143. */
  144. _defaultWinReplace: function (configProperty, mountPoint) {
  145. var winDrive = mountPoint.toLowerCase().replace(winRegex, '$1:');
  146. var winDir = Em.get(configProperty, 'recommendedValue').replace(/\//g, '\\');
  147. return winDrive + winDir + '\n';
  148. },
  149. /**
  150. * Same to <code>_defaultWinReplace</code>, but with extra-slash in the end
  151. *
  152. * @param {configProperty} configProperty
  153. * @param {string} mountPoint
  154. * @returns {string}
  155. * @private
  156. */
  157. _defaultWinReplaceWithAdditionalSlashes: function (configProperty, mountPoint) {
  158. var winDrive = mountPoint.toLowerCase().replace(winRegex, '$1:');
  159. var winDir = Em.get(configProperty, 'recommendedValue').replace(/\//g, '\\\\');
  160. return winDrive + winDir + '\n';
  161. },
  162. /**
  163. * Update information from localDB using <code>App.Host</code>-model
  164. *
  165. * @param {object} hostsInfo
  166. * @returns {object}
  167. * @private
  168. */
  169. _updateHostInfo: function (hostsInfo) {
  170. App.Host.find().forEach(function (item) {
  171. if (!hostsInfo[item.get('id')]) {
  172. hostsInfo[item.get('id')] = {
  173. name: item.get('id'),
  174. cpu: item.get('cpu'),
  175. memory: item.get('memory'),
  176. disk_info: item.get('diskInfo'),
  177. bootStatus: "REGISTERED",
  178. isInstalled: true
  179. };
  180. }
  181. });
  182. return hostsInfo;
  183. },
  184. /**
  185. * Determines if mount point is valid
  186. * Criterias:
  187. * <ul>
  188. * <li>Should has available space</li>
  189. * <li>Should not be home-dir</li>
  190. * <li>Should not be docker-dir</li>
  191. * <li>Should not be boot-dir</li>
  192. * <li>Should not be dev-dir</li>
  193. * <li>Valid mount point started from /usr/hdp/ should be /usr/hdp/current
  194. * or /usr/hdp/<STACK_VERSION_NUMBER> e.g. /usr/hdp/2.5.0.0
  195. * </li>
  196. * </ul>
  197. *
  198. * @param {{mountpoint: string, available: number}} mPoint
  199. * @returns {function} true - valid, false - invalid
  200. * @private
  201. */
  202. _filterMountPoint: function (localDB) {
  203. var stackVersionNumber = [Em.getWithDefault(localDB.selectedStack || {}, 'repository_version', null)].compact();
  204. return function(mPoint) {
  205. var isAvailable = mPoint.available !== 0;
  206. if (!isAvailable) {
  207. return false;
  208. }
  209. var stackRoot = '/usr/hdp';
  210. var notHome = !['/', '/home'].contains(mPoint.mountpoint);
  211. var notDocker = !['/etc/resolv.conf', '/etc/hostname', '/etc/hosts'].contains(mPoint.mountpoint);
  212. var notBoot = mPoint.mountpoint && !(mPoint.mountpoint.startsWith('/boot')
  213. || mPoint.mountpoint.startsWith('/mnt')
  214. || mPoint.mountpoint.startsWith('/tmp'));
  215. var notDev = !(['devtmpfs', 'tmpfs', 'vboxsf', 'CDFS'].contains(mPoint.type));
  216. var validStackRootMount = !(mPoint.mountpoint.startsWith(stackRoot) && !['current'].concat(stackVersionNumber).filter(function(i) {
  217. return mPoint.mountpoint === stackRoot + '/' + i;
  218. }).length);
  219. return notHome && notDocker && notBoot && notDev && validStackRootMount;
  220. };
  221. },
  222. /**
  223. * Get list of hostNames from localDB which contains needed components
  224. *
  225. * @param {topologyLocalDB} localDB
  226. * @param {object} initializer
  227. * @returns {string[]}
  228. * @private
  229. */
  230. _getSetOfHostNames: function (localDB, initializer) {
  231. var masterComponentHostsInDB = Em.getWithDefault(localDB, 'masterComponentHosts', []);
  232. var slaveComponentHostsInDB = Em.getWithDefault(localDB, 'slaveComponentHosts', []);
  233. var hosts = masterComponentHostsInDB.filter(function (master) {
  234. return initializer.components.contains(master.component);
  235. }).mapProperty('hostName');
  236. var sHosts = slaveComponentHostsInDB.find(function (slave) {
  237. return initializer.components.contains(slave.componentName);
  238. });
  239. if (sHosts) {
  240. hosts = hosts.concat(sHosts.hosts.mapProperty('hostName'));
  241. }
  242. return hosts;
  243. },
  244. /**
  245. * Get list of all unique valid mount points for hosts
  246. *
  247. * @param {string[]} setOfHostNames
  248. * @param {object} hostsInfo
  249. * @param {topologyLocalDB} localDB
  250. * @returns {string[]}
  251. * @private
  252. */
  253. _getAllMountPoints: function (setOfHostNames, hostsInfo, localDB) {
  254. var allMountPoints = [],
  255. mountPointFilter = this._filterMountPoint(localDB);
  256. for (var i = 0; i < setOfHostNames.length; i++) {
  257. var hostname = setOfHostNames[i];
  258. var mountPointsPerHost = hostsInfo[hostname].disk_info;
  259. var mountPointAsRoot = mountPointsPerHost.findProperty('mountpoint', '/');
  260. // If Server does not send any host details information then atleast one mountpoint should be presumed as root
  261. // This happens in a single container Linux Docker environment.
  262. if (!mountPointAsRoot) {
  263. mountPointAsRoot = {
  264. mountpoint: '/'
  265. };
  266. }
  267. mountPointsPerHost.filter(mountPointFilter).forEach(function (mPoint) {
  268. if( !allMountPoints.findProperty("mountpoint", mPoint.mountpoint)) {
  269. allMountPoints.push(mPoint);
  270. }
  271. }, this);
  272. }
  273. if (!allMountPoints.length) {
  274. allMountPoints.push(mountPointAsRoot);
  275. }
  276. return allMountPoints;
  277. },
  278. /**
  279. * Settings for <code>single_mountpoint</code>-initializer
  280. * Used for configs with value as one of the possible mount points
  281. *
  282. * @see _initAsSingleMountPoint
  283. * @param {string|string[]} components
  284. * @param {string} winReplacer
  285. * @returns {{components: string[], winReplacer: string, type: string}}
  286. */
  287. getSingleMountPointConfig: function (components, winReplacer) {
  288. winReplacer = winReplacer || 'default';
  289. return {
  290. components: Em.makeArray(components),
  291. winReplacer: winReplacer,
  292. type: 'single_mountpoint'
  293. };
  294. },
  295. /**
  296. * Settings for <code>multiple_mountpoints</code>-initializer
  297. * Used for configs with value as all of the possible mount points
  298. *
  299. * @see _initAsMultipleMountPoints
  300. * @param {string|string[]} components
  301. * @param {string} winReplacer
  302. * @returns {{components: string[], winReplacer: string, type: string}}
  303. */
  304. getMultipleMountPointsConfig: function (components, winReplacer) {
  305. winReplacer = winReplacer || 'default';
  306. return {
  307. components: Em.makeArray(components),
  308. winReplacer: winReplacer,
  309. type: 'multiple_mountpoints'
  310. };
  311. }
  312. });