mirror of
https://opendev.org/openstack/ironic.git
synced 2026-01-11 19:57:20 +00:00
Separate states code from states constants
Currently, the nova virt driver for ironic has a file containing nothing but our states as constants. A recent bug was caused, in part, by these not being properly updated. The goal here is to move ironic state machine code and constants into separate files -- once merged, I will update the nova driver to use a copy of this file (and add a comment to the file here saying it's synced over there). This should help prevent this kinda issue in the future and in the long run cause less duplicated work. Assisted-by: Claude Code (claude) Signed-off-by: Jay Faulkner <jay@jvf.cc> Change-Id: Ief4533b69899c893f150ef3a7006fb99f7e42964
This commit is contained in:
parent
7495f77258
commit
c69caf28e8
8 changed files with 406 additions and 378 deletions
|
|
@ -47,6 +47,7 @@ from ironic.common import exception
|
|||
from ironic.common.i18n import _
|
||||
from ironic.common import metrics_utils
|
||||
from ironic.common import policy
|
||||
from ironic.common import state_machine
|
||||
from ironic.common import states as ir_states
|
||||
from ironic.conductor import steps as conductor_steps
|
||||
import ironic.conf
|
||||
|
|
@ -1240,7 +1241,7 @@ class NodeStatesController(rest.RestController):
|
|||
# This code does upfront state sanity checking, in that we're past
|
||||
# basic RBAC policy checking, and we're shifting gears to content
|
||||
# validation.
|
||||
m = ir_states.machine.copy()
|
||||
m = state_machine.machine.copy()
|
||||
m.initialize(rpc_node.provision_state)
|
||||
if not m.is_actionable_event(ir_states.VERBS.get(target, target)):
|
||||
# Normally, we let the task manager recognize and deal with
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ from ironic.common import exception
|
|||
from ironic.common import faults
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common import policy
|
||||
from ironic.common import state_machine
|
||||
from ironic.common import states
|
||||
from ironic.common import utils
|
||||
from ironic.conductor import steps as conductor_steps
|
||||
|
|
@ -957,7 +958,7 @@ def check_for_invalid_state_and_allow_filter(provision_state):
|
|||
if (api.request.version.minor
|
||||
< versions.MINOR_9_PROVISION_STATE_FILTER):
|
||||
raise exception.NotAcceptable()
|
||||
valid_states = states.machine.states
|
||||
valid_states = state_machine.machine.states
|
||||
if provision_state not in valid_states:
|
||||
raise exception.InvalidParameterValue(
|
||||
_('Provision state "%s" is not valid') % provision_state)
|
||||
|
|
|
|||
370
ironic/common/state_machine.py
Normal file
370
ironic/common/state_machine.py
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
# Copyright (c) 2012 NTT DOCOMO, INC.
|
||||
# Copyright 2010 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Mapping of bare metal node states.
|
||||
|
||||
Setting the node `power_state` is handled by the conductor's power
|
||||
synchronization thread. Based on the power state retrieved from the driver
|
||||
for the node, the state is set to POWER_ON or POWER_OFF, accordingly.
|
||||
Should this fail, the `power_state` value is left unchanged, and the node
|
||||
is placed into maintenance mode.
|
||||
|
||||
The `power_state` can also be set manually via the API. A failure to change
|
||||
the state leaves the current state unchanged. The node is NOT placed into
|
||||
maintenance mode in this case.
|
||||
"""
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from ironic.common import fsm
|
||||
from ironic.common import states as st
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
#####################
|
||||
# State machine model
|
||||
#####################
|
||||
|
||||
|
||||
def on_exit(old_state, event):
|
||||
"""Used to log when a state is exited."""
|
||||
LOG.debug("Exiting old state '%s' in response to event '%s'",
|
||||
old_state, event)
|
||||
|
||||
|
||||
def on_enter(new_state, event):
|
||||
"""Used to log when entering a state."""
|
||||
LOG.debug("Entering new state '%s' in response to event '%s'",
|
||||
new_state, event)
|
||||
|
||||
|
||||
watchers = {}
|
||||
watchers['on_exit'] = on_exit
|
||||
watchers['on_enter'] = on_enter
|
||||
|
||||
machine = fsm.FSM()
|
||||
|
||||
# Add stable states
|
||||
for state in st.STABLE_STATES:
|
||||
machine.add_state(state, stable=True, **watchers)
|
||||
|
||||
# Add verifying state
|
||||
machine.add_state(st.VERIFYING, target=st.MANAGEABLE, **watchers)
|
||||
|
||||
# Add deploy* states
|
||||
# NOTE(tenbrae): Juno shows a target_provision_state of DEPLOYDONE
|
||||
# this is changed in Kilo to ACTIVE
|
||||
machine.add_state(st.DEPLOYING, target=st.ACTIVE, **watchers)
|
||||
machine.add_state(st.DEPLOYWAIT, target=st.ACTIVE, **watchers)
|
||||
machine.add_state(st.DEPLOYFAIL, target=st.ACTIVE, **watchers)
|
||||
machine.add_state(st.DEPLOYHOLD, target=st.ACTIVE, **watchers)
|
||||
|
||||
# Add clean* states
|
||||
machine.add_state(st.CLEANING, target=st.AVAILABLE, **watchers)
|
||||
machine.add_state(st.CLEANWAIT, target=st.AVAILABLE, **watchers)
|
||||
machine.add_state(st.CLEANFAIL, target=st.AVAILABLE, **watchers)
|
||||
machine.add_state(st.CLEANHOLD, target=st.AVAILABLE, **watchers)
|
||||
|
||||
# Add delete* states
|
||||
machine.add_state(st.DELETING, target=st.AVAILABLE, **watchers)
|
||||
|
||||
# From AVAILABLE, a deployment may be started
|
||||
machine.add_transition(st.AVAILABLE, st.DEPLOYING, 'deploy')
|
||||
|
||||
# Add inspect* states.
|
||||
machine.add_state(st.INSPECTING, target=st.MANAGEABLE, **watchers)
|
||||
machine.add_state(st.INSPECTFAIL, target=st.MANAGEABLE, **watchers)
|
||||
machine.add_state(st.INSPECTWAIT, target=st.MANAGEABLE, **watchers)
|
||||
|
||||
# Add adopt* states
|
||||
machine.add_state(st.ADOPTING, target=st.ACTIVE, **watchers)
|
||||
machine.add_state(st.ADOPTFAIL, target=st.ACTIVE, **watchers)
|
||||
|
||||
# rescue states
|
||||
machine.add_state(st.RESCUING, target=st.RESCUE, **watchers)
|
||||
machine.add_state(st.RESCUEWAIT, target=st.RESCUE, **watchers)
|
||||
machine.add_state(st.RESCUEFAIL, target=st.RESCUE, **watchers)
|
||||
machine.add_state(st.UNRESCUING, target=st.ACTIVE, **watchers)
|
||||
machine.add_state(st.UNRESCUEFAIL, target=st.ACTIVE, **watchers)
|
||||
|
||||
# A deployment may fail
|
||||
machine.add_transition(st.DEPLOYING, st.DEPLOYFAIL, 'fail')
|
||||
|
||||
# A failed deployment may be retried
|
||||
# ironic/conductor/manager.py:do_node_deploy()
|
||||
machine.add_transition(st.DEPLOYFAIL, st.DEPLOYING, 'rebuild')
|
||||
# NOTE(tenbrae): Juno allows a client to send "active" to initiate a rebuild
|
||||
machine.add_transition(st.DEPLOYFAIL, st.DEPLOYING, 'deploy')
|
||||
|
||||
# A deployment may also wait on external callbacks
|
||||
machine.add_transition(st.DEPLOYING, st.DEPLOYWAIT, 'wait')
|
||||
machine.add_transition(st.DEPLOYING, st.DEPLOYHOLD, 'hold')
|
||||
machine.add_transition(st.DEPLOYWAIT, st.DEPLOYHOLD, 'hold')
|
||||
machine.add_transition(st.DEPLOYWAIT, st.DEPLOYING, 'resume')
|
||||
|
||||
# A deployment waiting on callback may time out
|
||||
machine.add_transition(st.DEPLOYWAIT, st.DEPLOYFAIL, 'fail')
|
||||
|
||||
# Return the node into a deploying state from holding
|
||||
machine.add_transition(st.DEPLOYHOLD, st.DEPLOYWAIT, 'unhold')
|
||||
|
||||
# A node in deploy hold may also be aborted
|
||||
machine.add_transition(st.DEPLOYHOLD, st.DEPLOYFAIL, 'abort')
|
||||
|
||||
# A deployment may complete
|
||||
machine.add_transition(st.DEPLOYING, st.ACTIVE, 'done')
|
||||
|
||||
# An active instance may be re-deployed
|
||||
# ironic/conductor/manager.py:do_node_deploy()
|
||||
machine.add_transition(st.ACTIVE, st.DEPLOYING, 'rebuild')
|
||||
|
||||
# An active instance may be deleted
|
||||
# ironic/conductor/manager.py:do_node_tear_down()
|
||||
machine.add_transition(st.ACTIVE, st.DELETING, 'delete')
|
||||
|
||||
# While a deployment is waiting, it may be deleted
|
||||
# ironic/conductor/manager.py:do_node_tear_down()
|
||||
machine.add_transition(st.DEPLOYWAIT, st.DELETING, 'delete')
|
||||
|
||||
# A failed deployment may also be deleted
|
||||
# ironic/conductor/manager.py:do_node_tear_down()
|
||||
machine.add_transition(st.DEPLOYFAIL, st.DELETING, 'delete')
|
||||
|
||||
# This state can also transition to error
|
||||
machine.add_transition(st.DELETING, st.ERROR, 'fail')
|
||||
|
||||
# When finished deleting, a node will begin cleaning
|
||||
machine.add_transition(st.DELETING, st.CLEANING, 'clean')
|
||||
|
||||
# If cleaning succeeds, it becomes available for scheduling
|
||||
machine.add_transition(st.CLEANING, st.AVAILABLE, 'done')
|
||||
|
||||
# If cleaning fails, wait for operator intervention
|
||||
machine.add_transition(st.CLEANING, st.CLEANFAIL, 'fail')
|
||||
machine.add_transition(st.CLEANWAIT, st.CLEANFAIL, 'fail')
|
||||
|
||||
# While waiting for a clean step to be finished, cleaning may be aborted
|
||||
machine.add_transition(st.CLEANWAIT, st.CLEANFAIL, 'abort')
|
||||
|
||||
# Cleaning may also wait on external callbacks
|
||||
machine.add_transition(st.CLEANING, st.CLEANWAIT, 'wait')
|
||||
machine.add_transition(st.CLEANING, st.CLEANHOLD, 'hold')
|
||||
machine.add_transition(st.CLEANWAIT, st.CLEANHOLD, 'hold')
|
||||
machine.add_transition(st.CLEANWAIT, st.CLEANING, 'resume')
|
||||
|
||||
# A node in a clean hold step may also be aborted
|
||||
machine.add_transition(st.CLEANHOLD, st.CLEANFAIL, 'abort')
|
||||
|
||||
# Return the node back to cleaning
|
||||
machine.add_transition(st.CLEANHOLD, st.CLEANWAIT, 'unhold')
|
||||
|
||||
# An operator may want to move a CLEANFAIL node to MANAGEABLE, to perform
|
||||
# other actions like cleaning
|
||||
machine.add_transition(st.CLEANFAIL, st.MANAGEABLE, 'manage')
|
||||
|
||||
# From MANAGEABLE, a node may move to available after going through automated
|
||||
# cleaning
|
||||
machine.add_transition(st.MANAGEABLE, st.CLEANING, 'provide')
|
||||
|
||||
# From MANAGEABLE, a node may be manually cleaned, going back to manageable
|
||||
# after cleaning is completed
|
||||
machine.add_transition(st.MANAGEABLE, st.CLEANING, 'clean')
|
||||
machine.add_transition(st.CLEANING, st.MANAGEABLE, 'manage')
|
||||
|
||||
# From AVAILABLE, a node may be made unavailable by managing it
|
||||
machine.add_transition(st.AVAILABLE, st.MANAGEABLE, 'manage')
|
||||
|
||||
# An errored instance can be rebuilt
|
||||
# ironic/conductor/manager.py:do_node_deploy()
|
||||
machine.add_transition(st.ERROR, st.DEPLOYING, 'rebuild')
|
||||
# or deleted
|
||||
# ironic/conductor/manager.py:do_node_tear_down()
|
||||
machine.add_transition(st.ERROR, st.DELETING, 'delete')
|
||||
|
||||
# Added transitions for inspection.
|
||||
# Initiate inspection.
|
||||
machine.add_transition(st.MANAGEABLE, st.INSPECTING, 'inspect')
|
||||
|
||||
# ironic/conductor/manager.py:inspect_hardware().
|
||||
machine.add_transition(st.INSPECTING, st.MANAGEABLE, 'done')
|
||||
|
||||
# Inspection may fail.
|
||||
machine.add_transition(st.INSPECTING, st.INSPECTFAIL, 'fail')
|
||||
|
||||
# Transition for asynchronous inspection
|
||||
machine.add_transition(st.INSPECTING, st.INSPECTWAIT, 'wait')
|
||||
|
||||
# Inspection is done
|
||||
machine.add_transition(st.INSPECTWAIT, st.MANAGEABLE, 'done')
|
||||
|
||||
# Inspection failed.
|
||||
machine.add_transition(st.INSPECTWAIT, st.INSPECTFAIL, 'fail')
|
||||
|
||||
# Inspection is aborted.
|
||||
machine.add_transition(st.INSPECTWAIT, st.INSPECTFAIL, 'abort')
|
||||
|
||||
# Inspection is continued.
|
||||
machine.add_transition(st.INSPECTWAIT, st.INSPECTING, 'resume')
|
||||
|
||||
# Move the node to manageable state for any other
|
||||
# action.
|
||||
machine.add_transition(st.INSPECTFAIL, st.MANAGEABLE, 'manage')
|
||||
|
||||
# Reinitiate the inspect after inspectfail.
|
||||
machine.add_transition(st.INSPECTFAIL, st.INSPECTING, 'inspect')
|
||||
|
||||
# A provisioned node may have a rescue initiated.
|
||||
machine.add_transition(st.ACTIVE, st.RESCUING, 'rescue')
|
||||
|
||||
# A rescue may succeed.
|
||||
machine.add_transition(st.RESCUING, st.RESCUE, 'done')
|
||||
|
||||
# A rescue may also wait on external callbacks
|
||||
machine.add_transition(st.RESCUING, st.RESCUEWAIT, 'wait')
|
||||
machine.add_transition(st.RESCUEWAIT, st.RESCUING, 'resume')
|
||||
|
||||
# A rescued node may be re-rescued.
|
||||
machine.add_transition(st.RESCUE, st.RESCUING, 'rescue')
|
||||
|
||||
# A rescued node may be deleted.
|
||||
machine.add_transition(st.RESCUE, st.DELETING, 'delete')
|
||||
|
||||
# A rescue may fail.
|
||||
machine.add_transition(st.RESCUEWAIT, st.RESCUEFAIL, 'fail')
|
||||
machine.add_transition(st.RESCUING, st.RESCUEFAIL, 'fail')
|
||||
|
||||
# While waiting for a rescue step to be finished, rescuing may be aborted
|
||||
machine.add_transition(st.RESCUEWAIT, st.RESCUEFAIL, 'abort')
|
||||
|
||||
# A failed rescue may be re-rescued.
|
||||
machine.add_transition(st.RESCUEFAIL, st.RESCUING, 'rescue')
|
||||
|
||||
# A failed rescue may be unrescued.
|
||||
machine.add_transition(st.RESCUEFAIL, st.UNRESCUING, 'unrescue')
|
||||
|
||||
# A failed rescue may be deleted.
|
||||
machine.add_transition(st.RESCUEFAIL, st.DELETING, 'delete')
|
||||
|
||||
# A rescuewait node may be deleted.
|
||||
machine.add_transition(st.RESCUEWAIT, st.DELETING, 'delete')
|
||||
|
||||
# A rescued node may be unrescued.
|
||||
machine.add_transition(st.RESCUE, st.UNRESCUING, 'unrescue')
|
||||
|
||||
# An unrescuing node may succeed
|
||||
machine.add_transition(st.UNRESCUING, st.ACTIVE, 'done')
|
||||
|
||||
# An unrescuing node may fail
|
||||
machine.add_transition(st.UNRESCUING, st.UNRESCUEFAIL, 'fail')
|
||||
|
||||
# A failed unrescue may be re-rescued
|
||||
machine.add_transition(st.UNRESCUEFAIL, st.RESCUING, 'rescue')
|
||||
|
||||
# A failed unrescue may be re-unrescued
|
||||
machine.add_transition(st.UNRESCUEFAIL, st.UNRESCUING, 'unrescue')
|
||||
|
||||
# A failed unrescue may be deleted.
|
||||
machine.add_transition(st.UNRESCUEFAIL, st.DELETING, 'delete')
|
||||
|
||||
# Start power credentials verification
|
||||
machine.add_transition(st.ENROLL, st.VERIFYING, 'manage')
|
||||
|
||||
# Verification can succeed
|
||||
machine.add_transition(st.VERIFYING, st.MANAGEABLE, 'done')
|
||||
|
||||
# Verification can fail with setting last_error and rolling back to ENROLL
|
||||
machine.add_transition(st.VERIFYING, st.ENROLL, 'fail')
|
||||
|
||||
# Node Adoption is being attempted
|
||||
machine.add_transition(st.MANAGEABLE, st.ADOPTING, 'adopt')
|
||||
|
||||
# Adoption can succeed and the node should be set to ACTIVE
|
||||
machine.add_transition(st.ADOPTING, st.ACTIVE, 'done')
|
||||
|
||||
# Node adoptions can fail and as such nodes shall be set
|
||||
# into a dedicated state to hold the nodes.
|
||||
machine.add_transition(st.ADOPTING, st.ADOPTFAIL, 'fail')
|
||||
|
||||
# Node adoption can be retried when it previously failed.
|
||||
machine.add_transition(st.ADOPTFAIL, st.ADOPTING, 'adopt')
|
||||
|
||||
# A node that failed adoption can be moved back to manageable
|
||||
machine.add_transition(st.ADOPTFAIL, st.MANAGEABLE, 'manage')
|
||||
|
||||
# Add service* states
|
||||
machine.add_state(st.SERVICING, target=st.ACTIVE, **watchers)
|
||||
machine.add_state(st.SERVICEWAIT, target=st.ACTIVE, **watchers)
|
||||
machine.add_state(st.SERVICEFAIL, target=st.ACTIVE, **watchers)
|
||||
machine.add_state(st.SERVICEHOLD, target=st.ACTIVE, **watchers)
|
||||
|
||||
# A node in service an be returned to active
|
||||
machine.add_transition(st.SERVICING, st.ACTIVE, 'done')
|
||||
|
||||
# A node in active can be serviced
|
||||
machine.add_transition(st.ACTIVE, st.SERVICING, 'service')
|
||||
|
||||
# A node in servicing can be failed
|
||||
machine.add_transition(st.SERVICING, st.SERVICEFAIL, 'fail')
|
||||
|
||||
# A node in service can enter a wait state
|
||||
machine.add_transition(st.SERVICING, st.SERVICEWAIT, 'wait')
|
||||
|
||||
# A node in service can be held
|
||||
machine.add_transition(st.SERVICING, st.SERVICEHOLD, 'hold')
|
||||
machine.add_transition(st.SERVICEWAIT, st.SERVICEHOLD, 'hold')
|
||||
|
||||
# A held node in service can get more service steps to start over
|
||||
machine.add_transition(st.SERVICEHOLD, st.SERVICING, 'service')
|
||||
|
||||
# A held node in service can be removed from service
|
||||
machine.add_transition(st.SERVICEHOLD, st.SERVICEWAIT, 'unhold')
|
||||
|
||||
# A node in service wait can resume
|
||||
machine.add_transition(st.SERVICEWAIT, st.SERVICING, 'resume')
|
||||
|
||||
# A node in service wait can failed
|
||||
machine.add_transition(st.SERVICEWAIT, st.SERVICEFAIL, 'fail')
|
||||
|
||||
# A node in service hold can failed
|
||||
machine.add_transition(st.SERVICEHOLD, st.SERVICEFAIL, 'fail')
|
||||
|
||||
# A node in service wait can be aborted
|
||||
machine.add_transition(st.SERVICEWAIT, st.SERVICEFAIL, 'abort')
|
||||
|
||||
# A node in service hold can be aborted
|
||||
machine.add_transition(st.SERVICEHOLD, st.SERVICEFAIL, 'abort')
|
||||
|
||||
# A node in service fail can re-enter service
|
||||
machine.add_transition(st.SERVICEFAIL, st.SERVICING, 'service')
|
||||
|
||||
# A node in service fail can be rescued
|
||||
machine.add_transition(st.SERVICEFAIL, st.RESCUING, 'rescue')
|
||||
|
||||
# A node in service fail can enter wait state
|
||||
machine.add_transition(st.SERVICEFAIL, st.SERVICEWAIT, 'wait')
|
||||
|
||||
# A node in service fail can be held
|
||||
machine.add_transition(st.SERVICEFAIL, st.SERVICEHOLD, 'hold')
|
||||
|
||||
# A node in service fail may be deleted.
|
||||
machine.add_transition(st.SERVICEFAIL, st.DELETING, 'delete')
|
||||
|
||||
# A node in service fail may be aborted (returned to active)
|
||||
machine.add_transition(st.SERVICEFAIL, st.ACTIVE, 'abort')
|
||||
|
||||
# A node in service wait may be deleted.
|
||||
machine.add_transition(st.SERVICEWAIT, st.DELETING, 'delete')
|
||||
|
|
@ -1,44 +1,26 @@
|
|||
# Copyright (c) 2012 NTT DOCOMO, INC.
|
||||
# Copyright 2010 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
# 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
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# 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.
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Mapping of bare metal node states.
|
||||
Constants for bare metal node states.
|
||||
|
||||
Setting the node `power_state` is handled by the conductor's power
|
||||
synchronization thread. Based on the power state retrieved from the driver
|
||||
for the node, the state is set to POWER_ON or POWER_OFF, accordingly.
|
||||
Should this fail, the `power_state` value is left unchanged, and the node
|
||||
is placed into maintenance mode.
|
||||
|
||||
The `power_state` can also be set manually via the API. A failure to change
|
||||
the state leaves the current state unchanged. The node is NOT placed into
|
||||
maintenance mode in this case.
|
||||
This module contains only state constant definitions with no executable code.
|
||||
For the state machine implementation, see ironic.common.states.
|
||||
"""
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from ironic.common import fsm
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
#####################
|
||||
# Provisioning states
|
||||
#####################
|
||||
|
||||
# TODO(tenbrae): add add'l state mappings here
|
||||
VERBS = {
|
||||
'active': 'deploy',
|
||||
'deploy': 'deploy',
|
||||
|
|
@ -140,9 +122,7 @@ DELETING = 'deleting'
|
|||
DELETED = 'deleted'
|
||||
""" Node tear down was successful.
|
||||
|
||||
In Juno, target_provision_state was set to this value during node tear down.
|
||||
|
||||
In Kilo, this will be a transitory value of provision_state, and never
|
||||
This is a transitory value of provision_state, and never
|
||||
represented in target_provision_state.
|
||||
"""
|
||||
|
||||
|
|
@ -151,6 +131,7 @@ CLEANING = 'cleaning'
|
|||
|
||||
UNDEPLOY = 'undeploy'
|
||||
""" Node tear down process has started.
|
||||
|
||||
This is an alias for DELETED.
|
||||
"""
|
||||
|
||||
|
|
@ -305,6 +286,14 @@ FAILURE_STATES = frozenset((DEPLOYFAIL, CLEANFAIL, INSPECTFAIL,
|
|||
RESCUEFAIL, UNRESCUEFAIL, ADOPTFAIL,
|
||||
SERVICEFAIL))
|
||||
|
||||
# NOTE(JayF) This isn't used in Ironic, but is used in Nova as a copy of this
|
||||
# file will be proposed into the nova driver.
|
||||
ALL_STATES = frozenset((ACTIVE, ADOPTFAIL, ADOPTING, AVAILABLE, CLEANFAIL,
|
||||
CLEANHOLD, CLEANING, CLEANWAIT, DELETED, DELETING, DEPLOYDONE, DEPLOYFAIL,
|
||||
DEPLOYHOLD, DEPLOYING, DEPLOYWAIT, ENROLL, ERROR, INSPECTFAIL, INSPECTING,
|
||||
INSPECTWAIT, MANAGEABLE, RESCUE, RESCUEFAIL, RESCUING, RESCUEWAIT,
|
||||
SERVICEFAIL, SERVICEHOLD, SERVICING, SERVICEWAIT, UNRESCUEFAIL,
|
||||
UNRESCUING, VERIFYING)) # noqa
|
||||
|
||||
##############
|
||||
# Power states
|
||||
|
|
@ -338,11 +327,8 @@ ALLOCATING = 'allocating'
|
|||
###########################
|
||||
|
||||
PROVISIONING = "provisioning"
|
||||
CLEANING = "cleaning"
|
||||
DEPLOYING = "deploying"
|
||||
TAKEOVER = "takeover"
|
||||
INTROSPECTION = "introspection"
|
||||
RESCUE = "rescue"
|
||||
CONDUCTOR = "conductor"
|
||||
TRANSITION = "transition"
|
||||
STARTFAIL = "startup failure"
|
||||
|
|
@ -351,336 +337,3 @@ ADOPTION = "adoption"
|
|||
CONSOLE = "console"
|
||||
MONITORING = "monitoring"
|
||||
VERIFY = "verify"
|
||||
|
||||
|
||||
#####################
|
||||
# State machine model
|
||||
#####################
|
||||
def on_exit(old_state, event):
|
||||
"""Used to log when a state is exited."""
|
||||
LOG.debug("Exiting old state '%s' in response to event '%s'",
|
||||
old_state, event)
|
||||
|
||||
|
||||
def on_enter(new_state, event):
|
||||
"""Used to log when entering a state."""
|
||||
LOG.debug("Entering new state '%s' in response to event '%s'",
|
||||
new_state, event)
|
||||
|
||||
|
||||
watchers = {}
|
||||
watchers['on_exit'] = on_exit
|
||||
watchers['on_enter'] = on_enter
|
||||
|
||||
machine = fsm.FSM()
|
||||
|
||||
# Add stable states
|
||||
for state in STABLE_STATES:
|
||||
machine.add_state(state, stable=True, **watchers)
|
||||
|
||||
# Add verifying state
|
||||
machine.add_state(VERIFYING, target=MANAGEABLE, **watchers)
|
||||
|
||||
# Add deploy* states
|
||||
# NOTE(tenbrae): Juno shows a target_provision_state of DEPLOYDONE
|
||||
# this is changed in Kilo to ACTIVE
|
||||
machine.add_state(DEPLOYING, target=ACTIVE, **watchers)
|
||||
machine.add_state(DEPLOYWAIT, target=ACTIVE, **watchers)
|
||||
machine.add_state(DEPLOYFAIL, target=ACTIVE, **watchers)
|
||||
machine.add_state(DEPLOYHOLD, target=ACTIVE, **watchers)
|
||||
|
||||
# Add clean* states
|
||||
machine.add_state(CLEANING, target=AVAILABLE, **watchers)
|
||||
machine.add_state(CLEANWAIT, target=AVAILABLE, **watchers)
|
||||
machine.add_state(CLEANFAIL, target=AVAILABLE, **watchers)
|
||||
machine.add_state(CLEANHOLD, target=AVAILABLE, **watchers)
|
||||
|
||||
# Add delete* states
|
||||
machine.add_state(DELETING, target=AVAILABLE, **watchers)
|
||||
|
||||
# From AVAILABLE, a deployment may be started
|
||||
machine.add_transition(AVAILABLE, DEPLOYING, 'deploy')
|
||||
|
||||
# Add inspect* states.
|
||||
machine.add_state(INSPECTING, target=MANAGEABLE, **watchers)
|
||||
machine.add_state(INSPECTFAIL, target=MANAGEABLE, **watchers)
|
||||
machine.add_state(INSPECTWAIT, target=MANAGEABLE, **watchers)
|
||||
|
||||
# Add adopt* states
|
||||
machine.add_state(ADOPTING, target=ACTIVE, **watchers)
|
||||
machine.add_state(ADOPTFAIL, target=ACTIVE, **watchers)
|
||||
|
||||
# rescue states
|
||||
machine.add_state(RESCUING, target=RESCUE, **watchers)
|
||||
machine.add_state(RESCUEWAIT, target=RESCUE, **watchers)
|
||||
machine.add_state(RESCUEFAIL, target=RESCUE, **watchers)
|
||||
machine.add_state(UNRESCUING, target=ACTIVE, **watchers)
|
||||
machine.add_state(UNRESCUEFAIL, target=ACTIVE, **watchers)
|
||||
|
||||
# A deployment may fail
|
||||
machine.add_transition(DEPLOYING, DEPLOYFAIL, 'fail')
|
||||
|
||||
# A failed deployment may be retried
|
||||
# ironic/conductor/manager.py:do_node_deploy()
|
||||
machine.add_transition(DEPLOYFAIL, DEPLOYING, 'rebuild')
|
||||
# NOTE(tenbrae): Juno allows a client to send "active" to initiate a rebuild
|
||||
machine.add_transition(DEPLOYFAIL, DEPLOYING, 'deploy')
|
||||
|
||||
# A deployment may also wait on external callbacks
|
||||
machine.add_transition(DEPLOYING, DEPLOYWAIT, 'wait')
|
||||
machine.add_transition(DEPLOYING, DEPLOYHOLD, 'hold')
|
||||
machine.add_transition(DEPLOYWAIT, DEPLOYHOLD, 'hold')
|
||||
machine.add_transition(DEPLOYWAIT, DEPLOYING, 'resume')
|
||||
|
||||
# A deployment waiting on callback may time out
|
||||
machine.add_transition(DEPLOYWAIT, DEPLOYFAIL, 'fail')
|
||||
|
||||
# Return the node into a deploying state from holding
|
||||
machine.add_transition(DEPLOYHOLD, DEPLOYWAIT, 'unhold')
|
||||
|
||||
# A node in deploy hold may also be aborted
|
||||
machine.add_transition(DEPLOYHOLD, DEPLOYFAIL, 'abort')
|
||||
|
||||
# A deployment may complete
|
||||
machine.add_transition(DEPLOYING, ACTIVE, 'done')
|
||||
|
||||
# An active instance may be re-deployed
|
||||
# ironic/conductor/manager.py:do_node_deploy()
|
||||
machine.add_transition(ACTIVE, DEPLOYING, 'rebuild')
|
||||
|
||||
# An active instance may be deleted
|
||||
# ironic/conductor/manager.py:do_node_tear_down()
|
||||
machine.add_transition(ACTIVE, DELETING, 'delete')
|
||||
|
||||
# While a deployment is waiting, it may be deleted
|
||||
# ironic/conductor/manager.py:do_node_tear_down()
|
||||
machine.add_transition(DEPLOYWAIT, DELETING, 'delete')
|
||||
|
||||
# A failed deployment may also be deleted
|
||||
# ironic/conductor/manager.py:do_node_tear_down()
|
||||
machine.add_transition(DEPLOYFAIL, DELETING, 'delete')
|
||||
|
||||
# This state can also transition to error
|
||||
machine.add_transition(DELETING, ERROR, 'fail')
|
||||
|
||||
# When finished deleting, a node will begin cleaning
|
||||
machine.add_transition(DELETING, CLEANING, 'clean')
|
||||
|
||||
# If cleaning succeeds, it becomes available for scheduling
|
||||
machine.add_transition(CLEANING, AVAILABLE, 'done')
|
||||
|
||||
# If cleaning fails, wait for operator intervention
|
||||
machine.add_transition(CLEANING, CLEANFAIL, 'fail')
|
||||
machine.add_transition(CLEANWAIT, CLEANFAIL, 'fail')
|
||||
|
||||
# While waiting for a clean step to be finished, cleaning may be aborted
|
||||
machine.add_transition(CLEANWAIT, CLEANFAIL, 'abort')
|
||||
|
||||
# Cleaning may also wait on external callbacks
|
||||
machine.add_transition(CLEANING, CLEANWAIT, 'wait')
|
||||
machine.add_transition(CLEANING, CLEANHOLD, 'hold')
|
||||
machine.add_transition(CLEANWAIT, CLEANHOLD, 'hold')
|
||||
machine.add_transition(CLEANWAIT, CLEANING, 'resume')
|
||||
|
||||
# A node in a clean hold step may also be aborted
|
||||
machine.add_transition(CLEANHOLD, CLEANFAIL, 'abort')
|
||||
|
||||
# Return the node back to cleaning
|
||||
machine.add_transition(CLEANHOLD, CLEANWAIT, 'unhold')
|
||||
|
||||
# An operator may want to move a CLEANFAIL node to MANAGEABLE, to perform
|
||||
# other actions like cleaning
|
||||
machine.add_transition(CLEANFAIL, MANAGEABLE, 'manage')
|
||||
|
||||
# From MANAGEABLE, a node may move to available after going through automated
|
||||
# cleaning
|
||||
machine.add_transition(MANAGEABLE, CLEANING, 'provide')
|
||||
|
||||
# From MANAGEABLE, a node may be manually cleaned, going back to manageable
|
||||
# after cleaning is completed
|
||||
machine.add_transition(MANAGEABLE, CLEANING, 'clean')
|
||||
machine.add_transition(CLEANING, MANAGEABLE, 'manage')
|
||||
|
||||
# From AVAILABLE, a node may be made unavailable by managing it
|
||||
machine.add_transition(AVAILABLE, MANAGEABLE, 'manage')
|
||||
|
||||
# An errored instance can be rebuilt
|
||||
# ironic/conductor/manager.py:do_node_deploy()
|
||||
machine.add_transition(ERROR, DEPLOYING, 'rebuild')
|
||||
# or deleted
|
||||
# ironic/conductor/manager.py:do_node_tear_down()
|
||||
machine.add_transition(ERROR, DELETING, 'delete')
|
||||
|
||||
# Added transitions for inspection.
|
||||
# Initiate inspection.
|
||||
machine.add_transition(MANAGEABLE, INSPECTING, 'inspect')
|
||||
|
||||
# ironic/conductor/manager.py:inspect_hardware().
|
||||
machine.add_transition(INSPECTING, MANAGEABLE, 'done')
|
||||
|
||||
# Inspection may fail.
|
||||
machine.add_transition(INSPECTING, INSPECTFAIL, 'fail')
|
||||
|
||||
# Transition for asynchronous inspection
|
||||
machine.add_transition(INSPECTING, INSPECTWAIT, 'wait')
|
||||
|
||||
# Inspection is done
|
||||
machine.add_transition(INSPECTWAIT, MANAGEABLE, 'done')
|
||||
|
||||
# Inspection failed.
|
||||
machine.add_transition(INSPECTWAIT, INSPECTFAIL, 'fail')
|
||||
|
||||
# Inspection is aborted.
|
||||
machine.add_transition(INSPECTWAIT, INSPECTFAIL, 'abort')
|
||||
|
||||
# Inspection is continued.
|
||||
machine.add_transition(INSPECTWAIT, INSPECTING, 'resume')
|
||||
|
||||
# Move the node to manageable state for any other
|
||||
# action.
|
||||
machine.add_transition(INSPECTFAIL, MANAGEABLE, 'manage')
|
||||
|
||||
# Reinitiate the inspect after inspectfail.
|
||||
machine.add_transition(INSPECTFAIL, INSPECTING, 'inspect')
|
||||
|
||||
# A provisioned node may have a rescue initiated.
|
||||
machine.add_transition(ACTIVE, RESCUING, 'rescue')
|
||||
|
||||
# A rescue may succeed.
|
||||
machine.add_transition(RESCUING, RESCUE, 'done')
|
||||
|
||||
# A rescue may also wait on external callbacks
|
||||
machine.add_transition(RESCUING, RESCUEWAIT, 'wait')
|
||||
machine.add_transition(RESCUEWAIT, RESCUING, 'resume')
|
||||
|
||||
# A rescued node may be re-rescued.
|
||||
machine.add_transition(RESCUE, RESCUING, 'rescue')
|
||||
|
||||
# A rescued node may be deleted.
|
||||
machine.add_transition(RESCUE, DELETING, 'delete')
|
||||
|
||||
# A rescue may fail.
|
||||
machine.add_transition(RESCUEWAIT, RESCUEFAIL, 'fail')
|
||||
machine.add_transition(RESCUING, RESCUEFAIL, 'fail')
|
||||
|
||||
# While waiting for a rescue step to be finished, rescuing may be aborted
|
||||
machine.add_transition(RESCUEWAIT, RESCUEFAIL, 'abort')
|
||||
|
||||
# A failed rescue may be re-rescued.
|
||||
machine.add_transition(RESCUEFAIL, RESCUING, 'rescue')
|
||||
|
||||
# A failed rescue may be unrescued.
|
||||
machine.add_transition(RESCUEFAIL, UNRESCUING, 'unrescue')
|
||||
|
||||
# A failed rescue may be deleted.
|
||||
machine.add_transition(RESCUEFAIL, DELETING, 'delete')
|
||||
|
||||
# A rescuewait node may be deleted.
|
||||
machine.add_transition(RESCUEWAIT, DELETING, 'delete')
|
||||
|
||||
# A rescued node may be unrescued.
|
||||
machine.add_transition(RESCUE, UNRESCUING, 'unrescue')
|
||||
|
||||
# An unrescuing node may succeed
|
||||
machine.add_transition(UNRESCUING, ACTIVE, 'done')
|
||||
|
||||
# An unrescuing node may fail
|
||||
machine.add_transition(UNRESCUING, UNRESCUEFAIL, 'fail')
|
||||
|
||||
# A failed unrescue may be re-rescued
|
||||
machine.add_transition(UNRESCUEFAIL, RESCUING, 'rescue')
|
||||
|
||||
# A failed unrescue may be re-unrescued
|
||||
machine.add_transition(UNRESCUEFAIL, UNRESCUING, 'unrescue')
|
||||
|
||||
# A failed unrescue may be deleted.
|
||||
machine.add_transition(UNRESCUEFAIL, DELETING, 'delete')
|
||||
|
||||
# Start power credentials verification
|
||||
machine.add_transition(ENROLL, VERIFYING, 'manage')
|
||||
|
||||
# Verification can succeed
|
||||
machine.add_transition(VERIFYING, MANAGEABLE, 'done')
|
||||
|
||||
# Verification can fail with setting last_error and rolling back to ENROLL
|
||||
machine.add_transition(VERIFYING, ENROLL, 'fail')
|
||||
|
||||
# Node Adoption is being attempted
|
||||
machine.add_transition(MANAGEABLE, ADOPTING, 'adopt')
|
||||
|
||||
# Adoption can succeed and the node should be set to ACTIVE
|
||||
machine.add_transition(ADOPTING, ACTIVE, 'done')
|
||||
|
||||
# Node adoptions can fail and as such nodes shall be set
|
||||
# into a dedicated state to hold the nodes.
|
||||
machine.add_transition(ADOPTING, ADOPTFAIL, 'fail')
|
||||
|
||||
# Node adoption can be retried when it previously failed.
|
||||
machine.add_transition(ADOPTFAIL, ADOPTING, 'adopt')
|
||||
|
||||
# A node that failed adoption can be moved back to manageable
|
||||
machine.add_transition(ADOPTFAIL, MANAGEABLE, 'manage')
|
||||
|
||||
# Add service* states
|
||||
machine.add_state(SERVICING, target=ACTIVE, **watchers)
|
||||
machine.add_state(SERVICEWAIT, target=ACTIVE, **watchers)
|
||||
machine.add_state(SERVICEFAIL, target=ACTIVE, **watchers)
|
||||
machine.add_state(SERVICEHOLD, target=ACTIVE, **watchers)
|
||||
|
||||
# A node in service an be returned to active
|
||||
machine.add_transition(SERVICING, ACTIVE, 'done')
|
||||
|
||||
# A node in active can be serviced
|
||||
machine.add_transition(ACTIVE, SERVICING, 'service')
|
||||
|
||||
# A node in servicing can be failed
|
||||
machine.add_transition(SERVICING, SERVICEFAIL, 'fail')
|
||||
|
||||
# A node in service can enter a wait state
|
||||
machine.add_transition(SERVICING, SERVICEWAIT, 'wait')
|
||||
|
||||
# A node in service can be held
|
||||
machine.add_transition(SERVICING, SERVICEHOLD, 'hold')
|
||||
machine.add_transition(SERVICEWAIT, SERVICEHOLD, 'hold')
|
||||
|
||||
# A held node in service can get more service steps to start over
|
||||
machine.add_transition(SERVICEHOLD, SERVICING, 'service')
|
||||
|
||||
# A held node in service can be removed from service
|
||||
machine.add_transition(SERVICEHOLD, SERVICEWAIT, 'unhold')
|
||||
|
||||
# A node in service wait can resume
|
||||
machine.add_transition(SERVICEWAIT, SERVICING, 'resume')
|
||||
|
||||
# A node in service wait can failed
|
||||
machine.add_transition(SERVICEWAIT, SERVICEFAIL, 'fail')
|
||||
|
||||
# A node in service hold can failed
|
||||
machine.add_transition(SERVICEHOLD, SERVICEFAIL, 'fail')
|
||||
|
||||
# A node in service wait can be aborted
|
||||
machine.add_transition(SERVICEWAIT, SERVICEFAIL, 'abort')
|
||||
|
||||
# A node in service hold can be aborted
|
||||
machine.add_transition(SERVICEHOLD, SERVICEFAIL, 'abort')
|
||||
|
||||
# A node in service fail can re-enter service
|
||||
machine.add_transition(SERVICEFAIL, SERVICING, 'service')
|
||||
|
||||
# A node in service fail can be rescued
|
||||
machine.add_transition(SERVICEFAIL, RESCUING, 'rescue')
|
||||
|
||||
# A node in service fail can enter wait state
|
||||
machine.add_transition(SERVICEFAIL, SERVICEWAIT, 'wait')
|
||||
|
||||
# A node in service fail can be held
|
||||
machine.add_transition(SERVICEFAIL, SERVICEHOLD, 'hold')
|
||||
|
||||
# A node in service fail may be deleted.
|
||||
machine.add_transition(SERVICEFAIL, DELETING, 'delete')
|
||||
|
||||
# A node in service fail may be aborted (returned to active)
|
||||
machine.add_transition(SERVICEFAIL, ACTIVE, 'abort')
|
||||
|
||||
# A node in service wait may be deleted.
|
||||
machine.add_transition(SERVICEWAIT, DELETING, 'delete')
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ import tenacity
|
|||
from ironic.common import driver_factory
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common import state_machine
|
||||
from ironic.common import states
|
||||
from ironic.conductor import notification_utils as notify
|
||||
from ironic import objects
|
||||
|
|
@ -220,7 +221,7 @@ class TaskManager(object):
|
|||
self._retry = retry
|
||||
self._patient = patient
|
||||
|
||||
self.fsm = states.machine.copy()
|
||||
self.fsm = state_machine.machine.copy()
|
||||
self._purpose = purpose
|
||||
self._debug_timer = timeutils.StopWatch()
|
||||
|
||||
|
|
@ -500,7 +501,7 @@ class TaskManager(object):
|
|||
if self.node is None:
|
||||
# Rare case if resource released before notification
|
||||
task = copy.copy(self)
|
||||
task.fsm = states.machine.copy()
|
||||
task.fsm = state_machine.machine.copy()
|
||||
task.node = self._saved_node
|
||||
else:
|
||||
task = self
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import tenacity
|
|||
from ironic.common import driver_factory
|
||||
from ironic.common import exception
|
||||
from ironic.common import fsm
|
||||
from ironic.common import state_machine
|
||||
from ironic.common import states
|
||||
from ironic.conductor import notification_utils
|
||||
from ironic.conductor import task_manager
|
||||
|
|
@ -743,7 +744,7 @@ class TaskManagerTestCase(db_base.DbTestCase):
|
|||
on_error_handler.assert_called_once_with(expected_exception,
|
||||
'fake-argument')
|
||||
|
||||
@mock.patch.object(states.machine, 'copy', autospec=True)
|
||||
@mock.patch.object(state_machine.machine, 'copy', autospec=True)
|
||||
def test_init_prepares_fsm(
|
||||
self, copy_mock, get_volconn_mock, get_voltgt_mock,
|
||||
get_portgroups_mock, get_ports_mock,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ from oslo_config import cfg
|
|||
from testtools import matchers
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common import state_machine
|
||||
from ironic.common import states
|
||||
from ironic.conductor import cleaning
|
||||
from ironic.conductor import servicing
|
||||
|
|
@ -236,7 +237,7 @@ class HeartbeatMixinTest(AgentDeployMixinBaseTest):
|
|||
states.DEPLOYING, states.CLEANING, states.RESCUING,
|
||||
states.DEPLOYHOLD, states.CLEANHOLD, states.SERVICEHOLD,
|
||||
states.SERVICING, states.SERVICEWAIT}
|
||||
for state in set(states.machine.states) - allowed:
|
||||
for state in set(state_machine.machine.states) - allowed:
|
||||
for m in (next_step_mock, log_mock):
|
||||
m.reset_mock()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
|
|
@ -258,7 +259,7 @@ class HeartbeatMixinTest(AgentDeployMixinBaseTest):
|
|||
group='conductor')
|
||||
allowed = {states.DEPLOYWAIT, states.CLEANWAIT,
|
||||
states.SERVICEWAIT}
|
||||
for state in set(states.machine.states) - allowed:
|
||||
for state in set(state_machine.machine.states) - allowed:
|
||||
next_step_mock.reset_mock()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import sys
|
|||
|
||||
from automaton.converters import pydot
|
||||
|
||||
from ironic.common import states
|
||||
from ironic.common import state_machine
|
||||
|
||||
top_dir = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
os.pardir))
|
||||
|
|
@ -118,7 +118,7 @@ def main():
|
|||
attrs['fontcolor'] = 'gray'
|
||||
return attrs
|
||||
|
||||
source = states.machine
|
||||
source = state_machine.machine
|
||||
graph_name = '"Ironic states"'
|
||||
graph_attrs = {'size': 0}
|
||||
g = pydot.convert(source, graph_name, graph_attrs=graph_attrs,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue