Fix default approach for share group snapshot creation

Default approach uses common driver interface of single snapshot
creation. So, emulate it correctly providing all required keys.
Also add 'provider_location' attribute to share group snapshot
members DB model, because it is required for some drivers and
was absent.

Change-Id: If120d85ef3dd3ba90e2dc12a5b81b69feecb31ea
Closes-Bug: #1660321
This commit is contained in:
Valeriy Ponomaryov 2017-02-01 13:41:06 +02:00
parent 7c2c97d725
commit 64649b97e7
10 changed files with 410 additions and 66 deletions

View File

@ -41,6 +41,7 @@ class ShareGroupSnapshotViewBuilder(common.ViewBuilder):
'share_group_snapshot_id': member.get(
'share_group_snapshot_id'),
'share_id': member.get('share_id'),
# TODO(vponomaryov): add 'provider_location' key in Pike.
}
members_list.append(member_dict)

View File

@ -0,0 +1,41 @@
# 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 'provider_location' attr to 'share_group_snapshot_members' model.
Revision ID: 927920b37453
Revises: a77e2ad5012d
Create Date: 2017-01-31 20:10:44.937763
"""
# revision identifiers, used by Alembic.
revision = '927920b37453'
down_revision = 'a77e2ad5012d'
from alembic import op
import sqlalchemy as sa
SGSM_TABLE_NAME = 'share_group_snapshot_members'
PROVIDER_LOCATION_NAME = 'provider_location'
def upgrade():
op.add_column(
SGSM_TABLE_NAME,
sa.Column(PROVIDER_LOCATION_NAME, sa.String(255), nullable=True),
)
def downgrade():
op.drop_column(SGSM_TABLE_NAME, PROVIDER_LOCATION_NAME)

View File

@ -1148,14 +1148,20 @@ class ShareGroupShareTypeMapping(BASE, ManilaBase):
'ShareGroupShareTypeMapping.deleted == "False")')
)
# TODO(vponomaryov): add 'share_group_snapshot_member_export_locations' model
# to support mountable share group snapshots and add its relationship to
# 'share_group_snapshot_members' table.
class ShareGroupSnapshotMember(BASE, ManilaBase):
"""Represents the share snapshots in a share group snapshot."""
__tablename__ = 'share_group_snapshot_members'
id = Column(String(36), primary_key=True)
# TODO(vponomaryov): make 'share_group_snapshot_id' not nullable.
share_group_snapshot_id = Column(
String(36), ForeignKey('share_group_snapshots.id'))
share_id = Column(String(36), ForeignKey('shares.id'))
# TODO(vponomaryov): make 'share_instance_id' not nullable.
share_instance_id = Column(String(36), ForeignKey('share_instances.id'))
size = Column(Integer)
status = Column(String(255))
@ -1163,6 +1169,11 @@ class ShareGroupSnapshotMember(BASE, ManilaBase):
user_id = Column(String(255))
project_id = Column(String(255))
deleted = Column(String(36), default='False')
provider_location = Column(String(255))
# TODO(vponomaryov): add relationship to source share instance as it is
# done for share snapshot instances.
# TODO(vponomaryov): add share group snapshot member export locations
# relationship.
share_group_snapshot = orm.relationship(
ShareGroupSnapshot,
backref="share_group_snapshot_members",

View File

@ -1325,7 +1325,8 @@ class ShareDriver(object):
'deleted_at': None,
'share_id': 'some_fake_uuid',
'id': 'some_fake_uuid',
'size': 1
'size': 1,
'provider_location': None,
}
],
'deleted_at': None,
@ -1350,17 +1351,28 @@ class ShareDriver(object):
share_group=snap_dict['share_group_id'])
elif not snapshot_members:
LOG.warning(_LW('No shares in share group to create snapshot.'))
return None, None
else:
share_snapshots = []
snapshot_members_updates = []
for member in snapshot_members:
share_snapshot = {
'snapshot_id': member['share_group_snapshot_id'],
'share_id': member['share_id'],
'share_instance_id': member['share']['id'],
'id': member['id'],
'share': member['share'],
'size': member['share']['size'],
'share_size': member['share']['size'],
'share_proto': member['share']['share_proto'],
'provider_location': None,
}
try:
self.create_snapshot(context, share_snapshot,
share_server=share_server)
member_update = self.create_snapshot(
context, share_snapshot, share_server=share_server)
if member_update:
member_update['id'] = member['id']
snapshot_members_updates.append(member_update)
share_snapshots.append(share_snapshot)
except exception.ManilaException as e:
msg = _LE('Could not create share group snapshot. Failed '
@ -1381,8 +1393,7 @@ class ShareDriver(object):
LOG.debug('Successfully created share group snapshot %s.',
snap_dict['id'])
return None, None
return None, snapshot_members_updates
def delete_share_group_snapshot(self, context, snap_dict,
share_server=None):
@ -1417,7 +1428,8 @@ class ShareDriver(object):
'share_group_snapshot_id': 'some_fake_uuid',
'deleted_at': None,
'id': 'some_fake_uuid',
'size': 1
'size': 1,
'provider_location': 'fake_provider_location_value',
}
],
'deleted_at': None,
@ -1433,8 +1445,15 @@ class ShareDriver(object):
LOG.debug('Deleting share group snapshot %s.' % snap_dict['id'])
for member in snapshot_members:
share_snapshot = {
'snapshot_id': member['share_group_snapshot_id'],
'share_id': member['share_id'],
'share_instance_id': member['share']['id'],
'id': member['id'],
'share': member['share'],
'size': member['share']['size'],
'share_size': member['share']['size'],
'share_proto': member['share']['share_proto'],
'provider_location': member['provider_location'],
}
self.delete_snapshot(
context, share_snapshot, share_server=share_server)

View File

@ -3589,6 +3589,8 @@ class ShareManager(manager.SchedulerDependentManager):
member['share_id'] = member['share_instance_id']
status = constants.STATUS_AVAILABLE
now = timeutils.utcnow()
updated_members_ids = []
try:
LOG.info(_LI("Share group snapshot %s: creating"),
@ -3601,12 +3603,51 @@ class ShareManager(manager.SchedulerDependentManager):
self.driver.create_share_group_snapshot(
context, snap_ref, share_server=share_server))
if member_update_list:
snapshot_update = snapshot_update or {}
snapshot_update['share_group_snapshot_members'] = []
for update in (member_update_list or []):
snapshot_update['share_group_snapshot_members'].append(
update)
for update in (member_update_list or []):
# NOTE(vponomaryov): we expect that each member is a dict
# and has required 'id' key and some optional keys
# to be updated such as 'provider_location'. It is planned
# to have here also 'export_locations' when it is supported.
member_id = update.pop('id', None)
if not member_id:
LOG.warning(_LW(
"One of share group snapshot '%s' members does not "
"have reference ID. Its update was skipped."),
share_group_snapshot_id)
continue
# TODO(vponomaryov): remove following condition when
# sgs members start supporting export locations.
if 'export_locations' in update:
LOG.debug(
"Removing 'export_locations' data from "
"share group snapshot member '%s' update because "
"export locations are not supported.",
member_id)
update.pop('export_locations')
db_update = {
'updated_at': now,
'status': update.pop('status', status)
}
if 'provider_location' in update:
db_update['provider_location'] = (
update.pop('provider_location'))
if 'size' in update:
db_update['size'] = int(update.pop('size'))
updated_members_ids.append(member_id)
self.db.share_group_snapshot_member_update(
context, member_id, db_update)
if update:
LOG.debug(
"Share group snapshot ID='%(sgs_id)s', "
"share group snapshot member ID='%(sgsm_id)s'. "
"Following keys of sgs member were not updated "
"as not allowed: %(keys)s.",
{'sgs_id': share_group_snapshot_id,
'sgsm_id': member_id,
'keys': ', '.join(update)})
if snapshot_update:
snap_ref = self.db.share_group_snapshot_update(
@ -3621,15 +3662,16 @@ class ShareManager(manager.SchedulerDependentManager):
LOG.error(_LE("Share group snapshot %s: create failed"),
share_group_snapshot_id)
now = timeutils.utcnow()
for member in (snap_ref.get('share_group_snapshot_members') or []):
update = {'status': status, 'created_at': now}
if member['id'] in updated_members_ids:
continue
update = {'status': status, 'updated_at': now}
self.db.share_group_snapshot_member_update(
context, member['id'], update)
self.db.share_group_snapshot_update(
context, snap_ref['id'],
{'status': status, 'created_at': now})
{'status': status, 'updated_at': now})
LOG.info(_LI("Share group snapshot %s: created successfully"),
share_group_snapshot_id)

View File

@ -2016,3 +2016,96 @@ class ShareGroupMigrationChecks(BaseMigrationChecks):
self.cgsnapshot_id, member['cgsnapshot_id'])
self.test_case.assertIn('share_type_id', member)
self.test_case.assertEqual(self.share_type_id, member['share_type_id'])
@map_to_migration('927920b37453')
class ShareGroupSnapshotMemberNewProviderLocationColumnChecks(
BaseMigrationChecks):
table_name = 'share_group_snapshot_members'
share_group_type_id = uuidutils.generate_uuid()
share_group_id = uuidutils.generate_uuid()
share_id = uuidutils.generate_uuid()
share_instance_id = uuidutils.generate_uuid()
share_group_snapshot_id = uuidutils.generate_uuid()
share_group_snapshot_member_id = uuidutils.generate_uuid()
def setup_upgrade_data(self, engine):
# Setup share group type
sgt_data = {
'id': self.share_group_type_id,
'name': uuidutils.generate_uuid(),
}
sgt_table = utils.load_table('share_group_types', engine)
engine.execute(sgt_table.insert(sgt_data))
# Setup share group
sg_data = {
'id': self.share_group_id,
'project_id': 'fake_project_id',
'user_id': 'fake_user_id',
'share_group_type_id': self.share_group_type_id,
}
sg_table = utils.load_table('share_groups', engine)
engine.execute(sg_table.insert(sg_data))
# Setup shares
share_data = {
'id': self.share_id,
'share_group_id': self.share_group_id,
}
s_table = utils.load_table('shares', engine)
engine.execute(s_table.insert(share_data))
# Setup share instances
share_instance_data = {
'id': self.share_instance_id,
'share_id': share_data['id'],
'cast_rules_to_readonly': False,
}
si_table = utils.load_table('share_instances', engine)
engine.execute(si_table.insert(share_instance_data))
# Setup share group snapshot
sgs_data = {
'id': self.share_group_snapshot_id,
'share_group_id': self.share_group_id,
'project_id': 'fake_project_id',
'user_id': 'fake_user_id',
}
sgs_table = utils.load_table('share_group_snapshots', engine)
engine.execute(sgs_table.insert(sgs_data))
# Setup share group snapshot member
sgsm_data = {
'id': self.share_group_snapshot_member_id,
'share_group_snapshot_id': self.share_group_snapshot_id,
'share_id': self.share_id,
'share_instance_id': self.share_instance_id,
'project_id': 'fake_project_id',
'user_id': 'fake_user_id',
}
sgsm_table = utils.load_table(self.table_name, engine)
engine.execute(sgsm_table.insert(sgsm_data))
def check_upgrade(self, engine, data):
sgsm_table = utils.load_table(self.table_name, engine)
db_result = engine.execute(sgsm_table.select().where(
sgsm_table.c.id == self.share_group_snapshot_member_id))
self.test_case.assertEqual(1, db_result.rowcount)
for sgsm in db_result:
self.test_case.assertTrue(hasattr(sgsm, 'provider_location'))
# Check that we can write string data to the new field
engine.execute(sgsm_table.update().where(
sgsm_table.c.id == self.share_group_snapshot_member_id,
).values({
'provider_location': ('z' * 255),
}))
def check_downgrade(self, engine):
sgsm_table = utils.load_table(self.table_name, engine)
db_result = engine.execute(sgsm_table.select().where(
sgsm_table.c.id == self.share_group_snapshot_member_id))
self.test_case.assertEqual(1, db_result.rowcount)
for sgsm in db_result:
self.test_case.assertFalse(hasattr(sgsm, 'provider_location'))

View File

@ -239,6 +239,9 @@ class DummyDriver(driver.ShareDriver):
}
)
return {
'fake_key1': 'fake_value1',
'fake_key2': 'fake_value2',
'fake_key3': 'fake_value3',
"provider_location": mountpoint,
"export_locations": self._generate_export_locations(
mountpoint, share_server=share_server)
@ -257,6 +260,7 @@ class DummyDriver(driver.ShareDriver):
@slow_me_down
def delete_snapshot(self, context, snapshot, share_server=None):
"""Is called to remove snapshot."""
LOG.debug('Deleting snapshot with following data: %s', snapshot)
self.private_storage.delete(snapshot["id"])
@slow_me_down

View File

@ -806,16 +806,28 @@ class ShareDriverTestCase(test.TestCase):
def test_create_share_group_snapshot(self):
fake_snap_member_1 = {
'share_group_snapshot_id': 'fake_sg_snap_id',
'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e',
'share_id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2',
'status': 'bar',
'share_id': 'a3ebdba5-b4e1-46c8-a0ea-a9ac8daf5296',
'share_group_snapshot_id': 'fake_share_group_snapshot_id',
'share_instance_id': 'fake_share_instance_id_1',
'provider_location': 'should_not_be_used_1',
'share': {
'id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2',
'size': 3,
'share_proto': 'fake_share_proto',
},
}
fake_snap_member_2 = {
'share_group_snapshot_id': 'fake_sg_snap_id',
'id': '1e010dfe-545b-432d-ab95-4ef03cd82f89',
'share_id': 'a3ebdba5-b4e1-46c8-a0ea-a9ac8daf5296',
'status': 'foo',
'share_group_snapshot_id': 'fake_share_group_snapshot_id',
'share_instance_id': 'fake_share_instance_id_2',
'provider_location': 'should_not_be_used_2',
'share': {
'id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2',
'size': '2',
'share_proto': 'fake_share_proto',
},
}
fake_snap_dict = {
'status': 'available',
@ -832,7 +844,10 @@ class ShareDriverTestCase(test.TestCase):
}
share_driver = self._instantiate_share_driver(None, False)
share_driver._stats['share_group_snapshot_support'] = True
mock_create_snap = self.mock_object(share_driver, 'create_snapshot')
mock_create_snap = self.mock_object(
share_driver, 'create_snapshot',
mock.Mock(side_effect=lambda *args, **kwargs: {
'foo_k': 'foo_v', 'bar_k': 'bar_v_%s' % args[1]['id']}))
share_group_snapshot_update, member_update_list = (
share_driver.create_share_group_snapshot(
@ -841,32 +856,50 @@ class ShareDriverTestCase(test.TestCase):
mock_create_snap.assert_has_calls([
mock.call(
'fake_context',
{'id': fake_snap_member_1['id'],
'share_id': fake_snap_member_1['share_id'],
'snapshot_id': fake_snap_member_1['share_group_snapshot_id']},
share_server=None),
mock.call(
'fake_context',
{'id': fake_snap_member_2['id'],
'share_id': fake_snap_member_2['share_id'],
'snapshot_id': fake_snap_member_2['share_group_snapshot_id']},
share_server=None),
{'snapshot_id': member['share_group_snapshot_id'],
'share_id': member['share_id'],
'share_instance_id': member['share']['id'],
'id': member['id'],
'share': member['share'],
'size': member['share']['size'],
'share_size': member['share']['size'],
'share_proto': member['share']['share_proto'],
'provider_location': None},
share_server=None)
for member in (fake_snap_member_1, fake_snap_member_2)
])
self.assertIsNone(share_group_snapshot_update)
self.assertIsNone(member_update_list)
self.assertEqual(
[{'id': member['id'], 'foo_k': 'foo_v',
'bar_k': 'bar_v_%s' % member['id']}
for member in (fake_snap_member_1, fake_snap_member_2)],
member_update_list,
)
def test_create_share_group_snapshot_failed_snapshot(self):
fake_snap_member_1 = {
'share_group_snapshot_id': 'fake_sg_snap_id',
'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e',
'share_id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2',
'status': 'bar',
'share_id': 'a3ebdba5-b4e1-46c8-a0ea-a9ac8daf5296',
'share_group_snapshot_id': 'fake_share_group_snapshot_id',
'share_instance_id': 'fake_share_instance_id_1',
'provider_location': 'should_not_be_used_1',
'share': {
'id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2',
'size': 3,
'share_proto': 'fake_share_proto',
},
}
fake_snap_member_2 = {
'share_group_snapshot_id': 'fake_sg_snap_id',
'id': '1e010dfe-545b-432d-ab95-4ef03cd82f89',
'share_id': 'a3ebdba5-b4e1-46c8-a0ea-a9ac8daf5296',
'status': 'foo',
'share_group_snapshot_id': 'fake_share_group_snapshot_id',
'share_instance_id': 'fake_share_instance_id_2',
'provider_location': 'should_not_be_used_2',
'share': {
'id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2',
'size': '2',
'share_proto': 'fake_share_proto',
},
}
fake_snap_dict = {
'status': 'available',
@ -896,20 +929,30 @@ class ShareDriverTestCase(test.TestCase):
'fake_context', fake_snap_dict)
fake_snap_member_1_expected = {
'id': fake_snap_member_1['id'],
'share_id': fake_snap_member_1['share_id'],
'snapshot_id': fake_snap_member_1['share_group_snapshot_id'],
'share_id': fake_snap_member_1['share_id'],
'share_instance_id': fake_snap_member_1['share']['id'],
'id': fake_snap_member_1['id'],
'share': fake_snap_member_1['share'],
'size': fake_snap_member_1['share']['size'],
'share_size': fake_snap_member_1['share']['size'],
'share_proto': fake_snap_member_1['share']['share_proto'],
'provider_location': None,
}
mock_create_snap.assert_has_calls([
mock.call(
'fake_context', fake_snap_member_1_expected, share_server=None,
),
mock.call(
'fake_context',
{'id': fake_snap_member_2['id'],
'share_id': fake_snap_member_2['share_id'],
'snapshot_id': fake_snap_member_2['share_group_snapshot_id']},
share_server=None),
{'snapshot_id': member['share_group_snapshot_id'],
'share_id': member['share_id'],
'share_instance_id': member['share']['id'],
'id': member['id'],
'share': member['share'],
'size': member['share']['size'],
'share_size': member['share']['size'],
'share_proto': member['share']['share_proto'],
'provider_location': None},
share_server=None)
for member in (fake_snap_member_1, fake_snap_member_2)
])
mock_delete_snap.assert_called_with(
'fake_context', fake_snap_member_1_expected, share_server=None)
@ -975,11 +1018,27 @@ class ShareDriverTestCase(test.TestCase):
def test_delete_share_group_snapshot(self):
fake_snap_member_1 = {
'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e',
'share_id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2'
'share_id': 'a3ebdba5-b4e1-46c8-a0ea-a9ac8daf5296',
'share_group_snapshot_id': 'fake_share_group_snapshot_id',
'share_instance_id': 'fake_share_instance_id_1',
'provider_location': 'fake_provider_location_2',
'share': {
'id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2',
'size': 3,
'share_proto': 'fake_share_proto',
},
}
fake_snap_member_2 = {
'id': '1e010dfe-545b-432d-ab95-4ef03cd82f89',
'share_id': 'a3ebdba5-b4e1-46c8-a0ea-a9ac8daf5296'
'share_id': 'a3ebdba5-b4e1-46c8-a0ea-a9ac8daf5296',
'share_group_snapshot_id': 'fake_share_group_snapshot_id',
'share_instance_id': 'fake_share_instance_id_2',
'provider_location': 'fake_provider_location_2',
'share': {
'id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2',
'size': '2',
'share_proto': 'fake_share_proto',
},
}
fake_snap_dict = {
'status': 'available',
@ -1004,8 +1063,19 @@ class ShareDriverTestCase(test.TestCase):
'fake_context', fake_snap_dict))
mock_delete_snap.assert_has_calls([
mock.call('fake_context', fake_snap_member_1, share_server=None),
mock.call('fake_context', fake_snap_member_2, share_server=None),
mock.call(
'fake_context',
{'snapshot_id': member['share_group_snapshot_id'],
'share_id': member['share_id'],
'share_instance_id': member['share']['id'],
'id': member['id'],
'share': member['share'],
'size': member['share']['size'],
'share_size': member['share']['size'],
'share_proto': member['share']['share_proto'],
'provider_location': member['provider_location']},
share_server=None)
for member in (fake_snap_member_1, fake_snap_member_2)
])
self.assertIsNone(share_group_snapshot_update)
self.assertIsNone(member_update_list)

View File

@ -3580,7 +3580,7 @@ class ShareManagerTestCase(test.TestCase):
mock_sg_snap_update.assert_called_once_with(
mock.ANY, fake_snap['id'],
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
{'status': constants.STATUS_AVAILABLE, 'updated_at': mock.ANY})
def test_create_share_group_snapshot_with_update(self):
fake_snap = {'id': 'fake_snap_id', 'share_group': {},
@ -3600,39 +3600,96 @@ class ShareManagerTestCase(test.TestCase):
mock.ANY, 'fake_snap_id', {'foo': 'bar'})
self.share_manager.db.share_group_snapshot_update.assert_any_call(
mock.ANY, fake_snap['id'],
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
{'status': constants.STATUS_AVAILABLE, 'updated_at': mock.ANY})
def test_create_share_group_snapshot_with_member_update(self):
fake_member = {'id': 'fake_member_id', 'share_instance_id': 'blah'}
fake_member_update = {'id': 'fake_member_id', 'foo': 'bar'}
fake_snap = {'id': 'fake_snap_id', 'share_group': {},
'share_group_snapshot_members': [fake_member]}
fake_member1 = {'id': 'fake_member_id_1', 'share_instance_id': 'si_1'}
fake_member2 = {'id': 'fake_member_id_2', 'share_instance_id': 'si_2'}
fake_member3 = {'id': 'fake_member_id_3', 'share_instance_id': 'si_3'}
fake_member_update1 = {
'id': fake_member1['id'],
'provider_location': 'fake_provider_location_1',
'size': 13,
'export_locations': ['fake_el_1_1', 'fake_el_1_2'],
'should_not_be_used_k1': 'should_not_be_used_v1',
}
fake_member_update2 = {
'id': fake_member2['id'],
'provider_location': 'fake_provider_location_2',
'size': 31,
'export_locations': ['fake_el_2_1', 'fake_el_2_2'],
'status': 'fake_status_for_update',
'should_not_be_used_k2': 'should_not_be_used_k2',
}
fake_member_update3 = {
'provider_location': 'fake_provider_location_3',
'size': 42,
'export_locations': ['fake_el_3_1', 'fake_el_3_2'],
'should_not_be_used_k3': 'should_not_be_used_k3',
}
expected_member_update1 = {
'id': fake_member_update1['id'],
'provider_location': fake_member_update1['provider_location'],
'size': fake_member_update1['size'],
}
expected_member_update2 = {
'id': fake_member_update2['id'],
'provider_location': fake_member_update2['provider_location'],
'size': fake_member_update2['size'],
'status': fake_member_update2['status'],
}
fake_snap = {
'id': 'fake_snap_id',
'share_group': {},
'share_group_snapshot_members': [
fake_member1, fake_member2, fake_member3],
}
self.mock_object(
self.share_manager.db, 'share_group_snapshot_get',
mock.Mock(return_value=fake_snap))
self.mock_object(
mock_sg_snapshot_update = self.mock_object(
self.share_manager.db, 'share_group_snapshot_update',
mock.Mock(return_value=fake_snap))
self.mock_object(
mock_sg_snapshot_member_update = self.mock_object(
self.share_manager.db, 'share_group_snapshot_member_update')
self.mock_object(
self.share_manager.db, 'share_instance_get',
mock.Mock(return_value={'id': 'blah'}))
self.mock_object(
timeutils, 'utcnow', mock.Mock(side_effect=range(1, 10)))
mock_driver_create_sg_snapshot = self.mock_object(
self.share_manager.driver, 'create_share_group_snapshot',
mock.Mock(return_value=(None, [fake_member_update])))
mock.Mock(return_value=(
None, [fake_member_update1, fake_member_update2,
fake_member_update3])))
self.share_manager.create_share_group_snapshot(
self.context, fake_snap['id'])
self.share_manager.db.share_group_snapshot_update.assert_any_call(
mock_driver_create_sg_snapshot.assert_called_once_with(
utils.IsAMatcher(context.RequestContext),
fake_snap, share_server=None)
mock_sg_snapshot_update.assert_called_once_with(
mock.ANY, fake_snap['id'],
{'share_group_snapshot_members': [fake_member_update]})
self.share_manager.db.share_group_snapshot_update.assert_any_call(
mock.ANY, fake_snap['id'],
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
self.assertTrue(
self.share_manager.db.share_group_snapshot_member_update.called)
{'status': constants.STATUS_AVAILABLE, 'updated_at': mock.ANY})
mock_sg_snapshot_member_update.assert_has_calls([
mock.call(
utils.IsAMatcher(context.RequestContext),
expected_member_update1['id'],
{'provider_location': expected_member_update1[
'provider_location'],
'size': expected_member_update1['size'],
'updated_at': 1,
'status': manager.constants.STATUS_AVAILABLE}),
mock.call(
utils.IsAMatcher(context.RequestContext),
expected_member_update2['id'],
{'provider_location': expected_member_update2[
'provider_location'],
'size': expected_member_update2['size'],
'updated_at': 1,
'status': expected_member_update2['status']}),
])
def test_create_group_snapshot_with_error(self):
fake_snap = {'id': 'fake_snap_id', 'share_group': {},

View File

@ -0,0 +1,6 @@
---
fixes:
- Fixed default approach for creating share group snapshots that uses
common share driver interface by making proper call of this method.
Before, some drivers that were depending on some specific data
from 'snapshot' object were failing not being able to get these data.