diff --git a/doc/source/index.rst b/doc/source/index.rst index aa34e722cb..2b308c58f6 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -35,7 +35,6 @@ There are several different types of objects in the magnum system: * **Bay:** A collection of node objects where work is scheduled * **BayModel:** An object stores template information about the bay which is used to create new bays consistently -* **Node:** A baremetal or virtual machine where work executes * **Pod:** A collection of containers running on one physical or virtual machine * **Service:** An abstraction which defines a logical set of pods and a policy diff --git a/doc/source/objects.rst b/doc/source/objects.rst index 3fd1efb28d..ad6062708a 100644 --- a/doc/source/objects.rst +++ b/doc/source/objects.rst @@ -86,7 +86,6 @@ magnum/tests/unit/objects/test_objects.py:: 'BayModel': '1.0-06863f04ab4b98307e3d1b736d3137bf', 'Container': '1.1-22b40e8eed0414561ca921906b189820', 'MyObj': '1.0-b43567e512438205e32f4e95ca616697', - 'Node': '1.0-30943e6e3387a2fae7490b57c4239a17', 'Pod': '1.0-69b579203c6d726be7878c606626e438', 'ReplicationController': '1.0-782b7deb9307b2807101541b7e58b8a2', 'Service': '1.0-d4b8c0f3a234aec35d273196e18f7ed1', diff --git a/etc/magnum/policy.json b/etc/magnum/policy.json index e33473d071..6c10c50593 100644 --- a/etc/magnum/policy.json +++ b/etc/magnum/policy.json @@ -19,13 +19,6 @@ "baymodel:update": "rule:default", "baymodel:publish": "rule:admin_or_owner", - "node:create": "rule:default", - "node:delete": "rule:default", - "node:detail": "rule:default", - "node:get": "rule:default", - "node:get_all": "rule:default", - "node:update": "rule:default", - "pod:create": "rule:default", "pod:delete": "rule:default", "pod:detail": "rule:default", diff --git a/magnum/api/controllers/v1/__init__.py b/magnum/api/controllers/v1/__init__.py index 1fdda1c661..17b9feae89 100644 --- a/magnum/api/controllers/v1/__init__.py +++ b/magnum/api/controllers/v1/__init__.py @@ -31,7 +31,6 @@ from magnum.api.controllers.v1 import baymodel from magnum.api.controllers.v1 import certificate from magnum.api.controllers.v1 import container from magnum.api.controllers.v1 import magnum_services -from magnum.api.controllers.v1 import node from magnum.api.controllers.v1 import pod from magnum.api.controllers.v1 import replicationcontroller as rc from magnum.api.controllers.v1 import service @@ -192,7 +191,6 @@ class Controller(rest.RestController): bays = bay.BaysController() baymodels = baymodel.BayModelsController() containers = container.ContainersController() - nodes = node.NodesController() pods = pod.PodsController() rcs = rc.ReplicationControllersController() services = service.ServicesController() diff --git a/magnum/api/controllers/v1/node.py b/magnum/api/controllers/v1/node.py deleted file mode 100644 index 4c074f63dc..0000000000 --- a/magnum/api/controllers/v1/node.py +++ /dev/null @@ -1,255 +0,0 @@ -# Copyright 2013 UnitedStack Inc. -# 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. - -from oslo_utils import timeutils -import pecan -from pecan import rest -import wsme -from wsme import types as wtypes - -from magnum.api.controllers import base -from magnum.api.controllers import link -from magnum.api.controllers.v1 import collection -from magnum.api.controllers.v1 import types -from magnum.api import expose -from magnum.api import utils as api_utils -from magnum.common import exception -from magnum.common import policy -from magnum import objects - - -class NodePatchType(types.JsonPatchType): - pass - - -class Node(base.APIBase): - """API representation of a node. - - This class enforces type checking and value constraints, and converts - between the internal object model and the API representation of a node. - """ - - uuid = types.uuid - """Unique UUID for this node""" - - type = wtypes.text - """Type of this node""" - - image_id = wtypes.text - """The image name or UUID to use as a base image for this node""" - - ironic_node_id = wtypes.text - """The Ironic node ID associated with this node""" - - links = wsme.wsattr([link.Link], readonly=True) - """A list containing a self link and associated node links""" - - def __init__(self, **kwargs): - self.fields = [] - for field in objects.Node.fields: - # Skip fields we do not expose. - if not hasattr(self, field): - continue - self.fields.append(field) - setattr(self, field, kwargs.get(field, wtypes.Unset)) - - @staticmethod - def _convert_with_links(node, url, expand=True): - if not expand: - node.unset_fields_except(['uuid', 'name', 'type', 'image_id', - 'ironic_node_id']) - - node.links = [link.Link.make_link('self', url, - 'nodes', node.uuid), - link.Link.make_link('bookmark', url, - 'nodes', node.uuid, - bookmark=True)] - return node - - @classmethod - 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) - - @classmethod - def sample(cls, expand=True): - sample = cls(uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c', - type='virt', - image_id='Fedora-k8s', - ironic_node_id='4b6ec4a9-d412-494a-be77-a2fd16361402', - created_at=timeutils.utcnow(), - updated_at=timeutils.utcnow()) - return cls._convert_with_links(sample, 'http://localhost:9511', expand) - - -class NodeCollection(collection.Collection): - """API representation of a collection of nodes.""" - - nodes = [Node] - """A list containing nodes objects""" - - def __init__(self, **kwargs): - self._type = 'nodes' - - @staticmethod - def convert_with_links(rpc_nodes, limit, url=None, expand=False, **kwargs): - collection = NodeCollection() - collection.nodes = [Node.convert_with_links(p, expand) - for p in rpc_nodes] - collection.next = collection.get_next(limit, url=url, **kwargs) - return collection - - @classmethod - def sample(cls): - sample = cls() - sample.nodes = [Node.sample(expand=False)] - return sample - - -class NodesController(rest.RestController): - """REST controller for Nodes.""" - - _custom_actions = { - 'detail': ['GET'], - } - - def _get_nodes_collection(self, marker, limit, - sort_key, sort_dir, expand=False, - resource_url=None): - - limit = api_utils.validate_limit(limit) - sort_dir = api_utils.validate_sort_dir(sort_dir) - - marker_obj = None - if marker: - marker_obj = objects.Node.get_by_uuid(pecan.request.context, - marker) - - nodes = objects.Node.list(pecan.request.context, limit, - marker_obj, sort_key=sort_key, - sort_dir=sort_dir) - - return NodeCollection.convert_with_links(nodes, limit, - url=resource_url, - expand=expand, - sort_key=sort_key, - sort_dir=sort_dir) - - @expose.expose(NodeCollection, types.uuid, int, wtypes.text, - wtypes.text) - @policy.enforce_wsgi("node") - def get_all(self, marker=None, limit=None, sort_key='id', - sort_dir='asc'): - """Retrieve a list of nodes. - - :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_nodes_collection(marker, limit, sort_key, - sort_dir) - - @expose.expose(NodeCollection, types.uuid, int, wtypes.text, - wtypes.text) - @policy.enforce_wsgi("node") - def detail(self, marker=None, limit=None, sort_key='id', - sort_dir='asc'): - """Retrieve a list of nodes with detail. - - :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. - """ - # NOTE(lucasagomes): /detail should only work against collections - parent = pecan.request.path.split('/')[:-1][-1] - if parent != "nodes": - raise exception.HTTPNotFound - - expand = True - resource_url = '/'.join(['nodes', 'detail']) - return self._get_nodes_collection(marker, limit, - sort_key, sort_dir, expand, - resource_url) - - @expose.expose(Node, types.uuid) - @policy.enforce_wsgi("node", "get") - def get_one(self, node_uuid): - """Retrieve information about the given node. - - :param node_uuid: UUID of a node. - """ - rpc_node = objects.Node.get_by_uuid(pecan.request.context, node_uuid) - return Node.convert_with_links(rpc_node) - - @expose.expose(Node, body=Node, status_code=201) - @policy.enforce_wsgi("node", "create") - def post(self, node): - """Create a new node. - - :param node: a node within the request body. - """ - node_dict = node.as_dict() - context = pecan.request.context - node_dict['project_id'] = context.project_id - node_dict['user_id'] = context.user_id - new_node = objects.Node(context, **node_dict) - new_node.create() - # Set the HTTP Location Header - pecan.response.location = link.build_url('nodes', new_node.uuid) - return Node.convert_with_links(new_node) - - @wsme.validate(types.uuid, [NodePatchType]) - @expose.expose(Node, types.uuid, body=[NodePatchType]) - @policy.enforce_wsgi("node", "update") - def patch(self, node_uuid, patch): - """Update an existing node. - - :param node_uuid: UUID of a node. - :param patch: a json PATCH document to apply to this node. - """ - rpc_node = objects.Node.get_by_uuid(pecan.request.context, node_uuid) - try: - node_dict = rpc_node.as_dict() - node = Node(**api_utils.apply_jsonpatch(node_dict, patch)) - except api_utils.JSONPATCH_EXCEPTIONS as e: - raise exception.PatchError(patch=patch, reason=e) - - # Update only the fields that have changed - for field in objects.Node.fields: - try: - patch_val = getattr(node, field) - except AttributeError: - # Ignore fields that aren't exposed in the API - continue - if patch_val == wtypes.Unset: - patch_val = None - if rpc_node[field] != patch_val: - rpc_node[field] = patch_val - - rpc_node.save() - return Node.convert_with_links(rpc_node) - - @expose.expose(None, types.uuid, status_code=204) - @policy.enforce_wsgi("node", "delete") - def delete(self, node_uuid): - """Delete a node. - - :param node_uuid: UUID of a node. - """ - rpc_node = objects.Node.get_by_uuid(pecan.request.context, - node_uuid) - rpc_node.destroy() diff --git a/magnum/common/exception.py b/magnum/common/exception.py index 615ab12b65..291634c1bb 100644 --- a/magnum/common/exception.py +++ b/magnum/common/exception.py @@ -314,11 +314,6 @@ class InvalidParameterValue(Invalid): message = _("%(err)s") -class InstanceAssociated(Conflict): - message = _("Instance %(instance_uuid)s is already associated with a node," - " it cannot be associated with this other node %(node)s") - - class InstanceNotFound(ResourceNotFound): message = _("Instance %(instance)s could not be found.") @@ -351,18 +346,6 @@ class ConfigInvalid(MagnumException): message = _("Invalid configuration file. %(error_msg)s") -class NodeAlreadyExists(Conflict): - message = _("A node with UUID %(uuid)s already exists.") - - -class NodeNotFound(ResourceNotFound): - message = _("Node %(node)s could not be found.") - - -class NodeAssociated(InvalidState): - message = _("Node %(node)s is associated with instance %(instance)s.") - - class SSHConnectFailed(MagnumException): message = _("Failed to establish SSH connection to host %(host)s.") diff --git a/magnum/db/api.py b/magnum/db/api.py index 18ada651ef..436b52fe56 100644 --- a/magnum/db/api.py +++ b/magnum/db/api.py @@ -289,78 +289,6 @@ class Connection(object): """ @abc.abstractmethod - def get_node_list(self, context, filters=None, limit=None, - marker=None, sort_key=None, sort_dir=None): - """Get for matching nodes. - - Return a list of the specified columns for all nodes that match the - specified filters. - - :param context: The security context - :param filters: Filters to apply. Defaults to None. - - :param limit: Maximum number of nodes to return. - :param marker: the last item of the previous page; we return the next - result set. - :param sort_key: Attribute by which results should be sorted. - :param sort_dir: direction in which results should be sorted. - (asc, desc) - :returns: A list of tuples of the specified columns. - """ - - @abc.abstractmethod - def create_node(self, values): - """Create a new node. - - :param values: A dict containing several items used to identify - and track the node, and several dicts which are passed - into the Drivers when managing this node. For example: - - :: - - { - 'uuid': utils.generate_uuid(), - 'name': 'example', - 'type': 'virt' - } - :returns: A node. - """ - - @abc.abstractmethod - def get_node_by_id(self, context, node_id): - """Return a node. - - :param context: The security context - :param node_id: The id of a node. - :returns: A node. - """ - - @abc.abstractmethod - def get_node_by_uuid(self, context, node_uuid): - """Return a node. - - :param context: The security context - :param node_uuid: The uuid of a node. - :returns: A node. - """ - - @abc.abstractmethod - def destroy_node(self, node_id): - """Destroy a node and all associated interfaces. - - :param node_id: The id or uuid of a node. - """ - - @abc.abstractmethod - def update_node(self, node_id, values): - """Update properties of a node. - - :param node_id: The id or uuid of a node. - :returns: A node. - :raises: NodeAssociated - :raises: NodeNotFound - """ - @abc.abstractmethod def get_pod_list(self, context, filters=None, limit=None, marker=None, sort_key=None, sort_dir=None): """Get matching pods. diff --git a/magnum/db/sqlalchemy/alembic/versions/bb42b7cad130_remove_node_object.py b/magnum/db/sqlalchemy/alembic/versions/bb42b7cad130_remove_node_object.py new file mode 100644 index 0000000000..53ff07690c --- /dev/null +++ b/magnum/db/sqlalchemy/alembic/versions/bb42b7cad130_remove_node_object.py @@ -0,0 +1,29 @@ +# 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. + +"""remove node object + +Revision ID: bb42b7cad130 +Revises: 05d3e97de9ee +Create Date: 2016-02-02 16:04:36.501547 + +""" + +# revision identifiers, used by Alembic. +revision = 'bb42b7cad130' +down_revision = '05d3e97de9ee' + +from alembic import op + + +def upgrade(): + op.drop_table('node') diff --git a/magnum/db/sqlalchemy/api.py b/magnum/db/sqlalchemy/api.py index 81e562561b..d1fc0fbeaa 100644 --- a/magnum/db/sqlalchemy/api.py +++ b/magnum/db/sqlalchemy/api.py @@ -21,7 +21,6 @@ from oslo_db.sqlalchemy import utils as db_utils from oslo_utils import timeutils from sqlalchemy.orm.exc import MultipleResultsFound from sqlalchemy.orm.exc import NoResultFound -from sqlalchemy import sql from magnum.common import exception from magnum.common import utils @@ -463,110 +462,6 @@ class Connection(api.Connection): ref.update(values) return ref - def _add_nodes_filters(self, query, filters): - if filters is None: - filters = {} - - if 'associated' in filters: - if filters['associated']: - query = query.filter(models.Node.ironic_node_id != sql.null()) - else: - query = query.filter(models.Node.ironic_node_id == sql.null()) - if 'type' in filters: - query = query.filter_by(type=filters['type']) - if 'image_id' in filters: - query = query.filter_by(image_id=filters['image_id']) - if 'project_id' in filters: - query = query.filter_by(project_id=filters['project_id']) - if 'user_id' in filters: - query = query.filter_by(user_id=filters['user_id']) - - return query - - def get_node_list(self, context, filters=None, limit=None, marker=None, - sort_key=None, sort_dir=None): - query = model_query(models.Node) - query = self._add_tenant_filters(context, query) - query = self._add_nodes_filters(query, filters) - return _paginate_query(models.Node, limit, marker, - sort_key, sort_dir, query) - - def create_node(self, values): - # ensure defaults are present for new nodes - if not values.get('uuid'): - values['uuid'] = utils.generate_uuid() - - node = models.Node() - node.update(values) - try: - node.save() - except db_exc.DBDuplicateEntry as exc: - if 'ironic_node_id' in exc.columns: - raise exception.InstanceAssociated( - instance_uuid=values['ironic_node_id'], - node=values['uuid']) - raise exception.NodeAlreadyExists(uuid=values['uuid']) - return node - - def get_node_by_id(self, context, node_id): - query = model_query(models.Node) - query = self._add_tenant_filters(context, query) - query = query.filter_by(id=node_id) - try: - return query.one() - except NoResultFound: - raise exception.NodeNotFound(node=node_id) - - def get_node_by_uuid(self, context, node_uuid): - query = model_query(models.Node) - query = self._add_tenant_filters(context, query) - query = query.filter_by(uuid=node_uuid) - try: - return query.one() - except NoResultFound: - raise exception.NodeNotFound(node=node_uuid) - - def destroy_node(self, node_id): - session = get_session() - with session.begin(): - query = model_query(models.Node, session=session) - query = add_identity_filter(query, node_id) - count = query.delete() - if count != 1: - raise exception.NodeNotFound(node_id) - - 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 Node.") - raise exception.InvalidParameterValue(err=msg) - - try: - return self._do_update_node(node_id, values) - except db_exc.DBDuplicateEntry: - raise exception.InstanceAssociated( - instance_uuid=values['ironic_node_id'], - node=node_id) - - def _do_update_node(self, node_id, values): - session = get_session() - with session.begin(): - query = model_query(models.Node, session=session) - query = add_identity_filter(query, node_id) - try: - ref = query.with_lockmode('update').one() - except NoResultFound: - raise exception.NodeNotFound(node=node_id) - - # Prevent ironic_node_id overwriting - if values.get("ironic_node_id") and ref.ironic_node_id: - raise exception.NodeAssociated( - node=node_id, - instance=ref.ironic_node_id) - - ref.update(values) - return ref - def _add_pods_filters(self, query, filters): if filters is None: filters = {} diff --git a/magnum/db/sqlalchemy/models.py b/magnum/db/sqlalchemy/models.py index 64c6dbf696..f61479cd35 100644 --- a/magnum/db/sqlalchemy/models.py +++ b/magnum/db/sqlalchemy/models.py @@ -190,25 +190,6 @@ class Container(Base): environment = Column(JSONEncodedDict) -class Node(Base): - """Represents a node.""" - - __tablename__ = 'node' - __table_args__ = ( - schema.UniqueConstraint('uuid', name='uniq_node0uuid'), - schema.UniqueConstraint('ironic_node_id', - name='uniq_node0ironic_node_id'), - table_args() - ) - id = Column(Integer, primary_key=True) - uuid = Column(String(36)) - type = Column(String(20)) - project_id = Column(String(255)) - user_id = Column(String(255)) - image_id = Column(String(255)) - ironic_node_id = Column(String(36)) - - class Pod(Base): """Represents a pod.""" diff --git a/magnum/objects/__init__.py b/magnum/objects/__init__.py index f112dfcd1a..96b0a3005c 100644 --- a/magnum/objects/__init__.py +++ b/magnum/objects/__init__.py @@ -17,7 +17,6 @@ from magnum.objects import baymodel from magnum.objects import certificate from magnum.objects import container from magnum.objects import magnum_service -from magnum.objects import node from magnum.objects import pod from magnum.objects import replicationcontroller as rc from magnum.objects import service @@ -28,7 +27,6 @@ Container = container.Container Bay = bay.Bay BayModel = baymodel.BayModel MagnumService = magnum_service.MagnumService -Node = node.Node Pod = pod.Pod ReplicationController = rc.ReplicationController Service = service.Service @@ -38,7 +36,6 @@ __all__ = (Bay, BayModel, Container, MagnumService, - Node, Pod, ReplicationController, Service, diff --git a/magnum/objects/node.py b/magnum/objects/node.py deleted file mode 100644 index bd92e72799..0000000000 --- a/magnum/objects/node.py +++ /dev/null @@ -1,161 +0,0 @@ -# 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. - -from oslo_versionedobjects import fields - -from magnum.db import api as dbapi -from magnum.objects import base - - -@base.MagnumObjectRegistry.register -class Node(base.MagnumPersistentObject, base.MagnumObject, - base.MagnumObjectDictCompat): - # Version 1.0: Initial version - VERSION = '1.0' - - dbapi = dbapi.get_instance() - - fields = { - 'id': fields.IntegerField(), - 'uuid': fields.StringField(nullable=True), - 'project_id': fields.StringField(nullable=True), - 'user_id': fields.StringField(nullable=True), - 'type': fields.StringField(nullable=True), - 'image_id': fields.StringField(nullable=True), - 'ironic_node_id': fields.StringField(nullable=True) - } - - @staticmethod - def _from_db_object(node, db_node): - """Converts a database entity to a formal object.""" - for field in node.fields: - node[field] = db_node[field] - - node.obj_reset_changes() - return node - - @staticmethod - def _from_db_object_list(db_objects, cls, context): - """Converts a list of database entities to a list of formal objects.""" - return [Node._from_db_object(cls(context), obj) for obj in db_objects] - - @base.remotable_classmethod - def get_by_id(cls, context, node_id): - """Find a node based on its integer id and return a Node object. - - :param node_id: the id of a node. - :param context: Security context - :returns: a :class:`Node` object. - """ - db_node = cls.dbapi.get_node_by_id(context, 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 node based on uuid and return a :class:`Node` object. - - :param uuid: the uuid of a node. - :param context: Security context - :returns: a :class:`Node` object. - """ - db_node = cls.dbapi.get_node_by_uuid(context, 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): - """Return a list of Node objects. - - :param context: Security context. - :param limit: maximum number of resources to return in a single result. - :param marker: pagination marker for large data sets. - :param sort_key: column to sort results by. - :param sort_dir: direction to sort. "asc" or "desc". - :returns: a list of :class:`Node` object. - - """ - db_nodes = cls.dbapi.get_node_list(context, limit=limit, - marker=marker, - sort_key=sort_key, - sort_dir=sort_dir) - return Node._from_db_object_list(db_nodes, cls, context) - - @base.remotable - def create(self, context=None): - """Create a Node record in 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.: Node(context) - - """ - values = self.obj_get_changes() - db_node = self.dbapi.create_node(values) - self._from_db_object(self, db_node) - - @base.remotable - def destroy(self, context=None): - """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.: Node(context) - """ - self.dbapi.destroy_node(self.uuid) - self.obj_reset_changes() - - @base.remotable - def save(self, context=None): - """Save updates to this Node. - - Updates will be made column by column based on the result - of self.what_changed(). - - :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.: Node(context) - """ - updates = self.obj_get_changes() - self.dbapi.update_node(self.uuid, updates) - - self.obj_reset_changes() - - @base.remotable - def refresh(self, context=None): - """Loads updates for this Node. - - Loads a node with the same uuid from the database and - checks for updated attributes. Updates are applied from - the loaded node column by column, if there are any updates. - - :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.: Node(context) - """ - current = self.__class__.get_by_uuid(self._context, uuid=self.uuid) - for field in self.fields: - if self.obj_attr_is_set(field) and self[field] != current[field]: - self[field] = current[field] diff --git a/magnum/tests/fake_policy.py b/magnum/tests/fake_policy.py index 10a62d292d..ea29c9d21f 100644 --- a/magnum/tests/fake_policy.py +++ b/magnum/tests/fake_policy.py @@ -34,13 +34,6 @@ policy_data = """ "baymodel:get_all": "", "baymodel:update": "", - "node:create": "", - "node:delete": "", - "node:detail": "", - "node:get": "", - "node:get_all": "", - "node:update": "", - "pod:create": "", "pod:delete": "", "pod:detail": "", diff --git a/magnum/tests/functional/k8s/test_magnum_python_client.py b/magnum/tests/functional/k8s/test_magnum_python_client.py index 4b79819dac..67f7e14e15 100644 --- a/magnum/tests/functional/k8s/test_magnum_python_client.py +++ b/magnum/tests/functional/k8s/test_magnum_python_client.py @@ -23,6 +23,3 @@ class TestListResources(BaseMagnumClient): def test_containers_list(self): self.assertIsNotNone(self.cs.containers.list()) - - def test_nodes_list(self): - self.assertIsNotNone(self.cs.nodes.list()) diff --git a/magnum/tests/unit/api/controllers/v1/test_node.py b/magnum/tests/unit/api/controllers/v1/test_node.py deleted file mode 100644 index 7163a44fa3..0000000000 --- a/magnum/tests/unit/api/controllers/v1/test_node.py +++ /dev/null @@ -1,341 +0,0 @@ -# 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. - -import datetime -import json - -import mock -from oslo_config import cfg -from oslo_utils import timeutils -from six.moves.urllib import parse as urlparse -from wsme import types as wtypes - -from magnum.api.controllers.v1 import node as api_node -from magnum.common import utils -from magnum.tests import base -from magnum.tests.unit.api import base as api_base -from magnum.tests.unit.api import utils as apiutils -from magnum.tests.unit.objects import utils as obj_utils - - -class TestNodeObject(base.TestCase): - - def test_node_init(self): - node_dict = apiutils.node_post_data() - del node_dict['image_id'] - node = api_node.Node(**node_dict) - self.assertEqual(wtypes.Unset, node.image_id) - - -class TestListNode(api_base.FunctionalTest): - - def test_empty(self): - response = self.get_json('/nodes') - self.assertEqual([], response['nodes']) - - def _assert_node_fields(self, node): - node_fields = ['type', 'image_id', 'ironic_node_id'] - for field in node_fields: - self.assertIn(field, node) - - def test_one(self): - node = obj_utils.create_test_node(self.context) - response = self.get_json('/nodes') - self.assertEqual(node.uuid, response['nodes'][0]["uuid"]) - self._assert_node_fields(response['nodes'][0]) - - def test_get_one(self): - node = obj_utils.create_test_node(self.context) - response = self.get_json('/nodes/%s' % node['uuid']) - self.assertEqual(node.uuid, response['uuid']) - self._assert_node_fields(response) - - def test_get_all_with_pagination_marker(self): - node_list = [] - for id_ in range(4): - node = obj_utils.create_test_node(self.context, id=id_, - uuid=utils.generate_uuid()) - node_list.append(node.uuid) - - response = self.get_json('/nodes?limit=3&marker=%s' % node_list[2]) - self.assertEqual(1, len(response['nodes'])) - self.assertEqual(node_list[-1], response['nodes'][0]['uuid']) - - def test_detail(self): - node = obj_utils.create_test_node(self.context) - response = self.get_json('/nodes/detail') - self.assertEqual(node.uuid, response['nodes'][0]["uuid"]) - self._assert_node_fields(response['nodes'][0]) - - def test_detail_with_pagination_marker(self): - node_list = [] - for id_ in range(4): - node = obj_utils.create_test_node(self.context, id=id_, - uuid=utils.generate_uuid()) - node_list.append(node.uuid) - - response = self.get_json('/nodes/detail?limit=3&marker=%s' - % node_list[2]) - self.assertEqual(1, len(response['nodes'])) - self.assertEqual(node_list[-1], response['nodes'][0]['uuid']) - self._assert_node_fields(response['nodes'][0]) - - def test_detail_against_single(self): - node = obj_utils.create_test_node(self.context) - response = self.get_json('/nodes/%s/detail' % node['uuid'], - expect_errors=True) - self.assertEqual(404, response.status_int) - - def test_many(self): - node_list = [] - for id_ in range(5): - node = obj_utils.create_test_node(self.context, id=id_, - uuid=utils.generate_uuid()) - node_list.append(node.uuid) - response = self.get_json('/nodes') - self.assertEqual(len(node_list), len(response['nodes'])) - uuids = [s['uuid'] for s in response['nodes']] - self.assertEqual(sorted(node_list), sorted(uuids)) - - def test_links(self): - uuid = utils.generate_uuid() - obj_utils.create_test_node(self.context, id=1, uuid=uuid) - response = self.get_json('/nodes/%s' % uuid) - self.assertIn('links', response.keys()) - self.assertEqual(2, len(response['links'])) - self.assertIn(uuid, response['links'][0]['href']) - for l in response['links']: - bookmark = l['rel'] == 'bookmark' - self.assertTrue(self.validate_link(l['href'], bookmark=bookmark)) - - def test_collection_links(self): - for id_ in range(5): - obj_utils.create_test_node(self.context, id=id_, - uuid=utils.generate_uuid()) - response = self.get_json('/nodes/?limit=3') - self.assertEqual(3, len(response['nodes'])) - - next_marker = response['nodes'][-1]['uuid'] - self.assertIn(next_marker, response['next']) - - def test_collection_links_default_limit(self): - cfg.CONF.set_override('max_limit', 3, 'api') - for id_ in range(5): - obj_utils.create_test_node(self.context, id=id_, - uuid=utils.generate_uuid()) - response = self.get_json('/nodes') - self.assertEqual(3, len(response['nodes'])) - - next_marker = response['nodes'][-1]['uuid'] - self.assertIn(next_marker, response['next']) - - -class TestPatch(api_base.FunctionalTest): - - def setUp(self): - super(TestPatch, self).setUp() - self.node = obj_utils.create_test_node(self.context, image_id='Fedora') - - @mock.patch('oslo_utils.timeutils.utcnow') - def test_replace_ok(self, mock_utcnow): - test_time = datetime.datetime(2000, 1, 1, 0, 0) - mock_utcnow.return_value = test_time - - new_image = 'Ubuntu' - response = self.get_json('/nodes/%s' % self.node.uuid) - self.assertNotEqual(new_image, response['image_id']) - - response = self.patch_json('/nodes/%s' % self.node.uuid, - [{'path': '/image_id', 'value': new_image, - 'op': 'replace'}]) - self.assertEqual('application/json', response.content_type) - self.assertEqual(200, response.status_code) - - response = self.get_json('/nodes/%s' % self.node.uuid) - self.assertEqual(new_image, response['image_id']) - return_updated_at = timeutils.parse_isotime( - response['updated_at']).replace(tzinfo=None) - self.assertEqual(test_time, return_updated_at) - - def test_replace_non_existent_node(self): - response = self.patch_json('/nodes/%s' % utils.generate_uuid(), - [{'path': '/image_id', 'value': 'Ubuntu', - 'op': 'replace'}], - expect_errors=True) - self.assertEqual(404, response.status_int) - self.assertEqual('application/json', response.content_type) - self.assertTrue(response.json['error_message']) - - def test_add_non_existent_property(self): - response = self.patch_json( - '/nodes/%s' % self.node.uuid, - [{'path': '/foo', 'value': 'bar', 'op': 'add'}], - expect_errors=True) - self.assertEqual('application/json', response.content_type) - self.assertEqual(400, response.status_int) - self.assertTrue(response.json['error_message']) - - def test_remove_ok(self): - response = self.get_json('/nodes/%s' % self.node.uuid) - self.assertIsNotNone(response['image_id']) - - response = self.patch_json('/nodes/%s' % self.node.uuid, - [{'path': '/image_id', 'op': 'remove'}]) - self.assertEqual('application/json', response.content_type) - self.assertEqual(200, response.status_code) - - response = self.get_json('/nodes/%s' % self.node.uuid) - self.assertIsNone(response['image_id']) - - def test_remove_uuid(self): - response = self.patch_json('/nodes/%s' % self.node.uuid, - [{'path': '/uuid', 'op': 'remove'}], - expect_errors=True) - self.assertEqual(400, response.status_int) - self.assertEqual('application/json', response.content_type) - self.assertTrue(response.json['error_message']) - - def test_remove_non_existent_property(self): - response = self.patch_json( - '/nodes/%s' % self.node.uuid, - [{'path': '/non-existent', 'op': 'remove'}], - expect_errors=True) - self.assertEqual(400, response.status_code) - self.assertEqual('application/json', response.content_type) - self.assertTrue(response.json['error_message']) - - -class TestPost(api_base.FunctionalTest): - - @mock.patch('oslo_utils.timeutils.utcnow') - def test_create_node(self, mock_utcnow): - node_dict = apiutils.node_post_data() - test_time = datetime.datetime(2000, 1, 1, 0, 0) - mock_utcnow.return_value = test_time - - response = self.post_json('/nodes', node_dict) - self.assertEqual('application/json', response.content_type) - self.assertEqual(201, response.status_int) - # Check location header - self.assertIsNotNone(response.location) - expected_location = '/v1/nodes/%s' % node_dict['uuid'] - self.assertEqual(expected_location, - urlparse.urlparse(response.location).path) - self.assertEqual(node_dict['uuid'], response.json['uuid']) - self.assertNotIn('updated_at', response.json.keys) - return_created_at = timeutils.parse_isotime( - response.json['created_at']).replace(tzinfo=None) - self.assertEqual(test_time, return_created_at) - - def test_create_node_set_project_id_and_user_id(self): - with mock.patch.object(self.dbapi, 'create_node', - wraps=self.dbapi.create_node) as cc_mock: - node_dict = apiutils.node_post_data() - self.post_json('/nodes', node_dict) - cc_mock.assert_called_once_with(mock.ANY) - self.assertEqual(self.context.project_id, - cc_mock.call_args[0][0]['project_id']) - self.assertEqual(self.context.user_id, - cc_mock.call_args[0][0]['user_id']) - - def test_create_node_doesnt_contain_id(self): - with mock.patch.object(self.dbapi, 'create_node', - wraps=self.dbapi.create_node) as cn_mock: - node_dict = apiutils.node_post_data(image_id='Ubuntu') - response = self.post_json('/nodes', node_dict) - self.assertEqual(node_dict['image_id'], response.json['image_id']) - cn_mock.assert_called_once_with(mock.ANY) - # Check that 'id' is not in first arg of positional args - self.assertNotIn('id', cn_mock.call_args[0][0]) - - def test_create_node_generate_uuid(self): - node_dict = apiutils.node_post_data() - del node_dict['uuid'] - - response = self.post_json('/nodes', node_dict) - self.assertEqual('application/json', response.content_type) - self.assertEqual(201, response.status_int) - self.assertEqual(node_dict['image_id'], - response.json['image_id']) - self.assertTrue(utils.is_uuid_like(response.json['uuid'])) - - -class TestDelete(api_base.FunctionalTest): - - def setUp(self): - super(TestDelete, self).setUp() - self.node = obj_utils.create_test_node(self.context, image_id='Fedora') - - def test_delete_node(self): - self.delete('/nodes/%s' % self.node.uuid) - response = self.get_json('/nodes/%s' % self.node.uuid, - expect_errors=True) - self.assertEqual(404, response.status_int) - self.assertEqual('application/json', response.content_type) - self.assertTrue(response.json['error_message']) - - def test_delete_node_not_found(self): - uuid = utils.generate_uuid() - response = self.delete('/nodes/%s' % uuid, expect_errors=True) - self.assertEqual(404, response.status_int) - self.assertEqual('application/json', response.content_type) - self.assertTrue(response.json['error_message']) - - -class TestNodePolicyEnforcement(api_base.FunctionalTest): - - def _common_policy_check(self, rule, func, *arg, **kwarg): - self.policy.set_rules({rule: "project:non_fake"}) - response = func(*arg, **kwarg) - self.assertEqual(403, response.status_int) - self.assertEqual('application/json', response.content_type) - self.assertTrue( - "Policy doesn't allow %s to be performed." % rule, - json.loads(response.json['error_message'])['faultstring']) - - def test_policy_disallow_get_all(self): - self._common_policy_check( - "node:get_all", self.get_json, '/nodes', expect_errors=True) - - def test_policy_disallow_get_one(self): - self._common_policy_check( - "node:get", self.get_json, - '/nodes/%s' % utils.generate_uuid(), - expect_errors=True) - - def test_policy_disallow_detail(self): - self._common_policy_check( - "node:detail", self.get_json, - '/nodes/%s/detail' % utils.generate_uuid(), - expect_errors=True) - - def test_policy_disallow_update(self): - node = obj_utils.create_test_node(self.context, - type='type_A', - uuid=utils.generate_uuid()) - self._common_policy_check( - "node:update", self.patch_json, - '/nodes/%s' % node.uuid, - [{'path': '/type', 'value': "new_type", 'op': 'replace'}], - expect_errors=True) - - def test_policy_disallow_create(self): - bdict = apiutils.node_post_data(name='node_example_A') - self._common_policy_check( - "node:create", self.post_json, '/nodes', bdict, expect_errors=True) - - def test_policy_disallow_delete(self): - node = obj_utils.create_test_node(self.context, - uuid=utils.generate_uuid()) - self._common_policy_check( - "node:delete", self.delete, - '/nodes/%s' % node.uuid, expect_errors=True) diff --git a/magnum/tests/unit/api/utils.py b/magnum/tests/unit/api/utils.py index 87c62e7f3e..b65302ffd7 100644 --- a/magnum/tests/unit/api/utils.py +++ b/magnum/tests/unit/api/utils.py @@ -17,7 +17,6 @@ import pytz from magnum.api.controllers.v1 import bay as bay_controller from magnum.api.controllers.v1 import baymodel as baymodel_controller -from magnum.api.controllers.v1 import node as node_controller from magnum.api.controllers.v1 import pod as pod_controller from magnum.api.controllers.v1 import replicationcontroller as rc_controller from magnum.api.controllers.v1 import service as service_controller @@ -138,12 +137,6 @@ def rc_post_data(**kw): return remove_internal(rc, internal) -def node_post_data(**kw): - node = utils.get_test_node(**kw) - internal = node_controller.NodePatchType.internal_attrs() - return remove_internal(node, internal) - - def x509keypair_post_data(**kw): x509keypair = utils.get_test_x509keypair(**kw) internal = x509keypair_controller.X509KeyPairPatchType.internal_attrs() diff --git a/magnum/tests/unit/db/test_node.py b/magnum/tests/unit/db/test_node.py deleted file mode 100644 index 77df9a1a0d..0000000000 --- a/magnum/tests/unit/db/test_node.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright 2015 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Tests for manipulating Nodes via the DB API""" - -import six - -from magnum.common import exception -from magnum.common import utils as magnum_utils -from magnum.tests.unit.db import base -from magnum.tests.unit.db import utils - - -class DbNodeTestCase(base.DbTestCase): - - def test_create_node(self): - utils.create_test_node() - - def test_create_node_already_exists(self): - utils.create_test_node() - self.assertRaises(exception.NodeAlreadyExists, - utils.create_test_node) - - def test_create_node_instance_already_associated(self): - instance_uuid = magnum_utils.generate_uuid() - utils.create_test_node(uuid=magnum_utils.generate_uuid(), - ironic_node_id=instance_uuid) - self.assertRaises(exception.InstanceAssociated, - utils.create_test_node, - uuid=magnum_utils.generate_uuid(), - ironic_node_id=instance_uuid) - - def test_get_node_by_id(self): - node = utils.create_test_node() - res = self.dbapi.get_node_by_id(self.context, node.id) - self.assertEqual(node.id, res.id) - self.assertEqual(node.uuid, res.uuid) - - def test_get_node_by_uuid(self): - node = utils.create_test_node() - res = self.dbapi.get_node_by_uuid(self.context, node.uuid) - self.assertEqual(node.id, res.id) - self.assertEqual(node.uuid, res.uuid) - - def test_get_node_that_does_not_exist(self): - self.assertRaises(exception.NodeNotFound, - self.dbapi.get_node_by_id, self.context, 99) - self.assertRaises(exception.NodeNotFound, - self.dbapi.get_node_by_uuid, - self.context, - magnum_utils.generate_uuid()) - - def test_get_node_list(self): - uuids = [] - for i in range(1, 6): - node = utils.create_test_node(uuid=magnum_utils.generate_uuid()) - uuids.append(six.text_type(node['uuid'])) - res = self.dbapi.get_node_list(self.context) - res_uuids = [r.uuid for r in res] - self.assertEqual(sorted(uuids), sorted(res_uuids)) - - def test_get_node_list_sorted(self): - uuids = [] - for _ in range(5): - node = utils.create_test_node(uuid=magnum_utils.generate_uuid()) - uuids.append(six.text_type(node.uuid)) - res = self.dbapi.get_node_list(self.context, sort_key='uuid') - res_uuids = [r.uuid for r in res] - self.assertEqual(sorted(uuids), res_uuids) - - self.assertRaises(exception.InvalidParameterValue, - self.dbapi.get_node_list, - self.context, - sort_key='foo') - - def test_get_node_list_with_filters(self): - node1 = utils.create_test_node( - type='virt', - ironic_node_id=magnum_utils.generate_uuid(), - uuid=magnum_utils.generate_uuid()) - node2 = utils.create_test_node( - type='bare', - uuid=magnum_utils.generate_uuid()) - - res = self.dbapi.get_node_list(self.context, filters={'type': 'virt'}) - self.assertEqual([node1.id], [r.id for r in res]) - - res = self.dbapi.get_node_list(self.context, - filters={'type': 'bad-type'}) - self.assertEqual([], [r.id for r in res]) - - res = self.dbapi.get_node_list(self.context, - filters={'associated': True}) - self.assertEqual([node1.id], [r.id for r in res]) - - res = self.dbapi.get_node_list(self.context, - filters={'associated': False}) - self.assertEqual([node2.id], [r.id for r in res]) - - def test_destroy_node(self): - node = utils.create_test_node() - self.dbapi.destroy_node(node.id) - self.assertRaises(exception.NodeNotFound, - self.dbapi.get_node_by_id, self.context, node.id) - - def test_destroy_node_by_uuid(self): - node = utils.create_test_node() - self.dbapi.destroy_node(node.uuid) - self.assertRaises(exception.NodeNotFound, - self.dbapi.get_node_by_uuid, - self.context, node.uuid) - - def test_destroy_node_that_does_not_exist(self): - self.assertRaises(exception.NodeNotFound, - self.dbapi.destroy_node, - magnum_utils.generate_uuid()) - - def test_update_node(self): - node = utils.create_test_node() - old_image = node.image_id - new_image = 'new-image' - self.assertNotEqual(old_image, new_image) - - res = self.dbapi.update_node(node.id, {'image_id': new_image}) - self.assertEqual(new_image, res.image_id) - - def test_update_node_not_found(self): - node_uuid = magnum_utils.generate_uuid() - new_image = 'new-image' - self.assertRaises(exception.NodeNotFound, self.dbapi.update_node, - node_uuid, {'image_id': new_image}) - - def test_update_node_uuid(self): - node = utils.create_test_node() - self.assertRaises(exception.InvalidParameterValue, - self.dbapi.update_node, node.id, - {'uuid': ''}) - - def test_update_node_associate_and_disassociate(self): - node = utils.create_test_node() - new_i_uuid = magnum_utils.generate_uuid() - res = self.dbapi.update_node(node.id, {'ironic_node_id': new_i_uuid}) - self.assertEqual(new_i_uuid, res.ironic_node_id) - res = self.dbapi.update_node(node.id, {'ironic_node_id': None}) - self.assertIsNone(res.ironic_node_id) - - def test_update_node_already_associated(self): - node = utils.create_test_node() - new_i_uuid_one = magnum_utils.generate_uuid() - self.dbapi.update_node(node.id, {'ironic_node_id': new_i_uuid_one}) - new_i_uuid_two = magnum_utils.generate_uuid() - self.assertRaises(exception.NodeAssociated, - self.dbapi.update_node, node.id, - {'ironic_node_id': new_i_uuid_two}) - - def test_update_node_instance_already_associated(self): - node1 = utils.create_test_node(uuid=magnum_utils.generate_uuid()) - new_i_uuid = magnum_utils.generate_uuid() - self.dbapi.update_node(node1.id, {'ironic_node_id': new_i_uuid}) - node2 = utils.create_test_node(uuid=magnum_utils.generate_uuid()) - self.assertRaises(exception.InstanceAssociated, - self.dbapi.update_node, node2.id, - {'ironic_node_id': new_i_uuid}) diff --git a/magnum/tests/unit/db/utils.py b/magnum/tests/unit/db/utils.py index 2e9453dd6e..5dd6537694 100644 --- a/magnum/tests/unit/db/utils.py +++ b/magnum/tests/unit/db/utils.py @@ -183,35 +183,6 @@ def create_test_service(**kw): return dbapi.create_service(service) -def get_test_node(**kw): - return { - 'id': kw.get('id', 42), - 'uuid': kw.get('uuid', 'ea8e2a25-2901-438d-8157-de7ffd68d051'), - 'type': kw.get('type', 'virt'), - 'project_id': kw.get('project_id', 'fake_project'), - 'user_id': kw.get('user_id', 'fake_user'), - 'image_id': kw.get('image_id', 'ubuntu'), - 'ironic_node_id': kw.get('ironic_node_id'), - 'created_at': kw.get('created_at'), - 'updated_at': kw.get('updated_at'), - } - - -def create_test_node(**kw): - """Create test node entry in DB and return Node DB object. - - Function to be used to create test Node objects in the database. - :param kw: kwargs with overriding values for node's attributes. - :returns: Test Node DB object. - """ - node = get_test_node(**kw) - # Let DB generate ID if it isn't specified explicitly - if 'id' not in kw: - del node['id'] - dbapi = db_api.get_instance() - return dbapi.create_node(node) - - def get_test_container(**kw): return { 'id': kw.get('id', 42), diff --git a/magnum/tests/unit/objects/test_node.py b/magnum/tests/unit/objects/test_node.py deleted file mode 100644 index 099dcc654a..0000000000 --- a/magnum/tests/unit/objects/test_node.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright 2015 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -from testtools.matchers import HasLength - -from magnum.common import utils as magnum_utils -from magnum import objects -from magnum.tests.unit.db import base -from magnum.tests.unit.db import utils - - -class TestNodeObject(base.DbTestCase): - - def setUp(self): - super(TestNodeObject, self).setUp() - self.fake_node = utils.get_test_node() - - def test_get_by_id(self): - node_id = self.fake_node['id'] - with mock.patch.object(self.dbapi, 'get_node_by_id', - autospec=True) as mock_get_node: - mock_get_node.return_value = self.fake_node - node = objects.Node.get_by_id(self.context, node_id) - mock_get_node.assert_called_once_with(self.context, node_id) - self.assertEqual(self.context, node._context) - - def test_get_by_uuid(self): - uuid = self.fake_node['uuid'] - with mock.patch.object(self.dbapi, 'get_node_by_uuid', - autospec=True) as mock_get_node: - mock_get_node.return_value = self.fake_node - node = objects.Node.get_by_uuid(self.context, uuid) - mock_get_node.assert_called_once_with(self.context, uuid) - self.assertEqual(self.context, node._context) - - def test_list(self): - with mock.patch.object(self.dbapi, 'get_node_list', - autospec=True) as mock_get_list: - mock_get_list.return_value = [self.fake_node] - nodes = objects.Node.list(self.context) - self.assertEqual(1, mock_get_list.call_count) - self.assertThat(nodes, HasLength(1)) - self.assertIsInstance(nodes[0], objects.Node) - self.assertEqual(self.context, nodes[0]._context) - - def test_create(self): - with mock.patch.object(self.dbapi, 'create_node', - autospec=True) as mock_create_node: - mock_create_node.return_value = self.fake_node - node = objects.Node(self.context, **self.fake_node) - node.create() - mock_create_node.assert_called_once_with(self.fake_node) - self.assertEqual(self.context, node._context) - - def test_destroy(self): - uuid = self.fake_node['uuid'] - with mock.patch.object(self.dbapi, 'get_node_by_uuid', - autospec=True) as mock_get_node: - mock_get_node.return_value = self.fake_node - with mock.patch.object(self.dbapi, 'destroy_node', - autospec=True) as mock_destroy_node: - node = objects.Node.get_by_uuid(self.context, uuid) - node.destroy() - mock_get_node.assert_called_once_with(self.context, uuid) - mock_destroy_node.assert_called_once_with(uuid) - self.assertEqual(self.context, node._context) - - def test_save(self): - uuid = self.fake_node['uuid'] - with mock.patch.object(self.dbapi, 'get_node_by_uuid', - autospec=True) as mock_get_node: - mock_get_node.return_value = self.fake_node - with mock.patch.object(self.dbapi, 'update_node', - autospec=True) as mock_update_node: - node = objects.Node.get_by_uuid(self.context, uuid) - node.type = 'bare' - node.save() - - mock_get_node.assert_called_once_with(self.context, uuid) - mock_update_node.assert_called_once_with( - uuid, {'type': 'bare'}) - self.assertEqual(self.context, node._context) - - def test_refresh(self): - uuid = self.fake_node['uuid'] - new_uuid = magnum_utils.generate_uuid() - returns = [dict(self.fake_node, uuid=uuid), - dict(self.fake_node, uuid=new_uuid)] - expected = [mock.call(self.context, uuid), - mock.call(self.context, uuid)] - with mock.patch.object(self.dbapi, 'get_node_by_uuid', - side_effect=returns, - autospec=True) as mock_get_node: - node = objects.Node.get_by_uuid(self.context, uuid) - self.assertEqual(uuid, node.uuid) - node.refresh() - self.assertEqual(new_uuid, node.uuid) - self.assertEqual(expected, mock_get_node.call_args_list) - self.assertEqual(self.context, node._context) diff --git a/magnum/tests/unit/objects/test_objects.py b/magnum/tests/unit/objects/test_objects.py index d3db2dd7b6..68e23d4339 100644 --- a/magnum/tests/unit/objects/test_objects.py +++ b/magnum/tests/unit/objects/test_objects.py @@ -428,7 +428,6 @@ object_data = { 'Certificate': '1.0-2aff667971b85c1edf8d15684fd7d5e2', 'Container': '1.3-e2d9d2e8a8844d421148cd9fde6c6bd6', 'MyObj': '1.0-b43567e512438205e32f4e95ca616697', - 'Node': '1.0-30943e6e3387a2fae7490b57c4239a17', 'Pod': '1.1-39f221ad1dad0eb7f7bee3569d42fa7e', 'ReplicationController': '1.0-a471c2429c212ed91833cfcf0f934eab', 'Service': '1.0-f4a1c5a4618708824a553568c1ada0ea', diff --git a/magnum/tests/unit/objects/utils.py b/magnum/tests/unit/objects/utils.py index 610ae09b5b..e5d973ca28 100644 --- a/magnum/tests/unit/objects/utils.py +++ b/magnum/tests/unit/objects/utils.py @@ -160,33 +160,6 @@ def create_test_rc(context, **kw): return rc -def get_test_node(context, **kw): - """Return a Node object with appropriate attributes. - - NOTE: The object leaves the attributes marked as changed, such - that a create() could be used to commit it to the DB. - """ - db_node = db_utils.get_test_node(**kw) - # Let DB generate ID if it isn't specified explicitly - if 'id' not in kw: - del db_node['id'] - node = objects.Node(context) - for key in db_node: - setattr(node, key, db_node[key]) - return node - - -def create_test_node(context, **kw): - """Create and return a test Node object. - - Create a node in the DB and return a Node object with appropriate - attributes. - """ - node = get_test_node(context, **kw) - node.create() - return node - - def get_test_x509keypair(context, **kw): """Return a X509KeyPair object with appropriate attributes.