VOLUME_ID = 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'
CLONE_ID = 'd03338a9-9115-48a3-8dfc-000000000000'
+ VOLUME_TYPE_ID_DEDUP = 'd03338a9-9115-48a3-8dfc-11111111111'
VOLUME_NAME = 'volume-' + VOLUME_ID
VOLUME_NAME_3PAR = 'osv-0DM4qZEVSKON-DXN-NwVpw'
SNAPSHOT_ID = '2f823bdc-e36e-4dc8-bd15-de1c7a28ff31'
'volume_type': None,
'volume_type_id': None}
+ volume_dedup = {'name': VOLUME_NAME,
+ 'id': VOLUME_ID,
+ 'display_name': 'Foo Volume',
+ 'size': 2,
+ 'host': FAKE_HOST,
+ 'volume_type': 'dedup',
+ 'volume_type_id': VOLUME_TYPE_ID_DEDUP}
+
volume_pool = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'deleted_at': None,
'id': 'gold'}
+ volume_type_dedup = {'name': 'dedup',
+ 'deleted': False,
+ 'updated_at': None,
+ 'extra_specs': {'cpg': HP3PAR_CPG2,
+ 'provisioning': 'dedup'},
+ 'deleted_at': None,
+ 'id': VOLUME_TYPE_ID_DEDUP}
+
cpgs = [
{'SAGrowth': {'LDLayout': {'diskPatterns': [{'diskType': 2}]},
'incrementMiB': 8192},
'vvs': RETYPE_VVS_NAME,
'qos': RETYPE_QOS_SPECS,
'tpvv': True,
+ 'tdvv': False,
'volume_type': volume_type
}
}
'vvs': VVS_NAME,
'qos': QOS,
'tpvv': True,
+ 'tdvv': False,
'volume_type': volume_type
}
}
'vvs': RETYPE_VVS_NAME,
'qos': RETYPE_QOS_SPECS,
'tpvv': True,
+ 'tdvv': False,
'volume_type': volume_type
}
}
+ RETYPE_VOLUME_TYPE_3 = {
+ 'name': 'purple',
+ 'id': RETYPE_VOLUME_TYPE_ID,
+ 'extra_specs': {
+ 'cpg': HP3PAR_CPG_QOS,
+ 'snap_cpg': HP3PAR_CPG_SNAP,
+ 'vvs': RETYPE_VVS_NAME,
+ 'qos': RETYPE_QOS_SPECS,
+ 'tpvv': False,
+ 'tdvv': True,
+ 'volume_type': volume_type
+ }
+ }
RETYPE_VOLUME_TYPE_BAD_PERSONA = {
'name': 'bad_persona',
'id': 'any_id',
'comment': RETYPE_TEST_COMMENT
}
+ RETYPE_TEST_COMMENT_2 = "{'retype_test': 'test comment 2'}"
+
+ RETYPE_VOLUME_INFO_2 = {
+ 'name': VOLUME_NAME,
+ 'id': VOLUME_ID,
+ 'display_name': 'Retype Vol2',
+ 'size': 1,
+ 'host': RETYPE_HOST,
+ 'userCPG': HP3PAR_CPG,
+ 'snapCPG': HP3PAR_CPG_SNAP,
+ 'provisioningType': 3,
+ 'comment': RETYPE_TEST_COMMENT
+ }
# Test for when we don't get a snapCPG.
RETYPE_VOLUME_INFO_NO_SNAP = {
'name': VOLUME_NAME,
# intentionally removed to make _retype more usable for other use cases.
RETYPE_DIFF = None
+ wsapi_version = {'major': 1,
+ 'build': 30201120,
+ 'minor': 4,
+ 'revision': 1}
+
+ wsapi_version_312 = {'major': 1,
+ 'build': 30102422,
+ 'minor': 3,
+ 'revision': 1}
+
standard_login = [
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.setSSHOptions(
1907, {
'comment': comment,
'tpvv': True,
+ 'tdvv': False,
'snapCPG': HP3PAR_CPG_SNAP})]
mock_client.assert_has_calls(
1907, {
'comment': comment,
'tpvv': True,
+ 'tdvv': False,
'snapCPG': HP3PAR_CPG_SNAP})]
mock_client.assert_has_calls(
self.standard_logout)
self.assertEqual(return_model, None)
+ @mock.patch.object(volume_types, 'get_volume_type')
+ def test_unsupported_dedup_volume_type(self, _mock_volume_types):
+
+ mock_client = self.setup_driver_312()
+ _mock_volume_types.return_value = {
+ 'name': 'dedup',
+ 'extra_specs': {
+ 'cpg': HP3PAR_CPG_QOS,
+ 'snap_cpg': HP3PAR_CPG_SNAP,
+ 'vvs_name': self.VVS_NAME,
+ 'qos': self.QOS,
+ 'provisioning': 'dedup',
+ 'volume_type': self.volume_type_dedup}}
+
+ with mock.patch.object(hpcommon.HP3PARCommon,
+ '_create_client') as mock_create_client:
+ mock_create_client.return_value = mock_client
+ common = self.driver._login()
+
+ self.assertRaises(exception.InvalidInput,
+ common.get_volume_settings_from_type_id,
+ self.VOLUME_TYPE_ID_DEDUP,
+ "mock")
+
@mock.patch.object(volume_types, 'get_volume_type')
def test_get_snap_cpg_from_volume_type(self, _mock_volume_types):
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'tpvv': True,
+ 'tdvv': False,
'volume_type': self.volume_type}}
with mock.patch.object(hpcommon.HP3PARCommon,
1907, {
'comment': comment,
'tpvv': True,
+ 'tdvv': False,
+ 'snapCPG': HP3PAR_CPG_SNAP})]
+
+ mock_client.assert_has_calls(
+ self.standard_login +
+ expected +
+ self.standard_logout)
+ self.assertEqual(return_model,
+ {'host': volume_utils.append_host(
+ self.FAKE_HOST,
+ HP3PAR_CPG_QOS)})
+
+ @mock.patch.object(volume_types, 'get_volume_type')
+ def test_create_volume_dedup(self, _mock_volume_types):
+ # setup_mock_client drive with default configuration
+ # and return the mock HTTP 3PAR client
+ mock_client = self.setup_driver()
+
+ _mock_volume_types.return_value = {
+ 'name': 'dedup',
+ 'extra_specs': {
+ 'cpg': HP3PAR_CPG_QOS,
+ 'snap_cpg': HP3PAR_CPG_SNAP,
+ 'vvs_name': self.VVS_NAME,
+ 'qos': self.QOS,
+ 'provisioning': 'dedup',
+ 'volume_type': self.volume_type_dedup}}
+
+ with mock.patch.object(hpcommon.HP3PARCommon,
+ '_create_client') as mock_create_client:
+ mock_create_client.return_value = mock_client
+
+ return_model = self.driver.create_volume(self.volume_dedup)
+ comment = (
+ '{"volume_type_name": "dedup", "display_name": "Foo Volume"'
+ ', "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7'
+ '", "volume_type_id": "d03338a9-9115-48a3-8dfc-11111111111"'
+ ', "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"'
+ ', "qos": {}, "type": "OpenStack"}')
+
+ expected = [
+ mock.call.getCPG(HP3PAR_CPG_QOS),
+ mock.call.createVolume(
+ self.VOLUME_3PAR_NAME,
+ HP3PAR_CPG_QOS,
+ 1907, {
+ 'comment': comment,
+ 'tpvv': False,
+ 'tdvv': True,
'snapCPG': HP3PAR_CPG_SNAP})]
mock_client.assert_has_calls(
"old_type", "old_type_id",
HP3PARBaseDriver.RETYPE_HOST,
None, cpg, cpg, snap_cpg, snap_cpg,
- True, True, None, None,
+ True, False, False, True, None, None,
self.QOS_SPECS, self.RETYPE_QOS_SPECS,
"{}")
'osv-0DM4qZEVSKON-DXN-NwVpw')]
mock_client.assert_has_calls(expected)
+ @mock.patch.object(volume_types, 'get_volume_type')
+ def test_retype_dedup(self, _mock_volume_types):
+ _mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_3
+ mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF)
+
+ cpg = "any_cpg"
+ snap_cpg = "any_cpg"
+ with mock.patch.object(hpcommon.HP3PARCommon,
+ '_create_client') as mock_create_client:
+ mock_create_client.return_value = mock_client
+ common = self.driver._login()
+ common._retype(self.volume,
+ HP3PARBaseDriver.VOLUME_3PAR_NAME,
+ "old_type", "old_type_id",
+ HP3PARBaseDriver.RETYPE_HOST,
+ None, cpg, cpg, snap_cpg, snap_cpg,
+ True, False, False, True, None, None,
+ self.QOS_SPECS, self.RETYPE_QOS_SPECS,
+ "{}")
+
+ expected = [
+ mock.call.modifyVolume('osv-0DM4qZEVSKON-DXN-NwVpw',
+ {'action': 6,
+ 'userCPG': 'any_cpg',
+ 'conversionOperation': 3,
+ 'tuneOperation': 1}),
+ mock.call.getTask(1)]
+ mock_client.assert_has_calls(expected)
+
def test_delete_volume(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
'osv-0DM4qZEVSKON-AAAAAAAAA',
HP3PAR_CPG2,
{'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
- 'online': True})]
+ 'tdvv': False, 'online': True})]
mock_client.assert_has_calls(
self.standard_login +
'osv-0DM4qZEVSKON-AAAAAAAAA',
expected_cpg,
{'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
- 'online': True})]
+ 'tdvv': False, 'online': True})]
mock_client.assert_has_calls(
self.standard_login +
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
- self.driver.create_snapshot(self.snapshot)
+ with mock.patch.object(hpcommon.HP3PARCommon,
+ '_create_client') as mock_create_client:
+ mock_create_client.return_value = mock_client
+ self.driver.create_snapshot(self.snapshot)
- try:
- ex = hpexceptions.HTTPNotFound("not found")
- mock_client.deleteVolume = mock.Mock(side_effect=ex)
- self.driver.delete_snapshot(self.snapshot)
- except Exception:
- self.fail("Deleting a snapshot that is missing should act as if "
- "it worked.")
+ try:
+ ex = hpexceptions.HTTPNotFound("not found")
+ mock_client.deleteVolume = mock.Mock(side_effect=ex)
+ self.driver.delete_snapshot(self.snapshot)
+ except Exception:
+ self.fail("Deleting a snapshot that is missing should act "
+ "as if it worked.")
def test_create_volume_from_snapshot(self):
# setup_mock_client drive with default configuration
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'tpvv': True,
+ 'tdvv': False,
'volume_type': self.volume_type}}
with mock.patch.object(hpcommon.HP3PARCommon,
'getVolume.return_value': {}
}
- self.setup_driver(mock_conf=conf)
+ mock_client = self.setup_driver(mock_conf=conf)
- volume = self.volume.copy()
- volume['size'] = self.volume['size'] + 10
+ with mock.patch.object(hpcommon.HP3PARCommon,
+ '_create_client') as mock_create_client:
+ mock_create_client.return_value = mock_client
+ volume = self.volume.copy()
+ volume['size'] = self.volume['size'] + 10
- self.assertRaises(exception.CinderException,
- self.driver.create_volume_from_snapshot,
- volume, self.snapshot)
+ self.assertRaises(exception.CinderException,
+ self.driver.create_volume_from_snapshot,
+ volume, self.snapshot)
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_from_snapshot_qos(self, _mock_volume_types):
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'tpvv': True,
+ 'tdvv': False,
'volume_type': self.volume_type}}
self.driver.create_volume_from_snapshot(
self.volume_qos,
'vvs_name': self.VVS_NAME,
'qos': self.QOS,
'tpvv': True,
+ 'tdvv': False,
'volume_type': self.volume_type}}
volume = {'display_name': None,
m_conf=mock_conf,
driver=hpfcdriver.HP3PARFCDriver)
+ mock_client.getWsApiVersion.return_value = self.wsapi_version
+
+ expected = [
+ mock.call.getCPG(HP3PAR_CPG),
+ mock.call.getCPG(HP3PAR_CPG2)]
+ mock_client.assert_has_calls(
+ self.standard_login +
+ expected +
+ self.standard_logout)
+ mock_client.reset_mock()
+ return mock_client
+
+ def setup_driver_312(self, config=None, mock_conf=None):
+
+ self.ctxt = context.get_admin_context()
+ mock_client = self.setup_mock_client(
+ conf=config,
+ m_conf=mock_conf,
+ driver=hpfcdriver.HP3PARFCDriver)
+
+ mock_client.getWsApiVersion.return_value = self.wsapi_version_312
+
expected = [
mock.call.getCPG(HP3PAR_CPG),
mock.call.getCPG(HP3PAR_CPG2)]
m_conf=mock_conf,
driver=hpdriver.HP3PARISCSIDriver)
+ mock_client.getWsApiVersion.return_value = self.wsapi_version
+
+ expected_get_cpgs = [
+ mock.call.getCPG(HP3PAR_CPG),
+ mock.call.getCPG(HP3PAR_CPG2)]
+ expected_get_ports = [mock.call.getPorts()]
+ mock_client.assert_has_calls(
+ self.standard_login +
+ expected_get_cpgs +
+ self.standard_logout +
+ self.standard_login +
+ expected_get_ports +
+ self.standard_logout)
+ mock_client.reset_mock()
+
+ return mock_client
+
+ def setup_driver_312(self, config=None, mock_conf=None):
+
+ self.ctxt = context.get_admin_context()
+
+ mock_client = self.setup_mock_client(
+ conf=config,
+ m_conf=mock_conf,
+ driver=hpdriver.HP3PARISCSIDriver)
+
+ mock_client.getWsApiVersion.return_value = self.wsapi_version_312
+
expected_get_cpgs = [
mock.call.getCPG(HP3PAR_CPG),
mock.call.getCPG(HP3PAR_CPG2)]
-# (c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
+# (c) Copyright 2012-2015 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
LOG = logging.getLogger(__name__)
MIN_CLIENT_VERSION = '3.1.2'
+DEDUP_API_VERSION = 30201120
hp3par_opts = [
cfg.StrOpt('hp3par_api_url',
2.0.33 - Fix host persona to match WSAPI mapping bug #1403997
2.0.34 - Fix log messages to match guidelines. bug #1411370
2.0.35 - Fix default snapCPG for manage_existing bug #1393609
+ 2.0.36 - Added support for dedup provisioning
"""
- VERSION = "2.0.35"
+ VERSION = "2.0.36"
stats = {}
VLUN_TYPE_HOST_SET = 5
THIN = 2
+ DEDUP = 6
CONVERT_TO_THIN = 1
CONVERT_TO_FULL = 2
+ CONVERT_TO_DEDUP = 3
# Valid values for volume type extra specs
# The first value in the list is the default value
- valid_prov_values = ['thin', 'full']
+ valid_prov_values = ['thin', 'full', 'dedup']
valid_persona_values = ['2 - Generic-ALUA',
'1 - Generic',
'3 - Generic-legacy',
raise exception.VolumeBackendAPIException(data=msg)
try:
self.client = self._create_client()
+ wsapi_version = self.client.getWsApiVersion()
+ self.API_VERSION = wsapi_version['build']
except hpexceptions.UnsupportedVersion as ex:
raise exception.InvalidInput(ex)
LOG.info(_LI("HP3PARCommon %(common_ver)s, hp3parclient %(rest_ver)s"),
if persona_value not in self.valid_persona_values:
err = (_("Must specify a valid persona %(valid)s,"
"value '%(persona)s' is invalid.") %
- ({'valid': self.valid_persona_values,
- 'persona': persona_value}))
+ {'valid': self.valid_persona_values,
+ 'persona': persona_value})
LOG.error(err)
raise exception.InvalidInput(reason=err)
# persona is set by the id so remove the text and return the id
default_prov)
# check for valid provisioning type
if prov_value not in self.valid_prov_values:
- err = _("Must specify a valid provisioning type %(valid)s, "
- "value '%(prov)s' is invalid.") % \
- ({'valid': self.valid_prov_values,
- 'prov': prov_value})
+ err = (_("Must specify a valid provisioning type %(valid)s, "
+ "value '%(prov)s' is invalid.") %
+ {'valid': self.valid_prov_values,
+ 'prov': prov_value})
LOG.error(err)
raise exception.InvalidInput(reason=err)
tpvv = True
+ tdvv = False
if prov_value == "full":
tpvv = False
+ elif prov_value == "dedup":
+ tpvv = False
+ tdvv = True
+
+ if tdvv and (self.API_VERSION < DEDUP_API_VERSION):
+ err = (_("Dedup is a valid provisioning type, "
+ "but requires WSAPI version '%(dedup_version)s' "
+ "version '%(version)s' is installed.") %
+ {'dedup_version': DEDUP_API_VERSION,
+ 'version': self.API_VERSION})
+ LOG.error(err)
+ raise exception.InvalidInput(reason=err)
return {'hp3par_keys': hp3par_keys,
'cpg': cpg, 'snap_cpg': snap_cpg,
'vvs_name': vvs_name, 'qos': qos,
- 'tpvv': tpvv, 'volume_type': volume_type}
+ 'tpvv': tpvv, 'tdvv': tdvv, 'volume_type': volume_type}
def get_volume_settings_from_type(self, volume, host=None):
"""Get 3PAR volume settings given a volume.
cpg = type_info['cpg']
snap_cpg = type_info['snap_cpg']
tpvv = type_info['tpvv']
+ tdvv = type_info['tdvv']
type_id = volume.get('volume_type_id', None)
if type_id is not None:
'snapCPG': snap_cpg,
'tpvv': tpvv}
+ # Only set the dedup option if the backend supports it.
+ if self.API_VERSION >= DEDUP_API_VERSION:
+ extras['tdvv'] = tdvv
+
capacity = self._capacity_from_size(volume['size'])
volume_name = self._get_3par_vol_name(volume['id'])
self.client.createVolume(volume_name, cpg, capacity, extras)
return self._get_model_update(volume['host'], cpg)
def _copy_volume(self, src_name, dest_name, cpg, snap_cpg=None,
- tpvv=True):
+ tpvv=True, tdvv=False):
# Virtual volume sets are not supported with the -online option
LOG.debug('Creating clone of a volume %(src)s to %(dest)s.',
{'src': src_name, 'dest': dest_name})
if snap_cpg is not None:
optional['snapCPG'] = snap_cpg
+ if self.API_VERSION >= DEDUP_API_VERSION:
+ optional['tdvv'] = tdvv
+
body = self.client.copyVolume(src_name, dest_name, cpg, optional)
return body['taskid']
cpg = type_info['cpg']
self._copy_volume(orig_name, vol_name, cpg=cpg,
snap_cpg=type_info['snap_cpg'],
- tpvv=type_info['tpvv'])
+ tpvv=type_info['tpvv'],
+ tdvv=type_info['tdvv'])
return self._get_model_update(volume['host'], cpg)
# Create a physical copy of the volume
task_id = self._copy_volume(volume_name, temp_vol_name,
- cpg, cpg, type_info['tpvv'])
+ cpg, cpg, type_info['tpvv'],
+ type_info['tdvv'])
LOG.debug('Copy volume scheduled: convert_to_base_volume: '
'id=%s.', volume['id'])
portPos['cardPort'] = int(split[2])
return portPos
- def tune_vv(self, old_tpvv, new_tpvv, old_cpg, new_cpg, volume_name):
+ def tune_vv(self, old_tpvv, new_tpvv, old_tdvv, new_tdvv,
+ old_cpg, new_cpg, volume_name):
"""Tune the volume to change the userCPG and/or provisioningType.
The volume will be modified/tuned/converted to the new userCPG and
either be done or it is in a state that we need to treat as an error.
"""
- if old_tpvv == new_tpvv:
+ if old_tpvv == new_tpvv and old_tdvv == new_tdvv:
if new_cpg != old_cpg:
LOG.info(_LI("Modifying %(volume_name)s userCPG "
"from %(old_cpg)s"
{'status': status, 'volume_name': volume_name})
raise exception.VolumeBackendAPIException(msg)
else:
- if old_tpvv:
- cop = self.CONVERT_TO_FULL
- LOG.info(_LI("Converting %(volume_name)s to full provisioning "
- "with userCPG=%(new_cpg)s"),
- {'volume_name': volume_name, 'new_cpg': new_cpg})
- else:
+ if new_tpvv:
cop = self.CONVERT_TO_THIN
LOG.info(_LI("Converting %(volume_name)s to thin provisioning "
- "with userCPG=%(new_cpg)s"),
+ "with userCPG=%(new_cpg)s") %
+ {'volume_name': volume_name, 'new_cpg': new_cpg})
+ elif new_tdvv:
+ cop = self.CONVERT_TO_DEDUP
+ LOG.info(_LI("Converting %(volume_name)s to thin dedup "
+ "provisioning with userCPG=%(new_cpg)s") %
+ {'volume_name': volume_name, 'new_cpg': new_cpg})
+ else:
+ cop = self.CONVERT_TO_FULL
+ LOG.info(_LI("Converting %(volume_name)s to full provisioning "
+ "with userCPG=%(new_cpg)s") %
{'volume_name': volume_name, 'new_cpg': new_cpg})
try:
def _retype(self, volume, volume_name, new_type_name, new_type_id, host,
new_persona, old_cpg, new_cpg, old_snap_cpg, new_snap_cpg,
- old_tpvv, new_tpvv, old_vvs, new_vvs, old_qos, new_qos,
- old_comment):
+ old_tpvv, new_tpvv, old_tdvv, new_tdvv, old_vvs, new_vvs,
+ old_qos, new_qos, old_comment):
action = "volume:retype"
store={'common': self,
'volume_name': volume_name, 'volume': volume,
'old_tpvv': old_tpvv, 'new_tpvv': new_tpvv,
+ 'old_tdvv': old_tdvv, 'new_tdvv': new_tdvv,
'old_cpg': old_cpg, 'new_cpg': new_cpg,
'old_snap_cpg': old_snap_cpg, 'new_snap_cpg': new_snap_cpg,
'old_vvs': old_vvs, 'new_vvs': new_vvs,
new_cpg = new_volume_settings['cpg']
new_snap_cpg = new_volume_settings['snap_cpg']
new_tpvv = new_volume_settings['tpvv']
+ new_tdvv = new_volume_settings['tdvv']
new_qos = new_volume_settings['qos']
new_vvs = new_volume_settings['vvs_name']
new_persona = None
# same settings that were used with this volume.
old_volume_info = self.client.getVolume(volume_name)
old_tpvv = old_volume_info['provisioningType'] == self.THIN
+ old_tdvv = old_volume_info['provisioningType'] == self.DEDUP
old_cpg = old_volume_info['userCPG']
old_comment = old_volume_info['comment']
old_snap_cpg = None
self._retype(volume, volume_name, new_type_name, new_type_id,
host, new_persona, old_cpg, new_cpg,
old_snap_cpg, new_snap_cpg, old_tpvv, new_tpvv,
- old_vvs, new_vvs, old_qos, new_qos, old_comment)
+ old_tdvv, new_tdvv, old_vvs, new_vvs, old_qos,
+ new_qos, old_comment)
if host:
return True, self._get_model_update(host['host'], new_cpg)
def __init__(self, action, **kwargs):
super(TuneVolumeTask, self).__init__(addons=[action])
- def execute(self, common, old_tpvv, new_tpvv, old_cpg, new_cpg,
- volume_name):
- common.tune_vv(old_tpvv, new_tpvv, old_cpg, new_cpg, volume_name)
+ def execute(self, common, old_tpvv, new_tpvv, old_tdvv, new_tdvv,
+ old_cpg, new_cpg, volume_name):
+ common.tune_vv(old_tpvv, new_tpvv, old_tdvv, new_tdvv,
+ old_cpg, new_cpg, volume_name)
class ModifySpecsTask(flow_utils.CinderTask):