diff --git a/ironic_ui/api/ironic.py b/ironic_ui/api/ironic.py
index 8763072f..bc115d3b 100755
--- a/ironic_ui/api/ironic.py
+++ b/ironic_ui/api/ironic.py
@@ -100,17 +100,21 @@ def node_set_power_state(request, node_id, state):
return ironicclient(request).node.set_power_state(node_id, state)
-def node_set_provision_state(request, node_uuid, state):
+def node_set_provision_state(request, node_uuid, state, cleansteps=None):
"""Set the target provision state for a given node.
:param request: HTTP request.
:param node_uuid: The UUID of the node.
:param state: the target provision state to set.
+ :param cleansteps: Optional list of cleaning steps
:return: node.
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.set_provision_state
"""
- return ironicclient(request).node.set_provision_state(node_uuid, state)
+ node_manager = ironicclient(request).node
+ return node_manager.set_provision_state(node_uuid,
+ state,
+ cleansteps=cleansteps)
def node_set_maintenance(request, node_id, state, maint_reason=None):
diff --git a/ironic_ui/api/ironic_rest_api.py b/ironic_ui/api/ironic_rest_api.py
index 8ae5c73d..9d24efbc 100755
--- a/ironic_ui/api/ironic_rest_api.py
+++ b/ironic_ui/api/ironic_rest_api.py
@@ -170,7 +170,11 @@ class StatesProvision(generic.View):
:return: Return code
"""
verb = request.DATA.get('verb')
- return ironic.node_set_provision_state(request, node_uuid, verb)
+ clean_steps = request.DATA.get('clean_steps')
+ return ironic.node_set_provision_state(request,
+ node_uuid,
+ verb,
+ clean_steps)
@urls.register
diff --git a/ironic_ui/static/dashboard/admin/ironic/clean-node/clean-node.controller.js b/ironic_ui/static/dashboard/admin/ironic/clean-node/clean-node.controller.js
new file mode 100644
index 00000000..247e129a
--- /dev/null
+++ b/ironic_ui/static/dashboard/admin/ironic/clean-node/clean-node.controller.js
@@ -0,0 +1,69 @@
+/*
+ * 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';
+
+ /**
+ * @ngdoc controller
+ * @name horizon.dashboard.admin.ironic:CleanNodeController
+ * @ngController
+ *
+ * @description
+ * Controller used to prompt the user for a list of clean-steps
+ * in JSON format that will be applied a node
+ */
+ angular
+ .module('horizon.dashboard.admin.ironic')
+ .controller('CleanNodeController', CleanNodeController);
+
+ CleanNodeController.$inject = [
+ '$uibModalInstance'
+ ];
+
+ function CleanNodeController($uibModalInstance) {
+ var ctrl = this;
+
+ ctrl.errMsg = '';
+
+ ctrl.cancel = function() {
+ $uibModalInstance.dismiss('cancel');
+ };
+
+ ctrl.clean = function(cleanSteps) {
+ try {
+ var steps = JSON.parse(cleanSteps);
+ if (angular.isArray(steps) && steps.length > 0) {
+ var valid = true;
+ angular.forEach(steps, function(step) {
+ if (angular.isUndefined(step.interface) ||
+ angular.isUndefined(step.step)) {
+ valid = false;
+ }
+ });
+ if (valid) {
+ $uibModalInstance.close(steps);
+ } else {
+ ctrl.errMsg = gettext('Each cleaning step must be an object that contains "interface" and "step" properties'); // eslint-disable-line max-len
+ }
+ } else {
+ ctrl.errMsg = gettext('Clean steps should be an non-empty array');
+ }
+ } catch (e) {
+ ctrl.errMsg = gettext('Unable to validate the JSON input');
+ }
+ };
+ }
+})();
diff --git a/ironic_ui/static/dashboard/admin/ironic/clean-node/clean-node.html b/ironic_ui/static/dashboard/admin/ironic/clean-node/clean-node.html
new file mode 100644
index 00000000..ffd1029c
--- /dev/null
+++ b/ironic_ui/static/dashboard/admin/ironic/clean-node/clean-node.html
@@ -0,0 +1,41 @@
+
+
+
+
Provide a list of cleaning steps in JSON format
+
+
+ {$ ctrl.errMsg $}
+
+
+
+
diff --git a/ironic_ui/static/dashboard/admin/ironic/clean-node/clean-node.service.js b/ironic_ui/static/dashboard/admin/ironic/clean-node/clean-node.service.js
new file mode 100644
index 00000000..cf373e45
--- /dev/null
+++ b/ironic_ui/static/dashboard/admin/ironic/clean-node/clean-node.service.js
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2016 Cray Inc.
+ * Copyright (c) 2016 Hewlett Packard Enterprise Development Company LP
+ *
+ * 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.maintenance.service
+ * @description Service for putting nodes in, and removing them from
+ * maintenance mode
+ */
+ angular
+ .module('horizon.dashboard.admin.ironic')
+ .factory('horizon.dashboard.admin.ironic.clean-node.service',
+ cleanNodeService);
+
+ cleanNodeService.$inject = [
+ '$uibModal',
+ 'horizon.dashboard.admin.ironic.basePath',
+ 'horizon.app.core.openstack-service-api.ironic'
+ ];
+
+ function cleanNodeService($uibModal, basePath, ironic) {
+ var service = {
+ clean: clean
+ };
+ return service;
+
+ /*
+ * @description Initiate manual cleaning of an Ironic node.
+ * The user is prompted for a list of steps that are then
+ * used to clean the node.
+ *
+ * @param {object} node - Node to be cleaned
+ * @return {void}
+ */
+ function clean(node) {
+ var options = {
+ controller: 'CleanNodeController as ctrl',
+ backdrop: 'static',
+ templateUrl: basePath + '/clean-node/clean-node.html'
+ };
+ $uibModal.open(options).result.then(function(cleanSteps) {
+ return ironic.setNodeProvisionState(node.uuid,
+ 'clean',
+ cleanSteps);
+ });
+ }
+ }
+})();
diff --git a/ironic_ui/static/dashboard/admin/ironic/ironic.service.js b/ironic_ui/static/dashboard/admin/ironic/ironic.service.js
index f5ad61df..211ff9a8 100755
--- a/ironic_ui/static/dashboard/admin/ironic/ironic.service.js
+++ b/ironic_ui/static/dashboard/admin/ironic/ironic.service.js
@@ -244,14 +244,14 @@
* @param {string} uuid – UUID of a node.
* @param {string} verb – Provisioning verb used to move node to desired
* target state
+ * @param {object []} cleanSteps - List of cleaning steps. Only used
+ * when the value of verb is 'clean'
* @return {promise} Promise
*/
- function setNodeProvisionState(uuid, verb) {
- var data = {
- verb: verb
- };
+ function setNodeProvisionState(uuid, verb, cleanSteps) {
return apiService.put('/api/ironic/nodes/' + uuid + '/states/provision',
- data)
+ {verb: verb,
+ clean_steps: cleanSteps})
.then(function() {
var msg = gettext(
'A request has been made to change the provisioning state of node %s');
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 641b595f..db41558a 100755
--- a/ironic_ui/static/dashboard/admin/ironic/node-actions.service.js
+++ b/ironic_ui/static/dashboard/admin/ironic/node-actions.service.js
@@ -30,6 +30,7 @@
'horizon.dashboard.admin.ironic.events',
'horizon.framework.widgets.modal.deleteModalService',
'horizon.dashboard.admin.ironic.create-port.service',
+ 'horizon.dashboard.admin.ironic.clean-node.service',
'$q',
'$rootScope'
];
@@ -39,6 +40,7 @@
ironicEvents,
deleteModalService,
createPortService,
+ cleanNodeService,
$q,
$rootScope) {
var service = {
@@ -169,7 +171,11 @@
* the node to the desired target state for the node.
*/
function setProvisionState(args) {
- ironic.setNodeProvisionState(args.node.uuid, args.verb);
+ if (args.verb === 'clean') {
+ cleanNodeService.clean(args.node);
+ } else {
+ ironic.setNodeProvisionState(args.node.uuid, args.verb);
+ }
}
function createPort(node) {
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
index d7b246a9..cbb1256e 100644
--- a/ironic_ui/static/dashboard/admin/ironic/node-state-transition.service.js
+++ b/ironic_ui/static/dashboard/admin/ironic/node-state-transition.service.js
@@ -56,6 +56,9 @@
states.manageable.addTransition('manageable',
'inspect',
gettext('Inspect'));
+ states.manageable.addTransition('manageable',
+ 'clean',
+ gettext('Clean'));
states.active.addTransition('available', 'deleted');
@@ -99,7 +102,7 @@
* @return {void}
*/
this.addTransition = function(target, verb, label) {
- this.transitions[target] =
+ this.transitions[verb] =
{source: this.name,
target: target,
verb: verb,