commit
e63611ad1a
@ -0,0 +1,61 @@
|
||||
# Copyright (c) 2018 European Organization for Nuclear Research.
|
||||
# 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.
|
||||
|
||||
"""add nodegoup table
|
||||
|
||||
Revision ID: ac92cbae311c
|
||||
Revises: cbbc65a86986
|
||||
Create Date: 2018-09-20 15:26:00.869885
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'ac92cbae311c'
|
||||
down_revision = '87e62e3c7abc'
|
||||
|
||||
from alembic import op
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from oslo_db.sqlalchemy.types import String
|
||||
|
||||
from magnum.db.sqlalchemy import models
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
'nodegroup',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('uuid', String(length=36), nullable=False),
|
||||
sa.Column('name', String(length=255), nullable=False),
|
||||
sa.Column('cluster_id', String(length=255), nullable=False),
|
||||
sa.Column('project_id', String(length=255), nullable=False),
|
||||
sa.Column('docker_volume_size', sa.Integer(), nullable=True),
|
||||
sa.Column('labels', models.JSONEncodedDict, nullable=True),
|
||||
sa.Column('flavor_id', String(length=255), nullable=True),
|
||||
sa.Column('image_id', String(length=255), nullable=True),
|
||||
sa.Column('node_addresses', models.JSONEncodedList(), nullable=True),
|
||||
sa.Column('node_count', sa.Integer(), nullable=True),
|
||||
sa.Column('max_node_count', sa.Integer(), nullable=True),
|
||||
sa.Column('min_node_count', sa.Integer(), nullable=True),
|
||||
sa.Column('role', String(length=255), nullable=True),
|
||||
sa.Column('is_default', sa.Boolean(), default=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('uuid', name='uniq_nodegroup0uuid'),
|
||||
sa.UniqueConstraint('cluster_id', 'name',
|
||||
name='uniq_nodegroup0cluster_id0name'),
|
||||
)
|
@ -0,0 +1,218 @@
|
||||
# Copyright (c) 2018 European Organization for Nuclear Research.
|
||||
# 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 strutils
|
||||
from oslo_utils import uuidutils
|
||||
from oslo_versionedobjects import fields
|
||||
|
||||
from magnum.db import api as dbapi
|
||||
from magnum.objects import base
|
||||
|
||||
|
||||
@base.MagnumObjectRegistry.register
|
||||
class NodeGroup(base.MagnumPersistentObject, base.MagnumObject,
|
||||
base.MagnumObjectDictCompat):
|
||||
# Version 1.0: Initial version
|
||||
|
||||
VERSION = '1.0'
|
||||
|
||||
dbapi = dbapi.get_instance()
|
||||
|
||||
fields = {
|
||||
'id': fields.IntegerField(),
|
||||
'uuid': fields.UUIDField(),
|
||||
'name': fields.StringField(),
|
||||
'cluster_id': fields.StringField(),
|
||||
'project_id': fields.StringField(),
|
||||
'docker_volume_size': fields.IntegerField(nullable=True),
|
||||
'labels': fields.DictOfStringsField(nullable=True),
|
||||
'flavor_id': fields.StringField(nullable=True),
|
||||
'image_id': fields.StringField(nullable=True),
|
||||
'node_addresses': fields.ListOfStringsField(nullable=True),
|
||||
'node_count': fields.IntegerField(nullable=False, default=1),
|
||||
'role': fields.StringField(),
|
||||
'max_node_count': fields.IntegerField(nullable=True),
|
||||
'min_node_count': fields.IntegerField(nullable=False, default=1),
|
||||
'is_default': fields.BooleanField(default=False)
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(nodegroup, db_nodegroup):
|
||||
"""Converts a database entity to a formal object."""
|
||||
for field in nodegroup.fields:
|
||||
nodegroup[field] = db_nodegroup[field]
|
||||
|
||||
nodegroup.obj_reset_changes()
|
||||
return nodegroup
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object_list(db_objects, cls, context):
|
||||
"""Converts a list of database entities to a list of formal objects."""
|
||||
return [NodeGroup._from_db_object(cls(context), obj)
|
||||
for obj in db_objects]
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get(cls, context, cluster_id, nodegroup_id):
|
||||
"""Find a nodegroup based on its id or uuid and return a NodeGroup.
|
||||
|
||||
:param cluster_id: the of id a cluster.
|
||||
:param nodegroup_id: the id of a nodegroup.
|
||||
:param context: Security context
|
||||
:returns: a :class:`NodeGroup` object.
|
||||
"""
|
||||
if strutils.is_int_like(nodegroup_id):
|
||||
return cls.get_by_id(context, cluster_id, nodegroup_id)
|
||||
elif uuidutils.is_uuid_like(nodegroup_id):
|
||||
return cls.get_by_uuid(context, cluster_id, nodegroup_id)
|
||||
else:
|
||||
return cls.get_by_name(context, cluster_id, nodegroup_id)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_id(cls, context, cluster, id_):
|
||||
"""Find a nodegroup based on its integer id and return a NodeGroup.
|
||||
|
||||
:param cluster: the id of a cluster.
|
||||
:param id_: the id of a nodegroup.
|
||||
:param context: Security context
|
||||
:returns: a :class:`NodeGroup` object.
|
||||
"""
|
||||
db_nodegroup = cls.dbapi.get_nodegroup_by_id(context, cluster, id_)
|
||||
nodegroup = NodeGroup._from_db_object(cls(context), db_nodegroup)
|
||||
return nodegroup
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_uuid(cls, context, cluster, uuid):
|
||||
"""Find a nodegroup based on uuid and return a :class:`NodeGroup`.
|
||||
|
||||
:param cluster: the id of a cluster.
|
||||
:param uuid: the uuid of a nodegroup.
|
||||
:param context: Security context
|
||||
:returns: a :class:`NodeGroup` object.
|
||||
"""
|
||||
db_nodegroup = cls.dbapi.get_nodegroup_by_uuid(context, cluster, uuid)
|
||||
nodegroup = NodeGroup._from_db_object(cls(context), db_nodegroup)
|
||||
return nodegroup
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_name(cls, context, cluster, name):
|
||||
"""Find a nodegroup based on name and return a NodeGroup object.
|
||||
|
||||
:param cluster: the id of a cluster.
|
||||
:param name: the logical name of a nodegroup.
|
||||
:param context: Security context
|
||||
:returns: a :class:`NodeGroup` object.
|
||||
"""
|
||||
db_nodegroup = cls.dbapi.get_nodegroup_by_name(context, cluster, name)
|
||||
nodegroup = NodeGroup._from_db_object(cls(context), db_nodegroup)
|
||||
return nodegroup
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_count_all(cls, context, cluster_id):
|
||||
"""Get count of nodegroups in cluster.
|
||||
|
||||
:param context: The security context
|
||||
:param cluster_id: The uuid of the cluster
|
||||
:returns: Count of nodegroups in the cluster.
|
||||
"""
|
||||
return cls.dbapi.get_cluster_nodegroup_count(context, cluster_id)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def list(cls, context, cluster, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None, filters=None):
|
||||
"""Return a list of NodeGroup objects.
|
||||
|
||||
:param context: Security context.
|
||||
:param cluster: The cluster uuid or name
|
||||
: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: filter dict, can includes 'name', 'node_count',
|
||||
'stack_id', 'node_addresses',
|
||||
'status'(should be a status list).
|
||||
:returns: a list of :class:`NodeGroup` objects.
|
||||
|
||||
"""
|
||||
db_nodegroups = cls.dbapi.list_cluster_nodegroups(context, cluster,
|
||||
limit=limit,
|
||||
marker=marker,
|
||||
sort_key=sort_key,
|
||||
sort_dir=sort_dir,
|
||||
filters=filters)
|
||||
return NodeGroup._from_db_object_list(db_nodegroups, cls, context)
|
||||
|
||||
@base.remotable
|
||||
def create(self, context=None):
|
||||
"""Create a nodegroup record in the DB.
|
||||
|
||||
:param context: Security context
|
||||
"""
|
||||
values = self.obj_get_changes()
|
||||
db_nodegroup = self.dbapi.create_nodegroup(values)
|
||||
self._from_db_object(self, db_nodegroup)
|
||||
|
||||
@base.remotable
|
||||
def destroy(self, context=None):
|
||||
"""Delete the NodeGroup from the DB.
|
||||
|
||||
:param context: Security context.
|
||||
"""
|
||||
self.dbapi.destroy_nodegroup(self.cluster_id, self.uuid)
|
||||
self.obj_reset_changes()
|
||||
|
||||
@base.remotable
|
||||
def save(self, context=None):
|
||||
"""Save updates to this NodeGroup.
|
||||
|
||||
Updates will be made column by column based on the result
|
||||
of self.what_changed().
|
||||
|
||||
:param context: Security context.
|
||||
"""
|
||||
updates = self.obj_get_changes()
|
||||
self.dbapi.update_nodegroup(self.cluster_id, self.uuid, updates)
|
||||
|
||||
self.obj_reset_changes()
|
||||
|
||||
@base.remotable
|
||||
def refresh(self, context=None):
|
||||
"""Loads updates for this NodeGroup.
|
||||
|
||||
Loads a NodeGroup with the same uuid from the database and
|
||||
checks for updated attributes. Updates are applied from
|
||||
the loaded NogeGroup column by column, if there are any updates.
|
||||
|
||||
:param context: Security context.
|
||||
"""
|
||||
current = self.__class__.get_by_uuid(self._context,
|
||||
cluster=self.cluster_id,
|
||||
uuid=self.uuid)
|
||||
for field in self.fields:
|
||||
if self.obj_attr_is_set(field) and self[field] != current[field]:
|
||||
self[field] = current[field]
|
||||
|
||||
@base.remotable_classmethod
|
||||
def update_nodegroup(cls, context, cluster_id, nodegroup_id, values):
|
||||
"""Updates a NodeGroup.
|
||||
|
||||
:param context: Security context.
|
||||
:param cluster_id:
|
||||
:param nodegroup_id:
|
||||
:param values: a dictionary with the changed values
|
||||
"""
|
||||
current = cls.get(context, cluster_id, nodegroup_id)
|
||||
db_nodegroup = cls.dbapi.update_nodegroup(cluster_id, current.uuid,
|
||||
values)
|
||||
return NodeGroup._from_db_object(cls(context), db_nodegroup)
|
@ -0,0 +1,228 @@
|
||||
# Copyright (c) 2018 European Organization for Nuclear Research.
|
||||
# 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 NodeGroups via the DB API"""
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from magnum.common import exception
|
||||
from magnum.tests.unit.db import base
|
||||
from magnum.tests.unit.db import utils
|
||||
|
||||
|
||||
class DbNodeGroupTestCase(base.DbTestCase):
|
||||
|
||||
def test_create_nodegroup(self):
|
||||
utils.create_test_nodegroup()
|
||||
|
||||
def test_create_nodegroup_already_exists(self):
|
||||
utils.create_test_nodegroup()
|
||||
self.assertRaises(exception.NodeGroupAlreadyExists,
|
||||
utils.create_test_nodegroup)
|
||||
|
||||
def test_create_nodegroup_same_name_same_cluster(self):
|
||||
# NOTE(ttsiouts): Don't allow the same name for nodegroups
|
||||
# in the same cluster.
|
||||
nodegroup = utils.create_test_nodegroup()
|
||||
new = {
|
||||
'name': nodegroup.name,
|
||||
'id': nodegroup.id + 8,
|
||||
'cluster_id': nodegroup.cluster_id
|
||||
}
|
||||
self.assertRaises(exception.NodeGroupAlreadyExists,
|
||||
utils.create_test_nodegroup, **new)
|
||||
|
||||
def test_create_nodegroup_same_name_different_cluster(self):
|
||||
# NOTE(ttsiouts): Verify nodegroups with the same name
|
||||
# but in different clusters are allowed.
|
||||
nodegroup = utils.create_test_nodegroup()
|
||||
new = {
|
||||
'name': nodegroup.name,
|
||||
'id': nodegroup.id + 8,
|
||||
'cluster_id': 'fake-cluster-uuid',
|
||||
'uuid': 'fake-nodegroup-uuid',
|
||||
'project_id': nodegroup.project_id,
|
||||
}
|
||||
try:
|
||||
utils.create_test_nodegroup(**new)
|
||||
except Exception:
|
||||
# Something went wrong, just fail the testcase
|
||||
self.assertTrue(False)
|
||||
|
||||
def test_get_nodegroup_by_id(self):
|
||||
nodegroup = utils.create_test_nodegroup()
|
||||
res = self.dbapi.get_nodegroup_by_id(self.context,
|
||||
nodegroup.cluster_id,
|
||||
nodegroup.id)
|
||||
self.assertEqual(nodegroup.id, res.id)
|
||||
self.assertEqual(nodegroup.uuid, res.uuid)
|
||||
|
||||
def test_get_nodegroup_by_name(self):
|
||||
nodegroup = utils.create_test_nodegroup()
|
||||
res = self.dbapi.get_nodegroup_by_name(self.context,
|
||||
nodegroup.cluster_id,
|
||||
nodegroup.name)
|
||||
self.assertEqual(nodegroup.name, res.name)
|
||||
self.assertEqual(nodegroup.uuid, res.uuid)
|
||||
|
||||
def test_get_cluster_by_uuid(self):
|
||||
nodegroup = utils.create_test_nodegroup()
|
||||
res = self.dbapi.get_nodegroup_by_uuid(self.context,
|
||||
nodegroup.cluster_id,
|
||||
nodegroup.uuid)
|
||||
self.assertEqual(nodegroup.id, res.id)
|
||||
self.assertEqual(nodegroup.uuid, res.uuid)
|
||||
|
||||
def test_get_nodegroup_that_does_not_exist(self):
|
||||
# Create a cluster with no nodegroups
|
||||
cluster = utils.create_test_cluster()
|
||||
self.assertRaises(exception.NodeGroupNotFound,
|
||||
self.dbapi.get_nodegroup_by_id,
|
||||
self.context, cluster.uuid, 100)
|
||||
self.assertRaises(exception.NodeGroupNotFound,
|
||||
self.dbapi.get_nodegroup_by_uuid,
|
||||
self.context, cluster.uuid,
|
||||
'12345678-9999-0000-aaaa-123456789012')
|
||||
self.assertRaises(exception.NodeGroupNotFound,
|
||||
self.dbapi.get_nodegroup_by_name,
|
||||
self.context, cluster.uuid, 'not_found')
|
||||
|
||||
def test_get_nodegroups_in_cluster(self):
|
||||
uuids_in_cluster = []
|
||||
uuids_not_in_cluster = []
|
||||
cluster = utils.create_test_cluster(uuid=uuidutils.generate_uuid())
|
||||
for i in range(2):
|
||||
ng = utils.create_test_nodegroup(uuid=uuidutils.generate_uuid(),
|
||||
name='test%(id)s' % {'id': i},
|
||||
cluster_id=cluster.uuid)
|
||||
uuids_in_cluster.append(ng.uuid)
|
||||
for i in range(2):
|
||||
ng = utils.create_test_nodegroup(uuid=uuidutils.generate_uuid(),
|
||||
name='test%(id)s' % {'id': i},
|
||||
cluster_id='fake_cluster')
|
||||
uuids_not_in_cluster.append(ng.uuid)
|
||||
res = self.dbapi.list_cluster_nodegroups(self.context, cluster.uuid)
|
||||
res_uuids = [r.uuid for r in res]
|
||||
self.assertEqual(sorted(uuids_in_cluster), sorted(res_uuids))
|
||||
for uuid in uuids_not_in_cluster:
|
||||
self.assertNotIn(uuid, res_uuids)
|
||||
|
||||
def test_get_cluster_list_sorted(self):
|
||||
uuids = []
|
||||
cluster = utils.create_test_cluster(uuid=uuidutils.generate_uuid())
|
||||
for i in range(5):
|
||||
ng = utils.create_test_nodegroup(uuid=uuidutils.generate_uuid(),
|
||||
name='test%(id)s' % {'id': i},
|
||||
cluster_id=cluster.uuid)
|
||||
uuids.append(ng.uuid)
|
||||
res = self.dbapi.list_cluster_nodegroups(self.context, cluster.uuid,
|
||||
sort_key='uuid')
|
||||
res_uuids = [r.uuid for r in res]
|
||||
self.assertEqual(sorted(uuids), res_uuids)
|
||||
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.dbapi.list_cluster_nodegroups,
|
||||
self.context,
|
||||
cluster.uuid,
|
||||
sort_key='not-there')
|
||||
|
||||
def test_get_nodegroup_list_with_filters(self):
|
||||
cluster_dict = utils.get_test_cluster(
|
||||
id=1, uuid=uuidutils.generate_uuid())
|
||||
cluster = self.dbapi.create_cluster(cluster_dict)
|
||||
|
||||
group1 = utils.create_test_nodegroup(
|
||||
name='group-one',
|
||||
cluster_id=cluster.uuid,
|
||||
flavor_id=1,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
node_count=1)
|
||||
group2 = utils.create_test_nodegroup(
|
||||
name='group-two',
|
||||
cluster_id=cluster.uuid,
|
||||
flavor_id=1,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
node_count=1)
|
||||
group3 = utils.create_test_nodegroup(
|
||||
name='group-four',
|
||||
cluster_id=cluster.uuid,
|
||||
flavor_id=2,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
node_count=3)
|
||||
|
||||
filters = {'name': 'group-one'}
|
||||
res = self.dbapi.list_cluster_nodegroups(
|
||||
self.context, cluster.uuid, filters=filters)
|
||||
self.assertEqual([group1.id], [r.id for r in res])
|
||||
|
||||
filters = {'node_count': 1}
|
||||
res = self.dbapi.list_cluster_nodegroups(
|
||||
self.context, cluster.uuid, filters=filters)
|
||||
self.assertEqual([group1.id, group2.id], [r.id for r in res])
|
||||
|
||||
filters = {'flavor_id': 2, 'node_count': 3}
|
||||
res = self.dbapi.list_cluster_nodegroups(
|
||||
self.context, cluster.uuid, filters=filters)
|
||||
self.assertEqual([group3.id], [r.id for r in res])
|
||||
|
||||
filters = {'name': 'group-five'}
|
||||
res = self.dbapi.list_cluster_nodegroups(
|
||||
self.context, cluster.uuid, filters=filters)
|
||||
self.assertEqual([], [r.id for r in res])
|
||||
|
||||
def test_destroy_nodegroup(self):
|
||||
cluster = utils.create_test_cluster()
|
||||
nodegroup = utils.create_test_nodegroup()
|
||||
self.assertEqual(nodegroup.uuid, self.dbapi.get_nodegroup_by_uuid(
|
||||
self.context, cluster.uuid, nodegroup.uuid).uuid)
|
||||
self.dbapi.destroy_nodegroup(cluster.uuid, nodegroup.uuid)
|
||||
|
||||
self.assertRaises(exception.NodeGroupNotFound,
|
||||
self.dbapi.get_nodegroup_by_uuid,
|
||||
self.context, cluster.uuid, nodegroup.uuid)
|
||||
|
||||
self.assertRaises(exception.NodeGroupNotFound,
|
||||
self.dbapi.destroy_nodegroup, cluster.uuid,
|
||||
nodegroup.uuid)
|
||||
|
||||
def test_destroy_nodegroup_by_uuid(self):
|
||||
cluster = utils.create_test_cluster()
|
||||
nodegroup = utils.create_test_nodegroup()
|
||||
self.assertIsNotNone(self.dbapi.get_nodegroup_by_uuid(self.context,
|
||||
cluster.uuid,
|
||||
nodegroup.uuid))
|
||||
self.dbapi.destroy_nodegroup(cluster.uuid, nodegroup.uuid)
|
||||
self.assertRaises(exception.NodeGroupNotFound,
|
||||
self.dbapi.get_nodegroup_by_uuid, self.context,
|
||||
cluster.uuid, nodegroup.uuid)
|
||||
|
||||
def test_destroy_cluster_by_uuid_that_does_not_exist(self):
|
||||
self.assertRaises(exception.NodeGroupNotFound,
|
||||
self.dbapi.destroy_nodegroup, 'c_uuid',
|
||||
'12345678-9999-0000-aaaa-123456789012')
|
||||
|
||||
def test_update_cluster(self):
|
||||
nodegroup = utils.create_test_nodegroup()
|
||||
old_flavor = nodegroup.flavor_id
|
||||
new_flavor = 5
|
||||
self.assertNotEqual(old_flavor, new_flavor)
|
||||
res = self.dbapi.update_nodegroup(nodegroup.cluster_id, nodegroup.id,
|
||||
{'flavor_id': new_flavor})
|
||||
self.assertEqual(new_flavor, res.flavor_id)
|
||||
|
||||
def test_update_nodegroup_not_found(self):
|
||||
uuid = uuidutils.generate_uuid()
|
||||
self.assertRaises(exception.NodeGroupNotFound,
|
||||
self.dbapi.update_nodegroup, "c_uuid", uuid,
|
||||
{'node_count': 5})
|
@ -0,0 +1,168 @@
|
||||
# Copyright (c) 2018 European Organization for Nuclear Research.
|
||||
# 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 oslo_utils import uuidutils
|
||||
from testtools.matchers import HasLength
|
||||
|
||||
from magnum import objects
|
||||
from magnum.tests.unit.db import base
|
||||
from magnum.tests.unit.db import utils
|
||||
|
||||
|
||||
class TestNodeGroupObject(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNodeGroupObject, self).setUp()
|
||||
self.fake_nodegroup = utils.get_test_nodegroup()
|
||||
self.fake_nodegroup['docker_volume_size'] = 3
|
||||
self.fake_nodegroup['labels'] = {}
|
||||
|
||||
def test_get_by_id(self):
|
||||
nodegroup_id = self.fake_nodegroup['id']
|
||||
cluster_id = self.fake_nodegroup['cluster_id']
|
||||
with mock.patch.object(self.dbapi, 'get_nodegroup_by_id',
|
||||
autospec=True) as mock_get_nodegroup:
|
||||
mock_get_nodegroup.return_value = self.fake_nodegroup
|
||||
nodegroup = objects.NodeGroup.get(self.context, cluster_id,
|
||||
nodegroup_id)
|
||||
mock_get_nodegroup.assert_called_once_with(self.context,
|
||||
cluster_id,
|
||||
nodegroup_id)
|
||||
self.assertEqual(self.context, nodegroup._context)
|
||||
|
||||
def test_get_by_uuid(self):
|
||||
uuid = self.fake_nodegroup['uuid']
|
||||
cluster_id = self.fake_nodegroup['cluster_id']
|
||||
with mock.patch.object(self.dbapi, 'get_nodegroup_by_uuid',
|
||||
autospec=True) as mock_get_nodegroup:
|
||||
mock_get_nodegroup.return_value = self.fake_nodegroup
|
||||
nodegroup = objects.NodeGroup.get(self.context, cluster_id, uuid)
|
||||
mock_get_nodegroup.assert_called_once_with(self.context,
|
||||
cluster_id, uuid)
|
||||
self.assertEqual(self.context, nodegroup._context)
|
||||
|
||||
def test_get_by_name(self):
|
||||
name = self.fake_nodegroup['name']
|
||||
cluster_id = self.fake_nodegroup['cluster_id']
|
||||
with mock.patch.object(self.dbapi, 'get_nodegroup_by_name',
|
||||
autospec=True) as mock_get_nodegroup:
|
||||
mock_get_nodegroup.return_value = self.fake_nodegroup
|
||||
nodegroup = objects.NodeGroup.get(self.context, cluster_id, name)
|
||||
mock_get_nodegroup.assert_called_once_with(self.context,
|
||||
cluster_id, name)
|
||||
self.assertEqual(self.context, nodegroup._context)
|
||||
|
||||
def test_list(self):
|
||||
cluster_id = self.fake_nodegroup['cluster_id']
|
||||
with mock.patch.object(self.dbapi, 'list_cluster_nodegroups',
|
||||
autospec=True) as mock_get_list:
|
||||
mock_get_list.return_value = [self.fake_nodegroup]
|
||||
nodegroups = objects.NodeGroup.list(self.context, cluster_id)
|
||||
self.assertEqual(1, mock_get_list.call_count)
|
||||
mock_get_list.assert_called_once_with(
|
||||
self.context, cluster_id, limit=None, marker=None,
|
||||
filters=None, sort_dir=None, sort_key=None)
|
||||
self.assertThat(nodegroups, HasLength(1))
|
||||
self.assertIsInstance(nodegroups[0], objects.NodeGroup)
|
||||
self.assertEqual(self.context, nodegroups[0]._context)
|
||||
|
||||
def test_list_with_filters(self):
|
||||
cluster_id = self.fake_nodegroup['cluster_id']
|
||||
with mock.patch.object(self.dbapi, 'list_cluster_nodegroups',
|
||||
autospec=True) as mock_get_list:
|
||||
mock_get_list.return_value = [self.fake_nodegroup]
|
||||
filters = {'name': self.fake_nodegroup['name']}
|
||||
nodegroups = objects.NodeGroup.list(self.context, cluster_id,
|
||||
filters=filters)
|
||||
self.assertEqual(1, mock_get_list.call_count)
|
||||
mock_get_list.assert_called_once_with(
|
||||
self.context, cluster_id, limit=None, marker=None,
|
||||
filters=filters, sort_dir=None, sort_key=None)
|
||||
self.assertThat(nodegroups, HasLength(1))
|
||||
self.assertIsInstance(nodegroups[0], objects.NodeGroup)
|
||||
self.assertEqual(self.context, nodegroups[0]._context)
|
||||
|
||||
def test_create(self):
|
||||
with mock.patch.object(self.dbapi, 'create_nodegroup',
|
||||
autospec=True) as mock_create_nodegroup:
|
||||
mock_create_nodegroup.return_value = self.fake_nodegroup
|
||||
nodegroup = objects.NodeGroup(self.context, **self.fake_nodegroup)
|
||||
nodegroup.create()
|
||||
mock_create_nodegroup.assert_called_once_with(self.fake_nodegroup)
|
||||
self.assertEqual(self.context, nodegroup._context)
|
||||
|
||||
def test_destroy(self):
|
||||
uuid = self.fake_nodegroup['uuid']
|
||||
cluster_id = self.fake_nodegroup['cluster_id']
|
||||
with mock.patch.object(self.dbapi, 'get_nodegroup_by_uuid',
|
||||
autospec=True) as mock_get_nodegroup:
|
||||
mock_get_nodegroup.return_value = self.fake_nodegroup
|
||||
with mock.patch.object(self.dbapi, 'destroy_nodegroup',
|
||||
autospec=True) as mock_destroy_nodegroup:
|
||||
nodegroup = objects.NodeGroup.get_by_uuid(self.context,
|
||||
cluster_id,
|
||||
uuid)
|
||||
nodegroup.destroy()
|
||||
mock_get_nodegroup.assert_called_once_with(self.context,
|
||||
cluster_id,
|
||||
uuid)
|
||||
mock_destroy_nodegroup.assert_called_once_with(cluster_id,
|
||||
uuid)
|
||||
self.assertEqual(self.context, nodegroup._context)
|
||||
|
||||
def test_save(self):
|
||||
uuid = self.fake_nodegroup['uuid']
|
||||
cluster_id = self.fake_nodegroup['cluster_id']
|
||||
with mock.patch.object(self.dbapi, 'get_nodegroup_by_uuid',
|
||||
autospec=True) as mock_get_nodegroup:
|
||||
mock_get_nodegroup.return_value = self.fake_nodegroup
|
||||
with mock.patch.object(self.dbapi, 'update_nodegroup',
|
||||
autospec=True) as mock_update_nodegroup:
|
||||
nodegroup = objects.NodeGroup.get_by_uuid(self.context,
|
||||
cluster_id,
|
||||
uuid)
|
||||
nodegroup.node_count = 10
|
||||
nodegroup.save()
|
||||
|
||||
mock_get_nodegroup.assert_called_once_with(self.context,
|
||||
cluster_id,
|
||||
uuid)
|
||||
expected_changes = {
|
||||
'node_count': 10,
|
||||
}
|
||||
mock_update_nodegroup.assert_called_once_with(
|
||||
cluster_id, uuid, expected_changes)
|
||||
self.assertEqual(self.context, nodegroup._context)
|
||||
|
||||
def test_refresh(self):
|
||||
uuid = self.fake_nodegroup['uuid']
|
||||
cluster_id = self.fake_nodegroup['cluster_id']
|
||||
new_uuid = uuidutils.generate_uuid()
|
||||
returns = [dict(self.fake_nodegroup, uuid=uuid),
|
||||
dict(self.fake_nodegroup, uuid=new_uuid)]
|
||||
expected = [mock.call(self.context, cluster_id, uuid),
|
||||
mock.call(self.context, cluster_id, uuid)]
|
||||
with mock.patch.object(self.dbapi, 'get_nodegroup_by_uuid',
|
||||
side_effect=returns,
|
||||
autospec=True) as mock_get_nodegroup:
|
||||
nodegroup = objects.NodeGroup.get_by_uuid(self.context,
|
||||
cluster_id,
|
||||
uuid)
|
||||
self.assertEqual(uuid, nodegroup.uuid)
|
||||
nodegroup.refresh()
|
||||
self.assertEqual(new_uuid, nodegroup.uuid)
|
||||
self.assertEqual(expected, mock_get_nodegroup.call_args_list)
|
||||
self.assertEqual(self.context, nodegroup._context)
|
Loading…
Reference in new issue