Merge "3PAR with pool-aware-cinder-scheduler"
This commit is contained in:
commit
0035c961d5
@ -31,6 +31,7 @@ from cinder.volume.drivers.san.hp import hp_3par_common as hpcommon
|
||||
from cinder.volume.drivers.san.hp import hp_3par_fc as hpfcdriver
|
||||
from cinder.volume.drivers.san.hp import hp_3par_iscsi as hpdriver
|
||||
from cinder.volume import qos_specs
|
||||
from cinder.volume import utils as volume_utils
|
||||
from cinder.volume import volume_types
|
||||
|
||||
hpexceptions = hp3parclient.hpexceptions
|
||||
@ -40,6 +41,8 @@ LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
HP3PAR_CPG = 'OpenStackCPG'
|
||||
HP3PAR_CPG2 = 'fakepool'
|
||||
HP3PAR_CPG_QOS = 'qospool'
|
||||
HP3PAR_CPG_SNAP = 'OpenStackCPGSnap'
|
||||
HP3PAR_USER_NAME = 'testUser'
|
||||
HP3PAR_USER_PASS = 'testPassword'
|
||||
@ -109,6 +112,14 @@ class HP3PARBaseDriver(object):
|
||||
'volume_type': None,
|
||||
'volume_type_id': None}
|
||||
|
||||
volume_pool = {'name': VOLUME_NAME,
|
||||
'id': VOLUME_ID,
|
||||
'display_name': 'Foo Volume',
|
||||
'size': 2,
|
||||
'host': volume_utils.append_host(FAKE_HOST, HP3PAR_CPG2),
|
||||
'volume_type': None,
|
||||
'volume_type_id': None}
|
||||
|
||||
volume_qos = {'name': VOLUME_NAME,
|
||||
'id': VOLUME_ID,
|
||||
'display_name': 'Foo Volume',
|
||||
@ -140,7 +151,8 @@ class HP3PARBaseDriver(object):
|
||||
volume_type = {'name': 'gold',
|
||||
'deleted': False,
|
||||
'updated_at': None,
|
||||
'extra_specs': {'qos:maxIOPS': '1000',
|
||||
'extra_specs': {'cpg': HP3PAR_CPG2,
|
||||
'qos:maxIOPS': '1000',
|
||||
'qos:maxBWS': '50',
|
||||
'qos:minIOPS': '100',
|
||||
'qos:minBWS': '25',
|
||||
@ -259,7 +271,7 @@ class HP3PARBaseDriver(object):
|
||||
'name': 'blue',
|
||||
'id': RETYPE_VOLUME_TYPE_ID,
|
||||
'extra_specs': {
|
||||
'cpg': HP3PAR_CPG,
|
||||
'cpg': HP3PAR_CPG_QOS,
|
||||
'snap_cpg': HP3PAR_CPG_SNAP,
|
||||
'vvs': RETYPE_VVS_NAME,
|
||||
'qos': RETYPE_QOS_SPECS,
|
||||
@ -353,7 +365,7 @@ class HP3PARBaseDriver(object):
|
||||
configuration.hp3par_username = HP3PAR_USER_NAME
|
||||
configuration.hp3par_password = HP3PAR_USER_PASS
|
||||
configuration.hp3par_api_url = 'https://1.1.1.1/api/v1'
|
||||
configuration.hp3par_cpg = HP3PAR_CPG
|
||||
configuration.hp3par_cpg = [HP3PAR_CPG, HP3PAR_CPG2]
|
||||
configuration.hp3par_cpg_snap = HP3PAR_CPG_SNAP
|
||||
configuration.iscsi_ip_address = '1.1.1.2'
|
||||
configuration.iscsi_port = '1234'
|
||||
@ -411,6 +423,7 @@ class HP3PARBaseDriver(object):
|
||||
conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.getCPG(HP3PAR_CPG2),
|
||||
mock.call.logout()]
|
||||
mock_client.assert_has_calls(expected)
|
||||
|
||||
@ -441,6 +454,7 @@ class HP3PARBaseDriver(object):
|
||||
conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.getCPG(HP3PAR_CPG2),
|
||||
mock.call.logout()]
|
||||
mock_client.assert_has_calls(expected)
|
||||
|
||||
@ -471,6 +485,7 @@ class HP3PARBaseDriver(object):
|
||||
conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.getCPG(HP3PAR_CPG2),
|
||||
mock.call.logout()]
|
||||
mock_client.assert_has_calls(expected)
|
||||
|
||||
@ -520,6 +535,30 @@ class HP3PARBaseDriver(object):
|
||||
|
||||
mock_client.assert_has_calls(expected)
|
||||
|
||||
def test_create_volume_in_pool(self):
|
||||
|
||||
# setup_mock_client drive with default configuration
|
||||
# and return the mock HTTP 3PAR client
|
||||
mock_client = self.setup_driver()
|
||||
return_model = self.driver.create_volume(self.volume_pool)
|
||||
comment = (
|
||||
'{"display_name": "Foo Volume", "type": "OpenStack",'
|
||||
' "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",'
|
||||
' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}')
|
||||
expected = [
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.createVolume(
|
||||
self.VOLUME_3PAR_NAME,
|
||||
HP3PAR_CPG2,
|
||||
1907, {
|
||||
'comment': comment,
|
||||
'tpvv': True,
|
||||
'snapCPG': HP3PAR_CPG_SNAP}),
|
||||
mock.call.logout()]
|
||||
|
||||
mock_client.assert_has_calls(expected)
|
||||
self.assertEqual(return_model, None)
|
||||
|
||||
@mock.patch.object(volume_types, 'get_volume_type')
|
||||
def test_create_volume_qos(self, _mock_volume_types):
|
||||
# setup_mock_client drive with default configuration
|
||||
@ -529,14 +568,14 @@ class HP3PARBaseDriver(object):
|
||||
_mock_volume_types.return_value = {
|
||||
'name': 'gold',
|
||||
'extra_specs': {
|
||||
'cpg': HP3PAR_CPG,
|
||||
'cpg': HP3PAR_CPG_QOS,
|
||||
'snap_cpg': HP3PAR_CPG_SNAP,
|
||||
'vvs_name': self.VVS_NAME,
|
||||
'qos': self.QOS,
|
||||
'tpvv': True,
|
||||
'volume_type': self.volume_type}}
|
||||
|
||||
self.driver.create_volume(self.volume_qos)
|
||||
return_model = self.driver.create_volume(self.volume_qos)
|
||||
comment = (
|
||||
'{"volume_type_name": "gold", "display_name": "Foo Volume"'
|
||||
', "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7'
|
||||
@ -545,9 +584,10 @@ class HP3PARBaseDriver(object):
|
||||
|
||||
expected = [
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getCPG(HP3PAR_CPG_QOS),
|
||||
mock.call.createVolume(
|
||||
self.VOLUME_3PAR_NAME,
|
||||
HP3PAR_CPG,
|
||||
HP3PAR_CPG_QOS,
|
||||
1907, {
|
||||
'comment': comment,
|
||||
'tpvv': True,
|
||||
@ -555,6 +595,9 @@ class HP3PARBaseDriver(object):
|
||||
mock.call.logout()]
|
||||
|
||||
mock_client.assert_has_calls(expected)
|
||||
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_retype_not_3par(self, _mock_volume_types):
|
||||
@ -803,7 +846,9 @@ class HP3PARBaseDriver(object):
|
||||
|
||||
volume = {'id': HP3PARBaseDriver.CLONE_ID}
|
||||
|
||||
self.driver.retype(self.ctxt, volume, type_ref, None, self.RETYPE_HOST)
|
||||
retyped = self.driver.retype(
|
||||
self.ctxt, volume, type_ref, None, self.RETYPE_HOST)
|
||||
self.assertTrue(retyped)
|
||||
|
||||
expected = [
|
||||
mock.call.modifyVolume('osv-0DM4qZEVSKON-AAAAAAAAA',
|
||||
@ -876,18 +921,51 @@ class HP3PARBaseDriver(object):
|
||||
'id': HP3PARBaseDriver.CLONE_ID,
|
||||
'display_name': 'Foo Volume',
|
||||
'size': 2,
|
||||
'host': HP3PARBaseDriver.FAKE_HOST,
|
||||
'host': volume_utils.append_host(self.FAKE_HOST,
|
||||
HP3PAR_CPG2),
|
||||
'source_volid': HP3PARBaseDriver.VOLUME_ID}
|
||||
src_vref = {}
|
||||
model_update = self.driver.create_cloned_volume(volume, src_vref)
|
||||
self.assertIsNotNone(model_update)
|
||||
self.assertIsNone(model_update)
|
||||
|
||||
expected = [
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.copyVolume(
|
||||
self.VOLUME_3PAR_NAME,
|
||||
'osv-0DM4qZEVSKON-AAAAAAAAA',
|
||||
HP3PAR_CPG,
|
||||
HP3PAR_CPG2,
|
||||
{'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
|
||||
'online': True}),
|
||||
mock.call.logout()]
|
||||
|
||||
mock_client.assert_has_calls(expected)
|
||||
|
||||
@mock.patch.object(volume_types, 'get_volume_type')
|
||||
def test_create_cloned_qos_volume(self, _mock_volume_types):
|
||||
_mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_2
|
||||
mock_client = self.setup_driver()
|
||||
mock_client.copyVolume.return_value = {'taskid': 1}
|
||||
|
||||
src_vref = {}
|
||||
volume = self.volume_qos.copy()
|
||||
host = "TEST_HOST"
|
||||
pool = "TEST_POOL"
|
||||
volume_host = volume_utils.append_host(host, pool)
|
||||
expected_cpg = self.RETYPE_VOLUME_TYPE_2['extra_specs']['cpg']
|
||||
expected_volume_host = volume_utils.append_host(host, expected_cpg)
|
||||
volume['id'] = HP3PARBaseDriver.CLONE_ID
|
||||
volume['host'] = volume_host
|
||||
volume['source_volid'] = HP3PARBaseDriver.VOLUME_ID
|
||||
model_update = self.driver.create_cloned_volume(volume, src_vref)
|
||||
self.assertEqual(model_update, {'host': expected_volume_host})
|
||||
|
||||
expected = [
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getCPG(expected_cpg),
|
||||
mock.call.copyVolume(
|
||||
self.VOLUME_3PAR_NAME,
|
||||
'osv-0DM4qZEVSKON-AAAAAAAAA',
|
||||
expected_cpg,
|
||||
{'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
|
||||
'online': True}),
|
||||
mock.call.logout()]
|
||||
@ -1146,7 +1224,9 @@ class HP3PARBaseDriver(object):
|
||||
# setup_mock_client drive with default configuration
|
||||
# and return the mock HTTP 3PAR client
|
||||
mock_client = self.setup_driver()
|
||||
self.driver.create_volume_from_snapshot(self.volume, self.snapshot)
|
||||
model_update = self.driver.create_volume_from_snapshot(self.volume,
|
||||
self.snapshot)
|
||||
self.assertIsNone(model_update)
|
||||
|
||||
comment = (
|
||||
'{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",'
|
||||
@ -1185,7 +1265,11 @@ class HP3PARBaseDriver(object):
|
||||
|
||||
volume = self.volume.copy()
|
||||
volume['size'] = self.volume['size'] + 10
|
||||
self.driver.create_volume_from_snapshot(volume, self.snapshot)
|
||||
model_update = self.driver.create_volume_from_snapshot(volume,
|
||||
self.snapshot)
|
||||
self.assertEqual(model_update,
|
||||
{'host': volume_utils.append_host(self.FAKE_HOST,
|
||||
HP3PAR_CPG)})
|
||||
|
||||
comment = (
|
||||
'{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",'
|
||||
@ -1204,7 +1288,68 @@ class HP3PARBaseDriver(object):
|
||||
{
|
||||
'comment': comment,
|
||||
'readOnly': False}),
|
||||
mock.call.copyVolume(osv_matcher, omv_matcher, mock.ANY, mock.ANY),
|
||||
mock.call.copyVolume(
|
||||
osv_matcher, omv_matcher, HP3PAR_CPG, mock.ANY),
|
||||
mock.call.getTask(mock.ANY),
|
||||
mock.call.getVolume(osv_matcher),
|
||||
mock.call.deleteVolume(osv_matcher),
|
||||
mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher}),
|
||||
mock.call.growVolume(osv_matcher, 10 * 1024),
|
||||
mock.call.logout()]
|
||||
|
||||
mock_client.assert_has_calls(expected)
|
||||
|
||||
@mock.patch.object(volume_types, 'get_volume_type')
|
||||
def test_create_volume_from_snapshot_and_extend_with_qos(
|
||||
self, _mock_volume_types):
|
||||
# setup_mock_client drive with default configuration
|
||||
# and return the mock HTTP 3PAR client
|
||||
conf = {
|
||||
'getTask.return_value': {
|
||||
'status': 1},
|
||||
'copyVolume.return_value': {'taskid': 1},
|
||||
'getVolume.return_value': {}
|
||||
}
|
||||
|
||||
mock_client = self.setup_driver(mock_conf=conf)
|
||||
_mock_volume_types.return_value = {
|
||||
'name': 'gold',
|
||||
'extra_specs': {
|
||||
'cpg': HP3PAR_CPG_QOS,
|
||||
'snap_cpg': HP3PAR_CPG_SNAP,
|
||||
'vvs_name': self.VVS_NAME,
|
||||
'qos': self.QOS,
|
||||
'tpvv': True,
|
||||
'volume_type': self.volume_type}}
|
||||
|
||||
volume = self.volume_qos.copy()
|
||||
volume['size'] = self.volume['size'] + 10
|
||||
model_update = self.driver.create_volume_from_snapshot(volume,
|
||||
self.snapshot)
|
||||
self.assertEqual(model_update,
|
||||
{'host': volume_utils.append_host(self.FAKE_HOST,
|
||||
HP3PAR_CPG_QOS)})
|
||||
|
||||
comment = (
|
||||
'{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",'
|
||||
' "display_name": "Foo Volume",'
|
||||
' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}')
|
||||
|
||||
volume_name_3par = self.driver.common._encode_name(volume['id'])
|
||||
osv_matcher = 'osv-' + volume_name_3par
|
||||
omv_matcher = 'omv-' + volume_name_3par
|
||||
|
||||
expected = [
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.createSnapshot(
|
||||
self.VOLUME_3PAR_NAME,
|
||||
'oss-L4I73ONuTci9Fd4ceij-MQ',
|
||||
{
|
||||
'comment': comment,
|
||||
'readOnly': False}),
|
||||
mock.call.getCPG(HP3PAR_CPG_QOS),
|
||||
mock.call.copyVolume(
|
||||
osv_matcher, omv_matcher, HP3PAR_CPG_QOS, mock.ANY),
|
||||
mock.call.getTask(mock.ANY),
|
||||
mock.call.getVolume(osv_matcher),
|
||||
mock.call.deleteVolume(osv_matcher),
|
||||
@ -1537,6 +1682,7 @@ class HP3PARBaseDriver(object):
|
||||
"type": "OpenStack"}
|
||||
|
||||
volume = {'display_name': None,
|
||||
'host': 'my-stack1@3parxxx#CPGNOTUSED',
|
||||
'volume_type': 'gold',
|
||||
'volume_type_id': 'acfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
|
||||
'id': '007dbfce-7579-40bc-8f90-a20b3902283e'}
|
||||
@ -1552,7 +1698,8 @@ class HP3PARBaseDriver(object):
|
||||
|
||||
obj = self.driver.manage_existing(volume, existing_ref)
|
||||
|
||||
expected_obj = {'display_name': 'Foo Volume'}
|
||||
expected_obj = {'display_name': 'Foo Volume',
|
||||
'host': 'my-stack1@3parxxx#fakepool'}
|
||||
|
||||
expected_manage = [
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
@ -1593,10 +1740,11 @@ class HP3PARBaseDriver(object):
|
||||
'bwMinGoalKB': 25600, 'priority': 1, 'latencyGoal': 25,
|
||||
'bwMaxLimitKB': 51200}),
|
||||
mock.call.addVolumeToVolumeSet(vvs_matcher, osv_matcher),
|
||||
mock.call.modifyVolume(osv_matcher,
|
||||
{'action': 6, 'userCPG': 'OpenStackCPG',
|
||||
'conversionOperation': 1,
|
||||
'tuneOperation': 1}),
|
||||
mock.call.modifyVolume(
|
||||
osv_matcher,
|
||||
{'action': 6,
|
||||
'userCPG': self.volume_type['extra_specs']['cpg'],
|
||||
'conversionOperation': 1, 'tuneOperation': 1}),
|
||||
mock.call.getTask(1),
|
||||
mock.call.logout()
|
||||
]
|
||||
@ -1624,6 +1772,7 @@ class HP3PARBaseDriver(object):
|
||||
"type": "OpenStack"}
|
||||
|
||||
volume = {'display_name': 'Test Volume',
|
||||
'host': 'my-stack1@3parxxx#CPGNOTUSED',
|
||||
'volume_type': 'gold',
|
||||
'volume_type_id': 'acfa9fa4-54a0-4340-a3d8-bfcf19aea65e',
|
||||
'id': id}
|
||||
@ -1636,7 +1785,8 @@ class HP3PARBaseDriver(object):
|
||||
|
||||
obj = self.driver.manage_existing(volume, existing_ref)
|
||||
|
||||
expected_obj = {'display_name': 'Test Volume'}
|
||||
expected_obj = {'display_name': 'Test Volume',
|
||||
'host': 'my-stack1@3parxxx#qospool'}
|
||||
expected_manage = [
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getVolume(existing_ref['source-name']),
|
||||
@ -1661,7 +1811,8 @@ class HP3PARBaseDriver(object):
|
||||
mock.call.deleteVolumeSet(vvs_matcher),
|
||||
mock.call.addVolumeToVolumeSet(vvs, osv_matcher),
|
||||
mock.call.modifyVolume(osv_matcher,
|
||||
{'action': 6, 'userCPG': 'OpenStackCPG',
|
||||
{'action': 6, 'userCPG':
|
||||
test_volume_type['extra_specs']['cpg'],
|
||||
'conversionOperation': 1,
|
||||
'tuneOperation': 1}),
|
||||
mock.call.getTask(1),
|
||||
@ -1976,6 +2127,7 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
|
||||
conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.getCPG(HP3PAR_CPG2),
|
||||
mock.call.logout()]
|
||||
mock_client.assert_has_calls(expected)
|
||||
mock_client.reset_mock()
|
||||
@ -2264,30 +2416,33 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
|
||||
# and return the mock HTTP 3PAR client
|
||||
mock_client = self.setup_driver()
|
||||
mock_client.getCPG.return_value = self.cpgs[0]
|
||||
totalCapacityMiB = 8000
|
||||
freeCapacityMiB = 4000
|
||||
mock_client.getStorageSystemInfo.return_value = {
|
||||
'serialNumber': '1234',
|
||||
'totalCapacityMiB': totalCapacityMiB,
|
||||
'freeCapacityMiB': freeCapacityMiB
|
||||
'freeCapacityMiB': 1024.0 * 2,
|
||||
'totalCapacityMiB': 1024.0 * 123
|
||||
}
|
||||
stats = self.driver.get_volume_stats(True)
|
||||
const = 0.0009765625
|
||||
self.assertEqual(stats['storage_protocol'], 'FC')
|
||||
self.assertEqual(stats['total_capacity_gb'], totalCapacityMiB * const)
|
||||
self.assertEqual(stats['free_capacity_gb'], freeCapacityMiB * const)
|
||||
self.assertEqual(stats['total_capacity_gb'], 0)
|
||||
self.assertEqual(stats['free_capacity_gb'], 0)
|
||||
self.assertEqual(stats['pools'][0]['total_capacity_gb'], 123.0)
|
||||
self.assertEqual(stats['pools'][0]['free_capacity_gb'], 2.0)
|
||||
|
||||
expected = [
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getStorageSystemInfo(),
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.getCPG(HP3PAR_CPG2),
|
||||
mock.call.logout()]
|
||||
|
||||
mock_client.assert_has_calls(expected)
|
||||
stats = self.driver.get_volume_stats(True)
|
||||
self.assertEqual(stats['storage_protocol'], 'FC')
|
||||
self.assertEqual(stats['total_capacity_gb'], totalCapacityMiB * const)
|
||||
self.assertEqual(stats['free_capacity_gb'], freeCapacityMiB * const)
|
||||
self.assertEqual(stats['total_capacity_gb'], 0)
|
||||
self.assertEqual(stats['free_capacity_gb'], 0)
|
||||
self.assertEqual(stats['pools'][0]['total_capacity_gb'], 123.0)
|
||||
self.assertEqual(stats['pools'][0]['free_capacity_gb'], 2.0)
|
||||
|
||||
cpg2 = self.cpgs[0].copy()
|
||||
cpg2.update({'SDGrowth': {'limitMiB': 8192}})
|
||||
@ -2296,10 +2451,14 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
|
||||
stats = self.driver.get_volume_stats(True)
|
||||
self.assertEqual(stats['storage_protocol'], 'FC')
|
||||
total_capacity_gb = 8192 * const
|
||||
self.assertEqual(stats['total_capacity_gb'], total_capacity_gb)
|
||||
self.assertEqual(stats['total_capacity_gb'], 0)
|
||||
self.assertEqual(stats['pools'][0]['total_capacity_gb'],
|
||||
total_capacity_gb)
|
||||
free_capacity_gb = int(
|
||||
(8192 - self.cpgs[0]['UsrUsage']['usedMiB']) * const)
|
||||
self.assertEqual(stats['free_capacity_gb'], free_capacity_gb)
|
||||
self.assertEqual(stats['free_capacity_gb'], 0)
|
||||
self.assertEqual(stats['pools'][0]['free_capacity_gb'],
|
||||
free_capacity_gb)
|
||||
self.driver.common.client.deleteCPG(HP3PAR_CPG)
|
||||
self.driver.common.client.createCPG(HP3PAR_CPG, {})
|
||||
|
||||
@ -2501,6 +2660,7 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
|
||||
conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.getCPG(HP3PAR_CPG2),
|
||||
mock.call.logout(),
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getPorts(),
|
||||
@ -2557,23 +2717,24 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
|
||||
# and return the mock HTTP 3PAR client
|
||||
mock_client = self.setup_driver()
|
||||
mock_client.getCPG.return_value = self.cpgs[0]
|
||||
totalCapacityMiB = 8000
|
||||
freeCapacityMiB = 4000
|
||||
mock_client.getStorageSystemInfo.return_value = {
|
||||
'serialNumber': '1234',
|
||||
'totalCapacityMiB': totalCapacityMiB,
|
||||
'freeCapacityMiB': freeCapacityMiB
|
||||
'freeCapacityMiB': 1024.0 * 2,
|
||||
'totalCapacityMiB': 1024.0 * 123
|
||||
}
|
||||
stats = self.driver.get_volume_stats(True)
|
||||
const = 0.0009765625
|
||||
self.assertEqual(stats['storage_protocol'], 'iSCSI')
|
||||
self.assertEqual(stats['total_capacity_gb'], totalCapacityMiB * const)
|
||||
self.assertEqual(stats['free_capacity_gb'], freeCapacityMiB * const)
|
||||
self.assertEqual(stats['total_capacity_gb'], 0)
|
||||
self.assertEqual(stats['free_capacity_gb'], 0)
|
||||
self.assertEqual(stats['pools'][0]['total_capacity_gb'], 123.0)
|
||||
self.assertEqual(stats['pools'][0]['free_capacity_gb'], 2.0)
|
||||
|
||||
expected = [
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getStorageSystemInfo(),
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.getCPG(HP3PAR_CPG2),
|
||||
mock.call.logout()]
|
||||
|
||||
mock_client.assert_has_calls(expected)
|
||||
@ -2585,10 +2746,14 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
|
||||
stats = self.driver.get_volume_stats(True)
|
||||
self.assertEqual(stats['storage_protocol'], 'iSCSI')
|
||||
total_capacity_gb = 8192 * const
|
||||
self.assertEqual(stats['total_capacity_gb'], total_capacity_gb)
|
||||
self.assertEqual(stats['total_capacity_gb'], 0)
|
||||
self.assertEqual(stats['pools'][0]['total_capacity_gb'],
|
||||
total_capacity_gb)
|
||||
free_capacity_gb = int(
|
||||
(8192 - self.cpgs[0]['UsrUsage']['usedMiB']) * const)
|
||||
self.assertEqual(stats['free_capacity_gb'], free_capacity_gb)
|
||||
self.assertEqual(stats['free_capacity_gb'], 0)
|
||||
self.assertEqual(stats['pools'][0]['free_capacity_gb'],
|
||||
free_capacity_gb)
|
||||
|
||||
def test_create_host(self):
|
||||
# setup_mock_client drive with default configuration
|
||||
@ -3325,6 +3490,24 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
|
||||
mock_client.assert_has_calls(expected)
|
||||
self.assertEqual(model, expected_model)
|
||||
|
||||
@mock.patch.object(volume_types, 'get_volume_type')
|
||||
def test_get_volume_settings_default_pool(self, _mock_volume_types):
|
||||
_mock_volume_types.return_value = {
|
||||
'name': 'gold',
|
||||
'id': 'gold-id',
|
||||
'extra_specs': {}}
|
||||
self.setup_driver()
|
||||
volume = {'host': 'test-host@3pariscsi#pool_foo',
|
||||
'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'}
|
||||
model = self.driver.common.get_volume_settings_from_type_id('gold-id',
|
||||
volume)
|
||||
self.assertEqual(model['cpg'], 'pool_foo')
|
||||
|
||||
def test_get_model_update(self):
|
||||
self.setup_driver()
|
||||
model_update = self.driver.common._get_model_update('xxx@yyy#zzz',
|
||||
'CPG')
|
||||
self.assertEqual(model_update, {'host': 'xxx@yyy#CPG'})
|
||||
|
||||
VLUNS5_RET = ({'members':
|
||||
[{'portPos': {'node': 0, 'slot': 8, 'cardPort': 2},
|
||||
|
@ -59,6 +59,7 @@ from cinder.openstack.common import log as logging
|
||||
from cinder.openstack.common import loopingcall
|
||||
from cinder.openstack.common import units
|
||||
from cinder.volume import qos_specs
|
||||
from cinder.volume import utils as volume_utils
|
||||
from cinder.volume import volume_types
|
||||
|
||||
import taskflow.engines
|
||||
@ -81,13 +82,13 @@ hp3par_opts = [
|
||||
default='',
|
||||
help="3PAR Super user password",
|
||||
secret=True),
|
||||
cfg.StrOpt('hp3par_cpg',
|
||||
default="OpenStack",
|
||||
help="The CPG to use for volume creation"),
|
||||
cfg.ListOpt('hp3par_cpg',
|
||||
default=["OpenStack"],
|
||||
help="List of the CPG(s) to use for volume creation"),
|
||||
cfg.StrOpt('hp3par_cpg_snap',
|
||||
default="",
|
||||
help="The CPG to use for Snapshots for volumes. "
|
||||
"If empty hp3par_cpg will be used"),
|
||||
"If empty the userCPG will be used."),
|
||||
cfg.StrOpt('hp3par_snapshot_retention',
|
||||
default="",
|
||||
help="The time in hours to retain a snapshot. "
|
||||
@ -151,10 +152,11 @@ class HP3PARCommon(object):
|
||||
2.0.21 - Remove bogus invalid snapCPG=None exception
|
||||
2.0.22 - HP 3PAR drivers should not claim to have 'infinite' space
|
||||
2.0.23 - Increase the hostname size from 23 to 31 Bug #1371242
|
||||
2.0.24 - Add pools (hp3par_cpg now accepts a list of CPGs)
|
||||
|
||||
"""
|
||||
|
||||
VERSION = "2.0.23"
|
||||
VERSION = "2.0.24"
|
||||
|
||||
stats = {}
|
||||
|
||||
@ -270,8 +272,10 @@ class HP3PARCommon(object):
|
||||
self.client_login()
|
||||
|
||||
try:
|
||||
# make sure the default CPG exists
|
||||
self.validate_cpg(self.config.hp3par_cpg)
|
||||
cpg_names = self.config.hp3par_cpg
|
||||
for cpg_name in cpg_names:
|
||||
self.validate_cpg(cpg_name)
|
||||
|
||||
finally:
|
||||
self.client_logout()
|
||||
|
||||
@ -365,12 +369,15 @@ class HP3PARCommon(object):
|
||||
LOG.info(_("Virtual volume '%(ref)s' renamed to '%(new)s'.") %
|
||||
{'ref': existing_ref['source-name'], 'new': new_vol_name})
|
||||
|
||||
retyped = False
|
||||
model_update = None
|
||||
if volume_type:
|
||||
LOG.info(_("Virtual volume %(disp)s '%(new)s' is being retyped.") %
|
||||
{'disp': display_name, 'new': new_vol_name})
|
||||
|
||||
try:
|
||||
self._retype_from_no_type(volume, volume_type)
|
||||
retyped, model_update = self._retype_from_no_type(volume,
|
||||
volume_type)
|
||||
LOG.info(_("Virtual volume %(disp)s successfully retyped to "
|
||||
"%(new_type)s.") %
|
||||
{'disp': display_name,
|
||||
@ -386,11 +393,16 @@ class HP3PARCommon(object):
|
||||
{'newName': existing_ref['source-name'],
|
||||
'comment': old_comment_str})
|
||||
|
||||
updates = {'display_name': display_name}
|
||||
if retyped and model_update:
|
||||
updates.update(model_update)
|
||||
|
||||
LOG.info(_("Virtual volume %(disp)s '%(new)s' is now being managed.") %
|
||||
{'disp': display_name, 'new': new_vol_name})
|
||||
|
||||
# Return display name to update the name displayed in the GUI.
|
||||
return {'display_name': display_name}
|
||||
# Return display name to update the name displayed in the GUI and
|
||||
# any model updates from retype.
|
||||
return updates
|
||||
|
||||
def manage_existing_get_size(self, volume, existing_ref):
|
||||
"""Return size of volume to be managed by manage_existing.
|
||||
@ -439,30 +451,33 @@ class HP3PARCommon(object):
|
||||
|
||||
def _extend_volume(self, volume, volume_name, growth_size_mib,
|
||||
_convert_to_base=False):
|
||||
model_update = None
|
||||
try:
|
||||
if _convert_to_base:
|
||||
LOG.debug("Converting to base volume prior to growing.")
|
||||
self._convert_to_base_volume(volume)
|
||||
model_update = self._convert_to_base_volume(volume)
|
||||
self.client.growVolume(volume_name, growth_size_mib)
|
||||
except Exception as ex:
|
||||
with excutils.save_and_reraise_exception() as ex_ctxt:
|
||||
if (not _convert_to_base and
|
||||
isinstance(ex, hpexceptions.HTTPForbidden) and
|
||||
ex.get_code() == 150):
|
||||
# Error code 150 means 'invalid operation: Cannot grow
|
||||
# this type of volume'.
|
||||
# Suppress raising this exception because we can
|
||||
# resolve it by converting it into a base volume.
|
||||
# Afterwards, extending the volume should succeed, or
|
||||
# fail with a different exception/error code.
|
||||
ex_ctxt.reraise = False
|
||||
self._extend_volume(volume, volume_name,
|
||||
growth_size_mib,
|
||||
_convert_to_base=True)
|
||||
# Error code 150 means 'invalid operation: Cannot grow
|
||||
# this type of volume'.
|
||||
# Suppress raising this exception because we can
|
||||
# resolve it by converting it into a base volume.
|
||||
# Afterwards, extending the volume should succeed, or
|
||||
# fail with a different exception/error code.
|
||||
ex_ctxt.reraise = False
|
||||
model_update = self._extend_volume(
|
||||
volume, volume_name,
|
||||
growth_size_mib,
|
||||
_convert_to_base=True)
|
||||
else:
|
||||
LOG.error(_("Error extending volume: %(vol)s. "
|
||||
"Exception: %(ex)s") %
|
||||
{'vol': volume_name, 'ex': ex})
|
||||
return model_update
|
||||
|
||||
def _get_3par_vol_name(self, volume_id):
|
||||
"""Get converted 3PAR volume name.
|
||||
@ -616,40 +631,47 @@ class HP3PARCommon(object):
|
||||
|
||||
# storage_protocol and volume_backend_name are
|
||||
# set in the child classes
|
||||
stats = {'driver_version': '1.0',
|
||||
'free_capacity_gb': 'unknown',
|
||||
'reserved_percentage': 0,
|
||||
'storage_protocol': None,
|
||||
'total_capacity_gb': 'unknown',
|
||||
'QoS_support': True,
|
||||
'vendor_name': 'Hewlett-Packard',
|
||||
'volume_backend_name': None}
|
||||
|
||||
pools = []
|
||||
info = self.client.getStorageSystemInfo()
|
||||
try:
|
||||
cpg = self.client.getCPG(self.config.hp3par_cpg)
|
||||
if 'limitMiB' not in cpg['SDGrowth']:
|
||||
# System capacity is best we can do for now.
|
||||
total_capacity = info['totalCapacityMiB'] * const
|
||||
free_capacity = info['freeCapacityMiB'] * const
|
||||
else:
|
||||
total_capacity = int(cpg['SDGrowth']['limitMiB'] * const)
|
||||
free_capacity = int((cpg['SDGrowth']['limitMiB'] -
|
||||
cpg['UsrUsage']['usedMiB']) * const)
|
||||
for cpg_name in self.config.hp3par_cpg:
|
||||
try:
|
||||
cpg = self.client.getCPG(cpg_name)
|
||||
if 'limitMiB' not in cpg['SDGrowth']:
|
||||
# System capacity is best we can do for now.
|
||||
total_capacity = info['totalCapacityMiB'] * const
|
||||
free_capacity = info['freeCapacityMiB'] * const
|
||||
else:
|
||||
total_capacity = int(cpg['SDGrowth']['limitMiB'] * const)
|
||||
free_capacity = int((cpg['SDGrowth']['limitMiB'] -
|
||||
cpg['UsrUsage']['usedMiB']) * const)
|
||||
|
||||
stats['total_capacity_gb'] = total_capacity
|
||||
stats['free_capacity_gb'] = free_capacity
|
||||
except hpexceptions.HTTPNotFound:
|
||||
err = (_("CPG (%s) doesn't exist on array")
|
||||
% self.config.hp3par_cpg)
|
||||
LOG.error(err)
|
||||
raise exception.InvalidInput(reason=err)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
err = (_("CPG (%s) doesn't exist on array")
|
||||
% cpg_name)
|
||||
LOG.error(err)
|
||||
raise exception.InvalidInput(reason=err)
|
||||
|
||||
stats['location_info'] = ('HP3PARDriver:%(sys_id)s:%(dest_cpg)s' %
|
||||
{'sys_id': info['serialNumber'],
|
||||
'dest_cpg': self.config.safe_get(
|
||||
'hp3par_cpg')})
|
||||
self.stats = stats
|
||||
pool = {'pool_name': cpg_name,
|
||||
'total_capacity_gb': total_capacity,
|
||||
'free_capacity_gb': free_capacity,
|
||||
'QoS_support': True,
|
||||
'reserved_percentage': 0,
|
||||
'location_info': ('HP3PARDriver:%(sys_id)s:%(dest_cpg)s' %
|
||||
{'sys_id': info['serialNumber'],
|
||||
'dest_cpg': cpg_name})
|
||||
}
|
||||
pools.append(pool)
|
||||
|
||||
self.stats = {'driver_version': '1.0',
|
||||
'storage_protocol': None,
|
||||
'vendor_name': 'Hewlett-Packard',
|
||||
'volume_backend_name': None,
|
||||
# Use zero capacities here so we always use a pool.
|
||||
'total_capacity_gb': 0,
|
||||
'free_capacity_gb': 0,
|
||||
'reserved_percentage': 0,
|
||||
'pools': pools}
|
||||
|
||||
def _get_vlun(self, volume_name, hostname, lun_id=None):
|
||||
"""find a VLUN on a 3PAR host."""
|
||||
@ -921,11 +943,13 @@ class HP3PARCommon(object):
|
||||
qos = self._get_qos_by_volume_type(volume_type)
|
||||
return hp3par_keys, qos, volume_type, vvs_name
|
||||
|
||||
def get_volume_settings_from_type_id(self, type_id):
|
||||
def get_volume_settings_from_type_id(self, type_id, volume):
|
||||
"""Get 3PAR volume settings given a type_id.
|
||||
|
||||
Combines type info and config settings to return a dictionary
|
||||
describing the 3PAR volume settings. Does some validation (CPG).
|
||||
Uses volume['host'] to determine default cpg (when not specified in
|
||||
volume type specs).
|
||||
|
||||
:param type_id:
|
||||
:return: dict
|
||||
@ -933,9 +957,22 @@ class HP3PARCommon(object):
|
||||
|
||||
hp3par_keys, qos, volume_type, vvs_name = self.get_type_info(type_id)
|
||||
|
||||
cpg = self._get_key_value(hp3par_keys, 'cpg',
|
||||
self.config.hp3par_cpg)
|
||||
if cpg is not self.config.hp3par_cpg:
|
||||
# Default to 1st configured CPG unless we can extract pool from host.
|
||||
default_cpg = self.config.hp3par_cpg[0]
|
||||
try:
|
||||
pool = volume_utils.extract_host(volume['host'], 'pool')
|
||||
if pool:
|
||||
default_cpg = pool
|
||||
LOG.debug("Default CPG from volume['host'] is (%s)" %
|
||||
default_cpg)
|
||||
else:
|
||||
LOG.debug("Default CPG from volume['host'] not found")
|
||||
except Exception as ex:
|
||||
LOG.debug("Default CPG from volume['host'] not found due to (%s)" %
|
||||
ex)
|
||||
|
||||
cpg = self._get_key_value(hp3par_keys, 'cpg', default_cpg)
|
||||
if cpg not in self.config.hp3par_cpg:
|
||||
# The cpg was specified in a volume type extra spec so it
|
||||
# needs to be validated that it's in the correct domain.
|
||||
self.validate_cpg(cpg)
|
||||
@ -987,7 +1024,8 @@ class HP3PARCommon(object):
|
||||
|
||||
type_id = volume.get('volume_type_id', None)
|
||||
|
||||
volume_settings = self.get_volume_settings_from_type_id(type_id)
|
||||
volume_settings = self.get_volume_settings_from_type_id(type_id,
|
||||
volume)
|
||||
|
||||
# check for valid persona even if we don't use it until
|
||||
# attach time, this will give the end user notice that the
|
||||
@ -1060,6 +1098,8 @@ class HP3PARCommon(object):
|
||||
LOG.error(ex)
|
||||
raise exception.CinderException(ex)
|
||||
|
||||
return self._get_model_update(volume['host'], cpg)
|
||||
|
||||
def _copy_volume(self, src_name, dest_name, cpg, snap_cpg=None,
|
||||
tpvv=True):
|
||||
# Virtual volume sets are not supported with the -online option
|
||||
@ -1088,6 +1128,32 @@ class HP3PARCommon(object):
|
||||
return comment_dict[key]
|
||||
return None
|
||||
|
||||
def _get_model_update(self, volume_host, cpg):
|
||||
"""Get model_update dict to use when we select a pool.
|
||||
|
||||
The pools implementation uses a volume['host'] suffix of :poolname.
|
||||
When the volume comes in with this selected pool, we sometimes use
|
||||
a different pool (e.g. because the type says to use a different pool).
|
||||
So in the several places that we do this, we need to return a model
|
||||
update so that the volume will have the actual pool name in the host
|
||||
suffix after the operation.
|
||||
|
||||
Given a volume_host, which should (might) have the pool suffix, and
|
||||
given the CPG we actually chose to use, return a dict to use for a
|
||||
model update iff an update is needed.
|
||||
|
||||
:param volume_host: The volume's host string.
|
||||
:param cpg: The actual pool (cpg) used, for example from the type.
|
||||
:return: dict Model update if we need to update volume host, else None
|
||||
"""
|
||||
model_update = None
|
||||
host = volume_utils.extract_host(volume_host, 'backend')
|
||||
host_and_pool = volume_utils.append_host(host, cpg)
|
||||
if volume_host != host_and_pool:
|
||||
# Since we selected a pool based on type, update the model.
|
||||
model_update = {'host': host_and_pool}
|
||||
return model_update
|
||||
|
||||
def create_cloned_volume(self, volume, src_vref):
|
||||
try:
|
||||
orig_name = self._get_3par_vol_name(volume['source_volid'])
|
||||
@ -1097,10 +1163,13 @@ class HP3PARCommon(object):
|
||||
|
||||
# make the 3PAR copy the contents.
|
||||
# can't delete the original until the copy is done.
|
||||
self._copy_volume(orig_name, vol_name, cpg=type_info['cpg'],
|
||||
cpg = type_info['cpg']
|
||||
self._copy_volume(orig_name, vol_name, cpg=cpg,
|
||||
snap_cpg=type_info['snap_cpg'],
|
||||
tpvv=type_info['tpvv'])
|
||||
return None
|
||||
|
||||
return self._get_model_update(volume['host'], cpg)
|
||||
|
||||
except hpexceptions.HTTPForbidden:
|
||||
raise exception.NotAuthorized()
|
||||
except hpexceptions.HTTPNotFound:
|
||||
@ -1188,6 +1257,7 @@ class HP3PARCommon(object):
|
||||
(pprint.pformat(volume['display_name']),
|
||||
pprint.pformat(snapshot['display_name'])))
|
||||
|
||||
model_update = None
|
||||
if volume['size'] < snapshot['volume_size']:
|
||||
err = ("You cannot reduce size of the volume. It must "
|
||||
"be greater than or equal to the snapshot.")
|
||||
@ -1225,7 +1295,7 @@ class HP3PARCommon(object):
|
||||
try:
|
||||
LOG.debug('Converting to base volume type: %s.' %
|
||||
volume['id'])
|
||||
self._convert_to_base_volume(volume)
|
||||
model_update = self._convert_to_base_volume(volume)
|
||||
growth_size_mib = growth_size * units.Gi / units.Mi
|
||||
LOG.debug('Growing volume: %(id)s by %(size)s GiB.' %
|
||||
{'id': volume['id'], 'size': growth_size})
|
||||
@ -1238,11 +1308,11 @@ class HP3PARCommon(object):
|
||||
raise exception.CinderException(ex)
|
||||
|
||||
if qos or vvs_name is not None:
|
||||
cpg = self._get_key_value(hp3par_keys, 'cpg',
|
||||
self.config.hp3par_cpg)
|
||||
cpg_names = self._get_key_value(hp3par_keys, 'cpg',
|
||||
self.config.hp3par_cpg)
|
||||
try:
|
||||
self._add_volume_to_volume_set(volume, volume_name,
|
||||
cpg, vvs_name, qos)
|
||||
cpg_names[0], vvs_name, qos)
|
||||
except Exception as ex:
|
||||
# Delete the volume if unable to add it to the volume set
|
||||
self.client.deleteVolume(volume_name)
|
||||
@ -1257,6 +1327,7 @@ class HP3PARCommon(object):
|
||||
except Exception as ex:
|
||||
LOG.error(ex)
|
||||
raise exception.CinderException(ex)
|
||||
return model_update
|
||||
|
||||
def create_snapshot(self, snapshot):
|
||||
LOG.debug("Create Snapshot\n%s" % pprint.pformat(snapshot))
|
||||
@ -1489,6 +1560,8 @@ class HP3PARCommon(object):
|
||||
LOG.error(ex)
|
||||
raise exception.CinderException(ex)
|
||||
|
||||
return self._get_model_update(volume['host'], cpg)
|
||||
|
||||
def delete_snapshot(self, snapshot):
|
||||
LOG.debug("Delete Snapshot id %s %s" % (snapshot['id'],
|
||||
pprint.pformat(snapshot)))
|
||||
@ -1733,7 +1806,7 @@ class HP3PARCommon(object):
|
||||
new_type_name = new_type['name']
|
||||
new_type_id = new_type['id']
|
||||
new_volume_settings = self.get_volume_settings_from_type_id(
|
||||
new_type_id)
|
||||
new_type_id, volume)
|
||||
new_cpg = new_volume_settings['cpg']
|
||||
new_snap_cpg = new_volume_settings['snap_cpg']
|
||||
new_tpvv = new_volume_settings['tpvv']
|
||||
@ -1765,7 +1838,11 @@ class HP3PARCommon(object):
|
||||
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)
|
||||
return True
|
||||
|
||||
if host:
|
||||
return True, self._get_model_update(host['host'], new_cpg)
|
||||
else:
|
||||
return True, self._get_model_update(volume['host'], new_cpg)
|
||||
|
||||
def _retype_from_no_type(self, volume, new_type):
|
||||
"""Convert the volume to be of the new type. Starting from no type.
|
||||
@ -1777,7 +1854,8 @@ class HP3PARCommon(object):
|
||||
volume-type is not used here. This method uses None.
|
||||
:param new_type: A dictionary describing the volume type to convert to
|
||||
"""
|
||||
none_type_settings = self.get_volume_settings_from_type_id(None)
|
||||
none_type_settings = self.get_volume_settings_from_type_id(
|
||||
None, volume)
|
||||
return self._retype_from_old_to_new(volume, new_type,
|
||||
none_type_settings, None)
|
||||
|
||||
|
@ -34,6 +34,7 @@ try:
|
||||
except ImportError:
|
||||
hpexceptions = None
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder import utils
|
||||
@ -69,10 +70,11 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
2.0.7 - Only one FC port is used when a single FC path
|
||||
is present. bug #1360001
|
||||
2.0.8 - Fixing missing login/logout around attach/detach bug #1367429
|
||||
2.0.9 - Add support for pools with model update
|
||||
|
||||
"""
|
||||
|
||||
VERSION = "2.0.8"
|
||||
VERSION = "2.0.9"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HP3PARFCDriver, self).__init__(*args, **kwargs)
|
||||
@ -118,8 +120,7 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
def create_volume(self, volume):
|
||||
self.common.client_login()
|
||||
try:
|
||||
metadata = self.common.create_volume(volume)
|
||||
return {'metadata': metadata}
|
||||
return self.common.create_volume(volume)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
|
||||
@ -127,8 +128,7 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
def create_cloned_volume(self, volume, src_vref):
|
||||
self.common.client_login()
|
||||
try:
|
||||
new_vol = self.common.create_cloned_volume(volume, src_vref)
|
||||
return {'metadata': new_vol}
|
||||
return self.common.create_cloned_volume(volume, src_vref)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
|
||||
@ -148,9 +148,8 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
"""
|
||||
self.common.client_login()
|
||||
try:
|
||||
metadata = self.common.create_volume_from_snapshot(volume,
|
||||
snapshot)
|
||||
return {'metadata': metadata}
|
||||
return self.common.create_volume_from_snapshot(volume,
|
||||
snapshot)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
|
||||
@ -466,3 +465,14 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
return self.common.migrate_volume(volume, host)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
|
||||
def get_pool(self, volume):
|
||||
self.common.client_login()
|
||||
try:
|
||||
return self.common.get_cpg(volume)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
reason = (_("Volume %s doesn't exist on array.") % volume)
|
||||
LOG.error(reason)
|
||||
raise exception.InvalidVolume(reason)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
|
@ -74,10 +74,11 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
2.0.5 - Added CHAP support, requires 3.1.3 MU1 firmware
|
||||
and hp3parclient 3.1.0.
|
||||
2.0.6 - Fixing missing login/logout around attach/detach bug #1367429
|
||||
2.0.7 - Add support for pools with model update
|
||||
|
||||
"""
|
||||
|
||||
VERSION = "2.0.6"
|
||||
VERSION = "2.0.7"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HP3PARISCSIDriver, self).__init__(*args, **kwargs)
|
||||
@ -189,8 +190,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
def create_volume(self, volume):
|
||||
self.common.client_login()
|
||||
try:
|
||||
metadata = self.common.create_volume(volume)
|
||||
return {'metadata': metadata}
|
||||
return self.common.create_volume(volume)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
|
||||
@ -199,8 +199,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
"""Clone an existing volume."""
|
||||
self.common.client_login()
|
||||
try:
|
||||
new_vol = self.common.create_cloned_volume(volume, src_vref)
|
||||
return {'metadata': new_vol}
|
||||
return self.common.create_cloned_volume(volume, src_vref)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
|
||||
@ -220,9 +219,8 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
"""
|
||||
self.common.client_login()
|
||||
try:
|
||||
metadata = self.common.create_volume_from_snapshot(volume,
|
||||
snapshot)
|
||||
return {'metadata': metadata}
|
||||
return self.common.create_volume_from_snapshot(volume,
|
||||
snapshot)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
|
||||
@ -674,3 +672,14 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
return self.common.migrate_volume(volume, host)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
|
||||
def get_pool(self, volume):
|
||||
self.common.client_login()
|
||||
try:
|
||||
return self.common.get_cpg(volume)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
reason = (_("Volume %s doesn't exist on array.") % volume)
|
||||
LOG.error(reason)
|
||||
raise exception.InvalidVolume(reason)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
|
@ -1920,11 +1920,11 @@
|
||||
# 3PAR Super user password (string value)
|
||||
#hp3par_password=
|
||||
|
||||
# The CPG to use for volume creation (string value)
|
||||
# List of the CPG(s) to use for volume creation (list value)
|
||||
#hp3par_cpg=OpenStack
|
||||
|
||||
# The CPG to use for Snapshots for volumes. If empty
|
||||
# hp3par_cpg will be used (string value)
|
||||
# The CPG to use for Snapshots for volumes. If empty the
|
||||
# userCPG will be used. (string value)
|
||||
#hp3par_cpg_snap=
|
||||
|
||||
# The time in hours to retain a snapshot. You can't delete it
|
||||
|
Loading…
x
Reference in New Issue
Block a user