Merge "Add support for soft power transitions"

This commit is contained in:
Jenkins 2017-05-17 00:58:20 +00:00 committed by Gerrit Code Review
commit c7bec6f519
8 changed files with 144 additions and 139 deletions

View File

@ -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):

View File

@ -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

View File

@ -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() {

View File

@ -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;
}
}
})();

View File

@ -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();

View File

@ -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)

View File

@ -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 $}">

View File

@ -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>