Add support for soft power transitions
- Add support for reboot, soft reboot, and soft power off transitions - When appropriate node action lists will contain additional items for soft power transitions - The order of items in node action lists has been slightly modified, placing a known to exist singleton "Edit" at the top of the list. Power items are now generated as a data-driven enumeration. Change-Id: If308539e78a7bf707304049534affaa8468fe9f0 Closes-Bug: #1663341
This commit is contained in:
parent
6193569103
commit
47198c2af2
@ -24,7 +24,7 @@ from horizon.utils.memoized import memoized # noqa
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
|
||||
DEFAULT_IRONIC_API_VERSION = '1.20'
|
||||
DEFAULT_IRONIC_API_VERSION = '1.27'
|
||||
DEFAULT_INSECURE = False
|
||||
DEFAULT_CACERT = None
|
||||
IRONIC_CLIENT_CLASS_NAME = 'baremetal'
|
||||
@ -86,17 +86,20 @@ def node_list_ports(request, node_id):
|
||||
return ironicclient(request).node.list_ports(node_id, limit=0, detail=True)
|
||||
|
||||
|
||||
def node_set_power_state(request, node_id, state):
|
||||
def node_set_power_state(request, node_id, state, soft=False):
|
||||
"""Set power state for a given node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:param state: the power state to set.
|
||||
:param state: the power state to set ['on', 'off', 'reboot'].
|
||||
:param soft: flag for graceful power 'off' or reboot
|
||||
:return: node.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.set_power_state
|
||||
"""
|
||||
return ironicclient(request).node.set_power_state(node_id, state)
|
||||
return ironicclient(request).node.set_power_state(node_id,
|
||||
state,
|
||||
soft)
|
||||
|
||||
|
||||
def node_set_provision_state(request, node_id, state, cleansteps=None):
|
||||
|
@ -152,8 +152,10 @@ class StatesPower(generic.View):
|
||||
:param node_id: Node name or uuid
|
||||
:return: Return code
|
||||
"""
|
||||
state = request.DATA.get('state')
|
||||
return ironic.node_set_power_state(request, node_id, state)
|
||||
return ironic.node_set_power_state(request,
|
||||
node_id,
|
||||
request.DATA.get('state'),
|
||||
request.DATA.get('soft'))
|
||||
|
||||
|
||||
@urls.register
|
||||
|
@ -52,8 +52,7 @@
|
||||
getBootDevice: getBootDevice,
|
||||
nodeGetConsole: nodeGetConsole,
|
||||
nodeSetConsoleMode: nodeSetConsoleMode,
|
||||
powerOffNode: powerOffNode,
|
||||
powerOnNode: powerOnNode,
|
||||
nodeSetPowerState: nodeSetPowerState,
|
||||
putNodeInMaintenanceMode: putNodeInMaintenanceMode,
|
||||
removeNodeFromMaintenanceMode: removeNodeFromMaintenanceMode,
|
||||
setNodeProvisionState: setNodeProvisionState,
|
||||
@ -222,39 +221,17 @@
|
||||
* http://developer.openstack.org/api-ref/baremetal/#change-node-power-state
|
||||
*
|
||||
* @param {string} uuid – UUID or logical name of a node.
|
||||
* @param {string} state - Target power state ['on', 'off', 'reboot']
|
||||
* @param {boolean} soft - Flag for graceful power 'off' or reboot
|
||||
* @return {promise} Promise
|
||||
*/
|
||||
function powerOnNode(uuid) {
|
||||
function nodeSetPowerState(uuid, state, soft) {
|
||||
var data = {
|
||||
state: 'on'
|
||||
};
|
||||
return apiService.patch('/api/ironic/nodes/' + uuid + '/states/power',
|
||||
data)
|
||||
.then(function() {
|
||||
toastService.add('success',
|
||||
gettext('Refresh page to see updated power status'));
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(gettext('Unable to power on the node: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Set the power state of the node.
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#change-node-power-state
|
||||
*
|
||||
* @param {string} uuid – UUID or logical name of a node.
|
||||
* @return {promise} Promise
|
||||
*/
|
||||
function powerOffNode(uuid) {
|
||||
var data = {
|
||||
state: 'off'
|
||||
state: state
|
||||
};
|
||||
if (angular.isDefined(soft)) {
|
||||
data.soft = soft;
|
||||
}
|
||||
return apiService.patch('/api/ironic/nodes/' + uuid + '/states/power',
|
||||
data)
|
||||
.then(function() {
|
||||
|
@ -17,8 +17,25 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var POWER_STATE_ON = 'power on';
|
||||
var POWER_STATE_OFF = 'power off';
|
||||
function PowerTransition(label, state, soft) {
|
||||
this.label = label;
|
||||
this.state = state;
|
||||
this.soft = soft;
|
||||
}
|
||||
|
||||
var POWER_ON_TRANSITIONS = [
|
||||
new PowerTransition(gettext('Power on'), 'on', false)
|
||||
];
|
||||
|
||||
var POWER_OFF_TRANSITIONS = [
|
||||
new PowerTransition(gettext('Power off'), 'off', false),
|
||||
new PowerTransition(gettext('Soft power off'), 'off', true),
|
||||
new PowerTransition(gettext('Reboot'), 'reboot', false),
|
||||
new PowerTransition(gettext('Soft reboot'), 'reboot', true)
|
||||
];
|
||||
|
||||
var ALL_POWER_TRANSITIONS =
|
||||
POWER_ON_TRANSITIONS.concat(POWER_OFF_TRANSITIONS);
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
@ -26,7 +43,6 @@
|
||||
|
||||
actions.$inject = [
|
||||
'horizon.app.core.openstack-service-api.ironic',
|
||||
'horizon.framework.widgets.toast.service',
|
||||
'horizon.dashboard.admin.ironic.events',
|
||||
'horizon.framework.widgets.modal.deleteModalService',
|
||||
'horizon.dashboard.admin.ironic.create-port.service',
|
||||
@ -36,7 +52,6 @@
|
||||
];
|
||||
|
||||
function actions(ironic,
|
||||
toastService,
|
||||
ironicEvents,
|
||||
deleteModalService,
|
||||
createPortService,
|
||||
@ -47,13 +62,11 @@
|
||||
createPort: createPort,
|
||||
deleteNode: deleteNode,
|
||||
deletePort: deletePort,
|
||||
powerOn: powerOn,
|
||||
powerOff: powerOff,
|
||||
powerOnAll: powerOnNodes,
|
||||
powerOffAll: powerOffNodes,
|
||||
setPowerState: setPowerState,
|
||||
putNodeInMaintenanceMode: putNodeInMaintenanceMode,
|
||||
removeNodeFromMaintenanceMode: removeNodeFromMaintenanceMode,
|
||||
setProvisionState: setProvisionState
|
||||
setProvisionState: setProvisionState,
|
||||
getPowerTransitions : getPowerTransitions
|
||||
};
|
||||
|
||||
return service;
|
||||
@ -87,38 +100,25 @@
|
||||
|
||||
// power state
|
||||
|
||||
function powerOn(node) {
|
||||
if (node.power_state !== POWER_STATE_OFF) {
|
||||
var msg = gettext("Node %s is not powered off.");
|
||||
return $q.reject(interpolate(msg, [node.uuid], false));
|
||||
}
|
||||
return ironic.powerOnNode(node.uuid).then(
|
||||
function() {
|
||||
// Set power state to be indeterminate
|
||||
node.power_state = null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function powerOff(node) {
|
||||
if (node.power_state !== POWER_STATE_ON) {
|
||||
var msg = gettext("Node %s is not powered on.");
|
||||
return $q.reject(interpolate(msg, [node.uuid], false));
|
||||
}
|
||||
return ironic.powerOffNode(node.uuid).then(
|
||||
function() {
|
||||
// Set power state to be indeterminate
|
||||
node.power_state = null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function powerOnNodes(nodes) {
|
||||
return applyFuncToNodes(powerOn, nodes);
|
||||
}
|
||||
|
||||
function powerOffNodes(nodes) {
|
||||
return applyFuncToNodes(powerOff, nodes);
|
||||
/**
|
||||
* @description Set the power state of a list of nodes
|
||||
*
|
||||
* @param {object[]} nodes - List of node objects
|
||||
* @param {string} state - Target power state
|
||||
* @param {boolean} [soft] - Flag for graceful power 'off' or reboot
|
||||
* @return {promise} promise
|
||||
*/
|
||||
function setPowerState(nodes, state, soft) {
|
||||
var promises = [];
|
||||
angular.forEach(nodes,
|
||||
function(node) {
|
||||
promises.push(
|
||||
ironic.nodeSetPowerState(node.uuid,
|
||||
state,
|
||||
soft)
|
||||
);
|
||||
});
|
||||
return $q.all(promises);
|
||||
}
|
||||
|
||||
// maintenance
|
||||
@ -232,6 +232,22 @@
|
||||
});
|
||||
return $q.all(promises);
|
||||
}
|
||||
|
||||
/*
|
||||
* @name horizon.dashboard.admin.ironic.actions.getPowerTransitions
|
||||
* @description Get the list of power transitions for a specified
|
||||
* node, or all power transitions if the node is not specified.
|
||||
*
|
||||
* @param {object} node – Node object for which power transitions
|
||||
* are requested. If node is undefined all possible power transitions
|
||||
* are returned.
|
||||
* @return {object[]} - List of PowerTransition objects
|
||||
*/
|
||||
function getPowerTransitions(node) {
|
||||
return angular.isUndefined(node) ? ALL_POWER_TRANSITIONS
|
||||
: node.power_state === 'power on'
|
||||
? POWER_OFF_TRANSITIONS : POWER_ON_TRANSITIONS;
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
|
@ -74,6 +74,7 @@
|
||||
ctrl.nodeValidation = [];
|
||||
ctrl.nodeValidationMap = {}; // Indexed by interface
|
||||
ctrl.nodeStateTransitions = [];
|
||||
ctrl.nodePowerTransitions = [];
|
||||
ctrl.ports = [];
|
||||
ctrl.portsSrc = [];
|
||||
ctrl.basePath = basePath;
|
||||
@ -135,6 +136,7 @@
|
||||
retrieveNode(uuid).then(function () {
|
||||
ctrl.nodeStateTransitions =
|
||||
nodeStateTransitionService.getTransitions(ctrl.node.provision_state);
|
||||
ctrl.nodePowerTransitions = actions.getPowerTransitions(ctrl.node);
|
||||
retrievePorts();
|
||||
retrieveBootDevice();
|
||||
validateNode();
|
||||
|
@ -10,18 +10,22 @@
|
||||
<action-list uib-dropdown>
|
||||
<action button-type="split-button"
|
||||
action-classes="'btn btn-default btn-sm'"
|
||||
callback="ctrl.actions.powerOn"
|
||||
item="ctrl.node"
|
||||
disabled="ctrl.node.power_state!=='power off'">
|
||||
{$ ::'Power on' | translate $}
|
||||
callback="ctrl.editNode">
|
||||
{$ ::'Edit' | translate $}
|
||||
</action>
|
||||
<menu>
|
||||
<action button-type="menu-item"
|
||||
callback="ctrl.actions.powerOff"
|
||||
item="ctrl.node"
|
||||
disabled="ctrl.node.power_state!=='power on'">
|
||||
{$ ::'Power off' | translate $}
|
||||
</action>
|
||||
<li role="presentation"
|
||||
ng-repeat="transition in ctrl.nodePowerTransitions">
|
||||
<a role="menuitem"
|
||||
ng-click="ctrl.actions.setPowerState(
|
||||
[ctrl.node],
|
||||
transition.state,
|
||||
transition.soft);
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault()">
|
||||
<span>{$ transition.label $}</span>
|
||||
</a>
|
||||
</li>
|
||||
<action button-type="menu-item"
|
||||
callback="ctrl.maintenanceService.putNodeInMaintenanceMode"
|
||||
item="[ctrl.node]"
|
||||
@ -46,8 +50,10 @@
|
||||
</a>
|
||||
</li>
|
||||
<action button-type="menu-item"
|
||||
callback="ctrl.editNode">
|
||||
{$ ::'Edit' | translate $}
|
||||
disabled="isDefined(ctrl.nodeValidationMap.console)
|
||||
&& ctrl.nodeValidationMap.console.result===false"
|
||||
callback="ctrl.toggleConsoleMode">
|
||||
{$ ctrl.node.console_enabled ? 'Disable console' : 'Enable console' | translate $}
|
||||
</action>
|
||||
<action button-type="menu-item"
|
||||
disabled="isDefined(ctrl.nodeValidationMap.console)
|
||||
|
@ -21,7 +21,7 @@
|
||||
<dd>{$ ctrl.node.reservation | noValue $}</dd>
|
||||
<dt translate>Console Enabled</dt>
|
||||
<dd>{$ ctrl.node.console_enabled | yesno $}</dd>
|
||||
<dt translate>Console Info</dt>
|
||||
<dt translate>Console Info.</dt>
|
||||
<dd ng-if="ctrl.node.console_info.type==='shellinabox'">
|
||||
<a href="{$ ctrl.node.console_info.url $}"
|
||||
title="{$ 'Click link to view console' | translate $}">
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="8">
|
||||
<th colspan="9">
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-default btn-sm"
|
||||
style="margin-right:10px;"
|
||||
@ -26,45 +26,44 @@
|
||||
<span class="fa fa-plus"></span>
|
||||
<span translate>Enroll Node</span>
|
||||
</button>
|
||||
</div>
|
||||
</th>
|
||||
<th class="action-col">
|
||||
<action-list uib-dropdown class="pull-right">
|
||||
<action button-type="split-button"
|
||||
action-classes="'btn btn-default btn-sm'"
|
||||
callback="table.actions.powerOnAll"
|
||||
item="tCtrl.selected"
|
||||
disabled="tCtrl.selected.length === 0">
|
||||
{$ ::'Power on' | translate $}
|
||||
</action>
|
||||
<menu>
|
||||
<action button-type="menu-item"
|
||||
callback="table.actions.powerOffAll"
|
||||
item="tCtrl.selected"
|
||||
disabled="tCtrl.selected.length === 0">
|
||||
{$ ::'Power off' | translate $}
|
||||
</action>
|
||||
<action button-type="menu-item"
|
||||
callback="table.maintenanceService.putNodeInMaintenanceMode"
|
||||
item="tCtrl.selected"
|
||||
disabled="tCtrl.selected.length === 0">
|
||||
{$ ::'Maintenance on' | translate $}
|
||||
</action>
|
||||
<action button-type="menu-item"
|
||||
callback="table.maintenanceService.removeNodeFromMaintenanceMode"
|
||||
item="tCtrl.selected"
|
||||
disabled="tCtrl.selected.length === 0">
|
||||
{$ ::'Maintenance off' | translate $}
|
||||
</action>
|
||||
<action button-type="menu-item"
|
||||
<action-list uib-dropdown class="pull-right">
|
||||
<action button-type="split-button"
|
||||
action-classes="'btn btn-default btn-sm'"
|
||||
callback="table.actions.deleteNode"
|
||||
item="tCtrl.selected"
|
||||
disabled="tCtrl.selected.length === 0">
|
||||
<span class="fa fa-trash"></span>
|
||||
{$ ::'Delete nodes' | translate $}
|
||||
</action>
|
||||
</menu>
|
||||
</action-list>
|
||||
<menu>
|
||||
<li role="presentation"
|
||||
ng-repeat="transition in table.actions.getPowerTransitions()"
|
||||
ng-class="{disabled: tCtrl.selected.length === 0}">
|
||||
<a role="menuitem"
|
||||
ng-click="table.actions.setPowerState(
|
||||
tCtrl.selected,
|
||||
transition.state,
|
||||
transition.soft);
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault()">
|
||||
<span>{$ transition.label $}</span>
|
||||
</a>
|
||||
</li>
|
||||
<action button-type="menu-item"
|
||||
callback="table.maintenanceService.putNodeInMaintenanceMode"
|
||||
item="tCtrl.selected"
|
||||
disabled="tCtrl.selected.length === 0">
|
||||
{$ ::'Maintenance on' | translate $}
|
||||
</action>
|
||||
<action button-type="menu-item"
|
||||
callback="table.maintenanceService.removeNodeFromMaintenanceMode"
|
||||
item="tCtrl.selected"
|
||||
disabled="tCtrl.selected.length === 0">
|
||||
{$ ::'Maintenance off' | translate $}
|
||||
</action>
|
||||
</menu>
|
||||
</action-list>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
@ -136,18 +135,23 @@
|
||||
<action-list uib-dropdown>
|
||||
<action button-type="split-button"
|
||||
action-classes="'btn btn-default btn-sm'"
|
||||
callback="table.actions.powerOn"
|
||||
disabled="node.power_state !== 'power off'"
|
||||
callback="table.editNode"
|
||||
item="node">
|
||||
{$ ::'Power on' | translate $}
|
||||
{$ ::'Edit' | translate $}
|
||||
</action>
|
||||
<menu>
|
||||
<action button-type="menu-item"
|
||||
callback="table.actions.powerOff"
|
||||
disabled="node.power_state !== 'power on'"
|
||||
item="node">
|
||||
{$ ::'Power off' | translate $}
|
||||
</action>
|
||||
<li role="presentation"
|
||||
ng-repeat="transition in table.actions.getPowerTransitions(node)">
|
||||
<a role="menuitem"
|
||||
ng-click="table.actions.setPowerState(
|
||||
[node],
|
||||
transition.state,
|
||||
transition.soft);
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault()">
|
||||
<span>{$ transition.label $}</span>
|
||||
</a>
|
||||
</li>
|
||||
<action button-type="menu-item"
|
||||
callback="table.maintenanceService.putNodeInMaintenanceMode"
|
||||
item="[node]"
|
||||
@ -183,11 +187,6 @@
|
||||
<span>{$ transition.label $}</span>
|
||||
</a>
|
||||
</li>
|
||||
<action button-type="menu-item"
|
||||
callback="table.editNode"
|
||||
item="node">
|
||||
{$ ::'Edit' | translate $}
|
||||
</action>
|
||||
</menu>
|
||||
</action-list>
|
||||
</td>
|
||||
|
Loading…
x
Reference in New Issue
Block a user