Merge "3PAR: Adding Consistency Group Support"
This commit is contained in:
commit
40de3b7f40
@ -94,6 +94,10 @@ class HP3PARBaseDriver(object):
|
||||
SNAPSHOT_NAME = 'snapshot-2f823bdc-e36e-4dc8-bd15-de1c7a28ff31'
|
||||
VOLUME_3PAR_NAME = 'osv-0DM4qZEVSKON-DXN-NwVpw'
|
||||
SNAPSHOT_3PAR_NAME = 'oss-L4I73ONuTci9Fd4ceij-MQ'
|
||||
CONSIS_GROUP_ID = '6044fedf-c889-4752-900f-2039d247a5df'
|
||||
CONSIS_GROUP_NAME = 'vvs-YET.38iJR1KQDyA50kel3w'
|
||||
CGSNAPSHOT_ID = 'e91c5ed5-daee-4e84-8724-1c9e31e7a1f2'
|
||||
CGSNAPSHOT_BASE_NAME = 'oss-6Rxe1druToSHJByeMeeh8g'
|
||||
# fake host on the 3par
|
||||
FAKE_HOST = 'fakehost'
|
||||
FAKE_CINDER_HOST = 'fakehost@foo#' + HP3PAR_CPG
|
||||
@ -264,6 +268,11 @@ class HP3PARBaseDriver(object):
|
||||
'state': 1,
|
||||
'uuid': '29c214aa-62b9-41c8-b198-543f6cf24edf'}]
|
||||
|
||||
cgsnapshot = {'consistencygroup_id': CONSIS_GROUP_ID,
|
||||
'description': 'cgsnapshot',
|
||||
'id': CGSNAPSHOT_ID,
|
||||
'readOnly': False}
|
||||
|
||||
TASK_DONE = 1
|
||||
TASK_ACTIVE = 2
|
||||
STATUS_DONE = {'status': 1}
|
||||
@ -2947,6 +2956,505 @@ class HP3PARBaseDriver(object):
|
||||
safe_host = common._safe_hostname(long_hostname)
|
||||
self.assertEqual(fixed_hostname, safe_host)
|
||||
|
||||
@mock.patch('hp3parclient.version', "3.2.2")
|
||||
def test_create_consistency_group(self):
|
||||
class fake_consitencygroup_object(object):
|
||||
volume_type_id = '49fa96b5-828e-4653-b622-873a1b7e6f1c'
|
||||
name = 'cg_name'
|
||||
cgsnapshot_id = None
|
||||
host = self.FAKE_CINDER_HOST
|
||||
id = self.CONSIS_GROUP_ID
|
||||
description = 'consistency group'
|
||||
|
||||
mock_client = self.setup_driver()
|
||||
|
||||
comment = (
|
||||
"{'display_name': 'cg_name',"
|
||||
" 'consistency_group_id':"
|
||||
" '" + self.CONSIS_GROUP_ID + "',"
|
||||
" 'description': 'consistency group'}")
|
||||
|
||||
with mock.patch.object(hpcommon.HP3PARCommon,
|
||||
'_create_client') as mock_create_client:
|
||||
mock_create_client.return_value = mock_client
|
||||
mock_client.getCPG.return_value = {'domain': None}
|
||||
# create a consistency group
|
||||
group = fake_consitencygroup_object()
|
||||
self.driver.create_consistencygroup(context.get_admin_context(),
|
||||
group)
|
||||
|
||||
expected = [
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.createVolumeSet(
|
||||
self.CONSIS_GROUP_NAME,
|
||||
domain=None,
|
||||
comment=comment)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
|
||||
@mock.patch('hp3parclient.version', "3.2.2")
|
||||
def test_create_consistency_group_from_src(self):
|
||||
class fake_consitencygroup_object(object):
|
||||
volume_type_id = '49fa96b5-828e-4653-b622-873a1b7e6f1c'
|
||||
name = 'cg_name'
|
||||
cgsnapshot_id = None
|
||||
host = self.FAKE_CINDER_HOST
|
||||
id = self.CONSIS_GROUP_ID
|
||||
description = 'consistency group'
|
||||
|
||||
mock_client = self.setup_driver()
|
||||
volume = self.volume
|
||||
|
||||
cgsnap_optional = (
|
||||
{'comment': '{"consistency_group_id":'
|
||||
' "6044fedf-c889-4752-900f-2039d247a5df",'
|
||||
' "description": "cgsnapshot",'
|
||||
' "cgsnapshot_id": "e91c5ed5-daee-4e84-8724-1c9e31e7a1f2"}',
|
||||
'readOnly': False})
|
||||
|
||||
cg_comment = (
|
||||
"{'display_name': 'cg_name',"
|
||||
" 'consistency_group_id':"
|
||||
" '" + self.CONSIS_GROUP_ID + "',"
|
||||
" 'description': 'consistency group'}")
|
||||
|
||||
with mock.patch.object(hpcommon.HP3PARCommon,
|
||||
'_create_client') as mock_create_client:
|
||||
mock_create_client.return_value = mock_client
|
||||
mock_client.getCPG.return_value = {'domain': None}
|
||||
|
||||
# create a consistency group
|
||||
group = fake_consitencygroup_object()
|
||||
self.driver.create_consistencygroup(context.get_admin_context(),
|
||||
group)
|
||||
|
||||
expected = [
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.createVolumeSet(
|
||||
self.CONSIS_GROUP_NAME,
|
||||
domain=None,
|
||||
comment=cg_comment)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
mock_client.reset_mock()
|
||||
|
||||
# add a volume to the consistency group
|
||||
self.driver.update_consistencygroup(context.get_admin_context(),
|
||||
group,
|
||||
add_volumes=[volume],
|
||||
remove_volumes=[])
|
||||
|
||||
expected = [
|
||||
mock.call.addVolumeToVolumeSet(
|
||||
self.CONSIS_GROUP_NAME,
|
||||
self.VOLUME_NAME_3PAR)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
mock_client.reset_mock()
|
||||
|
||||
# create a snapshot of the consistency group
|
||||
self.driver.create_cgsnapshot(context.get_admin_context(),
|
||||
self.cgsnapshot)
|
||||
|
||||
expected = [
|
||||
mock.call.createSnapshotOfVolumeSet(
|
||||
self.CGSNAPSHOT_BASE_NAME + "-@count@",
|
||||
self.CONSIS_GROUP_NAME,
|
||||
optional=cgsnap_optional)]
|
||||
|
||||
# create a consistency group from the cgsnapshot
|
||||
self.driver.create_consistencygroup_from_src(
|
||||
context.get_admin_context(), group,
|
||||
[volume], cgsnapshot=self.cgsnapshot,
|
||||
snapshots=[self.snapshot])
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
|
||||
@mock.patch('hp3parclient.version', "3.2.2")
|
||||
def test_delete_consistency_group(self):
|
||||
class fake_consitencygroup_object(object):
|
||||
volume_type_id = '49fa96b5-828e-4653-b622-873a1b7e6f1c'
|
||||
name = 'cg_name'
|
||||
cgsnapshot_id = None
|
||||
host = self.FAKE_CINDER_HOST
|
||||
id = self.CONSIS_GROUP_ID
|
||||
description = 'consistency group'
|
||||
|
||||
mock_client = self.setup_driver()
|
||||
|
||||
comment = (
|
||||
"{'display_name': 'cg_name',"
|
||||
" 'consistency_group_id':"
|
||||
" '" + self.CONSIS_GROUP_ID + "',"
|
||||
" 'description': 'consistency group'}")
|
||||
|
||||
with mock.patch.object(hpcommon.HP3PARCommon,
|
||||
'_create_client') as mock_create_client:
|
||||
mock_create_client.return_value = mock_client
|
||||
mock_client.getCPG.return_value = {'domain': None}
|
||||
|
||||
# create a consistency group
|
||||
group = fake_consitencygroup_object()
|
||||
self.driver.create_consistencygroup(context.get_admin_context(),
|
||||
group)
|
||||
|
||||
expected = [
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.createVolumeSet(
|
||||
self.CONSIS_GROUP_NAME,
|
||||
domain=None,
|
||||
comment=comment)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
mock_client.reset_mock()
|
||||
|
||||
# remove the consistency group
|
||||
group.status = 'deleting'
|
||||
self.driver.delete_consistencygroup(context.get_admin_context(),
|
||||
group)
|
||||
|
||||
expected = [
|
||||
mock.call.deleteVolumeSet(
|
||||
self.CONSIS_GROUP_NAME)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
|
||||
@mock.patch('hp3parclient.version', "3.2.2")
|
||||
def test_update_consistency_group_add_vol(self):
|
||||
class fake_consitencygroup_object(object):
|
||||
volume_type_id = '49fa96b5-828e-4653-b622-873a1b7e6f1c'
|
||||
name = 'cg_name'
|
||||
cgsnapshot_id = None
|
||||
host = self.FAKE_CINDER_HOST
|
||||
id = self.CONSIS_GROUP_ID
|
||||
description = 'consistency group'
|
||||
|
||||
mock_client = self.setup_driver()
|
||||
volume = self.volume
|
||||
|
||||
comment = (
|
||||
"{'display_name': 'cg_name',"
|
||||
" 'consistency_group_id':"
|
||||
" '" + self.CONSIS_GROUP_ID + "',"
|
||||
" 'description': 'consistency group'}")
|
||||
|
||||
with mock.patch.object(hpcommon.HP3PARCommon,
|
||||
'_create_client') as mock_create_client:
|
||||
mock_create_client.return_value = mock_client
|
||||
mock_client.getCPG.return_value = {'domain': None}
|
||||
|
||||
# create a consistency group
|
||||
group = fake_consitencygroup_object()
|
||||
self.driver.create_consistencygroup(context.get_admin_context(),
|
||||
group)
|
||||
|
||||
expected = [
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.createVolumeSet(
|
||||
self.CONSIS_GROUP_NAME,
|
||||
domain=None,
|
||||
comment=comment)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
mock_client.reset_mock()
|
||||
|
||||
# add a volume to the consistency group
|
||||
self.driver.update_consistencygroup(context.get_admin_context(),
|
||||
group,
|
||||
add_volumes=[volume],
|
||||
remove_volumes=[])
|
||||
|
||||
expected = [
|
||||
mock.call.addVolumeToVolumeSet(
|
||||
self.CONSIS_GROUP_NAME,
|
||||
self.VOLUME_NAME_3PAR)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
|
||||
@mock.patch('hp3parclient.version', "3.2.2")
|
||||
def test_update_consistency_group_remove_vol(self):
|
||||
class fake_consitencygroup_object(object):
|
||||
volume_type_id = '49fa96b5-828e-4653-b622-873a1b7e6f1c'
|
||||
name = 'cg_name'
|
||||
cgsnapshot_id = None
|
||||
host = self.FAKE_CINDER_HOST
|
||||
id = self.CONSIS_GROUP_ID
|
||||
description = 'consistency group'
|
||||
|
||||
mock_client = self.setup_driver()
|
||||
volume = self.volume
|
||||
|
||||
comment = (
|
||||
"{'display_name': 'cg_name',"
|
||||
" 'consistency_group_id':"
|
||||
" '" + self.CONSIS_GROUP_ID + "',"
|
||||
" 'description': 'consistency group'}")
|
||||
|
||||
with mock.patch.object(hpcommon.HP3PARCommon,
|
||||
'_create_client') as mock_create_client:
|
||||
mock_create_client.return_value = mock_client
|
||||
mock_client.getCPG.return_value = {'domain': None}
|
||||
|
||||
# create a consistency group
|
||||
group = fake_consitencygroup_object()
|
||||
self.driver.create_consistencygroup(context.get_admin_context(),
|
||||
group)
|
||||
|
||||
expected = [
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.createVolumeSet(
|
||||
self.CONSIS_GROUP_NAME,
|
||||
domain=None,
|
||||
comment=comment)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
mock_client.reset_mock()
|
||||
|
||||
# add a volume to the consistency group
|
||||
self.driver.update_consistencygroup(context.get_admin_context(),
|
||||
group,
|
||||
add_volumes=[volume],
|
||||
remove_volumes=[])
|
||||
|
||||
expected = [
|
||||
mock.call.addVolumeToVolumeSet(
|
||||
self.CONSIS_GROUP_NAME,
|
||||
self.VOLUME_NAME_3PAR)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
mock_client.reset_mock()
|
||||
|
||||
# remove the volume from the consistency group
|
||||
self.driver.update_consistencygroup(context.get_admin_context(),
|
||||
group,
|
||||
add_volumes=[],
|
||||
remove_volumes=[volume])
|
||||
|
||||
expected = [
|
||||
mock.call.removeVolumeFromVolumeSet(
|
||||
self.CONSIS_GROUP_NAME,
|
||||
self.VOLUME_NAME_3PAR)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
|
||||
@mock.patch('hp3parclient.version', "3.2.2")
|
||||
def test_create_cgsnapshot(self):
|
||||
class fake_consitencygroup_object(object):
|
||||
volume_type_id = '49fa96b5-828e-4653-b622-873a1b7e6f1c'
|
||||
name = 'cg_name'
|
||||
cgsnapshot_id = None
|
||||
host = self.FAKE_CINDER_HOST
|
||||
id = self.CONSIS_GROUP_ID
|
||||
description = 'consistency group'
|
||||
|
||||
mock_client = self.setup_driver()
|
||||
volume = self.volume
|
||||
|
||||
cg_comment = (
|
||||
"{'display_name': 'cg_name',"
|
||||
" 'consistency_group_id':"
|
||||
" '" + self.CONSIS_GROUP_ID + "',"
|
||||
" 'description': 'consistency group'}")
|
||||
|
||||
cgsnap_optional = (
|
||||
{'comment': '{"consistency_group_id":'
|
||||
' "6044fedf-c889-4752-900f-2039d247a5df",'
|
||||
' "description": "cgsnapshot",'
|
||||
' "cgsnapshot_id": "e91c5ed5-daee-4e84-8724-1c9e31e7a1f2"}',
|
||||
'readOnly': False})
|
||||
|
||||
with mock.patch.object(hpcommon.HP3PARCommon,
|
||||
'_create_client') as mock_create_client:
|
||||
mock_create_client.return_value = mock_client
|
||||
mock_client.getCPG.return_value = {'domain': None}
|
||||
|
||||
# create a consistency group
|
||||
group = fake_consitencygroup_object()
|
||||
self.driver.create_consistencygroup(context.get_admin_context(),
|
||||
group)
|
||||
|
||||
expected = [
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.createVolumeSet(
|
||||
self.CONSIS_GROUP_NAME,
|
||||
domain=None,
|
||||
comment=cg_comment)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
mock_client.reset_mock()
|
||||
|
||||
# add a volume to the consistency group
|
||||
self.driver.update_consistencygroup(context.get_admin_context(),
|
||||
group,
|
||||
add_volumes=[volume],
|
||||
remove_volumes=[])
|
||||
|
||||
expected = [
|
||||
mock.call.addVolumeToVolumeSet(
|
||||
self.CONSIS_GROUP_NAME,
|
||||
self.VOLUME_NAME_3PAR)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
mock_client.reset_mock()
|
||||
|
||||
# create a snapshot of the consistency group
|
||||
self.driver.create_cgsnapshot(context.get_admin_context(),
|
||||
self.cgsnapshot)
|
||||
|
||||
expected = [
|
||||
mock.call.createSnapshotOfVolumeSet(
|
||||
self.CGSNAPSHOT_BASE_NAME + "-@count@",
|
||||
self.CONSIS_GROUP_NAME,
|
||||
optional=cgsnap_optional)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
|
||||
@mock.patch('hp3parclient.version', "3.2.2")
|
||||
def test_delete_cgsnapshot(self):
|
||||
class fake_consitencygroup_object(object):
|
||||
volume_type_id = '49fa96b5-828e-4653-b622-873a1b7e6f1c'
|
||||
name = 'cg_name'
|
||||
cgsnapshot_id = None
|
||||
host = self.FAKE_CINDER_HOST
|
||||
id = self.CONSIS_GROUP_ID
|
||||
description = 'consistency group'
|
||||
|
||||
mock_client = self.setup_driver()
|
||||
volume = self.volume
|
||||
cgsnapshot = self.cgsnapshot
|
||||
|
||||
cg_comment = (
|
||||
"{'display_name': 'cg_name',"
|
||||
" 'consistency_group_id':"
|
||||
" '" + self.CONSIS_GROUP_ID + "',"
|
||||
" 'description': 'consistency group'}")
|
||||
|
||||
cgsnap_optional = (
|
||||
{'comment': '{"consistency_group_id":'
|
||||
' "6044fedf-c889-4752-900f-2039d247a5df",'
|
||||
' "description": "cgsnapshot",'
|
||||
' "cgsnapshot_id": "e91c5ed5-daee-4e84-8724-1c9e31e7a1f2"}',
|
||||
'readOnly': False})
|
||||
|
||||
with mock.patch.object(hpcommon.HP3PARCommon,
|
||||
'_create_client') as mock_create_client:
|
||||
mock_create_client.return_value = mock_client
|
||||
mock_client.getCPG.return_value = {'domain': None}
|
||||
|
||||
# create a consistency group
|
||||
group = fake_consitencygroup_object()
|
||||
self.driver.create_consistencygroup(context.get_admin_context(),
|
||||
group)
|
||||
|
||||
expected = [
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.createVolumeSet(
|
||||
self.CONSIS_GROUP_NAME,
|
||||
domain=None,
|
||||
comment=cg_comment)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
mock_client.reset_mock()
|
||||
|
||||
# add a volume to the consistency group
|
||||
self.driver.update_consistencygroup(context.get_admin_context(),
|
||||
group,
|
||||
add_volumes=[volume],
|
||||
remove_volumes=[])
|
||||
|
||||
expected = [
|
||||
mock.call.addVolumeToVolumeSet(
|
||||
self.CONSIS_GROUP_NAME,
|
||||
self.VOLUME_NAME_3PAR)]
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
mock_client.reset_mock()
|
||||
|
||||
# create a snapshot of the consistency group
|
||||
self.driver.create_cgsnapshot(context.get_admin_context(),
|
||||
cgsnapshot)
|
||||
|
||||
expected = [
|
||||
mock.call.createSnapshotOfVolumeSet(
|
||||
self.CGSNAPSHOT_BASE_NAME + "-@count@",
|
||||
self.CONSIS_GROUP_NAME,
|
||||
optional=cgsnap_optional)]
|
||||
|
||||
# delete the snapshot of the consistency group
|
||||
cgsnapshot['status'] = 'deleting'
|
||||
self.driver.delete_cgsnapshot(context.get_admin_context(),
|
||||
cgsnapshot)
|
||||
|
||||
mock_client.assert_has_calls(
|
||||
[mock.call.getWsApiVersion()] +
|
||||
self.standard_login +
|
||||
expected +
|
||||
self.standard_logout)
|
||||
|
||||
|
||||
class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
|
||||
|
||||
|
@ -61,6 +61,7 @@ from cinder import context
|
||||
from cinder import exception
|
||||
from cinder import flow_utils
|
||||
from cinder.i18n import _, _LE, _LI, _LW
|
||||
from cinder import objects
|
||||
from cinder.volume import qos_specs
|
||||
from cinder.volume import utils as volume_utils
|
||||
from cinder.volume import volume_types
|
||||
@ -72,6 +73,7 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
MIN_CLIENT_VERSION = '3.1.2'
|
||||
GETCPGSTATDATA_VERSION = '3.2.2'
|
||||
MIN_CG_CLIENT_VERSION = '3.2.2'
|
||||
DEDUP_API_VERSION = 30201120
|
||||
FLASH_CACHE_API_VERSION = 30201200
|
||||
SRSTATLD_API_VERSION = 30201200
|
||||
@ -198,10 +200,11 @@ class HP3PARCommon(object):
|
||||
2.0.48 - Adding changes to support 3PAR iSCSI multipath.
|
||||
2.0.49 - Added client CPG stats to driver volume stats. bug #1482741
|
||||
2.0.50 - Add over subscription support
|
||||
2.0.51 - Adds consistency group support
|
||||
|
||||
"""
|
||||
|
||||
VERSION = "2.0.50"
|
||||
VERSION = "2.0.51"
|
||||
|
||||
stats = {}
|
||||
|
||||
@ -242,6 +245,7 @@ class HP3PARCommon(object):
|
||||
self.config = config
|
||||
self.client = None
|
||||
self.uuid = uuid.uuid4()
|
||||
self.db = importutils.import_module('cinder.db')
|
||||
|
||||
def get_version(self):
|
||||
return self.VERSION
|
||||
@ -373,6 +377,167 @@ class HP3PARCommon(object):
|
||||
growth_size_mib = growth_size * units.Ki
|
||||
self._extend_volume(volume, volume_name, growth_size_mib)
|
||||
|
||||
def create_consistencygroup(self, context, group):
|
||||
"""Creates a consistencygroup."""
|
||||
|
||||
pool = volume_utils.extract_host(group.host, level='pool')
|
||||
domain = self.get_domain(pool)
|
||||
cg_name = self._get_3par_vvs_name(group.id)
|
||||
|
||||
extra = {'consistency_group_id': group.id}
|
||||
extra['description'] = group.description
|
||||
extra['display_name'] = group.name
|
||||
if group.cgsnapshot_id:
|
||||
extra['cgsnapshot_id'] = group.cgsnapshot_id
|
||||
|
||||
self.client.createVolumeSet(cg_name, domain=domain,
|
||||
comment=six.text_type(extra))
|
||||
|
||||
model_update = {'status': 'available'}
|
||||
return model_update
|
||||
|
||||
def create_consistencygroup_from_src(self, context, group, volumes,
|
||||
cgsnapshot=None, snapshots=None,
|
||||
source_cg=None, source_vols=None):
|
||||
|
||||
if cgsnapshot and snapshots:
|
||||
self.create_consistencygroup(context, group)
|
||||
vvs_name = self._get_3par_vvs_name(group.id)
|
||||
cgsnap_name = self._get_3par_snap_name(cgsnapshot['id'])
|
||||
for i, (volume, snapshot) in enumerate(zip(volumes, snapshots)):
|
||||
snap_name = cgsnap_name + "-" + six.text_type(i)
|
||||
volume_name = self._get_3par_vol_name(volume['id'])
|
||||
type_info = self.get_volume_settings_from_type(volume)
|
||||
cpg = type_info['cpg']
|
||||
optional = {'online': True, 'snapCPG': cpg}
|
||||
self.client.copyVolume(snap_name, volume_name, cpg, optional)
|
||||
self.client.addVolumeToVolumeSet(vvs_name, volume_name)
|
||||
else:
|
||||
msg = _("create_consistencygroup_from_src only supports a"
|
||||
" cgsnapshot source, other sources cannot be used.")
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
return None, None
|
||||
|
||||
def delete_consistencygroup(self, context, group):
|
||||
"""Deletes a consistency group."""
|
||||
|
||||
try:
|
||||
cg_name = self._get_3par_vvs_name(group.id)
|
||||
self.client.deleteVolumeSet(cg_name)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
err = (_LW("Virtual Volume Set '%s' doesn't exist on array.") %
|
||||
cg_name)
|
||||
LOG.warning(err)
|
||||
except hpexceptions.HTTPConflict as e:
|
||||
err = (_LE("Conflict detected in Virtual Volume Set"
|
||||
" %(volume_set): %(error)"),
|
||||
{"volume_set": cg_name,
|
||||
"error": e})
|
||||
LOG.error(err)
|
||||
|
||||
volumes = self.db.volume_get_all_by_group(context, group.id)
|
||||
for volume in volumes:
|
||||
self.delete_volume(volume)
|
||||
volume.status = 'deleted'
|
||||
|
||||
model_update = {'status': group.status}
|
||||
|
||||
return model_update, volumes
|
||||
|
||||
def update_consistencygroup(self, context, group,
|
||||
add_volumes=None, remove_volumes=None):
|
||||
|
||||
volume_set_name = self._get_3par_vvs_name(group.id)
|
||||
|
||||
for volume in add_volumes:
|
||||
volume_name = self._get_3par_vol_name(volume['id'])
|
||||
try:
|
||||
self.client.addVolumeToVolumeSet(volume_set_name, volume_name)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
msg = (_LE('Virtual Volume Set %s does not exist.') %
|
||||
volume_set_name)
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
for volume in remove_volumes:
|
||||
volume_name = self._get_3par_vol_name(volume['id'])
|
||||
try:
|
||||
self.client.removeVolumeFromVolumeSet(
|
||||
volume_set_name, volume_name)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
msg = (_LE('Virtual Volume Set %s does not exist.') %
|
||||
volume_set_name)
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
return None, None, None
|
||||
|
||||
def create_cgsnapshot(self, context, cgsnapshot):
|
||||
"""Creates a cgsnapshot."""
|
||||
|
||||
cg_id = cgsnapshot['consistencygroup_id']
|
||||
snap_shot_name = self._get_3par_snap_name(cgsnapshot['id']) + (
|
||||
"-@count@")
|
||||
copy_of_name = self._get_3par_vvs_name(cg_id)
|
||||
|
||||
extra = {'cgsnapshot_id': cgsnapshot['id']}
|
||||
extra['consistency_group_id'] = cg_id
|
||||
extra['description'] = cgsnapshot['description']
|
||||
|
||||
optional = {'comment': json.dumps(extra),
|
||||
'readOnly': False}
|
||||
if self.config.hp3par_snapshot_expiration:
|
||||
optional['expirationHours'] = (
|
||||
int(self.config.hp3par_snapshot_expiration))
|
||||
|
||||
if self.config.hp3par_snapshot_retention:
|
||||
optional['retentionHours'] = (
|
||||
int(self.config.hp3par_snapshot_retention))
|
||||
|
||||
self.client.createSnapshotOfVolumeSet(snap_shot_name, copy_of_name,
|
||||
optional=optional)
|
||||
|
||||
snapshots = objects.SnapshotList().get_all_for_cgsnapshot(
|
||||
context, cgsnapshot['id'])
|
||||
|
||||
for snapshot in snapshots:
|
||||
snapshot.status = 'available'
|
||||
|
||||
model_update = {'status': 'available'}
|
||||
|
||||
return model_update, snapshots
|
||||
|
||||
def delete_cgsnapshot(self, context, cgsnapshot):
|
||||
"""Deletes a cgsnapshot."""
|
||||
|
||||
cgsnap_name = self._get_3par_snap_name(cgsnapshot['id'])
|
||||
|
||||
snapshots = objects.SnapshotList().get_all_for_cgsnapshot(
|
||||
context, cgsnapshot['id'])
|
||||
|
||||
for i, snapshot in enumerate(snapshots):
|
||||
try:
|
||||
snap_name = cgsnap_name + "-" + six.text_type(i)
|
||||
self.client.deleteVolume(snap_name)
|
||||
except hpexceptions.HTTPForbidden as ex:
|
||||
LOG.error(_LE("Exception: %s."), ex)
|
||||
raise exception.NotAuthorized()
|
||||
except hpexceptions.HTTPNotFound as ex:
|
||||
# We'll let this act as if it worked
|
||||
# it helps clean up the cinder entries.
|
||||
LOG.warning(_LW("Delete Snapshot id not found. Removing from "
|
||||
"cinder: %(id)s Ex: %(msg)s"),
|
||||
{'id': snapshot['id'], 'msg': ex})
|
||||
except hpexceptions.HTTPConflict as ex:
|
||||
LOG.error(_LE("Exception: %s."), ex)
|
||||
raise exception.SnapshotIsBusy(snapshot_name=snapshot['id'])
|
||||
snapshot['status'] = 'deleted'
|
||||
|
||||
model_update = {'status': cgsnapshot['status']}
|
||||
|
||||
return model_update, snapshots
|
||||
|
||||
def manage_existing(self, volume, existing_ref):
|
||||
"""Manage an existing 3PAR volume.
|
||||
|
||||
@ -817,6 +982,9 @@ class HP3PARCommon(object):
|
||||
'multiattach': True,
|
||||
}
|
||||
|
||||
if hp3parclient.version >= MIN_CG_CLIENT_VERSION:
|
||||
pool['consistencygroup_support'] = True
|
||||
|
||||
pools.append(pool)
|
||||
|
||||
self.stats = {'driver_version': '1.0',
|
||||
@ -1191,7 +1359,6 @@ class HP3PARCommon(object):
|
||||
|
||||
cpg = pool
|
||||
self.validate_cpg(cpg)
|
||||
|
||||
# Look to see if the snap_cpg was specified in volume type
|
||||
# extra spec, if not use hp3par_cpg_snap from config as the
|
||||
# default.
|
||||
@ -1292,6 +1459,10 @@ class HP3PARCommon(object):
|
||||
tdvv = type_info['tdvv']
|
||||
flash_cache = self.get_flash_cache_policy(type_info['hp3par_keys'])
|
||||
|
||||
cg_id = volume.get('consistencygroup_id', None)
|
||||
if cg_id:
|
||||
vvs_name = self._get_3par_vvs_name(cg_id)
|
||||
|
||||
type_id = volume.get('volume_type_id', None)
|
||||
if type_id is not None:
|
||||
comments['volume_type_name'] = volume_type.get('name')
|
||||
@ -1494,7 +1665,8 @@ class HP3PARCommon(object):
|
||||
LOG.error(_LE("Exception: %s"), ex)
|
||||
raise exception.CinderException(ex)
|
||||
|
||||
def create_volume_from_snapshot(self, volume, snapshot):
|
||||
def create_volume_from_snapshot(self, volume, snapshot, snap_name=None,
|
||||
vvs_name=None):
|
||||
"""Creates a volume from a snapshot.
|
||||
|
||||
"""
|
||||
@ -1510,7 +1682,8 @@ class HP3PARCommon(object):
|
||||
raise exception.InvalidInput(reason=err)
|
||||
|
||||
try:
|
||||
snap_name = self._get_3par_snap_name(snapshot['id'])
|
||||
if not snap_name:
|
||||
snap_name = self._get_3par_snap_name(snapshot['id'])
|
||||
volume_name = self._get_3par_vol_name(volume['id'])
|
||||
|
||||
extra = {'volume_id': volume['id'],
|
||||
@ -1518,8 +1691,10 @@ class HP3PARCommon(object):
|
||||
|
||||
type_id = volume.get('volume_type_id', None)
|
||||
|
||||
hp3par_keys, qos, _volume_type, vvs_name = self.get_type_info(
|
||||
hp3par_keys, qos, _volume_type, vvs = self.get_type_info(
|
||||
type_id)
|
||||
if vvs:
|
||||
vvs_name = vvs
|
||||
|
||||
name = volume.get('display_name', None)
|
||||
if name:
|
||||
|
@ -80,10 +80,11 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
2.0.16 - Added encrypted property to initialize_connection #1439917
|
||||
2.0.17 - Improved VLUN creation and deletion logic. #1469816
|
||||
2.0.18 - Changed initialize_connection to use getHostVLUNs. #1475064
|
||||
2.0.19 - Adds consistency group support
|
||||
|
||||
"""
|
||||
|
||||
VERSION = "2.0.18"
|
||||
VERSION = "2.0.19"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HP3PARFCDriver, self).__init__(*args, **kwargs)
|
||||
@ -427,6 +428,54 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def create_consistencygroup(self, context, group):
|
||||
common = self._login()
|
||||
try:
|
||||
return common.create_consistencygroup(context, group)
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def create_consistencygroup_from_src(self, context, group, volumes,
|
||||
cgsnapshot=None, snapshots=None,
|
||||
source_cg=None, source_vols=None):
|
||||
common = self._login()
|
||||
try:
|
||||
return common.create_consistencygroup_from_src(
|
||||
context, group, volumes, cgsnapshot, snapshots, source_cg,
|
||||
source_vols)
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def delete_consistencygroup(self, context, group):
|
||||
common = self._login()
|
||||
try:
|
||||
return common.delete_consistencygroup(context, group)
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def update_consistencygroup(self, context, group,
|
||||
add_volumes=None, remove_volumes=None):
|
||||
common = self._login()
|
||||
try:
|
||||
return common.update_consistencygroup(context, group, add_volumes,
|
||||
remove_volumes)
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def create_cgsnapshot(self, context, cgsnapshot):
|
||||
common = self._login()
|
||||
try:
|
||||
return common.create_cgsnapshot(context, cgsnapshot)
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def delete_cgsnapshot(self, context, cgsnapshot):
|
||||
common = self._login()
|
||||
try:
|
||||
return common.delete_cgsnapshot(context, cgsnapshot)
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def manage_existing(self, volume, existing_ref):
|
||||
common = self._login()
|
||||
try:
|
||||
|
@ -90,10 +90,11 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
2.0.18 - Improved VLUN creation and deletion logic. #1469816
|
||||
2.0.19 - Changed initialize_connection to use getHostVLUNs. #1475064
|
||||
2.0.20 - Adding changes to support 3PAR iSCSI multipath.
|
||||
2.0.21 - Adds consistency group support
|
||||
|
||||
"""
|
||||
|
||||
VERSION = "2.0.20"
|
||||
VERSION = "2.0.21"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HP3PARISCSIDriver, self).__init__(*args, **kwargs)
|
||||
@ -724,6 +725,54 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def create_consistencygroup(self, context, group):
|
||||
common = self._login()
|
||||
try:
|
||||
common.create_consistencygroup(context, group)
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def create_consistencygroup_from_src(self, context, group, volumes,
|
||||
cgsnapshot=None, snapshots=None,
|
||||
source_cg=None, source_vols=None):
|
||||
common = self._login()
|
||||
try:
|
||||
return common.create_consistencygroup_from_src(
|
||||
context, group, volumes, cgsnapshot, snapshots, source_cg,
|
||||
source_vols)
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def delete_consistencygroup(self, context, group):
|
||||
common = self._login()
|
||||
try:
|
||||
return common.delete_consistencygroup(context, group)
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def update_consistencygroup(self, context, group,
|
||||
add_volumes=None, remove_volumes=None):
|
||||
common = self._login()
|
||||
try:
|
||||
return common.update_consistencygroup(context, group, add_volumes,
|
||||
remove_volumes)
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def create_cgsnapshot(self, context, cgsnapshot):
|
||||
common = self._login()
|
||||
try:
|
||||
return common.create_cgsnapshot(context, cgsnapshot)
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def delete_cgsnapshot(self, context, cgsnapshot):
|
||||
common = self._login()
|
||||
try:
|
||||
return common.delete_cgsnapshot(context, cgsnapshot)
|
||||
finally:
|
||||
self._logout(common)
|
||||
|
||||
def manage_existing(self, volume, existing_ref):
|
||||
common = self._login()
|
||||
try:
|
||||
|
Loading…
x
Reference in New Issue
Block a user