Extend backend mock with port operations

Extended the backend mock with handlers to create, delete, and retrieve
ports. Added unit tests to the Ironic-API service for port creation and
deletion.

Change-Id: I8488a8662db2cf4804150405e1646efe1fc8b363
This commit is contained in:
Peter Piela 2017-06-12 13:12:57 -04:00
parent e626c392d1
commit d6f90b4d34
2 changed files with 246 additions and 27 deletions

View File

@ -29,10 +29,13 @@
ironicBackendMockService.$inject = [
'$httpBackend',
'horizon.framework.util.uuid.service'
'horizon.framework.util.uuid.service',
'horizon.dashboard.admin.ironic.validMacAddressPattern'
];
function ironicBackendMockService($httpBackend, uuidService) {
function ironicBackendMockService($httpBackend,
uuidService,
validMacAddressPattern) {
// Default node object.
var defaultNode = {
chassis_uuid: null,
@ -65,6 +68,20 @@
uuid: undefined
};
// Default port object.
var defaultPort = {
address: undefined,
created_at: null,
extra: {},
internal_info: {},
local_link_connection: {},
node_uuid: undefined,
portgroup_uuid: null,
pxe_enabled: true,
updated_at: null,
uuid: undefined
};
// Value of the next available system port
var nextAvailableSystemPort = 1024;
@ -92,12 +109,24 @@
getNode: getNode,
nodeGetConsoleUrl: nodeGetConsoleUrl,
getDrivers: getDrivers,
getImages: getImages
getImages: getImages,
getPort: getPort
};
var responseCode = {
SUCCESS: 200,
EMPTY_RESPONSE: 204,
BAD_QUERY: 400,
RESOURCE_NOT_FOUND: 404,
RESOURCE_CONFLICT: 409
};
// Dictionary of active nodes indexed by node-id (uuid and name)
var nodes = {};
// Dictionary of active ports indexed by port-uuid
var ports = {};
return service;
/**
@ -114,7 +143,7 @@
*
* @param {object} params - Dictionary of parameters that define
* the node to be created.
* @return {object | null} Node object, or null if the nde could
* @return {object|null} Node object, or null if the node could
* not be created.
*/
function createNode(params) {
@ -132,7 +161,8 @@
var backendNode = {
base: node,
consolePort: getNextAvailableSystemPort()
consolePort: getNextAvailableSystemPort(),
ports: {} // Indexed by port-uuid
};
nodes[node.uuid] = backendNode;
@ -148,22 +178,93 @@
* description Get a specified node.
*
* @param {string} nodeId - Uuid or name of the requested node.
* @return {object} Base node object.
* @return {object|null} Base node object, or null if the node
* does not exist.
*/
function getNode(nodeId) {
return angular.isDefined(nodes[nodeId]) ? nodes[nodeId].base : undefined;
return angular.isDefined(nodes[nodeId]) ? nodes[nodeId].base : null;
}
/*
/**
* @description Get the console-url for a specified node.
*
* @param {string} nodeId - Uuid or name of the node.
* @return {string} Console url if the console is enabled, null otherwise.
* @return {string|null} Console url if the console is enabled,
* null otherwise.
*/
function nodeGetConsoleUrl(nodeId) {
return nodes[nodeId].base.console_enabled
? service.params.consoleUrl + nodes[nodeId].consolePort
: undefined;
: null;
}
/**
* @description Test whether a mac address is being used by an
* existing port.
*
* @param {string} address - Mac address.
* @return {boolean} True if the mac address is being used by
* another port, otherwise false.
*/
function macAddressInUse(address) {
for (var uuid in ports) {
if (ports.hasOwnProperty(uuid) &&
angular.isDefined(ports[uuid].address)) {
if (ports[uuid].address === address) {
return true;
}
}
}
return false;
}
/**
* @description Create a backend managed port.
*
* @param {object} params - Dictionary of parameters that define
* the port to be created.
* @return {object|null} Port object, or null if the port could
* not be created.
*/
function createPort(params) {
var port = null;
var status = responseCode.BAD_QUERY;
if (angular.isDefined(params.address) &&
angular.isDefined(params.node_uuid) &&
params.address.match(validMacAddressPattern) &&
angular.isDefined(nodes[params.node_uuid])) {
if (macAddressInUse(params.address)) {
status = responseCode.RESOURCE_CONFLICT;
} else {
port = angular.copy(defaultPort);
angular.forEach(params, function(value, key) {
port[key] = value;
});
if (angular.isUndefined(port.uuid)) {
port.uuid = uuidService.generate();
}
ports[port.uuid] = port;
nodes[port.node_uuid].ports[port.uuid] = port;
status = responseCode.SUCCESS;
}
}
return [status, port];
}
/**
* description Get a specified port.
*
* @param {string} portUuid - Uuid of the requested port.
* @return {object|null} Port object, or null if the port
* does not exist.
*/
function getPort(portUuid) {
return angular.isDefined(ports[portUuid]) ? ports[portUuid] : null;
}
/**
@ -177,14 +278,14 @@
$httpBackend.whenPOST(/\/api\/ironic\/nodes\/$/)
.respond(function(method, url, data) {
var node = createNode(JSON.parse(data).node);
return [node ? 200 : 400, node];
return [node ? responseCode.SUCCESS : responseCode.BAD_QUERY, node];
});
// Delete node
$httpBackend.whenDELETE(/\/api\/ironic\/nodes\/$/)
.respond(function(method, url, data) {
var nodeId = JSON.parse(data).node;
var status = 400;
var status = responseCode.RESOURCE_NOT_FOUND;
if (angular.isDefined(nodes[nodeId])) {
var node = nodes[nodeId].base;
if (node.name !== null) {
@ -193,7 +294,7 @@
} else {
delete nodes[nodeId];
}
status = 204;
status = responseCode.EMPTY_RESPONSE;
}
return [status, ""];
});
@ -225,7 +326,7 @@
function _replaceItem(node, path, value) {
if (path === "/name" &&
node.name !== null) {
delete nodes[name];
delete nodes[node.name];
if (value !== null) {
nodes[value] = node;
}
@ -246,7 +347,7 @@
undefined,
['nodeId'])
.respond(function(method, url, data, headers, params) {
var status = 400;
var status = responseCode.RESOURCE_NOT_FOUND;
var node = service.getNode(params.nodeId);
if (angular.isDefined(node)) {
var patch = JSON.parse(data).patch;
@ -264,7 +365,7 @@
default:
}
});
status = 200;
status = responseCode.SUCCESS;
}
return [status, node];
});
@ -275,9 +376,9 @@
['nodeId'])
.respond(function(method, url, data, headers, params) {
if (angular.isDefined(nodes[params.nodeId])) {
return [200, nodes[params.nodeId].base];
return [responseCode.SUCCESS, nodes[params.nodeId].base];
} else {
return [400, null];
return [responseCode.RESOURCE_NOT_FOUND, null];
}
});
@ -296,7 +397,7 @@
var info = {
console_enabled: consoleEnabled,
console_info: consoleInfo};
return [200, info];
return [responseCode.SUCCESS, info];
});
// Set console
@ -307,38 +408,71 @@
.respond(function(method, url, data, headers, params) {
data = JSON.parse(data);
nodes[params.nodeId].base.console_enabled = data.enabled;
return [200, {}];
return [responseCode.SUCCESS, {}];
});
// Get the ports belonging to a specified node
$httpBackend.whenGET(/\/api\/ironic\/ports/)
.respond(200, []);
.respond(responseCode.SUCCESS, []);
// Get boot device
$httpBackend.whenGET(/\/api\/ironic\/nodes\/([^\/]+)\/boot_device$/,
undefined,
['nodeId'])
.respond(200, service.params.bootDevice);
.respond(responseCode.SUCCESS, service.params.bootDevice);
// Validate the interfaces associated with a specified node
$httpBackend.whenGET(/\/api\/ironic\/nodes\/([^\/]+)\/validate$/,
undefined,
['nodeId'])
.respond(200, []);
.respond(responseCode.SUCCESS, []);
// Get the currently available drivers
$httpBackend.whenGET(/\/api\/ironic\/drivers\/$/)
.respond(200, {drivers: drivers});
.respond(responseCode.SUCCESS, {drivers: drivers});
// Get driver properties
$httpBackend.whenGET(/\/api\/ironic\/drivers\/([^\/]+)\/properties$/,
undefined,
['driverName'])
.respond(200, []);
.respond(responseCode.SUCCESS, []);
// Get glance images
$httpBackend.whenGET(/\/api\/glance\/images/)
.respond(200, {items: images});
.respond(responseCode.SUCCESS, {items: images});
// Create port
$httpBackend.whenPOST(/\/api\/ironic\/ports\/$/)
.respond(function(method, url, data) {
return createPort(JSON.parse(data).port);
});
// Delete port
$httpBackend.whenDELETE(/\/api\/ironic\/ports\/$/)
.respond(function(method, url, data) {
var portUuid = JSON.parse(data).port_uuid;
var status = responseCode.RESOURCE_NOT_FOUND;
if (angular.isDefined(ports[portUuid])) {
delete ports[portUuid];
status = responseCode.EMPTY_RESPONSE;
}
return [status, ""];
});
// Get ports
$httpBackend.whenGET(/\/api\/ironic\/ports\/$/)
.respond(function(method, url, data) {
var nodeId = JSON.parse(data).node_id;
var status = responseCode.RESOURCE_NOT_FOUND;
var ports = [];
if (angular.isDefined(nodes[nodeId])) {
angular.forEach(nodes[nodeId].ports, function(port) {
ports.push(port);
});
status = responseCode.SUCCESS;
}
return [status, {ports: ports}];
});
}
/**

View File

@ -161,7 +161,7 @@
})
.then(function(node) {
expect(
ironicBackendMockService.getNode(node.uuid)).toBe(undefined);
ironicBackendMockService.getNode(node.uuid)).toBeNull();
})
.catch(failTest);
@ -317,6 +317,91 @@
ironicBackendMockService.flush();
});
it('createPort', function() {
var macAddr = '00:00:00:00:00:00';
var node;
createNode({driver: defaultDriver})
.then(function(createNode) {
node = createNode;
return ironicAPI.createPort({address: macAddr,
node_uuid: node.uuid});
})
.then(function(port) {
expect(port.address).toBe(macAddr);
expect(port.node_uuid).toBe(node.uuid);
expect(port)
.toEqual(ironicBackendMockService.getPort(port.uuid));
})
.catch(failTest);
ironicBackendMockService.flush();
});
it('createPort - missing input data', function() {
ironicAPI.createPort({})
.then(failTest);
ironicBackendMockService.flush();
});
it('createPort - bad input data', function() {
ironicAPI.createPort({address: "", node_uuid: ""})
.then(failTest);
ironicBackendMockService.flush();
});
it('createPort - duplicate mac address', function() {
var macAddr = '00:00:00:00:00:00';
var node;
createNode({driver: defaultDriver})
.then(function(createNode) {
node = createNode;
return ironicAPI.createPort({address: macAddr,
node_uuid: node.uuid});
})
.then(function(port) {
expect(port.address).toBe(macAddr);
expect(port.node_uuid).toBe(node.uuid);
expect(port)
.toEqual(ironicBackendMockService.getPort(port.uuid));
return ironicAPI.createPort({address: macAddr,
node_uuid: node.uuid});
})
.then(failTest);
ironicBackendMockService.flush();
});
it('deletePort', function() {
var macAddr = '00:00:00:00:00:00';
createNode({driver: defaultDriver})
.then(function(node) {
return ironicAPI.createPort({address: macAddr,
node_uuid: node.uuid});
})
.then(function(port) {
expect(port).toBeDefined();
expect(port)
.toEqual(ironicBackendMockService.getPort(port.uuid));
ironicAPI.deletePort(port.uuid).then(function() {
expect(ironicBackendMockService.getPort(port.uuid))
.toBeNull();
});
})
.catch(failTest);
ironicBackendMockService.flush();
});
it('deletePort - nonexistent port', function() {
ironicAPI.deletePort(0)
.then(failTest);
ironicBackendMockService.flush();
});
});
});
})();