From 2e6c49985f213de073d34ef151d883a9d64b6b2f Mon Sep 17 00:00:00 2001 From: Peter Piela Date: Wed, 26 Jul 2017 16:51:29 -0400 Subject: [PATCH] Add unit tests for creating and editing ports This change includes an initial set of unit tests for creating and editing ports. Change-Id: I48be40f0d34018a506507e7042c9ece1dc426775 --- .../base-port/base-port.controller.spec.js | 179 +++++++++++++ .../create-port/create-port.controller.js | 2 +- .../create-port.controller.spec.js | 134 ++++++++++ .../ironic/edit-port/edit-port.controller.js | 9 +- .../edit-port/edit-port.controller.spec.js | 250 ++++++++++++++++++ .../ironic/ironic.backend-mock.service.js | 132 +++++---- .../dashboard/admin/ironic/test-data.spec.js | 11 + 7 files changed, 663 insertions(+), 54 deletions(-) create mode 100644 ironic_ui/static/dashboard/admin/ironic/base-port/base-port.controller.spec.js create mode 100644 ironic_ui/static/dashboard/admin/ironic/create-port/create-port.controller.spec.js create mode 100644 ironic_ui/static/dashboard/admin/ironic/edit-port/edit-port.controller.spec.js diff --git a/ironic_ui/static/dashboard/admin/ironic/base-port/base-port.controller.spec.js b/ironic_ui/static/dashboard/admin/ironic/base-port/base-port.controller.spec.js new file mode 100644 index 00000000..d7c4c1d4 --- /dev/null +++ b/ironic_ui/static/dashboard/admin/ironic/base-port/base-port.controller.spec.js @@ -0,0 +1,179 @@ +/* + * Copyright 2017 Cray Inc. + * + * Licensed 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. + */ +(function () { + 'use strict'; + + describe('horizon.dashboard.admin.ironic.base-port', function () { + var uibModalInstance, ironicBackendMockService, ironicAPI; + var ctrl = {}; + + beforeEach(module('horizon.framework.util')); + + beforeEach(module('horizon.dashboard.admin.ironic')); + + beforeEach(module(function($provide) { + $provide.value('horizon.framework.widgets.toast.service', { + add: function() {} + }); + })); + + beforeEach(module('horizon.app.core.openstack-service-api')); + + beforeEach(module(function($provide) { + $provide.value('$uibModal', {}); + })); + + beforeEach(module(function($provide) { + uibModalInstance = { + dismiss: jasmine.createSpy() + }; + $provide.value('$uibModalInstance', uibModalInstance); + })); + + beforeEach(inject(function($injector) { + ironicBackendMockService = + $injector.get('horizon.dashboard.admin.ironic.backend-mock.service'); + ironicBackendMockService.init(); + + ironicAPI = + $injector.get('horizon.app.core.openstack-service-api.ironic'); + + ironicAPI.createNode( + {driver: ironicBackendMockService.params.defaultDriver}) + .then(function(response) { + var node = response.data; + var controller = $injector.get('$controller'); + controller('BasePortController', {ctrl: ctrl, + node: node}); + }); + ironicBackendMockService.flush(); + })); + + afterEach(function() { + ironicBackendMockService.postTest(); + }); + + it('controller should be defined', function () { + expect(ctrl).toBeDefined(); + }); + + it('base construction', function () { + expect(Object.getOwnPropertyNames(ctrl).sort()).toEqual( + BASE_PORT_CONTROLLER_PROPERTIES.sort()); + + angular.forEach( + ['address', 'pxeEnabled', 'portgroup_uuid'], + function(propertyName) { + expect(Object.keys(ctrl[propertyName])).toContain('value'); + }); + + expect(Object.keys(ctrl.extra)).toContain('properties'); + }); + + it('localLinkConnectionMgr', function () { + var props = ['port_id', 'switch_id', 'switch_info']; + angular.forEach( + props, + function(propertyName) { + expect(ctrl.localLinkConnection[propertyName].constructor.name) + .toBe('FormField'); + expect(Object.keys(ctrl.localLinkConnection[propertyName])) + .toContain('value'); + }); + + expect(Object.keys(ctrl.localLinkConnection.fields).sort()) + .toEqual(props.sort()); + + angular.forEach( + props, + function(propertyName) { + expect(ctrl.localLinkConnection[propertyName]) + .toEqual(ctrl.localLinkConnection.fields[propertyName]); + }); + + expect(ctrl.localLinkConnection.update).toBeDefined(); + expect(ctrl.localLinkConnection.toPortAttr).toBeDefined(); + expect(ctrl.localLinkConnection.setValues).toBeDefined(); + expect(ctrl.localLinkConnection.disable).toBeDefined(); + }); + + it('localLinkConnectionMgr.update', function () { + ctrl.localLinkConnection.update(); + expect(ctrl.localLinkConnection.port_id.required).toBe(false); + expect(ctrl.localLinkConnection.switch_id.required).toBe(false); + }); + + it('localLinkConnectionMgr.setValues', function () { + var values = {port_id: 'port-id', + switch_id: '00:00:00:00:00:00', + switch_info: 'switch-info'}; + ctrl.localLinkConnection.setValues(values); + angular.forEach( + Object.keys(values), + function(value, key) { + if (ctrl.localLinkConnection.hasOwnProperty(key)) { + expect(ctrl.localLinkConnection[key].value).toEqual(values[key]); + } + }); + }); + + it('localLinkConnectionMgr.update - port_id has value', function () { + ctrl.localLinkConnection.setValues({port_id: 'port-id'}); + ctrl.localLinkConnection.update(); + expect(ctrl.localLinkConnection.port_id.required).toBe(true); + expect(ctrl.localLinkConnection.switch_id.required).toBe(true); + }); + + it('localLinkConnectionMgr.update - switch_id has value', function () { + ctrl.localLinkConnection.setValues({switch_id: '00:00:00:00:00:00'}); + ctrl.localLinkConnection.update(); + expect(ctrl.localLinkConnection.port_id.required).toBe(true); + expect(ctrl.localLinkConnection.switch_id.required).toBe(true); + }); + + it('localLinkConnectionMgr.toPortAttr - no values', function () { + expect(ctrl.localLinkConnection.toPortAttr()).toBeNull(); + }); + + it('localLinkConnectionMgr.toPortAttr - values', function () { + var values = {port_id: 'port-id', + switch_id: '00:00:00:00:00:00', + switch_info: 'switch-info'}; + ctrl.localLinkConnection.setValues(values); + expect(ctrl.localLinkConnection.toPortAttr()).toEqual(values); + }); + + it('localLinkConnectionMgr.disable', function () { + function validateDisabled(state) { + angular.forEach( + ['port_id', 'switch_id', 'switch_info'], + function(propertyName) { + expect(ctrl.localLinkConnection[propertyName]). + toEqual(jasmine.objectContaining({disabled: state})); + }); + } + + validateDisabled(false); + ctrl.localLinkConnection.disable(); + validateDisabled(true); + }); + + it('cancel', function () { + ctrl.cancel(); + expect(uibModalInstance.dismiss).toHaveBeenCalledWith('cancel'); + }); + }); +})(); diff --git a/ironic_ui/static/dashboard/admin/ironic/create-port/create-port.controller.js b/ironic_ui/static/dashboard/admin/ironic/create-port/create-port.controller.js index 475c68d5..a8e3edce 100644 --- a/ironic_ui/static/dashboard/admin/ironic/create-port/create-port.controller.js +++ b/ironic_ui/static/dashboard/admin/ironic/create-port/create-port.controller.js @@ -56,7 +56,7 @@ ctrl.createPort = function() { var port = { extra: ctrl.extra.properties, - node_uuid: node.id, + node_uuid: node.uuid, address: ctrl.address.value }; diff --git a/ironic_ui/static/dashboard/admin/ironic/create-port/create-port.controller.spec.js b/ironic_ui/static/dashboard/admin/ironic/create-port/create-port.controller.spec.js new file mode 100644 index 00000000..c186abe3 --- /dev/null +++ b/ironic_ui/static/dashboard/admin/ironic/create-port/create-port.controller.spec.js @@ -0,0 +1,134 @@ +/* + * Copyright 2017 Cray Inc. + * + * Licensed 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. + */ +(function () { + 'use strict'; + + describe('horizon.dashboard.admin.ironic.create-port', function () { + var ironicBackendMockService, uibModalInstance, ironicAPI, controller, + rootScope, ironicEvents; + + beforeEach(module('horizon.dashboard.admin.ironic')); + + beforeEach(module('horizon.framework.util')); + + beforeEach(module(function($provide) { + $provide.value('$uibModal', {}); + })); + + beforeEach(module(function($provide) { + uibModalInstance = {}; + $provide.value('$uibModalInstance', uibModalInstance); + })); + + beforeEach(module(function($provide) { + $provide.value('horizon.framework.widgets.toast.service', { + add: function() {} + }); + })); + + beforeEach(module('horizon.app.core.openstack-service-api')); + + beforeEach(inject(function($injector) { + ironicBackendMockService = + $injector.get('horizon.dashboard.admin.ironic.backend-mock.service'); + ironicBackendMockService.init(); + + ironicAPI = + $injector.get('horizon.app.core.openstack-service-api.ironic'); + + controller = $injector.get('$controller'); + rootScope = $injector.get('$rootScope'); + ironicEvents = $injector.get('horizon.dashboard.admin.ironic.events'); + })); + + afterEach(function() { + ironicBackendMockService.postTest(); + }); + + function createController() { + return ironicAPI.createNode({ + driver: ironicBackendMockService.params.defaultDriver}) + .then(function(response) { + var node = response.data; + return {node: response.data, + ctrl: controller('CreatePortController', + {node: node})}; + }); + } + + it('controller should be defined', function () { + createController() + .then(function(data) { + expect(data.ctrl).toBeDefined(); + }) + .catch(function() { + fail(); + }); + ironicBackendMockService.flush(); + }); + + it('base construction', function () { + createController() + .then(function(data) { + var ctrl = data.ctrl; + var properties = angular.copy(BASE_PORT_CONTROLLER_PROPERTIES); + properties.push('modalTitle'); + properties.push('submitButtonTitle'); + properties.push('createPort'); + properties.push('submit'); + expect(Object.getOwnPropertyNames(ctrl).sort()).toEqual( + properties.sort()); + }) + .catch(function() { + fail(); + }); + ironicBackendMockService.flush(); + }); + + it('submit - success', function () { + var portParams = { + address: '00:00:00:00:00:00' + }; + + spyOn(ironicAPI, 'createPort').and.callThrough(); + spyOn(rootScope, '$emit'); + + uibModalInstance.close = function(port) { + expect(port.address).toEqual(portParams.address); + expect(port).toEqual( + ironicBackendMockService.getPort(port.uuid)); + expect(rootScope.$emit) + .toHaveBeenCalledWith(ironicEvents.CREATE_PORT_SUCCESS); + }; + + createController() + .then(function(data) { + var ctrl = data.ctrl; + angular.forEach( + portParams, + function(value, param) { + ctrl[param].value = value; + }); + ctrl.submit(); + expect(ironicAPI.createPort).toHaveBeenCalled(); + }) + .catch(function() { + fail(); + }); + ironicBackendMockService.flush(); + }); + }); +})(); diff --git a/ironic_ui/static/dashboard/admin/ironic/edit-port/edit-port.controller.js b/ironic_ui/static/dashboard/admin/ironic/edit-port/edit-port.controller.js index daf3dc48..32f37de3 100644 --- a/ironic_ui/static/dashboard/admin/ironic/edit-port/edit-port.controller.js +++ b/ironic_ui/static/dashboard/admin/ironic/edit-port/edit-port.controller.js @@ -105,9 +105,12 @@ patcher.buildPatch(port.pxe_enabled ? 'True' : 'False', ctrl.pxeEnabled.value, "/pxe_enabled"); - patcher.buildPatch(port.local_link_connection, - ctrl.localLinkConnection.toPortAttr(), - "/local_link_connection"); + var attr = ctrl.localLinkConnection.toPortAttr(); + if (attr) { + patcher.buildPatch(port.local_link_connection, + attr, + "/local_link_connection"); + } patcher.buildPatch(port.extra, ctrl.extra.properties, "/extra"); patcher.buildPatch(port.portgroup_uuid, ctrl.portgroup_uuid.value, diff --git a/ironic_ui/static/dashboard/admin/ironic/edit-port/edit-port.controller.spec.js b/ironic_ui/static/dashboard/admin/ironic/edit-port/edit-port.controller.spec.js new file mode 100644 index 00000000..2582efdd --- /dev/null +++ b/ironic_ui/static/dashboard/admin/ironic/edit-port/edit-port.controller.spec.js @@ -0,0 +1,250 @@ +/* + * Copyright 2017 Cray Inc. + * + * Licensed 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. + */ +(function () { + 'use strict'; + + describe('horizon.dashboard.admin.ironic.edit-port', function () { + var ironicBackendMockService, uibModalInstance, ironicAPI, controller, + rootScope, ironicEvents; + + beforeEach(module('horizon.dashboard.admin.ironic')); + + beforeEach(module('horizon.framework.util')); + + beforeEach(module(function($provide) { + $provide.value('$uibModal', {}); + })); + + beforeEach(module(function($provide) { + uibModalInstance = {}; + $provide.value('$uibModalInstance', uibModalInstance); + })); + + beforeEach(module(function($provide) { + $provide.value('horizon.framework.widgets.toast.service', { + add: function() {} + }); + })); + + beforeEach(module('horizon.app.core.openstack-service-api')); + + beforeEach(inject(function($injector) { + ironicBackendMockService = + $injector.get('horizon.dashboard.admin.ironic.backend-mock.service'); + ironicBackendMockService.init(); + + ironicAPI = + $injector.get('horizon.app.core.openstack-service-api.ironic'); + + controller = $injector.get('$controller'); + rootScope = $injector.get('$rootScope'); + ironicEvents = $injector.get('horizon.dashboard.admin.ironic.events'); + })); + + afterEach(function() { + ironicBackendMockService.postTest(); + }); + + function createController(nodeParams) { + if (angular.isUndefined(nodeParams)) { + nodeParams = {}; + } + + if (angular.isUndefined(nodeParams.driver)) { + nodeParams.driver = ironicBackendMockService.params.defaultDriver; + } + + return ironicAPI.createNode(nodeParams) + .then(function(response) { + return response.data; + }) + .then(function(node) { + return ironicAPI.createPort({address:'00:00:00:00:00:00', + node_uuid: node.uuid}) + .then(function(port) { + return {node: node, port: port}; + }); + }) + .then(function(data) { + return {node: data.node, + port: data.port, + ctrl: controller('EditPortController', + {node: data.node, + port: data.port})}; + }); + } + + it('controller should be defined', function () { + createController() + .then(function(data) { + expect(data.ctrl).toBeDefined(); + }) + .catch(function() { + fail(); + }); + ironicBackendMockService.flush(); + }); + + it('base construction', function () { + createController() + .then(function(data) { + var ctrl = data.ctrl; + var properties = angular.copy(BASE_PORT_CONTROLLER_PROPERTIES); + properties.push('modalTitle'); + properties.push('submitButtonTitle'); + properties.push('updatePort'); + properties.push('submit'); + expect(Object.getOwnPropertyNames(ctrl).sort()).toEqual( + properties.sort()); + expect(ctrl.address.disabled).toBe(false); + expect(ctrl.pxeEnabled.disabled).toBe(false); + angular.forEach(ctrl.localLinkConnection.fields, + function(field) { + expect(field.disabled).toBe(false); + }); + }) + .catch(function() { + fail(); + }); + ironicBackendMockService.flush(); + }); + + it('node in enroll state', function () { + createController() + .then(function(data) { + var ctrl = data.ctrl; + expect(data.node.provision_state).toBe('enroll'); + expect(ctrl.address.disabled).toBe(false); + expect(ctrl.pxeEnabled.disabled).toBe(false); + angular.forEach(ctrl.localLinkConnection.fields, + function(field) { + expect(field.disabled).toBe(false); + }); + }) + .catch(function() { + fail(); + }); + ironicBackendMockService.flush(); + }); + + it('node in active state', function () { + createController({provision_state: 'active'}) + .then(function(data) { + var ctrl = data.ctrl; + expect(data.node.provision_state).toBe('active'); + expect(ctrl.address.disabled).toBe(true); + expect(ctrl.pxeEnabled.disabled).toBe(true); + angular.forEach(ctrl.localLinkConnection.fields, + function(field) { + expect(field.disabled).toBe(true); + }); + }) + .catch(function() { + fail(); + }); + ironicBackendMockService.flush(); + }); + + it('node in available state', function () { + createController({provision_state: 'available'}) + .then(function(data) { + var ctrl = data.ctrl; + expect(data.node.provision_state).toBe('available'); + expect(ctrl.address.disabled).toBe(false); + expect(ctrl.pxeEnabled.disabled).toBe(true); + angular.forEach(ctrl.localLinkConnection.fields, + function(field) { + expect(field.disabled).toBe(true); + }); + }) + .catch(function() { + fail(); + }); + ironicBackendMockService.flush(); + }); + + it('node in maintenance mode', function () { + createController({provision_state: 'active', + maintenance: true}) + .then(function(data) { + var ctrl = data.ctrl; + expect(data.node.provision_state).toBe('active'); + expect(data.node.maintenance).toBe(true); + expect(ctrl.address.disabled).toBe(false); + expect(ctrl.pxeEnabled.disabled).toBe(false); + angular.forEach(ctrl.localLinkConnection.fields, + function(field) { + expect(field.disabled).toBe(false); + }); + }) + .catch(function() { + fail(); + }); + ironicBackendMockService.flush(); + }); + + it('updatePort - no change', function () { + createController() + .then(function(data) { + spyOn(ironicAPI, 'updatePort').and.callThrough(); + spyOn(rootScope, '$emit'); + + uibModalInstance.close = function(port) { + expect(port.address).toEqual(data.port.address); + expect(port).toEqual( + ironicBackendMockService.getPort(port.uuid)); + expect(rootScope.$emit) + .toHaveBeenCalledWith(ironicEvents.EDIT_PORT_SUCCESS); + }; + + var ctrl = data.ctrl; + ctrl.updatePort(); + expect(ironicAPI.updatePort) + .toHaveBeenCalledWith(data.port.uuid, []); + }) + .catch(function() { + fail(); + }); + ironicBackendMockService.flush(); + }); + + it('submit - change MAC address', function () { + var newAddress = '12:12:12:12:12:12'; + + createController() + .then(function(data) { + spyOn(ironicAPI, 'updatePort').and.callThrough(); + spyOn(rootScope, '$emit'); + + uibModalInstance.close = function(port) { + expect(port.address).toEqual(newAddress); + expect(port).toEqual( + ironicBackendMockService.getPort(port.uuid)); + expect(rootScope.$emit) + .toHaveBeenCalledWith(ironicEvents.EDIT_PORT_SUCCESS); + }; + + var ctrl = data.ctrl; + ctrl.address.value = newAddress; + ctrl.submit(); + }) + .catch(function() { + fail(); + }); + ironicBackendMockService.flush(); + }); + }); +})(); diff --git a/ironic_ui/static/dashboard/admin/ironic/ironic.backend-mock.service.js b/ironic_ui/static/dashboard/admin/ironic/ironic.backend-mock.service.js index 2901f347..ca23bbe1 100644 --- a/ironic_ui/static/dashboard/admin/ironic/ironic.backend-mock.service.js +++ b/ironic_ui/static/dashboard/admin/ironic/ironic.backend-mock.service.js @@ -402,46 +402,76 @@ return [status, ""]; }); - function _addItem(node, path, value) { - var parts = path.substring(1).split("/"); - var leaf = parts.pop(); - var obj = node; - for (var i = 0; i < parts.length; i++) { - var part = parts[i]; - if (angular.isUndefined(obj[part])) { - obj[part] = {}; + function _addItem(obj, path, value) { + var pathNames = path.substring(1).split("/"); + var leaf = pathNames.pop(); + var part = obj; + for (var i = 0; i < pathNames.length; i++) { + var name = pathNames[i]; + if (angular.isUndefined(part[name])) { + part[name] = {}; } - obj = obj[part]; + part = part[name]; } - obj[leaf] = value; + part[leaf] = value; } - function _removeItem(node, path) { - var parts = path.substring(1).split("/"); - var leaf = parts.pop(); - var obj = node; - for (var i = 0; i < parts.length; i++) { - obj = obj[parts[i]]; + function _removeItem(obj, path) { + var pathNames = path.substring(1).split("/"); + var leaf = pathNames.pop(); + var part = obj; + for (var i = 0; i < pathNames.length; i++) { + part = part[pathNames[i]]; } - delete obj[leaf]; + delete part[leaf]; } - function _replaceItem(node, path, value) { - if (path === "/name" && - node.name !== null) { - delete nodes[node.name]; - if (value !== null) { - nodes[value] = node; + function _replaceItem(obj, path, value, collection) { + // Special handling for changing the name of an object + // that is stored in a name-indexed collection. + if (path === "/name" && obj.name !== null) { + if (angular.isDefined(collection)) { + delete collection[obj.name]; + if (value !== null) { + collection[value] = obj; + } } } - var parts = path.substring(1).split("/"); - var leaf = parts.pop(); - var obj = node; - for (var i = 0; i < parts.length; i++) { - obj = obj[parts[i]]; + var pathNames = path.substring(1).split("/"); + var leaf = pathNames.pop(); + var part = obj; + for (var i = 0; i < pathNames.length; i++) { + part = part[pathNames[i]]; } - obj[leaf] = value; + part[leaf] = value; + } + + /** + * @description Apply a patch to a specified object. + * + * @param {object} obj - Object to be patched, e.g. node, port, ... + * @param {object} patch - Patch object. + * @param {object} collection - Optional. Collection to which the + * object belongs. Only required if the collection indexes the + * object by name. + * @return {void} + */ + function patchObject(obj, patch, collection) { + angular.forEach(patch, function(operation) { + switch (operation.op) { + case "add": + _addItem(obj, operation.path, operation.value); + break; + case "remove": + _removeItem(obj, operation.path); + break; + case "replace": + _replaceItem(obj, operation.path, operation.value, collection); + break; + default: + } + }); } // Update node @@ -453,21 +483,7 @@ var status = responseCode.RESOURCE_NOT_FOUND; var node = service.getNode(params.nodeId); if (angular.isDefined(node)) { - var patch = JSON.parse(data).patch; - angular.forEach(patch, function(operation) { - switch (operation.op) { - case "add": - _addItem(node, operation.path, operation.value); - break; - case "remove": - _removeItem(node, operation.path); - break; - case "replace": - _replaceItem(node, operation.path, operation.value); - break; - default: - } - }); + patchObject(node, JSON.parse(data).patch, nodes); status = responseCode.SUCCESS; } return [status, node]; @@ -520,9 +536,9 @@ ['nodeId']) .respond(function(method, url, data, headers, params) { if (angular.isDefined(nodes[params.nodeId])) { - return [200, nodes[params.nodeId].bootDevice]; + return [responseCode.SUCCESS, nodes[params.nodeId].bootDevice]; } else { - return [400, null]; + return [responseCode.BAD_QUERY, null]; } }); @@ -533,9 +549,10 @@ ['nodeId']) .respond(function(method, url, data, headers, params) { if (angular.isDefined(nodes[params.nodeId])) { - return [200, nodes[params.nodeId].supportedBootDevices]; + return [responseCode.SUCCESS, + nodes[params.nodeId].supportedBootDevices]; } else { - return [400, null]; + return [responseCode.BAD_QUERY, null]; } }); @@ -546,7 +563,7 @@ ['nodeId']) .respond(function(method, url, data, headers, params) { data = JSON.parse(data); - var status = 404; + var status = responseCode.RESOURCE_NOT_FOUND; if (angular.isDefined(nodes[params.nodeId])) { var node = nodes[params.nodeId]; if (node.supportedBootDevices.indexOf(data.boot_device) !== -1) { @@ -554,7 +571,7 @@ if (angular.isDefined(data.persistent)) { node.bootDevice.persistent = data.persistent; } - status = 200; + status = responseCode.SUCCESS; } } return [status, null]; @@ -598,6 +615,21 @@ return [status, ""]; }); + // Update port + $httpBackend.whenPATCH(/\/api\/ironic\/ports\/([^\/]+)$/, + undefined, + undefined, + ['portId']) + .respond(function(method, url, data, headers, params) { + var status = responseCode.RESOURCE_NOT_FOUND; + var port = service.getPort(params.portId); + if (angular.isDefined(port)) { + patchObject(port, JSON.parse(data).patch); + status = responseCode.SUCCESS; + } + return [status, port]; + }); + // Get ports $httpBackend.whenGET(/\/api\/ironic\/ports\//) .respond(function(method, url, data, header, params) { diff --git a/ironic_ui/static/dashboard/admin/ironic/test-data.spec.js b/ironic_ui/static/dashboard/admin/ironic/test-data.spec.js index ca4c67f0..bece3eab 100644 --- a/ironic_ui/static/dashboard/admin/ironic/test-data.spec.js +++ b/ironic_ui/static/dashboard/admin/ironic/test-data.spec.js @@ -41,7 +41,18 @@ var BASE_NODE_CONTROLLER_PROPERTIES = [ 'submitButtonTitle', 'validHostNameRegex']; +/* exported BASE_PORT_CONTROLLER_PROPERTIES */ + +var BASE_PORT_CONTROLLER_PROPERTIES = [ + 'address', + 'cancel', + 'extra', + 'localLinkConnection', + 'pxeEnabled', + 'portgroup_uuid']; + /* exported BASE_PORTGROUP_CONTROLLER_PROPERTIES */ + var BASE_PORTGROUP_CONTROLLER_PROPERTIES = [ 'address', 'cancel',