Add the capability to associate ports with portgroups
The create/edit port elements have been modified to enable specification of an associated portgroup. The portgroup table in the node-detail/configuration tab has been modeified to show the number of ports for each portgroup. Change-Id: I851b07110bcf85cce8ba1351509d4a8afcc9cd60
This commit is contained in:
parent
8b1d09c454
commit
386b6783cf
@ -357,3 +357,15 @@ def portgroup_delete(request, portgroup_id):
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.portgroup.html#ironicclient.v1.portgroup.PortgroupManager.delete
|
||||
"""
|
||||
return ironicclient(request).portgroup.delete(portgroup_id)
|
||||
|
||||
|
||||
def portgroup_get_ports(request, portgroup_id):
|
||||
"""Get the ports associated with a specified portgroup.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param portgroup_id: The UUID or name of the portgroup.
|
||||
:return: List of ports.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.portgroup.html#ironicclient.v1.portgroup.PortgroupManager.list_ports
|
||||
"""
|
||||
return ironicclient(request).portgroup.list_ports(portgroup_id)
|
||||
|
@ -379,3 +379,23 @@ class Portgroups(generic.View):
|
||||
"""
|
||||
return ironic.portgroup_delete(request,
|
||||
request.DATA.get('portgroup_id'))
|
||||
|
||||
|
||||
@urls.register
|
||||
class PortgroupPorts(generic.View):
|
||||
|
||||
url_regex = r'ironic/portgroups/(?P<portgroup_id>{})/ports$'. \
|
||||
format(LOGICAL_NAME_PATTERN)
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, portgroup_id):
|
||||
"""Get the ports for a specified portgroup.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: UUID or name of portgroup.
|
||||
:return: List of port objects.
|
||||
"""
|
||||
ports = ironic.portgroup_get_ports(request, portgroup_id)
|
||||
return {
|
||||
'ports': [i.to_dict() for i in ports]
|
||||
}
|
||||
|
@ -28,7 +28,9 @@
|
||||
'horizon.dashboard.admin.ironic.validMacAddressPattern',
|
||||
'horizon.dashboard.admin.ironic.validDatapathIdPattern',
|
||||
'horizon.dashboard.admin.ironic.form-field.service',
|
||||
'ctrl'
|
||||
'horizon.app.core.openstack-service-api.ironic',
|
||||
'ctrl',
|
||||
'node'
|
||||
];
|
||||
|
||||
/**
|
||||
@ -138,9 +140,12 @@
|
||||
validMacAddressPattern,
|
||||
validDatapathIdPattern,
|
||||
formFieldService,
|
||||
ctrl) {
|
||||
ironic,
|
||||
ctrl,
|
||||
node) {
|
||||
ctrl.port = {
|
||||
extra: {}
|
||||
extra: {},
|
||||
node_uuid: node.uuid
|
||||
};
|
||||
|
||||
ctrl.address = new formFieldService.FormField({
|
||||
@ -162,12 +167,34 @@
|
||||
options: ['True', 'False'],
|
||||
value: 'True'});
|
||||
|
||||
ctrl.portgroup_uuid = new formFieldService.FormField({
|
||||
type: "select",
|
||||
id: "portgroup-uuid",
|
||||
title: gettext("Portgroup"),
|
||||
desc: gettext("Portgroup that this port belongs to"),
|
||||
portgroups: [],
|
||||
options: "portgroup.uuid as portgroup.name ? portgroup.name : portgroup.uuid for portgroup in field.portgroups", // eslint-disable-line max-len
|
||||
value: null});
|
||||
|
||||
// Object used to manage local-link-connection form fields
|
||||
ctrl.localLinkConnection =
|
||||
new LocalLinkConnectionMgr(formFieldService,
|
||||
validMacAddressPattern,
|
||||
validDatapathIdPattern);
|
||||
|
||||
ironic.getPortgroups(node.uuid).then(function(portgroups) {
|
||||
var field = ctrl.portgroup_uuid;
|
||||
|
||||
if (portgroups.length > 0) {
|
||||
field.portgroups.push({uuid: null, name: gettext("Select a portgroup")});
|
||||
}
|
||||
field.portgroups = field.portgroups.concat(portgroups);
|
||||
|
||||
if (portgroups.length === 0) {
|
||||
field.disable();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Cancel the modal
|
||||
*
|
||||
|
@ -12,6 +12,7 @@
|
||||
<form id="CreatePortForm" name="CreatePortForm">
|
||||
<form-field field="ctrl.address" form="CreatePortForm"></form-field>
|
||||
<form-field field="ctrl.pxeEnabled" form="CreatePortForm"></form-field>
|
||||
<form-field field="ctrl.portgroup_uuid" form="CreatePortForm"></form-field>
|
||||
</form>
|
||||
|
||||
<form id="LocalLinkConnectionForm"
|
||||
|
@ -42,6 +42,7 @@
|
||||
|
||||
$controller('BasePortController',
|
||||
{ctrl: ctrl,
|
||||
node: node,
|
||||
$uibModalInstance: $uibModalInstance});
|
||||
|
||||
ctrl.modalTitle = gettext("Create Port");
|
||||
@ -54,7 +55,6 @@
|
||||
*/
|
||||
ctrl.createPort = function() {
|
||||
var port = angular.copy(ctrl.port);
|
||||
port.node_uuid = node.id;
|
||||
|
||||
port.address = ctrl.address.value;
|
||||
|
||||
@ -67,6 +67,10 @@
|
||||
port.pxe_enabled = ctrl.pxeEnabled.value;
|
||||
}
|
||||
|
||||
if (ctrl.portgroup_uuid.value !== null) {
|
||||
port.portgroup_uuid = ctrl.portgroup_uuid.value;
|
||||
}
|
||||
|
||||
ironic.createPort(port).then(
|
||||
function(createdPort) {
|
||||
$rootScope.$emit(ironicEvents.CREATE_PORT_SUCCESS);
|
||||
|
@ -51,6 +51,7 @@
|
||||
var ctrl = this;
|
||||
$controller('BasePortController',
|
||||
{ctrl: ctrl,
|
||||
node: node,
|
||||
$uibModalInstance: $uibModalInstance});
|
||||
|
||||
ctrl.modalTitle = gettext("Edit Port");
|
||||
@ -69,8 +70,15 @@
|
||||
}
|
||||
|
||||
ctrl.pxeEnabled.value = port.pxe_enabled ? 'True' : 'False';
|
||||
|
||||
ctrl.portgroup_uuid.value = port.portgroup_uuid;
|
||||
|
||||
if (cannotEditConnectivityAttr) {
|
||||
ctrl.pxeEnabled.disable(UNABLE_TO_UPDATE_CONNECTIVITY_ATTR_MSG);
|
||||
angular.forEach(
|
||||
[ctrl.pxeEnabled, ctrl.portgroup_uuid],
|
||||
function(field) {
|
||||
field.disable(UNABLE_TO_UPDATE_CONNECTIVITY_ATTR_MSG);
|
||||
});
|
||||
}
|
||||
|
||||
ctrl.localLinkConnection.setValues(
|
||||
@ -101,6 +109,9 @@
|
||||
ctrl.localLinkConnection.toPortAttr(),
|
||||
"/local_link_connection");
|
||||
patcher.buildPatch(port.extra, ctrl.port.extra, "/extra");
|
||||
patcher.buildPatch(port.portgroup_uuid,
|
||||
ctrl.portgroup_uuid.value,
|
||||
"/portgroup_uuid");
|
||||
|
||||
var patch = patcher.getPatch();
|
||||
$log.info("patch = " + JSON.stringify(patch.patch));
|
||||
|
@ -3,15 +3,24 @@
|
||||
form[field.id].$dirty}">
|
||||
<label for="{$ field.id $}"
|
||||
class="control-label">{$ field.title $}</label>
|
||||
<span ng-if="field.getHelpText()"
|
||||
<span ng-if="field.desc"
|
||||
class="help-icon"
|
||||
data-container="body"
|
||||
data-html="true"
|
||||
title=""
|
||||
data-toggle="tooltip"
|
||||
data-original-title="{$ field.getHelpText() $}">
|
||||
data-original-title="{$ field.desc $}">
|
||||
<span class="fa fa-question-circle"></span>
|
||||
</span>
|
||||
<span ng-if="field.info"
|
||||
class="help-icon"
|
||||
data-container="body"
|
||||
data-html="true"
|
||||
title=""
|
||||
data-toggle="tooltip"
|
||||
data-original-title="{$ field.info $}">
|
||||
<span class="fa fa-info-circle"></span>
|
||||
</span>
|
||||
<span ng-if="field.required"
|
||||
class="hz-icon-required fa fa-asterisk"></span>
|
||||
<div ng-switch="field.type">
|
||||
@ -24,8 +33,8 @@
|
||||
ng-required="field.required"
|
||||
ng-disabled="field.disabled"
|
||||
ng-pattern="field.pattern"
|
||||
ng-change="field.change()"
|
||||
placeholder="{$ field.getHelpText() $}"/>
|
||||
ng-change="{$ field.change $}"
|
||||
placeholder="{$ field.desc $}"/>
|
||||
<div ng-switch-when="radio"
|
||||
class="btn-group"
|
||||
id="{$ field.id $}">
|
||||
|
@ -84,31 +84,6 @@
|
||||
return angular.isDefined(this.value) && this.value !== '';
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Test whether the field has help-text.
|
||||
*
|
||||
* @return {boolean} Return true if the field has help text.
|
||||
*/
|
||||
this.hasHelpText = function() {
|
||||
return angular.isDefined(this.desc) || angular.isDefined(this.info);
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Get the help-text associated with this field
|
||||
*
|
||||
* @return {string} Return true if the field has help text
|
||||
*/
|
||||
this.getHelpText = function() {
|
||||
var text = angular.isDefined(this.desc) ? this.desc : '';
|
||||
if (angular.isDefined(this.info)) {
|
||||
if (text !== '') {
|
||||
text += '<br><br>';
|
||||
}
|
||||
text += this.info;
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Disable this field.
|
||||
*
|
||||
|
@ -53,7 +53,8 @@
|
||||
expect(field.info).toBeUndefined();
|
||||
expect(field.autoFocus).toBe(false);
|
||||
expect(field.change).toBeUndefined();
|
||||
expect(formFieldService).toBeDefined();
|
||||
expect(field.hasValue).toBeDefined();
|
||||
expect(field.disable).toBeDefined();
|
||||
});
|
||||
|
||||
it('FormField - local parameters', function() {
|
||||
@ -79,37 +80,6 @@
|
||||
expect(field.hasValue()).toBe(true);
|
||||
});
|
||||
|
||||
it('hasHelpText', function() {
|
||||
var field = new formFieldService.FormField({});
|
||||
expect(field.hasHelpText()).toBe(false);
|
||||
expect(field.getHelpText()).toBe('');
|
||||
});
|
||||
|
||||
it('hasHelpText/getHelpText - desc', function() {
|
||||
var field = new formFieldService.FormField({
|
||||
desc: 'desc'
|
||||
});
|
||||
expect(field.hasHelpText()).toBe(true);
|
||||
expect(field.getHelpText()).toBe('desc');
|
||||
});
|
||||
|
||||
it('hasHelpText/getHelpText - info', function() {
|
||||
var field = new formFieldService.FormField({
|
||||
info: 'info'
|
||||
});
|
||||
expect(field.hasHelpText()).toBe(true);
|
||||
expect(field.getHelpText()).toBe('info');
|
||||
});
|
||||
|
||||
it('getHelpText - desc/info', function() {
|
||||
var field = new formFieldService.FormField({
|
||||
desc: 'desc',
|
||||
info: 'info'
|
||||
});
|
||||
expect(field.hasHelpText()).toBe(true);
|
||||
expect(field.getHelpText()).toBe('desc<br><br>info');
|
||||
});
|
||||
|
||||
it('disable', function() {
|
||||
var field = new formFieldService.FormField({});
|
||||
expect(field.disabled).toBe(false);
|
||||
|
@ -655,7 +655,27 @@
|
||||
}
|
||||
return [status, ""];
|
||||
});
|
||||
|
||||
// Get portgroup ports
|
||||
$httpBackend.whenGET(/\/api\/ironic\/portgroups\/([^\/]+)\/ports$/,
|
||||
undefined,
|
||||
['portgroupId'])
|
||||
.respond(function(method, url, data, headers, params) {
|
||||
var ports = [];
|
||||
var status = responseCode.RESOURCE_NOT_FOUND;
|
||||
if (angular.isDefined(portgroups[params.portgroupId])) {
|
||||
var portgroup = portgroups[params.portgroupId];
|
||||
var node = nodes[portgroup.node_uuid];
|
||||
angular.forEach(node.ports, function(port) {
|
||||
if (port.portgroup_uuid === portgroup.uuid) {
|
||||
ports.push(port);
|
||||
}
|
||||
});
|
||||
status = responseCode.SUCCESS;
|
||||
}
|
||||
return [status, {ports: ports}];
|
||||
});
|
||||
} // init()
|
||||
|
||||
/**
|
||||
* @description Get the list of supported drivers
|
||||
@ -694,6 +714,5 @@
|
||||
$httpBackend.verifyNoOutstandingExpectation();
|
||||
$httpBackend.verifyNoOutstandingRequest();
|
||||
}
|
||||
}
|
||||
|
||||
}());
|
||||
} // ironicBackendMockService()
|
||||
})();
|
||||
|
@ -63,7 +63,8 @@
|
||||
validateNode: validateNode,
|
||||
createPortgroup: createPortgroup,
|
||||
getPortgroups: getPortgroups,
|
||||
deletePortgroup: deletePortgroup
|
||||
deletePortgroup: deletePortgroup,
|
||||
getPortgroupPorts: getPortgroupPorts
|
||||
};
|
||||
|
||||
return service;
|
||||
@ -644,5 +645,29 @@
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Get the ports associated with a specified portgroup.
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#list-ports-by-portgroup
|
||||
*
|
||||
* @param {string} portgroupId – UUID or name of the portgroup.
|
||||
* @return {promise} Promise containing a list of ports.
|
||||
*/
|
||||
function getPortgroupPorts(portgroupId) {
|
||||
return apiService.get(
|
||||
'/api/ironic/portgroups/' + portgroupId + '/ports')
|
||||
.then(function(response) {
|
||||
return response.data.ports; // List of ports
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(
|
||||
gettext('Unable to retrieve portgroup ports: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
}());
|
||||
|
@ -28,6 +28,7 @@
|
||||
'getDriverProperties',
|
||||
'getNode',
|
||||
'getNodes',
|
||||
'getPortgroupPorts',
|
||||
'getPortgroups',
|
||||
'getPortsWithNode',
|
||||
'getBootDevice',
|
||||
@ -576,6 +577,24 @@
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('getPortgroupPorts', 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.getPortgroupPorts(portgroup.uuid).then(function(ports) {
|
||||
expect(ports).toEqual([]);
|
||||
});
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
@ -182,6 +182,12 @@
|
||||
function retrievePortgroups() {
|
||||
ironic.getPortgroups(ctrl.node.uuid).then(function(portgroups) {
|
||||
ctrl.portgroupsSrc = portgroups;
|
||||
angular.forEach(portgroups, function(portgroup) {
|
||||
portgroup.ports = [];
|
||||
ironic.getPortgroupPorts(portgroup.uuid).then(function(ports) {
|
||||
portgroup.ports = ports;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -168,6 +168,9 @@
|
||||
<th translate class="rsp-p2" style="width:white-space:nowrap;">
|
||||
Name
|
||||
</th>
|
||||
<th translate class="rsp-p2" style="width:white-space:nowrap;">
|
||||
Ports
|
||||
</th>
|
||||
<th translate class="actions_column">
|
||||
Actions
|
||||
</th>
|
||||
@ -194,6 +197,9 @@
|
||||
<td class="rsp-p1">
|
||||
{$ portgroup.name | noValue $}
|
||||
</td>
|
||||
<td class="rsp-p1">
|
||||
{$ portgroup.ports.length | noValue $}
|
||||
</td>
|
||||
<td class="actions_column">
|
||||
<action-list uib-dropdown class="pull-right">
|
||||
<action button-type="split-button"
|
||||
@ -203,9 +209,11 @@
|
||||
{$ ::'Edit portgroup' | translate $}
|
||||
</action>
|
||||
<menu>
|
||||
<li role="presentation">
|
||||
<li role="presentation"
|
||||
ng-class="{disabled: portgroup.ports.length > 0}">
|
||||
<a role="menuitem"
|
||||
ng-click="ctrl.deletePortgroups([portgroup]);
|
||||
ng-click="portgroup.ports.length > 0 ||
|
||||
ctrl.deletePortgroups([portgroup]);
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault()">
|
||||
<span class="fa fa-trash"></span>
|
||||
|
@ -140,8 +140,12 @@
|
||||
|
||||
if (isProperty(source) && isProperty(target)) {
|
||||
if (source !== target) {
|
||||
if (target === null) {
|
||||
patcher.patch.push({op: "remove", path: path});
|
||||
} else {
|
||||
patcher.patch.push({op: "replace", path: path, value: target});
|
||||
}
|
||||
}
|
||||
} else if (isCollection(source) && isCollection(target)) {
|
||||
angular.forEach(source, function(sourceItem, sourceItemName) {
|
||||
if (angular.isDefined(target[sourceItemName])) {
|
||||
|
Loading…
Reference in New Issue
Block a user