ProphetStor: Support generic group

Add CG capability to Groups in the ProphetStor driver.

Change-Id: I4d0c2d8d9add815d5d0e4e4f3e36d7c1ace5f3b5
Close-Bug: #1682243
This commit is contained in:
rick.chen 2017-06-19 16:02:33 +08:00
parent 1fa43b8f79
commit 3cc8eef15d
3 changed files with 384 additions and 192 deletions

View File

@ -12,7 +12,6 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import copy
import errno import errno
import re import re
@ -20,18 +19,21 @@ import mock
from oslo_utils import units from oslo_utils import units
from six.moves import http_client from six.moves import http_client
from cinder import context
from cinder import exception from cinder import exception
from cinder.objects import fields from cinder.objects import fields
from cinder import test from cinder import test
from cinder.tests.unit import fake_constants
from cinder.tests.unit import fake_snapshot from cinder.tests.unit import fake_snapshot
from cinder.tests.unit import utils as test_utils
from cinder.volume import configuration as conf from cinder.volume import configuration as conf
from cinder.volume.drivers.prophetstor import dpl_iscsi as DPLDRIVER from cinder.volume.drivers.prophetstor import dpl_iscsi as DPLDRIVER
from cinder.volume.drivers.prophetstor import dplcommon as DPLCOMMON from cinder.volume.drivers.prophetstor import dplcommon as DPLCOMMON
from cinder.volume import group_types
POOLUUID = 'ac33fc6e417440d5a1ef27d7231e1cc4' POOLUUID = 'ac33fc6e417440d5a1ef27d7231e1cc4'
VOLUMEUUID = 'a000000000000000000000000000001' VOLUMEUUID = 'a000000000000000000000000000001'
INITIATOR = 'iqn.2013-08.org.debian:01:aaaaaaaa' INITIATOR = 'iqn.2013-08.org.debian:01:aaaaaaaa'
DATA_IN_VOLUME = {'id': VOLUMEUUID}
DATA_IN_CONNECTOR = {'initiator': INITIATOR} DATA_IN_CONNECTOR = {'initiator': INITIATOR}
DATA_SERVER_INFO = 0, { DATA_SERVER_INFO = 0, {
'metadata': {'vendor': 'ProphetStor', 'metadata': {'vendor': 'ProphetStor',
@ -101,17 +103,17 @@ DATA_IN_GROUP = {'id': 'fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
'description': 'des123', 'description': 'des123',
'status': ''} 'status': ''}
DATA_IN_VOLUME = {'id': 'abc123', DATA_IN_VOLUME = {'id': 'c11e902-87e9-348d-0b48-89695f1ec4be5',
'display_name': 'abc123', 'display_name': 'abc123',
'display_description': '', 'display_description': '',
'size': 10, 'size': 10,
'host': "hostname@backend#%s" % POOLUUID} 'host': "hostname@backend#%s" % POOLUUID}
DATA_IN_VOLUME_VG = {'id': 'abc123', DATA_IN_VOLUME_VG = {'id': 'fe2dbc5-1581-0451-dab2-f8c8a48d15bee',
'display_name': 'abc123', 'display_name': 'abc123',
'display_description': '', 'display_description': '',
'size': 10, 'size': 10,
'consistencygroup_id': 'group_id':
'fe2dbc51-5810-451d-ab2f-8c8a48d15bee', 'fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
'status': 'available', 'status': 'available',
'host': "hostname@backend#%s" % POOLUUID} 'host': "hostname@backend#%s" % POOLUUID}
@ -121,35 +123,35 @@ DATA_IN_REMOVE_VOLUME_VG = {
'display_name': 'fe2dbc515810451dab2f8c8a48d15bee', 'display_name': 'fe2dbc515810451dab2f8c8a48d15bee',
'display_description': '', 'display_description': '',
'size': 10, 'size': 10,
'consistencygroup_id': 'fe2dbc51-5810-451d-ab2f-8c8a48d15bee', 'group_id': 'fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
'status': 'available', 'status': 'available',
'host': "hostname@backend#%s" % POOLUUID} 'host': "hostname@backend#%s" % POOLUUID}
DATA_IN_VOLUME1 = {'id': 'abc456', DATA_IN_VOLUME1 = {'id': 'c11e902-87e9-348d-0b48-89695f1ec4bef',
'display_name': 'abc456', 'display_name': 'abc456',
'display_description': '', 'display_description': '',
'size': 10, 'size': 10,
'host': "hostname@backend#%s" % POOLUUID} 'host': "hostname@backend#%s" % POOLUUID}
DATA_IN_CG_SNAPSHOT = { DATA_IN_CG_SNAPSHOT = {
'consistencygroup_id': 'fe2dbc51-5810-451d-ab2f-8c8a48d15bee', 'group_id': 'fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
'id': 'cgsnapshot1', 'id': 'cgsnapshot1',
'name': 'cgsnapshot1', 'name': 'cgsnapshot1',
'description': 'cgsnapshot1', 'description': 'cgsnapshot1',
'status': ''} 'status': ''}
DATA_IN_SNAPSHOT = {'id': 'snapshot1', DATA_IN_SNAPSHOT = {'id': 'fe2dbc5-1581-0451-dab2-f8c8a48d15bee',
'volume_id': 'abc123', 'volume_id': 'c11e902-87e9-348d-0b48-89695f1ec4be5',
'display_name': 'snapshot1', 'display_name': 'snapshot1',
'display_description': '', 'display_description': '',
'volume_size': 5} 'volume_size': 5}
DATA_OUT_SNAPSHOT_CG = { DATA_OUT_SNAPSHOT_CG = {
'id': 'snapshot1', 'id': 'snapshot1',
'volume_id': 'abc123', 'volume_id': 'c11e902-87e9-348d-0b48-89695f1ec4be5',
'display_name': 'snapshot1', 'display_name': 'snapshot1',
'display_description': '', 'display_description': '',
'cgsnapshot_id': 'fe2dbc51-5810-451d-ab2f-8c8a48d15bee'} 'group_snapshot_id': 'fe2dbc51-5810-451d-ab2f-8c8a48d15bee'}
DATA_OUT_CG = { DATA_OUT_CG = {
"objectType": "application/cdmi-container", "objectType": "application/cdmi-container",
@ -183,7 +185,7 @@ DATA_OUT_CG = {
"childrenrange": "<range>", "childrenrange": "<range>",
"children": "children":
[ [
"fe2dbc515810451dab2f8c8a48d15bee", 'fe2dbc515810451dab2f8c8a48d15bee',
], ],
} }
@ -491,7 +493,7 @@ class TestProphetStorDPLDriver(test.TestCase):
self.configuration.san_thin_provision = True self.configuration.san_thin_provision = True
self.configuration.driver_ssl_cert_verify = False self.configuration.driver_ssl_cert_verify = False
self.configuration.driver_ssl_cert_path = None self.configuration.driver_ssl_cert_path = None
self.context = '' self.context = context.get_admin_context()
self.DPL_MOCK = mock.MagicMock() self.DPL_MOCK = mock.MagicMock()
self.DB_MOCK = mock.MagicMock() self.DB_MOCK = mock.MagicMock()
self.dpldriver = DPLDRIVER.DPLISCSIDriver( self.dpldriver = DPLDRIVER.DPLISCSIDriver(
@ -513,99 +515,164 @@ class TestProphetStorDPLDriver(test.TestCase):
self.assertFalse(pool['QoS_support']) self.assertFalse(pool['QoS_support'])
def test_create_volume(self): def test_create_volume(self):
volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME['id'],
display_name=DATA_IN_VOLUME['display_name'],
size=DATA_IN_VOLUME['size'],
host=DATA_IN_VOLUME['host'])
self.DPL_MOCK.create_vdev.return_value = DATA_OUTPUT self.DPL_MOCK.create_vdev.return_value = DATA_OUTPUT
self.dpldriver.create_volume(DATA_IN_VOLUME) self.dpldriver.create_volume(volume)
self.DPL_MOCK.create_vdev.assert_called_once_with( self.DPL_MOCK.create_vdev.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_VOLUME['id']), self._conver_uuid2hex(volume.id),
DATA_IN_VOLUME['display_name'], volume.display_name,
DATA_IN_VOLUME['display_description'], volume.display_description,
self.configuration.dpl_pool, self.configuration.dpl_pool,
int(DATA_IN_VOLUME['size']) * units.Gi, int(volume.size) * units.Gi,
True) True)
def test_create_volume_without_pool(self): def test_create_volume_without_pool(self):
fake_volume = copy.deepcopy(DATA_IN_VOLUME) volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME['id'],
display_name=DATA_IN_VOLUME['display_name'],
size=DATA_IN_VOLUME['size'],
host=DATA_IN_VOLUME['host'])
self.DPL_MOCK.create_vdev.return_value = DATA_OUTPUT self.DPL_MOCK.create_vdev.return_value = DATA_OUTPUT
self.configuration.dpl_pool = "" self.configuration.dpl_pool = ""
fake_volume['host'] = "host@backend" # missing pool volume.host = "host@backend" # missing pool
self.assertRaises(exception.InvalidHost, self.dpldriver.create_volume, self.assertRaises(exception.InvalidHost, self.dpldriver.create_volume,
volume=fake_volume) volume=volume)
def test_create_volume_with_configuration_pool(self): def test_create_volume_with_configuration_pool(self):
fake_volume = copy.deepcopy(DATA_IN_VOLUME) volume = test_utils.create_volume(
fake_volume['host'] = "host@backend" # missing pool self.context,
id=DATA_IN_VOLUME['id'],
display_name=DATA_IN_VOLUME['display_name'],
size=DATA_IN_VOLUME['size'],
host="host@backend")
self.DPL_MOCK.create_vdev.return_value = DATA_OUTPUT self.DPL_MOCK.create_vdev.return_value = DATA_OUTPUT
self.dpldriver.create_volume(fake_volume) self.dpldriver.create_volume(volume)
self.DPL_MOCK.create_vdev.assert_called_once_with( self.DPL_MOCK.create_vdev.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_VOLUME['id']), self._conver_uuid2hex(volume.id),
DATA_IN_VOLUME['display_name'], volume.display_name, volume.display_description,
DATA_IN_VOLUME['display_description'], self.configuration.dpl_pool, int(volume.size) * units.Gi, True)
self.configuration.dpl_pool,
int(DATA_IN_VOLUME['size']) * units.Gi,
True)
def test_create_volume_of_group(self): def test_create_volume_of_group(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
group = test_utils.create_group(
self.context,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
self.DPL_MOCK.create_vdev.return_value = DATA_OUTPUT self.DPL_MOCK.create_vdev.return_value = DATA_OUTPUT
self.DPL_MOCK.join_vg.return_value = DATA_OUTPUT self.DPL_MOCK.join_vg.return_value = DATA_OUTPUT
self.dpldriver.create_volume(DATA_IN_VOLUME_VG) volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME_VG['id'],
display_name=DATA_IN_VOLUME_VG['display_name'],
size=DATA_IN_VOLUME_VG['size'],
group_id=group.id,
host=DATA_IN_VOLUME_VG['host'])
self.dpldriver.create_volume(volume)
self.DPL_MOCK.create_vdev.assert_called_once_with( self.DPL_MOCK.create_vdev.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_VOLUME['id']), self._conver_uuid2hex(volume.id),
DATA_IN_VOLUME['display_name'], volume.display_name,
DATA_IN_VOLUME['display_description'], volume.display_description,
self.configuration.dpl_pool, self.configuration.dpl_pool,
int(DATA_IN_VOLUME['size']) * units.Gi, int(volume.size) * units.Gi,
True) True)
self.DPL_MOCK.join_vg.assert_called_once_with( self.DPL_MOCK.join_vg.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_VOLUME_VG['id']), self._conver_uuid2hex(volume.id),
self._conver_uuid2hex( self._conver_uuid2hex(volume.group_id))
DATA_IN_VOLUME_VG['consistencygroup_id']))
def test_delete_volume(self): def test_delete_volume(self):
volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME['id'],
display_name=DATA_IN_VOLUME['display_name'],
size=DATA_IN_VOLUME['size'],
host=DATA_IN_VOLUME['host'])
self.DPL_MOCK.delete_vdev.return_value = DATA_OUTPUT self.DPL_MOCK.delete_vdev.return_value = DATA_OUTPUT
self.dpldriver.delete_volume(DATA_IN_VOLUME) self.dpldriver.delete_volume(volume)
self.DPL_MOCK.delete_vdev.assert_called_once_with( self.DPL_MOCK.delete_vdev.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_VOLUME['id'])) self._conver_uuid2hex(volume.id))
def test_delete_volume_of_group(self): def test_delete_volume_of_group(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
group = test_utils.create_group(
self.context,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME_VG['id'],
display_name=DATA_IN_VOLUME_VG['display_name'],
size=DATA_IN_VOLUME_VG['size'],
group_id=group.id,
host=DATA_IN_VOLUME_VG['host'])
self.DPL_MOCK.delete_vdev.return_value = DATA_OUTPUT self.DPL_MOCK.delete_vdev.return_value = DATA_OUTPUT
self.DPL_MOCK.leave_vg.return_volume = DATA_OUTPUT self.DPL_MOCK.leave_vg.return_volume = DATA_OUTPUT
self.dpldriver.delete_volume(DATA_IN_VOLUME_VG) self.dpldriver.delete_volume(volume)
self.DPL_MOCK.leave_vg.assert_called_once_with( self.DPL_MOCK.leave_vg.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_VOLUME_VG['id']), self._conver_uuid2hex(volume.id),
self._conver_uuid2hex(DATA_IN_GROUP['id']) self._conver_uuid2hex(volume.group_id)
) )
self.DPL_MOCK.delete_vdev.assert_called_once_with( self.DPL_MOCK.delete_vdev.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_VOLUME['id'])) self._conver_uuid2hex(volume.id))
def test_create_volume_from_snapshot(self): def test_create_volume_from_snapshot(self):
self.DPL_MOCK.create_vdev_from_snapshot.return_value = DATA_OUTPUT self.DPL_MOCK.create_vdev_from_snapshot.return_value = DATA_OUTPUT
self.DPL_MOCK.extend_vdev.return_value = DATA_OUTPUT self.DPL_MOCK.extend_vdev.return_value = DATA_OUTPUT
self.dpldriver.create_volume_from_snapshot(DATA_IN_VOLUME, volume = test_utils.create_volume(
DATA_IN_SNAPSHOT) self.context,
id=DATA_IN_VOLUME_VG['id'],
display_name=DATA_IN_VOLUME_VG['display_name'],
size=DATA_IN_VOLUME_VG['size'],
host=DATA_IN_VOLUME_VG['host'])
self.dpldriver.create_volume_from_snapshot(
volume, DATA_IN_SNAPSHOT)
self.DPL_MOCK.create_vdev_from_snapshot.assert_called_once_with( self.DPL_MOCK.create_vdev_from_snapshot.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_VOLUME['id']), self._conver_uuid2hex(volume.id),
DATA_IN_VOLUME['display_name'], volume.display_name,
DATA_IN_VOLUME['display_description'], volume.display_description,
self._conver_uuid2hex(DATA_IN_SNAPSHOT['id']), self._conver_uuid2hex(volume.id),
self.configuration.dpl_pool, self.configuration.dpl_pool,
True) True)
self.DPL_MOCK.extend_vdev.assert_called_once_with( self.DPL_MOCK.extend_vdev.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_VOLUME['id']), self._conver_uuid2hex(volume.id),
DATA_IN_VOLUME['display_name'], volume.display_name,
DATA_IN_VOLUME['display_description'], volume.display_description,
DATA_IN_VOLUME['size'] * units.Gi) volume.size * units.Gi)
def test_create_cloned_volume(self): def test_create_cloned_volume(self):
new_volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME1['id'],
display_name=DATA_IN_VOLUME1['display_name'],
size=DATA_IN_VOLUME1['size'],
host=DATA_IN_VOLUME1['host'])
src_volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME['id'])
self.DPL_MOCK.clone_vdev.return_value = DATA_OUTPUT self.DPL_MOCK.clone_vdev.return_value = DATA_OUTPUT
self.dpldriver.create_cloned_volume(DATA_IN_VOLUME1, DATA_IN_VOLUME) self.dpldriver.create_cloned_volume(new_volume, src_volume)
self.DPL_MOCK.clone_vdev.assert_called_once_with( self.DPL_MOCK.clone_vdev.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_VOLUME['id']), self._conver_uuid2hex(src_volume.id),
self._conver_uuid2hex(DATA_IN_VOLUME1['id']), self._conver_uuid2hex(new_volume.id),
self.configuration.dpl_pool, self.configuration.dpl_pool,
DATA_IN_VOLUME1['display_name'], new_volume.display_name,
DATA_IN_VOLUME1['display_description'], new_volume.display_description,
int(DATA_IN_VOLUME1['size']) * int(new_volume.size) *
units.Gi, units.Gi,
True) True)
@ -686,95 +753,179 @@ class TestProphetStorDPLDriver(test.TestCase):
self.assertEqual(4294967296, res['metadata']['total_capacity']) self.assertEqual(4294967296, res['metadata']['total_capacity'])
self.assertEqual('8173612007304181810', res['metadata']['zpool_guid']) self.assertEqual('8173612007304181810', res['metadata']['zpool_guid'])
def test_create_consistency_group(self): def test_create_group(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
group = test_utils.create_group(
self.context,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
self.DPL_MOCK.create_vg.return_value = DATA_OUTPUT self.DPL_MOCK.create_vg.return_value = DATA_OUTPUT
model_update = self.dpldriver.create_consistencygroup(self.context, model_update = self.dpldriver.create_group(self.context, group)
DATA_IN_GROUP)
self.DPL_MOCK.create_vg.assert_called_once_with( self.DPL_MOCK.create_vg.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_GROUP['id']), DATA_IN_GROUP['name'], self._conver_uuid2hex(fake_constants.CONSISTENCY_GROUP_ID),
DATA_IN_GROUP['description']) 'test_group',
'this is a test group')
self.assertDictEqual({'status': ( self.assertDictEqual({'status': (
fields.ConsistencyGroupStatus.AVAILABLE)}, model_update) fields.ConsistencyGroupStatus.AVAILABLE)}, model_update)
def test_delete_consistency_group(self): def test_delete_group(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
group = test_utils.create_group(
self.context,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
self.DB_MOCK.volume_get_all_by_group.return_value = ( self.DB_MOCK.volume_get_all_by_group.return_value = (
[DATA_IN_VOLUME_VG]) [DATA_IN_VOLUME_VG])
self.DPL_MOCK.delete_vdev.return_value = DATA_OUTPUT self.DPL_MOCK.delete_vdev.return_value = DATA_OUTPUT
self.DPL_MOCK.delete_cg.return_value = DATA_OUTPUT self.DPL_MOCK.delete_cg.return_value = DATA_OUTPUT
model_update, volumes = self.dpldriver.delete_consistencygroup( model_update, volumes = self.dpldriver.delete_group(
self.context, DATA_IN_GROUP, []) self.context, group, [])
self.DPL_MOCK.delete_vg.assert_called_once_with( self.DPL_MOCK.delete_vg.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_GROUP['id'])) self._conver_uuid2hex(fake_constants.CONSISTENCY_GROUP_ID))
self.DPL_MOCK.delete_vdev.assert_called_once_with( self.DPL_MOCK.delete_vdev.assert_called_once_with(
self._conver_uuid2hex((DATA_IN_VOLUME_VG['id']))) self._conver_uuid2hex((DATA_IN_VOLUME_VG['id'])))
self.assertDictEqual({'status': ( self.assertDictEqual({'status': (
fields.ConsistencyGroupStatus.DELETED)}, model_update) fields.ConsistencyGroupStatus.DELETED)}, model_update)
def test_update_consistencygroup(self): def test_update_group(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
self.DPL_MOCK.get_vg.return_value = (0, DATA_OUT_CG) self.DPL_MOCK.get_vg.return_value = (0, DATA_OUT_CG)
self.DPL_MOCK.join_vg.return_value = DATA_OUTPUT self.DPL_MOCK.join_vg.return_value = DATA_OUTPUT
self.DPL_MOCK.leave_vg.return_value = DATA_OUTPUT self.DPL_MOCK.leave_vg.return_value = DATA_OUTPUT
add_vol = DATA_IN_VOLUME_VG group = test_utils.create_group(
remove_vol = DATA_IN_REMOVE_VOLUME_VG self.context,
id='fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
vol_add = test_utils.create_volume(
self.context,
id=fake_constants.VOLUME2_ID,
display_name=DATA_IN_VOLUME_VG['display_name'],
size=DATA_IN_VOLUME_VG['size'],
group_id='fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
host=DATA_IN_VOLUME_VG['host'])
vol_del = test_utils.create_volume(
self.context,
id=DATA_IN_REMOVE_VOLUME_VG['id'],
display_name=DATA_IN_REMOVE_VOLUME_VG['display_name'],
size=DATA_IN_REMOVE_VOLUME_VG['size'],
group_id='fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
host=DATA_IN_REMOVE_VOLUME_VG['host'])
(model_update, add_vols, remove_vols) = ( (model_update, add_vols, remove_vols) = (
self.dpldriver.update_consistencygroup(self.context, self.dpldriver.update_group(
DATA_IN_GROUP, self.context, group, [vol_add], [vol_del]))
[add_vol],
[remove_vol]))
self.DPL_MOCK.join_vg.assert_called_once_with( self.DPL_MOCK.join_vg.assert_called_once_with(
self._conver_uuid2hex(add_vol['id']), self._conver_uuid2hex(vol_add.id),
self._conver_uuid2hex(DATA_IN_GROUP['id'])) self._conver_uuid2hex(group.id))
self.DPL_MOCK.leave_vg.assert_called_once_with( self.DPL_MOCK.leave_vg.assert_called_once_with(
self._conver_uuid2hex(remove_vol['id']), self._conver_uuid2hex(vol_del.id),
self._conver_uuid2hex(DATA_IN_GROUP['id'])) self._conver_uuid2hex(group.id))
self.assertDictEqual({'status': ( self.assertDictEqual({'status': (
fields.ConsistencyGroupStatus.AVAILABLE)}, model_update) fields.ConsistencyGroupStatus.AVAILABLE)}, model_update)
def test_update_consistencygroup_exception_join(self): def test_update_group_exception_join(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
self.DPL_MOCK.get_vg.return_value = (0, DATA_OUT_CG) self.DPL_MOCK.get_vg.return_value = (0, DATA_OUT_CG)
self.DPL_MOCK.join_vg.return_value = -1, None self.DPL_MOCK.join_vg.return_value = -1, None
self.DPL_MOCK.leave_vg.return_value = DATA_OUTPUT self.DPL_MOCK.leave_vg.return_value = DATA_OUTPUT
add_vol = DATA_IN_VOLUME_VG volume = test_utils.create_volume(
self.context,
id=fake_constants.VOLUME2_ID,
display_name=DATA_IN_VOLUME_VG['display_name'],
size=DATA_IN_VOLUME_VG['size'],
host=DATA_IN_VOLUME_VG['host'])
group = test_utils.create_group(
self.context,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.dpldriver.update_consistencygroup, self.dpldriver.update_group,
context=None, context=None,
group=DATA_IN_GROUP, group=group,
add_volumes=[add_vol], add_volumes=[volume],
remove_volumes=None) remove_volumes=None)
def test_update_consistencygroup_exception_leave(self): def test_update_group_exception_leave(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
self.DPL_MOCK.get_vg.return_value = (0, DATA_OUT_CG) self.DPL_MOCK.get_vg.return_value = (0, DATA_OUT_CG)
self.DPL_MOCK.leave_vg.return_value = -1, None self.DPL_MOCK.leave_vg.return_value = -1, None
remove_vol = DATA_IN_REMOVE_VOLUME_VG volume = test_utils.create_volume(
self.context,
id='fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
display_name=DATA_IN_VOLUME_VG['display_name'],
size=DATA_IN_VOLUME_VG['size'],
host=DATA_IN_VOLUME_VG['host'])
group = test_utils.create_group(
self.context,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.dpldriver.update_consistencygroup, self.dpldriver.update_group,
context=None, context=None,
group=DATA_IN_GROUP, group=group,
add_volumes=None, add_volumes=None,
remove_volumes=[remove_vol]) remove_volumes=[volume])
@mock.patch('cinder.objects.snapshot.SnapshotList.get_all_for_cgsnapshot') @mock.patch(
def test_create_consistency_group_snapshot(self, get_all_for_cgsnapshot): 'cinder.objects.snapshot.SnapshotList.get_all_for_group_snapshot')
def test_create_group_snapshot(self, get_all_for_group_snapshot):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
snapshot_obj = fake_snapshot.fake_snapshot_obj(self.context) snapshot_obj = fake_snapshot.fake_snapshot_obj(self.context)
snapshot_obj.consistencygroup_id = \ snapshot_obj.group_id = \
DATA_IN_CG_SNAPSHOT['consistencygroup_id'] DATA_IN_CG_SNAPSHOT['group_id']
get_all_for_cgsnapshot.return_value = [snapshot_obj] snapshot_obj.group_type_id = group_type.id
get_all_for_group_snapshot.return_value = [snapshot_obj]
self.DPL_MOCK.create_vdev_snapshot.return_value = DATA_OUTPUT self.DPL_MOCK.create_vdev_snapshot.return_value = DATA_OUTPUT
model_update, snapshots = self.dpldriver.create_cgsnapshot( model_update, snapshots = self.dpldriver.create_group_snapshot(
self.context, snapshot_obj, []) self.context, snapshot_obj, [])
self.assertDictEqual({'status': 'available'}, model_update) self.assertDictEqual({'status': 'available'}, model_update)
@mock.patch('cinder.objects.snapshot.SnapshotList.get_all_for_cgsnapshot') @mock.patch(
def test_delete_consistency_group_snapshot(self, get_all_for_cgsnapshot): 'cinder.objects.snapshot.SnapshotList.get_all_for_group_snapshot')
def test_delete_group_snapshot(self, get_all_for_group_snapshot):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
snapshot_obj = fake_snapshot.fake_snapshot_obj(self.context) snapshot_obj = fake_snapshot.fake_snapshot_obj(self.context)
snapshot_obj.consistencygroup_id = \ snapshot_obj.group_id = \
DATA_IN_CG_SNAPSHOT['consistencygroup_id'] DATA_IN_CG_SNAPSHOT['group_id']
get_all_for_cgsnapshot.return_value = [snapshot_obj] snapshot_obj.group_type_id = group_type.id
self.DPL_MOCK.delete_cgsnapshot.return_value = DATA_OUTPUT get_all_for_group_snapshot.return_value = [snapshot_obj]
model_update, snapshots = self.dpldriver.delete_cgsnapshot( self.DPL_MOCK.delete_group_snapshot.return_value = DATA_OUTPUT
self.context, DATA_IN_CG_SNAPSHOT, []) model_update, snapshots = self.dpldriver.delete_group_snapshot(
self.context, snapshot_obj, [])
self.DPL_MOCK.delete_vdev_snapshot.assert_called_once_with( self.DPL_MOCK.delete_vdev_snapshot.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_CG_SNAPSHOT['consistencygroup_id']), self._conver_uuid2hex(snapshot_obj.group_id),
self._conver_uuid2hex(DATA_IN_CG_SNAPSHOT['id']), self._conver_uuid2hex(snapshot_obj.id),
True) True)
self.assertDictEqual({'status': 'deleted'}, model_update) self.assertDictEqual({'status': 'deleted'}, model_update)

View File

@ -844,44 +844,44 @@ class DPLCOMMONDriver(driver.CloneableImageVD,
def remove_export(self, context, volume): def remove_export(self, context, volume):
pass pass
def create_consistencygroup(self, context, group): def _create_consistencygroup(self, context, group):
"""Creates a consistencygroup.""" """Creates a consistencygroup."""
LOG.info('Start to create consistency group: %(group_name)s ' LOG.info('Start to create consistency group: %(group_name)s '
'id: %(id)s', 'id: %(id)s',
{'group_name': group['name'], 'id': group['id']}) {'group_name': group.name, 'id': group.id})
model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE} model_update = {'status': fields.GroupStatus.AVAILABLE}
try: try:
ret, output = self.dpl.create_vg( ret, output = self.dpl.create_vg(
self._conver_uuid2hex(group['id']), self._conver_uuid2hex(group.id),
group['name'], group.name,
group['description']) group.description)
if ret: if ret:
msg = _('Failed to create consistency group ' msg = _('Failed to create consistency group '
'%(id)s:%(ret)s.') % {'id': group['id'], '%(id)s:%(ret)s.') % {'id': group.id,
'ret': ret} 'ret': ret}
raise exception.VolumeBackendAPIException(data=msg) raise exception.VolumeBackendAPIException(data=msg)
else: else:
return model_update return model_update
except Exception as e: except Exception as e:
msg = _('Failed to create consistency group ' msg = _('Failed to create consistency group '
'%(id)s due to %(reason)s.') % {'id': group['id'], '%(id)s due to %(reason)s.') % {'id': group.id,
'reason': six.text_type(e)} 'reason': six.text_type(e)}
raise exception.VolumeBackendAPIException(data=msg) raise exception.VolumeBackendAPIException(data=msg)
def delete_consistencygroup(self, context, group, volumes): def _delete_consistencygroup(self, context, group, volumes):
"""Delete a consistency group.""" """Delete a consistency group."""
ret = 0 ret = 0
volumes = self.db.volume_get_all_by_group( volumes = self.db.volume_get_all_by_group(
context, group['id']) context, group.id)
model_update = {} model_update = {}
model_update['status'] = group['status'] model_update['status'] = group.status
LOG.info('Start to delete consistency group: %(cg_name)s', LOG.info('Start to delete consistency group: %(cg_name)s',
{'cg_name': group['id']}) {'cg_name': group.id})
try: try:
self.dpl.delete_vg(self._conver_uuid2hex(group['id'])) self.dpl.delete_vg(self._conver_uuid2hex(group.id))
except Exception as e: except Exception as e:
msg = _('Failed to delete consistency group %(id)s ' msg = _('Failed to delete consistency group %(id)s '
'due to %(reason)s.') % {'id': group['id'], 'due to %(reason)s.') % {'id': group.id,
'reason': six.text_type(e)} 'reason': six.text_type(e)}
raise exception.VolumeBackendAPIException(data=msg) raise exception.VolumeBackendAPIException(data=msg)
@ -893,31 +893,31 @@ class DPLCOMMONDriver(driver.CloneableImageVD,
ret = errno.EFAULT ret = errno.EFAULT
volume_ref['status'] = 'error_deleting' volume_ref['status'] = 'error_deleting'
model_update['status'] = ( model_update['status'] = (
fields.ConsistencyGroupStatus.ERROR_DELETING) fields.GroupStatus.ERROR_DELETING)
if ret == 0: if ret == 0:
model_update['status'] = fields.ConsistencyGroupStatus.DELETED model_update['status'] = fields.GroupStatus.DELETED
return model_update, volumes return model_update, volumes
def create_cgsnapshot(self, context, cgsnapshot, snapshots): def _create_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Creates a cgsnapshot.""" """Creates a cgsnapshot."""
snapshots = objects.SnapshotList().get_all_for_cgsnapshot( snapshots = objects.SnapshotList().get_all_for_group_snapshot(
context, cgsnapshot['id']) context, cgsnapshot.id)
model_update = {} model_update = {}
LOG.info('Start to create cgsnapshot for consistency group' LOG.info('Start to create cgsnapshot for consistency group'
': %(group_name)s', ': %(group_name)s',
{'group_name': cgsnapshot['consistencygroup_id']}) {'group_name': cgsnapshot.group_id})
try: try:
self.dpl.create_vdev_snapshot( self.dpl.create_vdev_snapshot(
self._conver_uuid2hex(cgsnapshot['consistencygroup_id']), self._conver_uuid2hex(cgsnapshot.group_id),
self._conver_uuid2hex(cgsnapshot['id']), self._conver_uuid2hex(cgsnapshot.id),
cgsnapshot['name'], cgsnapshot.name,
cgsnapshot.get('description', ''), '',
True) True)
for snapshot in snapshots: for snapshot in snapshots:
snapshot.status = fields.SnapshotStatus.AVAILABLE snapshot.status = fields.SnapshotStatus.AVAILABLE
except Exception as e: except Exception as e:
msg = _('Failed to create cg snapshot %(id)s ' msg = _('Failed to create cg snapshot %(id)s '
'due to %(reason)s.') % {'id': cgsnapshot['id'], 'due to %(reason)s.') % {'id': cgsnapshot.id,
'reason': six.text_type(e)} 'reason': six.text_type(e)}
raise exception.VolumeBackendAPIException(data=msg) raise exception.VolumeBackendAPIException(data=msg)
@ -925,38 +925,40 @@ class DPLCOMMONDriver(driver.CloneableImageVD,
return model_update, snapshots return model_update, snapshots
def delete_cgsnapshot(self, context, cgsnapshot, snapshots): def _delete_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Deletes a cgsnapshot.""" """Deletes a cgsnapshot."""
snapshots = objects.SnapshotList().get_all_for_cgsnapshot( snapshots = objects.SnapshotList().get_all_for_group_snapshot(
context, cgsnapshot['id']) context, cgsnapshot.id)
model_update = {} model_update = {}
model_update['status'] = cgsnapshot['status'] model_update['status'] = cgsnapshot.status
LOG.info('Delete cgsnapshot %(snap_name)s for consistency group: ' LOG.info('Delete cgsnapshot %(snap_name)s for consistency group: '
'%(group_name)s', '%(group_name)s',
{'snap_name': cgsnapshot['id'], {'snap_name': cgsnapshot.id,
'group_name': cgsnapshot['consistencygroup_id']}) 'group_name': cgsnapshot.group_id})
try: try:
self.dpl.delete_vdev_snapshot( self.dpl.delete_vdev_snapshot(
self._conver_uuid2hex(cgsnapshot['consistencygroup_id']), self._conver_uuid2hex(cgsnapshot.group_id),
self._conver_uuid2hex(cgsnapshot['id']), True) self._conver_uuid2hex(cgsnapshot.id), True)
for snapshot in snapshots: for snapshot in snapshots:
snapshot.status = fields.SnapshotStatus.DELETED snapshot.status = fields.SnapshotStatus.DELETED
except Exception as e: except Exception as e:
msg = _('Failed to delete cgsnapshot %(id)s due to ' msg = _('Failed to delete cgsnapshot %(id)s due to '
'%(reason)s.') % {'id': cgsnapshot['id'], '%(reason)s.') % {'id': cgsnapshot.id,
'reason': six.text_type(e)} 'reason': six.text_type(e)}
raise exception.VolumeBackendAPIException(data=msg) raise exception.VolumeBackendAPIException(data=msg)
model_update['status'] = 'deleted' model_update['status'] = 'deleted'
return model_update, snapshots return model_update, snapshots
def update_consistencygroup(self, context, group, add_volumes=None, def update_group(self, context, group, add_volumes=None,
remove_volumes=None): remove_volumes=None):
addvollist = [] addvollist = []
removevollist = [] removevollist = []
cgid = group['id'] cgid = group.id
vid = '' vid = ''
model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE} model_update = {'status': fields.GroupStatus.AVAILABLE}
if not volume_utils.is_group_a_cg_snapshot_type(group):
raise NotImplementedError()
# Get current group info in backend storage. # Get current group info in backend storage.
ret, output = self.dpl.get_vg(self._conver_uuid2hex(cgid)) ret, output = self.dpl.get_vg(self._conver_uuid2hex(cgid))
if ret == 0: if ret == 0:
@ -995,6 +997,33 @@ class DPLCOMMONDriver(driver.CloneableImageVD,
raise exception.VolumeBackendAPIException(data=msg) raise exception.VolumeBackendAPIException(data=msg)
return model_update, None, None return model_update, None, None
def create_group(self, context, group):
if volume_utils.is_group_a_cg_snapshot_type(group):
return self._create_consistencygroup(context, group)
raise NotImplementedError()
def delete_group(self, context, group, volumes):
if volume_utils.is_group_a_cg_snapshot_type(group):
return self._delete_consistencygroup(context, group, volumes)
raise NotImplementedError()
def create_group_snapshot(self, context, group_snapshot, snapshots):
if volume_utils.is_group_a_cg_snapshot_type(group_snapshot):
return self._create_cgsnapshot(context, group_snapshot, snapshots)
raise NotImplementedError()
def delete_group_snapshot(self, context, group_snapshot, snapshots):
if volume_utils.is_group_a_cg_snapshot_type(group_snapshot):
return self._delete_cgsnapshot(context, group_snapshot, snapshots)
raise NotImplementedError()
def create_group_from_src(self, context, group, volumes,
group_snapshot=None, snapshots=None,
source_group=None, source_vols=None):
err_msg = _("Prophet Storage doesn't support create_group_from_src.")
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
def create_volume(self, volume): def create_volume(self, volume):
"""Create a volume.""" """Create a volume."""
pool = volume_utils.extract_host(volume['host'], pool = volume_utils.extract_host(volume['host'],
@ -1039,16 +1068,18 @@ class DPLCOMMONDriver(driver.CloneableImageVD,
LOG.info('Flexvisor succeeded to create volume %(id)s.', LOG.info('Flexvisor succeeded to create volume %(id)s.',
{'id': volume['id']}) {'id': volume['id']})
if volume.get('consistencygroup_id', None): if volume.group_id:
group = volume_utils.group_get_by_id(volume.group_id)
if volume_utils.is_group_a_cg_snapshot_type(group):
try: try:
self._join_volume_group(volume, volume['consistencygroup_id']) self._join_volume_group(volume, volume.group_id)
except Exception: except Exception:
# Delete volume if volume failed to join group. # Delete volume if volume failed to join group.
self.dpl.delete_vdev(self._conver_uuid2hex(volume['id'])) self.dpl.delete_vdev(self._conver_uuid2hex(volume['id']))
msg = _('Flexvisor failed to create volume %(id)s in the ' msg = _('Flexvisor failed to create volume %(id)s in the '
'group %(vgid)s.') % { 'group %(vgid)s.') % {
'id': volume['id'], 'id': volume['id'],
'vgid': volume['consistencygroup_id']} 'vgid': volume.group_id}
raise exception.VolumeBackendAPIException(data=msg) raise exception.VolumeBackendAPIException(data=msg)
def create_volume_from_snapshot(self, volume, snapshot): def create_volume_from_snapshot(self, volume, snapshot):
@ -1059,7 +1090,7 @@ class DPLCOMMONDriver(driver.CloneableImageVD,
snapshotID = snapshot['id'] snapshotID = snapshot['id']
# Try to get cgid if volume belong in the group. # Try to get cgid if volume belong in the group.
src_volumeID = snapshot['volume_id'] src_volumeID = snapshot['volume_id']
cgsnapshotID = snapshot.get('cgsnapshot_id', None) cgsnapshotID = snapshot.get('group_snapshot_id', None)
if cgsnapshotID: if cgsnapshotID:
try: try:
src_volume = self.db.volume_get(src_volumeID) src_volume = self.db.volume_get(src_volumeID)
@ -1068,7 +1099,7 @@ class DPLCOMMONDriver(driver.CloneableImageVD,
"%(id)s info.") % {'id': src_volumeID} "%(id)s info.") % {'id': src_volumeID}
raise exception.VolumeBackendAPIException(data=msg) raise exception.VolumeBackendAPIException(data=msg)
if src_volume: if src_volume:
vgID = src_volume.get('consistencygroup_id', None) vgID = src_volume.group_id
# Get the volume origin snapshot id if the source snapshot is group # Get the volume origin snapshot id if the source snapshot is group
# snapshot. # snapshot.
@ -1125,9 +1156,11 @@ class DPLCOMMONDriver(driver.CloneableImageVD,
if volume['size'] > snapshot['volume_size']: if volume['size'] > snapshot['volume_size']:
self.extend_volume(volume, volume['size']) self.extend_volume(volume, volume['size'])
if volume.get('consistencygroup_id', None): if volume.group_id:
group = volume_utils.group_get_by_id(volume.group_id)
if volume_utils.is_group_a_cg_snapshot_type(group):
try: try:
self._join_volume_group(volume, volume['consistencygroup_id']) self._join_volume_group(volume, volume.group_id)
except Exception: except Exception:
# Delete volume if volume failed to join group. # Delete volume if volume failed to join group.
self.dpl.delete_vdev(self._conver_uuid2hex(volume['id'])) self.dpl.delete_vdev(self._conver_uuid2hex(volume['id']))
@ -1213,36 +1246,40 @@ class DPLCOMMONDriver(driver.CloneableImageVD,
LOG.info('Flexvisor succeeded to clone volume %(id)s.', LOG.info('Flexvisor succeeded to clone volume %(id)s.',
{'id': volume['id']}) {'id': volume['id']})
if volume.get('consistencygroup_id', None): if volume.group_id:
group = volume_utils.group_get_by_id(volume.group_id)
if volume_utils.is_group_a_cg_snapshot_type(group):
try: try:
self._join_volume_group(volume, volume['consistencygroup_id']) self._join_volume_group(volume, volume.group_id)
except Exception: except Exception:
# Delete volume if volume failed to join group. # Delete volume if volume failed to join group.
self.dpl.delete_vdev(self._conver_uuid2hex(volume['id'])) self.dpl.delete_vdev(self._conver_uuid2hex(volume['id']))
msg = _('Flexvisor volume %(id)s failed to join group ' msg = _('Flexvisor volume %(id)s failed to join group '
'%(vgid)s.') % {'id': volume['id'], '%(vgid)s.') % {'id': volume['id'],
'vgid': volume['consistencygroup_id']} 'vgid': volume.group_id}
raise exception.VolumeBackendAPIException(data=msg) raise exception.VolumeBackendAPIException(data=msg)
def delete_volume(self, volume): def delete_volume(self, volume):
"""Deletes a volume.""" """Deletes a volume."""
ret = 0 ret = 0
if volume.get('consistencygroup_id', None): if volume.group_id:
group = volume_utils.group_get_by_id(volume.group_id)
if group and volume_utils.is_group_a_cg_snapshot_type(group):
msg = '' msg = ''
try: try:
ret, out = self.dpl.leave_vg( ret, out = self.dpl.leave_vg(
self._conver_uuid2hex(volume['id']), self._conver_uuid2hex(volume['id']),
self._conver_uuid2hex(volume['consistencygroup_id'])) self._conver_uuid2hex(volume.group_id))
if ret: if ret:
LOG.warning('Flexvisor failed to delete volume ' LOG.warning('Flexvisor failed to delete volume '
'%(id)s from the group %(vgid)s.', '%(id)s from the group %(vgid)s.',
{'id': volume['id'], {'id': volume['id'],
'vgid': volume['consistencygroup_id']}) 'vgid': volume.group_id})
except Exception as e: except Exception as e:
LOG.warning('Flexvisor failed to delete volume %(id)s ' LOG.warning('Flexvisor failed to delete volume %(id)s '
'from group %(vgid)s due to %(status)s.', 'from group %(vgid)s due to %(status)s.',
{'id': volume['id'], {'id': volume['id'],
'vgid': volume['consistencygroup_id'], 'vgid': volume.group_id,
'status': e}) 'status': e})
if ret: if ret:
@ -1435,6 +1472,7 @@ class DPLCOMMONDriver(driver.CloneableImageVD,
data['storage_protocol'] = 'iSCSI' data['storage_protocol'] = 'iSCSI'
data['location_info'] = location_info data['location_info'] = location_info
data['consistencygroup_support'] = True data['consistencygroup_support'] = True
data['consistent_group_snapshot_enabled'] = True
data['pools'] = pools data['pools'] = pools
self._stats = data self._stats = data
except Exception as e: except Exception as e:

View File

@ -0,0 +1,3 @@
---
features:
- Added consistent group capability to generic volume groups in ProphetStor driver.