summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorGary Kotton <gkotton@redhat.com>2013-05-09 13:42:44 +0000
committerGary Kotton <gkotton@redhat.com>2013-06-11 08:50:50 +0000
commit501ff418df4c08e52a697772200aa08dd67a4a43 (patch)
tree983c4e5fcf3015d880f7c974a933cfd75e3bd2ec /nova
parenta79eea05db0e907c3f942fd8380f238aa34b8f2b (diff)
downloadnova-501ff418df4c08e52a697772200aa08dd67a4a43.tar.gz
nova-501ff418df4c08e52a697772200aa08dd67a4a43.tar.xz
nova-501ff418df4c08e52a697772200aa08dd67a4a43.zip
Nova instance group DB support
DB support for blueprint instance-group-api-extension Change-Id: I615af9826ef61fd63d4cd8017908f943969bf177
Diffstat (limited to 'nova')
-rw-r--r--nova/db/api.py90
-rw-r--r--nova/db/sqlalchemy/api.py354
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/187_add_instance_groups.py121
-rw-r--r--nova/db/sqlalchemy/models.py69
-rw-r--r--nova/exception.py22
-rw-r--r--nova/tests/db/test_db_api.py364
-rw-r--r--nova/tests/db/test_migrations.py44
7 files changed, 1064 insertions, 0 deletions
diff --git a/nova/db/api.py b/nova/db/api.py
index 8a7c6dc48..7ecf6dfc0 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -708,6 +708,96 @@ def instance_remove_security_group(context, instance_id, security_group_id):
security_group_id)
+####################
+
+
+def instance_group_create(context, values, policies=None, metadata=None,
+ members=None):
+ """Create a new group with metadata.
+
+ Each group will receive a unique uuid. This will be used for access to the
+ group.
+ """
+ return IMPL.instance_group_create(context, values, policies, metadata,
+ members)
+
+
+def instance_group_get(context, group_uuid):
+ """Get a specific group by id."""
+ return IMPL.instance_group_get(context, group_uuid)
+
+
+def instance_group_update(context, group_uuid, values):
+ """Update the attributes of an group."""
+ return IMPL.instance_group_update(context, group_uuid, values)
+
+
+def instance_group_delete(context, group_uuid):
+ """Delete an group."""
+ return IMPL.instance_group_delete(context, group_uuid)
+
+
+def instance_group_get_all(context):
+ """Get all groups."""
+ return IMPL.instance_group_get_all(context)
+
+
+def instance_group_get_all_by_project_id(context, project_id):
+ """Get all groups for a specific project_id."""
+ return IMPL.instance_group_get_all_by_project_id(context, project_id)
+
+
+def instance_group_metadata_add(context, group_uuid, metadata,
+ set_delete=False):
+ """Add metadata to the group."""
+ return IMPL.instance_group_metadata_add(context, group_uuid, metadata,
+ set_delete)
+
+
+def instance_group_metadata_delete(context, group_uuid, key):
+ """Delete metadata from the group."""
+ return IMPL.instance_group_metadata_delete(context, group_uuid, key)
+
+
+def instance_group_metadata_get(context, group_uuid):
+ """Get the metadata from the group."""
+ return IMPL.instance_group_metadata_get(context, group_uuid)
+
+
+def instance_group_members_add(context, group_uuid, members,
+ set_delete=False):
+ """Add members to the group."""
+ return IMPL.instance_group_members_add(context, group_uuid, members,
+ set_delete=set_delete)
+
+
+def instance_group_member_delete(context, group_uuid, instance_id):
+ """Delete a specific member from the group."""
+ return IMPL.instance_group_member_delete(context, group_uuid, instance_id)
+
+
+def instance_group_members_get(context, group_uuid):
+ """Get the members from the group."""
+ return IMPL.instance_group_members_get(context, group_uuid)
+
+
+def instance_group_policies_add(context, group_uuid, policies,
+ set_delete=False):
+ """Add policies to the group."""
+ return IMPL.instance_group_policies_add(context, group_uuid, policies,
+ set_delete=set_delete)
+
+
+def instance_group_policy_delete(context, group_uuid, policy):
+ """Delete a specific policy from the group."""
+ return IMPL.instance_group_policy_delete(context, group_uuid, policy)
+
+
+def instance_group_policies_get(context, group_uuid):
+ """Get the policies from the group."""
+ return IMPL.instance_group_policies_get(context, group_uuid)
+
+
###################
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index adacc6ead..d27f4e695 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -5187,3 +5187,357 @@ def archive_deleted_rows(context, max_rows=None):
if rows_archived >= max_rows:
break
return rows_archived
+
+
+####################
+
+
+def _instance_group_get_query(context, model_class, id_field=None, id=None,
+ session=None, read_deleted=None):
+ columns_to_join = {models.InstanceGroup: ['_policies', '_metadata',
+ '_members']}
+ query = model_query(context, model_class, session=session,
+ read_deleted=read_deleted)
+
+ for c in columns_to_join.get(model_class, []):
+ query = query.options(joinedload(c))
+
+ if id and id_field:
+ query = query.filter(id_field == id)
+
+ return query
+
+
+def instance_group_create(context, values, policies=None, metadata=None,
+ members=None):
+ """Create a new group with metadata."""
+ uuid = values.get('uuid', None)
+ if uuid is None:
+ uuid = uuidutils.generate_uuid()
+ values['uuid'] = uuid
+ session = get_session()
+ with session.begin():
+ try:
+ group = models.InstanceGroup()
+ group.update(values)
+ group.save(session=session)
+ except db_exc.DBDuplicateEntry:
+ raise exception.InstanceGroupIdExists(group_uuid=uuid)
+
+ # We don't want these to be lazy loaded later. We know there is
+ # nothing here since we just created this instance group.
+ group._policies = []
+ group._metadata = []
+ group._members = []
+ if policies:
+ _instance_group_policies_add(context, group.id, policies,
+ session=session)
+ if metadata:
+ _instance_group_metadata_add(context, group.id, metadata,
+ session=session)
+ if members:
+ _instance_group_members_add(context, group.id, members,
+ session=session)
+ return instance_group_get(context, uuid)
+
+
+def instance_group_get(context, group_uuid):
+ """Get a specific group by uuid."""
+ group = _instance_group_get_query(context,
+ models.InstanceGroup,
+ models.InstanceGroup.uuid,
+ group_uuid).\
+ first()
+ if not group:
+ raise exception.InstanceGroupNotFound(group_uuid=group_uuid)
+ return group
+
+
+def instance_group_update(context, group_uuid, values):
+ """Update the attributes of an group.
+
+ If values contains a metadata key, it updates the aggregate metadata
+ too. Similary for the policies and members.
+ """
+ session = get_session()
+ with session.begin():
+ group = model_query(context,
+ models.InstanceGroup,
+ session=session).\
+ filter_by(uuid=group_uuid).\
+ first()
+ if not group:
+ raise exception.InstanceGroupNotFound(group_uuid=group_uuid)
+
+ policies = values.get('policies')
+ if policies is not None:
+ _instance_group_policies_add(context,
+ group.id,
+ values.pop('policies'),
+ set_delete=True,
+ session=session)
+ metadata = values.get('metadata')
+ if metadata is not None:
+ _instance_group_metadata_add(context,
+ group.id,
+ values.pop('metadata'),
+ set_delete=True,
+ session=session)
+ members = values.get('members')
+ if members is not None:
+ _instance_group_members_add(context,
+ group.id,
+ values.pop('members'),
+ set_delete=True,
+ session=session)
+
+ group.update(values)
+ group.save(session=session)
+ if policies:
+ values['policies'] = policies
+ if metadata:
+ values['metadata'] = metadata
+ if members:
+ values['members'] = members
+
+
+def instance_group_delete(context, group_uuid):
+ """Delete an group."""
+ session = get_session()
+ with session.begin():
+ count = _instance_group_get_query(context,
+ models.InstanceGroup,
+ models.InstanceGroup.uuid,
+ group_uuid,
+ session=session).soft_delete()
+ if count == 0:
+ raise exception.InstanceGroupNotFound(group_uuid=group_uuid)
+
+ # Delete policies, metadata and members
+ instance_models = [models.InstanceGroupPolicy,
+ models.InstanceGroupMetadata,
+ models.InstanceGroupMember]
+ for model in instance_models:
+ model_query(context, model, session=session).\
+ filter_by(group_id=group_uuid).\
+ soft_delete()
+
+
+def instance_group_get_all(context):
+ """Get all groups."""
+ return _instance_group_get_query(context, models.InstanceGroup).all()
+
+
+def instance_group_get_all_by_project_id(context, project_id):
+ """Get all groups."""
+ return _instance_group_get_query(context, models.InstanceGroup).\
+ filter_by(project_id=project_id).\
+ all()
+
+
+def _instance_group_model_get_query(context, model_class, group_uuid,
+ session=None, read_deleted='no'):
+ return model_query(context,
+ model_class,
+ read_deleted=read_deleted,
+ session=session).\
+ filter_by(group_id=group_uuid)
+
+
+def _instance_group_id(context, group_uuid):
+ """Returns the group database ID for the group UUID."""
+
+ result = model_query(context,
+ models.InstanceGroup.id,
+ base_model=models.InstanceGroup).\
+ filter_by(uuid=group_uuid).\
+ first()
+ if not result:
+ raise exception.InstanceGroupNotFound(group_uuid=group_uuid)
+ return result.id
+
+
+def _instance_group_metadata_add(context, id, metadata, set_delete=False,
+ session=None):
+ if not session:
+ session = get_session()
+
+ with session.begin(subtransactions=True):
+ all_keys = metadata.keys()
+ query = _instance_group_model_get_query(context,
+ models.InstanceGroupMetadata,
+ id,
+ session=session)
+ if set_delete:
+ query.filter(~models.InstanceGroupMetadata.key.in_(all_keys)).\
+ soft_delete(synchronize_session=False)
+
+ query = query.filter(models.InstanceGroupMetadata.key.in_(all_keys))
+ already_existing_keys = set()
+ for meta_ref in query.all():
+ key = meta_ref.key
+ meta_ref.update({'value': metadata[key]})
+ already_existing_keys.add(key)
+
+ for key, value in metadata.iteritems():
+ if key in already_existing_keys:
+ continue
+ meta_ref = models.InstanceGroupMetadata()
+ meta_ref.update({'key': key,
+ 'value': value,
+ 'group_id': id})
+ session.add(meta_ref)
+
+ return metadata
+
+
+def instance_group_metadata_add(context, group_uuid, metadata,
+ set_delete=False):
+ id = _instance_group_id(context, group_uuid)
+ return _instance_group_metadata_add(context, id, metadata,
+ set_delete=set_delete)
+
+
+def instance_group_metadata_delete(context, group_uuid, key):
+ id = _instance_group_id(context, group_uuid)
+ count = _instance_group_get_query(context,
+ models.InstanceGroupMetadata,
+ models.InstanceGroupMetadata.group_id,
+ id).\
+ filter_by(key=key).\
+ soft_delete()
+ if count == 0:
+ raise exception.InstanceGroupMetadataNotFound(group_uuid=group_uuid,
+ metadata_key=key)
+
+
+def instance_group_metadata_get(context, group_uuid):
+ id = _instance_group_id(context, group_uuid)
+ rows = model_query(context,
+ models.InstanceGroupMetadata.key,
+ models.InstanceGroupMetadata.value,
+ base_model=models.InstanceGroupMetadata).\
+ filter_by(group_id=id).all()
+ return dict((r[0], r[1]) for r in rows)
+
+
+def _instance_group_members_add(context, id, members, set_delete=False,
+ session=None):
+ if not session:
+ session = get_session()
+
+ all_members = set(members)
+ with session.begin(subtransactions=True):
+ query = _instance_group_model_get_query(context,
+ models.InstanceGroupMember,
+ id,
+ session=session)
+ if set_delete:
+ query.filter(~models.InstanceGroupMember.instance_id.in_(
+ all_members)).\
+ soft_delete(synchronize_session=False)
+
+ query = query.filter(
+ models.InstanceGroupMember.instance_id.in_(all_members))
+ already_existing = set()
+ for member_ref in query.all():
+ already_existing.add(member_ref.instance_id)
+
+ for instance_id in members:
+ if instance_id in already_existing:
+ continue
+ member_ref = models.InstanceGroupMember()
+ member_ref.update({'instance_id': instance_id,
+ 'group_id': id})
+ session.add(member_ref)
+
+ return members
+
+
+def instance_group_members_add(context, group_uuid, members,
+ set_delete=False):
+ id = _instance_group_id(context, group_uuid)
+ return _instance_group_members_add(context, id, members,
+ set_delete=set_delete)
+
+
+def instance_group_member_delete(context, group_uuid, instance_id):
+ id = _instance_group_id(context, group_uuid)
+ count = _instance_group_get_query(context,
+ models.InstanceGroupMember,
+ models.InstanceGroupMember.group_id,
+ id).\
+ filter_by(instance_id=instance_id).\
+ soft_delete()
+ if count == 0:
+ raise exception.InstanceGroupMemberNotFound(group_uuid=group_uuid,
+ instance_id=instance_id)
+
+
+def instance_group_members_get(context, group_uuid):
+ id = _instance_group_id(context, group_uuid)
+ instances = model_query(context,
+ models.InstanceGroupMember.instance_id,
+ base_model=models.InstanceGroupMember).\
+ filter_by(group_id=id).all()
+ return [instance[0] for instance in instances]
+
+
+def _instance_group_policies_add(context, id, policies, set_delete=False,
+ session=None):
+ if not session:
+ session = get_session()
+
+ allpols = set(policies)
+ with session.begin(subtransactions=True):
+ query = _instance_group_model_get_query(context,
+ models.InstanceGroupPolicy,
+ id,
+ session=session)
+ if set_delete:
+ query.filter(~models.InstanceGroupPolicy.policy.in_(allpols)).\
+ soft_delete(synchronize_session=False)
+
+ query = query.filter(models.InstanceGroupPolicy.policy.in_(allpols))
+ already_existing = set()
+ for policy_ref in query.all():
+ already_existing.add(policy_ref.policy)
+
+ for policy in policies:
+ if policy in already_existing:
+ continue
+ policy_ref = models.InstanceGroupPolicy()
+ policy_ref.update({'policy': policy,
+ 'group_id': id})
+ session.add(policy_ref)
+
+ return policies
+
+
+def instance_group_policies_add(context, group_uuid, policies,
+ set_delete=False):
+ id = _instance_group_id(context, group_uuid)
+ return _instance_group_policies_add(context, id, policies,
+ set_delete=set_delete)
+
+
+def instance_group_policy_delete(context, group_uuid, policy):
+ id = _instance_group_id(context, group_uuid)
+ count = _instance_group_get_query(context,
+ models.InstanceGroupPolicy,
+ models.InstanceGroupPolicy.group_id,
+ id).\
+ filter_by(policy=policy).\
+ soft_delete()
+ if count == 0:
+ raise exception.InstanceGroupPolicyNotFound(group_uuid=group_uuid,
+ policy=policy)
+
+
+def instance_group_policies_get(context, group_uuid):
+ id = _instance_group_id(context, group_uuid)
+ policies = model_query(context,
+ models.InstanceGroupPolicy.policy,
+ base_model=models.InstanceGroupPolicy).\
+ filter_by(group_id=id).all()
+ return [policy[0] for policy in policies]
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/187_add_instance_groups.py b/nova/db/sqlalchemy/migrate_repo/versions/187_add_instance_groups.py
new file mode 100644
index 000000000..227188e90
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/187_add_instance_groups.py
@@ -0,0 +1,121 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack Foundation
+#
+# 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 migrate.changeset import UniqueConstraint
+from sqlalchemy import Column
+from sqlalchemy import DateTime
+from sqlalchemy import ForeignKey
+from sqlalchemy import Index
+from sqlalchemy import Integer
+from sqlalchemy import MetaData
+from sqlalchemy import String
+from sqlalchemy import Table
+
+from nova.db.sqlalchemy import api as db
+from nova.db.sqlalchemy import utils
+
+
+def upgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ groups = Table('instance_groups', meta,
+ Column('created_at', DateTime),
+ Column('updated_at', DateTime),
+ Column('deleted_at', DateTime),
+ Column('deleted', Integer),
+ Column('id', Integer, primary_key=True, nullable=False),
+ Column('user_id', String(length=255)),
+ Column('project_id', String(length=255)),
+ Column('uuid', String(length=36), nullable=False),
+ Column('name', String(length=255)),
+ UniqueConstraint('uuid', 'deleted',
+ name='uniq_instance_groups0uuid0deleted'),
+ mysql_engine='InnoDB',
+ mysql_charset='utf8',
+ )
+
+ group_metadata = Table('instance_group_metadata', meta,
+ Column('created_at', DateTime),
+ Column('updated_at', DateTime),
+ Column('deleted_at', DateTime),
+ Column('deleted', Integer),
+ Column('id', Integer, primary_key=True, nullable=False),
+ Column('key', String(length=255)),
+ Column('value', String(length=255)),
+ Column('group_id', Integer, ForeignKey('instance_groups.id'),
+ nullable=False),
+ mysql_engine='InnoDB',
+ mysql_charset='utf8',
+ )
+
+ group_policy = Table('instance_group_policy', meta,
+ Column('created_at', DateTime),
+ Column('updated_at', DateTime),
+ Column('deleted_at', DateTime),
+ Column('deleted', Integer),
+ Column('id', Integer, primary_key=True, nullable=False),
+ Column('policy', String(length=255)),
+ Column('group_id', Integer, ForeignKey('instance_groups.id'),
+ nullable=False),
+ mysql_engine='InnoDB',
+ mysql_charset='utf8',
+ )
+
+ group_member = Table('instance_group_member', meta,
+ Column('created_at', DateTime),
+ Column('updated_at', DateTime),
+ Column('deleted_at', DateTime),
+ Column('deleted', Integer),
+ Column('id', Integer, primary_key=True, nullable=False),
+ Column('instance_id', String(length=255)),
+ Column('group_id', Integer, ForeignKey('instance_groups.id'),
+ nullable=False),
+ mysql_engine='InnoDB',
+ mysql_charset='utf8',
+ )
+
+ tables = [groups, group_metadata, group_policy, group_member]
+
+ # create all of the tables
+ for table in tables:
+ table.create()
+ utils.create_shadow_table(migrate_engine, table=table)
+
+ indexes = [
+ Index('instance_group_metadata_key_idx', group_metadata.c.key),
+ Index('instance_group_member_instance_idx',
+ group_member.c.instance_id),
+ Index('instance_group_policy_policy_idx', group_policy.c.policy)
+ ]
+
+ # Common indexes
+ if migrate_engine.name == 'mysql' or migrate_engine.name == 'postgresql':
+ for index in indexes:
+ index.create(migrate_engine)
+
+
+def downgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ table_names = ['instance_group_member', 'instance_group_policy',
+ 'instance_group_metadata', 'instance_groups']
+ for name in table_names:
+ table = Table(name, meta, autoload=True)
+ table.drop()
+ table = Table(db._SHADOW_TABLE_PREFIX + name, meta, autoload=True)
+ table.drop()
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 386fcbdad..28fe36a0d 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -1039,3 +1039,72 @@ class TaskLog(BASE, NovaBase):
message = Column(String(255), nullable=False)
task_items = Column(Integer(), default=0)
errors = Column(Integer(), default=0)
+
+
+class InstanceGroupMember(BASE, NovaBase):
+ """Represents the members for an instance group."""
+ __tablename__ = 'instance_group_member'
+ id = Column(Integer, primary_key=True, nullable=False)
+ instance_id = Column(String(255), nullable=False)
+ group_id = Column(Integer, ForeignKey('instance_groups.id'),
+ nullable=False)
+
+
+class InstanceGroupPolicy(BASE, NovaBase):
+ """Represents the policy type for an instance group."""
+ __tablename__ = 'instance_group_policy'
+ id = Column(Integer, primary_key=True, nullable=False)
+ policy = Column(String(255), nullable=False)
+ group_id = Column(Integer, ForeignKey('instance_groups.id'),
+ nullable=False)
+
+
+class InstanceGroupMetadata(BASE, NovaBase):
+ """Represents a key/value pair for an instance group."""
+ __tablename__ = 'instance_group_metadata'
+ id = Column(Integer, primary_key=True, nullable=False)
+ key = Column(String(255), nullable=False)
+ value = Column(String(255), nullable=False)
+ group_id = Column(Integer, ForeignKey('instance_groups.id'),
+ nullable=False)
+
+
+class InstanceGroup(BASE, NovaBase):
+ """Represents an instance group.
+
+ A group will maintain a collection of instances and the relationship
+ between them.
+ """
+
+ __tablename__ = 'instance_groups'
+ __table_args__ = (schema.UniqueConstraint("uuid", "deleted"), )
+
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ user_id = Column(String(255))
+ project_id = Column(String(255))
+ uuid = Column(String(36), nullable=False)
+ name = Column(String(255))
+ _policies = relationship(InstanceGroupPolicy, primaryjoin='and_('
+ 'InstanceGroup.id == InstanceGroupPolicy.group_id,'
+ 'InstanceGroupPolicy.deleted == 0,'
+ 'InstanceGroup.deleted == 0)')
+ _metadata = relationship(InstanceGroupMetadata, primaryjoin='and_('
+ 'InstanceGroup.id == InstanceGroupMetadata.group_id,'
+ 'InstanceGroupMetadata.deleted == 0,'
+ 'InstanceGroup.deleted == 0)')
+ _members = relationship(InstanceGroupMember, primaryjoin='and_('
+ 'InstanceGroup.id == InstanceGroupMember.group_id,'
+ 'InstanceGroupMember.deleted == 0,'
+ 'InstanceGroup.deleted == 0)')
+
+ @property
+ def policies(self):
+ return [p.policy for p in self._policies]
+
+ @property
+ def metadetails(self):
+ return dict((m.key, m.value) for m in self._metadata)
+
+ @property
+ def members(self):
+ return [m.instance_id for m in self._members]
diff --git a/nova/exception.py b/nova/exception.py
index 905ddf2da..d8ad08131 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -1235,3 +1235,25 @@ class IncompatibleObjectVersion(NovaException):
class CoreAPIMissing(NovaException):
message = _("Core API extensions are missing: %(missing_apis)s")
+
+
+class InstanceGroupNotFound(NotFound):
+ message = _("Instance group %(group_uuid)s could not be found.")
+
+
+class InstanceGroupIdExists(Duplicate):
+ message = _("Instance group %(group_uuid)s already exists.")
+
+
+class InstanceGroupMetadataNotFound(NotFound):
+ message = _("Instance group %(group_uuid)s has no metadata with "
+ "key %(metadata_key)s.")
+
+
+class InstanceGroupMemberNotFound(NotFound):
+ message = _("Instance group %(group_uuid)s has no member with "
+ "id %(instance_id)s.")
+
+
+class InstanceGroupPolicyNotFound(NotFound):
+ message = _("Instance group %(group_uuid)s has no policy %(policy)s.")
diff --git a/nova/tests/db/test_db_api.py b/nova/tests/db/test_db_api.py
index 04b5bdc33..711f81e52 100644
--- a/nova/tests/db/test_db_api.py
+++ b/nova/tests/db/test_db_api.py
@@ -4950,3 +4950,367 @@ class ArchiveTestCase(test.TestCase):
siim_rows = self.conn.execute(qsiim).fetchall()
si_rows = self.conn.execute(qsi).fetchall()
self.assertEqual(len(siim_rows) + len(si_rows), 8)
+
+
+class InstanceGroupDBApiTestCase(test.TestCase, ModelsObjectComparatorMixin):
+ def setUp(self):
+ super(InstanceGroupDBApiTestCase, self).setUp()
+ self.user_id = 'fake_user'
+ self.project_id = 'fake_project'
+ self.context = context.RequestContext(self.user_id, self.project_id)
+
+ def _get_default_values(self):
+ return {'name': 'fake_name',
+ 'user_id': self.user_id,
+ 'project_id': self.project_id}
+
+ def _create_instance_group(self, context, values, policies=None,
+ metadata=None, members=None):
+ return db.instance_group_create(context, values, policies=policies,
+ metadata=metadata, members=members)
+
+ def test_instance_group_create_no_key(self):
+ values = self._get_default_values()
+ result = self._create_instance_group(self.context, values)
+ ignored_keys = ['id', 'uuid', 'deleted', 'deleted_at', 'updated_at',
+ 'created_at']
+ self._assertEqualObjects(result, values, ignored_keys)
+ self.assertTrue(uuidutils.is_uuid_like(result['uuid']))
+
+ def test_instance_group_create_with_key(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ result = self._create_instance_group(self.context, values)
+ ignored_keys = ['id', 'deleted', 'deleted_at', 'updated_at',
+ 'created_at']
+ self._assertEqualObjects(result, values, ignored_keys)
+
+ def test_instance_group_create_with_same_key(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ result = self._create_instance_group(self.context, values)
+ self.assertRaises(exception.InstanceGroupIdExists,
+ self._create_instance_group, self.context, values)
+
+ def test_instance_group_get(self):
+ values = self._get_default_values()
+ result1 = self._create_instance_group(self.context, values)
+ result2 = db.instance_group_get(self.context, result1['uuid'])
+ self._assertEqualObjects(result1, result2)
+
+ def test_instance_group_update_simple(self):
+ values = self._get_default_values()
+ result1 = self._create_instance_group(self.context, values)
+ values = {'name': 'new_name', 'user_id': 'new_user',
+ 'project_id': 'new_project'}
+ db.instance_group_update(self.context, result1['uuid'],
+ values)
+ result2 = db.instance_group_get(self.context, result1['uuid'])
+ self.assertEquals(result1['uuid'], result2['uuid'])
+ ignored_keys = ['id', 'uuid', 'deleted', 'deleted_at', 'updated_at',
+ 'created_at']
+ self._assertEqualObjects(result2, values, ignored_keys)
+
+ def test_instance_group_delete(self):
+ values = self._get_default_values()
+ result = self._create_instance_group(self.context, values)
+ db.instance_group_delete(self.context, result['uuid'])
+ self.assertRaises(exception.InstanceGroupNotFound,
+ db.instance_group_get, self.context, result['uuid'])
+
+ def test_instance_group_get_all(self):
+ groups = db.instance_group_get_all(self.context)
+ self.assertEquals(0, len(groups))
+ value = self._get_default_values()
+ result1 = self._create_instance_group(self.context, value)
+ groups = db.instance_group_get_all(self.context)
+ self.assertEquals(1, len(groups))
+ value = self._get_default_values()
+ result2 = self._create_instance_group(self.context, value)
+ groups = db.instance_group_get_all(self.context)
+ results = [result1, result2]
+ self._assertEqualListsOfObjects(results, groups)
+
+ def test_instance_group_get_all_by_project_id(self):
+ groups = db.instance_group_get_all_by_project_id(self.context,
+ 'invalid_project_id')
+ self.assertEquals(0, len(groups))
+ values = self._get_default_values()
+ result1 = self._create_instance_group(self.context, values)
+ groups = db.instance_group_get_all_by_project_id(self.context,
+ 'fake_project')
+ self.assertEquals(1, len(groups))
+ values = self._get_default_values()
+ values['project_id'] = 'new_project_id'
+ result2 = self._create_instance_group(self.context, values)
+ groups = db.instance_group_get_all(self.context)
+ results = [result1, result2]
+ self._assertEqualListsOfObjects(results, groups)
+ projects = [{'name': 'fake_project', 'value': [result1]},
+ {'name': 'new_project_id', 'value': [result2]}]
+ for project in projects:
+ groups = db.instance_group_get_all_by_project_id(self.context,
+ project['name'])
+ self._assertEqualListsOfObjects(project['value'], groups)
+
+ def test_instance_group_update(self):
+ values = self._get_default_values()
+ result = self._create_instance_group(self.context, values)
+ ignored_keys = ['id', 'uuid', 'deleted', 'deleted_at', 'updated_at',
+ 'created_at']
+ self._assertEqualObjects(result, values, ignored_keys)
+ self.assertTrue(uuidutils.is_uuid_like(result['uuid']))
+ id = result['uuid']
+ values = self._get_default_values()
+ values['name'] = 'new_fake_name'
+ db.instance_group_update(self.context, id, values)
+ result = db.instance_group_get(self.context, id)
+ self.assertEquals(result['name'], 'new_fake_name')
+ # update metadata
+ values = self._get_default_values()
+ metadataInput = {'key11': 'value1',
+ 'key12': 'value2'}
+ values['metadata'] = metadataInput
+ db.instance_group_update(self.context, id, values)
+ result = db.instance_group_get(self.context, id)
+ metadata = result['metadetails']
+ self._assertEqualObjects(metadata, metadataInput)
+ # update update members
+ values = self._get_default_values()
+ members = ['instance_id1', 'instance_id2']
+ values['members'] = members
+ db.instance_group_update(self.context, id, values)
+ result = db.instance_group_get(self.context, id)
+ self._assertEqualListsOfPrimitivesAsSets(result['members'], members)
+ # update update policies
+ values = self._get_default_values()
+ policies = ['policy1', 'policy2']
+ values['policies'] = policies
+ db.instance_group_update(self.context, id, values)
+ result = db.instance_group_get(self.context, id)
+ self._assertEqualListsOfPrimitivesAsSets(result['policies'], policies)
+ # test invalid ID
+ self.assertRaises(exception.InstanceGroupNotFound,
+ db.instance_group_update, self.context,
+ 'invalid_id', values)
+
+
+class InstanceGroupMetadataDBApiTestCase(InstanceGroupDBApiTestCase):
+ def test_instance_group_metadata_on_create(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ metadata = {'key11': 'value1',
+ 'key12': 'value2'}
+ result = self._create_instance_group(self.context, values,
+ metadata=metadata)
+ ignored_keys = ['id', 'deleted', 'deleted_at', 'updated_at',
+ 'created_at']
+ self._assertEqualObjects(result, values, ignored_keys)
+ self._assertEqualObjects(metadata, result['metadetails'])
+
+ def test_instance_group_metadata_add(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ result = self._create_instance_group(self.context, values)
+ id = result['uuid']
+ metadata = db.instance_group_metadata_get(self.context, id)
+ self._assertEqualObjects(metadata, {})
+ metadata = {'key1': 'value1',
+ 'key2': 'value2'}
+ db.instance_group_metadata_add(self.context, id, metadata)
+ metadata2 = db.instance_group_metadata_get(self.context, id)
+ self._assertEqualObjects(metadata, metadata2)
+
+ def test_instance_group_update(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ result = self._create_instance_group(self.context, values)
+ id = result['uuid']
+ metadata = {'key1': 'value1',
+ 'key2': 'value2'}
+ db.instance_group_metadata_add(self.context, id, metadata)
+ metadata2 = db.instance_group_metadata_get(self.context, id)
+ self._assertEqualObjects(metadata, metadata2)
+ # check add with existing keys
+ metadata = {'key1': 'value1',
+ 'key2': 'value2',
+ 'key3': 'value3'}
+ db.instance_group_metadata_add(self.context, id, metadata)
+ metadata3 = db.instance_group_metadata_get(self.context, id)
+ self._assertEqualObjects(metadata, metadata3)
+
+ def test_instance_group_delete(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ result = self._create_instance_group(self.context, values)
+ id = result['uuid']
+ metadata = {'key1': 'value1',
+ 'key2': 'value2',
+ 'key3': 'value3'}
+ db.instance_group_metadata_add(self.context, id, metadata)
+ metadata3 = db.instance_group_metadata_get(self.context, id)
+ self._assertEqualObjects(metadata, metadata3)
+ db.instance_group_metadata_delete(self.context, id, 'key1')
+ metadata = db.instance_group_metadata_get(self.context, id)
+ self.assertTrue('key1' not in metadata)
+ db.instance_group_metadata_delete(self.context, id, 'key2')
+ metadata = db.instance_group_metadata_get(self.context, id)
+ self.assertTrue('key2' not in metadata)
+
+ def test_instance_group_metadata_invalid_ids(self):
+ values = self._get_default_values()
+ result = self._create_instance_group(self.context, values)
+ id = result['uuid']
+ self.assertRaises(exception.InstanceGroupNotFound,
+ db.instance_group_metadata_get,
+ self.context, 'invalid')
+ self.assertRaises(exception.InstanceGroupNotFound,
+ db.instance_group_metadata_delete, self.context,
+ 'invalidid', 'key1')
+ metadata = {'key1': 'value1',
+ 'key2': 'value2'}
+ db.instance_group_metadata_add(self.context, id, metadata)
+ self.assertRaises(exception.InstanceGroupMetadataNotFound,
+ db.instance_group_metadata_delete,
+ self.context, id, 'invalidkey')
+
+
+class InstanceGroupMembersDBApiTestCase(InstanceGroupDBApiTestCase):
+ def test_instance_group_members_on_create(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ members = ['instance_id1', 'instance_id2']
+ result = self._create_instance_group(self.context, values,
+ members=members)
+ ignored_keys = ['id', 'deleted', 'deleted_at', 'updated_at',
+ 'created_at']
+ self._assertEqualObjects(result, values, ignored_keys)
+ self._assertEqualListsOfPrimitivesAsSets(result['members'], members)
+
+ def test_instance_group_members_add(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ result = self._create_instance_group(self.context, values)
+ id = result['uuid']
+ members = db.instance_group_members_get(self.context, id)
+ self.assertEquals(members, [])
+ members2 = ['instance_id1', 'instance_id2']
+ db.instance_group_members_add(self.context, id, members2)
+ members = db.instance_group_members_get(self.context, id)
+ self._assertEqualListsOfPrimitivesAsSets(members, members2)
+
+ def test_instance_group_members_update(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ result = self._create_instance_group(self.context, values)
+ id = result['uuid']
+ members2 = ['instance_id1', 'instance_id2']
+ db.instance_group_members_add(self.context, id, members2)
+ members = db.instance_group_members_get(self.context, id)
+ self._assertEqualListsOfPrimitivesAsSets(members, members2)
+ # check add with existing keys
+ members3 = ['instance_id1', 'instance_id2', 'instance_id3']
+ db.instance_group_members_add(self.context, id, members3)
+ members = db.instance_group_members_get(self.context, id)
+ self._assertEqualListsOfPrimitivesAsSets(members, members3)
+
+ def test_instance_group_members_delete(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ result = self._create_instance_group(self.context, values)
+ id = result['uuid']
+ members3 = ['instance_id1', 'instance_id2', 'instance_id3']
+ db.instance_group_members_add(self.context, id, members3)
+ members = db.instance_group_members_get(self.context, id)
+ self._assertEqualListsOfPrimitivesAsSets(members, members3)
+ for instance_id in members3[:]:
+ db.instance_group_member_delete(self.context, id, instance_id)
+ members3.remove(instance_id)
+ members = db.instance_group_members_get(self.context, id)
+ self._assertEqualListsOfPrimitivesAsSets(members, members3)
+
+ def test_instance_group_members_invalid_ids(self):
+ values = self._get_default_values()
+ result = self._create_instance_group(self.context, values)
+ id = result['uuid']
+ self.assertRaises(exception.InstanceGroupNotFound,
+ db.instance_group_members_get,
+ self.context, 'invalid')
+ self.assertRaises(exception.InstanceGroupNotFound,
+ db.instance_group_member_delete, self.context,
+ 'invalidid', 'instance_id1')
+ members = ['instance_id1', 'instance_id2']
+ db.instance_group_members_add(self.context, id, members)
+ self.assertRaises(exception.InstanceGroupMemberNotFound,
+ db.instance_group_member_delete,
+ self.context, id, 'invalid_id')
+
+
+class InstanceGroupPoliciesDBApiTestCase(InstanceGroupDBApiTestCase):
+ def test_instance_group_policies_on_create(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ policies = ['policy1', 'policy2']
+ result = self._create_instance_group(self.context, values,
+ policies=policies)
+ ignored_keys = ['id', 'deleted', 'deleted_at', 'updated_at',
+ 'created_at']
+ self._assertEqualObjects(result, values, ignored_keys)
+ self._assertEqualListsOfPrimitivesAsSets(result['policies'], policies)
+
+ def test_instance_group_policies_add(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ result = self._create_instance_group(self.context, values)
+ id = result['uuid']
+ policies = db.instance_group_policies_get(self.context, id)
+ self.assertEquals(policies, [])
+ policies2 = ['policy1', 'policy2']
+ db.instance_group_policies_add(self.context, id, policies2)
+ policies = db.instance_group_policies_get(self.context, id)
+ self._assertEqualListsOfPrimitivesAsSets(policies, policies2)
+
+ def test_instance_group_policies_update(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ result = self._create_instance_group(self.context, values)
+ id = result['uuid']
+ policies2 = ['policy1', 'policy2']
+ db.instance_group_policies_add(self.context, id, policies2)
+ policies = db.instance_group_policies_get(self.context, id)
+ self._assertEqualListsOfPrimitivesAsSets(policies, policies2)
+ policies3 = ['policy1', 'policy2', 'policy3']
+ db.instance_group_policies_add(self.context, id, policies3)
+ policies = db.instance_group_policies_get(self.context, id)
+ self._assertEqualListsOfPrimitivesAsSets(policies, policies3)
+
+ def test_instance_group_policies_delete(self):
+ values = self._get_default_values()
+ values['uuid'] = 'fake_id'
+ result = self._create_instance_group(self.context, values)
+ id = result['uuid']
+ policies3 = ['policy1', 'policy2', 'policy3']
+ db.instance_group_policies_add(self.context, id, policies3)
+ policies = db.instance_group_policies_get(self.context, id)
+ self._assertEqualListsOfPrimitivesAsSets(policies, policies3)
+ for policy in policies3[:]:
+ db.instance_group_policy_delete(self.context, id, policy)
+ policies3.remove(policy)
+ policies = db.instance_group_policies_get(self.context, id)
+ self._assertEqualListsOfPrimitivesAsSets(policies, policies3)
+
+ def test_instance_group_policies_invalid_ids(self):
+ values = self._get_default_values()
+ result = self._create_instance_group(self.context, values)
+ id = result['uuid']
+ self.assertRaises(exception.InstanceGroupNotFound,
+ db.instance_group_policies_get,
+ self.context, 'invalid')
+ self.assertRaises(exception.InstanceGroupNotFound,
+ db.instance_group_policy_delete, self.context,
+ 'invalidid', 'policy1')
+ policies = ['policy1', 'policy2']
+ db.instance_group_policies_add(self.context, id, policies)
+ self.assertRaises(exception.InstanceGroupPolicyNotFound,
+ db.instance_group_policy_delete,
+ self.context, id, 'invalid_policy')
diff --git a/nova/tests/db/test_migrations.py b/nova/tests/db/test_migrations.py
index 0e89cd521..c73093d7d 100644
--- a/nova/tests/db/test_migrations.py
+++ b/nova/tests/db/test_migrations.py
@@ -62,6 +62,7 @@ import nova.db.sqlalchemy.migrate_repo
from nova.db.sqlalchemy import utils as db_utils
from nova.openstack.common import log as logging
from nova.openstack.common import timeutils
+from nova.openstack.common import uuidutils
from nova import test
from nova import utils
import nova.virt.baremetal.db.sqlalchemy.migrate_repo
@@ -1582,6 +1583,49 @@ class TestNovaMigrations(BaseMigrationTestCase, CommonTestsMixIn):
self.assertEqual(bdm_3s[3].image_id, 'fake_image_2')
self.assertEqual(bdm_3s[3].boot_index, 0)
+ # addition of the vm instance groups
+ def _check_no_group_instance_tables(self, engine):
+ self.assertRaises(sqlalchemy.exc.NoSuchTableError,
+ db_utils.get_table, engine,
+ 'instance_groups')
+ self.assertRaises(sqlalchemy.exc.NoSuchTableError,
+ db_utils.get_table, engine,
+ 'instance_group_member')
+ self.assertRaises(sqlalchemy.exc.NoSuchTableError,
+ db_utils.get_table, engine,
+ 'instance_group_policy')
+ self.assertRaises(sqlalchemy.exc.NoSuchTableError,
+ db_utils.get_table, engine,
+ 'instance_group_metadata')
+
+ def _check_group_instance_groups(self, engine):
+ groups = db_utils.get_table(engine, 'instance_groups')
+ uuid4 = uuidutils.generate_uuid()
+ uuid5 = uuidutils.generate_uuid()
+ group_data = [
+ {'id': 4, 'deleted': 4, 'uuid': uuid4},
+ {'id': 5, 'deleted': 0, 'uuid': uuid5},
+ ]
+ engine.execute(groups.insert(), group_data)
+ group = groups.select(groups.c.id == 4).execute().first()
+ self.assertEqual(4, group.deleted)
+ group = groups.select(groups.c.id == 5).execute().first()
+ self.assertEqual(0, group.deleted)
+
+ def _pre_upgrade_187(self, engine):
+ self._check_no_group_instance_tables(engine)
+
+ def _check_187(self, engine, data):
+ self._check_group_instance_groups(engine)
+ tables = ['instance_group_policy', 'instance_group_metadata',
+ 'instance_group_member']
+ for table in tables:
+ db_utils.get_table(engine, table)
+
+ def _post_downgrade_187(self, engine):
+ # check that groups does not exist
+ self._check_no_group_instance_tables(engine)
+
class TestBaremetalMigrations(BaseMigrationTestCase, CommonTestsMixIn):
"""Test sqlalchemy-migrate migrations."""