PageRenderTime 57ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/nailgun/static/js/models.js

https://github.com/aedocw/fuel-web
JavaScript | 761 lines | 699 code | 44 blank | 18 comment | 124 complexity | c087c947ce29b113b0976a20c0d6d16f MD5 | raw file
Possible License(s): Apache-2.0
  1. /*
  2. * Copyright 2013 Mirantis, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. * not use this file except in compliance with the License. You may obtain
  6. * a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations
  14. * under the License.
  15. **/
  16. define(['utils', 'deepModel'], function(utils) {
  17. 'use strict';
  18. var models = {};
  19. var cacheMixin = {
  20. fetch: function(options) {
  21. if (this.cacheFor && options && options.cache && this.lastSyncTime && (this.cacheFor > (new Date() - this.lastSyncTime))) {
  22. return $.Deferred().resolve();
  23. }
  24. return this.constructor.__super__.fetch.apply(this, arguments);
  25. },
  26. sync: function(options) {
  27. if (this.cacheFor) {
  28. this.lastSyncTime = new Date();
  29. }
  30. return this.constructor.__super__.sync.apply(this, arguments);
  31. }
  32. };
  33. models.Release = Backbone.Model.extend({
  34. constructorName: 'Release',
  35. urlRoot: '/api/releases'
  36. });
  37. models.Releases = Backbone.Collection.extend({
  38. constructorName: 'Releases',
  39. model: models.Release,
  40. url: '/api/releases',
  41. comparator: function(release) {
  42. return release.id;
  43. }
  44. });
  45. models.Cluster = Backbone.Model.extend({
  46. constructorName: 'Cluster',
  47. urlRoot: '/api/clusters',
  48. defaults: function() {
  49. var defaults = {
  50. nodes: new models.Nodes(),
  51. tasks: new models.Tasks()
  52. };
  53. defaults.nodes.cluster = defaults.tasks.cluster = this;
  54. return defaults;
  55. },
  56. validate: function(attrs) {
  57. var errors = {};
  58. if (!$.trim(attrs.name) || $.trim(attrs.name).length == 0) {
  59. errors.name = 'Environment name cannot be empty';
  60. }
  61. if (!attrs.release) {
  62. errors.release = 'Please choose OpenStack release';
  63. }
  64. return _.isEmpty(errors) ? null : errors;
  65. },
  66. groupings: function() {
  67. return {roles: $.t('cluster_page.nodes_tab.roles'), hardware: $.t('cluster_page.nodes_tab.hardware_info'), both: $.t('cluster_page.nodes_tab.roles_and_hardware_info')};
  68. },
  69. task: function(filter1, filter2) {
  70. var filters = _.isPlainObject(filter1) ? filter1 : {name: filter1, status: filter2};
  71. return this.get('tasks') && this.get('tasks').findTask(filters);
  72. },
  73. tasks: function(filter1, filter2) {
  74. var filters = _.isPlainObject(filter1) ? filter1 : {name: filter1, status: filter2};
  75. return this.get('tasks') && this.get('tasks').filterTasks(filters);
  76. },
  77. hasChanges: function() {
  78. return this.get('nodes').hasChanges() || (this.get('changes').length && this.get('nodes').currentNodes().length);
  79. },
  80. needsRedeployment: function() {
  81. return this.get('nodes').where({pending_addition: false, status: 'error'}).length;
  82. },
  83. availableModes: function() {
  84. return ['ha_compact', 'multinode'];
  85. },
  86. fetchRelated: function(related, options) {
  87. return this.get(related).fetch(_.extend({data: {cluster_id: this.id}}, options));
  88. },
  89. isAvailableForSettingsChanges: function() {
  90. return this.get('status') == 'new' || (this.get('status') == 'stopped' && !this.get('nodes').where({status: 'ready'}).length);
  91. }
  92. });
  93. models.Clusters = Backbone.Collection.extend({
  94. constructorName: 'Clusters',
  95. model: models.Cluster,
  96. url: '/api/clusters',
  97. comparator: function(cluster) {
  98. return cluster.id;
  99. }
  100. });
  101. models.Node = Backbone.Model.extend({
  102. constructorName: 'Node',
  103. urlRoot: '/api/nodes',
  104. resource: function(resourceName) {
  105. var resource = 0;
  106. try {
  107. if (resourceName == 'cores') {
  108. resource = this.get('meta').cpu.total;
  109. } else if (resourceName == 'hdd') {
  110. resource = _.reduce(this.get('meta').disks, function(hdd, disk) {return _.isNumber(disk.size) ? hdd + disk.size : hdd;}, 0);
  111. } else if (resourceName == 'ram') {
  112. resource = this.get('meta').memory.total;
  113. } else if (resourceName == 'disks') {
  114. resource = _.pluck(this.get('meta').disks, 'size').sort(function(a, b) {return a - b;});
  115. } else if (resourceName == 'interfaces') {
  116. resource = this.get('meta').interfaces.length;
  117. }
  118. } catch (ignore) {}
  119. if (_.isNaN(resource)) {
  120. resource = 0;
  121. }
  122. return resource;
  123. },
  124. sortedRoles: function() {
  125. var preferredOrder = this.collection.cluster.get('release').get('roles');
  126. return _.union(this.get('roles'), this.get('pending_roles')).sort(function(a, b) {
  127. return _.indexOf(preferredOrder, a) - _.indexOf(preferredOrder, b);
  128. });
  129. },
  130. toJSON: function(options) {
  131. var result = this.constructor.__super__.toJSON.call(this, options);
  132. return _.omit(result, 'checked');
  133. },
  134. isSelectable: function() {
  135. return this.get('status') != 'error' || this.get('cluster');
  136. },
  137. hasRole: function(role, onlyDeployedRoles) {
  138. var roles = onlyDeployedRoles ? this.get('roles') : _.union(this.get('roles'), this.get('pending_roles'));
  139. return _.contains(roles, role);
  140. },
  141. getRolesSummary: function() {
  142. var rolesMetaData = this.collection.cluster.get('release').get('roles_metadata');
  143. return _.map(this.sortedRoles(), function(role) {return rolesMetaData[role].name;}).join(', ');
  144. },
  145. getHardwareSummary: function() {
  146. return $.t('node_details.hdd') + ': ' + utils.showDiskSize(this.resource('hdd')) + ' \u00A0 ' + $.t('node_details.ram') + ': ' + utils.showMemorySize(this.resource('ram'));
  147. }
  148. });
  149. models.Nodes = Backbone.Collection.extend({
  150. constructorName: 'Nodes',
  151. model: models.Node,
  152. url: '/api/nodes',
  153. comparator: function(node) {
  154. return node.id;
  155. },
  156. hasChanges: function() {
  157. return !!this.filter(function(node) {
  158. return node.get('pending_addition') || node.get('pending_deletion') || node.get('pending_roles').length;
  159. }).length;
  160. },
  161. currentNodes: function() {
  162. return this.filter(function(node) {return !node.get('pending_addition');});
  163. },
  164. nodesAfterDeployment: function() {
  165. return this.filter(function(node) {return node.get('pending_addition') || !node.get('pending_deletion');});
  166. },
  167. nodesAfterDeploymentWithRole: function(role) {
  168. return _.filter(this.nodesAfterDeployment(), function(node) {return _.contains(_.union(node.get('roles'), node.get('pending_roles')), role);}).length;
  169. },
  170. resources: function(resourceName) {
  171. var resources = this.map(function(node) {return node.resource(resourceName);});
  172. return _.reduce(resources, function(sum, n) {return sum + n;}, 0);
  173. },
  174. getByIds: function(ids) {
  175. return this.filter(function(node) {return _.contains(ids, node.id);});
  176. },
  177. groupByAttribute: function(attr) {
  178. if (attr == 'roles') {
  179. return this.groupBy(function(node) {return node.getRolesSummary();});
  180. }
  181. if (attr == 'hardware') {
  182. return this.groupBy(function(node) {return node.getHardwareSummary();});
  183. }
  184. return this.groupBy(function(node) {return node.getRolesSummary() + '; \u00A0' + node.getHardwareSummary();});
  185. }
  186. });
  187. models.NodesStatistics = Backbone.Model.extend({
  188. constructorName: 'NodesStatistics',
  189. urlRoot: '/api/nodes/allocation/stats'
  190. });
  191. models.Task = Backbone.Model.extend({
  192. constructorName: 'Task',
  193. urlRoot: '/api/tasks',
  194. releaseId: function() {
  195. var id;
  196. try {
  197. id = this.get('result').release_info.release_id;
  198. } catch (ignore) {}
  199. return id;
  200. },
  201. groups: {
  202. release_setup: ['redhat_setup'],
  203. network: ['verify_networks', 'check_networks'],
  204. deployment: ['stop_deployment', 'deploy', 'reset_environment']
  205. },
  206. extendGroups: function(filters) {
  207. return _.union(utils.composeList(filters.name), _.flatten(_.map(utils.composeList(filters.group), _.bind(function(group) {return this.groups[group];}, this))));
  208. },
  209. match: function(filters) {
  210. filters = filters || {};
  211. var result = false;
  212. if (filters.group || filters.name) {
  213. if (_.contains(this.extendGroups(filters), this.get('name'))) {
  214. result = true;
  215. if (filters.status) {
  216. result = _.contains(utils.composeList(filters.status), this.get('status'));
  217. }
  218. if (filters.release) {
  219. result = result && this.releaseId() == filters.release;
  220. }
  221. }
  222. } else if (filters.status) {
  223. result = _.contains(utils.composeList(filters.status), this.get('status'));
  224. }
  225. return result;
  226. }
  227. });
  228. models.Tasks = Backbone.Collection.extend({
  229. constructorName: 'Tasks',
  230. model: models.Task,
  231. url: '/api/tasks',
  232. toJSON: function(options) {
  233. return this.pluck('id');
  234. },
  235. comparator: function(task) {
  236. return task.id;
  237. },
  238. filterTasks: function(filters) {
  239. return _.flatten(_.map(this.model.prototype.extendGroups(filters), function(name) {
  240. return this.filter(function(task) {
  241. return task.match(_.extend(_.omit(filters, 'group'), {name: name}));
  242. });
  243. }, this));
  244. },
  245. findTask: function(filters) {
  246. return this.filterTasks(filters)[0];
  247. },
  248. bindToView: function(view, filters, bindCallback, addCallback, removeCallback) {
  249. bindCallback = _.bind(bindCallback, view);
  250. addCallback = _.bind(addCallback || view.render, view);
  251. removeCallback = _.bind(removeCallback || view.render, view);
  252. function taskMatchesFilters(task) {
  253. return _.any(filters, task.match, task);
  254. }
  255. function onTaskAdd(task) {
  256. if (taskMatchesFilters(task)) {
  257. bindCallback(task);
  258. addCallback();
  259. }
  260. }
  261. function onTaskRemove(task) {
  262. if (taskMatchesFilters(task)) {
  263. removeCallback();
  264. }
  265. }
  266. this.each(function(task) {
  267. if (taskMatchesFilters(task)) {
  268. bindCallback(task);
  269. }
  270. });
  271. this.on('add', onTaskAdd, view);
  272. this.on('remove', onTaskRemove, view);
  273. }
  274. });
  275. models.Notification = Backbone.Model.extend({
  276. constructorName: 'Notification',
  277. urlRoot: '/api/notifications'
  278. });
  279. models.Notifications = Backbone.Collection.extend({
  280. constructorName: 'Notifications',
  281. model: models.Notification,
  282. url: '/api/notifications',
  283. comparator: function(notification) {
  284. return notification.id;
  285. }
  286. });
  287. models.Settings = Backbone.DeepModel.extend({
  288. constructorName: 'Settings',
  289. urlRoot: '/api/clusters/',
  290. cacheFor: 60 * 1000,
  291. isNew: function() {
  292. return false;
  293. },
  294. parse: function(response) {
  295. return response.editable;
  296. },
  297. toJSON: function(options) {
  298. var result = this.constructor.__super__.toJSON.call(this, options);
  299. _.each(result, function(group, groupName) {
  300. result[groupName].metadata = _.omit(group.metadata, 'disabled', 'visible');
  301. _.each(group, function(setting, settingName) {
  302. group[settingName] = _.omit(setting, 'disabled');
  303. _.each(setting.values, function(option, index) {
  304. setting.values[index] = _.omit(option, 'disabled');
  305. });
  306. });
  307. }, this);
  308. return {editable: result};
  309. },
  310. validate: function(attrs) {
  311. var errors = [];
  312. _.each(attrs, function(group, groupName) {
  313. _.each(group, function(setting, settingName) {
  314. if (setting.regex && setting.regex.source) {
  315. var regExp = new RegExp(setting.regex.source);
  316. if (!setting.value.match(regExp)) {
  317. errors.push({
  318. field: groupName + '.' + settingName,
  319. message: setting.regex.error
  320. });
  321. }
  322. }
  323. });
  324. });
  325. return errors.length ? errors : null;
  326. }
  327. });
  328. _.extend(models.Settings.prototype, cacheMixin);
  329. models.Disk = Backbone.Model.extend({
  330. constructorName: 'Disk',
  331. urlRoot: '/api/nodes/',
  332. parse: function(response) {
  333. response.volumes = new models.Volumes(response.volumes);
  334. response.volumes.disk = this;
  335. return response;
  336. },
  337. toJSON: function(options) {
  338. return _.extend(this.constructor.__super__.toJSON.call(this, options), {volumes: this.get('volumes').toJSON()});
  339. },
  340. getUnallocatedSpace: function(options) {
  341. options = options || {};
  342. var volumes = options.volumes || this.get('volumes');
  343. var allocatedSpace = volumes.reduce(function(sum, volume) {return volume.get('name') == options.skip ? sum : sum + volume.get('size');}, 0);
  344. return this.get('size') - allocatedSpace;
  345. },
  346. validate: function(attrs) {
  347. var error;
  348. var unallocatedSpace = this.getUnallocatedSpace({volumes: attrs.volumes});
  349. if (unallocatedSpace < 0) {
  350. error = 'Volume groups total size exceeds available space of ' + utils.formatNumber(unallocatedSpace * -1) + ' MB';
  351. }
  352. return error;
  353. }
  354. });
  355. models.Disks = Backbone.Collection.extend({
  356. constructorName: 'Disks',
  357. model: models.Disk,
  358. url: '/api/nodes/',
  359. comparator: function(disk) {
  360. return disk.get('name');
  361. }
  362. });
  363. models.Volume = Backbone.Model.extend({
  364. constructorName: 'Volume',
  365. urlRoot: '/api/volumes/',
  366. getMinimalSize: function(minimum) {
  367. var currentDisk = this.collection.disk;
  368. var groupAllocatedSpace = currentDisk.collection.reduce(function(sum, disk) {return disk.id == currentDisk.id ? sum : sum + disk.get('volumes').findWhere({name: this.get('name')}).get('size');}, 0, this);
  369. return minimum - groupAllocatedSpace;
  370. },
  371. validate: function(attrs, options) {
  372. var error;
  373. var min = this.getMinimalSize(options.minimum);
  374. if (_.isNaN(attrs.size)) {
  375. error = 'Invalid size';
  376. } else if (attrs.size < min) {
  377. error = 'The value is too low. You must allocate at least ' + utils.formatNumber(min) + ' MB';
  378. }
  379. return error;
  380. }
  381. });
  382. models.Volumes = Backbone.Collection.extend({
  383. constructorName: 'Volumes',
  384. model: models.Volume,
  385. url: '/api/volumes/'
  386. });
  387. models.Interface = Backbone.Model.extend({
  388. constructorName: 'Interface',
  389. bondingModes: ['active-backup', 'balance-slb', 'lacp-balance-tcp'],
  390. parse: function(response) {
  391. response.assigned_networks = new models.InterfaceNetworks(response.assigned_networks);
  392. response.assigned_networks.interface = this;
  393. return response;
  394. },
  395. toJSON: function(options) {
  396. return _.omit(_.extend(this.constructor.__super__.toJSON.call(this, options), {
  397. assigned_networks: this.get('assigned_networks').toJSON()
  398. }), 'checked');
  399. },
  400. isBond: function() {
  401. return this.get('type') == 'bond';
  402. },
  403. getSlaveInterfaces: function() {
  404. if (!this.isBond()) {return [this];}
  405. var slaveInterfaceNames = _.pluck(this.get('slaves'), 'name');
  406. return this.collection.filter(function(slaveInterface) {
  407. return _.contains(slaveInterfaceNames, slaveInterface.get('name'));
  408. });
  409. },
  410. validate: function() {
  411. var errors = [];
  412. var networks = new models.Networks(this.get('assigned_networks').invoke('getFullNetwork'));
  413. var untaggedNetworks = networks.filter(function(network) { return _.isNull(network.getVlanRange()); });
  414. // public and floating networks are allowed to be assigned to the same interface
  415. var maxUntaggedNetworksCount = networks.where({name: 'public'}).length && networks.where({name: 'floating'}).length ? 2 : 1;
  416. if (untaggedNetworks.length > maxUntaggedNetworksCount) {
  417. errors.push($.t('cluster_page.nodes_tab.configure_interfaces.validation.too_many_untagged_networks'));
  418. }
  419. return errors;
  420. }
  421. });
  422. models.Interfaces = Backbone.Collection.extend({
  423. constructorName: 'Interfaces',
  424. model: models.Interface,
  425. generateBondName: function() {
  426. var index, proposedName, base = 'ovs-bond';
  427. for (index = 0; true; index += 1) {
  428. proposedName = base + index;
  429. if (!this.where({name: proposedName}).length) {
  430. return proposedName;
  431. }
  432. }
  433. },
  434. comparator: function(ifc) {
  435. return [!ifc.isBond(), ifc.get('name')];
  436. }
  437. });
  438. models.InterfaceNetwork = Backbone.Model.extend({
  439. constructorName: 'InterfaceNetwork'
  440. });
  441. models.InterfaceNetworks = Backbone.Collection.extend({
  442. constructorName: 'InterfaceNetworks',
  443. model: models.InterfaceNetwork,
  444. preferredOrder: ['public', 'floating', 'storage', 'management', 'fixed'],
  445. comparator: function(network) {
  446. return _.indexOf(this.preferredOrder, network.get('name'));
  447. }
  448. });
  449. models.Network = Backbone.Model.extend({
  450. constructorName: 'Network'
  451. });
  452. models.Networks = Backbone.Collection.extend({
  453. constructorName: 'Networks',
  454. model: models.Network
  455. });
  456. models.NetworkingParameters = Backbone.Model.extend({
  457. constructorName: 'NetworkingParameters'
  458. });
  459. models.NetworkConfiguration = Backbone.Model.extend({
  460. constructorName: 'NetworkConfiguration',
  461. cacheFor: 60 * 1000,
  462. parse: function(response) {
  463. response.networks = new models.Networks(response.networks);
  464. response.networking_parameters = new models.NetworkingParameters(response.networking_parameters);
  465. return response;
  466. },
  467. toJSON: function() {
  468. return {
  469. networks: this.get('networks').toJSON(),
  470. networking_parameters: this.get('networking_parameters').toJSON()
  471. };
  472. },
  473. isNew: function() {
  474. return false;
  475. },
  476. validate: function(attrs) {
  477. var errors = {};
  478. var networksErrors = {};
  479. var networkingParametersErrors = {};
  480. // validate networks
  481. attrs.networks.each(function(network) {
  482. if (network.get('meta').configurable) {
  483. var networkErrors = {};
  484. if (network.get('meta').notation == 'ip_ranges') {
  485. var ipRangesErrors = utils.validateIpRanges(network.get('ip_ranges'), network.get('cidr'));
  486. if (ipRangesErrors.length) {
  487. networkErrors.ip_ranges = ipRangesErrors;
  488. }
  489. }
  490. _.extend(networkErrors, utils.validateCidr(network.get('cidr')));
  491. if (network.get('meta').use_gateway) {
  492. if (utils.validateIP(network.get('gateway'))) {
  493. networkErrors.gateway = $.t('cluster_page.network_tab.validation.invalid_gateway');
  494. } else if (!utils.validateIpCorrespondsToCIDR(network.get('cidr'), network.get('gateway'))) {
  495. networkErrors.gateway = $.t('cluster_page.network_tab.validation.gateway_is_out_of_ip_range');
  496. }
  497. }
  498. var forbiddenVlans = attrs.networks.map(function(net) {return net.id != network.id ? net.get('vlan_start') : null;});
  499. _.extend(networkErrors, utils.validateVlan(network.get('vlan_start'), forbiddenVlans, 'vlan_start'));
  500. if (!_.isEmpty(networkErrors)) {
  501. networksErrors[network.id] = networkErrors;
  502. }
  503. }
  504. }, this);
  505. if (!_.isEmpty(networksErrors)) {
  506. errors.networks = networksErrors;
  507. }
  508. // validate networking parameters
  509. var novaNetManager = attrs.networking_parameters.get('net_manager');
  510. if (novaNetManager) {
  511. networkingParametersErrors = _.extend(networkingParametersErrors, utils.validateCidr(attrs.networking_parameters.get('fixed_networks_cidr'), 'fixed_networks_cidr'));
  512. var fixedAmount = attrs.networking_parameters.get('fixed_networks_amount');
  513. var fixedVlan = attrs.networking_parameters.get('fixed_networks_vlan_start');
  514. if (!utils.isNaturalNumber(fixedAmount)) {
  515. networkingParametersErrors.fixed_networks_amount = $.t('cluster_page.network_tab.validation.invalid_amount');
  516. }
  517. var vlanErrors = utils.validateVlan(fixedVlan, attrs.networks.pluck('vlan_start'), 'fixed_networks_vlan_start', novaNetManager == 'VlanManager');
  518. _.extend(networkingParametersErrors, vlanErrors);
  519. if (_.isEmpty(vlanErrors)) {
  520. if (!networkingParametersErrors.fixed_networks_amount && fixedAmount > 4095 - fixedVlan) {
  521. networkingParametersErrors.fixed_networks_amount = $.t('cluster_page.network_tab.validation.need_more_vlan');
  522. }
  523. var vlanIntersection = false;
  524. _.each(_.compact(attrs.networks.pluck('vlan_start')), function(vlan) {
  525. if (utils.validateVlanRange(fixedVlan, fixedVlan + fixedAmount - 1, vlan)) {
  526. vlanIntersection = true;
  527. }
  528. });
  529. if (vlanIntersection) {
  530. networkingParametersErrors.fixed_networks_vlan_start = $.t('cluster_page.network_tab.validation.vlan_intersection');
  531. }
  532. }
  533. } else {
  534. var idRangeErrors = ['', ''];
  535. var segmentation = attrs.networking_parameters.get('segmentation_type');
  536. var idRangeAttr = segmentation == 'gre' ? 'gre_id_range' : 'vlan_range';
  537. var maxId = segmentation == 'gre' ? 65535 : 4094;
  538. var idRange = attrs.networking_parameters.get(idRangeAttr);
  539. var idStart = Number(idRange[0]), idEnd = Number(idRange[1]);
  540. if (!utils.isNaturalNumber(idStart) || idStart < 2 || idStart > maxId) {
  541. idRangeErrors[0] = $.t('cluster_page.network_tab.validation.invalid_id_start');
  542. } else if (!utils.isNaturalNumber(idEnd) || idEnd < 2 || idEnd > maxId) {
  543. idRangeErrors[1 ] = $.t('cluster_page.network_tab.validation.invalid_id_end');
  544. } else if (idStart > idEnd) {
  545. idRangeErrors[0] = idRangeErrors[1] = $.t('cluster_page.network_tab.validation.invalid_id_range');
  546. } else if (segmentation == 'vlan') {
  547. _.each(_.compact(attrs.networks.pluck('vlan_start')), function(vlan) {
  548. if (utils.validateVlanRange(idStart, idEnd, vlan)) {
  549. idRangeErrors[0] = $.t('cluster_page.network_tab.validation.vlan_intersection');
  550. }
  551. return idRangeErrors[0];
  552. });
  553. }
  554. if (_.compact(idRangeErrors).length) {
  555. networkingParametersErrors[idRangeAttr] = idRangeErrors;
  556. }
  557. if (!attrs.networking_parameters.get('base_mac').match(utils.regexes.mac)) {
  558. networkingParametersErrors.base_mac = $.t('cluster_page.network_tab.validation.invalid_mac');
  559. }
  560. var cidr = attrs.networking_parameters.get('internal_cidr');
  561. networkingParametersErrors = _.extend(networkingParametersErrors, utils.validateCidr(cidr, 'internal_cidr'));
  562. var gateway = attrs.networking_parameters.get('internal_gateway');
  563. if (utils.validateIP(gateway)) {
  564. networkingParametersErrors.internal_gateway = $.t('cluster_page.network_tab.validation.invalid_gateway');
  565. } else if (!utils.validateIpCorrespondsToCIDR(cidr, gateway)) {
  566. networkingParametersErrors.internal_gateway = $.t('cluster_page.network_tab.validation.gateway_is_out_of_internal_ip_range');
  567. }
  568. }
  569. var networkWithFloatingRange = attrs.networks.filter(function(network){ return network.get('meta').floating_range_var; })[0];
  570. var floatingRangesErrors = utils.validateIpRanges(attrs.networking_parameters.get('floating_ranges'), networkWithFloatingRange ? networkWithFloatingRange.get('cidr') : null);
  571. if (floatingRangesErrors.length) {
  572. networkingParametersErrors.floating_ranges = floatingRangesErrors;
  573. }
  574. var nameserverErrors = [];
  575. _.each(attrs.networking_parameters.get('dns_nameservers'), function(nameserver, i) {
  576. nameserverErrors.push(utils.validateIP(nameserver) ? $.t('cluster_page.network_tab.validation.invalid_nameserver') : null);
  577. });
  578. if (_.compact(nameserverErrors).length) {
  579. networkingParametersErrors.dns_nameservers = nameserverErrors;
  580. }
  581. if (!_.isEmpty(networkingParametersErrors)) {
  582. errors.networking_parameters = networkingParametersErrors;
  583. }
  584. return _.isEmpty(errors) ? null : errors;
  585. }
  586. });
  587. _.extend(models.NetworkConfiguration.prototype, cacheMixin);
  588. models.LogSource = Backbone.Model.extend({
  589. constructorName: 'LogSource',
  590. urlRoot: '/api/logs/sources'
  591. });
  592. models.LogSources = Backbone.Collection.extend({
  593. constructorName: 'LogSources',
  594. model: models.LogSource,
  595. url: '/api/logs/sources'
  596. });
  597. models.RedHatAccount = Backbone.Model.extend({
  598. constructorName: 'RedHatAccount',
  599. urlRoot: '/api/redhat/account',
  600. validate: function(attrs) {
  601. var errors = {};
  602. var regexes = {
  603. username: /^[A-z0-9\._%\+\-@]+$/,
  604. password: /^[\x21-\x7E]+$/,
  605. satellite: /(^(?:(?!\d+\.)[a-zA-Z0-9_\-]{1,63}\.?)+(?:[a-zA-Z]{2,})$)/,
  606. activation_key: /^[A-z0-9\*\.\+\-]+$/
  607. };
  608. var messages = {
  609. username: 'Invalid username',
  610. password: 'Invalid password',
  611. satellite: 'Only valid fully qualified domain name is allowed for the hostname field',
  612. activation_key: 'Invalid activation key'
  613. };
  614. var fields = ['username', 'password'];
  615. if (attrs.license_type == 'rhn') {
  616. fields = _.union(fields, ['satellite', 'activation_key']);
  617. }
  618. _.each(fields, function(attr) {
  619. if (!regexes[attr].test(attrs[attr])) {
  620. errors[attr] = messages[attr];
  621. }
  622. });
  623. return _.isEmpty(errors) ? null : errors;
  624. }
  625. });
  626. models.TestSet = Backbone.Model.extend({
  627. constructorName: 'TestSet',
  628. urlRoot: '/ostf/testsets'
  629. });
  630. models.TestSets = Backbone.Collection.extend({
  631. constructorName: 'TestSets',
  632. model: models.TestSet,
  633. url: '/ostf/testsets'
  634. });
  635. models.Test = Backbone.Model.extend({
  636. constructorName: 'Test',
  637. urlRoot: '/ostf/tests'
  638. });
  639. models.Tests = Backbone.Collection.extend({
  640. constructorName: 'Tests',
  641. model: models.Test,
  642. url: '/ostf/tests'
  643. });
  644. models.TestRun = Backbone.Model.extend({
  645. constructorName: 'TestRun',
  646. urlRoot: '/ostf/testruns'
  647. });
  648. models.TestRuns = Backbone.Collection.extend({
  649. constructorName: 'TestRuns',
  650. model: models.TestRun,
  651. url: '/ostf/testruns'
  652. });
  653. models.OSTFClusterMetadata = Backbone.Model.extend({
  654. constructorName: 'TestRun',
  655. urlRoot: '/api/ostf'
  656. });
  657. models.FuelKey = Backbone.Model.extend({
  658. constructorName: 'FuelKey',
  659. urlRoot: '/api/registration/key'
  660. });
  661. models.FuelVersion = Backbone.Model.extend({
  662. constructorName: 'FuelVersion',
  663. urlRoot: '/api/version'
  664. });
  665. models.LogsPackage = Backbone.Model.extend({
  666. constructorName: 'LogsPackage',
  667. urlRoot: '/api/logs/package'
  668. });
  669. models.CapacityLog = Backbone.Model.extend({
  670. constructorName: 'CapacityLog',
  671. urlRoot: '/api/capacity'
  672. });
  673. models.WizardModel = Backbone.DeepModel.extend({
  674. constructorName: 'WizardModel',
  675. parseConfig: function(config) {
  676. var result = {};
  677. _.each(config, _.bind(function(paneConfig, paneName) {
  678. result[paneName] = {};
  679. _.each(paneConfig, function(attributeConfig, attribute) {
  680. var attributeConfigValue = attributeConfig.value;
  681. if (_.isUndefined(attributeConfigValue)) {
  682. switch (attributeConfig.type) {
  683. case 'checkbox':
  684. attributeConfigValue = false;
  685. break;
  686. case 'radio':
  687. attributeConfigValue = _.first(attributeConfig.values).data;
  688. break;
  689. case 'password':
  690. case 'text':
  691. attributeConfigValue = "";
  692. break;
  693. }
  694. }
  695. result[paneName][attribute] = attributeConfigValue;
  696. });
  697. }, this));
  698. return result;
  699. },
  700. processConfig: function(config) {
  701. this.set(this.parseConfig(config));
  702. },
  703. restoreDefaultValues: function(panesToRestore) {
  704. var result = {};
  705. _.each(this.defaults, _.bind(function(paneConfig, paneName) {
  706. if (_.contains(panesToRestore, paneName)) {
  707. result[paneName] = this.defaults[paneName];
  708. }
  709. }, this));
  710. this.set(result);
  711. },
  712. initialize: function(config) {
  713. this.defaults = this.parseConfig(config);
  714. }
  715. });
  716. return models;
  717. });