Set current boot device on a node
This would also include collecting the possible boot devices. Change-Id: I49c47169d5c511f992480c60f64062183064bfc6 Closes-Bug: #1671567
This commit is contained in:
parent
6a1f2e2f1b
commit
41488737f6
@ -1,6 +1,7 @@
|
|||||||
#
|
#
|
||||||
# Copyright 2015, 2016 Hewlett Packard Enterprise Development Company LP
|
# Copyright 2015, 2016 Hewlett Packard Enterprise Development Company LP
|
||||||
# Copyright 2016 Cray Inc.
|
# Copyright 2016 Cray Inc.
|
||||||
|
# Copyright 2017 Intel Corporation
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -229,6 +230,35 @@ def node_get_boot_device(request, node_id):
|
|||||||
return ironicclient(request).node.get_boot_device(node_id)
|
return ironicclient(request).node.get_boot_device(node_id)
|
||||||
|
|
||||||
|
|
||||||
|
def node_set_boot_device(request, node_id, device, persistent):
|
||||||
|
"""Set the boot device for a specified node.
|
||||||
|
|
||||||
|
:param request: HTTP request.
|
||||||
|
:param node_id: The UUID or name of the node.
|
||||||
|
:param device: boot device.
|
||||||
|
:param persistent: True or False.
|
||||||
|
:return: null.
|
||||||
|
|
||||||
|
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.set_boot_device
|
||||||
|
"""
|
||||||
|
return ironicclient(request).node.set_boot_device(node_id,
|
||||||
|
device,
|
||||||
|
persistent)
|
||||||
|
|
||||||
|
|
||||||
|
def node_get_supported_boot_devices(request, node_id):
|
||||||
|
"""Get the list of supported boot devices for a specified node.
|
||||||
|
|
||||||
|
:param request: HTTP request.
|
||||||
|
:param node_id: The UUID or name of the node.
|
||||||
|
:return: List of supported boot devices (strings)
|
||||||
|
|
||||||
|
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.get_boot_device
|
||||||
|
"""
|
||||||
|
result = ironicclient(request).node.get_supported_boot_devices(node_id)
|
||||||
|
return result.get('supported_boot_devices', [])
|
||||||
|
|
||||||
|
|
||||||
def driver_list(request):
|
def driver_list(request):
|
||||||
"""Retrieve a list of drivers.
|
"""Retrieve a list of drivers.
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#
|
#
|
||||||
# Copyright 2015, 2016 Hewlett Packard Enterprise Development Company LP
|
# Copyright 2015, 2016 Hewlett Packard Enterprise Development Company LP
|
||||||
# Copyright 2016 Cray Inc.
|
# Copyright 2016 Cray Inc.
|
||||||
|
# Copyright 2017 Intel Corporation
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@ -277,6 +278,37 @@ class BootDevice(generic.View):
|
|||||||
"""
|
"""
|
||||||
return ironic.node_get_boot_device(request, node_id)
|
return ironic.node_get_boot_device(request, node_id)
|
||||||
|
|
||||||
|
@rest_utils.ajax(data_required=True)
|
||||||
|
def put(self, request, node_id):
|
||||||
|
"""Set the boot device for a specific node
|
||||||
|
|
||||||
|
:param request: HTTP request.
|
||||||
|
:param node_id: Node name or uuid
|
||||||
|
:return: null.
|
||||||
|
"""
|
||||||
|
return ironic.node_set_boot_device(
|
||||||
|
request,
|
||||||
|
node_id,
|
||||||
|
request.DATA.get('boot_device'),
|
||||||
|
persistent=request.DATA.get('persistent'))
|
||||||
|
|
||||||
|
|
||||||
|
@urls.register
|
||||||
|
class SupportedBootDevices(generic.View):
|
||||||
|
|
||||||
|
url_regex = r'ironic/nodes/(?P<node_id>{})/boot_device/supported$' . \
|
||||||
|
format(LOGICAL_NAME_PATTERN)
|
||||||
|
|
||||||
|
@rest_utils.ajax()
|
||||||
|
def get(self, request, node_id):
|
||||||
|
"""Get the list of supported boot devices for a specified node
|
||||||
|
|
||||||
|
:param request: HTTP request.
|
||||||
|
:param node_id: Node name or uuid
|
||||||
|
:return: List of supported boot devices
|
||||||
|
"""
|
||||||
|
return ironic.node_get_supported_boot_devices(request, node_id)
|
||||||
|
|
||||||
|
|
||||||
@urls.register
|
@urls.register
|
||||||
class Drivers(generic.View):
|
class Drivers(generic.View):
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Intel Corporation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc controller
|
||||||
|
* @name horizon.dashboard.admin.ironic:BootDeviceController
|
||||||
|
* @ngController
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Controller used to prompt the user for information associated with
|
||||||
|
* setting the boot device of a node
|
||||||
|
*/
|
||||||
|
angular
|
||||||
|
.module('horizon.dashboard.admin.ironic')
|
||||||
|
.controller('BootDeviceController', BootDeviceController);
|
||||||
|
|
||||||
|
BootDeviceController.$inject = [
|
||||||
|
'$uibModalInstance',
|
||||||
|
'horizon.app.core.openstack-service-api.ironic',
|
||||||
|
'node'
|
||||||
|
];
|
||||||
|
|
||||||
|
function BootDeviceController($uibModalInstance, ironic, node) {
|
||||||
|
var ctrl = this;
|
||||||
|
|
||||||
|
ctrl.modalTitle = gettext("Set Boot Device");
|
||||||
|
|
||||||
|
ironic.getSupportedBootDevices(node.uuid).then(
|
||||||
|
function(bootDevices) {
|
||||||
|
ctrl.supportedBootDevices = bootDevices;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize form fields to current values
|
||||||
|
ctrl.bootDevice = null;
|
||||||
|
ctrl.persistent = 'False';
|
||||||
|
ironic.getBootDevice(node.uuid).then(function(device) {
|
||||||
|
ctrl.bootDevice = device.boot_device;
|
||||||
|
ctrl.persistent = device.persistent ? 'True' : 'False';
|
||||||
|
});
|
||||||
|
|
||||||
|
ctrl.cancel = function() {
|
||||||
|
$uibModalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
|
||||||
|
ctrl.setSelectedBootDevice = function() {
|
||||||
|
$uibModalInstance.close({device: ctrl.bootDevice,
|
||||||
|
persistent: ctrl.persistent === 'True'});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})();
|
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Cray Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
describe('horizon.dashboard.admin.ironic.BootDeviceController', function () {
|
||||||
|
var BOOT_DEVICE_CONTROLLER_PROPERTIES = [
|
||||||
|
'bootDevice',
|
||||||
|
'cancel',
|
||||||
|
'modalTitle',
|
||||||
|
'persistent',
|
||||||
|
'setSelectedBootDevice',
|
||||||
|
'supportedBootDevices'
|
||||||
|
];
|
||||||
|
var uibModalInstance, ironicBackendMockService, node;
|
||||||
|
var ctrl = {};
|
||||||
|
|
||||||
|
beforeEach(module('horizon.dashboard.admin.ironic'));
|
||||||
|
|
||||||
|
beforeEach(module('horizon.framework.util'));
|
||||||
|
|
||||||
|
beforeEach(module(function($provide) {
|
||||||
|
$provide.value('$uibModal', {});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(module(function($provide) {
|
||||||
|
uibModalInstance = {
|
||||||
|
close: jasmine.createSpy(),
|
||||||
|
dismiss: jasmine.createSpy()
|
||||||
|
};
|
||||||
|
$provide.value('$uibModalInstance', uibModalInstance);
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(module(function($provide) {
|
||||||
|
$provide.value('horizon.framework.widgets.toast.service',
|
||||||
|
{});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(module('horizon.app.core.openstack-service-api'));
|
||||||
|
|
||||||
|
beforeEach(inject(function($injector) {
|
||||||
|
ironicBackendMockService =
|
||||||
|
$injector.get('horizon.dashboard.admin.ironic.backend-mock.service');
|
||||||
|
ironicBackendMockService.init();
|
||||||
|
|
||||||
|
var ironicAPI =
|
||||||
|
$injector.get('horizon.app.core.openstack-service-api.ironic');
|
||||||
|
|
||||||
|
ironicAPI.createNode(
|
||||||
|
{driver: ironicBackendMockService.params.defaultDriver})
|
||||||
|
.then(function(response) {
|
||||||
|
node = response.data;
|
||||||
|
var controller = $injector.get('$controller');
|
||||||
|
ctrl = controller('BootDeviceController', {node: node});
|
||||||
|
});
|
||||||
|
ironicBackendMockService.flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('controller should be defined', function () {
|
||||||
|
expect(ctrl).toBeDefined();
|
||||||
|
expect(Object.getOwnPropertyNames(ctrl).sort()).toEqual(
|
||||||
|
BOOT_DEVICE_CONTROLLER_PROPERTIES.sort());
|
||||||
|
expect(ctrl.supportedBootDevices).toEqual(
|
||||||
|
ironicBackendMockService.getNodeSupportedBootDevices(node.uuid));
|
||||||
|
var bootDevice = ironicBackendMockService.getNodeBootDevice(node.uuid);
|
||||||
|
expect(ctrl.bootDevice).toEqual(bootDevice.boot_device);
|
||||||
|
expect(ctrl.persistent).toEqual(bootDevice.persistent ? 'True' : 'False');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancel', function () {
|
||||||
|
ctrl.cancel();
|
||||||
|
expect(uibModalInstance.dismiss).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setSelectedBootDevice', function () {
|
||||||
|
ctrl.bootDevice = 'pxe';
|
||||||
|
ctrl.persistent = 'False';
|
||||||
|
ctrl.setSelectedBootDevice();
|
||||||
|
expect(uibModalInstance.close).toHaveBeenCalledWith(
|
||||||
|
{device: ctrl.bootDevice,
|
||||||
|
persistent: ctrl.persistent === 'True'});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
@ -0,0 +1,58 @@
|
|||||||
|
<div class="modal-header" modal-draggable>
|
||||||
|
<button type="button"
|
||||||
|
class="close"
|
||||||
|
ng-click="$dismiss()"
|
||||||
|
aria-hidden="true"
|
||||||
|
aria-label="Close">
|
||||||
|
<span aria-hidden="true" class="fa fa-times"></span>
|
||||||
|
</button>
|
||||||
|
<h3 class="modal-title">{$ ::ctrl.modalTitle $}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="SetBootDeviceForm" name="SetBootDeviceForm">
|
||||||
|
<!--boot device-->
|
||||||
|
<div class="form-group required">
|
||||||
|
<label for="bootDevice"
|
||||||
|
class="control-label"
|
||||||
|
translate>Boot Device</label>
|
||||||
|
<span class="hz-icon-required fa fa-asterisk"></span>
|
||||||
|
<div>
|
||||||
|
<select id="bootDevice"
|
||||||
|
class="form-control"
|
||||||
|
ng-options="device as device for device in ctrl.supportedBootDevices"
|
||||||
|
ng-model="ctrl.bootDevice">
|
||||||
|
<option value="" disabled selected translate>Select a boot device</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
<label for="persistent"
|
||||||
|
class="control-label"
|
||||||
|
translate>Persistent</label>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group" id="persistent">
|
||||||
|
<label class="btn btn-default"
|
||||||
|
ng-model="ctrl.persistent"
|
||||||
|
ng-repeat="opt in ['True', 'False']"
|
||||||
|
uib-btn-radio="opt">{$ opt $}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<!--modal footer-->
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-default secondary"
|
||||||
|
type="button"
|
||||||
|
ng-click="ctrl.cancel()"
|
||||||
|
translate>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary"
|
||||||
|
type="button"
|
||||||
|
ng-click="ctrl.setSelectedBootDevice()"
|
||||||
|
ng-disabled="ctrl.bootDevice === null"
|
||||||
|
translate>
|
||||||
|
Set Boot Device
|
||||||
|
</button>
|
||||||
|
</div>
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Intel Corporation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @ngdoc service
|
||||||
|
* @name horizon.dashboard.admin.ironic.bootdevice.service
|
||||||
|
* @description Service for setting the boot device of a node
|
||||||
|
*/
|
||||||
|
angular
|
||||||
|
.module('horizon.dashboard.admin.ironic')
|
||||||
|
.factory('horizon.dashboard.admin.ironic.bootdevice.service',
|
||||||
|
bootDeviceService);
|
||||||
|
|
||||||
|
bootDeviceService.$inject = [
|
||||||
|
'$uibModal',
|
||||||
|
'horizon.dashboard.admin.ironic.basePath',
|
||||||
|
'horizon.app.core.openstack-service-api.ironic'
|
||||||
|
];
|
||||||
|
|
||||||
|
function bootDeviceService($uibModal, basePath, ironic) {
|
||||||
|
var service = {
|
||||||
|
setBootDevice: setBootDevice
|
||||||
|
};
|
||||||
|
return service;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @description Set the boot device of a specified node
|
||||||
|
*
|
||||||
|
* @param {object} node - node object
|
||||||
|
* @return {promise}
|
||||||
|
*/
|
||||||
|
function setBootDevice(node) {
|
||||||
|
var promise;
|
||||||
|
var options = {
|
||||||
|
controller: "BootDeviceController as ctrl",
|
||||||
|
backdrop: 'static',
|
||||||
|
resolve: {
|
||||||
|
node: function() {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
templateUrl: basePath + '/bootdevice/bootdevice.html'
|
||||||
|
};
|
||||||
|
promise = $uibModal.open(options).result.then(
|
||||||
|
function(result) {
|
||||||
|
return ironic.nodeSetBootDevice(node.uuid,
|
||||||
|
result.device,
|
||||||
|
result.persistent);
|
||||||
|
});
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
@ -0,0 +1,142 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2017 Cray Inc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Unit tests for the Ironic-UI boot-device service
|
||||||
|
*/
|
||||||
|
|
||||||
|
describe('horizon.dashboard.admin.ironic.bootdevice.service',
|
||||||
|
function() {
|
||||||
|
var $q,
|
||||||
|
$uibModal,
|
||||||
|
bootDeviceService,
|
||||||
|
ironicAPI,
|
||||||
|
ironicBackendMockService,
|
||||||
|
defaultDriver;
|
||||||
|
|
||||||
|
beforeEach(module('horizon.dashboard.admin.ironic'));
|
||||||
|
|
||||||
|
beforeEach(module('horizon.framework.util'));
|
||||||
|
|
||||||
|
beforeEach(module(function($provide) {
|
||||||
|
$provide.value('$uibModal', {
|
||||||
|
open: function() {
|
||||||
|
return $q.when({device: 'pxe',
|
||||||
|
persistent: true});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(module(function($provide) {
|
||||||
|
$provide.value('horizon.framework.widgets.toast.service', {
|
||||||
|
add: function() {}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(module('horizon.app.core.openstack-service-api'));
|
||||||
|
|
||||||
|
beforeEach(inject(function($injector) {
|
||||||
|
ironicBackendMockService =
|
||||||
|
$injector.get('horizon.dashboard.admin.ironic.backend-mock.service');
|
||||||
|
ironicBackendMockService.init();
|
||||||
|
defaultDriver = ironicBackendMockService.params.defaultDriver;
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(inject(function($injector) {
|
||||||
|
$q = $injector.get('$q');
|
||||||
|
|
||||||
|
$uibModal = $injector.get('$uibModal');
|
||||||
|
|
||||||
|
ironicAPI =
|
||||||
|
$injector.get('horizon.app.core.openstack-service-api.ironic');
|
||||||
|
|
||||||
|
bootDeviceService =
|
||||||
|
$injector.get('horizon.dashboard.admin.ironic.bootdevice.service');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('defines the bootDeviceService', function() {
|
||||||
|
expect(bootDeviceService).toBeDefined();
|
||||||
|
expect(bootDeviceService.setBootDevice).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
ironicBackendMockService.postTest();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Utility function that creates a node and returns
|
||||||
|
* both it and its boot device
|
||||||
|
*
|
||||||
|
* @return {promise} Containing node and boot_device
|
||||||
|
*/
|
||||||
|
function createNode() {
|
||||||
|
return ironicAPI.createNode({driver: defaultDriver})
|
||||||
|
.then(function(response) {
|
||||||
|
return response.data;
|
||||||
|
})
|
||||||
|
.then(function(node) {
|
||||||
|
return ironicAPI.getBootDevice(node.uuid).then(function(device) {
|
||||||
|
return {node: node, boot_device: device};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it('setBootDevice', function() {
|
||||||
|
var targetBootDevice = {
|
||||||
|
device: "safe",
|
||||||
|
persistent: false
|
||||||
|
};
|
||||||
|
|
||||||
|
spyOn($uibModal, 'open').and.returnValue(
|
||||||
|
{result: $q.when(targetBootDevice)});
|
||||||
|
|
||||||
|
createNode().then(function(data) {
|
||||||
|
expect(data.node.boot_device).not.toEqual(targetBootDevice.device);
|
||||||
|
bootDeviceService.setBootDevice(data.node)
|
||||||
|
.then(function() {
|
||||||
|
ironicAPI.getBootDevice(data.node.uuid).then(function(device) {
|
||||||
|
expect(device).toEqual(
|
||||||
|
{boot_device: targetBootDevice.device,
|
||||||
|
persistent: targetBootDevice.persistent});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(fail);
|
||||||
|
});
|
||||||
|
|
||||||
|
ironicBackendMockService.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setBootDevice - cancel', function() {
|
||||||
|
spyOn($uibModal, 'open').and.returnValue(
|
||||||
|
{result: $q.reject('cancel')});
|
||||||
|
|
||||||
|
createNode().then(function(data) {
|
||||||
|
bootDeviceService.setBootDevice(data.node)
|
||||||
|
.then(fail)
|
||||||
|
.catch(function() {
|
||||||
|
ironicAPI.getBootDevice(data.node.uuid).then(function(device) {
|
||||||
|
expect(device).toEqual(data.boot_device);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ironicBackendMockService.flush();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
@ -103,12 +103,11 @@
|
|||||||
|
|
||||||
// Additional service parameters
|
// Additional service parameters
|
||||||
var params = {
|
var params = {
|
||||||
// Currently, all nodes have the same boot device.
|
|
||||||
bootDevice: {boot_device: 'pxe', persistent: true},
|
|
||||||
// Console info
|
// Console info
|
||||||
consoleType: "shellinabox",
|
consoleType: "shellinabox",
|
||||||
consoleUrl: "http://localhost:",
|
consoleUrl: "http://localhost:",
|
||||||
defaultDriver: "agent_ipmitool"
|
defaultDriver: "agent_ipmitool",
|
||||||
|
supportedBootDevices: ["pxe", "bios", "safe"]
|
||||||
};
|
};
|
||||||
|
|
||||||
// List of supported drivers
|
// List of supported drivers
|
||||||
@ -123,6 +122,8 @@
|
|||||||
flush: flush,
|
flush: flush,
|
||||||
postTest: postTest,
|
postTest: postTest,
|
||||||
getNode: getNode,
|
getNode: getNode,
|
||||||
|
getNodeBootDevice: getNodeBootDevice,
|
||||||
|
getNodeSupportedBootDevices: getNodeSupportedBootDevices,
|
||||||
nodeGetConsoleUrl: nodeGetConsoleUrl,
|
nodeGetConsoleUrl: nodeGetConsoleUrl,
|
||||||
getDrivers: getDrivers,
|
getDrivers: getDrivers,
|
||||||
getImages: getImages,
|
getImages: getImages,
|
||||||
@ -183,7 +184,12 @@
|
|||||||
base: node,
|
base: node,
|
||||||
consolePort: getNextAvailableSystemPort(),
|
consolePort: getNextAvailableSystemPort(),
|
||||||
ports: {}, // Indexed by port-uuid
|
ports: {}, // Indexed by port-uuid
|
||||||
portgroups: {} // Indexed by portgroup-uuid
|
portgroups: {}, // Indexed by portgroup-uuid
|
||||||
|
supportedBootDevices: service.params.supportedBootDevices,
|
||||||
|
bootDevice: {
|
||||||
|
boot_device: service.params.supportedBootDevices[0],
|
||||||
|
persistent: true
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
nodes[node.uuid] = backendNode;
|
nodes[node.uuid] = backendNode;
|
||||||
@ -196,7 +202,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* description Get a specified node.
|
* @description Get a specified node.
|
||||||
*
|
*
|
||||||
* @param {string} nodeId - Uuid or name of the requested node.
|
* @param {string} nodeId - Uuid or name of the requested node.
|
||||||
* @return {object|null} Base node object, or null if the node
|
* @return {object|null} Base node object, or null if the node
|
||||||
@ -207,6 +213,29 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @description Get the boot device of a specified node.
|
||||||
|
*
|
||||||
|
* @param {string} nodeId - Uuid or name of the requested node.
|
||||||
|
* @return {object} Boot device.
|
||||||
|
*/
|
||||||
|
function getNodeBootDevice(nodeId) {
|
||||||
|
return angular.isDefined(nodes[nodeId])
|
||||||
|
? nodes[nodeId].bootDevice : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Get the list of supported boot devices of
|
||||||
|
* a specified node.
|
||||||
|
*
|
||||||
|
* @param {string} nodeId - Uuid or name of the requested node.
|
||||||
|
* @return {string []} List of supported boot devices.
|
||||||
|
*/
|
||||||
|
function getNodeSupportedBootDevices(nodeId) {
|
||||||
|
return angular.isDefined(nodes[nodeId])
|
||||||
|
? nodes[nodeId].supportedBootDevices : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
* @description Get the console-url for a specified node.
|
* @description Get the console-url for a specified node.
|
||||||
*
|
*
|
||||||
* @param {string} nodeId - Uuid or name of the node.
|
* @param {string} nodeId - Uuid or name of the node.
|
||||||
@ -493,7 +522,47 @@
|
|||||||
$httpBackend.whenGET(/\/api\/ironic\/nodes\/([^\/]+)\/boot_device$/,
|
$httpBackend.whenGET(/\/api\/ironic\/nodes\/([^\/]+)\/boot_device$/,
|
||||||
undefined,
|
undefined,
|
||||||
['nodeId'])
|
['nodeId'])
|
||||||
.respond(responseCode.SUCCESS, service.params.bootDevice);
|
.respond(function(method, url, data, headers, params) {
|
||||||
|
if (angular.isDefined(nodes[params.nodeId])) {
|
||||||
|
return [200, nodes[params.nodeId].bootDevice];
|
||||||
|
} else {
|
||||||
|
return [400, null];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get supported boot devices
|
||||||
|
$httpBackend.whenGET(
|
||||||
|
/\/api\/ironic\/nodes\/([^\/]+)\/boot_device\/supported$/,
|
||||||
|
undefined,
|
||||||
|
['nodeId'])
|
||||||
|
.respond(function(method, url, data, headers, params) {
|
||||||
|
if (angular.isDefined(nodes[params.nodeId])) {
|
||||||
|
return [200, nodes[params.nodeId].supportedBootDevices];
|
||||||
|
} else {
|
||||||
|
return [400, null];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set boot device
|
||||||
|
$httpBackend.whenPUT(/\/api\/ironic\/nodes\/(.+)\/boot_device/,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
['nodeId'])
|
||||||
|
.respond(function(method, url, data, headers, params) {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
var status = 404;
|
||||||
|
if (angular.isDefined(nodes[params.nodeId])) {
|
||||||
|
var node = nodes[params.nodeId];
|
||||||
|
if (node.supportedBootDevices.indexOf(data.boot_device) !== -1) {
|
||||||
|
node.bootDevice.boot_device = data.boot_device;
|
||||||
|
if (angular.isDefined(data.persistent)) {
|
||||||
|
node.bootDevice.persistent = data.persistent;
|
||||||
|
}
|
||||||
|
status = 200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [status, null];
|
||||||
|
});
|
||||||
|
|
||||||
// Validate the interfaces associated with a specified node
|
// Validate the interfaces associated with a specified node
|
||||||
$httpBackend.whenGET(/\/api\/ironic\/nodes\/([^\/]+)\/validate$/,
|
$httpBackend.whenGET(/\/api\/ironic\/nodes\/([^\/]+)\/validate$/,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* © Copyright 2015,2016 Hewlett Packard Enterprise Development Company LP
|
* © Copyright 2015,2016 Hewlett Packard Enterprise Development Company LP
|
||||||
* © Copyright 2016 Cray Inc.
|
* © Copyright 2016 Cray Inc.
|
||||||
|
* Copyright 2017 Intel Corporation
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -50,9 +51,11 @@
|
|||||||
getNodes: getNodes,
|
getNodes: getNodes,
|
||||||
getPortsWithNode: getPortsWithNode,
|
getPortsWithNode: getPortsWithNode,
|
||||||
getBootDevice: getBootDevice,
|
getBootDevice: getBootDevice,
|
||||||
|
getSupportedBootDevices: getSupportedBootDevices,
|
||||||
nodeGetConsole: nodeGetConsole,
|
nodeGetConsole: nodeGetConsole,
|
||||||
nodeSetConsoleMode: nodeSetConsoleMode,
|
nodeSetConsoleMode: nodeSetConsoleMode,
|
||||||
nodeSetMaintenance: nodeSetMaintenance,
|
nodeSetMaintenance: nodeSetMaintenance,
|
||||||
|
nodeSetBootDevice: nodeSetBootDevice,
|
||||||
nodeSetPowerState: nodeSetPowerState,
|
nodeSetPowerState: nodeSetPowerState,
|
||||||
setNodeProvisionState: setNodeProvisionState,
|
setNodeProvisionState: setNodeProvisionState,
|
||||||
updateNode: updateNode,
|
updateNode: updateNode,
|
||||||
@ -119,11 +122,11 @@
|
|||||||
* @description Retrieve the boot device for a node
|
* @description Retrieve the boot device for a node
|
||||||
* https://developer.openstack.org/api-ref/baremetal/#get-boot-device
|
* https://developer.openstack.org/api-ref/baremetal/#get-boot-device
|
||||||
*
|
*
|
||||||
* @param {string} uuid – UUID or logical name of a node.
|
* @param {string} nodeId – UUID or logical name of a node.
|
||||||
* @return {promise} Dictionary describing the current boot device
|
* @return {promise} Dictionary describing the current boot device
|
||||||
*/
|
*/
|
||||||
function getBootDevice(uuid) {
|
function getBootDevice(nodeId) {
|
||||||
return apiService.get('/api/ironic/nodes/' + uuid + '/boot_device')
|
return apiService.get('/api/ironic/nodes/' + nodeId + '/boot_device')
|
||||||
.then(function(response) {
|
.then(function(response) {
|
||||||
return response.data;
|
return response.data;
|
||||||
})
|
})
|
||||||
@ -137,6 +140,30 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Retrieve the supported boot devices for a node
|
||||||
|
* https://developer.openstack.org/api-ref/baremetal/#get-supported-boot-devices
|
||||||
|
*
|
||||||
|
* @param {string} nodeId – UUID or logical name of a node.
|
||||||
|
* @return {promise} List of supported boot devices
|
||||||
|
*/
|
||||||
|
function getSupportedBootDevices(nodeId) {
|
||||||
|
return apiService.get('/api/ironic/nodes/' + nodeId +
|
||||||
|
'/boot_device/supported')
|
||||||
|
.then(function(response) {
|
||||||
|
return response.data; // List of supported boot devices
|
||||||
|
})
|
||||||
|
.catch(function(response) {
|
||||||
|
var msg = interpolate(
|
||||||
|
gettext(
|
||||||
|
'Unable to retrieve supported boot devices for Ironic node. %s'),
|
||||||
|
[response.data],
|
||||||
|
false);
|
||||||
|
toastService.add('error', msg);
|
||||||
|
return $q.reject(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Retrieve a list of ports associated with a node.
|
* @description Retrieve a list of ports associated with a node.
|
||||||
*
|
*
|
||||||
@ -200,6 +227,33 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Set the boot device of a node
|
||||||
|
*
|
||||||
|
* http://developer.openstack.org/api-ref/baremetal/#set-boot-device
|
||||||
|
*
|
||||||
|
* @param {string} nodeId – UUID or logical name of a node.
|
||||||
|
* @param {string} bootDevice - Selected boot device.
|
||||||
|
* @param {Boolean} persistent - True or False.
|
||||||
|
* @return {promise} Promise
|
||||||
|
*/
|
||||||
|
function nodeSetBootDevice(nodeId, bootDevice, persistent) {
|
||||||
|
return apiService.put('/api/ironic/nodes/' + nodeId + '/boot_device',
|
||||||
|
{boot_device: bootDevice,
|
||||||
|
persistent: persistent})
|
||||||
|
.then(function() {
|
||||||
|
toastService.add('success',
|
||||||
|
gettext('Refresh page to see set boot device'));
|
||||||
|
})
|
||||||
|
.catch(function(response) {
|
||||||
|
var msg = interpolate(gettext('Unable to set boot device: %s'),
|
||||||
|
[response.data],
|
||||||
|
false);
|
||||||
|
toastService.add('error', msg);
|
||||||
|
return $q.reject(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Set the power state of the node.
|
* @description Set the power state of the node.
|
||||||
*
|
*
|
||||||
|
@ -31,7 +31,9 @@
|
|||||||
'getPortgroups',
|
'getPortgroups',
|
||||||
'getPortsWithNode',
|
'getPortsWithNode',
|
||||||
'getBootDevice',
|
'getBootDevice',
|
||||||
|
'getSupportedBootDevices',
|
||||||
'nodeGetConsole',
|
'nodeGetConsole',
|
||||||
|
'nodeSetBootDevice',
|
||||||
'nodeSetConsoleMode',
|
'nodeSetConsoleMode',
|
||||||
'nodeSetPowerState',
|
'nodeSetPowerState',
|
||||||
'nodeSetMaintenance',
|
'nodeSetMaintenance',
|
||||||
@ -296,28 +298,91 @@
|
|||||||
.then(function(node) {
|
.then(function(node) {
|
||||||
expect(node.console_enabled).toEqual(false);
|
expect(node.console_enabled).toEqual(false);
|
||||||
return node;
|
return node;
|
||||||
});
|
})
|
||||||
|
.catch(failTest);
|
||||||
|
|
||||||
ironicBackendMockService.flush();
|
ironicBackendMockService.flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getBootDevice', function() {
|
it('getBootDevice', function() {
|
||||||
createNode({driver: defaultDriver})
|
createNode({driver: defaultDriver})
|
||||||
.then(function(node) {
|
|
||||||
expect(node.console_enabled).toEqual(false);
|
|
||||||
return node;
|
|
||||||
})
|
|
||||||
.then(function(node) {
|
.then(function(node) {
|
||||||
return ironicAPI.getBootDevice(node.uuid)
|
return ironicAPI.getBootDevice(node.uuid)
|
||||||
.then(function(bootDevice) {
|
.then(function(bootDevice) {
|
||||||
return bootDevice;
|
return {node: node, bootDevice: bootDevice};
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(function(bootDevice) {
|
.then(function(data) {
|
||||||
expect(bootDevice).toEqual(
|
expect(data.bootDevice).toEqual(
|
||||||
ironicBackendMockService.params.bootDevice);
|
ironicBackendMockService.getNodeBootDevice(data.node.uuid));
|
||||||
|
})
|
||||||
|
.catch(failTest);
|
||||||
|
|
||||||
|
ironicBackendMockService.flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getSupportedBootDevices', function() {
|
||||||
|
createNode({driver: defaultDriver})
|
||||||
|
.then(function(node) {
|
||||||
|
return ironicAPI.getSupportedBootDevices(node.uuid);
|
||||||
|
})
|
||||||
|
.then(function(bootDevices) {
|
||||||
|
expect(bootDevices).toEqual(
|
||||||
|
ironicBackendMockService.params.supportedBootDevices);
|
||||||
|
})
|
||||||
|
.catch(failTest);
|
||||||
|
|
||||||
|
ironicBackendMockService.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('nodeSetBootDevice', function() {
|
||||||
|
var bootDevice = {
|
||||||
|
boot_device: "bios",
|
||||||
|
persistent: false
|
||||||
|
};
|
||||||
|
|
||||||
|
createNode({driver: defaultDriver})
|
||||||
|
.then(function(node) {
|
||||||
|
return ironicAPI.nodeSetBootDevice(node.uuid,
|
||||||
|
bootDevice.boot_device,
|
||||||
|
bootDevice.persistent)
|
||||||
|
.then(function() {
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function(node) {
|
||||||
|
ironicAPI.getBootDevice(node.uuid).then(function(device) {
|
||||||
|
expect(device).toEqual(bootDevice);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(failTest);
|
||||||
|
|
||||||
|
ironicBackendMockService.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('nodeSetBootDevice - bad device', function() {
|
||||||
|
createNode({driver: defaultDriver})
|
||||||
|
.then(function(node) {
|
||||||
|
return ironicAPI.getBootDevice(node.uuid)
|
||||||
|
.then(function(device) {
|
||||||
|
return {node: node, currentBootDevice: device};
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function(data) {
|
||||||
|
ironicAPI.nodeSetBootDevice(data.node.uuid,
|
||||||
|
"bad-device",
|
||||||
|
false)
|
||||||
|
.then(failTest)
|
||||||
|
.catch(function() {
|
||||||
|
// Ensure the boot device is unchanged
|
||||||
|
ironicAPI.getBootDevice(data.node.uuid)
|
||||||
|
.then(function(device) {
|
||||||
|
expect(device).toEqual(data.currentBootDevice);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(failTest);
|
||||||
|
|
||||||
ironicBackendMockService.flush();
|
ironicBackendMockService.flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
'horizon.dashboard.admin.ironic.create-port.service',
|
'horizon.dashboard.admin.ironic.create-port.service',
|
||||||
'horizon.dashboard.admin.ironic.edit-port.service',
|
'horizon.dashboard.admin.ironic.edit-port.service',
|
||||||
'horizon.dashboard.admin.ironic.maintenance.service',
|
'horizon.dashboard.admin.ironic.maintenance.service',
|
||||||
|
'horizon.dashboard.admin.ironic.bootdevice.service',
|
||||||
'horizon.dashboard.admin.ironic.node-state-transition.service',
|
'horizon.dashboard.admin.ironic.node-state-transition.service',
|
||||||
'horizon.dashboard.admin.ironic.validUuidPattern'
|
'horizon.dashboard.admin.ironic.validUuidPattern'
|
||||||
];
|
];
|
||||||
@ -47,6 +48,7 @@
|
|||||||
createPortService,
|
createPortService,
|
||||||
editPortService,
|
editPortService,
|
||||||
maintenanceService,
|
maintenanceService,
|
||||||
|
bootDeviceService,
|
||||||
nodeStateTransitionService,
|
nodeStateTransitionService,
|
||||||
validUuidPattern) {
|
validUuidPattern) {
|
||||||
var ctrl = this;
|
var ctrl = this;
|
||||||
@ -57,6 +59,7 @@
|
|||||||
|
|
||||||
ctrl.actions = actions;
|
ctrl.actions = actions;
|
||||||
ctrl.maintenanceService = maintenanceService;
|
ctrl.maintenanceService = maintenanceService;
|
||||||
|
ctrl.bootDeviceService = bootDeviceService;
|
||||||
|
|
||||||
ctrl.sections = [
|
ctrl.sections = [
|
||||||
{
|
{
|
||||||
|
@ -37,6 +37,14 @@
|
|||||||
"Maintenance off" : "Maintenance on" | translate $}</span>
|
"Maintenance off" : "Maintenance on" | translate $}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a role="menuitem"
|
||||||
|
ng-click="ctrl.bootDeviceService.setBootDevice(ctrl.node);
|
||||||
|
$event.stopPropagation();
|
||||||
|
$event.preventDefault()">
|
||||||
|
<span>{$ "Set boot device" | translate $}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li role="presentation"
|
<li role="presentation"
|
||||||
ng-repeat="transition in ctrl.nodeStateTransitions">
|
ng-repeat="transition in ctrl.nodeStateTransitions">
|
||||||
<a role="menuitem"
|
<a role="menuitem"
|
||||||
|
@ -382,18 +382,6 @@
|
|||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Boot Device -->
|
|
||||||
<div class="col-md-6 status detail">
|
|
||||||
<h4 translate>Boot Device</h4>
|
|
||||||
<hr class="header_rule">
|
|
||||||
<dl class="dl-horizontal">
|
|
||||||
<dt translate>Device</dt>
|
|
||||||
<dd>{$ ctrl.node.bootDevice.boot_device | noValue $}</dd>
|
|
||||||
<dt translate>Persistent</dt>
|
|
||||||
<dd>{$ ctrl.node.bootDevice.persistent | noValue $}</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -407,4 +395,16 @@
|
|||||||
<dd ng-repeat-end>{$ propertyValue | noValue $}</dd>
|
<dd ng-repeat-end>{$ propertyValue | noValue $}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Boot Device -->
|
||||||
|
<div class="col-md-6 status detail">
|
||||||
|
<h4 translate>Boot Device</h4>
|
||||||
|
<hr class="header_rule">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt translate>Device</dt>
|
||||||
|
<dd>{$ ctrl.node.bootDevice.boot_device | noValue $}</dd>
|
||||||
|
<dt translate>Persistent</dt>
|
||||||
|
<dd>{$ ctrl.node.bootDevice.persistent | noValue $}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
'horizon.app.core.openstack-service-api.ironic',
|
'horizon.app.core.openstack-service-api.ironic',
|
||||||
'horizon.dashboard.admin.ironic.actions',
|
'horizon.dashboard.admin.ironic.actions',
|
||||||
'horizon.dashboard.admin.ironic.maintenance.service',
|
'horizon.dashboard.admin.ironic.maintenance.service',
|
||||||
|
'horizon.dashboard.admin.ironic.bootdevice.service',
|
||||||
'horizon.dashboard.admin.ironic.enroll-node.service',
|
'horizon.dashboard.admin.ironic.enroll-node.service',
|
||||||
'horizon.dashboard.admin.ironic.edit-node.service',
|
'horizon.dashboard.admin.ironic.edit-node.service',
|
||||||
'horizon.dashboard.admin.ironic.create-port.service',
|
'horizon.dashboard.admin.ironic.create-port.service',
|
||||||
@ -38,6 +39,7 @@
|
|||||||
ironic,
|
ironic,
|
||||||
actions,
|
actions,
|
||||||
maintenanceService,
|
maintenanceService,
|
||||||
|
bootDeviceService,
|
||||||
enrollNodeService,
|
enrollNodeService,
|
||||||
editNodeService,
|
editNodeService,
|
||||||
createPortService,
|
createPortService,
|
||||||
@ -48,6 +50,7 @@
|
|||||||
ctrl.nodesSrc = [];
|
ctrl.nodesSrc = [];
|
||||||
ctrl.actions = actions;
|
ctrl.actions = actions;
|
||||||
ctrl.maintenanceService = maintenanceService;
|
ctrl.maintenanceService = maintenanceService;
|
||||||
|
ctrl.bootDeviceService = bootDeviceService;
|
||||||
|
|
||||||
ctrl.enrollNode = enrollNode;
|
ctrl.enrollNode = enrollNode;
|
||||||
ctrl.editNode = editNode;
|
ctrl.editNode = editNode;
|
||||||
|
@ -163,6 +163,14 @@
|
|||||||
"Maintenance off" : "Maintenance on" | translate $}</span>
|
"Maintenance off" : "Maintenance on" | translate $}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a role="menuitem"
|
||||||
|
ng-click="table.bootDeviceService.setBootDevice(node);
|
||||||
|
$event.stopPropagation();
|
||||||
|
$event.preventDefault()">
|
||||||
|
<span>{$ "Set boot device" | translate $}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li role="presentation"
|
<li role="presentation"
|
||||||
ng-class="{disabled: !(
|
ng-class="{disabled: !(
|
||||||
node.provision_state === 'available' ||
|
node.provision_state === 'available' ||
|
||||||
|
Loading…
Reference in New Issue
Block a user