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 2016 Cray Inc.
|
||||
# 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.
|
||||
@ -229,6 +230,35 @@ def node_get_boot_device(request, 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):
|
||||
"""Retrieve a list of drivers.
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#
|
||||
# Copyright 2015, 2016 Hewlett Packard Enterprise Development Company LP
|
||||
# Copyright 2016 Cray Inc.
|
||||
# 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.
|
||||
@ -277,6 +278,37 @@ class BootDevice(generic.View):
|
||||
"""
|
||||
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
|
||||
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
|
||||
var params = {
|
||||
// Currently, all nodes have the same boot device.
|
||||
bootDevice: {boot_device: 'pxe', persistent: true},
|
||||
// Console info
|
||||
consoleType: "shellinabox",
|
||||
consoleUrl: "http://localhost:",
|
||||
defaultDriver: "agent_ipmitool"
|
||||
defaultDriver: "agent_ipmitool",
|
||||
supportedBootDevices: ["pxe", "bios", "safe"]
|
||||
};
|
||||
|
||||
// List of supported drivers
|
||||
@ -123,6 +122,8 @@
|
||||
flush: flush,
|
||||
postTest: postTest,
|
||||
getNode: getNode,
|
||||
getNodeBootDevice: getNodeBootDevice,
|
||||
getNodeSupportedBootDevices: getNodeSupportedBootDevices,
|
||||
nodeGetConsoleUrl: nodeGetConsoleUrl,
|
||||
getDrivers: getDrivers,
|
||||
getImages: getImages,
|
||||
@ -183,7 +184,12 @@
|
||||
base: node,
|
||||
consolePort: getNextAvailableSystemPort(),
|
||||
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;
|
||||
@ -196,7 +202,7 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* description Get a specified node.
|
||||
* @description Get a specified node.
|
||||
*
|
||||
* @param {string} nodeId - Uuid or name of the requested 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.
|
||||
*
|
||||
* @param {string} nodeId - Uuid or name of the node.
|
||||
@ -493,7 +522,47 @@
|
||||
$httpBackend.whenGET(/\/api\/ironic\/nodes\/([^\/]+)\/boot_device$/,
|
||||
undefined,
|
||||
['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
|
||||
$httpBackend.whenGET(/\/api\/ironic\/nodes\/([^\/]+)\/validate$/,
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* © Copyright 2015,2016 Hewlett Packard Enterprise Development Company LP
|
||||
* © Copyright 2016 Cray Inc.
|
||||
* 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.
|
||||
@ -50,9 +51,11 @@
|
||||
getNodes: getNodes,
|
||||
getPortsWithNode: getPortsWithNode,
|
||||
getBootDevice: getBootDevice,
|
||||
getSupportedBootDevices: getSupportedBootDevices,
|
||||
nodeGetConsole: nodeGetConsole,
|
||||
nodeSetConsoleMode: nodeSetConsoleMode,
|
||||
nodeSetMaintenance: nodeSetMaintenance,
|
||||
nodeSetBootDevice: nodeSetBootDevice,
|
||||
nodeSetPowerState: nodeSetPowerState,
|
||||
setNodeProvisionState: setNodeProvisionState,
|
||||
updateNode: updateNode,
|
||||
@ -119,11 +122,11 @@
|
||||
* @description Retrieve the boot device for a node
|
||||
* 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
|
||||
*/
|
||||
function getBootDevice(uuid) {
|
||||
return apiService.get('/api/ironic/nodes/' + uuid + '/boot_device')
|
||||
function getBootDevice(nodeId) {
|
||||
return apiService.get('/api/ironic/nodes/' + nodeId + '/boot_device')
|
||||
.then(function(response) {
|
||||
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.
|
||||
*
|
||||
@ -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.
|
||||
*
|
||||
|
@ -31,7 +31,9 @@
|
||||
'getPortgroups',
|
||||
'getPortsWithNode',
|
||||
'getBootDevice',
|
||||
'getSupportedBootDevices',
|
||||
'nodeGetConsole',
|
||||
'nodeSetBootDevice',
|
||||
'nodeSetConsoleMode',
|
||||
'nodeSetPowerState',
|
||||
'nodeSetMaintenance',
|
||||
@ -296,28 +298,91 @@
|
||||
.then(function(node) {
|
||||
expect(node.console_enabled).toEqual(false);
|
||||
return node;
|
||||
});
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('getBootDevice', function() {
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(node) {
|
||||
expect(node.console_enabled).toEqual(false);
|
||||
return node;
|
||||
})
|
||||
.then(function(node) {
|
||||
return ironicAPI.getBootDevice(node.uuid)
|
||||
.then(function(bootDevice) {
|
||||
return bootDevice;
|
||||
return {node: node, bootDevice: bootDevice};
|
||||
});
|
||||
})
|
||||
.then(function(bootDevice) {
|
||||
expect(bootDevice).toEqual(
|
||||
ironicBackendMockService.params.bootDevice);
|
||||
.then(function(data) {
|
||||
expect(data.bootDevice).toEqual(
|
||||
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();
|
||||
});
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
'horizon.dashboard.admin.ironic.create-port.service',
|
||||
'horizon.dashboard.admin.ironic.edit-port.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.validUuidPattern'
|
||||
];
|
||||
@ -47,6 +48,7 @@
|
||||
createPortService,
|
||||
editPortService,
|
||||
maintenanceService,
|
||||
bootDeviceService,
|
||||
nodeStateTransitionService,
|
||||
validUuidPattern) {
|
||||
var ctrl = this;
|
||||
@ -57,6 +59,7 @@
|
||||
|
||||
ctrl.actions = actions;
|
||||
ctrl.maintenanceService = maintenanceService;
|
||||
ctrl.bootDeviceService = bootDeviceService;
|
||||
|
||||
ctrl.sections = [
|
||||
{
|
||||
|
@ -37,6 +37,14 @@
|
||||
"Maintenance off" : "Maintenance on" | translate $}</span>
|
||||
</a>
|
||||
</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"
|
||||
ng-repeat="transition in ctrl.nodeStateTransitions">
|
||||
<a role="menuitem"
|
||||
|
@ -382,18 +382,6 @@
|
||||
</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 class="row">
|
||||
@ -407,4 +395,16 @@
|
||||
<dd ng-repeat-end>{$ propertyValue | noValue $}</dd>
|
||||
</dl>
|
||||
</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>
|
||||
|
@ -27,6 +27,7 @@
|
||||
'horizon.app.core.openstack-service-api.ironic',
|
||||
'horizon.dashboard.admin.ironic.actions',
|
||||
'horizon.dashboard.admin.ironic.maintenance.service',
|
||||
'horizon.dashboard.admin.ironic.bootdevice.service',
|
||||
'horizon.dashboard.admin.ironic.enroll-node.service',
|
||||
'horizon.dashboard.admin.ironic.edit-node.service',
|
||||
'horizon.dashboard.admin.ironic.create-port.service',
|
||||
@ -38,6 +39,7 @@
|
||||
ironic,
|
||||
actions,
|
||||
maintenanceService,
|
||||
bootDeviceService,
|
||||
enrollNodeService,
|
||||
editNodeService,
|
||||
createPortService,
|
||||
@ -48,6 +50,7 @@
|
||||
ctrl.nodesSrc = [];
|
||||
ctrl.actions = actions;
|
||||
ctrl.maintenanceService = maintenanceService;
|
||||
ctrl.bootDeviceService = bootDeviceService;
|
||||
|
||||
ctrl.enrollNode = enrollNode;
|
||||
ctrl.editNode = editNode;
|
||||
|
@ -163,6 +163,14 @@
|
||||
"Maintenance off" : "Maintenance on" | translate $}</span>
|
||||
</a>
|
||||
</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"
|
||||
ng-class="{disabled: !(
|
||||
node.provision_state === 'available' ||
|
||||
|
Loading…
Reference in New Issue
Block a user