Add compute node to DB and objects
Change-Id: Ic196736ff263220eb83acce72772f0ebcc462b9f
This commit is contained in:
parent
8053efa4cf
commit
7255e44328
@ -341,6 +341,10 @@ class ContainerNotFound(HTTPNotFound):
|
|||||||
message = _("Container %(container)s could not be found.")
|
message = _("Container %(container)s could not be found.")
|
||||||
|
|
||||||
|
|
||||||
|
class ComputeNodeNotFound(HTTPNotFound):
|
||||||
|
message = _("Compute node %(compute_node)s could not be found.")
|
||||||
|
|
||||||
|
|
||||||
class ImageNotFound(HTTPNotFound):
|
class ImageNotFound(HTTPNotFound):
|
||||||
message = _("Image %(image)s could not be found.")
|
message = _("Image %(image)s could not be found.")
|
||||||
|
|
||||||
@ -369,6 +373,10 @@ class ContainerAlreadyExists(ResourceExists):
|
|||||||
message = _("A container with %(field)s %(value)s already exists.")
|
message = _("A container with %(field)s %(value)s already exists.")
|
||||||
|
|
||||||
|
|
||||||
|
class ComputeNodeAlreadyExists(ResourceExists):
|
||||||
|
message = _("A compute node with %(field)s %(value)s already exists.")
|
||||||
|
|
||||||
|
|
||||||
class ImageAlreadyExists(ResourceExists):
|
class ImageAlreadyExists(ResourceExists):
|
||||||
message = _("An image with tag %(tag)s and repo %(repo)s already exists.")
|
message = _("An image with tag %(tag)s and repo %(repo)s already exists.")
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ def list_containers(context, filters=None, limit=None, marker=None,
|
|||||||
:param marker: the last item of the previous page; we return the next
|
:param marker: the last item of the previous page; we return the next
|
||||||
result set.
|
result set.
|
||||||
:param sort_key: Attribute by which results should be sorted.
|
:param sort_key: Attribute by which results should be sorted.
|
||||||
|
:param sort_dir: Direction in which results should be sorted.
|
||||||
(asc, desc)
|
(asc, desc)
|
||||||
:returns: A list of tuples of the specified columns.
|
:returns: A list of tuples of the specified columns.
|
||||||
"""
|
"""
|
||||||
@ -65,6 +66,7 @@ def list_containers(context, filters=None, limit=None, marker=None,
|
|||||||
def create_container(context, values):
|
def create_container(context, values):
|
||||||
"""Create a new container.
|
"""Create a new container.
|
||||||
|
|
||||||
|
:param context: The security context
|
||||||
:param values: A dict containing several items used to identify
|
:param values: A dict containing several items used to identify
|
||||||
and track the container, and several dicts which are
|
and track the container, and several dicts which are
|
||||||
passed
|
passed
|
||||||
@ -180,7 +182,7 @@ def list_zun_services(context, filters=None, limit=None,
|
|||||||
:param marker: the last item of the previous page; we return the next
|
:param marker: the last item of the previous page; we return the next
|
||||||
result set.
|
result set.
|
||||||
:param sort_key: Attribute by which results should be sorted.
|
:param sort_key: Attribute by which results should be sorted.
|
||||||
:param sort_dir: direction in which results should be sorted.
|
:param sort_dir: Direction in which results should be sorted.
|
||||||
(asc, desc)
|
(asc, desc)
|
||||||
:returns: A list of tuples of the specified columns.
|
:returns: A list of tuples of the specified columns.
|
||||||
"""
|
"""
|
||||||
@ -242,6 +244,7 @@ def list_images(context, filters=None,
|
|||||||
:param marker: the last item of the previous page; we
|
:param marker: the last item of the previous page; we
|
||||||
return the next
|
return the next
|
||||||
:param sort_key: Attribute by which results should be sorted.
|
:param sort_key: Attribute by which results should be sorted.
|
||||||
|
:param sort_dir: Direction in which results should be sorted.
|
||||||
(asc, desc)
|
(asc, desc)
|
||||||
:returns: A list of tuples of the specified columns.
|
:returns: A list of tuples of the specified columns.
|
||||||
"""
|
"""
|
||||||
@ -291,6 +294,7 @@ def list_resource_providers(context, filters=None, limit=None, marker=None,
|
|||||||
def create_resource_provider(context, values):
|
def create_resource_provider(context, values):
|
||||||
"""Create a new resource provider.
|
"""Create a new resource provider.
|
||||||
|
|
||||||
|
:param context: The security context
|
||||||
:param values: A dict containing several items used to identify and
|
:param values: A dict containing several items used to identify and
|
||||||
track the resource provider, and several dicts which are
|
track the resource provider, and several dicts which are
|
||||||
passed into the Drivers when managing this resource
|
passed into the Drivers when managing this resource
|
||||||
@ -344,6 +348,7 @@ def list_resource_classes(context, limit=None, marker=None, sort_key=None,
|
|||||||
:param marker: the last item of the previous page; we
|
:param marker: the last item of the previous page; we
|
||||||
return the next
|
return the next
|
||||||
:param sort_key: Attribute by which results should be sorted.
|
:param sort_key: Attribute by which results should be sorted.
|
||||||
|
:param sort_dir: Direction in which results should be sorted.
|
||||||
(asc, desc)
|
(asc, desc)
|
||||||
:returns: A list of tuples of the specified columns.
|
:returns: A list of tuples of the specified columns.
|
||||||
"""
|
"""
|
||||||
@ -354,6 +359,7 @@ def list_resource_classes(context, limit=None, marker=None, sort_key=None,
|
|||||||
def create_resource_class(context, values):
|
def create_resource_class(context, values):
|
||||||
"""Create a new resource class.
|
"""Create a new resource class.
|
||||||
|
|
||||||
|
:param context: The security context
|
||||||
:param values: A dict containing several items used to identify
|
:param values: A dict containing several items used to identify
|
||||||
and track the resource class, and several dicts which are
|
and track the resource class, and several dicts which are
|
||||||
passed into the Drivers when managing this resource class.
|
passed into the Drivers when managing this resource class.
|
||||||
@ -408,6 +414,7 @@ def list_inventories(context, filters=None, limit=None, marker=None,
|
|||||||
:param marker: the last item of the previous page; we return the next
|
:param marker: the last item of the previous page; we return the next
|
||||||
result set.
|
result set.
|
||||||
:param sort_key: Attribute by which results should be sorted.
|
:param sort_key: Attribute by which results should be sorted.
|
||||||
|
:param sort_dir: Direction in which results should be sorted.
|
||||||
(asc, desc)
|
(asc, desc)
|
||||||
:returns: A list of tuples of the specified columns.
|
:returns: A list of tuples of the specified columns.
|
||||||
"""
|
"""
|
||||||
@ -418,10 +425,11 @@ def list_inventories(context, filters=None, limit=None, marker=None,
|
|||||||
def create_inventory(context, provider_id, values):
|
def create_inventory(context, provider_id, values):
|
||||||
"""Create a new inventory.
|
"""Create a new inventory.
|
||||||
|
|
||||||
|
:param context: The security context
|
||||||
|
:param provider_id: The id of a resource provider.
|
||||||
:param values: A dict containing several items used to identify
|
:param values: A dict containing several items used to identify
|
||||||
and track the inventory, and several dicts which are
|
and track the inventory, and several dicts which are
|
||||||
passed into the Drivers when managing this inventory.
|
passed into the Drivers when managing this inventory.
|
||||||
:param provider_id: The id of a resource provider.
|
|
||||||
:returns: An inventory.
|
:returns: An inventory.
|
||||||
"""
|
"""
|
||||||
return _get_dbdriver_instance().create_inventory(
|
return _get_dbdriver_instance().create_inventory(
|
||||||
@ -473,6 +481,7 @@ def list_allocations(context, filters=None, limit=None, marker=None,
|
|||||||
:param marker: the last item of the previous page; we return the next
|
:param marker: the last item of the previous page; we return the next
|
||||||
result set.
|
result set.
|
||||||
:param sort_key: Attribute by which results should be sorted.
|
:param sort_key: Attribute by which results should be sorted.
|
||||||
|
:param sort_dir: Direction in which results should be sorted.
|
||||||
(asc, desc)
|
(asc, desc)
|
||||||
:returns: A list of tuples of the specified columns.
|
:returns: A list of tuples of the specified columns.
|
||||||
"""
|
"""
|
||||||
@ -483,10 +492,10 @@ def list_allocations(context, filters=None, limit=None, marker=None,
|
|||||||
def create_allocation(context, values):
|
def create_allocation(context, values):
|
||||||
"""Create a new allocation.
|
"""Create a new allocation.
|
||||||
|
|
||||||
|
:param context: The security context
|
||||||
:param values: A dict containing several items used to identify
|
:param values: A dict containing several items used to identify
|
||||||
and track the allocation, and several dicts which are
|
and track the allocation, and several dicts which are
|
||||||
passed into the Drivers when managing this allocation.
|
passed into the Drivers when managing this allocation.
|
||||||
:param provider_id: The id of a resource provider.
|
|
||||||
:returns: An allocation.
|
:returns: An allocation.
|
||||||
"""
|
"""
|
||||||
return _get_dbdriver_instance().create_allocation(context, values)
|
return _get_dbdriver_instance().create_allocation(context, values)
|
||||||
@ -522,3 +531,78 @@ def update_allocation(context, allocation_id, values):
|
|||||||
"""
|
"""
|
||||||
return _get_dbdriver_instance().update_allocation(
|
return _get_dbdriver_instance().update_allocation(
|
||||||
context, allocation_id, values)
|
context, allocation_id, values)
|
||||||
|
|
||||||
|
|
||||||
|
def list_compute_nodes(context, filters=None, limit=None, marker=None,
|
||||||
|
sort_key=None, sort_dir=None):
|
||||||
|
"""List matching compute nodes.
|
||||||
|
|
||||||
|
Return a list of the specified columns for all compute nodes that match
|
||||||
|
the specified filters.
|
||||||
|
:param context: The security context
|
||||||
|
:param filters: Filters to apply. Defaults to None.
|
||||||
|
:param limit: Maximum number of compute 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.
|
||||||
|
"""
|
||||||
|
return _get_dbdriver_instance().list_compute_nodes(
|
||||||
|
context, filters, limit, marker, sort_key, sort_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def create_compute_node(context, values):
|
||||||
|
"""Create a new compute node.
|
||||||
|
|
||||||
|
:param context: The security context
|
||||||
|
:param values: A dict containing several items used to identify
|
||||||
|
and track the compute node, and several dicts which are
|
||||||
|
passed into the Drivers when managing this compute node.
|
||||||
|
:returns: A compute node.
|
||||||
|
"""
|
||||||
|
return _get_dbdriver_instance().create_compute_node(context, values)
|
||||||
|
|
||||||
|
|
||||||
|
def get_compute_node(context, node_uuid):
|
||||||
|
"""Return a compute node.
|
||||||
|
|
||||||
|
:param context: The security context
|
||||||
|
:param node_uuid: The uuid of a compute node.
|
||||||
|
:returns: A compute node.
|
||||||
|
"""
|
||||||
|
return _get_dbdriver_instance().get_compute_node(context, node_uuid)
|
||||||
|
|
||||||
|
|
||||||
|
def get_compute_node_by_hostname(context, hostname):
|
||||||
|
"""Return a compute node.
|
||||||
|
|
||||||
|
:param context: The security context
|
||||||
|
:param hostname: The hostname of a compute node.
|
||||||
|
:returns: A compute node.
|
||||||
|
"""
|
||||||
|
return _get_dbdriver_instance().get_compute_node_by_hostname(
|
||||||
|
context, hostname)
|
||||||
|
|
||||||
|
|
||||||
|
def destroy_compute_node(context, node_uuid):
|
||||||
|
"""Destroy a compute node and all associated interfaces.
|
||||||
|
|
||||||
|
:param context: Request context
|
||||||
|
:param node_uuid: The uuid of a compute node.
|
||||||
|
"""
|
||||||
|
return _get_dbdriver_instance().destroy_compute_node(context, node_uuid)
|
||||||
|
|
||||||
|
|
||||||
|
def update_compute_node(context, node_uuid, values):
|
||||||
|
"""Update properties of a compute node.
|
||||||
|
|
||||||
|
:context: Request context
|
||||||
|
:param node_uuid: The uuid of a compute node.
|
||||||
|
:values: The properties to be updated
|
||||||
|
:returns: A compute node.
|
||||||
|
:raises: ComputeNodeNotFound
|
||||||
|
"""
|
||||||
|
return _get_dbdriver_instance().update_compute_node(
|
||||||
|
context, node_uuid, values)
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
"""add compute node table
|
||||||
|
|
||||||
|
Revision ID: eeac0d191f5a
|
||||||
|
Revises: 8192905fd835
|
||||||
|
Create Date: 2017-02-28 21:32:58.122924
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'eeac0d191f5a'
|
||||||
|
down_revision = '8192905fd835'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from zun.db.sqlalchemy import models
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.create_table(
|
||||||
|
'compute_node',
|
||||||
|
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('uuid', sa.String(length=36), nullable=False),
|
||||||
|
sa.Column('hostname', sa.String(length=255), nullable=False),
|
||||||
|
sa.Column('numa_topology', models.JSONEncodedDict(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('uuid'),
|
||||||
|
)
|
@ -87,10 +87,10 @@ def add_identity_filter(query, value):
|
|||||||
|
|
||||||
|
|
||||||
def _paginate_query(model, limit=None, marker=None, sort_key=None,
|
def _paginate_query(model, limit=None, marker=None, sort_key=None,
|
||||||
sort_dir=None, query=None):
|
sort_dir=None, query=None, default_sort_key='id'):
|
||||||
if not query:
|
if not query:
|
||||||
query = model_query(model)
|
query = model_query(model)
|
||||||
sort_keys = ['id']
|
sort_keys = [default_sort_key]
|
||||||
if sort_key and sort_key not in sort_keys:
|
if sort_key and sort_key not in sort_keys:
|
||||||
sort_keys.insert(0, sort_key)
|
sort_keys.insert(0, sort_key)
|
||||||
try:
|
try:
|
||||||
@ -300,7 +300,7 @@ class Connection(object):
|
|||||||
return _paginate_query(models.ZunService, query=query)
|
return _paginate_query(models.ZunService, query=query)
|
||||||
|
|
||||||
def pull_image(self, context, values):
|
def pull_image(self, context, values):
|
||||||
# ensure defaults are present for new containers
|
# ensure defaults are present for new images
|
||||||
if not values.get('uuid'):
|
if not values.get('uuid'):
|
||||||
values['uuid'] = uuidutils.generate_uuid()
|
values['uuid'] = uuidutils.generate_uuid()
|
||||||
image = models.Image()
|
image = models.Image()
|
||||||
@ -652,3 +652,88 @@ class Connection(object):
|
|||||||
|
|
||||||
ref.update(values)
|
ref.update(values)
|
||||||
return ref
|
return ref
|
||||||
|
|
||||||
|
def _add_compute_nodes_filters(self, query, filters):
|
||||||
|
if filters is None:
|
||||||
|
filters = {}
|
||||||
|
|
||||||
|
filter_names = ['hostname']
|
||||||
|
for name in filter_names:
|
||||||
|
if name in filters:
|
||||||
|
query = query.filter_by(**{name: filters[name]})
|
||||||
|
|
||||||
|
return query
|
||||||
|
|
||||||
|
def list_compute_nodes(self, context, filters=None, limit=None,
|
||||||
|
marker=None, sort_key=None, sort_dir=None):
|
||||||
|
query = model_query(models.ComputeNode)
|
||||||
|
query = self._add_compute_nodes_filters(query, filters)
|
||||||
|
return _paginate_query(models.ComputeNode, limit, marker,
|
||||||
|
sort_key, sort_dir, query,
|
||||||
|
default_sort_key='uuid')
|
||||||
|
|
||||||
|
def create_compute_node(self, context, values):
|
||||||
|
# ensure defaults are present for new compute nodes
|
||||||
|
if not values.get('uuid'):
|
||||||
|
values['uuid'] = uuidutils.generate_uuid()
|
||||||
|
|
||||||
|
compute_node = models.ComputeNode()
|
||||||
|
compute_node.update(values)
|
||||||
|
try:
|
||||||
|
compute_node.save()
|
||||||
|
except db_exc.DBDuplicateEntry:
|
||||||
|
raise exception.ComputeNodeAlreadyExists(
|
||||||
|
field='UUID', value=values['uuid'])
|
||||||
|
return compute_node
|
||||||
|
|
||||||
|
def get_compute_node(self, context, node_uuid):
|
||||||
|
query = model_query(models.ComputeNode)
|
||||||
|
query = query.filter_by(uuid=node_uuid)
|
||||||
|
try:
|
||||||
|
return query.one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.ComputeNodeNotFound(
|
||||||
|
compute_node=node_uuid)
|
||||||
|
|
||||||
|
def get_compute_node_by_hostname(self, context, hostname):
|
||||||
|
query = model_query(models.ComputeNode)
|
||||||
|
query = query.filter_by(hostname=hostname)
|
||||||
|
try:
|
||||||
|
return query.one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.ComputeNodeNotFound(
|
||||||
|
compute_node=hostname)
|
||||||
|
except MultipleResultsFound:
|
||||||
|
raise exception.Conflict('Multiple compute nodes exist with same '
|
||||||
|
'hostname. Please use the uuid instead.')
|
||||||
|
|
||||||
|
def destroy_compute_node(self, context, node_uuid):
|
||||||
|
session = get_session()
|
||||||
|
with session.begin():
|
||||||
|
query = model_query(models.ComputeNode, session=session)
|
||||||
|
query = query.filter_by(uuid=node_uuid)
|
||||||
|
count = query.delete()
|
||||||
|
if count != 1:
|
||||||
|
raise exception.ComputeNodeNotFound(
|
||||||
|
compute_node=node_uuid)
|
||||||
|
|
||||||
|
def update_compute_node(self, context, node_uuid, values):
|
||||||
|
if 'uuid' in values:
|
||||||
|
msg = _("Cannot overwrite UUID for an existing ComputeNode.")
|
||||||
|
raise exception.InvalidParameterValue(err=msg)
|
||||||
|
|
||||||
|
return self._do_update_compute_node(node_uuid, values)
|
||||||
|
|
||||||
|
def _do_update_compute_node(self, node_uuid, values):
|
||||||
|
session = get_session()
|
||||||
|
with session.begin():
|
||||||
|
query = model_query(models.ComputeNode, session=session)
|
||||||
|
query = query.filter_by(uuid=node_uuid)
|
||||||
|
try:
|
||||||
|
ref = query.with_lockmode('update').one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.ComputeNodeNotFound(
|
||||||
|
compute_node=node_uuid)
|
||||||
|
|
||||||
|
ref.update(values)
|
||||||
|
return ref
|
||||||
|
@ -259,3 +259,15 @@ class Allocation(Base):
|
|||||||
primaryjoin=('and_(Allocation.resource_provider_id == '
|
primaryjoin=('and_(Allocation.resource_provider_id == '
|
||||||
'ResourceProvider.id)'),
|
'ResourceProvider.id)'),
|
||||||
foreign_keys=resource_provider_id)
|
foreign_keys=resource_provider_id)
|
||||||
|
|
||||||
|
|
||||||
|
class ComputeNode(Base):
|
||||||
|
"""Represents a compute node. """
|
||||||
|
|
||||||
|
__tablename__ = 'compute_node'
|
||||||
|
__table_args__ = (
|
||||||
|
table_args()
|
||||||
|
)
|
||||||
|
uuid = Column(String(36), primary_key=True, nullable=False)
|
||||||
|
hostname = Column(String(255), nullable=False)
|
||||||
|
numa_topology = Column(JSONEncodedDict, nullable=True)
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from zun.objects import compute_node
|
||||||
from zun.objects import container
|
from zun.objects import container
|
||||||
from zun.objects import image
|
from zun.objects import image
|
||||||
from zun.objects import numa
|
from zun.objects import numa
|
||||||
@ -25,6 +25,7 @@ NUMANode = numa.NUMANode
|
|||||||
NUMATopology = numa.NUMATopology
|
NUMATopology = numa.NUMATopology
|
||||||
ResourceProvider = resource_provider.ResourceProvider
|
ResourceProvider = resource_provider.ResourceProvider
|
||||||
ResourceClass = resource_class.ResourceClass
|
ResourceClass = resource_class.ResourceClass
|
||||||
|
ComputeNode = compute_node.ComputeNode
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
Container,
|
Container,
|
||||||
@ -34,4 +35,5 @@ __all__ = (
|
|||||||
ResourceClass,
|
ResourceClass,
|
||||||
NUMANode,
|
NUMANode,
|
||||||
NUMATopology,
|
NUMATopology,
|
||||||
|
ComputeNode,
|
||||||
)
|
)
|
||||||
|
142
zun/objects/compute_node.py
Normal file
142
zun/objects/compute_node.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
# 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 zun.db import api as dbapi
|
||||||
|
from zun.objects import base
|
||||||
|
from zun.objects.numa import NUMATopology
|
||||||
|
|
||||||
|
|
||||||
|
@base.ZunObjectRegistry.register
|
||||||
|
class ComputeNode(base.ZunPersistentObject, base.ZunObject):
|
||||||
|
# Version 1.0: Initial version
|
||||||
|
VERSION = '1.0'
|
||||||
|
|
||||||
|
fields = {
|
||||||
|
'uuid': fields.UUIDField(read_only=True, nullable=False),
|
||||||
|
'numa_topology': fields.ObjectField('NUMATopology', nullable=True),
|
||||||
|
'hostname': fields.StringField(nullable=False),
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _from_db_object(context, compute_node, db_compute_node):
|
||||||
|
"""Converts a database entity to a formal object."""
|
||||||
|
for field in compute_node.fields:
|
||||||
|
if field == 'numa_topology':
|
||||||
|
numa_obj = NUMATopology._from_dict(
|
||||||
|
db_compute_node['numa_topology'])
|
||||||
|
compute_node.numa_topology = numa_obj
|
||||||
|
else:
|
||||||
|
setattr(compute_node, field, db_compute_node[field])
|
||||||
|
|
||||||
|
compute_node.obj_reset_changes(recursive=True)
|
||||||
|
return compute_node
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _from_db_object_list(db_objects, cls, context):
|
||||||
|
"""Converts a list of database entities to a list of formal objects."""
|
||||||
|
return [ComputeNode._from_db_object(context, cls(context), obj)
|
||||||
|
for obj in db_objects]
|
||||||
|
|
||||||
|
@base.remotable
|
||||||
|
def create(self, context):
|
||||||
|
"""Create a compute node record in the DB.
|
||||||
|
|
||||||
|
:param context: Security context.
|
||||||
|
|
||||||
|
"""
|
||||||
|
values = self.obj_get_changes()
|
||||||
|
numa_obj = values.pop('numa_topology', None)
|
||||||
|
if numa_obj is not None:
|
||||||
|
values['numa_topology'] = numa_obj._to_dict()
|
||||||
|
|
||||||
|
db_compute_node = dbapi.create_compute_node(context, values)
|
||||||
|
self._from_db_object(context, self, db_compute_node)
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def get_by_uuid(cls, context, uuid):
|
||||||
|
"""Find a compute node based on uuid.
|
||||||
|
|
||||||
|
:param uuid: the uuid of a compute node.
|
||||||
|
:param context: Security context
|
||||||
|
:returns: a :class:`ComputeNode` object.
|
||||||
|
"""
|
||||||
|
db_compute_node = dbapi.get_compute_node(context, uuid)
|
||||||
|
compute_node = ComputeNode._from_db_object(
|
||||||
|
context, cls(context), db_compute_node)
|
||||||
|
return compute_node
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def get_by_hostname(cls, context, hostname):
|
||||||
|
db_compute_node = dbapi.get_compute_node_by_hostname(
|
||||||
|
context, hostname)
|
||||||
|
return cls._from_db_object(context, cls(), db_compute_node)
|
||||||
|
|
||||||
|
@base.remotable_classmethod
|
||||||
|
def list(cls, context, limit=None, marker=None,
|
||||||
|
sort_key=None, sort_dir=None, filters=None):
|
||||||
|
"""Return a list of ComputeNode 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".
|
||||||
|
:param filters: filters when list resource providers.
|
||||||
|
:returns: a list of :class:`ComputeNode` object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
db_compute_nodes = dbapi.list_compute_nodes(
|
||||||
|
context, limit=limit, marker=marker, sort_key=sort_key,
|
||||||
|
sort_dir=sort_dir, filters=filters)
|
||||||
|
return ComputeNode._from_db_object_list(
|
||||||
|
db_compute_nodes, cls, context)
|
||||||
|
|
||||||
|
@base.remotable
|
||||||
|
def destroy(self, context=None):
|
||||||
|
"""Delete the ComputeNode from the DB.
|
||||||
|
|
||||||
|
:param context: Security context.
|
||||||
|
"""
|
||||||
|
dbapi.destroy_compute_node(context, self.uuid)
|
||||||
|
self.obj_reset_changes(recursive=True)
|
||||||
|
|
||||||
|
@base.remotable
|
||||||
|
def save(self, context=None):
|
||||||
|
"""Save updates to this ComputeNode.
|
||||||
|
|
||||||
|
Updates will be made column by column based on the result
|
||||||
|
of self.what_changed().
|
||||||
|
|
||||||
|
:param context: Security context.
|
||||||
|
"""
|
||||||
|
updates = self.obj_get_changes()
|
||||||
|
dbapi.update_compute_node(context, self.uuid, updates)
|
||||||
|
self.obj_reset_changes(recursive=True)
|
||||||
|
|
||||||
|
@base.remotable
|
||||||
|
def refresh(self, context=None):
|
||||||
|
"""Loads updates for this ComputeNode.
|
||||||
|
|
||||||
|
Loads a compute node with the same uuid from the database and
|
||||||
|
checks for updated attributes. Updates are applied from
|
||||||
|
the loaded compute node column by column, if there are any
|
||||||
|
updates.
|
||||||
|
|
||||||
|
:param context: Security 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 \
|
||||||
|
getattr(self, field) != getattr(current, field):
|
||||||
|
setattr(self, field, getattr(current, field))
|
@ -60,16 +60,16 @@ class NUMANode(base.ZunObject):
|
|||||||
def _to_dict(self):
|
def _to_dict(self):
|
||||||
return {
|
return {
|
||||||
'id': self.id,
|
'id': self.id,
|
||||||
'cpus': self.cpuset,
|
'cpuset': list(self.cpuset),
|
||||||
'pinned_cpus': self.pinned_cpus
|
'pinned_cpus': list(self.pinned_cpus)
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _from_dict(cls, data_dict):
|
def _from_dict(cls, data_dict):
|
||||||
cpuset = data_dict.get('cpus', '')
|
cpuset = set(data_dict.get('cpuset', ''))
|
||||||
cell_id = data_dict.get('id')
|
node_id = data_dict.get('id')
|
||||||
pinned_cpus = data_dict.get('pinned_cpus')
|
pinned_cpus = set(data_dict.get('pinned_cpus'))
|
||||||
return cls(id=cell_id, cpuset=cpuset,
|
return cls(id=node_id, cpuset=cpuset,
|
||||||
pinned_cpus=pinned_cpus)
|
pinned_cpus=pinned_cpus)
|
||||||
|
|
||||||
|
|
||||||
@ -88,5 +88,10 @@ class NUMATopology(base.ZunObject):
|
|||||||
NUMANode._from_dict(node_dict)
|
NUMANode._from_dict(node_dict)
|
||||||
for node_dict in data_dict.get('nodes', [])])
|
for node_dict in data_dict.get('nodes', [])])
|
||||||
|
|
||||||
|
def _to_dict(self):
|
||||||
|
return {
|
||||||
|
'nodes': [n._to_dict() for n in self.nodes],
|
||||||
|
}
|
||||||
|
|
||||||
def to_list(self):
|
def to_list(self):
|
||||||
return [n._to_dict() for n in self.nodes]
|
return [n._to_dict() for n in self.nodes]
|
||||||
|
@ -32,8 +32,8 @@ CONF = conf.CONF
|
|||||||
|
|
||||||
_numa_node = {
|
_numa_node = {
|
||||||
'id': 0,
|
'id': 0,
|
||||||
'cpus': set([8]),
|
'cpuset': [8],
|
||||||
'pinned_cpus': set([])
|
'pinned_cpus': []
|
||||||
}
|
}
|
||||||
|
|
||||||
_numa_topo_spec = [_numa_node]
|
_numa_topo_spec = [_numa_node]
|
||||||
|
161
zun/tests/unit/db/test_compute_host.py
Normal file
161
zun/tests/unit/db/test_compute_host.py
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
# 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 compute nodes via the DB API"""
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
import six
|
||||||
|
|
||||||
|
from zun.common import exception
|
||||||
|
import zun.conf
|
||||||
|
from zun.db import api as dbapi
|
||||||
|
from zun.tests.unit.db import base
|
||||||
|
from zun.tests.unit.db import utils
|
||||||
|
|
||||||
|
CONF = zun.conf.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class DbComputeNodeTestCase(base.DbTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
cfg.CONF.set_override('db_type', 'sql')
|
||||||
|
super(DbComputeNodeTestCase, self).setUp()
|
||||||
|
|
||||||
|
def test_create_compute_node(self):
|
||||||
|
utils.create_test_compute_node(context=self.context)
|
||||||
|
|
||||||
|
def test_create_compute_node_already_exists(self):
|
||||||
|
utils.create_test_compute_node(
|
||||||
|
context=self.context, uuid='123')
|
||||||
|
with self.assertRaisesRegexp(exception.ComputeNodeAlreadyExists,
|
||||||
|
'A compute node with UUID 123.*'):
|
||||||
|
utils.create_test_compute_node(
|
||||||
|
context=self.context, uuid='123')
|
||||||
|
|
||||||
|
def test_get_compute_node_by_uuid(self):
|
||||||
|
node = utils.create_test_compute_node(context=self.context)
|
||||||
|
res = dbapi.get_compute_node(
|
||||||
|
self.context, node.uuid)
|
||||||
|
self.assertEqual(node.uuid, res.uuid)
|
||||||
|
self.assertEqual(node.hostname, res.hostname)
|
||||||
|
|
||||||
|
def test_get_compute_node_by_hostname(self):
|
||||||
|
node = utils.create_test_compute_node(context=self.context)
|
||||||
|
res = dbapi.get_compute_node_by_hostname(
|
||||||
|
self.context, node.hostname)
|
||||||
|
self.assertEqual(node.uuid, res.uuid)
|
||||||
|
self.assertEqual(node.hostname, res.hostname)
|
||||||
|
|
||||||
|
def test_get_compute_node_that_does_not_exist(self):
|
||||||
|
self.assertRaises(exception.ComputeNodeNotFound,
|
||||||
|
dbapi.get_compute_node,
|
||||||
|
self.context,
|
||||||
|
uuidutils.generate_uuid())
|
||||||
|
|
||||||
|
def test_list_compute_nodes(self):
|
||||||
|
uuids = []
|
||||||
|
for i in range(1, 6):
|
||||||
|
node = utils.create_test_compute_node(
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
context=self.context,
|
||||||
|
hostname='node'+str(i))
|
||||||
|
uuids.append(six.text_type(node['uuid']))
|
||||||
|
res = dbapi.list_compute_nodes(self.context)
|
||||||
|
res_uuids = [r.uuid for r in res]
|
||||||
|
self.assertEqual(sorted(uuids), sorted(res_uuids))
|
||||||
|
|
||||||
|
def test_list_compute_nodes_sorted(self):
|
||||||
|
uuids = []
|
||||||
|
for i in range(5):
|
||||||
|
node = utils.create_test_compute_node(
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
context=self.context,
|
||||||
|
hostname='node'+str(i))
|
||||||
|
uuids.append(six.text_type(node.uuid))
|
||||||
|
res = dbapi.list_compute_nodes(self.context, sort_key='uuid')
|
||||||
|
res_uuids = [r.uuid for r in res]
|
||||||
|
self.assertEqual(sorted(uuids), res_uuids)
|
||||||
|
|
||||||
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
|
dbapi.list_compute_nodes,
|
||||||
|
self.context,
|
||||||
|
sort_key='foo')
|
||||||
|
|
||||||
|
def test_list_compute_nodes_with_filters(self):
|
||||||
|
node1 = utils.create_test_compute_node(
|
||||||
|
hostname='node-one',
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
context=self.context)
|
||||||
|
node2 = utils.create_test_compute_node(
|
||||||
|
hostname='node-two',
|
||||||
|
uuid=uuidutils.generate_uuid(),
|
||||||
|
context=self.context)
|
||||||
|
|
||||||
|
res = dbapi.list_compute_nodes(
|
||||||
|
self.context, filters={'hostname': 'node-one'})
|
||||||
|
self.assertEqual([node1.uuid], [r.uuid for r in res])
|
||||||
|
|
||||||
|
res = dbapi.list_compute_nodes(
|
||||||
|
self.context, filters={'hostname': 'node-two'})
|
||||||
|
self.assertEqual([node2.uuid], [r.uuid for r in res])
|
||||||
|
|
||||||
|
res = dbapi.list_compute_nodes(
|
||||||
|
self.context, filters={'hostname': 'bad-node'})
|
||||||
|
self.assertEqual([], [r.uuid for r in res])
|
||||||
|
|
||||||
|
res = dbapi.list_compute_nodes(
|
||||||
|
self.context,
|
||||||
|
filters={'hostname': node1.hostname})
|
||||||
|
self.assertEqual([node1.uuid], [r.uuid for r in res])
|
||||||
|
|
||||||
|
def test_destroy_compute_node(self):
|
||||||
|
node = utils.create_test_compute_node(context=self.context)
|
||||||
|
dbapi.destroy_compute_node(self.context, node.uuid)
|
||||||
|
self.assertRaises(exception.ComputeNodeNotFound,
|
||||||
|
dbapi.get_compute_node,
|
||||||
|
self.context, node.uuid)
|
||||||
|
|
||||||
|
def test_destroy_compute_node_by_uuid(self):
|
||||||
|
node = utils.create_test_compute_node(context=self.context)
|
||||||
|
dbapi.destroy_compute_node(self.context, node.uuid)
|
||||||
|
self.assertRaises(exception.ComputeNodeNotFound,
|
||||||
|
dbapi.get_compute_node,
|
||||||
|
self.context, node.uuid)
|
||||||
|
|
||||||
|
def test_destroy_compute_node_that_does_not_exist(self):
|
||||||
|
self.assertRaises(exception.ComputeNodeNotFound,
|
||||||
|
dbapi.destroy_compute_node, self.context,
|
||||||
|
uuidutils.generate_uuid())
|
||||||
|
|
||||||
|
def test_update_compute_node(self):
|
||||||
|
node = utils.create_test_compute_node(context=self.context)
|
||||||
|
old_hostname = node.hostname
|
||||||
|
new_hostname = 'new-hostname'
|
||||||
|
self.assertNotEqual(old_hostname, new_hostname)
|
||||||
|
|
||||||
|
res = dbapi.update_compute_node(
|
||||||
|
self.context, node.uuid, {'hostname': new_hostname})
|
||||||
|
self.assertEqual(new_hostname, res.hostname)
|
||||||
|
|
||||||
|
def test_update_compute_node_not_found(self):
|
||||||
|
node_uuid = uuidutils.generate_uuid()
|
||||||
|
new_hostname = 'new-hostname'
|
||||||
|
self.assertRaises(exception.ComputeNodeNotFound,
|
||||||
|
dbapi.update_compute_node, self.context,
|
||||||
|
node_uuid, {'hostname': new_hostname})
|
||||||
|
|
||||||
|
def test_update_compute_node_uuid(self):
|
||||||
|
node = utils.create_test_compute_node(context=self.context)
|
||||||
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
|
dbapi.update_compute_node, self.context,
|
||||||
|
node.uuid, {'uuid': ''})
|
@ -240,6 +240,39 @@ def create_test_allocation(**kw):
|
|||||||
return dbapi.create_allocation(kw['context'], allocation)
|
return dbapi.create_allocation(kw['context'], allocation)
|
||||||
|
|
||||||
|
|
||||||
|
def get_test_numa_topology(**kw):
|
||||||
|
return {
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"cpuset": [1, 2],
|
||||||
|
"pinned_cpus": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"cpuset": [3, 4],
|
||||||
|
"pinned_cpus": [3, 4]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_test_compute_node(**kw):
|
||||||
|
return {
|
||||||
|
'uuid': kw.get('uuid', '24a5b17a-f2eb-4556-89db-5f4169d13982'),
|
||||||
|
'hostname': kw.get('hostname', 'localhost'),
|
||||||
|
'numa_topology': kw.get('numa_topology', get_test_numa_topology()),
|
||||||
|
'created_at': kw.get('created_at'),
|
||||||
|
'updated_at': kw.get('updated_at'),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def create_test_compute_node(**kw):
|
||||||
|
compute_host = get_test_compute_node(**kw)
|
||||||
|
dbapi = db_api._get_dbdriver_instance()
|
||||||
|
return dbapi.create_compute_node(kw['context'], compute_host)
|
||||||
|
|
||||||
|
|
||||||
class FakeEtcdMultipleResult(object):
|
class FakeEtcdMultipleResult(object):
|
||||||
|
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
|
137
zun/tests/unit/objects/test_compute_node.py
Normal file
137
zun/tests/unit/objects/test_compute_node.py
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
# 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 zun import objects
|
||||||
|
from zun.tests.unit.db import base
|
||||||
|
from zun.tests.unit.db import utils
|
||||||
|
|
||||||
|
|
||||||
|
class TestComputeNodeObject(base.DbTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestComputeNodeObject, self).setUp()
|
||||||
|
self.fake_numa_topology = utils.get_test_numa_topology()
|
||||||
|
self.fake_compute_node = utils.get_test_compute_node(
|
||||||
|
numa_topology=self.fake_numa_topology)
|
||||||
|
|
||||||
|
def test_get_by_uuid(self):
|
||||||
|
uuid = self.fake_compute_node['uuid']
|
||||||
|
with mock.patch.object(self.dbapi, 'get_compute_node',
|
||||||
|
autospec=True) as mock_get_compute_node:
|
||||||
|
mock_get_compute_node.return_value = self.fake_compute_node
|
||||||
|
compute_node = objects.ComputeNode.get_by_uuid(self.context, uuid)
|
||||||
|
mock_get_compute_node.assert_called_once_with(
|
||||||
|
self.context, uuid)
|
||||||
|
self.assertEqual(self.context, compute_node._context)
|
||||||
|
|
||||||
|
def test_get_by_hostname(self):
|
||||||
|
hostname = self.fake_compute_node['hostname']
|
||||||
|
with mock.patch.object(self.dbapi, 'get_compute_node_by_hostname',
|
||||||
|
autospec=True) as mock_get:
|
||||||
|
mock_get.return_value = self.fake_compute_node
|
||||||
|
compute_node = objects.ComputeNode.get_by_hostname(
|
||||||
|
self.context, hostname)
|
||||||
|
mock_get.assert_called_once_with(self.context, hostname)
|
||||||
|
self.assertEqual(self.context, compute_node._context)
|
||||||
|
|
||||||
|
def test_list(self):
|
||||||
|
with mock.patch.object(self.dbapi, 'list_compute_nodes',
|
||||||
|
autospec=True) as mock_get_list:
|
||||||
|
mock_get_list.return_value = [self.fake_compute_node]
|
||||||
|
compute_nodes = objects.ComputeNode.list(self.context)
|
||||||
|
self.assertEqual(1, mock_get_list.call_count)
|
||||||
|
self.assertThat(compute_nodes, HasLength(1))
|
||||||
|
self.assertIsInstance(compute_nodes[0], objects.ComputeNode)
|
||||||
|
self.assertEqual(self.context, compute_nodes[0]._context)
|
||||||
|
|
||||||
|
def test_list_with_filters(self):
|
||||||
|
with mock.patch.object(self.dbapi, 'list_compute_nodes',
|
||||||
|
autospec=True) as mock_get_list:
|
||||||
|
mock_get_list.return_value = [self.fake_compute_node]
|
||||||
|
filt = {'hostname': 'test'}
|
||||||
|
compute_nodes = objects.ComputeNode.list(
|
||||||
|
self.context, filters=filt)
|
||||||
|
self.assertEqual(1, mock_get_list.call_count)
|
||||||
|
self.assertThat(compute_nodes, HasLength(1))
|
||||||
|
self.assertIsInstance(compute_nodes[0], objects.ComputeNode)
|
||||||
|
self.assertEqual(self.context, compute_nodes[0]._context)
|
||||||
|
mock_get_list.assert_called_once_with(
|
||||||
|
self.context, filters=filt, limit=None, marker=None,
|
||||||
|
sort_key=None, sort_dir=None)
|
||||||
|
|
||||||
|
def test_create(self):
|
||||||
|
with mock.patch.object(self.dbapi, 'create_compute_node',
|
||||||
|
autospec=True) as mock_create:
|
||||||
|
mock_create.return_value = self.fake_compute_node
|
||||||
|
compute_node_dict = dict(self.fake_compute_node)
|
||||||
|
compute_node_dict['numa_topology'] = objects.NUMATopology\
|
||||||
|
._from_dict(compute_node_dict['numa_topology'])
|
||||||
|
compute_node = objects.ComputeNode(
|
||||||
|
self.context, **compute_node_dict)
|
||||||
|
compute_node.create(self.context)
|
||||||
|
mock_create.assert_called_once_with(
|
||||||
|
self.context, self.fake_compute_node)
|
||||||
|
self.assertEqual(self.context, compute_node._context)
|
||||||
|
|
||||||
|
def test_destroy(self):
|
||||||
|
uuid = self.fake_compute_node['uuid']
|
||||||
|
with mock.patch.object(self.dbapi, 'get_compute_node',
|
||||||
|
autospec=True) as mock_get:
|
||||||
|
mock_get.return_value = self.fake_compute_node
|
||||||
|
with mock.patch.object(self.dbapi, 'destroy_compute_node',
|
||||||
|
autospec=True) as mock_destroy:
|
||||||
|
compute_node = objects.ComputeNode.get_by_uuid(
|
||||||
|
self.context, uuid)
|
||||||
|
compute_node.destroy()
|
||||||
|
mock_get.assert_called_once_with(self.context, uuid)
|
||||||
|
mock_destroy.assert_called_once_with(None, uuid)
|
||||||
|
self.assertEqual(self.context, compute_node._context)
|
||||||
|
|
||||||
|
def test_save(self):
|
||||||
|
uuid = self.fake_compute_node['uuid']
|
||||||
|
with mock.patch.object(self.dbapi, 'get_compute_node',
|
||||||
|
autospec=True) as mock_get:
|
||||||
|
mock_get.return_value = self.fake_compute_node
|
||||||
|
with mock.patch.object(self.dbapi, 'update_compute_node',
|
||||||
|
autospec=True) as mock_update:
|
||||||
|
compute_node = objects.ComputeNode.get_by_uuid(
|
||||||
|
self.context, uuid)
|
||||||
|
compute_node.hostname = 'myhostname'
|
||||||
|
compute_node.save()
|
||||||
|
|
||||||
|
mock_get.assert_called_once_with(self.context, uuid)
|
||||||
|
mock_update.assert_called_once_with(
|
||||||
|
None, uuid,
|
||||||
|
{'hostname': 'myhostname'})
|
||||||
|
self.assertEqual(self.context, compute_node._context)
|
||||||
|
|
||||||
|
def test_refresh(self):
|
||||||
|
uuid = self.fake_compute_node['uuid']
|
||||||
|
hostname = self.fake_compute_node['hostname']
|
||||||
|
new_hostname = 'myhostname'
|
||||||
|
returns = [dict(self.fake_compute_node, hostname=hostname),
|
||||||
|
dict(self.fake_compute_node, hostname=new_hostname)]
|
||||||
|
expected = [mock.call(self.context, uuid),
|
||||||
|
mock.call(self.context, uuid)]
|
||||||
|
with mock.patch.object(self.dbapi, 'get_compute_node',
|
||||||
|
side_effect=returns,
|
||||||
|
autospec=True) as mock_get:
|
||||||
|
compute_node = objects.ComputeNode.get_by_uuid(self.context, uuid)
|
||||||
|
self.assertEqual(hostname, compute_node.hostname)
|
||||||
|
compute_node.refresh()
|
||||||
|
self.assertEqual(new_hostname, compute_node.hostname)
|
||||||
|
self.assertEqual(expected, mock_get.call_args_list)
|
||||||
|
self.assertEqual(self.context, compute_node._context)
|
@ -362,6 +362,7 @@ object_data = {
|
|||||||
'ResourceClass': '1.1-d661c7675b3cd5b8c3618b68ba64324e',
|
'ResourceClass': '1.1-d661c7675b3cd5b8c3618b68ba64324e',
|
||||||
'ResourceProvider': '1.0-92b427359d5a4cf9ec6c72cbe630ee24',
|
'ResourceProvider': '1.0-92b427359d5a4cf9ec6c72cbe630ee24',
|
||||||
'ZunService': '1.0-2a19ab9987a746621b2ada02d8aadf22',
|
'ZunService': '1.0-2a19ab9987a746621b2ada02d8aadf22',
|
||||||
|
'ComputeNode': '1.0-790d40d5af2d05dc6302bd2e4aa4d80b',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user