From 342e83f033e141db1d4eea323416af695ae0ea0e Mon Sep 17 00:00:00 2001 From: Hua Wang Date: Tue, 2 Feb 2016 16:39:31 +0800 Subject: [PATCH] Remove node object from Magnum The node object represents either a bare metal or virtual machine node that is provisioned with an OS to run the containers, or alternatively, run kubernetes. Magnum use Heat to deploy the nodes, so it is unnecessary to maintain node object in Magnum. Heat can do the work for us. The code about node object is useless now, so let's remove it from Magnum. Closes-Bug: #1540790 Change-Id: If8761b06a364127683099afb4dc51ea551be6f89 --- doc/source/index.rst | 1 - doc/source/objects.rst | 1 - etc/magnum/policy.json | 7 - magnum/api/controllers/v1/__init__.py | 2 - magnum/api/controllers/v1/node.py | 255 ------------- magnum/common/exception.py | 17 - magnum/db/api.py | 72 ---- .../bb42b7cad130_remove_node_object.py | 29 ++ magnum/db/sqlalchemy/api.py | 105 ------ magnum/db/sqlalchemy/models.py | 19 - magnum/objects/__init__.py | 3 - magnum/objects/node.py | 161 --------- magnum/tests/fake_policy.py | 7 - .../k8s/test_magnum_python_client.py | 3 - .../unit/api/controllers/v1/test_node.py | 341 ------------------ magnum/tests/unit/api/utils.py | 7 - magnum/tests/unit/db/test_node.py | 175 --------- magnum/tests/unit/db/utils.py | 29 -- magnum/tests/unit/objects/test_node.py | 112 ------ magnum/tests/unit/objects/test_objects.py | 1 - magnum/tests/unit/objects/utils.py | 27 -- 21 files changed, 29 insertions(+), 1345 deletions(-) delete mode 100644 magnum/api/controllers/v1/node.py create mode 100644 magnum/db/sqlalchemy/alembic/versions/bb42b7cad130_remove_node_object.py delete mode 100644 magnum/objects/node.py delete mode 100644 magnum/tests/unit/api/controllers/v1/test_node.py delete mode 100644 magnum/tests/unit/db/test_node.py delete mode 100644 magnum/tests/unit/objects/test_node.py 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.