A context manager to perform a series of tasks on a set of resources.
TaskManager is a context manager, created on-demand to allow synchronized access to a node and its resources.
The TaskManager will, by default, acquire an exclusive lock on a node for the duration that the TaskManager instance exists. You may create a TaskManager instance without locking by passing “shared=True” when creating it, but certain operations on the resources held by such an instance of TaskManager will not be possible. Requiring this exclusive lock guards against parallel operations interfering with each other.
A shared lock is useful when performing non-interfering operations, such as validating the driver interfaces.
An exclusive lock is stored in the database to coordinate between ironic.conductor.manager instances, that are typically deployed on different hosts.
TaskManager methods, as well as driver methods, may be decorated to determine whether their invocation requires an exclusive lock.
The TaskManager instance exposes certain node resources and properties as attributes that you may access:
- task.context
- The context passed to TaskManager()
- task.shared
- False if Node is locked, True if it is not locked. (The ‘shared’ kwarg arg of TaskManager())
- task.node
- The Node object
- task.ports
- Ports belonging to the Node
- task.driver
- The Driver for the Node, or the Driver based on the ‘driver_name’ kwarg of TaskManager().
Example usage:
with task_manager.acquire(context, node_id, purpose='power on') as task:
task.driver.power.power_on(task.node)
If you need to execute task-requiring code in a background thread, the TaskManager instance provides an interface to handle this for you, making sure to release resources when the thread finishes (successfully or if an exception occurs). Common use of this is within the Manager like so:
with task_manager.acquire(context, node_id, purpose='some work') as task:
<do some work>
task.spawn_after(self._spawn_worker,
utils.node_power_action, task, new_state)
All exceptions that occur in the current GreenThread as part of the spawn handling are re-raised. You can specify a hook to execute custom code when such exceptions occur. For example, the hook is a more elegant solution than wrapping the “with task_manager.acquire()” with a try..exception block. (Note that this hook does not handle exceptions raised in the background thread.):
def on_error(e):
if isinstance(e, Exception):
...
with task_manager.acquire(context, node_id, purpose='some work') as task:
<do some work>
task.set_spawn_error_hook(on_error)
task.spawn_after(self._spawn_worker,
utils.node_power_action, task, new_state)
Bases: object
Context manager for tasks.
This class wraps the locking, driver loading, and acquisition of related resources (eg, Node and Ports) when beginning a unit of work.
Process the given event for the task’s current state.
Parameters: |
|
---|---|
Raises: | InvalidState if the event is not allowed by the associated state machine |
Unlock a node and release resources.
If an exclusive lock is held, unlock the node. Reset attributes to make it clear that this instance of TaskManager should no longer be accessed.
Create a hook to handle exceptions when spawning a task.
Create a hook that gets called upon an exception being raised from spawning a background thread to do a task.
Parameters: |
|
---|
Call this to spawn a thread to complete the task.
The specified method will be called when the TaskManager instance exits.
Parameters: |
|
---|
Upgrade a shared lock to an exclusive lock.
Also reloads node object from the database. If lock is already exclusive only changes the lock purpose when provided with one.
Parameters: | purpose – optionally change the purpose of the lock |
---|---|
Raises: | NodeLocked if an exclusive lock remains on the node after “node_locked_retry_attempts” |