diff --git a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_mirror_fc.py b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_mirror_fc.py index b4e3e135d81..43df2e611d5 100644 --- a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_mirror_fc.py +++ b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_mirror_fc.py @@ -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) diff --git a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_fc.py b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_fc.py index 3293c0007c9..4c78d6918d1 100644 --- a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_fc.py +++ b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_fc.py @@ -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) diff --git a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_iscsi.py b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_iscsi.py index 698c6571552..9f573a76881 100644 --- a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_iscsi.py +++ b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hbsd_rest_iscsi.py @@ -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), diff --git a/cinder/tests/unit/volume/drivers/hpe/xp/test_hpe_xp_rest_fc.py b/cinder/tests/unit/volume/drivers/hpe/xp/test_hpe_xp_rest_fc.py index 4e2d4f83e9b..8091c0a20f8 100644 --- a/cinder/tests/unit/volume/drivers/hpe/xp/test_hpe_xp_rest_fc.py +++ b/cinder/tests/unit/volume/drivers/hpe/xp/test_hpe_xp_rest_fc.py @@ -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), diff --git a/cinder/tests/unit/volume/drivers/hpe/xp/test_hpe_xp_rest_iscsi.py b/cinder/tests/unit/volume/drivers/hpe/xp/test_hpe_xp_rest_iscsi.py index 7173edc42f2..513d0c153a2 100644 --- a/cinder/tests/unit/volume/drivers/hpe/xp/test_hpe_xp_rest_iscsi.py +++ b/cinder/tests/unit/volume/drivers/hpe/xp/test_hpe_xp_rest_iscsi.py @@ -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), diff --git a/cinder/tests/unit/volume/drivers/nec/v/test_internal_nec_rest_fc.py b/cinder/tests/unit/volume/drivers/nec/v/test_internal_nec_rest_fc.py index 0cc49dd31e1..38c195c4b98 100644 --- a/cinder/tests/unit/volume/drivers/nec/v/test_internal_nec_rest_fc.py +++ b/cinder/tests/unit/volume/drivers/nec/v/test_internal_nec_rest_fc.py @@ -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), diff --git a/cinder/tests/unit/volume/drivers/nec/v/test_internal_nec_rest_iscsi.py b/cinder/tests/unit/volume/drivers/nec/v/test_internal_nec_rest_iscsi.py index b1ade2db115..b36a977a286 100644 --- a/cinder/tests/unit/volume/drivers/nec/v/test_internal_nec_rest_iscsi.py +++ b/cinder/tests/unit/volume/drivers/nec/v/test_internal_nec_rest_iscsi.py @@ -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), diff --git a/cinder/tests/unit/volume/drivers/nec/v/test_nec_rest_fc.py b/cinder/tests/unit/volume/drivers/nec/v/test_nec_rest_fc.py index dadbd1f1d9e..2598638f422 100644 --- a/cinder/tests/unit/volume/drivers/nec/v/test_nec_rest_fc.py +++ b/cinder/tests/unit/volume/drivers/nec/v/test_nec_rest_fc.py @@ -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) diff --git a/cinder/tests/unit/volume/drivers/nec/v/test_nec_rest_iscsi.py b/cinder/tests/unit/volume/drivers/nec/v/test_nec_rest_iscsi.py index 2edb42a566d..b51225f3b3d 100644 --- a/cinder/tests/unit/volume/drivers/nec/v/test_nec_rest_iscsi.py +++ b/cinder/tests/unit/volume/drivers/nec/v/test_nec_rest_iscsi.py @@ -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) diff --git a/cinder/volume/drivers/hitachi/hbsd_common.py b/cinder/volume/drivers/hitachi/hbsd_common.py index 77ff8e32b58..1def1176934 100644 --- a/cinder/volume/drivers/hitachi/hbsd_common.py +++ b/cinder/volume/drivers/hitachi/hbsd_common.py @@ -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), } diff --git a/cinder/volume/drivers/hitachi/hbsd_fc.py b/cinder/volume/drivers/hitachi/hbsd_fc.py index 9afb63ed74c..4195306a2f1 100644 --- a/cinder/volume/drivers/hitachi/hbsd_fc.py +++ b/cinder/volume/drivers/hitachi/hbsd_fc.py @@ -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. """ diff --git a/cinder/volume/drivers/hitachi/hbsd_iscsi.py b/cinder/volume/drivers/hitachi/hbsd_iscsi.py index 8bcfe6b38cd..f5e7c38cb01 100644 --- a/cinder/volume/drivers/hitachi/hbsd_iscsi.py +++ b/cinder/volume/drivers/hitachi/hbsd_iscsi.py @@ -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. """ diff --git a/cinder/volume/drivers/hitachi/hbsd_replication.py b/cinder/volume/drivers/hitachi/hbsd_replication.py index 3cef57aefce..4615fe4c807 100644 --- a/cinder/volume/drivers/hitachi/hbsd_replication.py +++ b/cinder/volume/drivers/hitachi/hbsd_replication.py @@ -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: diff --git a/cinder/volume/drivers/hitachi/hbsd_rest.py b/cinder/volume/drivers/hitachi/hbsd_rest.py index 28cdbc4e2c5..eb1356a215e 100644 --- a/cinder/volume/drivers/hitachi/hbsd_rest.py +++ b/cinder/volume/drivers/hitachi/hbsd_rest.py @@ -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', {}) diff --git a/cinder/volume/drivers/hitachi/hbsd_rest_api.py b/cinder/volume/drivers/hitachi/hbsd_rest_api.py index 93583396d20..d47d779cfe8 100644 --- a/cinder/volume/drivers/hitachi/hbsd_rest_api.py +++ b/cinder/volume/drivers/hitachi/hbsd_rest_api.py @@ -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( diff --git a/cinder/volume/drivers/hitachi/hbsd_utils.py b/cinder/volume/drivers/hitachi/hbsd_utils.py index 7bd33bbf1f0..b16092d87e3 100644 --- a/cinder/volume/drivers/hitachi/hbsd_utils.py +++ b/cinder/volume/drivers/hitachi/hbsd_utils.py @@ -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' diff --git a/doc/source/configuration/block-storage/drivers/hitachi-vsp-driver.rst b/doc/source/configuration/block-storage/drivers/hitachi-vsp-driver.rst index 3c8825dca4a..d3c916c6174 100644 --- a/doc/source/configuration/block-storage/drivers/hitachi-vsp-driver.rst +++ b/doc/source/configuration/block-storage/drivers/hitachi-vsp-driver.rst @@ -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 [consumer=back-end] \ + = \ + [= ...] + +\ + b. If you use the openstack command: + +.. code-block:: console + + $ openstack volume qos create [--consumer back-end] \ + --property \ + = \ + [--property \ + = ...] \ + + +\ + Specify a name for ````. + + Specify ```` and + ```` 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 + +\ + b. If you use the openstack command: + +.. code-block:: console + + $ openstack volume qos associate \ + + +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 + +\ + b. If you use the openstack command: + +.. code-block:: console + + $ openstack volume create --size --type \ + + +**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 diff --git a/doc/source/reference/support-matrix.ini b/doc/source/reference/support-matrix.ini index 7649a8c9255..a7abe29d822 100644 --- a/doc/source/reference/support-matrix.ini +++ b/doc/source/reference/support-matrix.ini @@ -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 diff --git a/releasenotes/notes/hitachi-vsp-support-qos-667ca4f8ae8c2ba2.yaml b/releasenotes/notes/hitachi-vsp-support-qos-667ca4f8ae8c2ba2.yaml new file mode 100644 index 00000000000..501da5f7d86 --- /dev/null +++ b/releasenotes/notes/hitachi-vsp-support-qos-667ca4f8ae8c2ba2.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Hitachi driver: Added QoS support.