From 58df012f66d25ca58709683bf0bd0e48e9323853 Mon Sep 17 00:00:00 2001 From: Peter Piela Date: Tue, 13 Dec 2016 12:10:50 -0500 Subject: [PATCH] Extend support for the Ironic state machine The following changes have been made: (1) Create a separate node-state-transition service that is used to orchestrate changes in the node provision state (2) Extend the node list and detail UIs (actions) to support states other the manageable, available, and active (3) It should be noted that state transition actions are rendered as html list items as opposed to actions. The reason for this is that the tag is not compatible with the ng-repeat directive. Change-Id: Id6543667f944c47f8f9727c1c2f3085ae75a1625 --- .../dashboard/admin/ironic/ironic.service.js | 55 -------- .../admin/ironic/node-actions.service.js | 5 - .../node-details/node-details.controller.js | 5 + .../ironic/node-details/node-details.html | 23 ++-- .../ironic/node-list/node-list.controller.js | 11 +- .../admin/ironic/node-list/node-list.html | 24 ++-- .../ironic/node-state-transition.service.js | 128 ++++++++++++++++++ 7 files changed, 164 insertions(+), 87 deletions(-) create mode 100644 ironic_ui/static/dashboard/admin/ironic/node-state-transition.service.js diff --git a/ironic_ui/static/dashboard/admin/ironic/ironic.service.js b/ironic_ui/static/dashboard/admin/ironic/ironic.service.js index 83eaaf9c..12198724 100755 --- a/ironic_ui/static/dashboard/admin/ironic/ironic.service.js +++ b/ironic_ui/static/dashboard/admin/ironic/ironic.service.js @@ -18,41 +18,6 @@ (function () { 'use strict'; - var provisionStateTransitionMatrix = { - enroll: { - manageable: 'manage' - }, - manageable: { - active: 'adopt', - available: 'provide' - }, - active: { - available: 'deleted' - }, - available: { - active: 'active', - manageable: 'manage' - }, - 'adopt failed': { - manageable: 'manage', - active: 'adopt' - }, - 'inspect failed': { - manageable: 'manage' - }, - 'clean failed': { - manageable: 'manage' - }, - 'deploy failed': { - active: 'active', - manageable: 'deleted' - }, - error: { - active: 'rebuild', - manageable: 'deleted' - } - }; - angular .module('horizon.app.core.openstack-service-api') .factory('horizon.app.core.openstack-service-api.ironic', ironicAPI); @@ -82,7 +47,6 @@ getNode: getNode, getNodes: getNodes, getPortsWithNode: getPortsWithNode, - getProvisionStateTransitionVerb: getProvisionStateTransitionVerb, powerOffNode: powerOffNode, powerOnNode: powerOnNode, putNodeInMaintenanceMode: putNodeInMaintenanceMode, @@ -447,25 +411,6 @@ toastService.add('error', interpolate(msg, [reason], false)); }); } - - /** - * @description Get the verb used to transition a node from a source - * provision-state to a target provision-state - * - * @param {string} sourceState – source state - * @param {string} targetState – target state - * @return {string} Verb used to transition from source to target state. - * null if the requested transition is not allowed. - */ - function getProvisionStateTransitionVerb(sourceState, targetState) { - var verb = null; - if (angular.isDefined(provisionStateTransitionMatrix[sourceState]) && - angular.isDefined( - provisionStateTransitionMatrix[sourceState][targetState])) { - verb = provisionStateTransitionMatrix[sourceState][targetState]; - } - return verb; - } } }()); diff --git a/ironic_ui/static/dashboard/admin/ironic/node-actions.service.js b/ironic_ui/static/dashboard/admin/ironic/node-actions.service.js index d90af358..34ad4a3c 100755 --- a/ironic_ui/static/dashboard/admin/ironic/node-actions.service.js +++ b/ironic_ui/static/dashboard/admin/ironic/node-actions.service.js @@ -75,7 +75,6 @@ deleteNodes: deleteNodes, deletePort: deletePort, deletePorts: deletePorts, - getProvisionStateTransitionVerb: getProvisionStateTransitionVerb, powerOn: powerOn, powerOff: powerOff, powerOnAll: powerOnNodes, @@ -207,10 +206,6 @@ ironic.setNodeProvisionState(args.node.uuid, args.verb); } - function getProvisionStateTransitionVerb(sourceState, targetState) { - return ironic.getProvisionStateTransitionVerb(sourceState, targetState); - } - function createPort(node) { return createPortService.modal(node); } diff --git a/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.controller.js b/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.controller.js index 00acd79f..d1d3e5ab 100755 --- a/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.controller.js +++ b/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.controller.js @@ -33,6 +33,7 @@ 'horizon.dashboard.admin.ironic.basePath', 'horizon.dashboard.admin.ironic.edit-node.service', 'horizon.dashboard.admin.ironic.maintenance.service', + 'horizon.dashboard.admin.ironic.node-state-transition.service', 'horizon.dashboard.admin.ironic.validUuidPattern' ]; @@ -46,6 +47,7 @@ basePath, editNodeService, maintenanceService, + nodeStateTransitionService, validUuidPattern) { var ctrl = this; var path = basePath + '/node-details/sections/'; @@ -67,6 +69,7 @@ ctrl.node = null; ctrl.nodeValidation = {}; + ctrl.nodeStateTransitions = []; ctrl.ports = []; ctrl.portsSrc = []; ctrl.basePath = basePath; @@ -121,6 +124,8 @@ var uuid = $location.absUrl().match(pattern)[2]; retrieveNode(uuid).then(function () { + ctrl.nodeStateTransitions = + nodeStateTransitionService.getTransitions(ctrl.node.provision_state); retrievePorts(uuid); ironic.validateNode(uuid).then(function(response) { ctrl.nodeValidation = response.data; diff --git a/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.html b/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.html index 5dcb3b0b..473403ac 100644 --- a/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.html +++ b/ironic_ui/static/dashboard/admin/ironic/node-details/node-details.html @@ -32,18 +32,17 @@ disabled="!ctrl.node.maintenance"> {$ 'Maintenance off' | translate $} - - {$ ('Move to ' | translate) + targetState $} - +
  • + + {$ transition.label $} + +
  • {$ 'Edit' | translate $} diff --git a/ironic_ui/static/dashboard/admin/ironic/node-list/node-list.controller.js b/ironic_ui/static/dashboard/admin/ironic/node-list/node-list.controller.js index 1b522833..a130cc98 100755 --- a/ironic_ui/static/dashboard/admin/ironic/node-list/node-list.controller.js +++ b/ironic_ui/static/dashboard/admin/ironic/node-list/node-list.controller.js @@ -31,7 +31,8 @@ 'horizon.dashboard.admin.ironic.actions', 'horizon.dashboard.admin.ironic.maintenance.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.node-state-transition.service' ]; function IronicNodeListController($scope, @@ -43,7 +44,8 @@ actions, maintenanceService, enrollNodeService, - editNodeService) { + editNodeService, + nodeStateTransitionService) { var ctrl = this; ctrl.nodes = []; @@ -57,6 +59,7 @@ ctrl.enrollNode = enrollNode; ctrl.editNode = editNode; ctrl.refresh = refresh; + ctrl.getNodeStateTransitions = getNodeStateTransitions; /** * Filtering - client-side MagicSearch @@ -188,6 +191,10 @@ function refresh() { init(); } + + function getNodeStateTransitions(node) { + return nodeStateTransitionService.getTransitions(node.provision_state); + } } })(); diff --git a/ironic_ui/static/dashboard/admin/ironic/node-list/node-list.html b/ironic_ui/static/dashboard/admin/ironic/node-list/node-list.html index 91abbd37..1732a2b5 100644 --- a/ironic_ui/static/dashboard/admin/ironic/node-list/node-list.html +++ b/ironic_ui/static/dashboard/admin/ironic/node-list/node-list.html @@ -172,19 +172,17 @@ item="node"> {$ 'Create port' | translate $} - - {$ ('Move to ' | translate) + targetState $} - +
  • + + {$ transition.label $} + +
  • diff --git a/ironic_ui/static/dashboard/admin/ironic/node-state-transition.service.js b/ironic_ui/static/dashboard/admin/ironic/node-state-transition.service.js new file mode 100644 index 00000000..46ea0e82 --- /dev/null +++ b/ironic_ui/static/dashboard/admin/ironic/node-state-transition.service.js @@ -0,0 +1,128 @@ +/* + * © Copyright 2016 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'; + + angular + .module('horizon.dashboard.admin.ironic') + .service('horizon.dashboard.admin.ironic.node-state-transition.service', + nodeStateTransitionService); + + nodeStateTransitionService.$inject = []; + + /** + * @description Utility service for working with the node state machine + * @return {void} + */ + function nodeStateTransitionService() { + // Dictionary of NodeState objects indexed by state name + var states = {}; + + // Create node state objects + angular.forEach(['enroll', + 'manageable', + 'active', + 'available', + 'adopt failed', + 'clean failed', + 'inspect failed', + 'clean wait', + 'deploy failed', + 'error'], + function(state) { + states[state] = new NodeState(state); + }); + + // Add state transitions + + states.enroll.addTransition('manageable', 'manage'); + + states.manageable.addTransition('active', 'adopt'); + states.manageable.addTransition('available', 'provide'); + + states.active.addTransition('available', 'deleted'); + + states.available.addTransition('active', 'active'); + states.available.addTransition('manageable', 'manage'); + + states['adopt failed'].addTransition('manageable', 'manage'); + states['adopt failed'].addTransition('active', 'adopt'); + + states['inspect failed'].addTransition('manageable', 'manage'); + + states['clean failed'].addTransition('manageable', 'manage'); + + states['deploy failed'].addTransition('active', 'active'); + states['deploy failed'].addTransition('manageable', 'deleted'); + + states.error.addTransition('active', 'rebuild'); + states.error.addTransition('manageable', 'deleted'); + + /** + * @description Class constructor for NodeState object. + * A NodeState maintains a set of transitions to other states. + * + * @param {name} name – Name of state + * @return {void} + */ + function NodeState(name) { + this.name = name; + this.transitions = {}; + + /** + * @description Add a transition to a specified target state. + * + * @param {string} target – Name of target state + * @param {string} verb – Verb used to accomplish transition + * @param {string} label – Description of the transition. Optional. + * @return {void} + */ + this.addTransition = function(target, verb, label) { + this.transitions[target] = + {source: this.name, + target: target, + verb: verb, + label: angular.isDefined(label) + ? label : gettext("Move to") + " " + target}; + }; + + /** + * @description Get the transition object associated with a + * specified target state. + * + * @param {string} targetState – Name of target state + * @return {object} Transition object. A value of null + * is returned if a transition does not exist. + */ + this.getTransition = function(targetState) { + return this.transitions.hasOwnProperty(targetState) + ? this.transitions[targetState] : null; + }; + } + + this.getTransitions = function(sourceState) { + var transitions = []; + if (states.hasOwnProperty(sourceState)) { + angular.forEach(states[sourceState].transitions, + function(transition) { + transitions.push(transition); + }); + } + return transitions; + }; + } +}());