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)
|
||||
return dict([(f, getattr(port, f, ''))
|
||||
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 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
|
||||
};
|
||||
|
||||
// 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
|
||||
var nextAvailableSystemPort = 1024;
|
||||
|
||||
@ -110,7 +126,8 @@
|
||||
nodeGetConsoleUrl: nodeGetConsoleUrl,
|
||||
getDrivers: getDrivers,
|
||||
getImages: getImages,
|
||||
getPort: getPort
|
||||
getPort: getPort,
|
||||
getPortgroup: getPortgroup
|
||||
};
|
||||
|
||||
var responseCode = {
|
||||
@ -127,6 +144,9 @@
|
||||
// Dictionary of active ports indexed by port-uuid
|
||||
var ports = {};
|
||||
|
||||
// Dictionary of active portgroups indexed by portgroup-uuid
|
||||
var portgroups = {};
|
||||
|
||||
return service;
|
||||
|
||||
/**
|
||||
@ -162,7 +182,8 @@
|
||||
var backendNode = {
|
||||
base: node,
|
||||
consolePort: getNextAvailableSystemPort(),
|
||||
ports: {} // Indexed by port-uuid
|
||||
ports: {}, // Indexed by port-uuid
|
||||
portgroups: {} // Indexed by portgroup-uuid
|
||||
};
|
||||
|
||||
nodes[node.uuid] = backendNode;
|
||||
@ -256,6 +277,47 @@
|
||||
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.
|
||||
*
|
||||
@ -267,6 +329,18 @@
|
||||
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.
|
||||
* Create the handlers that intercept http requests.
|
||||
@ -473,6 +547,45 @@
|
||||
}
|
||||
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,
|
||||
updateNode: updateNode,
|
||||
updatePort: updatePort,
|
||||
validateNode: validateNode
|
||||
validateNode: validateNode,
|
||||
createPortgroup: createPortgroup,
|
||||
getPortgroups: getPortgroups,
|
||||
deletePortgroup: deletePortgroup
|
||||
};
|
||||
|
||||
return service;
|
||||
@ -513,6 +516,79 @@
|
||||
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 = [
|
||||
'createNode',
|
||||
'createPort',
|
||||
'createPortgroup',
|
||||
'deleteNode',
|
||||
'deletePort',
|
||||
'deletePortgroup',
|
||||
'getDrivers',
|
||||
'getDriverProperties',
|
||||
'getNode',
|
||||
'getNodes',
|
||||
'getPortgroups',
|
||||
'getPortsWithNode',
|
||||
'getBootDevice',
|
||||
'nodeGetConsole',
|
||||
@ -402,6 +405,112 @@
|
||||
|
||||
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 = {
|
||||
deleteNode: deleteNode,
|
||||
deletePort: deletePort,
|
||||
deletePortgroups: deletePortgroups,
|
||||
setPowerState: setPowerState,
|
||||
setMaintenance: setMaintenance,
|
||||
setProvisionState: setProvisionState,
|
||||
@ -188,7 +189,7 @@
|
||||
'Successfully deleted ports "%s"',
|
||||
ports.length),
|
||||
error: ngettext('Unable to delete port "%s"',
|
||||
'Unable to delete portss "%s"',
|
||||
'Unable to delete ports "%s"',
|
||||
ports.length)
|
||||
},
|
||||
deleteEntity: ironic.deletePort,
|
||||
@ -197,6 +198,32 @@
|
||||
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
|
||||
* @description Get the list of power transitions for a specified
|
||||
|
@ -53,6 +53,7 @@
|
||||
var path = basePath + '/node-details/sections/';
|
||||
|
||||
ctrl.noPortsText = gettext('No network ports have been defined');
|
||||
ctrl.noPortgroupsText = gettext('No portgroups have been defined');
|
||||
|
||||
ctrl.actions = actions;
|
||||
ctrl.maintenanceService = maintenanceService;
|
||||
@ -68,6 +69,9 @@
|
||||
}
|
||||
];
|
||||
|
||||
ctrl.portDetailsTemplateUrl = path + "port-details.html";
|
||||
ctrl.portgroupDetailsTemplateUrl = path + "portgroup-details.html";
|
||||
|
||||
ctrl.node = null;
|
||||
ctrl.nodeValidation = [];
|
||||
ctrl.nodeValidationMap = {}; // Indexed by interface
|
||||
@ -75,6 +79,8 @@
|
||||
ctrl.nodePowerTransitions = [];
|
||||
ctrl.ports = [];
|
||||
ctrl.portsSrc = [];
|
||||
ctrl.portgroups = [];
|
||||
ctrl.portgroupsSrc = [];
|
||||
ctrl.basePath = basePath;
|
||||
ctrl.re_uuid = new RegExp(validUuidPattern);
|
||||
ctrl.isUuid = isUuid;
|
||||
@ -85,6 +91,7 @@
|
||||
ctrl.editPort = editPort;
|
||||
ctrl.refresh = refresh;
|
||||
ctrl.toggleConsoleMode = toggleConsoleMode;
|
||||
ctrl.deletePortgroups = deletePortgroups;
|
||||
|
||||
$scope.emptyObject = function(obj) {
|
||||
return angular.isUndefined(obj) || Object.keys(obj).length === 0;
|
||||
@ -112,6 +119,7 @@
|
||||
ctrl.nodePowerTransitions = actions.getPowerTransitions(ctrl.node);
|
||||
retrievePorts();
|
||||
retrieveBootDevice();
|
||||
retrievePortgroups();
|
||||
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
|
||||
* @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
|
||||
* @description Update node information
|
||||
|
@ -69,6 +69,10 @@
|
||||
return $q.when(ports);
|
||||
},
|
||||
|
||||
getPortgroups: function() {
|
||||
return $q.when([]);
|
||||
},
|
||||
|
||||
getBootDevice: function () {
|
||||
return $q.when(bootDevice);
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="row">
|
||||
|
||||
<!-- General -->
|
||||
<div class="col-md-6 status detail">
|
||||
<div class="col-md-12 status detail">
|
||||
<h4 translate>General</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
@ -15,7 +15,9 @@
|
||||
<dd>{$ ctrl.node.created_at | date:'medium' | noValue $}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Ports -->
|
||||
<div class="col-md-6 status detail">
|
||||
<h4 translate>Ports</h4>
|
||||
@ -26,7 +28,7 @@
|
||||
class="table table-striped table-rsp table-detail">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="4" class="action-col">
|
||||
<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'"
|
||||
@ -50,11 +52,15 @@
|
||||
<input type="checkbox"
|
||||
hz-select-all="ctrl.ports"/>
|
||||
</th>
|
||||
<th> </th>
|
||||
<th translate class="rsp-p1" style="white-space:nowrap">
|
||||
MAC Address
|
||||
</th>
|
||||
<th translate class="rsp-p2" style="width:100%;">
|
||||
Properties
|
||||
<th translate class="rsp-p2" style="white-space:nowrap;">
|
||||
PXE Enabled
|
||||
</th>
|
||||
<th translate class="rsp-p2" style="white-space:nowrap;">
|
||||
Portgroup
|
||||
</th>
|
||||
<th translate class="actions_column">
|
||||
Actions
|
||||
@ -62,34 +68,24 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="port in ctrl.ports">
|
||||
<tr ng-repeat-start="port in ctrl.ports">
|
||||
<td class="multi_select_column">
|
||||
<input type="checkbox"
|
||||
hz-select="port"
|
||||
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">
|
||||
{$ port.address $}
|
||||
</td>
|
||||
<td>
|
||||
<ul style="list-style:none;padding-left:0;">
|
||||
<li><strong>pxe_enabled</strong>: {$ port.pxe_enabled $}</li>
|
||||
<li ng-repeat="propertyObject in
|
||||
['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>
|
||||
{$ port.pxe_enabled $}
|
||||
<td>
|
||||
{$ port.portgroup_uuid | noValue $}
|
||||
</td>
|
||||
<td class="actions_column">
|
||||
<action-list uib-dropdown class="pull-right">
|
||||
@ -113,6 +109,13 @@
|
||||
</action-list>
|
||||
</td>
|
||||
</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
|
||||
items="ctrl.ports"
|
||||
message="ctrl.noPortsText">
|
||||
@ -120,6 +123,113 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</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 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