blueprint.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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. module.exports = {
  19. mergeBlueprints: function(masterBlueprint, slaveBlueprint) {
  20. console.time('mergeBlueprints');
  21. var self = this;
  22. // Check edge cases
  23. if (!slaveBlueprint && !masterBlueprint) {
  24. throw 'slaveBlueprint or masterBlueprint should not be empty';
  25. }
  26. else
  27. if (slaveBlueprint && !masterBlueprint) {
  28. return slaveBlueprint;
  29. }
  30. else
  31. if (!slaveBlueprint && masterBlueprint) {
  32. return masterBlueprint;
  33. }
  34. // Work with main case (both blueprint are presented)
  35. var matches = self.matchGroups(masterBlueprint, slaveBlueprint);
  36. var res = {
  37. blueprint: { host_groups: [] },
  38. blueprint_cluster_binding: { host_groups: [] }
  39. };
  40. var tmpObj = {hosts: [], components: []};
  41. var masterBluePrintHostGroupsCluster = this.blueprintToObject(masterBlueprint, 'blueprint_cluster_binding.host_groups');
  42. var slaveBluePrintHostGroupsCluster = this.blueprintToObject(slaveBlueprint, 'blueprint_cluster_binding.host_groups');
  43. var masterBluePrintHostGroupsBlueprint = this.blueprintToObject(masterBlueprint, 'blueprint.host_groups');
  44. var slaveBluePrintHostGroupsBlueprint = this.blueprintToObject(slaveBlueprint, 'blueprint.host_groups');
  45. matches.forEach(function (match, i) {
  46. var group_name = 'host-group-' + (i + 1);
  47. var masterComponents = match.g1 ? Em.getWithDefault(masterBluePrintHostGroupsBlueprint, match.g1, tmpObj).components : [];
  48. var slaveComponents = match.g2 ? Em.getWithDefault(slaveBluePrintHostGroupsBlueprint, match.g2, tmpObj).components : [];
  49. res.blueprint.host_groups.push({
  50. name: group_name,
  51. components: masterComponents.concat(slaveComponents)
  52. });
  53. var hosts = match.g1 ? Em.getWithDefault(masterBluePrintHostGroupsCluster, match.g1, tmpObj).hosts :
  54. Em.getWithDefault(slaveBluePrintHostGroupsCluster, match.g2, tmpObj).hosts;
  55. res.blueprint_cluster_binding.host_groups.push({
  56. name: group_name,
  57. hosts: hosts
  58. });
  59. });
  60. console.timeEnd('mergeBlueprints');
  61. return res;
  62. },
  63. /**
  64. * Convert <code>blueprint</code>-object to the array with keys equal to the host-groups names
  65. * Used to improve performance when user try to search value in the blueprint using host-group name as search-field
  66. * Example:
  67. * Before:
  68. * <pre>
  69. * // blueprint
  70. * {
  71. * blueprint: {
  72. * host_groups: [
  73. * {
  74. * components: [{}, {}, ...],
  75. * name: 'n1'
  76. * },
  77. * {
  78. * components: [{}, {}, ...],
  79. * name: 'n2'
  80. * }
  81. * ]
  82. * },
  83. * blueprint_cluster_binding: {
  84. * host_groups: [
  85. * {
  86. * hosts: [{}, {}, ...],
  87. * name: 'n1'
  88. * },
  89. * {
  90. * hosts: [{}, {}, ...],
  91. * name: 'n2'
  92. * }
  93. * ]
  94. * }
  95. * }
  96. * </pre>
  97. * Return:
  98. * <pre>
  99. * // field = 'blueprint_cluster_binding.host_groups'
  100. * {
  101. * n1: {
  102. * hosts: [{}, {}, ...],
  103. * name: 'n1'
  104. * },
  105. * n2: {
  106. * hosts: [{}, {}, ...],
  107. * name: 'n2'
  108. * }
  109. * }
  110. *
  111. * // field = 'blueprint.host_groups'
  112. * {
  113. * n1: {
  114. * components: [{}, {}, ...],
  115. * name: 'n1'
  116. * },
  117. * n2: {
  118. * components: [{}, {}, ...],
  119. * name: 'n2'
  120. * }
  121. * }
  122. * </pre>
  123. * @param {object} blueprint
  124. * @param {string} field
  125. * @returns {object}
  126. */
  127. blueprintToObject: function(blueprint, field) {
  128. var valueToMap = Em.get(blueprint, field);
  129. if (!Array.isArray(valueToMap)) {
  130. return {};
  131. }
  132. return valueToMap.toMapByProperty('name');
  133. },
  134. matchGroups: function(masterBlueprint, slaveBlueprint) {
  135. var self = this;
  136. var res = [];
  137. var groups1 = masterBlueprint.blueprint_cluster_binding.host_groups;
  138. var groups2 = slaveBlueprint.blueprint_cluster_binding.host_groups;
  139. var groups1_used = groups1.map(function() { return false; });
  140. var groups2_used = groups2.map(function() { return false; });
  141. self.matchGroupsWithLeft(groups1, groups2, groups1_used, groups2_used, res, false);
  142. self.matchGroupsWithLeft(groups2, groups1, groups2_used, groups1_used, res, true);
  143. return res;
  144. },
  145. matchGroupsWithLeft: function(groups1, groups2, groups1_used, groups2_used, res, inverse) {
  146. var gs2 = this.groupsToObject(groups2);
  147. for (var i = 0; i < groups1.length; i++) {
  148. if (groups1_used[i]) {
  149. continue;
  150. }
  151. var group1 = groups1[i];
  152. groups1_used[i] = true;
  153. var group2 = gs2[group1.hosts.mapProperty('fqdn').join(',')];
  154. if (group2) {
  155. groups2_used[group2.index] = true;
  156. }
  157. var item = {};
  158. if (inverse) {
  159. item.g2 = group1.name;
  160. if (group2) {
  161. item.g1 = group2.name;
  162. }
  163. }
  164. else {
  165. item.g1 = group1.name;
  166. if (group2) {
  167. item.g2 = group2.name;
  168. }
  169. }
  170. res.push(item);
  171. }
  172. // remove unneeded property
  173. groups2.forEach(function(group) {
  174. delete group.index;
  175. });
  176. },
  177. /**
  178. * Convert array of objects to the one object to improve performance with searching objects in the provided array
  179. * Example:
  180. * Before:
  181. * <pre>
  182. * // groups
  183. * [
  184. * {
  185. * hosts: [
  186. * {fqdn: 'h1'}, {fqdn: 'h2'}
  187. * ],
  188. * name: 'n1'
  189. * },
  190. * {
  191. * hosts: [
  192. * {fqdn: 'h3'}, {fqdn: 'h4'}
  193. * ]
  194. * }
  195. * ]
  196. * </pre>
  197. * Return:
  198. * <pre>
  199. * {
  200. * 'h1,h2': {
  201. * hosts: [
  202. * {fqdn: 'h1'}, {fqdn: 'h2'}
  203. * ],
  204. * name: 'n1',
  205. * index: 0
  206. * },
  207. * 'h3,h4': {
  208. * hosts: [
  209. * {fqdn: 'h3'}, {fqdn: 'h4'}
  210. * ],
  211. * name: 'n2',
  212. * index: 1
  213. * }
  214. * }
  215. * </pre>
  216. * @param {{hosts: object[], name: string}[]} groups
  217. * @returns {object}
  218. */
  219. groupsToObject: function (groups) {
  220. var ret = {};
  221. groups.forEach(function (group, index) {
  222. var key = group.hosts.mapProperty('fqdn').join(',');
  223. ret[key] = group;
  224. ret[key].index = index;
  225. });
  226. return ret;
  227. },
  228. /**
  229. * Remove from blueprint all components expect given components
  230. * @param {object} blueprint
  231. * @param {string[]} components
  232. */
  233. filterByComponents: function(blueprint, components) {
  234. if (!blueprint) {
  235. return null;
  236. }
  237. var res = JSON.parse(JSON.stringify(blueprint));
  238. var emptyGroups = [];
  239. for (var i = 0; i < res.blueprint.host_groups.length; i++) {
  240. res.blueprint.host_groups[i].components = res.blueprint.host_groups[i].components.filter(function(c) {
  241. return components.contains(c.name);
  242. });
  243. if (res.blueprint.host_groups[i].components.length == 0) {
  244. emptyGroups.push(res.blueprint.host_groups[i].name);
  245. }
  246. }
  247. res.blueprint.host_groups = res.blueprint.host_groups.filter(function(g) {
  248. return !emptyGroups.contains(g.name);
  249. });
  250. res.blueprint_cluster_binding.host_groups = res.blueprint_cluster_binding.host_groups.filter(function(g) {
  251. return !emptyGroups.contains(g.name);
  252. });
  253. return res;
  254. },
  255. addComponentsToBlueprint: function(blueprint, components) {
  256. var res = JSON.parse(JSON.stringify(blueprint));
  257. res.blueprint.host_groups.forEach(function(group) {
  258. components.forEach(function(component) {
  259. group.components.push({ name: component });
  260. });
  261. });
  262. return res;
  263. },
  264. /**
  265. * @method buildConfigsJSON - generates JSON according to blueprint format
  266. * @param {Em.Array} stepConfigs - array of Ember Objects
  267. * @returns {Object}
  268. * Example:
  269. * {
  270. * "yarn-env": {
  271. * "properties": {
  272. * "content": "some value",
  273. * "yarn_heapsize": "1024",
  274. * "resourcemanager_heapsize": "1024",
  275. * }
  276. * },
  277. * "yarn-log4j": {
  278. * "properties": {
  279. * "content": "some other value"
  280. * }
  281. * }
  282. * }
  283. */
  284. buildConfigsJSON: function (stepConfigs) {
  285. var configurations = {};
  286. stepConfigs.forEach(function (stepConfig) {
  287. stepConfig.get('configs').forEach(function (config) {
  288. if (config.get('isRequiredByAgent')) {
  289. var type = App.config.getConfigTagFromFileName(config.get('filename'));
  290. if (!configurations[type]) {
  291. configurations[type] = {properties: {}}
  292. }
  293. configurations[type]['properties'][config.get('name')] = config.get('value');
  294. }
  295. });
  296. });
  297. return configurations;
  298. },
  299. /**
  300. * @method generateHostGroups
  301. * @param {Array} hostNames - list of all hostNames
  302. * @returns {{blueprint: {host_groups: Array}, blueprint_cluster_binding: {host_groups: Array}}}
  303. */
  304. generateHostGroups: function(hostNames) {
  305. var recommendations = {
  306. blueprint: {
  307. host_groups: []
  308. },
  309. blueprint_cluster_binding: {
  310. host_groups: []
  311. }
  312. };
  313. var hostsMap = this.getComponentForHosts();
  314. for (var i = 0; i <= hostNames.length; i++) {
  315. var host_group = {
  316. name: "host-group-" + (i + 1),
  317. components: []
  318. };
  319. var hcFiltered = hostsMap[hostNames[i]];
  320. if (Em.isNone(hcFiltered)) continue;
  321. for (var j = 0; j < hcFiltered.length; j++) {
  322. host_group.components.push({
  323. name: hcFiltered[j]
  324. });
  325. }
  326. recommendations.blueprint.host_groups.push(host_group);
  327. recommendations.blueprint_cluster_binding.host_groups.push({
  328. name: "host-group-" + (i + 1),
  329. hosts: [{
  330. fqdn: hostNames[i]
  331. }]
  332. });
  333. }
  334. return recommendations;
  335. },
  336. /**
  337. * Small helper method to update hostMap
  338. * it perform update of object only
  339. * if unique component per host is added
  340. *
  341. * @param {Object} hostMapObject
  342. * @param {string[]} hostNames
  343. * @param {string} componentName
  344. * @returns {Object}
  345. * @private
  346. */
  347. _generateHostMap: function(hostMapObject, hostNames, componentName) {
  348. Em.assert('hostMapObject, hostNames, componentName should be defined', !!hostMapObject && !!hostNames && !!componentName);
  349. if (!hostNames.length) return hostMapObject;
  350. hostNames.forEach(function(hostName) {
  351. if (!hostMapObject[hostName])
  352. hostMapObject[hostName] = [];
  353. if (!hostMapObject[hostName].contains(componentName))
  354. hostMapObject[hostName].push(componentName);
  355. });
  356. return hostMapObject;
  357. },
  358. /**
  359. * Clean up host groups from components that should be removed
  360. *
  361. * @param hostGroups
  362. * @param serviceNames
  363. */
  364. removeDeletedComponents: function(hostGroups, serviceNames) {
  365. var components = [];
  366. App.StackService.find().forEach(function(s) {
  367. if (serviceNames.contains(s.get('serviceName'))) {
  368. components = components.concat(s.get('serviceComponents').mapProperty('componentName'));
  369. }
  370. });
  371. hostGroups.blueprint.host_groups.forEach(function(hg) {
  372. hg.components = hg.components.filter(function(c) {
  373. return !components.contains(c.name);
  374. })
  375. });
  376. return hostGroups;
  377. },
  378. /**
  379. * collect all component names that are present on hosts
  380. * @returns {object}
  381. */
  382. getComponentForHosts: function() {
  383. var hostsMap = {};
  384. App.ClientComponent.find().forEach(function(c) {
  385. hostsMap = this._generateHostMap(hostsMap, c.get('hostNames'), c.get('componentName'));
  386. }, this);
  387. App.SlaveComponent.find().forEach(function (c) {
  388. hostsMap = this._generateHostMap(hostsMap, c.get('hostNames'), c.get('componentName'));
  389. }, this);
  390. App.MasterComponent.find().forEach(function (c) {
  391. hostsMap = this._generateHostMap(hostsMap, c.get('hostNames'), c.get('componentName'));
  392. }, this);
  393. return hostsMap;
  394. }
  395. };