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
|
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.portgroup.html#ironicclient.v1.portgroup.PortgroupManager.delete
|
||||||
"""
|
"""
|
||||||
return ironicclient(request).portgroup.delete(portgroup_id)
|
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,
|
return ironic.portgroup_delete(request,
|
||||||
request.DATA.get('portgroup_id'))
|
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.validMacAddressPattern',
|
||||||
'horizon.dashboard.admin.ironic.validDatapathIdPattern',
|
'horizon.dashboard.admin.ironic.validDatapathIdPattern',
|
||||||
'horizon.dashboard.admin.ironic.form-field.service',
|
'horizon.dashboard.admin.ironic.form-field.service',
|
||||||
'ctrl'
|
'horizon.app.core.openstack-service-api.ironic',
|
||||||
|
'ctrl',
|
||||||
|
'node'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,9 +140,12 @@
|
|||||||
validMacAddressPattern,
|
validMacAddressPattern,
|
||||||
validDatapathIdPattern,
|
validDatapathIdPattern,
|
||||||
formFieldService,
|
formFieldService,
|
||||||
ctrl) {
|
ironic,
|
||||||
|
ctrl,
|
||||||
|
node) {
|
||||||
ctrl.port = {
|
ctrl.port = {
|
||||||
extra: {}
|
extra: {},
|
||||||
|
node_uuid: node.uuid
|
||||||
};
|
};
|
||||||
|
|
||||||
ctrl.address = new formFieldService.FormField({
|
ctrl.address = new formFieldService.FormField({
|
||||||
@ -162,12 +167,34 @@
|
|||||||
options: ['True', 'False'],
|
options: ['True', 'False'],
|
||||||
value: 'True'});
|
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
|
// Object used to manage local-link-connection form fields
|
||||||
ctrl.localLinkConnection =
|
ctrl.localLinkConnection =
|
||||||
new LocalLinkConnectionMgr(formFieldService,
|
new LocalLinkConnectionMgr(formFieldService,
|
||||||
validMacAddressPattern,
|
validMacAddressPattern,
|
||||||
validDatapathIdPattern);
|
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
|
* Cancel the modal
|
||||||
*
|
*
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
<form id="CreatePortForm" name="CreatePortForm">
|
<form id="CreatePortForm" name="CreatePortForm">
|
||||||
<form-field field="ctrl.address" form="CreatePortForm"></form-field>
|
<form-field field="ctrl.address" form="CreatePortForm"></form-field>
|
||||||
<form-field field="ctrl.pxeEnabled" 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>
|
||||||
|
|
||||||
<form id="LocalLinkConnectionForm"
|
<form id="LocalLinkConnectionForm"
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
$controller('BasePortController',
|
$controller('BasePortController',
|
||||||
{ctrl: ctrl,
|
{ctrl: ctrl,
|
||||||
|
node: node,
|
||||||
$uibModalInstance: $uibModalInstance});
|
$uibModalInstance: $uibModalInstance});
|
||||||
|
|
||||||
ctrl.modalTitle = gettext("Create Port");
|
ctrl.modalTitle = gettext("Create Port");
|
||||||
@ -54,7 +55,6 @@
|
|||||||
*/
|
*/
|
||||||
ctrl.createPort = function() {
|
ctrl.createPort = function() {
|
||||||
var port = angular.copy(ctrl.port);
|
var port = angular.copy(ctrl.port);
|
||||||
port.node_uuid = node.id;
|
|
||||||
|
|
||||||
port.address = ctrl.address.value;
|
port.address = ctrl.address.value;
|
||||||
|
|
||||||
@ -67,6 +67,10 @@
|
|||||||
port.pxe_enabled = ctrl.pxeEnabled.value;
|
port.pxe_enabled = ctrl.pxeEnabled.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctrl.portgroup_uuid.value !== null) {
|
||||||
|
port.portgroup_uuid = ctrl.portgroup_uuid.value;
|
||||||
|
}
|
||||||
|
|
||||||
ironic.createPort(port).then(
|
ironic.createPort(port).then(
|
||||||
function(createdPort) {
|
function(createdPort) {
|
||||||
$rootScope.$emit(ironicEvents.CREATE_PORT_SUCCESS);
|
$rootScope.$emit(ironicEvents.CREATE_PORT_SUCCESS);
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
var ctrl = this;
|
var ctrl = this;
|
||||||
$controller('BasePortController',
|
$controller('BasePortController',
|
||||||
{ctrl: ctrl,
|
{ctrl: ctrl,
|
||||||
|
node: node,
|
||||||
$uibModalInstance: $uibModalInstance});
|
$uibModalInstance: $uibModalInstance});
|
||||||
|
|
||||||
ctrl.modalTitle = gettext("Edit Port");
|
ctrl.modalTitle = gettext("Edit Port");
|
||||||
@ -69,8 +70,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctrl.pxeEnabled.value = port.pxe_enabled ? 'True' : 'False';
|
ctrl.pxeEnabled.value = port.pxe_enabled ? 'True' : 'False';
|
||||||
|
|
||||||
|
ctrl.portgroup_uuid.value = port.portgroup_uuid;
|
||||||
|
|
||||||
if (cannotEditConnectivityAttr) {
|
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(
|
ctrl.localLinkConnection.setValues(
|
||||||
@ -101,6 +109,9 @@
|
|||||||
ctrl.localLinkConnection.toPortAttr(),
|
ctrl.localLinkConnection.toPortAttr(),
|
||||||
"/local_link_connection");
|
"/local_link_connection");
|
||||||
patcher.buildPatch(port.extra, ctrl.port.extra, "/extra");
|
patcher.buildPatch(port.extra, ctrl.port.extra, "/extra");
|
||||||
|
patcher.buildPatch(port.portgroup_uuid,
|
||||||
|
ctrl.portgroup_uuid.value,
|
||||||
|
"/portgroup_uuid");
|
||||||
|
|
||||||
var patch = patcher.getPatch();
|
var patch = patcher.getPatch();
|
||||||
$log.info("patch = " + JSON.stringify(patch.patch));
|
$log.info("patch = " + JSON.stringify(patch.patch));
|
||||||
|
@ -3,15 +3,24 @@
|
|||||||
form[field.id].$dirty}">
|
form[field.id].$dirty}">
|
||||||
<label for="{$ field.id $}"
|
<label for="{$ field.id $}"
|
||||||
class="control-label">{$ field.title $}</label>
|
class="control-label">{$ field.title $}</label>
|
||||||
<span ng-if="field.getHelpText()"
|
<span ng-if="field.desc"
|
||||||
class="help-icon"
|
class="help-icon"
|
||||||
data-container="body"
|
data-container="body"
|
||||||
data-html="true"
|
data-html="true"
|
||||||
title=""
|
title=""
|
||||||
data-toggle="tooltip"
|
data-toggle="tooltip"
|
||||||
data-original-title="{$ field.getHelpText() $}">
|
data-original-title="{$ field.desc $}">
|
||||||
<span class="fa fa-question-circle"></span>
|
<span class="fa fa-question-circle"></span>
|
||||||
</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"
|
<span ng-if="field.required"
|
||||||
class="hz-icon-required fa fa-asterisk"></span>
|
class="hz-icon-required fa fa-asterisk"></span>
|
||||||
<div ng-switch="field.type">
|
<div ng-switch="field.type">
|
||||||
@ -24,8 +33,8 @@
|
|||||||
ng-required="field.required"
|
ng-required="field.required"
|
||||||
ng-disabled="field.disabled"
|
ng-disabled="field.disabled"
|
||||||
ng-pattern="field.pattern"
|
ng-pattern="field.pattern"
|
||||||
ng-change="field.change()"
|
ng-change="{$ field.change $}"
|
||||||
placeholder="{$ field.getHelpText() $}"/>
|
placeholder="{$ field.desc $}"/>
|
||||||
<div ng-switch-when="radio"
|
<div ng-switch-when="radio"
|
||||||
class="btn-group"
|
class="btn-group"
|
||||||
id="{$ field.id $}">
|
id="{$ field.id $}">
|
||||||
|
@ -84,31 +84,6 @@
|
|||||||
return angular.isDefined(this.value) && this.value !== '';
|
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.
|
* @description Disable this field.
|
||||||
*
|
*
|
||||||
|
@ -53,7 +53,8 @@
|
|||||||
expect(field.info).toBeUndefined();
|
expect(field.info).toBeUndefined();
|
||||||
expect(field.autoFocus).toBe(false);
|
expect(field.autoFocus).toBe(false);
|
||||||
expect(field.change).toBeUndefined();
|
expect(field.change).toBeUndefined();
|
||||||
expect(formFieldService).toBeDefined();
|
expect(field.hasValue).toBeDefined();
|
||||||
|
expect(field.disable).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('FormField - local parameters', function() {
|
it('FormField - local parameters', function() {
|
||||||
@ -79,37 +80,6 @@
|
|||||||
expect(field.hasValue()).toBe(true);
|
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() {
|
it('disable', function() {
|
||||||
var field = new formFieldService.FormField({});
|
var field = new formFieldService.FormField({});
|
||||||
expect(field.disabled).toBe(false);
|
expect(field.disabled).toBe(false);
|
||||||
|
@ -655,7 +655,27 @@
|
|||||||
}
|
}
|
||||||
return [status, ""];
|
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
|
* @description Get the list of supported drivers
|
||||||
@ -694,6 +714,5 @@
|
|||||||
$httpBackend.verifyNoOutstandingExpectation();
|
$httpBackend.verifyNoOutstandingExpectation();
|
||||||
$httpBackend.verifyNoOutstandingRequest();
|
$httpBackend.verifyNoOutstandingRequest();
|
||||||
}
|
}
|
||||||
}
|
} // ironicBackendMockService()
|
||||||
|
})();
|
||||||
}());
|
|
||||||
|
@ -63,7 +63,8 @@
|
|||||||
validateNode: validateNode,
|
validateNode: validateNode,
|
||||||
createPortgroup: createPortgroup,
|
createPortgroup: createPortgroup,
|
||||||
getPortgroups: getPortgroups,
|
getPortgroups: getPortgroups,
|
||||||
deletePortgroup: deletePortgroup
|
deletePortgroup: deletePortgroup,
|
||||||
|
getPortgroupPorts: getPortgroupPorts
|
||||||
};
|
};
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
@ -644,5 +645,29 @@
|
|||||||
return $q.reject(msg);
|
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',
|
'getDriverProperties',
|
||||||
'getNode',
|
'getNode',
|
||||||
'getNodes',
|
'getNodes',
|
||||||
|
'getPortgroupPorts',
|
||||||
'getPortgroups',
|
'getPortgroups',
|
||||||
'getPortsWithNode',
|
'getPortsWithNode',
|
||||||
'getBootDevice',
|
'getBootDevice',
|
||||||
@ -576,6 +577,24 @@
|
|||||||
|
|
||||||
ironicBackendMockService.flush();
|
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() {
|
function retrievePortgroups() {
|
||||||
ironic.getPortgroups(ctrl.node.uuid).then(function(portgroups) {
|
ironic.getPortgroups(ctrl.node.uuid).then(function(portgroups) {
|
||||||
ctrl.portgroupsSrc = 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;">
|
<th translate class="rsp-p2" style="width:white-space:nowrap;">
|
||||||
Name
|
Name
|
||||||
</th>
|
</th>
|
||||||
|
<th translate class="rsp-p2" style="width:white-space:nowrap;">
|
||||||
|
Ports
|
||||||
|
</th>
|
||||||
<th translate class="actions_column">
|
<th translate class="actions_column">
|
||||||
Actions
|
Actions
|
||||||
</th>
|
</th>
|
||||||
@ -194,6 +197,9 @@
|
|||||||
<td class="rsp-p1">
|
<td class="rsp-p1">
|
||||||
{$ portgroup.name | noValue $}
|
{$ portgroup.name | noValue $}
|
||||||
</td>
|
</td>
|
||||||
|
<td class="rsp-p1">
|
||||||
|
{$ portgroup.ports.length | noValue $}
|
||||||
|
</td>
|
||||||
<td class="actions_column">
|
<td class="actions_column">
|
||||||
<action-list uib-dropdown class="pull-right">
|
<action-list uib-dropdown class="pull-right">
|
||||||
<action button-type="split-button"
|
<action button-type="split-button"
|
||||||
@ -203,9 +209,11 @@
|
|||||||
{$ ::'Edit portgroup' | translate $}
|
{$ ::'Edit portgroup' | translate $}
|
||||||
</action>
|
</action>
|
||||||
<menu>
|
<menu>
|
||||||
<li role="presentation">
|
<li role="presentation"
|
||||||
|
ng-class="{disabled: portgroup.ports.length > 0}">
|
||||||
<a role="menuitem"
|
<a role="menuitem"
|
||||||
ng-click="ctrl.deletePortgroups([portgroup]);
|
ng-click="portgroup.ports.length > 0 ||
|
||||||
|
ctrl.deletePortgroups([portgroup]);
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
$event.preventDefault()">
|
$event.preventDefault()">
|
||||||
<span class="fa fa-trash"></span>
|
<span class="fa fa-trash"></span>
|
||||||
|
@ -140,7 +140,11 @@
|
|||||||
|
|
||||||
if (isProperty(source) && isProperty(target)) {
|
if (isProperty(source) && isProperty(target)) {
|
||||||
if (source !== target) {
|
if (source !== target) {
|
||||||
patcher.patch.push({op: "replace", path: path, value: 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)) {
|
} else if (isCollection(source) && isCollection(target)) {
|
||||||
angular.forEach(source, function(sourceItem, sourceItemName) {
|
angular.forEach(source, function(sourceItem, sourceItemName) {
|
||||||
|
Loading…
Reference in New Issue
Block a user