Rename "manager" to "conductor"
This rename to "conductor" more clearly communicates that this service has a many-to-many relationship. One or more service instances coordinate between each other to conduct actions on a set of nodes, using guarded locks to prevent conflicting simultaneous actions on any given node. The old name "manager" suggested a more one-to-many relationship, which is not the design pattern which we use here. Rename ironic/manager to ironic/conductor Rename ironic.manager.manager.ManagerService to ironic.conductor.manager.ConductorManager Rename ironic-manager to ironic-conductor Update docs too Change-Id: I3191be72a44bdaf14c763ce7519a7ae9066b2bc5
This commit is contained in:
parent
81cf262d21
commit
a360cb8771
|
@ -32,8 +32,8 @@ from ironic.common import exception
|
|||
from ironic.common import paths
|
||||
from ironic.common import states
|
||||
from ironic.common import utils
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers import base
|
||||
from ironic.manager import task_manager
|
||||
from ironic.openstack.common import excutils
|
||||
from ironic.openstack.common import jsonutils as json
|
||||
from ironic.openstack.common import log as logging
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# coding=utf-8
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# 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.
|
|
@ -1,82 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# coding=utf-8
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# 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.
|
||||
"""Handles all activity related to bare-metal deployments.
|
||||
|
||||
A single instance of :py:class:`ironic.manager.manager.ManagerService` is
|
||||
created within the *ironic-manager* process, and is responsible for performing
|
||||
all actions on bare metal resources (Chassis, Nodes, and Ports). Commands are
|
||||
received via RPC calls. The manager service also performs periodic tasks, eg.
|
||||
to monitor the status of active deployments.
|
||||
|
||||
Drivers are loaded via entrypoints, by the
|
||||
:py:class:`ironic.manager.resource_manager.NodeManager` class. Each driver is
|
||||
instantiated once and a ref to that singleton is included in each resource
|
||||
manager, depending on the node's configuration. In this way, a single
|
||||
ManagerService may use multiple drivers, and manage heterogeneous hardware.
|
||||
|
||||
When multiple :py:class:`ManagerService` are run on different hosts, they are
|
||||
all active and cooperatively manage all nodes in the deplyment. Nodes are
|
||||
locked by each manager when performing actions which change the state of that
|
||||
node; these locks are represented by the
|
||||
:py:class:`ironic.manager.task_manager.TaskManager` class.
|
||||
"""
|
||||
|
||||
from ironic.common import service
|
||||
from ironic.db import api as dbapi
|
||||
from ironic.manager import task_manager
|
||||
from ironic.openstack.common import log
|
||||
|
||||
MANAGER_TOPIC = 'ironic.manager'
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class ManagerService(service.PeriodicService):
|
||||
"""Ironic Manager service main class."""
|
||||
|
||||
RPC_API_VERSION = '1.0'
|
||||
|
||||
def __init__(self, host, topic):
|
||||
super(ManagerService, self).__init__(host, topic)
|
||||
|
||||
def start(self):
|
||||
super(ManagerService, self).start()
|
||||
self.dbapi = dbapi.get_instance()
|
||||
|
||||
def initialize_service_hook(self, service):
|
||||
pass
|
||||
|
||||
def process_notification(self, notification):
|
||||
LOG.debug(_('Received notification: %r') %
|
||||
notification.get('event_type'))
|
||||
# TODO(deva)
|
||||
|
||||
def periodic_tasks(self, context):
|
||||
# TODO(deva)
|
||||
pass
|
||||
|
||||
def get_node_power_state(self, context, node_id):
|
||||
"""Get and return the power state for a single node."""
|
||||
|
||||
with task_manager.acquire([node_id], shared=True) as task:
|
||||
node = task.resources[0].node
|
||||
driver = task.resources[0].driver
|
||||
state = driver.power.get_power_state(task, node)
|
||||
return state
|
||||
|
||||
# TODO(deva)
|
|
@ -1,108 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# coding=utf-8
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Hold the data and drivers for a distinct node within a given context.
|
||||
|
||||
Each :py:class:`ironic.manager.resource_manager.NodeManager` instance is a
|
||||
semi-singleton, keyed by the node id. It contains references to all
|
||||
:py:class:`ironic.manager.task_manager.TaskManager` which called it. When no
|
||||
more TaskManagers reference a given NodeManager, it is automatically destroyed.
|
||||
|
||||
Do not request a NodeManager directly; instead, you should use a TaskManager to
|
||||
manage the resource in a given context. See the documentation on TaskManager
|
||||
for an example.
|
||||
"""
|
||||
|
||||
from stevedore import dispatch
|
||||
|
||||
from ironic.openstack.common import lockutils
|
||||
from ironic.openstack.common import log
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.db import api as dbapi
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
RESOURCE_MANAGER_SEMAPHORE = "node_resource"
|
||||
|
||||
|
||||
class NodeManager(object):
|
||||
"""The data model, state, and drivers to manage a Node."""
|
||||
|
||||
_nodes = {}
|
||||
|
||||
_driver_factory = dispatch.NameDispatchExtensionManager(
|
||||
namespace='ironic.drivers',
|
||||
check_func=lambda x: True,
|
||||
invoke_on_load=True)
|
||||
|
||||
def __init__(self, id, t):
|
||||
db = dbapi.get_instance()
|
||||
|
||||
self.id = id
|
||||
self.task_refs = [t]
|
||||
self.node = db.get_node(id)
|
||||
self.ports = db.get_ports_by_node(id)
|
||||
|
||||
def _get_instance(ext, *args, **kwds):
|
||||
return ext.obj
|
||||
|
||||
# NOTE(deva): Driver loading here may get refactored, depend on:
|
||||
# https://github.com/dreamhost/stevedore/issues/15
|
||||
try:
|
||||
ref = NodeManager._driver_factory.map(
|
||||
[self.node.get('driver')], _get_instance)
|
||||
self.driver = ref[0]
|
||||
except KeyError:
|
||||
raise exception.IronicException(_(
|
||||
"Failed to load driver %s.") %
|
||||
self.node.get('driver'))
|
||||
|
||||
@classmethod
|
||||
@lockutils.synchronized(RESOURCE_MANAGER_SEMAPHORE, 'ironic-')
|
||||
def acquire(cls, id, t):
|
||||
"""Acquire a NodeManager and associate to a TaskManager."""
|
||||
n = cls._nodes.get(id)
|
||||
if n:
|
||||
n.task_refs.append(t)
|
||||
else:
|
||||
n = cls(id, t)
|
||||
cls._nodes[id] = n
|
||||
return n
|
||||
|
||||
@classmethod
|
||||
@lockutils.synchronized(RESOURCE_MANAGER_SEMAPHORE, 'ironic-')
|
||||
def release(cls, id, t):
|
||||
"""Release a NodeManager previously acquired."""
|
||||
|
||||
n = cls._nodes.get(id)
|
||||
if not n:
|
||||
raise exception.IronicException(_(
|
||||
"Release called on node %s for which no lock "
|
||||
"has been acquired.") % id)
|
||||
|
||||
try:
|
||||
n.task_refs.remove(t)
|
||||
except ValueError:
|
||||
raise exception.IronicException(_(
|
||||
"Can not release node %s because it was not "
|
||||
"reserved by this tracker.") % id)
|
||||
|
||||
# Delete the resource when no TaskManager references it.
|
||||
if len(n.task_refs) == 0:
|
||||
del(cls._nodes[id])
|
|
@ -1,54 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# coding=utf-8
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# 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.
|
||||
"""
|
||||
Client side of the manager RPC API.
|
||||
"""
|
||||
|
||||
import ironic.openstack.common.rpc.proxy
|
||||
|
||||
MANAGER_TOPIC = 'ironic.manager'
|
||||
|
||||
|
||||
class ManagerAPI(ironic.openstack.common.rpc.proxy.RpcProxy):
|
||||
"""Client side of the manager RPC API.
|
||||
|
||||
API version history:
|
||||
|
||||
1.0 - Initial version.
|
||||
"""
|
||||
|
||||
RPC_API_VERSION = '1.0'
|
||||
|
||||
def __init__(self, topic=None):
|
||||
if topic is None:
|
||||
topic = MANAGER_TOPIC
|
||||
|
||||
super(ManagerAPI, self).__init__(
|
||||
topic=topic,
|
||||
default_version=self.RPC_API_VERSION)
|
||||
|
||||
def get_node_power_state(self, context, node_id):
|
||||
"""Ask a manager for the node power state.
|
||||
|
||||
:param context: request context.
|
||||
:param node_id: node id or uuid.
|
||||
:returns: power status.
|
||||
"""
|
||||
return self.call(context,
|
||||
self.make_msg('get_node_power_state',
|
||||
node_id=node_id))
|
|
@ -1,128 +0,0 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# coding=utf-8
|
||||
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# 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.
|
||||
|
||||
"""
|
||||
A context manager to peform a series of tasks on a set of resources.
|
||||
|
||||
:class:`TaskManager` is a context manager, created on-demand to synchronize
|
||||
locking and simplify operations across a set of
|
||||
:class:`ironic.manager.resource_manager.NodeManager` instances. Each
|
||||
NodeManager holds the data model for a node, as well as references to the
|
||||
driver singleton appropriate for that node.
|
||||
|
||||
The :class:`TaskManager` will acquire either a shared or exclusive lock, as
|
||||
indicated. Multiple shared locks for the same resource may coexist with an
|
||||
exclusive lock, but only one exclusive lock will be granted across a
|
||||
deployment; attempting to allocate another will raise an exception. An
|
||||
exclusive lock is represented in the database to coordinate between
|
||||
:class:`ironic.manager.manager` instances, even when deployed on
|
||||
different hosts.
|
||||
|
||||
:class:`TaskManager` methods, as well as driver methods, may be decorated to
|
||||
determine whether their invocation requires an exclusive lock. For example::
|
||||
|
||||
from ironic.manager import task_manager
|
||||
|
||||
node_ids = [1, 2, 3]
|
||||
try:
|
||||
# Get an exclusive lock so we can manage power state.
|
||||
# This is the default behaviour of acquire_nodes.
|
||||
with task_manager.acquire(node_ids) as task:
|
||||
task.power_on()
|
||||
states = task.get_power_state()
|
||||
except exception.NodeLocked:
|
||||
LOG.info(_("Unable to power nodes on."))
|
||||
# Get a shared lock, just to check the power state.
|
||||
with task_manager.acquire(node_ids, shared=True) as task:
|
||||
states = nodes.get_power_state()
|
||||
|
||||
In case :class:`TaskManager` does not provide a method wrapping a particular
|
||||
driver function, you can access the drivers directly in this way::
|
||||
|
||||
with task_manager.acquire(node_ids) as task:
|
||||
states = []
|
||||
for node, driver in [r.node, r.driver
|
||||
for r in task.resources]:
|
||||
# the driver is loaded based on that node's configuration.
|
||||
states.append(driver.power.get_power_state(task, node)
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
from oslo.config import cfg
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.db import api as dbapi
|
||||
from ironic.manager import resource_manager
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def require_exclusive_lock(f):
|
||||
"""Decorator to require an exclusive lock.
|
||||
|
||||
Decorated functions must take a :class:`TaskManager` as the first
|
||||
parameter. Decorated class methods should take a :class:`TaskManager`
|
||||
as the first parameter after "self".
|
||||
|
||||
"""
|
||||
def wrapper(*args, **kwargs):
|
||||
task = args[0] if isinstance(args[0], TaskManager) else args[1]
|
||||
if task.shared:
|
||||
raise exception.ExclusiveLockRequired()
|
||||
return f(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def acquire(node_ids, shared=False):
|
||||
"""Context manager for acquiring a lock on one or more Nodes.
|
||||
|
||||
Acquire a lock atomically on a non-empty set of nodes. The lock
|
||||
can be either shared or exclusive. Shared locks may be used for
|
||||
read-only or non-disruptive actions only, and must be considerate
|
||||
to what other threads may be doing on the nodes at the same time.
|
||||
|
||||
:param node_ids: A list of ids or uuids of nodes to lock.
|
||||
:param shared: Boolean indicating whether to take a shared or exclusive
|
||||
lock. Default: False.
|
||||
:returns: An instance of :class:`TaskManager`.
|
||||
|
||||
"""
|
||||
|
||||
t = TaskManager(shared)
|
||||
|
||||
try:
|
||||
if not shared:
|
||||
t.dbapi.reserve_nodes(CONF.host, node_ids)
|
||||
for id in node_ids:
|
||||
t.resources.append(resource_manager.NodeManager.acquire(id, t))
|
||||
yield t
|
||||
finally:
|
||||
for id in [r.id for r in t.resources]:
|
||||
resource_manager.NodeManager.release(id, t)
|
||||
if not shared:
|
||||
t.dbapi.release_nodes(CONF.host, node_ids)
|
||||
|
||||
|
||||
class TaskManager(object):
|
||||
"""Context manager for tasks."""
|
||||
|
||||
def __init__(self, shared):
|
||||
self.shared = shared
|
||||
self.resources = []
|
||||
self.dbapi = dbapi.get_instance()
|
|
@ -30,7 +30,7 @@ packages =
|
|||
console_scripts =
|
||||
ironic-api = ironic.cmd.api:main
|
||||
ironic-dbsync = ironic.cmd.dbsync:main
|
||||
ironic-manager = ironic.cmd.manager:main
|
||||
ironic-conductor = ironic.cmd.conductor:main
|
||||
ironic-rootwrap = ironic.openstack.common.rootwrap.cmd:main
|
||||
|
||||
ironic.drivers =
|
||||
|
|
Loading…
Reference in New Issue