Add functionality to view portgroups
This is the first in a series of reviews that will add functionality to manage portgroups within the Ironic UI. Specifically, this commit adds the following items: - API functions for listing, creating, and deleting portgroups - A tabular view of the portgroups associated with a node in the node-details/configuration table - The portgroup table utilizes expandable detail rows for giving the user access to additional information. Detail rows provide a scalable way to display additional information without resorting to a separate tab/page. - The node-details/configuration ports table has been reworked to take advantage of detail rows. - The batch and inividual delete-portgroup actions are working. I am looking for feedback on all aspects of the new functionality, including the use of detail rows in data tables. Change-Id: I4c150db4e56fa6970cc112c87cdc54cb3fbb28e5
This commit is contained in:
parent
7ce520e06b
commit
863e9e6295
@ -290,3 +290,40 @@ def port_update(request, port_uuid, patch):
|
|||||||
port = ironicclient(request).port.update(port_uuid, patch)
|
port = ironicclient(request).port.update(port_uuid, patch)
|
||||||
return dict([(f, getattr(port, f, ''))
|
return dict([(f, getattr(port, f, ''))
|
||||||
for f in res_fields.PORT_DETAILED_RESOURCE.fields])
|
for f in res_fields.PORT_DETAILED_RESOURCE.fields])
|
||||||
|
|
||||||
|
|
||||||
|
def portgroup_list(request, node_id):
|
||||||
|
"""List the portgroups associated with a given node.
|
||||||
|
|
||||||
|
:param request: HTTP request.
|
||||||
|
:param node_id: The UUID or name of the node.
|
||||||
|
:return: A full list of portgroups. (limit=0)
|
||||||
|
|
||||||
|
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.portgroup.html#ironicclient.v1.portgroup.PortgroupManager.list_portgroups
|
||||||
|
"""
|
||||||
|
return ironicclient(request).portgroup.list(node_id, limit=0, detail=True)
|
||||||
|
|
||||||
|
|
||||||
|
def portgroup_create(request, params):
|
||||||
|
"""Create a portgroup.
|
||||||
|
|
||||||
|
:param request: HTTP request.
|
||||||
|
:param params: Portgroup creation parameters.
|
||||||
|
:return: Portgroup.
|
||||||
|
|
||||||
|
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.portgroup.html#ironicclient.v1.portgroup.PortgroupManager.create
|
||||||
|
"""
|
||||||
|
portgroup_manager = ironicclient(request).portgroup
|
||||||
|
return portgroup_manager.create(**params)
|
||||||
|
|
||||||
|
|
||||||
|
def portgroup_delete(request, portgroup_id):
|
||||||
|
"""Delete a portgroup from the DB.
|
||||||
|
|
||||||
|
:param request: HTTP request.
|
||||||
|
:param portgroup_id: The UUID or name of the portgroup.
|
||||||
|
:return: Portgroup.
|
||||||
|
|
||||||
|
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.portgroup.html#ironicclient.v1.portgroup.PortgroupManager.delete
|
||||||
|
"""
|
||||||
|
return ironicclient(request).portgroup.delete(portgroup_id)
|
||||||
|
@ -310,3 +310,40 @@ class DriverProperties(generic.View):
|
|||||||
:return: Dictionary of properties
|
:return: Dictionary of properties
|
||||||
"""
|
"""
|
||||||
return ironic.driver_properties(request, driver_name)
|
return ironic.driver_properties(request, driver_name)
|
||||||
|
|
||||||
|
|
||||||
|
@urls.register
|
||||||
|
class Portgroups(generic.View):
|
||||||
|
|
||||||
|
url_regex = r'ironic/portgroups/$'
|
||||||
|
|
||||||
|
@rest_utils.ajax()
|
||||||
|
def get(self, request):
|
||||||
|
"""Get the list of portgroups associated with a specified node.
|
||||||
|
|
||||||
|
:param request: HTTP request.
|
||||||
|
:return: List of portgroups.
|
||||||
|
"""
|
||||||
|
portgroups = ironic.portgroup_list(request,
|
||||||
|
request.GET.get('node_id'))
|
||||||
|
return {
|
||||||
|
'portgroups': [i.to_dict() for i in portgroups]
|
||||||
|
}
|
||||||
|
|
||||||
|
@rest_utils.ajax(data_required=True)
|
||||||
|
def post(self, request):
|
||||||
|
"""Create a portgroup.
|
||||||
|
|
||||||
|
:param request: HTTP request.
|
||||||
|
:return: Portgroup.
|
||||||
|
"""
|
||||||
|
return ironic.portgroup_create(request, request.DATA).to_dict()
|
||||||
|
|
||||||
|
@rest_utils.ajax(data_required=True)
|
||||||
|
def delete(self, request):
|
||||||
|
"""Delete a portgroup.
|
||||||
|
|
||||||
|
:param request: HTTP request.
|
||||||
|
"""
|
||||||
|
return ironic.portgroup_delete(request,
|
||||||
|
request.DATA.get('portgroup_id'))
|
||||||
|
@ -82,6 +82,22 @@
|
|||||||
uuid: undefined
|
uuid: undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Default portgroup object.
|
||||||
|
var defaultPortgroup = {
|
||||||
|
address: null,
|
||||||
|
created_at: null,
|
||||||
|
extra: {},
|
||||||
|
internal_info: {},
|
||||||
|
mode: "active-backup",
|
||||||
|
name: null,
|
||||||
|
node_uuid: undefined,
|
||||||
|
ports: [],
|
||||||
|
properties: {},
|
||||||
|
standalone_ports_supported: true,
|
||||||
|
updated_at: null,
|
||||||
|
uuid: undefined
|
||||||
|
};
|
||||||
|
|
||||||
// Value of the next available system port
|
// Value of the next available system port
|
||||||
var nextAvailableSystemPort = 1024;
|
var nextAvailableSystemPort = 1024;
|
||||||
|
|
||||||
@ -110,7 +126,8 @@
|
|||||||
nodeGetConsoleUrl: nodeGetConsoleUrl,
|
nodeGetConsoleUrl: nodeGetConsoleUrl,
|
||||||
getDrivers: getDrivers,
|
getDrivers: getDrivers,
|
||||||
getImages: getImages,
|
getImages: getImages,
|
||||||
getPort: getPort
|
getPort: getPort,
|
||||||
|
getPortgroup: getPortgroup
|
||||||
};
|
};
|
||||||
|
|
||||||
var responseCode = {
|
var responseCode = {
|
||||||
@ -127,6 +144,9 @@
|
|||||||
// Dictionary of active ports indexed by port-uuid
|
// Dictionary of active ports indexed by port-uuid
|
||||||
var ports = {};
|
var ports = {};
|
||||||
|
|
||||||
|
// Dictionary of active portgroups indexed by portgroup-uuid
|
||||||
|
var portgroups = {};
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,7 +182,8 @@
|
|||||||
var backendNode = {
|
var backendNode = {
|
||||||
base: node,
|
base: node,
|
||||||
consolePort: getNextAvailableSystemPort(),
|
consolePort: getNextAvailableSystemPort(),
|
||||||
ports: {} // Indexed by port-uuid
|
ports: {}, // Indexed by port-uuid
|
||||||
|
portgroups: {} // Indexed by portgroup-uuid
|
||||||
};
|
};
|
||||||
|
|
||||||
nodes[node.uuid] = backendNode;
|
nodes[node.uuid] = backendNode;
|
||||||
@ -256,6 +277,47 @@
|
|||||||
return [status, port];
|
return [status, port];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Create a portgroup.
|
||||||
|
* This function is not yet fully implemented.
|
||||||
|
*
|
||||||
|
* @param {object} params - Dictionary of parameters that define
|
||||||
|
* the portgroup to be created.
|
||||||
|
* @return {object|null} Portgroup object, or null if the port could
|
||||||
|
* not be created.
|
||||||
|
*/
|
||||||
|
function createPortgroup(params) {
|
||||||
|
var portgroup = null;
|
||||||
|
var status = responseCode.BAD_QUERY;
|
||||||
|
if (angular.isDefined(nodes[params.node_uuid])) {
|
||||||
|
if (angular.isDefined(params.name) &&
|
||||||
|
params.name !== null &&
|
||||||
|
angular.isDefined(portgroups[params.name])) {
|
||||||
|
status = responseCode.RESOURCE_CONFLICT;
|
||||||
|
} else {
|
||||||
|
portgroup = angular.copy(defaultPortgroup);
|
||||||
|
angular.forEach(params, function(value, key) {
|
||||||
|
portgroup[key] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (angular.isUndefined(portgroup.uuid)) {
|
||||||
|
portgroup.uuid = uuidService.generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
portgroups[portgroup.uuid] = portgroup;
|
||||||
|
if (portgroup.name !== null) {
|
||||||
|
portgroups[portgroup.name] = portgroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes[portgroup.node_uuid].portgroups[portgroup.uuid] = portgroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = responseCode.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [status, portgroup];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* description Get a specified port.
|
* description Get a specified port.
|
||||||
*
|
*
|
||||||
@ -267,6 +329,18 @@
|
|||||||
return angular.isDefined(ports[portUuid]) ? ports[portUuid] : null;
|
return angular.isDefined(ports[portUuid]) ? ports[portUuid] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* description Get a specified portgroup.
|
||||||
|
*
|
||||||
|
* @param {string} portgroupId - Uuid or name of the requested portgroup.
|
||||||
|
* @return {object|null} Portgroup object, or null if the portgroup
|
||||||
|
* does not exist.
|
||||||
|
*/
|
||||||
|
function getPortgroup(portgroupId) {
|
||||||
|
return angular.isDefined(portgroups[portgroupId])
|
||||||
|
? portgroups[portgroupId] : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Initialize the Backend-Mock service.
|
* @description Initialize the Backend-Mock service.
|
||||||
* Create the handlers that intercept http requests.
|
* Create the handlers that intercept http requests.
|
||||||
@ -473,6 +547,45 @@
|
|||||||
}
|
}
|
||||||
return [status, {ports: ports}];
|
return [status, {ports: ports}];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Create portgroup
|
||||||
|
$httpBackend.whenPOST(/\/api\/ironic\/portgroups\/$/)
|
||||||
|
.respond(function(method, url, data) {
|
||||||
|
return createPortgroup(JSON.parse(data));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get portgroups. This function is not fully implemented.
|
||||||
|
$httpBackend.whenGET(/\/api\/ironic\/ports\/$/)
|
||||||
|
.respond(function(method, url, data) {
|
||||||
|
var nodeId = JSON.parse(data).node_id;
|
||||||
|
var status = responseCode.RESOURCE_NOT_FOUND;
|
||||||
|
var portgroups = [];
|
||||||
|
if (angular.isDefined(nodes[nodeId])) {
|
||||||
|
angular.forEach(nodes[nodeId].portgroups, function(portgroup) {
|
||||||
|
portgroups.push(portgroup);
|
||||||
|
});
|
||||||
|
status = responseCode.SUCCESS;
|
||||||
|
}
|
||||||
|
return [status, {portgroups: portgroups}];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete portgroup. This function is not yet implemented.
|
||||||
|
$httpBackend.whenDELETE(/\/api\/ironic\/portgroups\/$/)
|
||||||
|
.respond(function(method, url, data) {
|
||||||
|
var portgroupId = JSON.parse(data).portgroup_id;
|
||||||
|
var status = responseCode.RESOURCE_NOT_FOUND;
|
||||||
|
if (angular.isDefined(portgroups[portgroupId])) {
|
||||||
|
var portgroup = portgroups[portgroupId];
|
||||||
|
if (portgroup.name !== null) {
|
||||||
|
delete portgroups[portgroup.name];
|
||||||
|
delete portgroups[portgroup.uuid];
|
||||||
|
} else {
|
||||||
|
delete portgroups[portgroupId];
|
||||||
|
}
|
||||||
|
status = responseCode.EMPTY_RESPONSE;
|
||||||
|
}
|
||||||
|
return [status, ""];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,7 +57,10 @@
|
|||||||
setNodeProvisionState: setNodeProvisionState,
|
setNodeProvisionState: setNodeProvisionState,
|
||||||
updateNode: updateNode,
|
updateNode: updateNode,
|
||||||
updatePort: updatePort,
|
updatePort: updatePort,
|
||||||
validateNode: validateNode
|
validateNode: validateNode,
|
||||||
|
createPortgroup: createPortgroup,
|
||||||
|
getPortgroups: getPortgroups,
|
||||||
|
deletePortgroup: deletePortgroup
|
||||||
};
|
};
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
@ -513,6 +516,79 @@
|
|||||||
return $q.reject(msg);
|
return $q.reject(msg);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Retrieve a list of portgroups associated with a node.
|
||||||
|
*
|
||||||
|
* http://developer.openstack.org/api-ref/baremetal/#list-detailed-portgroups
|
||||||
|
*
|
||||||
|
* @param {string} nodeId – UUID or logical name of a node.
|
||||||
|
* @return {promise} List of portgroups.
|
||||||
|
*/
|
||||||
|
function getPortgroups(nodeId) {
|
||||||
|
return apiService.get('/api/ironic/portgroups/',
|
||||||
|
{params: {node_id: nodeId}})
|
||||||
|
.then(function(response) {
|
||||||
|
// Add id property to support delete operations
|
||||||
|
// using the deleteModalService
|
||||||
|
angular.forEach(response.data.portgroups, function(portgroup) {
|
||||||
|
portgroup.id = portgroup.uuid;
|
||||||
|
});
|
||||||
|
return response.data.portgroups;
|
||||||
|
})
|
||||||
|
.catch(function(response) {
|
||||||
|
var msg = interpolate(
|
||||||
|
gettext('Unable to retrieve Ironic node portgroups: %s'),
|
||||||
|
[response.data],
|
||||||
|
false);
|
||||||
|
toastService.add('error', msg);
|
||||||
|
return $q.reject(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Create a protgroup.
|
||||||
|
*
|
||||||
|
* http://developer.openstack.org/api-ref/baremetal/#create-portgroup
|
||||||
|
*
|
||||||
|
* @param {object} params – Object containing parameters that define
|
||||||
|
* the portgroup to be created.
|
||||||
|
* @return {promise} Promise containing the portgroup.
|
||||||
|
*/
|
||||||
|
function createPortgroup(params) {
|
||||||
|
return apiService.post('/api/ironic/portgroups/', params)
|
||||||
|
.then(function(response) {
|
||||||
|
toastService.add('success',
|
||||||
|
gettext('Portgroup successfully created'));
|
||||||
|
return response.data; // The newly created portgroup
|
||||||
|
})
|
||||||
|
.catch(function(response) {
|
||||||
|
var msg = interpolate(gettext('Unable to create portgroup: %s'),
|
||||||
|
[response.data],
|
||||||
|
false);
|
||||||
|
toastService.add('error', msg);
|
||||||
|
return $q.reject(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Delete a portgroup.
|
||||||
|
*
|
||||||
|
* http://developer.openstack.org/api-ref/baremetal/#delete-portgroup
|
||||||
|
*
|
||||||
|
* @param {string} portgroupId – UUID or name of the portgroup to be deleted.
|
||||||
|
* @return {promise} Promise.
|
||||||
|
*/
|
||||||
|
function deletePortgroup(portgroupId) {
|
||||||
|
return apiService.delete('/api/ironic/portgroups/',
|
||||||
|
{portgroup_id: portgroupId})
|
||||||
|
.catch(function(response) {
|
||||||
|
var msg = interpolate(gettext('Unable to delete portgroup: %s'),
|
||||||
|
[response.data],
|
||||||
|
false);
|
||||||
|
toastService.add('error', msg);
|
||||||
|
return $q.reject(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}());
|
}());
|
||||||
|
@ -20,12 +20,15 @@
|
|||||||
var IRONIC_API_PROPERTIES = [
|
var IRONIC_API_PROPERTIES = [
|
||||||
'createNode',
|
'createNode',
|
||||||
'createPort',
|
'createPort',
|
||||||
|
'createPortgroup',
|
||||||
'deleteNode',
|
'deleteNode',
|
||||||
'deletePort',
|
'deletePort',
|
||||||
|
'deletePortgroup',
|
||||||
'getDrivers',
|
'getDrivers',
|
||||||
'getDriverProperties',
|
'getDriverProperties',
|
||||||
'getNode',
|
'getNode',
|
||||||
'getNodes',
|
'getNodes',
|
||||||
|
'getPortgroups',
|
||||||
'getPortsWithNode',
|
'getPortsWithNode',
|
||||||
'getBootDevice',
|
'getBootDevice',
|
||||||
'nodeGetConsole',
|
'nodeGetConsole',
|
||||||
@ -402,6 +405,112 @@
|
|||||||
|
|
||||||
ironicBackendMockService.flush();
|
ironicBackendMockService.flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('createPortgroup', function() {
|
||||||
|
var node;
|
||||||
|
createNode({driver: defaultDriver})
|
||||||
|
.then(function(createNode) {
|
||||||
|
node = createNode;
|
||||||
|
return ironicAPI.createPortgroup({node_uuid: node.uuid});
|
||||||
|
})
|
||||||
|
.then(function(portgroup) {
|
||||||
|
expect(portgroup.node_uuid).toBe(node.uuid);
|
||||||
|
expect(portgroup)
|
||||||
|
.toEqual(ironicBackendMockService.getPortgroup(portgroup.uuid));
|
||||||
|
})
|
||||||
|
.catch(failTest);
|
||||||
|
|
||||||
|
ironicBackendMockService.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('createPortgroup - specify portgroup name', function() {
|
||||||
|
var node;
|
||||||
|
var portgroupName = "test-portgroup";
|
||||||
|
|
||||||
|
createNode({driver: defaultDriver})
|
||||||
|
.then(function(createNode) {
|
||||||
|
node = createNode;
|
||||||
|
return ironicAPI.createPortgroup({node_uuid: node.uuid,
|
||||||
|
name: portgroupName});
|
||||||
|
})
|
||||||
|
.then(function(portgroup) {
|
||||||
|
expect(portgroup.node_uuid).toBe(node.uuid);
|
||||||
|
expect(portgroup.name).toBe(portgroupName);
|
||||||
|
expect(portgroup)
|
||||||
|
.toEqual(ironicBackendMockService.getPortgroup(portgroup.uuid));
|
||||||
|
expect(portgroup)
|
||||||
|
.toEqual(ironicBackendMockService.getPortgroup(portgroup.name));
|
||||||
|
})
|
||||||
|
.catch(failTest);
|
||||||
|
|
||||||
|
ironicBackendMockService.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('createPortgroup - missing input data', function() {
|
||||||
|
ironicAPI.createPortgroup({})
|
||||||
|
.then(failTest);
|
||||||
|
|
||||||
|
ironicBackendMockService.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('createPort - bad input data', function() {
|
||||||
|
ironicAPI.createPort({node_uuid: ""})
|
||||||
|
.then(failTest);
|
||||||
|
|
||||||
|
ironicBackendMockService.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deletePortgroup', function() {
|
||||||
|
createNode({driver: defaultDriver})
|
||||||
|
.then(function(node) {
|
||||||
|
return ironicAPI.createPortgroup({node_uuid: node.uuid});
|
||||||
|
})
|
||||||
|
.then(function(portgroup) {
|
||||||
|
expect(portgroup).toBeDefined();
|
||||||
|
expect(portgroup)
|
||||||
|
.toEqual(ironicBackendMockService.getPortgroup(portgroup.uuid));
|
||||||
|
ironicAPI.deletePortgroup(portgroup.uuid).then(function() {
|
||||||
|
expect(ironicBackendMockService.getPortgroup(portgroup.uuid))
|
||||||
|
.toBeNull();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(failTest);
|
||||||
|
|
||||||
|
ironicBackendMockService.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deletePortgroup - by name', function() {
|
||||||
|
var portgroupName = "delete-portgroup";
|
||||||
|
|
||||||
|
createNode({driver: defaultDriver})
|
||||||
|
.then(function(node) {
|
||||||
|
return ironicAPI.createPortgroup({node_uuid: node.uuid,
|
||||||
|
name: portgroupName});
|
||||||
|
})
|
||||||
|
.then(function(portgroup) {
|
||||||
|
expect(portgroup).toBeDefined();
|
||||||
|
expect(portgroup)
|
||||||
|
.toEqual(ironicBackendMockService.getPortgroup(portgroup.uuid));
|
||||||
|
expect(portgroup)
|
||||||
|
.toEqual(ironicBackendMockService.getPortgroup(portgroup.name));
|
||||||
|
ironicAPI.deletePortgroup(portgroup.name).then(function() {
|
||||||
|
expect(ironicBackendMockService.getPortgroup(portgroup.name))
|
||||||
|
.toBeNull();
|
||||||
|
expect(ironicBackendMockService.getPortgroup(portgroup.uuid))
|
||||||
|
.toBeNull();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(failTest);
|
||||||
|
|
||||||
|
ironicBackendMockService.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deletePortgroup - nonexistent portgroup', function() {
|
||||||
|
ironicAPI.deletePortgroup(0)
|
||||||
|
.then(failTest);
|
||||||
|
|
||||||
|
ironicBackendMockService.flush();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
var service = {
|
var service = {
|
||||||
deleteNode: deleteNode,
|
deleteNode: deleteNode,
|
||||||
deletePort: deletePort,
|
deletePort: deletePort,
|
||||||
|
deletePortgroups: deletePortgroups,
|
||||||
setPowerState: setPowerState,
|
setPowerState: setPowerState,
|
||||||
setMaintenance: setMaintenance,
|
setMaintenance: setMaintenance,
|
||||||
setProvisionState: setProvisionState,
|
setProvisionState: setProvisionState,
|
||||||
@ -188,7 +189,7 @@
|
|||||||
'Successfully deleted ports "%s"',
|
'Successfully deleted ports "%s"',
|
||||||
ports.length),
|
ports.length),
|
||||||
error: ngettext('Unable to delete port "%s"',
|
error: ngettext('Unable to delete port "%s"',
|
||||||
'Unable to delete portss "%s"',
|
'Unable to delete ports "%s"',
|
||||||
ports.length)
|
ports.length)
|
||||||
},
|
},
|
||||||
deleteEntity: ironic.deletePort,
|
deleteEntity: ironic.deletePort,
|
||||||
@ -197,6 +198,32 @@
|
|||||||
return deleteModalService.open($rootScope, ports, context);
|
return deleteModalService.open($rootScope, ports, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deletePortgroups(portgroups) {
|
||||||
|
var context = {
|
||||||
|
labels: {
|
||||||
|
title: ngettext("Delete Portgroup",
|
||||||
|
"Delete Portgroups",
|
||||||
|
portgroups.length),
|
||||||
|
message: ngettext('Are you sure you want to delete portgroup "%s"? ' +
|
||||||
|
'This action cannot be undone.',
|
||||||
|
'Are you sure you want to delete portgroups "%s"? ' +
|
||||||
|
'This action cannot be undone.',
|
||||||
|
portgroups.length),
|
||||||
|
submit: ngettext("Delete Portgroup",
|
||||||
|
"Delete Portgroups",
|
||||||
|
portgroups.length),
|
||||||
|
success: ngettext('Successfully deleted portgroup "%s"',
|
||||||
|
'Successfully deleted portgroups "%s"',
|
||||||
|
portgroups.length),
|
||||||
|
error: ngettext('Unable to delete portgroup "%s"',
|
||||||
|
'Unable to delete portgroups "%s"',
|
||||||
|
portgroups.length)
|
||||||
|
},
|
||||||
|
deleteEntity: ironic.deletePortgroup
|
||||||
|
};
|
||||||
|
return deleteModalService.open($rootScope, portgroups, context);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @name horizon.dashboard.admin.ironic.actions.getPowerTransitions
|
* @name horizon.dashboard.admin.ironic.actions.getPowerTransitions
|
||||||
* @description Get the list of power transitions for a specified
|
* @description Get the list of power transitions for a specified
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
var path = basePath + '/node-details/sections/';
|
var path = basePath + '/node-details/sections/';
|
||||||
|
|
||||||
ctrl.noPortsText = gettext('No network ports have been defined');
|
ctrl.noPortsText = gettext('No network ports have been defined');
|
||||||
|
ctrl.noPortgroupsText = gettext('No portgroups have been defined');
|
||||||
|
|
||||||
ctrl.actions = actions;
|
ctrl.actions = actions;
|
||||||
ctrl.maintenanceService = maintenanceService;
|
ctrl.maintenanceService = maintenanceService;
|
||||||
@ -68,6 +69,9 @@
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
ctrl.portDetailsTemplateUrl = path + "port-details.html";
|
||||||
|
ctrl.portgroupDetailsTemplateUrl = path + "portgroup-details.html";
|
||||||
|
|
||||||
ctrl.node = null;
|
ctrl.node = null;
|
||||||
ctrl.nodeValidation = [];
|
ctrl.nodeValidation = [];
|
||||||
ctrl.nodeValidationMap = {}; // Indexed by interface
|
ctrl.nodeValidationMap = {}; // Indexed by interface
|
||||||
@ -75,6 +79,8 @@
|
|||||||
ctrl.nodePowerTransitions = [];
|
ctrl.nodePowerTransitions = [];
|
||||||
ctrl.ports = [];
|
ctrl.ports = [];
|
||||||
ctrl.portsSrc = [];
|
ctrl.portsSrc = [];
|
||||||
|
ctrl.portgroups = [];
|
||||||
|
ctrl.portgroupsSrc = [];
|
||||||
ctrl.basePath = basePath;
|
ctrl.basePath = basePath;
|
||||||
ctrl.re_uuid = new RegExp(validUuidPattern);
|
ctrl.re_uuid = new RegExp(validUuidPattern);
|
||||||
ctrl.isUuid = isUuid;
|
ctrl.isUuid = isUuid;
|
||||||
@ -85,6 +91,7 @@
|
|||||||
ctrl.editPort = editPort;
|
ctrl.editPort = editPort;
|
||||||
ctrl.refresh = refresh;
|
ctrl.refresh = refresh;
|
||||||
ctrl.toggleConsoleMode = toggleConsoleMode;
|
ctrl.toggleConsoleMode = toggleConsoleMode;
|
||||||
|
ctrl.deletePortgroups = deletePortgroups;
|
||||||
|
|
||||||
$scope.emptyObject = function(obj) {
|
$scope.emptyObject = function(obj) {
|
||||||
return angular.isUndefined(obj) || Object.keys(obj).length === 0;
|
return angular.isUndefined(obj) || Object.keys(obj).length === 0;
|
||||||
@ -112,6 +119,7 @@
|
|||||||
ctrl.nodePowerTransitions = actions.getPowerTransitions(ctrl.node);
|
ctrl.nodePowerTransitions = actions.getPowerTransitions(ctrl.node);
|
||||||
retrievePorts();
|
retrievePorts();
|
||||||
retrieveBootDevice();
|
retrieveBootDevice();
|
||||||
|
retrievePortgroups();
|
||||||
validateNode();
|
validateNode();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -161,6 +169,19 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name horizon.dashboard.admin.ironic.NodeDetailsController.retrievePortgroups
|
||||||
|
* @description Retrieve the port groups associated with the current node,
|
||||||
|
* and store them in the controller instance.
|
||||||
|
*
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
function retrievePortgroups() {
|
||||||
|
ironic.getPortgroups(ctrl.node.uuid).then(function(portgroups) {
|
||||||
|
ctrl.portgroupsSrc = portgroups;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.validateNode
|
* @name horizon.dashboard.admin.ironic.NodeDetailsController.validateNode
|
||||||
* @description Retrieve the ports associated with the current node,
|
* @description Retrieve the ports associated with the current node,
|
||||||
@ -256,6 +277,19 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name horizon.dashboard.admin.ironic.NodeDetailsController.portgroupDelete
|
||||||
|
* @description Delete a list of portgroups.
|
||||||
|
*
|
||||||
|
* @param {port []} portgroups – portgroups to be deleted.
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
function deletePortgroups(portgroups) {
|
||||||
|
actions.deletePortgroups(portgroups).then(function() {
|
||||||
|
ctrl.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.refresh
|
* @name horizon.dashboard.admin.ironic.NodeDetailsController.refresh
|
||||||
* @description Update node information
|
* @description Update node information
|
||||||
|
@ -69,6 +69,10 @@
|
|||||||
return $q.when(ports);
|
return $q.when(ports);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getPortgroups: function() {
|
||||||
|
return $q.when([]);
|
||||||
|
},
|
||||||
|
|
||||||
getBootDevice: function () {
|
getBootDevice: function () {
|
||||||
return $q.when(bootDevice);
|
return $q.when(bootDevice);
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<!-- General -->
|
<!-- General -->
|
||||||
<div class="col-md-6 status detail">
|
<div class="col-md-12 status detail">
|
||||||
<h4 translate>General</h4>
|
<h4 translate>General</h4>
|
||||||
<hr class="header_rule">
|
<hr class="header_rule">
|
||||||
<dl class="dl-horizontal">
|
<dl class="dl-horizontal">
|
||||||
@ -15,7 +15,9 @@
|
|||||||
<dd>{$ ctrl.node.created_at | date:'medium' | noValue $}</dd>
|
<dd>{$ ctrl.node.created_at | date:'medium' | noValue $}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
<!-- Ports -->
|
<!-- Ports -->
|
||||||
<div class="col-md-6 status detail">
|
<div class="col-md-6 status detail">
|
||||||
<h4 translate>Ports</h4>
|
<h4 translate>Ports</h4>
|
||||||
@ -26,7 +28,7 @@
|
|||||||
class="table table-striped table-rsp table-detail">
|
class="table table-striped table-rsp table-detail">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="4" class="action-col">
|
<th colspan="100" class="action-col">
|
||||||
<action-list uib-dropdown class="pull-right">
|
<action-list uib-dropdown class="pull-right">
|
||||||
<action button-type="split-button"
|
<action button-type="split-button"
|
||||||
action-classes="'btn btn-default btn-sm'"
|
action-classes="'btn btn-default btn-sm'"
|
||||||
@ -50,11 +52,15 @@
|
|||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
hz-select-all="ctrl.ports"/>
|
hz-select-all="ctrl.ports"/>
|
||||||
</th>
|
</th>
|
||||||
|
<th> </th>
|
||||||
<th translate class="rsp-p1" style="white-space:nowrap">
|
<th translate class="rsp-p1" style="white-space:nowrap">
|
||||||
MAC Address
|
MAC Address
|
||||||
</th>
|
</th>
|
||||||
<th translate class="rsp-p2" style="width:100%;">
|
<th translate class="rsp-p2" style="white-space:nowrap;">
|
||||||
Properties
|
PXE Enabled
|
||||||
|
</th>
|
||||||
|
<th translate class="rsp-p2" style="white-space:nowrap;">
|
||||||
|
Portgroup
|
||||||
</th>
|
</th>
|
||||||
<th translate class="actions_column">
|
<th translate class="actions_column">
|
||||||
Actions
|
Actions
|
||||||
@ -62,34 +68,24 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="port in ctrl.ports">
|
<tr ng-repeat-start="port in ctrl.ports">
|
||||||
<td class="multi_select_column">
|
<td class="multi_select_column">
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
hz-select="port"
|
hz-select="port"
|
||||||
ng-model="tCtrl.selections[port.id].checked"/>
|
ng-model="tCtrl.selections[port.id].checked"/>
|
||||||
|
<td>
|
||||||
|
<i class="fa fa-chevron-right"
|
||||||
|
hz-expand-detail="fa-chevron-right fa-chevron-down"
|
||||||
|
duration="200"
|
||||||
|
item="port"></i>
|
||||||
|
</td>
|
||||||
<td class="rsp-p1">
|
<td class="rsp-p1">
|
||||||
{$ port.address $}
|
{$ port.address $}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ul style="list-style:none;padding-left:0;">
|
{$ port.pxe_enabled $}
|
||||||
<li><strong>pxe_enabled</strong>: {$ port.pxe_enabled $}</li>
|
<td>
|
||||||
<li ng-repeat="propertyObject in
|
{$ port.portgroup_uuid | noValue $}
|
||||||
['local_link_connection',
|
|
||||||
'extra']"
|
|
||||||
ng-if="!emptyObject(port[propertyObject])">
|
|
||||||
<strong>{$ propertyObject $}</strong>:
|
|
||||||
<ul style="list-style:none;padding-left:10px;">
|
|
||||||
<li ng-repeat="(id, value) in port[propertyObject]">
|
|
||||||
<strong>{$ id $}</strong>:
|
|
||||||
<span ng-switch="id">
|
|
||||||
<a ng-switch-when="vif_port_id"
|
|
||||||
href="/dashboard/admin/networks/ports/{$ value $}/detail">{$ value $}</a>
|
|
||||||
<span ng-switch-default>{$ value $}</span>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</td>
|
</td>
|
||||||
<td class="actions_column">
|
<td class="actions_column">
|
||||||
<action-list uib-dropdown class="pull-right">
|
<action-list uib-dropdown class="pull-right">
|
||||||
@ -113,6 +109,13 @@
|
|||||||
</action-list>
|
</action-list>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr ng-repeat-end class="detail-row">
|
||||||
|
<td class="detail" colspan="100">
|
||||||
|
<hz-detail-row
|
||||||
|
template-url="ctrl.portDetailsTemplateUrl">
|
||||||
|
</hz-detail-row>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr hz-no-items
|
<tr hz-no-items
|
||||||
items="ctrl.ports"
|
items="ctrl.ports"
|
||||||
message="ctrl.noPortsText">
|
message="ctrl.noPortsText">
|
||||||
@ -120,6 +123,113 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Portgroups -->
|
||||||
|
<div class="col-md-6 status detail">
|
||||||
|
<h4 translate>Portgroups</h4>
|
||||||
|
<hr class="header_rule">
|
||||||
|
<table hz-table ng-cloak
|
||||||
|
st-table="ctrl.portgroups"
|
||||||
|
st-safe-src="ctrl.portgroupsSrc"
|
||||||
|
class="table table-striped table-rsp table-detail">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="100" class="action-col">
|
||||||
|
<action-list uib-dropdown class="pull-right">
|
||||||
|
<action button-type="split-button"
|
||||||
|
action-classes="'btn btn-default btn-sm'"
|
||||||
|
callback="">
|
||||||
|
{$ ::'Create portgroup' | translate $}
|
||||||
|
</action>
|
||||||
|
<menu>
|
||||||
|
<action button-type="menu-item"
|
||||||
|
callback="ctrl.deletePortgroups"
|
||||||
|
item="tCtrl.selected"
|
||||||
|
disabled="tCtrl.selected.length === 0">
|
||||||
|
<span class="fa fa-trash"></span>
|
||||||
|
{$ ::'Delete portgroups' | translate $}
|
||||||
|
</action>
|
||||||
|
</menu>
|
||||||
|
</action-list>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="multi_select_column">
|
||||||
|
<input type="checkbox"
|
||||||
|
hz-select-all="ctrl.portgroups"/>
|
||||||
|
</th>
|
||||||
|
<th> </th>
|
||||||
|
<th translate class="rsp-p1" style="width:100%;">
|
||||||
|
UUID
|
||||||
|
</th>
|
||||||
|
<th translate class="rsp-p2" style="white-space:nowrap;">
|
||||||
|
MAC Address
|
||||||
|
</th>
|
||||||
|
<th translate class="rsp-p2" style="width:white-space:nowrap;">
|
||||||
|
Name
|
||||||
|
</th>
|
||||||
|
<th translate class="actions_column">
|
||||||
|
Actions
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat-start="portgroup in ctrl.portgroups">
|
||||||
|
<td class="multi_select_column">
|
||||||
|
<input type="checkbox"
|
||||||
|
hz-select="portgroup"
|
||||||
|
ng-model="tCtrl.selections[portgroup.id].checked"/>
|
||||||
|
<td>
|
||||||
|
<i class="fa fa-chevron-right"
|
||||||
|
hz-expand-detail="fa-chevron-right fa-chevron-down"
|
||||||
|
duration="200"
|
||||||
|
item="portgroup"></i>
|
||||||
|
</td>
|
||||||
|
<td class="rsp-p1">
|
||||||
|
{$ portgroup.uuid $}
|
||||||
|
</td>
|
||||||
|
<td class="rsp-p1">
|
||||||
|
{$ portgroup.address | noValue $}
|
||||||
|
</td>
|
||||||
|
<td class="rsp-p1">
|
||||||
|
{$ portgroup.name | noValue $}
|
||||||
|
</td>
|
||||||
|
<td class="actions_column">
|
||||||
|
<action-list uib-dropdown class="pull-right">
|
||||||
|
<action button-type="split-button"
|
||||||
|
action-classes="'btn btn-default btn-sm'"
|
||||||
|
callback=""
|
||||||
|
item="port">
|
||||||
|
{$ ::'Edit portgroup' | translate $}
|
||||||
|
</action>
|
||||||
|
<menu>
|
||||||
|
<li role="presentation">
|
||||||
|
<a role="menuitem"
|
||||||
|
ng-click="ctrl.deletePortgroups([portgroup]);
|
||||||
|
$event.stopPropagation();
|
||||||
|
$event.preventDefault()">
|
||||||
|
<span class="fa fa-trash"></span>
|
||||||
|
<span>{$ :: 'Delete portgroup' | translate $}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</menu>
|
||||||
|
</action-list>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr ng-repeat-end class="detail-row">
|
||||||
|
<td class="detail" colspan="100">
|
||||||
|
<hz-detail-row
|
||||||
|
template-url="ctrl.portgroupDetailsTemplateUrl">
|
||||||
|
</hz-detail-row>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr hz-no-items
|
||||||
|
items="ctrl.portgroups"
|
||||||
|
message="ctrl.noPortgroupsText">
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h6 class="text-center" translate>Attributes</h6>
|
||||||
|
<hr style="margin:0;">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>UUID</dt>
|
||||||
|
<dd>{$ port.uuid $}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h6 class="text-center" translate>Local Link Connection</h6>
|
||||||
|
<hr style="margin:0;">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt ng-repeat-start="(id, value) in port.local_link_connection">{$ id $}</dt>
|
||||||
|
<dd ng-repeat-end>{$ value $}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h6 class="text-center" translate>Extra</h6>
|
||||||
|
<hr style="margin:0;">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt ng-repeat-start="(id, value) in port.extra">{$ id $}</dt>
|
||||||
|
<dd ng-repeat-end>
|
||||||
|
<span ng-switch="id">
|
||||||
|
<a ng-switch-when="vif_port_id"
|
||||||
|
href="/dashboard/admin/networks/ports/{$ value $}/detail">{$ value $}</a>
|
||||||
|
<span ng-switch-default>{$ value $}</span>
|
||||||
|
</span>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,28 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h6 class="text-center" translate>Attributes</h6>
|
||||||
|
<hr style="margin:0;">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>Mode</dt>
|
||||||
|
<dd>{$ portgroup.mode $}</dd>
|
||||||
|
<dt><abbr title="Standalone">SA</abbr> ports</dt>
|
||||||
|
<dd>{$ portgroup.standalone_ports_supported $}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h6 class="text-center" translate>Properties</h6>
|
||||||
|
<hr style="margin:0;">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt ng-repeat-start="(id, value) in portgroup.properties">{$ id $}</dt>
|
||||||
|
<dd ng-repeat-end>{$ value $}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h6 class="text-center" translate>Extra</h6>
|
||||||
|
<hr style="margin:0;">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt ng-repeat-start="(id, value) in portgroup.extra">{$ id $}</dt>
|
||||||
|
<dd ng-repeat-end>{$ value $}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
27
releasenotes/notes/view-portgroups-a3efb4407536caf2.yaml
Normal file
27
releasenotes/notes/view-portgroups-a3efb4407536caf2.yaml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Support has been added for viewing and managing the portgroups
|
||||||
|
associated with an Ironic node.
|
||||||
|
- |
|
||||||
|
A portgroup table has been added to the node-details/configuration tab.
|
||||||
|
- |
|
||||||
|
Each row in the table displays a single portgroup, and has column entries
|
||||||
|
for its UUID, MAC address, name, and number of ports. A dropdown menu
|
||||||
|
is also provided that contains actions that can be applied to the
|
||||||
|
portgroup.
|
||||||
|
- |
|
||||||
|
Detailed information for a portgroup is obtained by clicking the
|
||||||
|
detail-toggle-selector (right-chevron) located in its table row.
|
||||||
|
The additional information is displayed in a row expansion.
|
||||||
|
- |
|
||||||
|
The port table in node-details/configuration tab has been modified
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
* A column has been added that displays the UUID of the portgroup
|
||||||
|
to which the port belongs.
|
||||||
|
* The ``Properties`` column has been replaced with a column that
|
||||||
|
displays only the pxe_enabled property.
|
||||||
|
* Additional properties are displayed by clicking the
|
||||||
|
detail-toggle-selector for that port in a similar manner to the
|
||||||
|
portgroup table.
|
Loading…
Reference in New Issue
Block a user