rename Board -> Node

This commit is contained in:
root 2015-11-23 16:11:35 +01:00
parent 93d2f34680
commit 4bdc66d07a
11 changed files with 303 additions and 294 deletions

View File

@ -7,22 +7,22 @@ BASE=http://$HOST:$PORT/$VERSION
if [ $# -lt 1 ]
then
echo "list - create-board - delete-board - show"
echo "list - create-node - delete-node - show"
fi
case "$1" in
list) curl -sS $BASE/boards/ | python -m json.tool
list) curl -sS $BASE/nodes/ | python -m json.tool
echo "";
;;
create-board) curl -sS -H "Content-Type: application/json" -X POST $BASE/boards/ -d '{"code":"'"$2"'"}' | python -m json.tool
create-node) curl -sS -H "Content-Type: application/json" -X POST $BASE/nodes/ -d '{"code":"'"$2"'"}' | python -m json.tool
echo "";
;;
delete-board) curl -sS -X DELETE $BASE/boards/$2 | python -m json.tool
delete-node) curl -sS -X DELETE $BASE/nodes/$2 | python -m json.tool
echo "";
;;
show) curl -sS $BASE/boards/$2 | python -m json.tool
show) curl -sS $BASE/nodes/$2 | python -m json.tool
echo "";
;;
*) echo "list - create-board - delete-board - show"
*) echo "list - create-node - delete-node - show"
esac

View File

@ -21,7 +21,7 @@ from pecan import rest
from webob import exc
from wsme import types as wtypes
from iotronic.api.controllers import link
from iotronic.api.controllers.v1 import board
from iotronic.api.controllers.v1 import node
'''
@ -29,7 +29,7 @@ from iotronic.api.controllers.v1 import board
#from iotronic.api.controllers.v1 import chassis
#from iotronic.api.controllers.v1 import driver
from iotronic.api.controllers.v1 import node
#from iotronic.api.controllers.v1 import port
from iotronic.api.controllers.v1 import board
@ -79,10 +79,7 @@ class V1(base.APIBase):
#chassis = [link.Link]
"""Links to the chassis resource"""
#nodes = [link.Link]
"""Links to the nodes resource"""
boards = [link.Link]
nodes = [link.Link]
"""Links to the nodes resource"""
#ports = [link.Link]
@ -96,13 +93,13 @@ class V1(base.APIBase):
v1 = V1()
v1.id = "v1"
v1.boards = [link.Link.make_link('self', pecan.request.host_url,
'nodes', ''),
link.Link.make_link('bookmark',
pecan.request.host_url,
'nodes', '',
bookmark=True)
]
v1.nodes = [link.Link.make_link('self', pecan.request.host_url,
'nodes', ''),
link.Link.make_link('bookmark',
pecan.request.host_url,
'nodes', '',
bookmark=True)
]
'''
v1.links = [link.Link.make_link('self', pecan.request.host_url,
@ -124,14 +121,6 @@ class V1(base.APIBase):
'chassis', '',
bookmark=True)
]
v1.nodes = [link.Link.make_link('self', pecan.request.host_url,
'nodes', ''),
link.Link.make_link('bookmark',
pecan.request.host_url,
'nodes', '',
bookmark=True)
]
'''
'''
v1.ports = [link.Link.make_link('self', pecan.request.host_url,
@ -155,8 +144,7 @@ class V1(base.APIBase):
class Controller(rest.RestController):
"""Version 1 API controller root."""
boards = board.BoardsController()
#nodes = node.NodesController()
nodes = node.NodesController()
#ports = port.PortsController()
#chassis = chassis.ChassisController()
#drivers = driver.DriversController()

View File

@ -12,8 +12,8 @@ import pecan
from pecan import rest
class Board(base.APIBase):
"""API representation of a board.
class Node(base.APIBase):
"""API representation of a node.
"""
uuid = types.uuid
@ -21,45 +21,45 @@ class Board(base.APIBase):
status = wsme.wsattr(wtypes.text)
@staticmethod
def _convert_with_links(board, url, expand=True, show_password=True):
def _convert_with_links(node, url, expand=True, show_password=True):
'''
if not expand:
except_list = ['instance_uuid', 'maintenance', 'power_state',
'provision_state', 'uuid', 'name']
board.unset_fields_except(except_list)
node.unset_fields_except(except_list)
else:
if not show_password:
board.driver_info = ast.literal_eval(strutils.mask_password(
board.driver_info,
node.driver_info = ast.literal_eval(strutils.mask_password(
node.driver_info,
"******"))
board.ports = [link.Link.make_link('self', url, 'boards',
board.uuid + "/ports"),
link.Link.make_link('bookmark', url, 'boards',
board.uuid + "/ports",
node.ports = [link.Link.make_link('self', url, 'nodes',
node.uuid + "/ports"),
link.Link.make_link('bookmark', url, 'nodes',
node.uuid + "/ports",
bookmark=True)
]
board.chassis_id = wtypes.Unset
node.chassis_id = wtypes.Unset
'''
'''
board.links = [link.Link.make_link('self', url, 'boards',
board.uuid),
link.Link.make_link('bookmark', url, 'boards',
board.uuid, bookmark=True)
node.links = [link.Link.make_link('self', url, 'nodes',
node.uuid),
link.Link.make_link('bookmark', url, 'nodes',
node.uuid, bookmark=True)
]
'''
return board
return node
@classmethod
def convert_with_links(cls, rpc_board, expand=True):
board = Board(**rpc_board.as_dict())
return cls._convert_with_links(board, pecan.request.host_url,
def convert_with_links(cls, rpc_node, expand=True):
node = Node(**rpc_node.as_dict())
return cls._convert_with_links(node, pecan.request.host_url,
expand,
pecan.request.context.show_password)
def __init__(self, **kwargs):
self.fields = []
fields = list(objects.Board.fields)
fields = list(objects.Node.fields)
for k in fields:
# Skip fields we do not expose.
if not hasattr(self, k):
@ -67,27 +67,27 @@ class Board(base.APIBase):
self.fields.append(k)
setattr(self, k, kwargs.get(k, wtypes.Unset))
class BoardCollection(collection.Collection):
"""API representation of a collection of boards."""
class NodeCollection(collection.Collection):
"""API representation of a collection of nodes."""
boards = [Board]
"""A list containing boards objects"""
nodes = [Node]
"""A list containing nodes objects"""
def __init__(self, **kwargs):
self._type = 'boards'
self._type = 'nodes'
@staticmethod
def convert_with_links(boards, limit, url=None, expand=False, **kwargs):
collection = BoardCollection()
collection.boards = [Board.convert_with_links(n, expand) for n in boards]
def convert_with_links(nodes, limit, url=None, expand=False, **kwargs):
collection = NodeCollection()
collection.nodes = [Node.convert_with_links(n, expand) for n in nodes]
collection.next = collection.get_next(limit, url=url, **kwargs)
return collection
class BoardsController(rest.RestController):
class NodesController(rest.RestController):
invalid_sort_key_list = ['properties']
def _get_boards_collection(self, chassis_uuid, instance_uuid, associated,
def _get_nodes_collection(self, chassis_uuid, instance_uuid, associated,
maintenance, marker, limit, sort_key, sort_dir,
expand=False, resource_url=None):
'''
@ -100,7 +100,7 @@ class BoardsController(rest.RestController):
marker_obj = None
if marker:
marker_obj = objects.Board.get_by_uuid(pecan.request.context,
marker_obj = objects.Node.get_by_uuid(pecan.request.context,
marker)
if sort_key in self.invalid_sort_key_list:
@ -109,7 +109,7 @@ class BoardsController(rest.RestController):
"sorting") % {'key': sort_key})
if instance_uuid:
boards = self._get_boards_by_instance(instance_uuid)
nodes = self._get_nodes_by_instance(instance_uuid)
else:
filters = {}
if chassis_uuid:
@ -119,7 +119,7 @@ class BoardsController(rest.RestController):
if maintenance is not None:
filters['maintenance'] = maintenance
boards = objects.Board.list(pecan.request.context, limit, marker_obj,
nodes = objects.Node.list(pecan.request.context, limit, marker_obj,
sort_key=sort_key, sort_dir=sort_dir,
filters=filters)
@ -128,80 +128,80 @@ class BoardsController(rest.RestController):
parameters['associated'] = associated
if maintenance:
parameters['maintenance'] = maintenance
return BoardCollection.convert_with_links(boards, limit,
return NodeCollection.convert_with_links(nodes, limit,
url=resource_url,
expand=expand,
**parameters)
@expose.expose(BoardCollection, types.uuid, types.uuid, types.boolean,
@expose.expose(NodeCollection, types.uuid, types.uuid, types.boolean,
types.boolean, types.uuid, int, wtypes.text, wtypes.text)
def get_all(self, chassis_uuid=None, instance_uuid=None, associated=None,
maintenance=None, marker=None, limit=None, sort_key='id',
sort_dir='asc'):
"""Retrieve a list of boards.
"""Retrieve a list of nodes.
:param chassis_uuid: Optional UUID of a chassis, to get only boards for
:param chassis_uuid: Optional UUID of a chassis, to get only nodes for
that chassis.
:param instance_uuid: Optional UUID of an instance, to find the board
:param instance_uuid: Optional UUID of an instance, to find the node
associated with that instance.
:param associated: Optional boolean whether to return a list of
associated or unassociated boards. May be combined
associated or unassociated nodes. May be combined
with other parameters.
:param maintenance: Optional boolean value that indicates whether
to get boards in maintenance mode ("True"), or not
to get nodes in maintenance mode ("True"), or not
in maintenance mode ("False").
:param marker: pagination marker for large data sets.
:param limit: maximum number of resources to return in a single result.
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
"""
return self._get_boards_collection(chassis_uuid, instance_uuid,
return self._get_nodes_collection(chassis_uuid, instance_uuid,
associated, maintenance, marker,
limit, sort_key, sort_dir)
@expose.expose(Board,types.uuid_or_name)
def get(self,board_ident):
"""Retrieve information about the given board.
@expose.expose(Node,types.uuid_or_name)
def get(self,node_ident):
"""Retrieve information about the given node.
:param node_ident: UUID or logical name of a board.
:param node_ident: UUID or logical name of a node.
"""
rpc_board = api_utils.get_rpc_board(board_ident)
board = Board(**rpc_board.as_dict())
return board
rpc_node = api_utils.get_rpc_node(node_ident)
node = Node(**rpc_node.as_dict())
return node
@expose.expose(None, types.uuid_or_name, status_code=204)
def delete(self, board_ident):
"""Delete a board.
def delete(self, node_ident):
"""Delete a node.
:param board_ident: UUID or logical name of a board.
:param node_ident: UUID or logical name of a node.
"""
rpc_board = api_utils.get_rpc_board(board_ident)
rpc_node = api_utils.get_rpc_node(node_ident)
try:
topic = pecan.request.rpcapi.get_topic_for(rpc_board)
topic = pecan.request.rpcapi.get_topic_for(rpc_node)
except exception.NoValidHost as e:
e.code = 400
raise e
pecan.request.rpcapi.destroy_board(pecan.request.context,
rpc_board.uuid, topic)
pecan.request.rpcapi.destroy_node(pecan.request.context,
rpc_node.uuid, topic)
@expose.expose(Board, body=Board, status_code=201)
def post(self,Board):
"""Create a new Board.
@expose.expose(Node, body=Node, status_code=201)
def post(self,Node):
"""Create a new Node.
:param Board: a Board within the request body.
:param Node: a Node within the request body.
"""
if not Board.uuid:
Board.uuid = uuidutils.generate_uuid()
if not Board.status:
Board.status = 'DISCONNECTED'
new_Board = objects.Board(pecan.request.context,
**Board.as_dict())
new_Board.create()
#pecan.response.location = link.build_url('Boards', new_Board.uuid)
return Board.convert_with_links(new_Board)
if not Node.uuid:
Node.uuid = uuidutils.generate_uuid()
if not Node.status:
Node.status = 'DISCONNECTED'
new_Node = objects.Node(pecan.request.context,
**Node.as_dict())
new_Node.create()
#pecan.response.location = link.build_url('Nodes', new_Node.uuid)
return Node.convert_with_links(new_Node)

View File

@ -337,7 +337,7 @@ class ConductorAPI(object):
:raises: InvalidState if the node is in the wrong provision
state to perform deletion.
"""
cctxt = self.client.prepare(topic=topic or self.topic, version='1.9')
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
return cctxt.call(context, 'destroy_node', node_id=node_id)
def get_console_information(self, context, node_id, topic=None):
@ -505,6 +505,7 @@ class ConductorAPI(object):
cctxt = self.client.prepare(topic=topic or self.topic, version='1.25')
return cctxt.call(context, 'destroy_port', port=port)
'''
######################### NEW
def destroy_board(self, context, board_id, topic=None):
@ -519,4 +520,5 @@ class ConductorAPI(object):
state to perform deletion.
"""
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
return cctxt.call(context, 'destroy_board', board_id=board_id)
return cctxt.call(context, 'destroy_board', board_id=board_id)
'''

View File

@ -19,10 +19,10 @@
A context manager to perform a series of tasks on a set of resources.
:class:`TaskManager` is a context manager, created on-demand to allow
synchronized access to a board and its resources.
synchronized access to a node and its resources.
The :class:`TaskManager` will, by default, acquire an exclusive lock on
a board for the duration that the TaskManager instance exists. You may
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
@ -38,28 +38,28 @@ different hosts.
:class:`TaskManager` methods, as well as driver methods, may be decorated to
determine whether their invocation requires an exclusive lock.
The TaskManager instance exposes certain board resources and properties as
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 Board is locked, True if it is not locked. (The
False if Node is locked, True if it is not locked. (The
'shared' kwarg arg of TaskManager())
task.board
The Board object
task.node
The Node object
task.ports
Ports belonging to the Board
Ports belonging to the Node
task.driver
The Driver for the Board, or the Driver based on the
The Driver for the Node, or the Driver based on the
'driver_name' kwarg of TaskManager().
Example usage:
::
with task_manager.acquire(context, board_id) as task:
task.driver.power.power_on(task.board)
with task_manager.acquire(context, node_id) 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
@ -68,10 +68,10 @@ an exception occurs). Common use of this is within the Manager like so:
::
with task_manager.acquire(context, board_id) as task:
with task_manager.acquire(context, node_id) as task:
<do some work>
task.spawn_after(self._spawn_worker,
utils.board_power_action, task, new_state)
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
@ -86,11 +86,11 @@ raised in the background thread.):
if isinstance(e, Exception):
...
with task_manager.acquire(context, board_id) as task:
with task_manager.acquire(context, node_id) as task:
<do some work>
task.set_spawn_error_hook(on_error)
task.spawn_after(self._spawn_worker,
utils.board_power_action, task, new_state)
utils.node_power_action, task, new_state)
"""
@ -129,18 +129,18 @@ def require_exclusive_lock(f):
return wrapper
def acquire(context, board_id, shared=False, driver_name=None):
"""Shortcut for acquiring a lock on a Board.
def acquire(context, node_id, shared=False, driver_name=None):
"""Shortcut for acquiring a lock on a Node.
:param context: Request context.
:param board_id: ID or UUID of board to lock.
:param node_id: ID or UUID of node to lock.
:param shared: Boolean indicating whether to take a shared or exclusive
lock. Default: False.
:param driver_name: Name of Driver. Default: None.
:returns: An instance of :class:`TaskManager`.
"""
return TaskManager(context, board_id, shared=shared,
return TaskManager(context, node_id, shared=shared,
driver_name=driver_name)
@ -148,27 +148,27 @@ class TaskManager(object):
"""Context manager for tasks.
This class wraps the locking, driver loading, and acquisition
of related resources (eg, Board and Ports) when beginning a unit of work.
of related resources (eg, Node and Ports) when beginning a unit of work.
"""
def __init__(self, context, board_id, shared=False, driver_name=None):
def __init__(self, context, node_id, shared=False, driver_name=None):
"""Create a new TaskManager.
Acquire a lock on a board. The lock can be either shared or
Acquire a lock on a node. 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 same board at the same time.
other threads may be doing on the same node at the same time.
:param context: request context
:param board_id: ID or UUID of board to lock.
:param node_id: ID or UUID of node to lock.
:param shared: Boolean indicating whether to take a shared or exclusive
lock. Default: False.
:param driver_name: The name of the driver to load, if different
from the Board's current driver.
from the Node's current driver.
:raises: DriverNotFound
:raises: BoardNotFound
:raises: BoardLocked
:raises: NodeNotFound
:raises: NodeLocked
"""
@ -176,41 +176,43 @@ class TaskManager(object):
self._on_error_method = None
self.context = context
#self.board = None
self.board = None
#self.node = None
self.node = None
self.shared = shared
self.fsm = states.machine.copy()
# BoardLocked exceptions can be annoying. Let's try to alleviate
# NodeLocked exceptions can be annoying. Let's try to alleviate
# some of that pain by retrying our lock attempts. The retrying
# module expects a wait_fixed value in milliseconds.
@retrying.retry(
retry_on_exception=lambda e: isinstance(e, exception.BoardLocked),
stop_max_attempt_number=CONF.conductor.board_locked_retry_attempts,
wait_fixed=CONF.conductor.board_locked_retry_interval * 1000)
def reserve_board():
LOG.debug("Attempting to reserve board %(board)s",
{'board': board_id})
self.board = objects.Board.reserve(context, CONF.host, board_id)
retry_on_exception=lambda e: isinstance(e, exception.NodeLocked),
stop_max_attempt_number=CONF.conductor.node_locked_retry_attempts,
wait_fixed=CONF.conductor.node_locked_retry_interval * 1000)
def reserve_node():
LOG.debug("Attempting to reserve node %(node)s",
{'node': node_id})
self.node = objects.Node.reserve(context, CONF.host, node_id)
try:
"""
if not self.shared:
reserve_board()
reserve_node()
else:
self.board = objects.Board.get(context, board_id)
#self.ports = objects.Port.list_by_board_id(context, self.board.id)
"""
self.node = objects.Node.get(context, node_id)
#self.ports = objects.Port.list_by_node_id(context, self.node.id)
#self.driver = driver_factory.get_driver(driver_name or
# self.board.driver)
# self.node.driver)
# NOTE(deva): this handles the Juno-era NOSTATE state
# and should be deleted after Kilo is released
'''
if self.board.provision_state is states.NOSTATE:
self.board.provision_state = states.AVAILABLE
self.board.save()
if self.node.provision_state is states.NOSTATE:
self.node.provision_state = states.AVAILABLE
self.node.save()
self.fsm.initialize(self.board.provision_state)
self.fsm.initialize(self.node.provision_state)
'''
except Exception:
with excutils.save_and_reraise_exception():
@ -248,25 +250,27 @@ class TaskManager(object):
self._on_error_kwargs = kwargs
def release_resources(self):
"""Unlock a board and release resources.
"""Unlock a node and release resources.
If an exclusive lock is held, unlock the board. Reset attributes
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.
"""
pass #don't need it at the moment
"""
if not self.shared:
try:
if self.board:
objects.Board.release(self.context, CONF.host, self.board.id)
except exception.BoardNotFound:
# squelch the exception if the board was deleted
if self.node:
objects.Node.release(self.context, CONF.host, self.node.id)
except exception.NodeNotFound:
# squelch the exception if the node was deleted
# within the task's context.
pass
self.board = None
self.node = None
self.driver = None
self.ports = None
self.fsm = None
"""
def _thread_release_resources(self, t):
"""Thread.link() callback to release resources."""
@ -282,38 +286,38 @@ class TaskManager(object):
:param call_kwargs: optional \**kwargs to pass to the callback method
:param err_handler: optional error handler to invoke if the
callback fails, eg. because there are no workers available
(err_handler should accept arguments board, prev_prov_state, and
(err_handler should accept arguments node, prev_prov_state, and
prev_target_state)
:raises: InvalidState if the event is not allowed by the associated
state machine
"""
# Advance the state model for the given event. Note that this doesn't
# alter the board in any way. This may raise InvalidState, if this event
# alter the node in any way. This may raise InvalidState, if this event
# is not allowed in the current state.
self.fsm.process_event(event)
# stash current states in the error handler if callback is set,
# in case we fail to get a worker from the pool
if err_handler and callback:
self.set_spawn_error_hook(err_handler, self.board,
self.board.provision_state,
self.board.target_provision_state)
self.set_spawn_error_hook(err_handler, self.node,
self.node.provision_state,
self.node.target_provision_state)
self.board.provision_state = self.fsm.current_state
self.board.target_provision_state = self.fsm.target_state
self.node.provision_state = self.fsm.current_state
self.node.target_provision_state = self.fsm.target_state
# set up the async worker
if callback:
# clear the error if we're going to start work in a callback
self.board.last_error = None
self.node.last_error = None
if call_args is None:
call_args = ()
if call_kwargs is None:
call_kwargs = {}
self.spawn_after(callback, *call_args, **call_kwargs)
# publish the state transition by saving the Board
self.board.save()
# publish the state transition by saving the Node
self.node.save()
def __enter__(self):
return self
@ -351,9 +355,9 @@ class TaskManager(object):
**self._on_error_kwargs)
except Exception:
LOG.warning(_LW("Task's on_error hook failed to "
"call %(method)s on board %(board)s"),
"call %(method)s on node %(node)s"),
{'method': self._on_error_method.__name__,
'board': self.board.uuid})
'node': self.node.uuid})
if thread is not None:
# This means the link() failed for some

View File

@ -395,7 +395,7 @@ class Connection(object):
"""
'''
###################### NEW #############################
@ -572,3 +572,4 @@ class Connection(object):
:raises: BoardAssociated
:raises: BoardNotFound
"""
'''

View File

@ -119,9 +119,9 @@ def add_port_filter_by_node(query, value):
if strutils.is_int_like(value):
return query.filter_by(node_id=value)
else:
query = query.join(models.Board,
models.Port.node_id == models.Board.id)
return query.filter(models.Board.uuid == value)
query = query.join(models.Node,
models.Port.node_id == models.Node.id)
return query.filter(models.Node.uuid == value)
def add_node_filter_by_chassis(query, value):
@ -129,7 +129,7 @@ def add_node_filter_by_chassis(query, value):
return query.filter_by(chassis_id=value)
else:
query = query.join(models.Chassis,
models.Board.chassis_id == models.Chassis.id)
models.Node.chassis_id == models.Chassis.id)
return query.filter(models.Chassis.uuid == value)
@ -167,14 +167,16 @@ class Connection(api.Connection):
query = query.filter_by(chassis_id=chassis_obj.id)
if 'associated' in filters:
if filters['associated']:
query = query.filter(models.Board.instance_uuid != None)
query = query.filter(models.Node.instance_uuid != None)
else:
query = query.filter(models.Board.instance_uuid == None)
query = query.filter(models.Node.instance_uuid == None)
"""
if 'reserved' in filters:
if filters['reserved']:
query = query.filter(models.Board.reservation != None)
query = query.filter(models.Node.reservation != None)
else:
query = query.filter(models.Board.reservation == None)
query = query.filter(models.Node.reservation == None)
"""
if 'maintenance' in filters:
query = query.filter_by(maintenance=filters['maintenance'])
if 'driver' in filters:
@ -184,12 +186,12 @@ class Connection(api.Connection):
if 'provisioned_before' in filters:
limit = (timeutils.utcnow() -
datetime.timedelta(seconds=filters['provisioned_before']))
query = query.filter(models.Board.provision_updated_at < limit)
query = query.filter(models.Node.provision_updated_at < limit)
if 'inspection_started_before' in filters:
limit = ((timeutils.utcnow()) -
(datetime.timedelta(
seconds=filters['inspection_started_before'])))
query = query.filter(models.Board.inspection_started_at < limit)
query = query.filter(models.Node.inspection_started_at < limit)
return query
@ -198,26 +200,26 @@ class Connection(api.Connection):
# list-ify columns default values because it is bad form
# to include a mutable list in function definitions.
if columns is None:
columns = [models.Board.id]
columns = [models.Node.id]
else:
columns = [getattr(models.Board, c) for c in columns]
columns = [getattr(models.Node, c) for c in columns]
query = model_query(*columns, base_model=models.Board)
query = model_query(*columns, base_model=models.Node)
query = self._add_nodes_filters(query, filters)
return _paginate_query(models.Board, limit, marker,
return _paginate_query(models.Node, limit, marker,
sort_key, sort_dir, query)
def get_node_list(self, filters=None, limit=None, marker=None,
sort_key=None, sort_dir=None):
query = model_query(models.Board)
query = model_query(models.Node)
query = self._add_nodes_filters(query, filters)
return _paginate_query(models.Board, limit, marker,
return _paginate_query(models.Node, limit, marker,
sort_key, sort_dir, query)
def reserve_node(self, tag, node_id):
session = get_session()
with session.begin():
query = model_query(models.Board, session=session)
query = model_query(models.Node, session=session)
query = add_identity_filter(query, node_id)
# be optimistic and assume we usually create a reservation
count = query.filter_by(reservation=None).update(
@ -236,7 +238,7 @@ class Connection(api.Connection):
def release_node(self, tag, node_id):
session = get_session()
with session.begin():
query = model_query(models.Board, session=session)
query = model_query(models.Node, session=session)
query = add_identity_filter(query, node_id)
# be optimistic and assume we usually release a reservation
count = query.filter_by(reservation=tag).update(
@ -262,7 +264,7 @@ class Connection(api.Connection):
# TODO(deva): change this to ENROLL
values['provision_state'] = states.AVAILABLE
node = models.Board()
node = models.Node()
node.update(values)
try:
node.save()
@ -277,21 +279,21 @@ class Connection(api.Connection):
return node
def get_node_by_id(self, node_id):
query = model_query(models.Board).filter_by(id=node_id)
query = model_query(models.Node).filter_by(id=node_id)
try:
return query.one()
except NoResultFound:
raise exception.NodeNotFound(node=node_id)
def get_node_by_uuid(self, node_uuid):
query = model_query(models.Board).filter_by(uuid=node_uuid)
query = model_query(models.Node).filter_by(uuid=node_uuid)
try:
return query.one()
except NoResultFound:
raise exception.NodeNotFound(node=node_uuid)
def get_node_by_name(self, node_name):
query = model_query(models.Board).filter_by(name=node_name)
query = model_query(models.Node).filter_by(name=node_name)
try:
return query.one()
except NoResultFound:
@ -301,7 +303,7 @@ class Connection(api.Connection):
if not uuidutils.is_uuid_like(instance):
raise exception.InvalidUUID(uuid=instance)
query = (model_query(models.Board)
query = (model_query(models.Node)
.filter_by(instance_uuid=instance))
try:
@ -312,9 +314,26 @@ class Connection(api.Connection):
return result
def destroy_node(self, node_id):
session = get_session()
with session.begin():
query = model_query(models.Board, session=session)
query = model_query(models.Node, session=session)
query = add_identity_filter(query, node_id)
try:
node_ref = query.one()
except NoResultFound:
raise exception.NodeNotFound(node=node_id)
# Get node ID, if an UUID was supplied. The ID is
# required for deleting all ports, attached to the node.
if uuidutils.is_uuid_like(node_id):
node_id = node_ref['id']
query.delete()
"""
session = get_session()
with session.begin():
query = model_query(models.Node, session=session)
query = add_identity_filter(query, node_id)
try:
@ -332,11 +351,12 @@ class Connection(api.Connection):
#port_query.delete()
query.delete()
"""
def update_node(self, node_id, values):
# NOTE(dtantsur): this can lead to very strange errors
if 'uuid' in values:
msg = _("Cannot overwrite UUID for an existing Board.")
msg = _("Cannot overwrite UUID for an existing Node.")
raise exception.InvalidParameterValue(err=msg)
try:
@ -356,7 +376,7 @@ class Connection(api.Connection):
def _do_update_node(self, node_id, values):
session = get_session()
with session.begin():
query = model_query(models.Board, session=session)
query = model_query(models.Node, session=session)
query = add_identity_filter(query, node_id)
try:
ref = query.with_lockmode('update').one()
@ -509,7 +529,7 @@ class Connection(api.Connection):
def chassis_not_empty(session):
"""Checks whether the chassis does not have nodes."""
query = model_query(models.Board, session=session)
query = model_query(models.Node, session=session)
query = add_node_filter_by_chassis(query, chassis_id)
return query.count() != 0
@ -579,7 +599,7 @@ class Connection(api.Connection):
session = get_session()
nodes = []
with session.begin():
query = (model_query(models.Board, session=session)
query = (model_query(models.Node, session=session)
.filter_by(reservation=hostname))
nodes = [node['uuid'] for node in query]
query.update({'reservation': None})
@ -607,6 +627,7 @@ class Connection(api.Connection):
return d2c
"""
###################### NEW #############################
def _add_boards_filters(self, query, filters):
if filters is None:
@ -818,4 +839,5 @@ class Connection(api.Connection):
values['inspection_started_at'] = None
'''
ref.update(values)
return ref
return ref
"""

View File

@ -139,8 +139,22 @@ class Conductor(Base):
class Node(Base):
"""Represents a bare metal node."""
"""Represents a board."""
__tablename__ = 'nodes'
'''
__table_args__ = (
schema.UniqueConstraint('uuid', name='uniq_nodes0uuid'),
schema.UniqueConstraint('instance_uuid',
name='uniq_nodes0instance_uuid'),
schema.UniqueConstraint('name', name='uniq_nodes0name'),
table_args())
'''
id = Column(Integer, primary_key=True)
uuid = Column(String(36))
code = Column(String(25))
status = Column(String(15), nullable=True)
"""
__tablename__ = 'nodes'
'''
__table_args__ = (
@ -193,7 +207,7 @@ class Node(Base):
#inspection_finished_at = Column(DateTime, nullable=True)
#inspection_started_at = Column(DateTime, nullable=True)
#extra = Column(JSONEncodedDict)
"""
class Port(Base):
"""Represents a network port of a bare metal node."""
@ -208,23 +222,4 @@ class Port(Base):
address = Column(String(18))
node_id = Column(Integer, ForeignKey('nodes.id'), nullable=True)
extra = Column(JSONEncodedDict)
##################### NEW
class Board(Base):
"""Represents a board."""
__tablename__ = 'boards'
'''
__table_args__ = (
schema.UniqueConstraint('uuid', name='uniq_nodes0uuid'),
schema.UniqueConstraint('instance_uuid',
name='uniq_nodes0instance_uuid'),
schema.UniqueConstraint('name', name='uniq_nodes0name'),
table_args())
'''
id = Column(Integer, primary_key=True)
uuid = Column(String(36))
code = Column(String(25))
status = Column(String(15), nullable=True)
#reservation = Column(String(255), nullable=True)

View File

@ -14,21 +14,18 @@
#from iotronic.objects import chassis
from iotronic.objects import conductor
#from iotronic.objects import node
from iotronic.objects import board
from iotronic.objects import node
#from iotronic.objects import port
#Chassis = chassis.Chassis
Conductor = conductor.Conductor
Board=board.Board
#Node = node.Node
Node = node.Node
#Port = port.Port
__all__ = (
#Chassis,
Conductor,
#Node,
Board,
Node,
#Port
)

View File

@ -22,7 +22,7 @@ from iotronic.objects import base
from iotronic.objects import utils as obj_utils
class Board(base.IotronicObject):
class Node(base.IotronicObject):
# Version 1.0: Initial version
VERSION = '1.0'
@ -37,75 +37,75 @@ class Board(base.IotronicObject):
}
@staticmethod
def _from_db_object(board, db_board):
def _from_db_object(node, db_node):
"""Converts a database entity to a formal object."""
for field in board.fields:
board[field] = db_board[field]
board.obj_reset_changes()
return board
for field in node.fields:
node[field] = db_node[field]
node.obj_reset_changes()
return node
@base.remotable_classmethod
def get(cls, context, board_id):
"""Find a board based on its id or uuid and return a Board object.
def get(cls, context, node_id):
"""Find a node based on its id or uuid and return a Node object.
:param board_id: the id *or* uuid of a board.
:returns: a :class:`Board` object.
:param node_id: the id *or* uuid of a node.
:returns: a :class:`Node` object.
"""
if strutils.is_int_like(board_id):
return cls.get_by_id(context, board_id)
elif uuidutils.is_uuid_like(board_id):
return cls.get_by_uuid(context, board_id)
if strutils.is_int_like(node_id):
return cls.get_by_id(context, node_id)
elif uuidutils.is_uuid_like(node_id):
return cls.get_by_uuid(context, node_id)
else:
raise exception.InvalidIdentity(identity=board_id)
raise exception.InvalidIdentity(identity=node_id)
@base.remotable_classmethod
def get_by_id(cls, context, board_id):
"""Find a board based on its integer id and return a Board object.
def get_by_id(cls, context, node_id):
"""Find a node based on its integer id and return a Node object.
:param board_id: the id of a board.
:returns: a :class:`Board` object.
:param node_id: the id of a node.
:returns: a :class:`Node` object.
"""
db_board = cls.dbapi.get_board_by_id(board_id)
board = Board._from_db_object(cls(context), db_board)
return board
db_node = cls.dbapi.get_node_by_id(node_id)
node = Node._from_db_object(cls(context), db_node)
return node
@base.remotable_classmethod
def get_by_uuid(cls, context, uuid):
"""Find a board based on uuid and return a Board object.
"""Find a node based on uuid and return a Node object.
:param uuid: the uuid of a board.
:returns: a :class:`Board` object.
:param uuid: the uuid of a node.
:returns: a :class:`Node` object.
"""
db_board = cls.dbapi.get_board_by_uuid(uuid)
board = Board._from_db_object(cls(context), db_board)
return board
db_node = cls.dbapi.get_node_by_uuid(uuid)
node = Node._from_db_object(cls(context), db_node)
return node
@base.remotable_classmethod
def get_by_code(cls, context, code):
"""Find a board based on name and return a Board object.
"""Find a node based on name and return a Node object.
:param name: the logical name of a board.
:returns: a :class:`Board` object.
:param name: the logical name of a node.
:returns: a :class:`Node` object.
"""
db_board = cls.dbapi.get_board_by_code(code)
board = Board._from_db_object(cls(context), db_board)
return board
db_node = cls.dbapi.get_node_by_code(code)
node = Node._from_db_object(cls(context), db_node)
return node
@base.remotable_classmethod
def get_by_instance_uuid(cls, context, instance_uuid):
"""Find a board based on the instance uuid and return a Board object.
"""Find a node based on the instance uuid and return a Node object.
:param uuid: the uuid of the instance.
:returns: a :class:`Board` object.
:returns: a :class:`Node` object.
"""
db_board = cls.dbapi.get_board_by_instance(instance_uuid)
board = Board._from_db_object(cls(context), db_board)
return board
db_node = cls.dbapi.get_node_by_instance(instance_uuid)
node = Node._from_db_object(cls(context), db_node)
return node
@base.remotable_classmethod
def list(cls, context, limit=None, marker=None, sort_key=None,
sort_dir=None, filters=None):
"""Return a list of Board objects.
"""Return a list of Node objects.
:param context: Security context.
:param limit: maximum number of resources to return in a single result.
@ -113,101 +113,101 @@ class Board(base.IotronicObject):
:param sort_key: column to sort results by.
:param sort_dir: direction to sort. "asc" or "desc".
:param filters: Filters to apply.
:returns: a list of :class:`Board` object.
:returns: a list of :class:`Node` object.
"""
db_boards = cls.dbapi.get_board_list(filters=filters, limit=limit,
db_nodes = cls.dbapi.get_node_list(filters=filters, limit=limit,
marker=marker, sort_key=sort_key,
sort_dir=sort_dir)
return [Board._from_db_object(cls(context), obj) for obj in db_boards]
return [Node._from_db_object(cls(context), obj) for obj in db_nodes]
@base.remotable_classmethod
def reserve(cls, context, tag, board_id):
"""Get and reserve a board.
def reserve(cls, context, tag, node_id):
"""Get and reserve a node.
To prevent other ManagerServices from manipulating the given
Board while a Task is performed, mark it reserved by this host.
Node while a Task is performed, mark it reserved by this host.
:param context: Security context.
:param tag: A string uniquely identifying the reservation holder.
:param board_id: A board id or uuid.
:raises: BoardNotFound if the board is not found.
:returns: a :class:`Board` object.
:param node_id: A node id or uuid.
:raises: NodeNotFound if the node is not found.
:returns: a :class:`Node` object.
"""
db_board = cls.dbapi.reserve_board(tag, board_id)
board = Board._from_db_object(cls(context), db_board)
return board
db_node = cls.dbapi.reserve_node(tag, node_id)
node = Node._from_db_object(cls(context), db_node)
return node
@base.remotable_classmethod
def release(cls, context, tag, board_id):
"""Release the reservation on a board.
def release(cls, context, tag, node_id):
"""Release the reservation on a node.
:param context: Security context.
:param tag: A string uniquely identifying the reservation holder.
:param board_id: A board id or uuid.
:raises: BoardNotFound if the board is not found.
:param node_id: A node id or uuid.
:raises: NodeNotFound if the node is not found.
"""
cls.dbapi.release_board(tag, board_id)
cls.dbapi.release_node(tag, node_id)
@base.remotable
def create(self, context=None):
"""Create a Board record in the DB.
"""Create a Node record in the DB.
Column-wise updates will be made based on the result of
self.what_changed(). If target_power_state is provided,
it will be checked against the in-database copy of the
board before updates are made.
node before updates are made.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: Board(context)
object, e.g.: Node(context)
"""
values = self.obj_get_changes()
db_board = self.dbapi.create_board(values)
self._from_db_object(self, db_board)
db_node = self.dbapi.create_node(values)
self._from_db_object(self, db_node)
@base.remotable
def destroy(self, context=None):
"""Delete the Board from the DB.
"""Delete the Node from the DB.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: Board(context)
object, e.g.: Node(context)
"""
self.dbapi.destroy_board(self.uuid)
self.dbapi.destroy_node(self.uuid)
self.obj_reset_changes()
@base.remotable
def save(self, context=None):
"""Save updates to this Board.
"""Save updates to this Node.
Column-wise updates will be made based on the result of
self.what_changed(). If target_power_state is provided,
it will be checked against the in-database copy of the
board before updates are made.
node before updates are made.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: Board(context)
object, e.g.: Node(context)
"""
updates = self.obj_get_changes()
if 'driver' in updates and 'driver_internal_info' not in updates:
# Clean driver_internal_info when changes driver
self.driver_internal_info = {}
updates = self.obj_get_changes()
self.dbapi.update_board(self.uuid, updates)
self.dbapi.update_node(self.uuid, updates)
self.obj_reset_changes()
@base.remotable
@ -219,7 +219,7 @@ class Board(base.IotronicObject):
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: Board(context)
object, e.g.: Node(context)
"""
current = self.__class__.get_by_uuid(self._context, self.uuid)
for field in self.fields:

View File

@ -20,13 +20,13 @@ CREATE DATABASE /*!32312 IF NOT EXISTS*/ `iotronic` /*!40100 DEFAULT CHARACTER S
USE `iotronic`;
--
-- Table structure for table `boards`
-- Table structure for table `nodes`
--
DROP TABLE IF EXISTS `boards`;
DROP TABLE IF EXISTS `nodes`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `boards` (
CREATE TABLE `nodes` (
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`id` int(11) NOT NULL AUTO_INCREMENT,