From 4bdc66d07a3c587640349a1883976f80c2762449 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 23 Nov 2015 16:11:35 +0100 Subject: [PATCH] rename Board -> Node --- bin/iotronic | 12 +- iotronic/api/controllers/v1/__init__.py | 34 ++--- .../api/controllers/v1/{board.py => node.py} | 136 ++++++++--------- iotronic/conductor/rpcapi.py | 6 +- iotronic/conductor/task_manager.py | 132 +++++++++-------- iotronic/db/api.py | 3 +- iotronic/db/sqlalchemy/api.py | 82 ++++++---- iotronic/db/sqlalchemy/models.py | 37 ++--- iotronic/objects/__init__.py | 9 +- iotronic/objects/{board.py => node.py} | 140 +++++++++--------- utils/dump.sql | 6 +- 11 files changed, 303 insertions(+), 294 deletions(-) rename iotronic/api/controllers/v1/{board.py => node.py} (57%) rename iotronic/objects/{board.py => node.py} (61%) diff --git a/bin/iotronic b/bin/iotronic index 230417c..10ce721 100755 --- a/bin/iotronic +++ b/bin/iotronic @@ -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 diff --git a/iotronic/api/controllers/v1/__init__.py b/iotronic/api/controllers/v1/__init__.py index dced5d2..3625055 100644 --- a/iotronic/api/controllers/v1/__init__.py +++ b/iotronic/api/controllers/v1/__init__.py @@ -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() diff --git a/iotronic/api/controllers/v1/board.py b/iotronic/api/controllers/v1/node.py similarity index 57% rename from iotronic/api/controllers/v1/board.py rename to iotronic/api/controllers/v1/node.py index a10e752..be51360 100644 --- a/iotronic/api/controllers/v1/board.py +++ b/iotronic/api/controllers/v1/node.py @@ -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) diff --git a/iotronic/conductor/rpcapi.py b/iotronic/conductor/rpcapi.py index 0ff61d4..75cd77d 100644 --- a/iotronic/conductor/rpcapi.py +++ b/iotronic/conductor/rpcapi.py @@ -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) \ No newline at end of file + return cctxt.call(context, 'destroy_board', board_id=board_id) +''' \ No newline at end of file diff --git a/iotronic/conductor/task_manager.py b/iotronic/conductor/task_manager.py index bc33875..1f31f3d 100644 --- a/iotronic/conductor/task_manager.py +++ b/iotronic/conductor/task_manager.py @@ -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: 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: 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 diff --git a/iotronic/db/api.py b/iotronic/db/api.py index 7cb1334..d58a5db 100644 --- a/iotronic/db/api.py +++ b/iotronic/db/api.py @@ -395,7 +395,7 @@ class Connection(object): """ - +''' ###################### NEW ############################# @@ -572,3 +572,4 @@ class Connection(object): :raises: BoardAssociated :raises: BoardNotFound """ +''' diff --git a/iotronic/db/sqlalchemy/api.py b/iotronic/db/sqlalchemy/api.py index b5d3425..2b4fcac 100644 --- a/iotronic/db/sqlalchemy/api.py +++ b/iotronic/db/sqlalchemy/api.py @@ -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 \ No newline at end of file + return ref +""" diff --git a/iotronic/db/sqlalchemy/models.py b/iotronic/db/sqlalchemy/models.py index 6095713..9e7ebd0 100644 --- a/iotronic/db/sqlalchemy/models.py +++ b/iotronic/db/sqlalchemy/models.py @@ -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) diff --git a/iotronic/objects/__init__.py b/iotronic/objects/__init__.py index 927ad82..064c8c7 100644 --- a/iotronic/objects/__init__.py +++ b/iotronic/objects/__init__.py @@ -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 ) diff --git a/iotronic/objects/board.py b/iotronic/objects/node.py similarity index 61% rename from iotronic/objects/board.py rename to iotronic/objects/node.py index 8e41102..931d508 100644 --- a/iotronic/objects/board.py +++ b/iotronic/objects/node.py @@ -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: diff --git a/utils/dump.sql b/utils/dump.sql index b0b13ff..4414e83 100644 --- a/utils/dump.sql +++ b/utils/dump.sql @@ -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,