summaryrefslogtreecommitdiff
path: root/ironic/conductor
diff options
context:
space:
mode:
authorDevananda van der Veen <devananda.vdv@gmail.com>2015-01-26 07:39:41 -0800
committerDevananda van der Veen <devananda.vdv@gmail.com>2015-02-04 19:11:07 -0800
commit5dbd06f89da2ef8395460d0c4af8f4bfc1963030 (patch)
treee587b1f82f9db83f6402cd6baa1247fc735d424f /ironic/conductor
parentb0af29e66478eeb48d42ba7be720f9ae06ab1044 (diff)
downloadironic-5dbd06f89da2ef8395460d0c4af8f4bfc1963030.tar.gz
Add MANAGEABLE state and associated transitions
This patch adds the MANAGEABLE state, and associated transitions to and from AVAILABLE, as well as a new RPC method to initiate transitions between provisioning states. It also adds a new VERBS entity to ironic/common/states.py to hold the action-phrases, as described in blueprint new-ironic-state-machine. This provides a single place to check whether a requested target state is allowed to be requested by checking if the value is "in" the keys of this dictionary. It also provides a mapping for two phrases which are currently inconsistent: target:deleted -> action:delete target:active -> action:deploy It updates the API controller for nodes.py without changing the HTTP status codes which are returned in exception cases. The changes in unit tests now prepare the node appropriately, but do not change the assertions being tested. Juno allowed the rebuild of a failed deploy to be initiated by PUTting a target state of "active"; this behavior, while inconsistent with the new state machine, is added to the state model for compatibility, now that stronger checking is done within the API service, based on the actual modelling of state transitions. Implements: blueprint new-ironic-state-machine Co-Authored-By: David Shrewsbury <shrewsbury.dave@gmail.com> Change-Id: Id0af5a607b7432d287eef643c1e30e324dc4f879
Diffstat (limited to 'ironic/conductor')
-rw-r--r--ironic/conductor/manager.py60
-rw-r--r--ironic/conductor/rpcapi.py22
2 files changed, 63 insertions, 19 deletions
diff --git a/ironic/conductor/manager.py b/ironic/conductor/manager.py
index 90e2e426f..f6251a0c4 100644
--- a/ironic/conductor/manager.py
+++ b/ironic/conductor/manager.py
@@ -171,7 +171,7 @@ class ConductorManager(periodic_task.PeriodicTasks):
"""Ironic Conductor manager main class."""
# NOTE(rloo): This must be in sync with rpcapi.ConductorAPI's.
- RPC_API_VERSION = '1.22'
+ RPC_API_VERSION = '1.23'
target = messaging.Target(version=RPC_API_VERSION)
@@ -600,8 +600,7 @@ class ConductorManager(periodic_task.PeriodicTasks):
exception.NodeLocked,
exception.NodeInMaintenance,
exception.InstanceDeployFailure,
- exception.InvalidParameterValue,
- exception.MissingParameterValue)
+ exception.InvalidStateRequested)
def do_node_deploy(self, context, node_id, rebuild=False,
configdrive=None):
"""RPC method to initiate deployment to a node.
@@ -621,6 +620,8 @@ class ConductorManager(periodic_task.PeriodicTasks):
:raises: NodeInMaintenance if the node is in maintenance mode.
:raises: NoFreeConductorWorker when there is no free worker to start
async task.
+ :raises: InvalidStateRequested when the requested state is not a valid
+ target from the current state.
"""
LOG.debug("RPC do_node_deploy called for node %s." % node_id)
@@ -666,18 +667,14 @@ class ConductorManager(periodic_task.PeriodicTasks):
configdrive),
err_handler=provisioning_error_handler)
except exception.InvalidState:
- raise exception.InstanceDeployFailure(_(
- "Request received to %(what)s %(node)s, but "
- "this is not possible in the current state of "
- "'%(state)s'. ") % {'what': event,
- 'node': node.uuid,
- 'state': node.provision_state})
+ raise exception.InvalidStateRequested(
+ action=event, node=task.node.uuid,
+ state=task.node.provision_state)
@messaging.expected_exceptions(exception.NoFreeConductorWorker,
exception.NodeLocked,
exception.InstanceDeployFailure,
- exception.InvalidParameterValue,
- exception.MissingParameterValue)
+ exception.InvalidStateRequested)
def do_node_tear_down(self, context, node_id):
"""RPC method to tear down an existing node deployment.
@@ -689,12 +686,13 @@ class ConductorManager(periodic_task.PeriodicTasks):
:raises: InstanceDeployFailure
:raises: NoFreeConductorWorker when there is no free worker to start
async task
+ :raises: InvalidStateRequested when the requested state is not a valid
+ target from the current state.
"""
LOG.debug("RPC do_node_tear_down called for node %s." % node_id)
with task_manager.acquire(context, node_id, shared=False) as task:
- node = task.node
try:
# NOTE(ghe): Valid power driver values are needed to perform
# a tear-down. Deploy info is useful to purge the cache but not
@@ -703,8 +701,8 @@ class ConductorManager(periodic_task.PeriodicTasks):
except (exception.InvalidParameterValue,
exception.MissingParameterValue) as e:
raise exception.InstanceDeployFailure(_(
- "RPC do_node_tear_down failed to validate power info. "
- "Error: %(msg)s") % {'msg': e})
+ "Failed to validate power driver interface. "
+ "Can not delete instance. Error: %(msg)s") % {'msg': e})
try:
task.process_event('delete',
@@ -712,10 +710,36 @@ class ConductorManager(periodic_task.PeriodicTasks):
call_args=(do_node_tear_down, task),
err_handler=provisioning_error_handler)
except exception.InvalidState:
- raise exception.InstanceDeployFailure(_(
- "RPC do_node_tear_down "
- "not allowed for node %(node)s in state %(state)s")
- % {'node': node_id, 'state': node.provision_state})
+ raise exception.InvalidStateRequested(
+ action='delete', node=task.node.uuid,
+ state=task.node.provision_state)
+
+ @messaging.expected_exceptions(exception.NoFreeConductorWorker,
+ exception.NodeLocked,
+ exception.InvalidParameterValue,
+ exception.MissingParameterValue,
+ exception.InvalidStateRequested)
+ def do_provisioning_action(self, context, node_id, action):
+ """RPC method to initiate certain provisioning state transitions.
+
+ Initiate a provisioning state change through the state machine,
+ rather than through an RPC call to do_node_deploy / do_node_tear_down
+
+ :param context: an admin context.
+ :param node_id: the id or uuid of a node.
+ :param action: an action. One of ironic.common.states.VERBS
+ :raises: InvalidParameterValue
+ :raises: InvalidStateRequested
+ :raises: NoFreeConductorWorker
+
+ """
+ with task_manager.acquire(context, node_id, shared=False) as task:
+ try:
+ task.process_event(action)
+ except exception.InvalidState:
+ raise exception.InvalidStateRequested(
+ action=action, node=task.node.uuid,
+ state=task.node.provision_state)
@periodic_task.periodic_task(
spacing=CONF.conductor.sync_power_state_interval)
diff --git a/ironic/conductor/rpcapi.py b/ironic/conductor/rpcapi.py
index a3115985f..9524123d4 100644
--- a/ironic/conductor/rpcapi.py
+++ b/ironic/conductor/rpcapi.py
@@ -65,11 +65,12 @@ class ConductorAPI(object):
| 1.21 - Added get_node_vendor_passthru_methods and
| get_driver_vendor_passthru_methods
| 1.22 - Added configdrive parameter to do_node_deploy.
+ | 1.23 - Added do_provisioning_action
"""
# NOTE(rloo): This must be in sync with manager.ConductorManager's.
- RPC_API_VERSION = '1.22'
+ RPC_API_VERSION = '1.23'
def __init__(self, topic=None):
super(ConductorAPI, self).__init__()
@@ -303,6 +304,25 @@ class ConductorAPI(object):
cctxt = self.client.prepare(topic=topic or self.topic, version='1.6')
return cctxt.call(context, 'do_node_tear_down', node_id=node_id)
+ def do_provisioning_action(self, context, node_id, action, topic=None):
+ """Signal to conductor service to perform the given action on a node.
+
+ :param context: request context.
+ :param node_id: node id or uuid.
+ :param action: an action. One of ironic.common.states.VERBS
+ :param topic: RPC topic. Defaults to self.topic.
+ :raises: InvalidParameterValue
+ :raises: NoFreeConductorWorker when there is no free worker to start
+ async task.
+ :raises: InvalidStateRequested if the requested action can not
+ be performed.
+
+ This encapsulates some provisioning actions in a single call.
+ """
+ cctxt = self.client.prepare(topic=topic or self.topic, version='1.23')
+ return cctxt.call(context, 'do_provisioning_action',
+ node_id=node_id, action=action)
+
def validate_driver_interfaces(self, context, node_id, topic=None):
"""Validate the `core` and `standardized` interfaces for drivers.