Add DB changes for consistency-groups

This patch adds the database migrations and db api changes
needed for the consistency groups feature.

Partially implements bp manila-consistency-groups

Change-Id: Id6fe4b77f0d31c3f0ade595b11d413cc6ac5f4c2
This commit is contained in:
Alex Meade 2015-08-12 12:55:26 -04:00
parent 15a9d4c5a6
commit 9e9d904412
8 changed files with 1175 additions and 7 deletions

View File

@ -330,6 +330,10 @@ def share_instances_get_all_by_share(context, share_id):
return IMPL.share_instances_get_all_by_share_network(context, share_id)
def share_instances_get_all_by_consistency_group_id(context, cg_id):
"""Returns list of share instances that belong to given cg."""
return IMPL.share_instances_get_all_by_consistency_group_id(context, cg_id)
###################
@ -370,6 +374,23 @@ def share_get_all_by_project(context, project_id, filters=None,
)
def share_get_all_by_consistency_group_id(context, cg_id,
filters=None, sort_key=None,
sort_dir=None):
"""Returns all shares with given project ID and CG id."""
return IMPL.share_get_all_by_consistency_group_id(
context, cg_id, filters=filters,
sort_key=sort_key, sort_dir=sort_dir)
def share_get_all_by_share_network(context, share_network_id, filters=None,
sort_key=None, sort_dir=None):
"""Returns list of shares that belong to given share network."""
return IMPL.share_get_all_by_share_network(
context, share_network_id, filters=filters, sort_key=sort_key,
sort_dir=sort_dir)
def share_get_all_by_share_server(context, share_server_id, filters=None,
sort_key=None, sort_dir=None):
"""Returns all shares with given share server ID."""
@ -847,3 +868,121 @@ def availability_zone_get(context, id_or_name):
def availability_zone_get_all(context):
"""Get all active availability zones."""
return IMPL.availability_zone_get_all(context)
####################
def consistency_group_get(context, consistency_group_id):
"""Get a consistency group or raise if it does not exist."""
return IMPL.consistency_group_get(context, consistency_group_id)
def consistency_group_get_all(context, detailed=True):
"""Get all consistency groups."""
return IMPL.consistency_group_get_all(context, detailed=detailed)
def consistency_group_get_all_by_host(context, host, detailed=True):
"""Get all consistency groups belonging to a host."""
return IMPL.consistency_group_get_all_by_host(context, host,
detailed=detailed)
def consistency_group_create(context, values):
"""Create a consistency group from the values dictionary."""
return IMPL.consistency_group_create(context, values)
def consistency_group_get_all_by_project(context, project_id, detailed=True):
"""Get all consistency groups belonging to a project."""
return IMPL.consistency_group_get_all_by_project(context, project_id,
detailed=detailed)
def consistency_group_update(context, consistency_group_id, values):
"""Set the given properties on a consistency group and update it.
Raises NotFound if consistency group does not exist.
"""
return IMPL.consistency_group_update(context, consistency_group_id, values)
def consistency_group_destroy(context, consistency_group_id):
"""Destroy the consistency group or raise if it does not exist."""
return IMPL.consistency_group_destroy(context, consistency_group_id)
def count_shares_in_consistency_group(context, consistency_group_id):
"""Returns the number of undeleted shares with the specified cg."""
return IMPL.count_shares_in_consistency_group(context,
consistency_group_id)
def count_cgsnapshots_in_consistency_group(context, consistency_group_id):
"""Returns the number of undeleted cgsnapshots with the specified cg."""
return IMPL.count_cgsnapshots_in_consistency_group(context,
consistency_group_id)
def count_consistency_groups_in_share_network(context, share_network_id,
session=None):
"""Returns the number of undeleted cgs with the specified share network."""
return IMPL.count_consistency_groups_in_share_network(context,
share_network_id)
def count_cgsnapshot_members_in_share(context, share_id, session=None):
"""Returns the number of cgsnapshot members linked to the share."""
return IMPL.count_cgsnapshot_members_in_share(context, share_id)
def cgsnapshot_get(context, cgsnapshot_id):
"""Get a cgsnapshot."""
return IMPL.cgsnapshot_get(context, cgsnapshot_id)
def cgsnapshot_get_all(context, detailed=True):
"""Get all cgsnapshots."""
return IMPL.cgsnapshot_get_all(context, detailed=detailed)
def cgsnapshot_get_all_by_project(context, project_id, detailed=True):
"""Get all cgsnapshots belonging to a project."""
return IMPL.cgsnapshot_get_all_by_project(context, project_id,
detailed=detailed)
def cgsnapshot_create(context, values):
"""Create a cgsnapshot from the values dictionary."""
return IMPL.cgsnapshot_create(context, values)
def cgsnapshot_update(context, cgsnapshot_id, values):
"""Set the given properties on a cgsnapshot and update it.
Raises NotFound if cgsnapshot does not exist.
"""
return IMPL.cgsnapshot_update(context, cgsnapshot_id, values)
def cgsnapshot_destroy(context, cgsnapshot_id):
"""Destroy the cgsnapshot or raise if it does not exist."""
return IMPL.cgsnapshot_destroy(context, cgsnapshot_id)
def cgsnapshot_members_get_all(context, cgsnapshot_id):
"""Return the members of a cgsnapshot."""
return IMPL.cgsnapshot_members_get_all(context, cgsnapshot_id)
def cgsnapshot_member_create(context, values):
"""Create a cgsnapshot member from the values dictionary."""
return IMPL.cgsnapshot_member_create(context, values)
def cgsnapshot_member_update(context, member_id, values):
"""Set the given properties on a cgsnapshot member and update it.
Raises NotFound if cgsnapshot member does not exist.
"""
return IMPL.cgsnapshot_member_update(context, member_id, values)

View File

@ -0,0 +1,200 @@
# Copyright (c) 2015 Alex Meade
# 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.
"""Create Consistency Groups Tables and Columns
Revision ID: 3651e16d7c43
Revises: 55761e5f59c5
Create Date: 2015-07-29 13:17:15.940454
"""
# revision identifiers, used by Alembic.
revision = '3651e16d7c43'
down_revision = '55761e5f59c5'
SHARE_NETWORK_FK_CONSTRAINT_NAME = "fk_cg_share_network_id"
SHARE_SERVER_FK_CONSTRAINT_NAME = "fk_cg_share_server_id"
SHARES_CG_FK_CONSTRAINT_NAME = "fk_shares_consistency_group_id"
CG_MAP_FK_CONSTRAINT_NAME = "fk_cgstm_cg_id"
SHARE_TYPE_FK_CONSTRAINT_NAME = "fk_cgstm_share_type_id"
CGSNAP_CG_ID_FK_CONSTRAINT_NAME = "fk_cgsnapshots_consistency_group_id"
CGSNAP_MEM_SHARETYPE_FK_CONSTRAINT_NAME = "fk_cgsnapshot_members_share_type_id"
CGSNAP_MEM_SNAP_ID_FK_CONSTRAINT_NAME = "fk_cgsnapshot_members_cgsnapshot_id"
CGSNAP_MEM_SHARE_FK_CONSTRAINT_NAME = "fk_cgsnapshot_members_share_id"
CGSNAP_MEM_INST_FK_CONSTRAINT_NAME = "fk_cgsnapshot_members_share_instance_id"
from alembic import op
from oslo_log import log
import sqlalchemy as sa
from manila.i18n import _LE
LOG = log.getLogger(__name__)
def upgrade():
# New table - consistency_groups
op.create_table(
'consistency_groups',
sa.Column('id', sa.String(36), primary_key=True, nullable=False),
sa.Column('created_at', sa.DateTime),
sa.Column('updated_at', sa.DateTime),
sa.Column('deleted_at', sa.DateTime),
sa.Column('deleted', sa.String(36), default='False'),
sa.Column('user_id', sa.String(length=255), nullable=False),
sa.Column('project_id', sa.String(length=255), nullable=False),
sa.Column('host', sa.String(length=255)),
sa.Column('name', sa.String(length=255)),
sa.Column('description', sa.String(length=255)),
sa.Column('status', sa.String(length=255)),
sa.Column('source_cgsnapshot_id', sa.String(length=36)),
sa.Column('share_network_id', sa.String(length=36),
sa.ForeignKey('share_networks.id',
name=SHARE_NETWORK_FK_CONSTRAINT_NAME),
nullable=True),
sa.Column('share_server_id', sa.String(length=36),
sa.ForeignKey('share_servers.id',
name=SHARE_SERVER_FK_CONSTRAINT_NAME),
nullable=True),
mysql_engine='InnoDB',
mysql_charset='utf8')
op.add_column(
'shares',
sa.Column('consistency_group_id',
sa.String(36),
sa.ForeignKey('consistency_groups.id',
name=SHARES_CG_FK_CONSTRAINT_NAME)))
op.add_column('shares',
sa.Column('source_cgsnapshot_member_id', sa.String(36)))
op.create_table(
'consistency_group_share_type_mappings',
sa.Column('id', sa.String(36), primary_key=True, nullable=False),
sa.Column('created_at', sa.DateTime),
sa.Column('updated_at', sa.DateTime),
sa.Column('deleted_at', sa.DateTime),
sa.Column('deleted', sa.String(36), default='False'),
sa.Column('consistency_group_id', sa.String(length=36),
sa.ForeignKey('consistency_groups.id',
name=CG_MAP_FK_CONSTRAINT_NAME),
nullable=False),
sa.Column('share_type_id', sa.String(length=36),
sa.ForeignKey('share_types.id',
name=SHARE_TYPE_FK_CONSTRAINT_NAME),
nullable=False),
mysql_engine='InnoDB',
mysql_charset='utf8')
op.create_table(
'cgsnapshots',
sa.Column('id', sa.String(36), primary_key=True, nullable=False),
sa.Column('created_at', sa.DateTime),
sa.Column('updated_at', sa.DateTime),
sa.Column('deleted_at', sa.DateTime),
sa.Column('deleted', sa.String(36), default='False'),
sa.Column('user_id', sa.String(length=255), nullable=False),
sa.Column('project_id', sa.String(length=255), nullable=False),
sa.Column('consistency_group_id', sa.String(length=36),
sa.ForeignKey('consistency_groups.id',
name=CGSNAP_CG_ID_FK_CONSTRAINT_NAME),
nullable=False),
sa.Column('name', sa.String(length=255)),
sa.Column('description', sa.String(length=255)),
sa.Column('status', sa.String(length=255)),
mysql_engine='InnoDB',
mysql_charset='utf8')
op.create_table(
'cgsnapshot_members',
sa.Column('id', sa.String(36), primary_key=True, nullable=False),
sa.Column('created_at', sa.DateTime),
sa.Column('updated_at', sa.DateTime),
sa.Column('deleted_at', sa.DateTime),
sa.Column('deleted', sa.String(36), default='False'),
sa.Column('user_id', sa.String(length=255), nullable=False),
sa.Column('project_id', sa.String(length=255), nullable=False),
sa.Column('cgsnapshot_id', sa.String(length=36),
sa.ForeignKey('cgsnapshots.id',
name=CGSNAP_MEM_SNAP_ID_FK_CONSTRAINT_NAME),
nullable=False),
sa.Column('share_instance_id', sa.String(length=36),
sa.ForeignKey('share_instances.id',
name=CGSNAP_MEM_INST_FK_CONSTRAINT_NAME),
nullable=False),
sa.Column('share_id', sa.String(length=36),
sa.ForeignKey('shares.id',
name=CGSNAP_MEM_SHARE_FK_CONSTRAINT_NAME),
nullable=False),
sa.Column('share_type_id', sa.String(length=36),
sa.ForeignKey('share_types.id',
name=CGSNAP_MEM_SHARETYPE_FK_CONSTRAINT_NAME),
nullable=False),
sa.Column('size', sa.Integer),
sa.Column('status', sa.String(length=255)),
sa.Column('share_proto', sa.String(length=255)),
mysql_engine='InnoDB',
mysql_charset='utf8')
def downgrade():
try:
op.drop_table('cgsnapshot_members')
except Exception:
LOG.exception(_LE("Error Dropping 'cgsnapshot_members' table."))
try:
op.drop_table('cgsnapshots')
except Exception:
LOG.exception(_LE("Error Dropping 'cgsnapshots' table."))
try:
op.drop_table('consistency_group_share_type_mappings')
except Exception:
LOG.exception(_LE("Error Dropping "
"'consistency_group_share_type_mappings' table."))
try:
op.drop_column('shares', 'source_cgsnapshot_member_id')
except Exception:
LOG.exception(_LE("Error Dropping 'source_cgsnapshot_member_id' "
"column from 'shares' table."))
try:
op.drop_constraint(SHARES_CG_FK_CONSTRAINT_NAME,
'shares',
type_='foreignkey')
except Exception:
LOG.exception(_LE("Error Dropping '%s' constraint.") %
SHARES_CG_FK_CONSTRAINT_NAME)
try:
op.drop_column('shares', 'consistency_group_id')
except Exception:
LOG.exception(_LE("Error Dropping 'consistency_group_id' column "
"from 'shares' table."))
try:
op.drop_table('consistency_groups')
except Exception:
LOG.exception(_LE("Error Dropping 'consistency_groups' table."))

View File

@ -21,6 +21,7 @@
import copy
import datetime
import sys
import uuid
import warnings
# NOTE(uglide): Required to override default oslo_db Query class
@ -1260,6 +1261,23 @@ def share_instances_get_all_by_share(context, share_id):
return result
@require_context
def share_instances_get_all_by_consistency_group_id(context, cg_id):
"""Returns list of share instances that belong to given cg."""
result = (
model_query(context, models.Share).filter(
models.Share.consistency_group_id == cg_id,
).all()
)
instances = []
for share in result:
instance = share.instance
instance.set_share_data(share)
instances.append(instance)
return instances
################
@ -1353,7 +1371,8 @@ def share_get(context, share_id, session=None):
@require_context
def _share_get_all_with_filters(context, project_id=None, share_server_id=None,
filters=None, is_public=False, sort_key=None,
consistency_group_id=None, filters=None,
is_public=False, sort_key=None,
sort_dir=None):
"""Returns sorted list of shares that satisfies filters.
@ -1389,6 +1408,10 @@ def _share_get_all_with_filters(context, project_id=None, share_server_id=None,
query = query.filter(
models.ShareInstance.share_server_id == share_server_id)
if consistency_group_id:
query = query.filter(
models.Share.consistency_group_id == consistency_group_id)
# Apply filters
if not filters:
filters = {}
@ -1450,6 +1473,18 @@ def share_get_all_by_project(context, project_id, filters=None,
return query
@require_context
def share_get_all_by_consistency_group_id(context, cg_id,
filters=None, sort_key=None,
sort_dir=None):
"""Returns list of shares with given CG ID."""
query = _share_get_all_with_filters(
context, consistency_group_id=cg_id,
filters=filters, sort_key=sort_key, sort_dir=sort_dir,
)
return query
@require_context
def share_get_all_by_share_server(context, share_server_id, filters=None,
sort_key=None, sort_dir=None):
@ -2364,6 +2399,7 @@ def share_server_get_all_unused_deletable(context, host, updated_before):
)
result = _server_get_query(context)\
.filter_by(host=host)\
.filter(~models.ShareServer.consistency_groups.any())\
.filter(~models.ShareServer.share_instances.any())\
.filter(models.ShareServer.status.in_(valid_server_status))\
.filter(models.ShareServer.updated_at < updated_before).all()
@ -2732,9 +2768,15 @@ def share_type_destroy(context, id):
session = get_session()
with session.begin():
_share_type_get(context, id, session)
results = model_query(context, models.Share, session=session). \
filter_by(share_type_id=id).all()
if results:
results = model_query(context, models.Share, session=session,
read_deleted="no").\
filter_by(share_type_id=id).count()
cg_count = model_query(context,
models.ConsistencyGroupShareTypeMapping,
read_deleted="no",
session=session).\
filter_by(share_type_id=id).count()
if results or cg_count:
LOG.error(_LE('ShareType %s deletion failed, ShareType in use.'),
id)
raise exception.ShareTypeInUse(share_type_id=id)
@ -2939,3 +2981,318 @@ def availability_zone_get_all(context):
read_deleted="no").filter(
models.AvailabilityZone.id.in_(enabled_services)
).all()
####################
def _consistency_group_get(context, consistency_group_id, session=None):
session = session or get_session()
result = model_query(context, models.ConsistencyGroup,
session=session,
project_only=True,
read_deleted='no').\
filter_by(id=consistency_group_id).\
options(joinedload('share_types')).\
first()
if not result:
raise exception.ConsistencyGroupNotFound(
consistency_group_id=consistency_group_id)
return result
@require_context
def consistency_group_get(context, consistency_group_id, session=None):
return _consistency_group_get(context, consistency_group_id,
session=session)
def _consistency_group_get_all_query(context, session=None):
session = session or get_session()
return model_query(context, models.ConsistencyGroup, session=session,
read_deleted='no')
@require_admin_context
def consistency_group_get_all(context, detailed=True):
query = _consistency_group_get_all_query(context)
if detailed:
return query.options(joinedload('share_types')).all()
else:
query = query.with_entities(models.ConsistencyGroup.id,
models.ConsistencyGroup.name)
values = []
for item in query.all():
id, name = item
values.append({"id": id, "name": name})
return values
@require_admin_context
def consistency_group_get_all_by_host(context, host, detailed=True):
query = _consistency_group_get_all_query(context).filter_by(host=host)
if detailed:
return query.options(joinedload('share_types')).all()
else:
query = query.with_entities(models.ConsistencyGroup.id,
models.ConsistencyGroup.name)
values = []
for item in query.all():
id, name = item
values.append({"id": id, "name": name})
return values
@require_context
def consistency_group_get_all_by_project(context, project_id, detailed=True):
authorize_project_context(context, project_id)
query = _consistency_group_get_all_query(context).filter_by(
project_id=project_id)
if detailed:
return query.options(joinedload('share_types')).all()
else:
query = query.with_entities(models.ConsistencyGroup.id,
models.ConsistencyGroup.name)
values = []
for item in query.all():
id, name = item
values.append({"id": id, "name": name})
return values
@require_context
def consistency_group_create(context, values):
consistency_group = models.ConsistencyGroup()
if not values.get('id'):
values['id'] = six.text_type(uuid.uuid4())
mappings = []
for item in values.get('share_types') or []:
mapping = models.ConsistencyGroupShareTypeMapping()
mapping['id'] = six.text_type(uuid.uuid4())
mapping['share_type_id'] = item
mapping['consistency_group_id'] = values['id']
mappings.append(mapping)
values['share_types'] = mappings
session = get_session()
with session.begin():
consistency_group.update(values)
session.add(consistency_group)
return _consistency_group_get(context, values['id'], session=session)
@require_context
def consistency_group_update(context, consistency_group_id, values):
session = get_session()
with session.begin():
cg_ref = _consistency_group_get(context, consistency_group_id,
session=session)
cg_ref.update(values)
cg_ref.save(session=session)
return cg_ref
@require_admin_context
def consistency_group_destroy(context, consistency_group_id):
session = get_session()
with session.begin():
cg_ref = _consistency_group_get(context, consistency_group_id,
session=session)
cg_ref.soft_delete(session)
session.query(models.ConsistencyGroupShareTypeMapping).\
filter_by(consistency_group_id=cg_ref['id']).soft_delete()
@require_context
def count_shares_in_consistency_group(context, consistency_group_id,
session=None):
session = session or get_session()
return model_query(
context, models.Share, session=session,
project_only=True, read_deleted="no").\
filter_by(consistency_group_id=consistency_group_id).\
count()
@require_context
def count_cgsnapshots_in_consistency_group(context, consistency_group_id,
session=None):
session = session or get_session()
return model_query(
context, models.CGSnapshot, session=session,
project_only=True, read_deleted="no").\
filter_by(consistency_group_id=consistency_group_id).\
count()
@require_context
def count_consistency_groups_in_share_network(context, share_network_id,
session=None):
session = session or get_session()
return model_query(
context, models.ConsistencyGroup, session=session,
project_only=True, read_deleted="no").\
filter_by(share_network_id=share_network_id).\
count()
@require_context
def count_cgsnapshot_members_in_share(context, share_id, session=None):
session = session or get_session()
return model_query(
context, models.CGSnapshotMember, session=session,
project_only=True, read_deleted="no").\
filter_by(share_id=share_id).\
count()
@require_context
def _cgsnapshot_get(context, cgsnapshot_id, session=None):
session = session or get_session()
result = model_query(context, models.CGSnapshot, session=session,
project_only=True, read_deleted='no').\
options(joinedload('cgsnapshot_members')).\
options(joinedload('consistency_group')).\
filter_by(id=cgsnapshot_id).\
first()
if not result:
raise exception.CGSnapshotNotFound(cgsnapshot_id=cgsnapshot_id)
return result
def _cgsnapshot_get_all_query(context, session=None):
session = session or get_session()
return model_query(context, models.CGSnapshot, session=session,
reade_deleted='no').\
options(joinedload('cgsnapshot_members')).\
options(joinedload('consistency_group'))
@require_context
def cgsnapshot_get(context, cgsnapshot_id, session=None):
session = session or get_session()
return _cgsnapshot_get(context, cgsnapshot_id, session=session)
@require_admin_context
def cgsnapshot_get_all(context, detailed=True):
query = _cgsnapshot_get_all_query(context)
if detailed:
return query.all()
else:
query = query.with_entities(models.CGSnapshot.id,
models.CGSnapshot.name)
values = []
for item in query.all():
id, name = item
values.append({"id": id, "name": name})
return values
@require_context
def cgsnapshot_get_all_by_project(context, project_id, detailed=True):
authorize_project_context(context, project_id)
query = _cgsnapshot_get_all_query(context).filter_by(
project_id=project_id)
if detailed:
return query.all()
else:
query = query.with_entities(models.CGSnapshot.id,
models.CGSnapshot.name)
values = []
for item in query.all():
id, name = item
values.append({"id": id, "name": name})
return values
@require_context
def cgsnapshot_create(context, values):
cgsnapshot = models.CGSnapshot()
if not values.get('id'):
values['id'] = six.text_type(uuid.uuid4())
session = get_session()
with session.begin():
cgsnapshot.update(values)
session.add(cgsnapshot)
return _cgsnapshot_get(context, values['id'], session=session)
@require_context
def cgsnapshot_update(context, cgsnapshot_id, values):
session = get_session()
with session.begin():
cg_ref = _cgsnapshot_get(context, cgsnapshot_id, session=session)
cg_ref.update(values)
cg_ref.save(session=session)
return cg_ref
@require_admin_context
def cgsnapshot_destroy(context, cgsnapshot_id):
session = get_session()
with session.begin():
cgsnap_ref = _cgsnapshot_get(context, cgsnapshot_id, session=session)
cgsnap_ref.soft_delete(session)
session.query(models.CGSnapshotMember).\
filter_by(cgsnapshot_id=cgsnapshot_id).soft_delete()
@require_context
def cgsnapshot_members_get_all(context, cgsnapshot_id, session=None):
session = session or get_session()
query = model_query(context, models.CGSnapshotMember,
session=session, read_deleted='no').filter_by(
cgsnapshot_id=cgsnapshot_id)
return query.all()
@require_context
def cgsnapshot_member_get(context, member_id, session=None):
result = model_query(context, models.CGSnapshotMember, session=session,
project_only=True, read_deleted='no').\
filter_by(id=member_id).\
first()
if not result:
raise exception.CGSnapshotMemberNotFound(member_id=member_id)
return result
@require_context
def cgsnapshot_member_create(context, values):
member = models.CGSnapshotMember()
if not values.get('id'):
values['id'] = six.text_type(uuid.uuid4())
session = get_session()
with session.begin():
member.update(values)
session.add(member)
return cgsnapshot_member_get(context, values['id'], session=session)
@require_context
def cgsnapshot_member_update(context, member_id, values):
session = get_session()
with session.begin():
member = cgsnapshot_member_get(context, member_id, session=session)
member.update(values)
session.add(member)
return cgsnapshot_member_get(context, member_id, session=session)

View File

@ -225,7 +225,7 @@ class Share(BASE, ManilaBase):
if item in proxified_properties:
return getattr(self.instance, item, None)
raise AttributeError
raise AttributeError(item)
@property
def share_server_id(self):
@ -250,6 +250,11 @@ class Share(BASE, ManilaBase):
share_type_id = Column(String(36), ForeignKey('share_types.id'),
nullable=True)
is_public = Column(Boolean, default=False)
consistency_group_id = Column(String(36),
ForeignKey('consistency_groups.id'),
nullable=True)
source_cgsnapshot_member_id = Column(String(36), nullable=True)
instances = orm.relationship(
"ShareInstance",
@ -271,7 +276,8 @@ class ShareInstance(BASE, ManilaBase):
_proxified_properties = ('user_id', 'project_id', 'size',
'display_name', 'display_description',
'snapshot_id', 'share_proto', 'share_type_id',
'is_public')
'is_public', 'consistency_group_id',
'source_cgsnapshot_member_id')
def set_share_data(self, share):
for share_property in self._proxified_properties:
@ -658,6 +664,11 @@ class ShareServer(BASE, ManilaBase):
'ShareServer.id == ShareInstance.share_server_id,'
'ShareInstance.deleted == "False")')
consistency_groups = orm.relationship(
"ConsistencyGroup", backref='share_server', primaryjoin='and_('
'ShareServer.id == ConsistencyGroup.share_server_id,'
'ConsistencyGroup.deleted == "False")')
_backend_details = orm.relationship(
"ShareServerBackendDetails",
lazy='immediate',
@ -728,6 +739,97 @@ class AvailabilityZone(BASE, ManilaBase):
name = Column(String(255), nullable=False)
class ConsistencyGroup(BASE, ManilaBase):
"""Represents a consistency group."""
__tablename__ = 'consistency_groups'
id = Column(String(36), primary_key=True)
user_id = Column(String(255), nullable=False)
project_id = Column(String(255), nullable=False)
deleted = Column(String(36), default='False')
host = Column(String(255))
name = Column(String(255))
description = Column(String(255))
status = Column(String(255))
source_cgsnapshot_id = Column(String(36))
share_network_id = Column(String(36), ForeignKey('share_networks.id'),
nullable=True)
share_server_id = Column(String(36), ForeignKey('share_servers.id'),
nullable=True)
class CGSnapshot(BASE, ManilaBase):
"""Represents a cgsnapshot."""
__tablename__ = 'cgsnapshots'
id = Column(String(36), primary_key=True)
consistency_group_id = Column(String(36),
ForeignKey('consistency_groups.id'))
user_id = Column(String(255), nullable=False)
project_id = Column(String(255), nullable=False)
deleted = Column(String(36), default='False')
name = Column(String(255))
description = Column(String(255))
status = Column(String(255))
consistency_group = orm.relationship(
ConsistencyGroup,
backref="cgsnapshots",
foreign_keys=consistency_group_id,
primaryjoin=('and_('
'CGSnapshot.consistency_group_id == ConsistencyGroup.id,'
'CGSnapshot.deleted == "False")')
)
class ConsistencyGroupShareTypeMapping(BASE, ManilaBase):
"""Represents the share types in a consistency group."""
__tablename__ = 'consistency_group_share_type_mappings'
id = Column(String(36), primary_key=True)
deleted = Column(String(36), default='False')
consistency_group_id = Column(String(36),
ForeignKey('consistency_groups.id'),
nullable=False)
share_type_id = Column(String(36),
ForeignKey('share_types.id'),
nullable=False)
consistency_group = orm.relationship(
ConsistencyGroup,
backref="share_types",
foreign_keys=consistency_group_id,
primaryjoin=('and_('
'ConsistencyGroupShareTypeMapping.consistency_group_id '
'== ConsistencyGroup.id,'
'ConsistencyGroupShareTypeMapping.deleted == "False")')
)
class CGSnapshotMember(BASE, ManilaBase):
"""Represents the share snapshots in a consistency group snapshot."""
__tablename__ = 'cgsnapshot_members'
id = Column(String(36), primary_key=True)
cgsnapshot_id = Column(String(36), ForeignKey('cgsnapshots.id'))
share_id = Column(String(36), ForeignKey('shares.id'))
share_instance_id = Column(String(36), ForeignKey('share_instances.id'))
size = Column(Integer)
status = Column(String(255))
share_proto = Column(String(255))
share_type_id = Column(String(36), ForeignKey('share_types.id'),
nullable=True)
user_id = Column(String(255))
project_id = Column(String(255))
deleted = Column(String(36), default='False')
cgsnapshot = orm.relationship(
CGSnapshot,
backref="cgsnapshot_members",
foreign_keys=cgsnapshot_id,
primaryjoin='CGSnapshot.id == CGSnapshotMember.cgsnapshot_id')
def register_models():
"""Register Models and create metadata.

View File

@ -644,3 +644,26 @@ class SSHInjectionThreat(ManilaException):
class HNASBackendException(ManilaException):
message = _("HNAS Backend Exception: %(msg)s")
# ConsistencyGroup
class ConsistencyGroupNotFound(NotFound):
message = _("ConsistencyGroup %(consistency_group_id)s could not be "
"found.")
class CGSnapshotNotFound(NotFound):
message = _("Consistency group snapshot %(cgsnapshot_id)s could not be "
"found.")
class CGSnapshotMemberNotFound(NotFound):
message = _("CG snapshot %(member_id)s could not be found.")
class InvalidConsistencyGroup(Invalid):
message = _("Invalid ConsistencyGroup: %(reason)s")
class InvalidCGSnapshot(Invalid):
message = _("Invalid CGSnapshot: %(reason)s")

View File

@ -178,6 +178,16 @@ class ShareDatabaseAPITestCase(test.TestCase):
self.assertEqual(1, len(actual_result))
self.assertEqual(share['id'], actual_result[0].id)
def test_share_filter_all_by_consistency_group(self):
cg = db_utils.create_consistency_group()
share = db_utils.create_share(consistency_group_id=cg['id'])
actual_result = db_api.share_get_all_by_consistency_group_id(
self.ctxt, cg['id'])
self.assertEqual(1, len(actual_result))
self.assertEqual(share['id'], actual_result[0].id)
def test_share_instance_delete_with_share(self):
share = db_utils.create_share()
@ -193,7 +203,20 @@ class ShareDatabaseAPITestCase(test.TestCase):
self.assertEqual('share-%s' % instance['id'], instance['name'])
@ddt.data('host')
def test_share_instance_get_all_by_consistency_group(self):
cg = db_utils.create_consistency_group()
db_utils.create_share(consistency_group_id=cg['id'])
db_utils.create_share()
instances = db_api.share_instances_get_all_by_consistency_group_id(
self.ctxt, cg['id'])
self.assertEqual(1, len(instances))
instance = instances[0]
self.assertEqual('share-%s' % instance['id'], instance['name'])
@ddt.data('host', 'consistency_group_id')
def test_share_get_all_sort_by_share_instance_fields(self, sort_key):
shares = [db_utils.create_share(**{sort_key: n, 'size': 1})
for n in ('test1', 'test2')]
@ -205,6 +228,291 @@ class ShareDatabaseAPITestCase(test.TestCase):
self.assertEqual(shares[0]['id'], actual_result[1]['id'])
@ddt.ddt
class ConsistencyGroupDatabaseAPITestCase(test.TestCase):
def setUp(self):
"""Run before each test."""
super(ConsistencyGroupDatabaseAPITestCase, self).setUp()
self.ctxt = context.get_admin_context()
def test_consistency_group_create_with_share_type(self):
fake_share_types = ["fake_share_type"]
cg = db_utils.create_consistency_group(share_types=fake_share_types)
cg = db_api.consistency_group_get(self.ctxt, cg['id'])
self.assertEqual(1, len(cg['share_types']))
def test_consistency_group_get(self):
cg = db_utils.create_consistency_group()
self.assertDictMatch(dict(cg),
dict(db_api.consistency_group_get(self.ctxt,
cg['id'])))
def test_count_consistency_groups_in_share_network(self):
share_network = db_utils.create_share_network()
db_utils.create_consistency_group()
db_utils.create_consistency_group(share_network_id=share_network['id'])
count = db_api.count_consistency_groups_in_share_network(
self.ctxt, share_network_id=share_network['id'])
self.assertEqual(1, count)
def test_consistency_group_get_all(self):
expected_cg = db_utils.create_consistency_group()
cgs = db_api.consistency_group_get_all(self.ctxt, detailed=False)
self.assertEqual(1, len(cgs))
cg = cgs[0]
self.assertEqual(2, len(dict(cg).keys()))
self.assertEqual(expected_cg['id'], cg['id'])
self.assertEqual(expected_cg['name'], cg['name'])
def test_consistency_group_get_all_with_detail(self):
expected_cg = db_utils.create_consistency_group()
cgs = db_api.consistency_group_get_all(self.ctxt, detailed=True)
self.assertEqual(1, len(cgs))
cg = cgs[0]
self.assertDictMatch(dict(expected_cg), dict(cg))
def test_consistency_group_get_all_by_host(self):
fake_host = 'my_fake_host'
expected_cg = db_utils.create_consistency_group(host=fake_host)
db_utils.create_consistency_group()
cgs = db_api.consistency_group_get_all_by_host(self.ctxt, fake_host,
detailed=False)
self.assertEqual(1, len(cgs))
cg = cgs[0]
self.assertEqual(2, len(dict(cg).keys()))
self.assertEqual(expected_cg['id'], cg['id'])
self.assertEqual(expected_cg['name'], cg['name'])
def test_consistency_group_get_all_by_host_with_details(self):
fake_host = 'my_fake_host'
expected_cg = db_utils.create_consistency_group(host=fake_host)
db_utils.create_consistency_group()
cgs = db_api.consistency_group_get_all_by_host(self.ctxt,
fake_host,
detailed=True)
self.assertEqual(1, len(cgs))
cg = cgs[0]
self.assertDictMatch(dict(expected_cg), dict(cg))
self.assertEqual(fake_host, cg['host'])
def test_consistency_group_get_all_by_project(self):
fake_project = 'fake_project'
expected_cg = db_utils.create_consistency_group(
project_id=fake_project)
db_utils.create_consistency_group()
cgs = db_api.consistency_group_get_all_by_project(self.ctxt,
fake_project,
detailed=False)
self.assertEqual(1, len(cgs))
cg = cgs[0]
self.assertEqual(2, len(dict(cg).keys()))
self.assertEqual(expected_cg['id'], cg['id'])
self.assertEqual(expected_cg['name'], cg['name'])
def test_consistency_group_get_all_by_project_with_details(self):
fake_project = 'fake_project'
expected_cg = db_utils.create_consistency_group(
project_id=fake_project)
db_utils.create_consistency_group()
cgs = db_api.consistency_group_get_all_by_project(self.ctxt,
fake_project,
detailed=True)
self.assertEqual(1, len(cgs))
cg = cgs[0]
self.assertDictMatch(dict(expected_cg), dict(cg))
self.assertEqual(fake_project, cg['project_id'])
def test_consistency_group_update(self):
fake_name = "my_fake_name"
expected_cg = db_utils.create_consistency_group()
expected_cg['name'] = fake_name
db_api.consistency_group_update(self.ctxt,
expected_cg['id'],
{'name': fake_name})
cg = db_api.consistency_group_get(self.ctxt, expected_cg['id'])
self.assertEqual(fake_name, cg['name'])
def test_consistency_group_destroy(self):
cg = db_utils.create_consistency_group()
db_api.consistency_group_get(self.ctxt, cg['id'])
db_api.consistency_group_destroy(self.ctxt, cg['id'])
self.assertRaises(exception.NotFound, db_api.consistency_group_get,
self.ctxt, cg['id'])
def test_count_shares_in_consistency_group(self):
cg = db_utils.create_consistency_group()
db_utils.create_share(consistency_group_id=cg['id'])
db_utils.create_share()
count = db_api.count_shares_in_consistency_group(self.ctxt, cg['id'])
self.assertEqual(1, count)
def test_count_cgsnapshots_in_consistency_group(self):
cg = db_utils.create_consistency_group()
db_utils.create_cgsnapshot(cg['id'])
db_utils.create_cgsnapshot(cg['id'])
count = db_api.count_cgsnapshots_in_consistency_group(self.ctxt,
cg['id'])
self.assertEqual(2, count)
def test_cgsnapshot_get(self):
cg = db_utils.create_consistency_group()
cgsnap = db_utils.create_cgsnapshot(cg['id'])
self.assertDictMatch(dict(cgsnap),
dict(db_api.cgsnapshot_get(self.ctxt,
cgsnap['id'])))
def test_cgsnapshot_get_all(self):
cg = db_utils.create_consistency_group()
expected_cgsnap = db_utils.create_cgsnapshot(cg['id'])
snaps = db_api.cgsnapshot_get_all(self.ctxt, detailed=False)
self.assertEqual(1, len(snaps))
snap = snaps[0]
self.assertEqual(2, len(dict(snap).keys()))
self.assertEqual(expected_cgsnap['id'], snap['id'])
self.assertEqual(expected_cgsnap['name'], snap['name'])
def test_cgsnapshot_get_all_with_detail(self):
cg = db_utils.create_consistency_group()
expected_cgsnap = db_utils.create_cgsnapshot(cg['id'])
snaps = db_api.cgsnapshot_get_all(self.ctxt, detailed=True)
self.assertEqual(1, len(snaps))
snap = snaps[0]
self.assertDictMatch(dict(expected_cgsnap), dict(snap))
def test_cgsnapshot_get_all_by_project(self):
fake_project = 'fake_project'
cg = db_utils.create_consistency_group()
expected_cgsnap = db_utils.create_cgsnapshot(cg['id'],
project_id=fake_project)
snaps = db_api.cgsnapshot_get_all_by_project(self.ctxt,
fake_project,
detailed=False)
self.assertEqual(1, len(snaps))
snap = snaps[0]
self.assertEqual(2, len(dict(snap).keys()))
self.assertEqual(expected_cgsnap['id'], snap['id'])
self.assertEqual(expected_cgsnap['name'], snap['name'])
def test_cgsnapshot_get_all_by_project_with_details(self):
fake_project = 'fake_project'
cg = db_utils.create_consistency_group()
expected_cgsnap = db_utils.create_cgsnapshot(cg['id'],
project_id=fake_project)
snaps = db_api.cgsnapshot_get_all_by_project(self.ctxt,
fake_project,
detailed=True)
self.assertEqual(1, len(snaps))
snap = snaps[0]
self.assertDictMatch(dict(expected_cgsnap), dict(snap))
self.assertEqual(fake_project, snap['project_id'])
def test_cgsnapshot_update(self):
fake_name = "my_fake_name"
cg = db_utils.create_consistency_group()
expected_cgsnap = db_utils.create_cgsnapshot(cg['id'])
expected_cgsnap['name'] = fake_name
db_api.cgsnapshot_update(self.ctxt, expected_cgsnap['id'],
{'name': fake_name})
cgsnap = db_api.cgsnapshot_get(self.ctxt, expected_cgsnap['id'])
self.assertEqual(fake_name, cgsnap['name'])
def test_cgsnapshot_destroy(self):
cg = db_utils.create_consistency_group()
cgsnap = db_utils.create_cgsnapshot(cg['id'])
db_api.cgsnapshot_get(self.ctxt, cgsnap['id'])
db_api.cgsnapshot_destroy(self.ctxt, cgsnap['id'])
self.assertRaises(exception.NotFound, db_api.cgsnapshot_get,
self.ctxt, cgsnap['id'])
def test_cgsnapshot_members_get_all(self):
cg = db_utils.create_consistency_group()
cgsnap = db_utils.create_cgsnapshot(cg['id'])
expected_member = db_utils.create_cgsnapshot_member(cgsnap['id'])
members = db_api.cgsnapshot_members_get_all(self.ctxt, cgsnap['id'])
self.assertEqual(1, len(members))
member = members[0]
self.assertDictMatch(dict(expected_member), dict(member))
def test_count_cgsnapshot_members_in_share(self):
share = db_utils.create_share()
share2 = db_utils.create_share()
cg = db_utils.create_consistency_group()
cgsnap = db_utils.create_cgsnapshot(cg['id'])
db_utils.create_cgsnapshot_member(cgsnap['id'], share_id=share['id'])
db_utils.create_cgsnapshot_member(cgsnap['id'], share_id=share2['id'])
count = db_api.count_cgsnapshot_members_in_share(
self.ctxt, share['id'])
self.assertEqual(1, count)
def test_cgsnapshot_members_get(self):
cg = db_utils.create_consistency_group()
cgsnap = db_utils.create_cgsnapshot(cg['id'])
expected_member = db_utils.create_cgsnapshot_member(cgsnap['id'])
member = db_api.cgsnapshot_member_get(self.ctxt,
expected_member['id'])
self.assertDictMatch(dict(expected_member), dict(member))
def test_cgsnapshot_members_get_not_found(self):
self.assertRaises(exception.CGSnapshotMemberNotFound,
db_api.cgsnapshot_member_get, self.ctxt, 'fake_id')
def test_cgsnapshot_member_update(self):
cg = db_utils.create_consistency_group()
cgsnap = db_utils.create_cgsnapshot(cg['id'])
expected_member = db_utils.create_cgsnapshot_member(cgsnap['id'])
db_api.cgsnapshot_member_update(
self.ctxt, expected_member['id'],
{'status': constants.STATUS_AVAILABLE})
member = db_api.cgsnapshot_member_get(self.ctxt, expected_member['id'])
self.assertEqual(constants.STATUS_AVAILABLE, member['status'])
class ShareSnapshotDatabaseAPITestCase(test.TestCase):
def setUp(self):

View File

@ -29,6 +29,45 @@ def _create_db_row(method, default_values, custom_values):
return method(context.get_admin_context(), default_values)
def create_consistency_group(**kwargs):
"""Create a consistency group object."""
cg = {
'share_network_id': None,
'share_server_id': None,
'user_id': 'fake',
'project_id': 'fake',
'status': constants.STATUS_CREATING,
'host': 'fake_host'
}
return _create_db_row(db.consistency_group_create, cg, kwargs)
def create_cgsnapshot(cg_id, **kwargs):
"""Create a cgsnapshot object."""
snapshot = {
'consistency_group_id': cg_id,
'user_id': 'fake',
'project_id': 'fake',
'status': constants.STATUS_CREATING,
}
return _create_db_row(db.cgsnapshot_create, snapshot, kwargs)
def create_cgsnapshot_member(cgsnapshot_id, **kwargs):
"""Create a cgsnapshot member object."""
member = {
'share_proto': "NFS",
'size': 0,
'share_id': None,
'share_instance_id': None,
'user_id': 'fake',
'project_id': 'fake',
'status': 'creating',
'cgsnapshot_id': cgsnapshot_id,
}
return _create_db_row(db.cgsnapshot_member_create, member, kwargs)
def create_share(**kwargs):
"""Create a share object."""
share = {