Merge "Hitachi: Support for QoS"

This commit is contained in:
Zuul 2024-09-13 20:01:54 +00:00 committed by Gerrit Code Review
commit 7affd8a8b4
19 changed files with 1292 additions and 228 deletions

@ -104,12 +104,12 @@ REMOTE_DEFAULT_CONNECTOR = {
CTXT = cinder_context.get_admin_context()
TEST_VOLUME = []
for i in range(7):
for i in range(8):
volume = {}
volume['id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
volume['name'] = 'test-volume{0:d}'.format(i)
volume['volume_type_id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
if i == 3:
if i == 3 or i == 7:
volume['provider_location'] = None
elif i == 4:
volume['provider_location'] = json.dumps(
@ -128,6 +128,8 @@ for i in range(7):
volume['size'] = 128
if i == 2 or i == 6:
volume['status'] = 'in-use'
elif i == 7:
volume['status'] = None
else:
volume['status'] = 'available'
volume = fake_volume.fake_volume_obj(CTXT, **volume)
@ -826,9 +828,12 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_create_volume(self, get_volume_type_extra_specs, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume(self, get_volume_type_qos_specs,
get_volume_type_extra_specs, request):
extra_specs = {"test1": "aaa"}
get_volume_type_extra_specs.return_value = extra_specs
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
self.driver.common.rep_primary._stats = {}
self.driver.common.rep_primary._stats['pools'] = [
@ -836,20 +841,20 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
self.driver.common.rep_secondary._stats = {}
self.driver.common.rep_secondary._stats['pools'] = [
{'location_info': {'pool_id': 40}}]
ret = self.driver.create_volume(fake_volume.fake_volume_obj(self.ctxt))
ret = self.driver.create_volume(TEST_VOLUME[7])
actual = {'provider_location': '1'}
self.assertEqual(actual, ret)
self.assertEqual(2, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_create_volume_replication(
self, get_volume_type_extra_specs, get_volume_type, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_replication(self, get_volume_type_qos_specs,
get_volume_type_extra_specs, request):
extra_specs = {"test1": "aaa",
"hbsd:topology": "active_active_mirror_volume"}
get_volume_type_extra_specs.return_value = extra_specs
get_volume_type.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
def _request_side_effect(
method, url, params, json, headers, auth, timeout, verify):
@ -885,6 +890,57 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
self.assertEqual(actual, ret)
self.assertEqual(14, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_replication_qos(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
input_qos_specs = {
'qos_specs': {
'consumer': 'back-end',
'specs': {'upperIops': '1000'}}}
get_volume_type_qos_specs.return_value = input_qos_specs
extra_specs = {"test1": "aaa",
"hbsd:topology": "active_active_mirror_volume"}
get_volume_type_extra_specs.return_value = extra_specs
def _request_side_effect(
method, url, params, json, headers, auth, timeout, verify):
if self.configuration.hitachi_storage_id in url:
if method in ('POST', 'PUT'):
return FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
elif method == 'GET':
if '/remote-mirror-copygroups' in url:
return FakeResponse(200, NOTFOUND_RESULT)
elif '/remote-mirror-copypairs/' in url:
return FakeResponse(
200, GET_REMOTE_MIRROR_COPYPAIR_RESULT)
else:
if method in ('POST', 'PUT'):
return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
elif method == 'GET':
if '/remote-mirror-copygroups' in url:
return FakeResponse(200, NOTFOUND_RESULT)
return FakeResponse(
500, ERROR_RESULT, headers={'Content-Type': 'json'})
request.side_effect = _request_side_effect
self.driver.common.rep_primary._stats = {}
self.driver.common.rep_primary._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
self.driver.common.rep_secondary._stats = {}
self.driver.common.rep_secondary._stats['pools'] = [
{'location_info': {'pool_id': 40}}]
ret = self.driver.create_volume(TEST_VOLUME[3])
actual = {
'provider_location': json.dumps(
{'pldev': 1, 'sldev': 2,
'remote-copy': hbsd_utils.MIRROR_ATTR})}
self.assertEqual(actual, ret)
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(16, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_delete_volume(self, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
@ -1028,11 +1084,14 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_snapshot(
self, volume_get, get_volume_type_extra_specs, request):
self, get_volume_type_qos_specs, volume_get,
get_volume_type_extra_specs, request):
extra_specs = {"test1": "aaa",
"hbsd:topology": "active_active_mirror_volume"}
get_volume_type_extra_specs.return_value = extra_specs
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1086,13 +1145,14 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
self.assertEqual(3, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_cloned_volume(
self, get_volume_type_extra_specs, get_volume_type, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
extra_specs = {"test1": "aaa"}
get_volume_type_extra_specs.return_value = extra_specs
get_volume_type.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1110,14 +1170,14 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
self.assertEqual(5, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_cloned_volume_replication(
self, get_volume_type_extra_specs, get_volume_type, request):
extra_specs = {"test1": "aaa",
"hbsd:topology": "active_active_mirror_volume"}
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
extra_specs = {"hbsd:topology": "active_active_mirror_volume"}
get_volume_type_extra_specs.return_value = extra_specs
get_volume_type.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.snapshot_count = 0
def _request_side_effect(
@ -1163,13 +1223,14 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
self.assertEqual(23, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_from_snapshot(
self, get_volume_type_extra_specs, get_volume_type, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
extra_specs = {"test1": "aaa"}
get_volume_type_extra_specs.return_value = extra_specs
get_volume_type.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1288,16 +1349,17 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
self.assertEqual(1, remove_fc_zone.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type')
def test_manage_existing(self, get_volume_type, request):
get_volume_type.return_value = {}
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing(self, get_volume_type_qos_specs, request):
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref)
actual = {'provider_location': '1'}
self.assertEqual(actual, ret)
self.assertEqual(2, request.call_count)
self.assertEqual(3, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@ -1483,13 +1545,14 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_volume(
self, get_volume_type_extra_specs, get_volume_type, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
extra_specs = {"test1": "aaa"}
get_volume_type_extra_specs.return_value = extra_specs
get_volume_type.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1513,13 +1576,14 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_Exception(
self, get_volume_type_extra_specs, get_volume_type, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
extra_specs = {"test1": "aaa"}
get_volume_type_extra_specs.return_value = extra_specs
get_volume_type.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1546,13 +1610,14 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
self.assertEqual(10, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_snapshot(
self, get_volume_type_extra_specs, get_volume_type, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
extra_specs = {"test1": "aaa"}
get_volume_type_extra_specs.return_value = extra_specs
get_volume_type.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1586,12 +1651,14 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_snapshot_non_cg(
self, is_group_a_cg_snapshot_type, volume_get,
get_volume_type_extra_specs, request):
self, get_volume_type_qos_specs, is_group_a_cg_snapshot_type,
volume_get, get_volume_type_extra_specs, request):
is_group_a_cg_snapshot_type.return_value = False
extra_specs = {"test1": "aaa"}
get_volume_type_extra_specs.return_value = extra_specs
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1641,14 +1708,15 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_rep_ldev_and_pair_deduplication_compression(
self, get_volume_type_extra_specs, get_volume_type, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
get_volume_type_extra_specs.return_value = {
'hbsd:topology': 'active_active_mirror_volume',
'hbsd:capacity_saving': 'deduplication_compression'}
get_volume_type.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.snapshot_count = 0
def _request_side_effect(
@ -1696,5 +1764,5 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
TEST_VOLUME[4],
TEST_VOLUME[5])
self.assertEqual(2, get_volume_type_extra_specs.call_count)
self.assertEqual(0, get_volume_type.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(14, request.call_count)

@ -94,18 +94,20 @@ DEFAULT_CONNECTOR_MULTI_WWN = {
CTXT = cinder_context.get_admin_context()
TEST_VOLUME = []
for i in range(4):
for i in range(5):
volume = {}
volume['id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
volume['name'] = 'test-volume{0:d}'.format(i)
volume['volume_type_id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
if i == 3:
if i == 3 or i == 4:
volume['provider_location'] = None
else:
volume['provider_location'] = '{0:d}'.format(i)
volume['size'] = 128
if i == 2:
volume['status'] = 'in-use'
elif i == 4:
volume['status'] = None
else:
volume['status'] = 'available'
volume = fake_volume.fake_volume_obj(CTXT, **volume)
@ -394,6 +396,16 @@ GET_LDEVS_RESULT = {
],
}
GET_LDEVS_RESULT_QOS = {
"data": [
{
"ldevId": 0,
"label": "15960cc738c94c5bb4f1365be5eeed44",
"qos": {"upperIops": 1000},
},
],
}
NOTFOUND_RESULT = {
"data": [],
}
@ -814,21 +826,29 @@ class HBSDRESTFCDriverTest(test.TestCase):
drv.common.client.keep_session_loop.stop()
@mock.patch.object(requests.Session, "request")
def test_create_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume(self, get_volume_type_qos_specs,
get_volume_type_extra_specs, request):
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_volume(fake_volume.fake_volume_obj(self.ctxt))
ret = self.driver.create_volume(TEST_VOLUME[4])
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_deduplication_compression(
self, get_volume_type_extra_specs, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
extra_specs = {'hbsd:capacity_saving': 'deduplication_compression'}
get_volume_type_extra_specs.return_value = extra_specs
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
@ -840,13 +860,19 @@ class HBSDRESTFCDriverTest(test.TestCase):
'compression_deduplication')
self.assertEqual('1', ret['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(2, request.call_count)
@reduce_retrying_time
@mock.patch.object(requests.Session, "request")
def test_create_volume_timeout(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_timeout(self, get_volume_type_qos_specs,
get_volume_type_extra_specs, request):
self.driver.common.conf.hitachi_rest_timeout = 0
self.driver.common.conf.hitachi_exec_retry_interval = 0
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.return_value = FakeResponse(
500, ERROR_RESULT,
headers={'Content-Type': 'json'})
@ -856,9 +882,46 @@ class HBSDRESTFCDriverTest(test.TestCase):
{'location_info': {'pool_id': 30}}]
self.assertRaises(exception.VolumeDriverException,
self.driver.create_volume,
fake_volume.fake_volume_obj(self.ctxt))
TEST_VOLUME[4])
self.assertGreater(request.call_count, 1)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_qos(self, get_volume_type_qos_specs,
get_volume_type_extra_specs, request):
specs = {}
get_volume_type_extra_specs.return_value = {}
input_qos_specs = {
'qos_specs': {
'consumer': 'back-end',
'specs': {'upperIops': '1000',
'upperTransferRate': '2000',
'lowerIops': '3000',
'lowerTransferRate': '4000',
'responsePriority': '3'}}}
get_volume_type_qos_specs.return_value = input_qos_specs
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_volume(TEST_VOLUME[0])
for i in range(1, 6):
args, kwargs = request.call_args_list[i]
body = kwargs['json']
for key, value in body['parameters'].items():
specs[key] = value
self.assertEqual(specs['upperIops'], 1000)
self.assertEqual(specs['upperTransferRate'], 2000)
self.assertEqual(specs['lowerIops'], 3000)
self.assertEqual(specs['lowerTransferRate'], 4000)
self.assertEqual(specs['responsePriority'], 3)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(7, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_delete_volume(self, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
@ -932,6 +995,7 @@ class HBSDRESTFCDriverTest(test.TestCase):
stats["pools"][0]['pool_name'])
self.assertEqual(self.configuration.reserved_percentage,
stats["pools"][0]['reserved_percentage'])
self.assertTrue(stats["pools"][0]['QoS_support'])
self.assertTrue(stats["pools"][0]['thin_provisioning_support'])
self.assertFalse(stats["pools"][0]['thick_provisioning_support'])
self.assertTrue(stats["pools"][0]['multiattach'])
@ -985,14 +1049,17 @@ class HBSDRESTFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_snapshot(
self, volume_get, get_volume_type_extra_specs, request):
self, get_volume_type_qos_specs, volume_get,
get_volume_type_extra_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -1004,8 +1071,10 @@ class HBSDRESTFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_snapshot_dedup_false(
self, volume_get, get_volume_type_extra_specs, request):
self, get_volume_type_qos_specs, volume_get,
get_volume_type_extra_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1013,6 +1082,7 @@ class HBSDRESTFCDriverTest(test.TestCase):
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {'hbsd:capacity_saving':
'disable'}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -1052,26 +1122,31 @@ class HBSDRESTFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_create_cloned_volume(
self, get_volume_type_extra_specs, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_cloned_volume(self, get_volume_type_qos_specs,
get_volume_type_extra_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
vol = self.driver.create_cloned_volume(TEST_VOLUME[0], TEST_VOLUME[1])
self.assertEqual('1', vol['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(5, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_from_snapshot(
self, get_volume_type_extra_specs, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1079,12 +1154,14 @@ class HBSDRESTFCDriverTest(test.TestCase):
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
self.driver.common._stats = {}
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
vol = self.driver.create_volume_from_snapshot(
TEST_VOLUME[0], TEST_SNAPSHOT[0])
self.assertEqual('1', vol['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(5, request.call_count)
@mock.patch.object(fczm_utils, "add_fc_zone")
@ -1256,23 +1333,49 @@ class HBSDRESTFCDriverTest(test.TestCase):
self.assertEqual(1, remove_fc_zone.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing(self, get_volume_type_qos_specs, request):
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(3, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing_qos(self, get_volume_type_qos_specs, request):
input_qos_specs = {
'qos_specs': {
'consumer': 'back-end',
'specs': {'upperIops': '1000'}}}
get_volume_type_qos_specs.return_value = input_qos_specs
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(4, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_name(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing_name(self, get_volume_type_qos_specs, request):
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEVS_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref_name)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(3, request.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(4, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_get_size(self, request):
@ -1353,10 +1456,13 @@ class HBSDRESTFCDriverTest(test.TestCase):
TEST_SNAPSHOT[0])
@mock.patch.object(requests.Session, "request")
def test_retype(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_retype(self, get_volume_type_qos_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
get_volume_type_qos_specs.return_value = {'qos_specs': None}
host = {
'capabilities': {
'location_info': {
@ -1364,8 +1470,10 @@ class HBSDRESTFCDriverTest(test.TestCase):
},
},
}
new_type = {'extra_specs': {
'hbsd:capacity_saving': 'deduplication_compression'}}
extra_specs = {'hbsd:capacity_saving': 'deduplication_compression'}
new_type = fake_volume.fake_volume_type_obj(
CTXT, id='00000000-0000-0000-0000-{0:012d}'.format(0),
extra_specs=extra_specs)
old_specs = {'hbsd:capacity_saving': 'disable'}
new_specs = {'hbsd:capacity_saving': 'deduplication_compression'}
old_type_ref = volume_types.create(self.ctxt, 'old', old_specs)
@ -1374,9 +1482,90 @@ class HBSDRESTFCDriverTest(test.TestCase):
new_type_ref['id'])[0]
ret = self.driver.retype(
self.ctxt, TEST_VOLUME[0], new_type, diff, host)
self.assertEqual(3, request.call_count)
self.assertEqual(4, request.call_count)
self.assertTrue(ret)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_retype_qos(self, get_volume_type_qos_specs, request):
input_qos_specs = {'qos_specs': {
'consumer': 'back-end',
'specs': {'upperIops': '2000'}}}
get_volume_type_qos_specs.return_value = input_qos_specs
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(200, GET_LDEVS_RESULT_QOS),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
host = {
'capabilities': {
'location_info': {
'pool_id': 30,
},
},
}
qos_spec_id = '00000000-0000-0000-0000-000000000001'
new_type = fake_volume.fake_volume_type_obj(
CTXT, id='00000000-0000-0000-0000-{0:012d}'.format(0),
qos_spec_id=qos_spec_id)
diff = {'qos_specs': {'upperIops': ('1000', '2000')}}
ret = self.driver.retype(
self.ctxt, TEST_VOLUME[0], new_type, diff, host)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(4, request.call_count)
self.assertTrue(ret)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_retype_migrate_qos(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
qos_spec_id = '00000000-0000-0000-0000-000000000001'
input_qos_specs = {'qos_specs': {
'consumer': 'back-end',
'id': qos_spec_id,
'specs': {'upperIops': '2000'}}}
get_volume_type_qos_specs.return_value = input_qos_specs
get_volume_type_extra_specs.return_value = {}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
host = {
'capabilities': {
'location_info': {
'storage_id': CONFIG_MAP['serial'],
'pool_id': 30,
},
},
}
extra_specs = {'hbsd:target_ports': 'CL1-A'}
new_type = fake_volume.fake_volume_type_obj(
CTXT, id='00000000-0000-0000-0000-{0:012d}'.format(0),
extra_specs=extra_specs, qos_specs_id=qos_spec_id)
diff = {'extra_specs': {'hbsd:target_ports': 'CL1-A'},
'qos_specs': {'upperIops': ('1000', '2000')},
'encryption': {}}
ret = self.driver.retype(
self.ctxt, TEST_VOLUME[0], new_type, diff, host)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(16, request.call_count)
actual = (True, {'provider_location': '1'})
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
def test_migrate_volume(self, request):
request.return_value = FakeResponse(200, GET_LDEV_RESULT)
@ -1395,9 +1584,11 @@ class HBSDRESTFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_migrate_volume_diff_pool(
self, get_volume_type_extra_specs, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_migrate_volume_diff_pool(self, get_volume_type_qos_specs,
get_volume_type_extra_specs, request):
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
@ -1423,6 +1614,7 @@ class HBSDRESTFCDriverTest(test.TestCase):
}
ret = self.driver.migrate_volume(self.ctxt, TEST_VOLUME[0], host)
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(15, request.call_count)
actual = (True, {'provider_location': '1'})
self.assertTupleEqual(actual, ret)
@ -1468,9 +1660,12 @@ class HBSDRESTFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_volume(
self, get_volume_type_extra_specs, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1484,6 +1679,7 @@ class HBSDRESTFCDriverTest(test.TestCase):
source_group=TEST_GROUP[0], source_vols=[TEST_VOLUME[0]]
)
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(5, request.call_count)
actual = (
None, [{'id': TEST_VOLUME[1]['id'], 'provider_location': '1'}])
@ -1491,9 +1687,12 @@ class HBSDRESTFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_snapshot(
self, get_volume_type_extra_specs, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1507,12 +1706,16 @@ class HBSDRESTFCDriverTest(test.TestCase):
group_snapshot=TEST_GROUP_SNAP[0], snapshots=[TEST_SNAPSHOT[0]]
)
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(5, request.call_count)
actual = (
None, [{'id': TEST_VOLUME[0]['id'], 'provider_location': '1'}])
self.assertTupleEqual(actual, ret)
def test_create_group_from_src_volume_error(self):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_volume_error(
self, get_volume_type_qos_specs):
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.assertRaises(
exception.VolumeDriverException, self.driver.create_group_from_src,
self.ctxt, TEST_GROUP[1], [TEST_VOLUME[1]],
@ -1539,11 +1742,13 @@ class HBSDRESTFCDriverTest(test.TestCase):
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_snapshot_non_cg(
self, is_group_a_cg_snapshot_type, get_volume_type_extra_specs,
volume_get, request):
self, get_volume_type_qos_specs, is_group_a_cg_snapshot_type,
get_volume_type_extra_specs, volume_get, request):
is_group_a_cg_snapshot_type.return_value = False
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1569,11 +1774,13 @@ class HBSDRESTFCDriverTest(test.TestCase):
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_snapshot_cg(
self, is_group_a_cg_snapshot_type, get_volume_type_extra_specs,
volume_get, request):
self, get_volume_type_qos_specs, is_group_a_cg_snapshot_type,
get_volume_type_extra_specs, volume_get, request):
is_group_a_cg_snapshot_type.return_value = True
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1587,6 +1794,7 @@ class HBSDRESTFCDriverTest(test.TestCase):
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
)
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(6, request.call_count)
actual = (
None,
@ -1641,12 +1849,16 @@ class HBSDRESTFCDriverTest(test.TestCase):
self.assertEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
def test_is_modifiable_dr_value_new_dr_mode_disabled(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_is_modifiable_dr_value_new_dr_mode_disabled(
self, get_volume_type_qos_specs, request):
request.side_effect = [
FakeResponse(200, GET_LDEV_RESULT_PAIR_STATUS_TEST),
FakeResponse(200, GET_LDEV_RESULT_PAIR_STATUS_TEST),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT),
]
get_volume_type_qos_specs.return_value = {'qos_specs': None}
host = {
'capabilities': {
'location_info': {
@ -1654,7 +1866,10 @@ class HBSDRESTFCDriverTest(test.TestCase):
},
},
}
new_type = {'extra_specs': {'hbsd:capacity_saving': 'disable'}}
extra_specs = {'hbsd:capacity_saving': 'disable'}
new_type = fake_volume.fake_volume_type_obj(
CTXT, id='00000000-0000-0000-0000-{0:012d}'.format(0),
extra_specs=extra_specs)
old_specs = {'hbsd:capacity_saving': 'deduplication_compression'}
new_specs = {'hbsd:capacity_saving': 'disable'}
old_type_ref = volume_types.create(self.ctxt, 'old', old_specs)
@ -1663,5 +1878,5 @@ class HBSDRESTFCDriverTest(test.TestCase):
new_type_ref['id'])[0]
ret = self.driver.retype(
self.ctxt, TEST_VOLUME[0], new_type, diff, host)
self.assertEqual(3, request.call_count)
self.assertEqual(4, request.call_count)
self.assertTrue(ret)

@ -69,18 +69,20 @@ DEFAULT_CONNECTOR = {
CTXT = cinder_context.get_admin_context()
TEST_VOLUME = []
for i in range(4):
for i in range(5):
volume = {}
volume['id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
volume['name'] = 'test-volume{0:d}'.format(i)
volume['volume_type_id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
if i == 3:
if i == 3 or i == 4:
volume['provider_location'] = None
else:
volume['provider_location'] = '{0:d}'.format(i)
volume['size'] = 128
if i == 2:
volume['status'] = 'in-use'
elif i == 4:
volume['status'] = None
else:
volume['status'] = 'available'
volume = fake_volume.fake_volume_obj(CTXT, **volume)
@ -94,19 +96,23 @@ def _volume_get(context, volume_id):
TEST_SNAPSHOT = []
snapshot = {}
snapshot['id'] = '10000000-0000-0000-0000-{0:012d}'.format(0)
snapshot['name'] = 'TEST_SNAPSHOT{0:d}'.format(0)
snapshot['provider_location'] = '{0:d}'.format(1)
snapshot['status'] = 'available'
snapshot['volume_id'] = '00000000-0000-0000-0000-{0:012d}'.format(0)
snapshot['volume'] = _volume_get(None, snapshot['volume_id'])
snapshot['volume_name'] = 'test-volume{0:d}'.format(0)
snapshot['volume_size'] = 128
snapshot = obj_snap.Snapshot._from_db_object(
CTXT, obj_snap.Snapshot(),
fake_snapshot.fake_db_snapshot(**snapshot))
TEST_SNAPSHOT.append(snapshot)
for i in range(2):
snapshot = {}
snapshot['id'] = '10000000-0000-0000-0000-{0:012d}'.format(i)
snapshot['name'] = 'TEST_SNAPSHOT{0:d}'.format(i)
snapshot['provider_location'] = '{0:d}'.format(i + 1)
snapshot['status'] = 'available'
snapshot['volume_id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
snapshot['volume'] = _volume_get(None, snapshot['volume_id'])
snapshot['volume_name'] = 'test-volume{0:d}'.format(i)
snapshot['volume_size'] = 128
if i == 1:
snapshot['volume_type_id'] =\
'00000000-0000-0000-0000-{0:012d}'.format(i)
snapshot = obj_snap.Snapshot._from_db_object(
CTXT, obj_snap.Snapshot(),
fake_snapshot.fake_db_snapshot(**snapshot))
TEST_SNAPSHOT.append(snapshot)
TEST_GROUP = []
for i in range(2):
@ -614,12 +620,17 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
self.assertEqual(1, get_goodness_function.call_count)
@mock.patch.object(requests.Session, "request")
def test_create_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume(self, get_volume_type_qos_specs,
get_volume_type_extra_specs, request):
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_volume(fake_volume.fake_volume_obj(self.ctxt))
ret = self.driver.create_volume(TEST_VOLUME[4])
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
@ -635,9 +646,12 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_snapshot(
self, volume_get, get_volume_type_extra_specs, request):
self, get_volume_type_qos_specs, volume_get,
get_volume_type_extra_specs, request):
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -649,8 +663,37 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
self.assertEqual('1', ret['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(5, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_snapshot_qos(
self, get_volume_type_qos_specs, volume_get,
get_volume_type_extra_specs, request):
input_qos_specs = {
'qos_specs': {
'consumer': 'back-end',
'specs': {'upperIops': '1000'}}}
get_volume_type_qos_specs.return_value = input_qos_specs
get_volume_type_extra_specs.return_value = {}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_snapshot(TEST_SNAPSHOT[1])
self.assertEqual('1', ret['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(6, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_delete_snapshot(self, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_SNAP),
@ -668,32 +711,66 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_create_cloned_volume(
self, get_volume_type_extra_specs, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_cloned_volume(self, get_volume_type_qos_specs,
get_volume_type_extra_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
vol = self.driver.create_cloned_volume(TEST_VOLUME[0], TEST_VOLUME[1])
self.assertEqual('1', vol['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(5, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_from_snapshot(
self, get_volume_type_extra_specs, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
vol = self.driver.create_volume_from_snapshot(
TEST_VOLUME[0], TEST_SNAPSHOT[0])
self.assertEqual('1', vol['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(5, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_from_snapshot_qos(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
input_qos_specs = {
'qos_specs': {
'consumer': 'back-end',
'specs': {'upperIops': '1000'}}}
get_volume_type_qos_specs.return_value = input_qos_specs
get_volume_type_extra_specs.return_value = {}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -701,7 +778,8 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
TEST_VOLUME[0], TEST_SNAPSHOT[0])
self.assertEqual('1', vol['provider_location'])
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(5, request.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(6, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@ -823,23 +901,31 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
self.assertEqual(6, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing(self, get_volume_type_qos_specs, request):
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(3, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_name(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing_name(self, get_volume_type_qos_specs, request):
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEVS_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref_name)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(3, request.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(4, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_get_size(self, request):
@ -895,10 +981,12 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
TEST_SNAPSHOT[0])
@mock.patch.object(requests.Session, "request")
def test_retype(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_retype(self, get_volume_type_qos_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
host = {
'capabilities': {
'location_info': {
@ -906,8 +994,10 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
},
},
}
new_type = {'extra_specs': {
'hbsd:capacity_saving': 'deduplication_compression'}}
extra_specs = {'hbsd:capacity_saving': 'deduplication_compression'}
new_type = fake_volume.fake_volume_type_obj(
CTXT, id='00000000-0000-0000-0000-{0:012d}'.format(0),
extra_specs=extra_specs)
old_specs = {'hbsd:capacity_saving': 'disable'}
new_specs = {'hbsd:capacity_saving': 'deduplication_compression'}
old_type_ref = volume_types.create(self.ctxt, 'old', old_specs)
@ -916,7 +1006,7 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
new_type_ref['id'])[0]
ret = self.driver.retype(
self.ctxt, TEST_VOLUME[0], new_type, diff, host)
self.assertEqual(3, request.call_count)
self.assertEqual(4, request.call_count)
self.assertTrue(ret)
@mock.patch.object(requests.Session, "request")
@ -970,9 +1060,12 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_volume(
self, get_volume_type_extra_specs, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -986,6 +1079,7 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
source_group=TEST_GROUP[0], source_vols=[TEST_VOLUME[0]]
)
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(5, request.call_count)
actual = (
None, [{'id': TEST_VOLUME[1]['id'], 'provider_location': '1'}])
@ -993,9 +1087,12 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_snapshot(
self, get_volume_type_extra_specs, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1009,11 +1106,44 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
group_snapshot=TEST_GROUP_SNAP[0], snapshots=[TEST_SNAPSHOT[0]]
)
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(5, request.call_count)
actual = (
None, [{'id': TEST_VOLUME[0]['id'], 'provider_location': '1'}])
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_snapshot_qos(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
input_qos_specs = {
'qos_specs': {
'consumer': 'back-end',
'specs': {'upperIops': '1000'}}}
get_volume_type_qos_specs.return_value = input_qos_specs
get_volume_type_extra_specs.return_value = {}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_group_from_src(
self.ctxt, TEST_GROUP[0], [TEST_VOLUME[0]],
group_snapshot=TEST_GROUP_SNAP[0], snapshots=[TEST_SNAPSHOT[1]]
)
self.assertEqual(1, get_volume_type_extra_specs.call_count)
self.assertEqual(1, get_volume_type_qos_specs.call_count)
self.assertEqual(6, request.call_count)
actual = (
None, [{'id': TEST_VOLUME[0]['id'], 'provider_location': '1'}])
self.assertTupleEqual(actual, ret)
def test_create_group_from_src_volume_error(self):
self.assertRaises(
exception.VolumeDriverException, self.driver.create_group_from_src,
@ -1041,11 +1171,13 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_snapshot_non_cg(
self, is_group_a_cg_snapshot_type, get_volume_type_extra_specs,
volume_get, request):
self, get_volume_type_qos_specs, is_group_a_cg_snapshot_type,
get_volume_type_extra_specs, volume_get, request):
is_group_a_cg_snapshot_type.return_value = False
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1071,11 +1203,13 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_snapshot_cg(
self, is_group_a_cg_snapshot_type, get_volume_type_extra_specs,
volume_get, request):
self, get_volume_type_qos_specs, is_group_a_cg_snapshot_type,
get_volume_type_extra_specs, volume_get, request):
is_group_a_cg_snapshot_type.return_value = True
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),

@ -76,20 +76,24 @@ DEFAULT_CONNECTOR = {
CTXT = cinder_context.get_admin_context()
TEST_VOLUME = []
for i in range(4):
for i in range(5):
volume = {}
volume['id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
volume['name'] = 'test-volume{0:d}'.format(i)
if i == 3:
volume['volume_type_id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
if i == 3 or i == 4:
volume['provider_location'] = None
else:
volume['provider_location'] = '{0:d}'.format(i)
volume['size'] = 128
if i == 2:
volume['status'] = 'in-use'
elif i == 4:
volume['status'] = None
else:
volume['status'] = 'available'
volume = fake_volume.fake_volume_obj(CTXT, **volume)
volume.volume_type = fake_volume.fake_volume_type_obj(CTXT)
TEST_VOLUME.append(volume)
@ -633,18 +637,30 @@ class HPEXPRESTFCDriverTest(test.TestCase):
self.driver.common.client.keep_session_loop.wait()
@mock.patch.object(requests.Session, "request")
def test_create_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_volume(fake_volume.fake_volume_obj(self.ctxt))
ret = self.driver.create_volume(TEST_VOLUME[4])
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
@reduce_retrying_time
@mock.patch.object(requests.Session, "request")
def test_create_volume_timeout(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_timeout(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.return_value = FakeResponse(
500, ERROR_RESULT,
headers={'Content-Type': 'json'})
@ -653,7 +669,7 @@ class HPEXPRESTFCDriverTest(test.TestCase):
{'location_info': {'pool_id': 30}}]
self.assertRaises(exception.VolumeDriverException,
self.driver.create_volume,
fake_volume.fake_volume_obj(self.ctxt))
TEST_VOLUME[4])
self.assertGreater(request.call_count, 1)
@mock.patch.object(requests.Session, "request")
@ -716,12 +732,18 @@ class HPEXPRESTFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
def test_create_snapshot(self, volume_get, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_snapshot(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
volume_get, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -755,12 +777,18 @@ class HPEXPRESTFCDriverTest(test.TestCase):
self.assertEqual(4, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_create_cloned_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_cloned_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -769,12 +797,18 @@ class HPEXPRESTFCDriverTest(test.TestCase):
self.assertEqual(5, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_create_volume_from_snapshot(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_from_snapshot(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -785,7 +819,10 @@ class HPEXPRESTFCDriverTest(test.TestCase):
@mock.patch.object(fczm_utils, "add_fc_zone")
@mock.patch.object(requests.Session, "request")
def test_initialize_connection(self, request, add_fc_zone):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_initialize_connection(
self, get_volume_type_extra_specs, request, add_fc_zone):
get_volume_type_extra_specs.return_value = {}
self.driver.common.conf.hitachi_zoning_request = True
self.driver.common._lookup_service = FakeLookupService()
request.side_effect = [FakeResponse(200, GET_HOST_WWNS_RESULT),
@ -800,8 +837,11 @@ class HPEXPRESTFCDriverTest(test.TestCase):
@mock.patch.object(fczm_utils, "add_fc_zone")
@mock.patch.object(requests.Session, "request")
def test_initialize_connection_already_mapped(self, request, add_fc_zone):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_initialize_connection_already_mapped(
self, get_volume_type_extra_specs, request, add_fc_zone):
"""Normal case: ldev have already mapped."""
get_volume_type_extra_specs.return_value = {}
self.driver.common.conf.hitachi_zoning_request = True
self.driver.common._lookup_service = FakeLookupService()
request.side_effect = [
@ -819,9 +859,12 @@ class HPEXPRESTFCDriverTest(test.TestCase):
@mock.patch.object(fczm_utils, "add_fc_zone")
@mock.patch.object(requests.Session, "request")
def test_initialize_connection_shared_target(self, request, add_fc_zone):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_initialize_connection_shared_target(
self, get_volume_type_extra_specs, request, add_fc_zone):
"""Normal case: A target shared with other systems."""
self.driver.common.conf.hitachi_zoning_request = True
get_volume_type_extra_specs.return_value = {}
self.driver.common._lookup_service = FakeLookupService()
request.side_effect = [FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
@ -911,23 +954,29 @@ class HPEXPRESTFCDriverTest(test.TestCase):
self.assertEqual(1, remove_fc_zone.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing(self, get_volume_type_qos_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
get_volume_type_qos_specs.return_value = {'qos_specs': None}
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
self.assertEqual(3, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_name(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing_name(self, get_volume_type_qos_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEVS_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
get_volume_type_qos_specs.return_value = {'qos_specs': None}
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref_name)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(3, request.call_count)
self.assertEqual(4, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_get_size(self, request):
@ -1041,12 +1090,18 @@ class HPEXPRESTFCDriverTest(test.TestCase):
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
def test_create_group_from_src_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -1060,12 +1115,18 @@ class HPEXPRESTFCDriverTest(test.TestCase):
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
def test_create_group_from_src_snapshot(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_snapshot(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -1078,7 +1139,10 @@ class HPEXPRESTFCDriverTest(test.TestCase):
None, [{'id': TEST_VOLUME[0]['id'], 'provider_location': '1'}])
self.assertTupleEqual(actual, ret)
def test_create_group_from_src_volume_error(self):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_volume_error(
self, get_volume_type_qos_specs):
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.assertRaises(
exception.VolumeDriverException, self.driver.create_group_from_src,
self.ctxt, TEST_GROUP[1], [TEST_VOLUME[1]],
@ -1104,9 +1168,14 @@ class HPEXPRESTFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_snapshot_non_cg(
self, is_group_a_cg_snapshot_type, volume_get, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
is_group_a_cg_snapshot_type, volume_get, request):
is_group_a_cg_snapshot_type.return_value = False
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1130,9 +1199,14 @@ class HPEXPRESTFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_snapshot_cg(
self, is_group_a_cg_snapshot_type, volume_get, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
is_group_a_cg_snapshot_type, volume_get, request):
is_group_a_cg_snapshot_type.return_value = True
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),

@ -69,20 +69,24 @@ DEFAULT_CONNECTOR = {
CTXT = cinder_context.get_admin_context()
TEST_VOLUME = []
for i in range(4):
for i in range(5):
volume = {}
volume['id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
volume['name'] = 'test-volume{0:d}'.format(i)
if i == 3:
volume['volume_type_id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
if i == 3 or i == 4:
volume['provider_location'] = None
else:
volume['provider_location'] = '{0:d}'.format(i)
volume['size'] = 128
if i == 2:
volume['status'] = 'in-use'
elif i == 4:
volume['status'] = None
else:
volume['status'] = 'available'
volume = fake_volume.fake_volume_obj(CTXT, **volume)
volume.volume_type = fake_volume.fake_volume_type_obj(CTXT)
TEST_VOLUME.append(volume)
@ -535,12 +539,18 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
self.assertEqual(1, get_goodness_function.call_count)
@mock.patch.object(requests.Session, "request")
def test_create_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_volume(fake_volume.fake_volume_obj(self.ctxt))
ret = self.driver.create_volume(TEST_VOLUME[4])
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
@ -555,12 +565,18 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
def test_create_snapshot(self, volume_get, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_snapshot(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
volume_get, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -578,12 +594,18 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
self.assertEqual(4, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_create_cloned_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_cloned_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -592,12 +614,18 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
self.assertEqual(5, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_create_volume_from_snapshot(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_from_snapshot(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -607,7 +635,10 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
self.assertEqual(5, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_initialize_connection(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_initialize_connection(
self, get_volume_type_extra_specs, request):
get_volume_type_extra_specs.return_value = {}
request.side_effect = [FakeResponse(200, GET_HOST_ISCSIS_RESULT),
FakeResponse(200, GET_HOST_GROUP_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
@ -630,8 +661,11 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
self.assertEqual(3, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_initialize_connection_shared_target(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_initialize_connection_shared_target(
self, get_volume_type_extra_specs, request):
"""Normal case: A target shared with other systems."""
get_volume_type_extra_specs.return_value = {}
request.side_effect = [FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, GET_HOST_GROUPS_RESULT),
FakeResponse(200, GET_HOST_ISCSIS_RESULT),
@ -716,23 +750,29 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
self.assertEqual(6, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing(self, get_volume_type_qos_specs, request):
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
self.assertEqual(3, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_name(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing_name(self, get_volume_type_qos_specs, request):
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEVS_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref_name)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(3, request.call_count)
self.assertEqual(4, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_get_size(self, request):
@ -840,12 +880,18 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
def test_create_group_from_src_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -859,12 +905,18 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
def test_create_group_from_src_snapshot(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_snapshot(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -903,9 +955,14 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_snapshot_non_cg(
self, is_group_a_cg_snapshot_type, volume_get, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
is_group_a_cg_snapshot_type, volume_get, request):
is_group_a_cg_snapshot_type.return_value = False
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -929,9 +986,15 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_snapshot_cg(
self, is_group_a_cg_snapshot_type, volume_get, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
is_group_a_cg_snapshot_type,
volume_get, request):
is_group_a_cg_snapshot_type.return_value = True
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),

@ -76,20 +76,24 @@ DEFAULT_CONNECTOR = {
CTXT = cinder_context.get_admin_context()
TEST_VOLUME = []
for i in range(4):
for i in range(5):
volume = {}
volume['id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
volume['name'] = 'test-volume{0:d}'.format(i)
if i == 3:
volume['volume_type_id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
if i == 3 or i == 4:
volume['provider_location'] = None
else:
volume['provider_location'] = '{0:d}'.format(i)
volume['size'] = 128
if i == 2:
volume['status'] = 'in-use'
elif i == 4:
volume['status'] = None
else:
volume['status'] = 'available'
volume = fake_volume.fake_volume_obj(CTXT, **volume)
volume.volume_type = fake_volume.fake_volume_type_obj(CTXT)
TEST_VOLUME.append(volume)
@ -623,27 +627,39 @@ class VStorageRESTFCDriverTest(test.TestCase):
self.driver.common.client.keep_session_loop.wait()
@mock.patch.object(requests.Session, "request")
def test_create_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_volume(fake_volume.fake_volume_obj(self.ctxt))
ret = self.driver.create_volume(TEST_VOLUME[4])
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
@reduce_retrying_time
@mock.patch.object(requests.Session, "request")
def test_create_volume_timeout(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_timeout(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.return_value = FakeResponse(
500, ERROR_RESULT,
headers={'Content-Type': 'json'})
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
self.assertRaises(exception.VolumeDriverException,
self.driver.create_volume,
fake_volume.fake_volume_obj(self.ctxt))
TEST_VOLUME[4])
self.assertGreater(request.call_count, 1)
@mock.patch.object(requests.Session, "request")
@ -706,12 +722,18 @@ class VStorageRESTFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
def test_create_snapshot(self, volume_get, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_snapshot(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
volume_get, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -745,12 +767,18 @@ class VStorageRESTFCDriverTest(test.TestCase):
self.assertEqual(4, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_create_cloned_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_cloned_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -759,12 +787,18 @@ class VStorageRESTFCDriverTest(test.TestCase):
self.assertEqual(5, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_create_volume_from_snapshot(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_from_snapshot(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -775,9 +809,12 @@ class VStorageRESTFCDriverTest(test.TestCase):
@mock.patch.object(fczm_utils, "add_fc_zone")
@mock.patch.object(requests.Session, "request")
def test_initialize_connection(self, request, add_fc_zone):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_initialize_connection(
self, get_volume_type_extra_specs, request, add_fc_zone):
self.driver.common.conf.hitachi_zoning_request = True
self.driver.common._lookup_service = FakeLookupService()
get_volume_type_extra_specs.return_value = {}
request.side_effect = [FakeResponse(200, GET_HOST_WWNS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
ret = self.driver.initialize_connection(
@ -790,10 +827,13 @@ class VStorageRESTFCDriverTest(test.TestCase):
@mock.patch.object(fczm_utils, "add_fc_zone")
@mock.patch.object(requests.Session, "request")
def test_initialize_connection_already_mapped(self, request, add_fc_zone):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_initialize_connection_already_mapped(
self, get_volume_type_extra_specs, request, add_fc_zone):
"""Normal case: ldev have already mapped."""
self.driver.common.conf.hitachi_zoning_request = True
self.driver.common._lookup_service = FakeLookupService()
get_volume_type_extra_specs.return_value = {}
request.side_effect = [
FakeResponse(200, GET_HOST_WWNS_RESULT),
FakeResponse(202, COMPLETED_FAILED_RESULT_LU_DEFINED),
@ -809,10 +849,13 @@ class VStorageRESTFCDriverTest(test.TestCase):
@mock.patch.object(fczm_utils, "add_fc_zone")
@mock.patch.object(requests.Session, "request")
def test_initialize_connection_shared_target(self, request, add_fc_zone):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_initialize_connection_shared_target(
self, get_volume_type_extra_specs, request, add_fc_zone):
"""Normal case: A target shared with other systems."""
self.driver.common.conf.hitachi_zoning_request = True
self.driver.common._lookup_service = FakeLookupService()
get_volume_type_extra_specs.return_value = {}
request.side_effect = [FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, GET_HOST_GROUPS_RESULT),
@ -901,27 +944,36 @@ class VStorageRESTFCDriverTest(test.TestCase):
self.assertEqual(1, remove_fc_zone.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing(self, get_volume_type_qos_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
get_volume_type_qos_specs.return_value = {'qos_specs': None}
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_name(self, request):
request.side_effect = [FakeResponse(200, GET_LDEVS_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref_name)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(3, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_get_size(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing_name(self, get_volume_type_qos_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEVS_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
get_volume_type_qos_specs.return_value = {'qos_specs': None}
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref_name)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(4, request.call_count)
@mock.patch.object(requests.Session, "request")
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing_get_size(
self, get_volume_type_qos_specs, request):
request.return_value = FakeResponse(200, GET_LDEV_RESULT)
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.manage_existing_get_size(
TEST_VOLUME[0], self.test_existing_ref)
self.assertEqual(1, request.call_count)
@ -1031,7 +1083,13 @@ class VStorageRESTFCDriverTest(test.TestCase):
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
def test_create_group_from_src_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1050,12 +1108,18 @@ class VStorageRESTFCDriverTest(test.TestCase):
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
def test_create_group_from_src_snapshot(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_snapshot(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -1068,7 +1132,10 @@ class VStorageRESTFCDriverTest(test.TestCase):
None, [{'id': TEST_VOLUME[0]['id'], 'provider_location': '1'}])
self.assertTupleEqual(actual, ret)
def test_create_group_from_src_volume_error(self):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_volume_error(
self, get_volume_type_qos_specs):
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.assertRaises(
exception.VolumeDriverException, self.driver.create_group_from_src,
self.ctxt, TEST_GROUP[1], [TEST_VOLUME[1]],
@ -1094,9 +1161,14 @@ class VStorageRESTFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_snapshot_non_cg(
self, is_group_a_cg_snapshot_type, volume_get, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
is_group_a_cg_snapshot_type, volume_get, request):
is_group_a_cg_snapshot_type.return_value = False
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -1120,9 +1192,14 @@ class VStorageRESTFCDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_snapshot_cg(
self, is_group_a_cg_snapshot_type, volume_get, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
is_group_a_cg_snapshot_type, volume_get, request):
is_group_a_cg_snapshot_type.return_value = True
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),

@ -70,20 +70,24 @@ DEFAULT_CONNECTOR = {
CTXT = cinder_context.get_admin_context()
TEST_VOLUME = []
for i in range(4):
for i in range(5):
volume = {}
volume['id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
volume['name'] = 'test-volume{0:d}'.format(i)
if i == 3:
volume['volume_type_id'] = '00000000-0000-0000-0000-{0:012d}'.format(i)
if i == 3 or i == 4:
volume['provider_location'] = None
else:
volume['provider_location'] = '{0:d}'.format(i)
volume['size'] = 128
if i == 2:
volume['status'] = 'in-use'
elif i == 4:
volume['status'] = None
else:
volume['status'] = 'available'
volume = fake_volume.fake_volume_obj(CTXT, **volume)
volume.volume_type = fake_volume.fake_volume_type_obj(CTXT)
TEST_VOLUME.append(volume)
@ -579,12 +583,18 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
self.assertEqual(1, get_goodness_function.call_count)
@mock.patch.object(requests.Session, "request")
def test_create_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_volume(fake_volume.fake_volume_obj(self.ctxt))
ret = self.driver.create_volume(TEST_VOLUME[4])
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
@ -599,12 +609,18 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
def test_create_snapshot(self, volume_get, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_snapshot(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
volume_get, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -622,12 +638,18 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
self.assertEqual(4, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_create_cloned_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_cloned_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -636,12 +658,18 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
self.assertEqual(5, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_create_volume_from_snapshot(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume_from_snapshot(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -651,10 +679,13 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
self.assertEqual(5, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_initialize_connection(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_initialize_connection(
self, get_volume_type_extra_specs, request):
request.side_effect = [FakeResponse(200, GET_HOST_ISCSIS_RESULT),
FakeResponse(200, GET_HOST_GROUP_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
ret = self.driver.initialize_connection(
TEST_VOLUME[0], DEFAULT_CONNECTOR)
self.assertEqual('iscsi', ret['driver_volume_type'])
@ -674,12 +705,15 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
self.assertEqual(3, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_initialize_connection_shared_target(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
def test_initialize_connection_shared_target(
self, get_volume_type_extra_specs, request):
"""Normal case: A target shared with other systems."""
request.side_effect = [FakeResponse(200, NOTFOUND_RESULT),
FakeResponse(200, GET_HOST_GROUPS_RESULT),
FakeResponse(200, GET_HOST_ISCSIS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
ret = self.driver.initialize_connection(
TEST_VOLUME[0], DEFAULT_CONNECTOR)
self.assertEqual('iscsi', ret['driver_volume_type'])
@ -760,23 +794,29 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
self.assertEqual(6, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing(self, get_volume_type_qos_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
get_volume_type_qos_specs.return_value = {'qos_specs': None}
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)
self.assertEqual(3, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_name(self, request):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_manage_existing_name(self, get_volume_type_qos_specs, request):
request.side_effect = [FakeResponse(200, GET_LDEVS_RESULT),
FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_LDEVS_RESULT)]
get_volume_type_qos_specs.return_value = {'qos_specs': None}
ret = self.driver.manage_existing(
TEST_VOLUME[0], self.test_existing_ref_name)
self.assertEqual('1', ret['provider_location'])
self.assertEqual(3, request.call_count)
self.assertEqual(4, request.call_count)
@mock.patch.object(requests.Session, "request")
def test_manage_existing_get_size(self, request):
@ -884,12 +924,18 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
def test_create_group_from_src_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -903,12 +949,18 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
self.assertTupleEqual(actual, ret)
@mock.patch.object(requests.Session, "request")
def test_create_group_from_src_snapshot(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_snapshot(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(200, GET_SNAPSHOTS_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
@ -921,7 +973,10 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
None, [{'id': TEST_VOLUME[0]['id'], 'provider_location': '1'}])
self.assertTupleEqual(actual, ret)
def test_create_group_from_src_volume_error(self):
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_from_src_volume_error(
self, get_volume_type_qos_specs):
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.assertRaises(
exception.VolumeDriverException, self.driver.create_group_from_src,
self.ctxt, TEST_GROUP[1], [TEST_VOLUME[1]],
@ -947,9 +1002,14 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_snapshot_non_cg(
self, is_group_a_cg_snapshot_type, volume_get, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
is_group_a_cg_snapshot_type, volume_get, request):
is_group_a_cg_snapshot_type.return_value = False
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
@ -973,9 +1033,14 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
@mock.patch.object(requests.Session, "request")
@mock.patch.object(sqlalchemy_api, 'volume_get', side_effect=_volume_get)
@mock.patch.object(volume_utils, 'is_group_a_cg_snapshot_type')
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_group_snapshot_cg(
self, is_group_a_cg_snapshot_type, volume_get, request):
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
is_group_a_cg_snapshot_type, volume_get, request):
is_group_a_cg_snapshot_type.return_value = True
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),

@ -1,4 +1,4 @@
# Copyright (C) 2021, 2023, NEC corporation
# Copyright (C) 2021, 2024, NEC corporation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@ -28,6 +28,7 @@ from cinder.volume.drivers.hitachi import hbsd_rest
from cinder.volume.drivers.hitachi import hbsd_rest_api
from cinder.volume.drivers.nec.v import nec_v_fc
from cinder.volume.drivers.nec.v import nec_v_rest
from cinder.volume import volume_types
from cinder.volume import volume_utils
# Configuration parameter values
@ -56,6 +57,17 @@ DEFAULT_CONNECTOR = {
'multipath': False,
}
CTXT = cinder_context.get_admin_context()
TEST_VOLUME = []
volume = {}
volume['id'] = '00000000-0000-0000-0000-{0:012d}'.format(0)
volume['name'] = 'test-volume{0:d}'.format(0)
volume['volume_type_id'] = '00000000-0000-0000-0000-{0:012d}'.format(0)
volume = fake_volume.fake_volume_obj(CTXT, **volume)
volume.volume_type = fake_volume.fake_volume_type_obj(CTXT)
TEST_VOLUME.append(volume)
# Dummy response for REST API
POST_SESSIONS_RESULT = {
"token": "b74777a3-f9f0-4ea8-bd8f-09847fac48d3",
@ -376,11 +388,17 @@ class VStorageRESTFCDriverTest(test.TestCase):
"SS")
@mock.patch.object(requests.Session, "request")
def test_create_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_volume(fake_volume.fake_volume_obj(self.ctxt))
ret = self.driver.create_volume(TEST_VOLUME[0])
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)

@ -1,4 +1,4 @@
# Copyright (C) 2021, 2023, NEC corporation
# Copyright (C) 2021, 2024, NEC corporation
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -29,6 +29,7 @@ from cinder.volume.drivers.hitachi import hbsd_rest
from cinder.volume.drivers.hitachi import hbsd_rest_api
from cinder.volume.drivers.nec.v import nec_v_iscsi
from cinder.volume.drivers.nec.v import nec_v_rest
from cinder.volume import volume_types
from cinder.volume import volume_utils
# Configuration parameter values
@ -57,6 +58,17 @@ DEFAULT_CONNECTOR = {
'multipath': False,
}
CTXT = cinder_context.get_admin_context()
TEST_VOLUME = []
volume = {}
volume['id'] = '00000000-0000-0000-0000-{0:012d}'.format(0)
volume['name'] = 'test-volume{0:d}'.format(0)
volume['volume_type_id'] = '00000000-0000-0000-0000-{0:012d}'.format(0)
volume = fake_volume.fake_volume_obj(CTXT, **volume)
volume.volume_type = fake_volume.fake_volume_type_obj(CTXT)
TEST_VOLUME.append(volume)
# Dummy response for REST API
POST_SESSIONS_RESULT = {
"token": "b74777a3-f9f0-4ea8-bd8f-09847fac48d3",
@ -396,11 +408,17 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
"SS")
@mock.patch.object(requests.Session, "request")
def test_create_volume(self, request):
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
@mock.patch.object(volume_types, 'get_volume_type_qos_specs')
def test_create_volume(
self, get_volume_type_qos_specs, get_volume_type_extra_specs,
request):
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
get_volume_type_extra_specs.return_value = {}
get_volume_type_qos_specs.return_value = {'qos_specs': None}
self.driver.common._stats = {}
self.driver.common._stats['pools'] = [
{'location_info': {'pool_id': 30}}]
ret = self.driver.create_volume(fake_volume.fake_volume_obj(self.ctxt))
ret = self.driver.create_volume(TEST_VOLUME[0])
self.assertEqual('1', ret['provider_location'])
self.assertEqual(2, request.call_count)

@ -252,7 +252,8 @@ class HBSDCommon():
return pool['location_info']['pool_id']
return None
def create_ldev(self, size, extra_specs, pool_id, ldev_range):
def create_ldev(
self, size, extra_specs, pool_id, ldev_range, qos_specs=None):
"""Create an LDEV and return its LDEV number."""
raise NotImplementedError()
@ -265,9 +266,10 @@ class HBSDCommon():
extra_specs = self.get_volume_extra_specs(volume)
pool_id = self.get_pool_id_of_volume(volume)
ldev_range = self.storage_info['ldev_range']
qos_specs = utils.get_qos_specs_from_volume(volume)
try:
ldev = self.create_ldev(
volume['size'], extra_specs, pool_id, ldev_range)
ldev = self.create_ldev(volume['size'], extra_specs, pool_id,
ldev_range, qos_specs=qos_specs)
except Exception:
with excutils.save_and_reraise_exception():
self.output_log(MSG.CREATE_LDEV_FAILED)
@ -291,13 +293,14 @@ class HBSDCommon():
def copy_on_storage(
self, pvol, size, extra_specs, pool_id, snap_pool_id, ldev_range,
is_snapshot=False, sync=False, is_rep=False):
is_snapshot=False, sync=False, is_rep=False, qos_specs=None):
"""Create a copy of the specified LDEV on the storage."""
ldev_info = self.get_ldev_info(['status', 'attributes'], pvol)
if ldev_info['status'] != 'NML':
msg = self.output_log(MSG.INVALID_LDEV_STATUS_FOR_COPY, ldev=pvol)
self.raise_error(msg)
svol = self.create_ldev(size, extra_specs, pool_id, ldev_range)
svol = self.create_ldev(
size, extra_specs, pool_id, ldev_range, qos_specs=qos_specs)
try:
self.create_pair_on_storage(
pvol, svol, snap_pool_id, is_snapshot=is_snapshot)
@ -326,9 +329,10 @@ class HBSDCommon():
pool_id = self.get_pool_id_of_volume(volume)
snap_pool_id = self.storage_info['snap_pool_id']
ldev_range = self.storage_info['ldev_range']
qos_specs = utils.get_qos_specs_from_volume(volume)
new_ldev = self.copy_on_storage(ldev, size, extra_specs, pool_id,
snap_pool_id, ldev_range,
is_rep=is_rep)
is_rep=is_rep, qos_specs=qos_specs)
self.modify_ldev_name(new_ldev, volume['id'].replace("-", ""))
if is_rep:
self.delete_pair(new_ldev)
@ -483,9 +487,10 @@ class HBSDCommon():
pool_id = self.get_pool_id_of_volume(snapshot['volume'])
snap_pool_id = self.storage_info['snap_pool_id']
ldev_range = self.storage_info['ldev_range']
qos_specs = utils.get_qos_specs_from_volume(snapshot)
new_ldev = self.copy_on_storage(
ldev, size, extra_specs, pool_id, snap_pool_id, ldev_range,
is_snapshot=True)
is_snapshot=True, qos_specs=qos_specs)
self.modify_ldev_name(new_ldev, snapshot.id.replace("-", ""))
return {
'provider_location': str(new_ldev),
@ -535,7 +540,7 @@ class HBSDCommon():
single_pool.update(dict(
pool_name=pool_name,
reserved_percentage=self.conf.safe_get('reserved_percentage'),
QoS_support=False,
QoS_support=True,
thin_provisioning_support=True,
thick_provisioning_support=False,
multiattach=True,
@ -621,6 +626,12 @@ class HBSDCommon():
"""Check if the LDEV meets the criteria for being managed."""
raise NotImplementedError()
def get_qos_specs_from_ldev(self, ldev):
raise NotImplementedError()
def change_qos_specs(self, ldev, old_qos_specs, new_qos_specs):
raise NotImplementedError()
def manage_existing(self, volume, existing_ref):
"""Return volume properties which Cinder needs to manage the volume."""
if 'source-name' in existing_ref:
@ -630,6 +641,10 @@ class HBSDCommon():
ldev = str2int(existing_ref.get('source-id'))
self.check_ldev_manageability(ldev, existing_ref)
self.modify_ldev_name(ldev, volume['id'].replace("-", ""))
new_qos_specs = utils.get_qos_specs_from_volume(volume)
old_qos_specs = self.get_qos_specs_from_ldev(ldev)
if old_qos_specs != new_qos_specs:
self.change_qos_specs(ldev, old_qos_specs, new_qos_specs)
return {
'provider_location': str(ldev),
}

@ -80,6 +80,7 @@ class HBSDFCDriver(driver.FibreChannelDriver):
2.3.3 - Add GAD volume support.
2.3.4 - Support data deduplication and compression.
2.3.5 - Fix key error when backend is down.
2.4.0 - Add QoS support.
"""

@ -80,6 +80,7 @@ class HBSDISCSIDriver(driver.ISCSIDriver):
2.3.3 - Add GAD volume support.
2.3.4 - Support data deduplication and compression.
2.3.5 - Fix key error when backend is down.
2.4.0 - Add QoS support.
"""

@ -345,16 +345,18 @@ class HBSDREPLICATION(rest.HBSDREST):
"""Create a primary volume and a secondary volume."""
pool_id = self.rep_secondary.storage_info['pool_id'][0]
ldev_range = self.rep_secondary.storage_info['ldev_range']
qos_specs = utils.get_qos_specs_from_volume(volume)
thread = greenthread.spawn(
self.rep_secondary.create_ldev, volume.size, extra_specs,
pool_id, ldev_range)
pool_id, ldev_range, qos_specs=qos_specs)
if pvol is None:
try:
pool_id = self.rep_primary.get_pool_id_of_volume(volume)
ldev_range = self.rep_primary.storage_info['ldev_range']
pvol = self.rep_primary.create_ldev(volume.size,
extra_specs,
pool_id, ldev_range)
pool_id, ldev_range,
qos_specs=qos_specs)
except exception.VolumeDriverException:
self.rep_primary.output_log(MSG.CREATE_LDEV_FAILED)
try:

@ -360,11 +360,24 @@ class HBSDREST(common.HBSDCommon):
body['endLdevId'] = max_ldev
return self.client.add_ldev(body, no_log=True)
def create_ldev(self, size, extra_specs, pool_id, ldev_range):
def set_qos_specs(self, ldev, qos_specs):
self.client.set_qos_specs(ldev, qos_specs)
def create_ldev(self, size, extra_specs, pool_id, ldev_range,
qos_specs=None):
"""Create an LDEV of the specified size and the specified type."""
ldev = self._create_ldev_on_storage(
size, extra_specs, pool_id, ldev_range)
LOG.debug('Created logical device. (LDEV: %s)', ldev)
if qos_specs:
try:
self.set_qos_specs(ldev, qos_specs)
except Exception:
with excutils.save_and_reraise_exception():
try:
self.delete_ldev(ldev)
except exception.VolumeDriverException:
self.output_log(MSG.DELETE_LDEV_FAILED, ldev=ldev)
return ldev
def modify_ldev_name(self, ldev, name):
@ -1296,8 +1309,10 @@ class HBSDREST(common.HBSDCommon):
pool_id = self.get_pool_id_of_volume(snapshot.volume)
ldev_range = self.storage_info['ldev_range']
extra_specs = self.get_volume_extra_specs(snapshot.volume)
qos_specs = utils.get_qos_specs_from_volume(snapshot)
pair['svol'] = self.create_ldev(size, extra_specs,
pool_id, ldev_range)
pool_id, ldev_range,
qos_specs=qos_specs)
self.modify_ldev_name(pair['svol'],
snapshot.id.replace("-", ""))
except Exception as exc:
@ -1488,6 +1503,10 @@ class HBSDREST(common.HBSDCommon):
(ldev_range and
(pvol < ldev_range[0] or ldev_range[1] < pvol))):
extra_specs = self.get_volume_extra_specs(volume)
if new_type:
qos_specs = utils.get_qos_specs_from_volume_type(new_type)
else:
qos_specs = utils.get_qos_specs_from_volume(volume)
snap_pool_id = host['capabilities']['location_info'].get(
'snap_pool_id')
ldev_range = host['capabilities']['location_info'].get(
@ -1495,7 +1514,7 @@ class HBSDREST(common.HBSDCommon):
svol = self.copy_on_storage(
pvol, volume.size, extra_specs, new_pool_id,
snap_pool_id, ldev_range,
is_snapshot=False, sync=True)
is_snapshot=False, sync=True, qos_specs=qos_specs)
self.modify_ldev_name(svol, volume['id'].replace("-", ""))
try:
@ -1540,6 +1559,9 @@ class HBSDREST(common.HBSDCommon):
def _check_specs_diff(diff, allowed_extra_specs):
for specs_key, specs_val in diff.items():
if specs_key == 'qos_specs':
diff_items.append(specs_key)
continue
for diff_key, diff_val in specs_val.items():
if (specs_key == 'extra_specs' and
diff_key in allowed_extra_specs):
@ -1593,6 +1615,12 @@ class HBSDREST(common.HBSDCommon):
self._modify_capacity_saving(ldev, new_dr_mode)
if 'qos_specs' in diff_items:
old_qos_specs = self.get_qos_specs_from_ldev(ldev)
new_qos_specs = utils.get_qos_specs_from_volume_type(new_type)
if old_qos_specs != new_qos_specs:
self.change_qos_specs(ldev, old_qos_specs, new_qos_specs)
return True
def wait_copy_completion(self, pvol, svol):
@ -1623,3 +1651,18 @@ class HBSDREST(common.HBSDCommon):
'_', host[:max_host_len])
return self.format_info['group_name_format'].format(
host=host, wwn=wwn, ip=ip)
def change_qos_specs(self, ldev, old_qos_specs, new_qos_specs):
delete_specs = {key: 0 for key in old_qos_specs
if key in utils.QOS_KEYS}
if delete_specs:
self.client.set_qos_specs(ldev, delete_specs)
if new_qos_specs:
self.client.set_qos_specs(ldev, new_qos_specs)
def get_qos_specs_from_ldev(self, ldev):
params = {'detailInfoType': 'qos',
'headLdevId': ldev,
'count': 1}
ldev_info = self.client.get_ldevs(params=params)[0]
return ldev_info.get('qos', {})

@ -1,4 +1,4 @@
# Copyright (C) 2020, 2022, Hitachi, Ltd.
# Copyright (C) 2020, 2024, Hitachi, Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@ -1024,6 +1024,16 @@ class RestApiClient():
body = {"parameters": {"virtualLdevId": virtual_ldev_id}}
self._invoke(url, body=body)
def set_qos_specs(self, ldev_id, qos_specs):
url = '%(url)s/ldevs/%(id)s/actions/%(action)s/invoke' % {
'url': self.object_url,
'id': ldev_id,
'action': 'set-qos',
}
for (key, value) in qos_specs.items():
body = {'parameters': {key: value}}
self._invoke(url, body=body)
def output_log(self, msg_enum, **kwargs):
if self.is_rep:
return utils.output_log(

@ -24,8 +24,9 @@ from oslo_utils import units
from cinder import exception
from cinder import utils as cinder_utils
from cinder.volume import volume_types
VERSION = '2.3.5'
VERSION = '2.4.0'
CI_WIKI_NAME = 'Hitachi_VSP_CI'
PARAM_PREFIX = 'hitachi'
VENDOR_NAME = 'Hitachi'
@ -58,6 +59,9 @@ PORT_ID_LENGTH = 5
BUSY_MESSAGE = "Device or resource is busy."
QOS_KEYS = ['upperIops', 'upperTransferRate',
'lowerIops', 'lowerTransferRate', 'responsePriority']
@enum.unique
class HBSDMsg(enum.Enum):
@ -788,6 +792,57 @@ def synchronized_on_copy_group():
return wrap
def get_qos_specs_from_volume(target):
"""Return a dictionary of the QoS specs of the target.
:param target: Volume or Snapshot whose QoS specs are queried.
:type target: Volume or Snapshot
:return: QoS specs.
:rtype: dict
"""
# If the target is a Volume, volume_type is volume.volume_type.
# If the target is a Snapshot, volume_type is snapshot.volume.volume_type.
# We combine these into "getattr(target, 'volume', target).volume_type)".
return get_qos_specs_from_volume_type(
getattr(target, 'volume', target).volume_type)
def get_qos_specs_from_volume_type(volume_type):
"""Return a dictionary of the QoS specs of the volume_type.
:param volume_type: VolumeType whose QoS specs are queried. This must not
be None.
:type volume_type: VolumeType
:return: QoS specs.
:rtype: dict
The following is an example of the returned value:
{'lowerTransferRate': 7,
'responsePriority': 2,
'upperIops': 456}
"""
qos = {}
specs = volume_types.get_volume_type_qos_specs(volume_type.id)['qos_specs']
# The following is an example of the specs:
# {'consumer': 'back-end',
# 'created_at': datetime.datetime(2024, 9, 2, 3, 11, 1),
# 'id': '81058c04-06eb-49d7-9199-7016785bf386',
# 'name': 'qos1',
# 'specs': {'lowerTransferRate': '7',
# 'responsePriority': '2',
# 'upperIops': '456'}}
if specs is None:
return qos
if 'consumer' in specs and specs['consumer'] not in ('back-end', 'both'):
return qos
for key in specs['specs'].keys():
if key in QOS_KEYS:
if specs['specs'][key].isdigit():
qos[key] = int(specs['specs'][key])
else:
qos[key] = specs['specs'][key]
return qos
DICT = '_dict'
CONF = '_conf'

@ -101,6 +101,7 @@ Hitachi block storage driver also supports the following additional features:
* Data deduplication and compression
* Port scheduler
* Port assignment using extra spec
* Configuring Quality of Service (QoS) settings
.. note::
@ -376,6 +377,203 @@ If the number of pairs exceeds the maximum, copying cannot proceed normally.
For information about the maximum number of copy pairs and consistency groups
that can be created, see the `Hitachi Thin Image User Guide`_.
Configuring Quality of Service (QoS) settings
---------------------------------------------
By configuring Quality of Service (QoS) settings, you can restrict the
I/O processing of each volume, thereby maintaining the required performance
and quality levels.
In Hitachi block storage driver, you can configure the following settings for
each volume. However, you cannot configure these settings for journal volumes.
* Throughput (IOPS, amount of data transferred in MB/s)
You can set the upper and lower limits on throughput. If an upper
limit is exceeded, I/O is suppressed. If a lower limit is not met, I/O
is adjusted so that the lower limit is met.
* Priority level of the I/O processing
You can set priority levels for the I/O processing of multiple
volumes. I/O is adjusted for faster I/O response, starting with
high-priority volumes.
**System requirements for a QoS**
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
**Storage firmware versions**
+-----------------+------------------------+
| Storage model | Firmware version |
+=================+========================+
| VSP F350, | 88-06-01 or later |
| F370, | |
| F700, | |
| F900 | |
| | |
| VSP G350, | |
| G370, | |
| G700, | |
| G900 | |
+-----------------+------------------------+
| VSP 5100, | 90-04-01 or later |
| 5500, | |
| 5100H, | |
| 5500H | |
+-----------------+------------------------+
**Storage management software**
Configuration Manager REST API version 10.2.0-00 or later is required.
**Configuring QoS settings and creating volumes**
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Create QoS specs that define QoS settings, and then associate the QoS
specs with a volume type. You can configure QoS settings for a volume
by running the following functions with this volume type specified.
* Create Volume
* Create Snapshot
* Create Volume from Snapshot
* Create Volume from Volume (Clone)
* Consistency Group
* Generic volume group
The following example describes the procedure for configuring QoS settings
when creating a new volume using the Create Volume function.
Before you begin, Check the following information.
* QoS settings
- Upper or lower limit on throughput (IOPS, amount of data transferred
in MB/s)
- Priority level of I/O processing
* ID and name of the volume type
A volume type is needed in order to associate it with the QoS specs.
If no volume types exist, create one in advance.
**Procedure**
1. Create the QoS specs
a. If you use the cinder command:
.. code-block:: console
$ cinder qos-create <name-of-the-QoS-specs> [consumer=back-end] \
<name-of-a-QoS-specs-property>=<value-of-the-QoS-specs-property> \
[<name-of-a-QoS-specs-property>=<value-of-the-QoS-specs-property> ...]
\
b. If you use the openstack command:
.. code-block:: console
$ openstack volume qos create [--consumer back-end] \
--property \
<name-of-a-QoS-specs-property>=<value-of-the-QoS-specs-property> \
[--property \
<name-of-a-QoS-specs-property>=<value-of-the-QoS-specs-property> ...] \
<name-of-the-QoS-specs>
\
Specify a name for ``<name-of-the-QoS-specs>``.
Specify ``<name-of-a-QoS-specs-property>`` and
``<value-of-the-QoS-specs-property>`` as follows.
For details on the range of values you can specify, see the overview of
QoS operations in the `Performance Guide`_.
+--------------------+------------------------------------------+
| QoS specs property | Description |
+====================+==========================================+
| upperIops | The upper limit on IOPS. |
+--------------------+------------------------------------------+
| upperTransferRate | The upper limit on the amount of data |
| | transferred in MB/s. |
+--------------------+------------------------------------------+
| lowerIops | The lower limit on IOPS. |
+--------------------+------------------------------------------+
| lowerTransferRate | The lower limit on the amount of data |
| | transferred in MB/s. |
+--------------------+------------------------------------------+
| responsePriority | The priority level of the I/O processing.|
+--------------------+------------------------------------------+
The following is an example of running the command.
\
a. If you use the cinder command:
.. code-block:: console
$ cinder qos-create test_qos consumer=back-end upperIops=2000
\
b. If you use the openstack command:
.. code-block:: console
$ openstack volume qos create --consumer back-end \
--property upperIops=2000 test_qos
\
When you run this command, the ID of the created QoS specs is also output.
Record this ID, because you will need it in a later step.
\
2. Associate the QoS specs with a volume type.
a. If you use the cinder command:
.. code-block:: console
$ cinder qos-associate <ID-of-the-QoS-specs> <ID-of-the-volume-type>
\
b. If you use the openstack command:
.. code-block:: console
$ openstack volume qos associate <name-of-the-QoS-specs> \
<name-of-the-volume-type>
3. Specify the volume type that is associated with the QoS specs, and then
create a volume.
a. If you use the cinder command:
.. code-block:: console
$ cinder create --volume-type <name-of-the-volume-type> <size>
\
b. If you use the openstack command:
.. code-block:: console
$ openstack volume create --size <size> --type <name-of-the-volume-type> \
<name>
**Changing QoS settings**
To change the QoS settings, use the Retype function to change the volume type
to one that has different QoS specs.
You can also change a volume type for which no QoS specs are set to a volume
type for which QoS specs are set, and vice versa.
**Clearing QoS settings**
To clear the QoS settings, clear the association between the volume type and
QoS specs, and then delete the QoS specs.
Data deduplication and compression
----------------------------------
@ -516,3 +714,6 @@ attach operations for each volume type.
capacity-saving-function-data-deduplication-and-compression
.. _bug #2072317:
https://bugs.launchpad.net/cinder/+bug/2072317
.. _Performance Guide:
https://docs.hitachivantara.com/r/en-us/svos/9.6.0/mk-98rd9019/
hitachi-performance-monitor-operations

@ -425,7 +425,7 @@ driver.dell_emc_powerflex=complete
driver.dell_emc_xtremio=missing
driver.fujitsu_eternus=missing
driver.fungible=missing
driver.hitachi_vsp=missing
driver.hitachi_vsp=complete
driver.hpe_3par=complete
driver.hpe_msa=missing
driver.hpe_nimble=missing

@ -0,0 +1,4 @@
---
features:
- |
Hitachi driver: Added QoS support.