Merge "Hitachi: Prevent to delete a LDEV assigned to multi objects"
This commit is contained in:
commit
221a66940e
@ -271,6 +271,29 @@ GET_LDEV_RESULT = {
|
|||||||
"poolId": 30,
|
"poolId": 30,
|
||||||
"dataReductionStatus": "DISABLED",
|
"dataReductionStatus": "DISABLED",
|
||||||
"dataReductionMode": "disabled",
|
"dataReductionMode": "disabled",
|
||||||
|
"label": "00000000000000000000000000000000",
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_SPLIT = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "HDP"],
|
||||||
|
"status": "NML",
|
||||||
|
"poolId": 30,
|
||||||
|
"dataReductionStatus": "DISABLED",
|
||||||
|
"dataReductionMode": "disabled",
|
||||||
|
"label": "00000000000000000000000000000004",
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_LABEL = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "HDP"],
|
||||||
|
"status": "NML",
|
||||||
|
"poolId": 30,
|
||||||
|
"dataReductionStatus": "DISABLED",
|
||||||
|
"dataReductionMode": "disabled",
|
||||||
|
"label": "00000000000000000000000000000001",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_LDEV_RESULT_MAPPED = {
|
GET_LDEV_RESULT_MAPPED = {
|
||||||
@ -308,6 +331,7 @@ GET_LDEV_RESULT_PAIR = {
|
|||||||
"blockCapacity": 2097152,
|
"blockCapacity": 2097152,
|
||||||
"attributes": ["CVS", "HDP", "HTI"],
|
"attributes": ["CVS", "HDP", "HTI"],
|
||||||
"status": "NML",
|
"status": "NML",
|
||||||
|
"label": "10000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_LDEV_RESULT_REP = {
|
GET_LDEV_RESULT_REP = {
|
||||||
@ -316,6 +340,16 @@ GET_LDEV_RESULT_REP = {
|
|||||||
"attributes": ["CVS", "HDP", "GAD"],
|
"attributes": ["CVS", "HDP", "GAD"],
|
||||||
"status": "NML",
|
"status": "NML",
|
||||||
"numOfPorts": 1,
|
"numOfPorts": 1,
|
||||||
|
"label": "00000000000000000000000000000004",
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_REP_LABEL = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "HDP", "GAD"],
|
||||||
|
"status": "NML",
|
||||||
|
"numOfPorts": 1,
|
||||||
|
"label": "00000000000000000000000000000001",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_POOL_RESULT = {
|
GET_POOL_RESULT = {
|
||||||
@ -889,19 +923,42 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
|
|||||||
self.ldev_count = self.ldev_count + 1
|
self.ldev_count = self.ldev_count + 1
|
||||||
return FakeResponse(200, GET_LDEV_RESULT_REP)
|
return FakeResponse(200, GET_LDEV_RESULT_REP)
|
||||||
else:
|
else:
|
||||||
return FakeResponse(200, GET_LDEV_RESULT)
|
return FakeResponse(200, GET_LDEV_RESULT_SPLIT)
|
||||||
else:
|
else:
|
||||||
if method in ('POST', 'PUT', 'DELETE'):
|
if method in ('POST', 'PUT', 'DELETE'):
|
||||||
return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
|
return FakeResponse(202, REMOTE_COMPLETED_SUCCEEDED_RESULT)
|
||||||
elif method == 'GET':
|
elif method == 'GET':
|
||||||
if '/ldevs/' in url:
|
if '/ldevs/' in url:
|
||||||
return FakeResponse(200, GET_LDEV_RESULT)
|
return FakeResponse(200, GET_LDEV_RESULT_SPLIT)
|
||||||
return FakeResponse(
|
return FakeResponse(
|
||||||
500, ERROR_RESULT, headers={'Content-Type': 'json'})
|
500, ERROR_RESULT, headers={'Content-Type': 'json'})
|
||||||
request.side_effect = _request_side_effect
|
request.side_effect = _request_side_effect
|
||||||
self.driver.delete_volume(TEST_VOLUME[4])
|
self.driver.delete_volume(TEST_VOLUME[4])
|
||||||
self.assertEqual(17, request.call_count)
|
self.assertEqual(17, request.call_count)
|
||||||
|
|
||||||
|
@mock.patch.object(requests.Session, "request")
|
||||||
|
def test_delete_volume_primary_is_invalid_ldev(self, request):
|
||||||
|
request.return_value = FakeResponse(200, GET_LDEV_RESULT_LABEL)
|
||||||
|
self.driver.delete_volume(TEST_VOLUME[0])
|
||||||
|
self.assertEqual(1, request.call_count)
|
||||||
|
|
||||||
|
@mock.patch.object(requests.Session, "request")
|
||||||
|
def test_delete_volume_primary_secondary_is_invalid_ldev(self, request):
|
||||||
|
request.return_value = FakeResponse(200, GET_LDEV_RESULT_REP_LABEL)
|
||||||
|
self.driver.delete_volume(TEST_VOLUME[4])
|
||||||
|
self.assertEqual(2, request.call_count)
|
||||||
|
|
||||||
|
@mock.patch.object(requests.Session, "request")
|
||||||
|
def test_delete_volume_secondary_is_invalid_ldev(self, request):
|
||||||
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_REP_LABEL),
|
||||||
|
FakeResponse(200, GET_LDEV_RESULT_REP),
|
||||||
|
FakeResponse(200, GET_LDEV_RESULT_REP),
|
||||||
|
FakeResponse(200, GET_LDEV_RESULT_REP),
|
||||||
|
FakeResponse(200, GET_LDEV_RESULT_REP),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
|
self.driver.delete_volume(TEST_VOLUME[4])
|
||||||
|
self.assertEqual(6, request.call_count)
|
||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_extend_volume(self, request):
|
def test_extend_volume(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
@ -979,7 +1036,8 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.driver.common.rep_primary._stats = {}
|
self.driver.common.rep_primary._stats = {}
|
||||||
self.driver.common.rep_primary._stats['pools'] = [
|
self.driver.common.rep_primary._stats['pools'] = [
|
||||||
{'location_info': {'pool_id': 30}}]
|
{'location_info': {'pool_id': 30}}]
|
||||||
@ -989,7 +1047,7 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
|
|||||||
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
||||||
actual = {'provider_location': '1'}
|
actual = {'provider_location': '1'}
|
||||||
self.assertEqual(actual, ret)
|
self.assertEqual(actual, ret)
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_snapshot(self, request):
|
def test_delete_snapshot(self, request):
|
||||||
@ -1286,41 +1344,25 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
|
|||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_update_migrated_volume(self, request):
|
def test_update_migrated_volume(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
|
||||||
FakeResponse(200, GET_LDEV_RESULT),
|
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.assertRaises(
|
ret = self.driver.update_migrated_volume(
|
||||||
NotImplementedError,
|
self.ctxt, TEST_VOLUME[0], TEST_VOLUME[1], "available")
|
||||||
self.driver.update_migrated_volume,
|
self.assertEqual(2, request.call_count)
|
||||||
self.ctxt,
|
actual = ({'_name_id': TEST_VOLUME[1]['id'],
|
||||||
TEST_VOLUME[0],
|
'provider_location': TEST_VOLUME[1]['provider_location']})
|
||||||
TEST_VOLUME[1],
|
self.assertEqual(actual, ret)
|
||||||
"available")
|
|
||||||
self.assertEqual(4, request.call_count)
|
|
||||||
args, kwargs = request.call_args_list[3]
|
|
||||||
self.assertEqual(kwargs['json']['label'],
|
|
||||||
TEST_VOLUME[0]['id'].replace("-", ""))
|
|
||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_update_migrated_volume_replication(self, request):
|
def test_update_migrated_volume_replication(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_REP),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_REP),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
|
||||||
FakeResponse(200, GET_LDEV_RESULT_REP),
|
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.assertRaises(
|
ret = self.driver.update_migrated_volume(
|
||||||
NotImplementedError,
|
self.ctxt, TEST_VOLUME[0], TEST_VOLUME[4], "available")
|
||||||
self.driver.update_migrated_volume,
|
self.assertEqual(3, request.call_count)
|
||||||
self.ctxt,
|
actual = ({'_name_id': TEST_VOLUME[4]['id'],
|
||||||
TEST_VOLUME[0],
|
'provider_location': TEST_VOLUME[4]['provider_location']})
|
||||||
TEST_VOLUME[4],
|
self.assertEqual(actual, ret)
|
||||||
"available")
|
|
||||||
self.assertEqual(6, request.call_count)
|
|
||||||
original_volume_nickname = TEST_VOLUME[0]['id'].replace("-", "")
|
|
||||||
for i in (4, 5):
|
|
||||||
args, kwargs = request.call_args_list[i]
|
|
||||||
self.assertEqual(kwargs['json']['label'], original_volume_nickname)
|
|
||||||
|
|
||||||
def test_unmanage_snapshot(self):
|
def test_unmanage_snapshot(self):
|
||||||
"""The driver don't support unmange_snapshot."""
|
"""The driver don't support unmange_snapshot."""
|
||||||
@ -1470,6 +1512,39 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
|
|||||||
'provider_location': '1'}])
|
'provider_location': '1'}])
|
||||||
self.assertTupleEqual(actual, ret)
|
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')
|
||||||
|
def test_create_group_from_src_Exception(
|
||||||
|
self, get_volume_type_extra_specs, get_volume_type, request):
|
||||||
|
extra_specs = {"test1": "aaa"}
|
||||||
|
get_volume_type_extra_specs.return_value = extra_specs
|
||||||
|
get_volume_type.return_value = {}
|
||||||
|
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),
|
||||||
|
FakeResponse(200, GET_LDEV_RESULT),
|
||||||
|
FakeResponse(200, GET_LDEV_RESULT),
|
||||||
|
FakeResponse(200, GET_LDEV_RESULT),
|
||||||
|
FakeResponse(200, GET_LDEV_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
|
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}}]
|
||||||
|
self.assertRaises(exception.VolumeDriverException,
|
||||||
|
self.driver.create_group_from_src,
|
||||||
|
self.ctxt, TEST_GROUP[1],
|
||||||
|
[TEST_VOLUME[1], TEST_VOLUME[1]],
|
||||||
|
source_group=TEST_GROUP[0],
|
||||||
|
source_vols=[TEST_VOLUME[0], TEST_VOLUME[3]]
|
||||||
|
)
|
||||||
|
self.assertEqual(10, request.call_count)
|
||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
@mock.patch.object(volume_types, 'get_volume_type')
|
@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_extra_specs')
|
||||||
@ -1520,7 +1595,8 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.driver.common.rep_primary._stats = {}
|
self.driver.common.rep_primary._stats = {}
|
||||||
self.driver.common.rep_primary._stats['pools'] = [
|
self.driver.common.rep_primary._stats['pools'] = [
|
||||||
{'location_info': {'pool_id': 30}}]
|
{'location_info': {'pool_id': 30}}]
|
||||||
@ -1530,7 +1606,7 @@ class HBSDMIRRORFCDriverTest(test.TestCase):
|
|||||||
ret = self.driver.create_group_snapshot(
|
ret = self.driver.create_group_snapshot(
|
||||||
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
||||||
)
|
)
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
actual = (
|
actual = (
|
||||||
{'status': 'available'},
|
{'status': 'available'},
|
||||||
[{'id': TEST_SNAPSHOT[0]['id'],
|
[{'id': TEST_SNAPSHOT[0]['id'],
|
||||||
|
@ -219,6 +219,29 @@ GET_LDEV_RESULT = {
|
|||||||
"poolId": 30,
|
"poolId": 30,
|
||||||
"dataReductionStatus": "DISABLED",
|
"dataReductionStatus": "DISABLED",
|
||||||
"dataReductionMode": "disabled",
|
"dataReductionMode": "disabled",
|
||||||
|
"label": "00000000000000000000000000000000",
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_LABEL = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "HDP"],
|
||||||
|
"status": "NML",
|
||||||
|
"poolId": 30,
|
||||||
|
"dataReductionStatus": "DISABLED",
|
||||||
|
"dataReductionMode": "disabled",
|
||||||
|
"label": "00000000000000000000000000000001",
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_SNAP = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "HDP"],
|
||||||
|
"status": "NML",
|
||||||
|
"poolId": 30,
|
||||||
|
"dataReductionStatus": "DISABLED",
|
||||||
|
"dataReductionMode": "disabled",
|
||||||
|
"label": "10000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_LDEV_RESULT_MAPPED = {
|
GET_LDEV_RESULT_MAPPED = {
|
||||||
@ -241,6 +264,15 @@ GET_LDEV_RESULT_PAIR = {
|
|||||||
"blockCapacity": 2097152,
|
"blockCapacity": 2097152,
|
||||||
"attributes": ["CVS", "HDP", "HTI"],
|
"attributes": ["CVS", "HDP", "HTI"],
|
||||||
"status": "NML",
|
"status": "NML",
|
||||||
|
"label": "00000000000000000000000000000000",
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_PAIR_SNAP = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "HDP", "HTI"],
|
||||||
|
"status": "NML",
|
||||||
|
"label": "10000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_LDEV_RESULT_PAIR_TEST = {
|
GET_LDEV_RESULT_PAIR_TEST = {
|
||||||
@ -872,6 +904,12 @@ class HBSDRESTFCDriverTest(test.TestCase):
|
|||||||
TEST_VOLUME[0])
|
TEST_VOLUME[0])
|
||||||
self.assertEqual(2, request.call_count)
|
self.assertEqual(2, request.call_count)
|
||||||
|
|
||||||
|
@mock.patch.object(requests.Session, "request")
|
||||||
|
def test_delete_volume_is_invalid_ldev(self, request):
|
||||||
|
request.return_value = FakeResponse(200, GET_LDEV_RESULT_LABEL)
|
||||||
|
self.driver.delete_volume(TEST_VOLUME[0])
|
||||||
|
self.assertEqual(1, request.call_count)
|
||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_extend_volume(self, request):
|
def test_extend_volume(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
@ -952,7 +990,8 @@ class HBSDRESTFCDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
get_volume_type_extra_specs.return_value = {}
|
get_volume_type_extra_specs.return_value = {}
|
||||||
self.driver.common._stats = {}
|
self.driver.common._stats = {}
|
||||||
self.driver.common._stats['pools'] = [
|
self.driver.common._stats['pools'] = [
|
||||||
@ -960,7 +999,7 @@ class HBSDRESTFCDriverTest(test.TestCase):
|
|||||||
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
||||||
self.assertEqual('1', ret['provider_location'])
|
self.assertEqual('1', ret['provider_location'])
|
||||||
self.assertEqual(1, get_volume_type_extra_specs.call_count)
|
self.assertEqual(1, get_volume_type_extra_specs.call_count)
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
|
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
|
||||||
@ -970,7 +1009,8 @@ class HBSDRESTFCDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
get_volume_type_extra_specs.return_value = {'hbsd:capacity_saving':
|
get_volume_type_extra_specs.return_value = {'hbsd:capacity_saving':
|
||||||
'disable'}
|
'disable'}
|
||||||
self.driver.common._stats = {}
|
self.driver.common._stats = {}
|
||||||
@ -979,11 +1019,11 @@ class HBSDRESTFCDriverTest(test.TestCase):
|
|||||||
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
||||||
self.assertEqual('1', ret['provider_location'])
|
self.assertEqual('1', ret['provider_location'])
|
||||||
self.assertEqual(1, get_volume_type_extra_specs.call_count)
|
self.assertEqual(1, get_volume_type_extra_specs.call_count)
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_snapshot(self, request):
|
def test_delete_snapshot(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR_SNAP),
|
||||||
FakeResponse(200, NOTFOUND_RESULT),
|
FakeResponse(200, NOTFOUND_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
@ -1003,7 +1043,7 @@ class HBSDRESTFCDriverTest(test.TestCase):
|
|||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_snapshot_no_pair(self, request):
|
def test_delete_snapshot_no_pair(self, request):
|
||||||
"""Normal case: Delete a snapshot without pair."""
|
"""Normal case: Delete a snapshot without pair."""
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_SNAP),
|
||||||
FakeResponse(200, GET_LDEV_RESULT),
|
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)]
|
||||||
@ -1298,17 +1338,12 @@ class HBSDRESTFCDriverTest(test.TestCase):
|
|||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_update_migrated_volume(self, request):
|
def test_update_migrated_volume(self, request):
|
||||||
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
|
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
|
||||||
self.assertRaises(
|
ret = self.driver.update_migrated_volume(
|
||||||
NotImplementedError,
|
self.ctxt, TEST_VOLUME[0], TEST_VOLUME[1], "available")
|
||||||
self.driver.update_migrated_volume,
|
self.assertEqual(1, request.call_count)
|
||||||
self.ctxt,
|
actual = ({'_name_id': TEST_VOLUME[1]['id'],
|
||||||
TEST_VOLUME[0],
|
'provider_location': TEST_VOLUME[1]['provider_location']})
|
||||||
TEST_VOLUME[1],
|
self.assertEqual(actual, ret)
|
||||||
"available")
|
|
||||||
self.assertEqual(2, request.call_count)
|
|
||||||
args, kwargs = request.call_args_list[1]
|
|
||||||
self.assertEqual(kwargs['json']['label'],
|
|
||||||
TEST_VOLUME[0]['id'].replace("-", ""))
|
|
||||||
|
|
||||||
def test_unmanage_snapshot(self):
|
def test_unmanage_snapshot(self):
|
||||||
"""The driver don't support unmange_snapshot."""
|
"""The driver don't support unmange_snapshot."""
|
||||||
@ -1512,7 +1547,8 @@ class HBSDRESTFCDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.driver.common._stats = {}
|
self.driver.common._stats = {}
|
||||||
self.driver.common._stats['pools'] = [
|
self.driver.common._stats['pools'] = [
|
||||||
{'location_info': {'pool_id': 30}}]
|
{'location_info': {'pool_id': 30}}]
|
||||||
@ -1520,7 +1556,7 @@ class HBSDRESTFCDriverTest(test.TestCase):
|
|||||||
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
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_extra_specs.call_count)
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
actual = (
|
actual = (
|
||||||
{'status': 'available'},
|
{'status': 'available'},
|
||||||
[{'id': TEST_SNAPSHOT[0]['id'],
|
[{'id': TEST_SNAPSHOT[0]['id'],
|
||||||
@ -1539,6 +1575,7 @@ class HBSDRESTFCDriverTest(test.TestCase):
|
|||||||
is_group_a_cg_snapshot_type.return_value = True
|
is_group_a_cg_snapshot_type.return_value = True
|
||||||
get_volume_type_extra_specs.return_value = {}
|
get_volume_type_extra_specs.return_value = {}
|
||||||
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
@ -1550,7 +1587,7 @@ class HBSDRESTFCDriverTest(test.TestCase):
|
|||||||
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
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_extra_specs.call_count)
|
||||||
self.assertEqual(5, request.call_count)
|
self.assertEqual(6, request.call_count)
|
||||||
actual = (
|
actual = (
|
||||||
None,
|
None,
|
||||||
[{'id': TEST_SNAPSHOT[0]['id'],
|
[{'id': TEST_SNAPSHOT[0]['id'],
|
||||||
@ -1561,7 +1598,7 @@ class HBSDRESTFCDriverTest(test.TestCase):
|
|||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_group_snapshot(self, request):
|
def test_delete_group_snapshot(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR_SNAP),
|
||||||
FakeResponse(200, NOTFOUND_RESULT),
|
FakeResponse(200, NOTFOUND_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
@ -194,6 +194,18 @@ GET_LDEV_RESULT = {
|
|||||||
"poolId": 30,
|
"poolId": 30,
|
||||||
"dataReductionStatus": "DISABLED",
|
"dataReductionStatus": "DISABLED",
|
||||||
"dataReductionMode": "disabled",
|
"dataReductionMode": "disabled",
|
||||||
|
"label": "00000000000000000000000000000000",
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_SNAP = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "HDP"],
|
||||||
|
"status": "NML",
|
||||||
|
"poolId": 30,
|
||||||
|
"dataReductionStatus": "DISABLED",
|
||||||
|
"dataReductionMode": "disabled",
|
||||||
|
"label": "10000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_LDEV_RESULT_MAPPED = {
|
GET_LDEV_RESULT_MAPPED = {
|
||||||
@ -216,6 +228,7 @@ GET_LDEV_RESULT_PAIR = {
|
|||||||
"blockCapacity": 2097152,
|
"blockCapacity": 2097152,
|
||||||
"attributes": ["CVS", "HDP", "HTI"],
|
"attributes": ["CVS", "HDP", "HTI"],
|
||||||
"status": "NML",
|
"status": "NML",
|
||||||
|
"label": "10000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_POOLS_RESULT = {
|
GET_POOLS_RESULT = {
|
||||||
@ -628,24 +641,31 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.driver.common._stats = {}
|
self.driver.common._stats = {}
|
||||||
self.driver.common._stats['pools'] = [
|
self.driver.common._stats['pools'] = [
|
||||||
{'location_info': {'pool_id': 30}}]
|
{'location_info': {'pool_id': 30}}]
|
||||||
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
||||||
self.assertEqual('1', ret['provider_location'])
|
self.assertEqual('1', ret['provider_location'])
|
||||||
self.assertEqual(1, get_volume_type_extra_specs.call_count)
|
self.assertEqual(1, get_volume_type_extra_specs.call_count)
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_snapshot(self, request):
|
def test_delete_snapshot(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_SNAP),
|
||||||
FakeResponse(200, GET_LDEV_RESULT),
|
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)]
|
||||||
self.driver.delete_snapshot(TEST_SNAPSHOT[0])
|
self.driver.delete_snapshot(TEST_SNAPSHOT[0])
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(4, request.call_count)
|
||||||
|
|
||||||
|
@mock.patch.object(requests.Session, "request")
|
||||||
|
def test_delete_snapshot_is_invalid_ldev(self, request):
|
||||||
|
request.return_value = FakeResponse(200, GET_LDEV_RESULT)
|
||||||
|
self.driver.delete_snapshot(TEST_SNAPSHOT[0])
|
||||||
|
self.assertEqual(1, request.call_count)
|
||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
|
@mock.patch.object(volume_types, 'get_volume_type_extra_specs')
|
||||||
def test_create_cloned_volume(
|
def test_create_cloned_volume(
|
||||||
@ -860,17 +880,12 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
|
|||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_update_migrated_volume(self, request):
|
def test_update_migrated_volume(self, request):
|
||||||
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
|
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
|
||||||
self.assertRaises(
|
ret = self.driver.update_migrated_volume(
|
||||||
NotImplementedError,
|
self.ctxt, TEST_VOLUME[0], TEST_VOLUME[1], "available")
|
||||||
self.driver.update_migrated_volume,
|
self.assertEqual(1, request.call_count)
|
||||||
self.ctxt,
|
actual = ({'_name_id': TEST_VOLUME[1]['id'],
|
||||||
TEST_VOLUME[0],
|
'provider_location': TEST_VOLUME[1]['provider_location']})
|
||||||
TEST_VOLUME[1],
|
self.assertEqual(actual, ret)
|
||||||
"available")
|
|
||||||
self.assertEqual(2, request.call_count)
|
|
||||||
args, kwargs = request.call_args_list[1]
|
|
||||||
self.assertEqual(kwargs['json']['label'],
|
|
||||||
TEST_VOLUME[0]['id'].replace("-", ""))
|
|
||||||
|
|
||||||
def test_unmanage_snapshot(self):
|
def test_unmanage_snapshot(self):
|
||||||
"""The driver don't support unmange_snapshot."""
|
"""The driver don't support unmange_snapshot."""
|
||||||
@ -1034,7 +1049,8 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.driver.common._stats = {}
|
self.driver.common._stats = {}
|
||||||
self.driver.common._stats['pools'] = [
|
self.driver.common._stats['pools'] = [
|
||||||
{'location_info': {'pool_id': 30}}]
|
{'location_info': {'pool_id': 30}}]
|
||||||
@ -1042,7 +1058,7 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
|
|||||||
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
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_extra_specs.call_count)
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
actual = (
|
actual = (
|
||||||
{'status': 'available'},
|
{'status': 'available'},
|
||||||
[{'id': TEST_SNAPSHOT[0]['id'],
|
[{'id': TEST_SNAPSHOT[0]['id'],
|
||||||
@ -1061,6 +1077,7 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
|
|||||||
is_group_a_cg_snapshot_type.return_value = True
|
is_group_a_cg_snapshot_type.return_value = True
|
||||||
get_volume_type_extra_specs.return_value = {}
|
get_volume_type_extra_specs.return_value = {}
|
||||||
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
@ -1072,7 +1089,7 @@ class HBSDRESTISCSIDriverTest(test.TestCase):
|
|||||||
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
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_extra_specs.call_count)
|
||||||
self.assertEqual(5, request.call_count)
|
self.assertEqual(6, request.call_count)
|
||||||
actual = (
|
actual = (
|
||||||
None,
|
None,
|
||||||
[{'id': TEST_SNAPSHOT[0]['id'],
|
[{'id': TEST_SNAPSHOT[0]['id'],
|
||||||
|
@ -187,6 +187,7 @@ GET_LDEV_RESULT = {
|
|||||||
"attributes": ["CVS", "THP"],
|
"attributes": ["CVS", "THP"],
|
||||||
"status": "NML",
|
"status": "NML",
|
||||||
"poolId": 30,
|
"poolId": 30,
|
||||||
|
"label": "00000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_LDEV_RESULT_MAPPED = {
|
GET_LDEV_RESULT_MAPPED = {
|
||||||
@ -204,11 +205,29 @@ GET_LDEV_RESULT_MAPPED = {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_SNAP = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "THP"],
|
||||||
|
"status": "NML",
|
||||||
|
"poolId": 30,
|
||||||
|
"label": "10000000000000000000000000000000",
|
||||||
|
}
|
||||||
|
|
||||||
GET_LDEV_RESULT_PAIR = {
|
GET_LDEV_RESULT_PAIR = {
|
||||||
"emulationType": "OPEN-V-CVS",
|
"emulationType": "OPEN-V-CVS",
|
||||||
"blockCapacity": 2097152,
|
"blockCapacity": 2097152,
|
||||||
"attributes": ["CVS", "THP", "FS"],
|
"attributes": ["CVS", "THP", "FS"],
|
||||||
"status": "NML",
|
"status": "NML",
|
||||||
|
"label": "00000000000000000000000000000000",
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_PAIR_SNAP = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "THP", "FS"],
|
||||||
|
"status": "NML",
|
||||||
|
"label": "10000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_POOL_RESULT = {
|
GET_POOL_RESULT = {
|
||||||
@ -701,17 +720,18 @@ class HPEXPRESTFCDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.driver.common._stats = {}
|
self.driver.common._stats = {}
|
||||||
self.driver.common._stats['pools'] = [
|
self.driver.common._stats['pools'] = [
|
||||||
{'location_info': {'pool_id': 30}}]
|
{'location_info': {'pool_id': 30}}]
|
||||||
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
||||||
self.assertEqual('1', ret['provider_location'])
|
self.assertEqual('1', ret['provider_location'])
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_snapshot(self, request):
|
def test_delete_snapshot(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR_SNAP),
|
||||||
FakeResponse(200, NOTFOUND_RESULT),
|
FakeResponse(200, NOTFOUND_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
@ -727,7 +747,7 @@ class HPEXPRESTFCDriverTest(test.TestCase):
|
|||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_snapshot_no_pair(self, request):
|
def test_delete_snapshot_no_pair(self, request):
|
||||||
"""Normal case: Delete a snapshot without pair."""
|
"""Normal case: Delete a snapshot without pair."""
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_SNAP),
|
||||||
FakeResponse(200, GET_LDEV_RESULT),
|
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)]
|
||||||
@ -948,17 +968,12 @@ class HPEXPRESTFCDriverTest(test.TestCase):
|
|||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_update_migrated_volume(self, request):
|
def test_update_migrated_volume(self, request):
|
||||||
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
|
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
|
||||||
self.assertRaises(
|
ret = self.driver.update_migrated_volume(
|
||||||
NotImplementedError,
|
self.ctxt, TEST_VOLUME[0], TEST_VOLUME[1], "available")
|
||||||
self.driver.update_migrated_volume,
|
self.assertEqual(1, request.call_count)
|
||||||
self.ctxt,
|
actual = ({'_name_id': TEST_VOLUME[1]['id'],
|
||||||
TEST_VOLUME[0],
|
'provider_location': TEST_VOLUME[1]['provider_location']})
|
||||||
TEST_VOLUME[1],
|
self.assertEqual(actual, ret)
|
||||||
"available")
|
|
||||||
self.assertEqual(2, request.call_count)
|
|
||||||
args, kwargs = request.call_args_list[1]
|
|
||||||
self.assertEqual(kwargs['json']['label'],
|
|
||||||
TEST_VOLUME[0]['id'].replace("-", ""))
|
|
||||||
|
|
||||||
def test_unmanage_snapshot(self):
|
def test_unmanage_snapshot(self):
|
||||||
"""The driver don't support unmange_snapshot."""
|
"""The driver don't support unmange_snapshot."""
|
||||||
@ -1095,14 +1110,15 @@ class HPEXPRESTFCDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.driver.common._stats = {}
|
self.driver.common._stats = {}
|
||||||
self.driver.common._stats['pools'] = [
|
self.driver.common._stats['pools'] = [
|
||||||
{'location_info': {'pool_id': 30}}]
|
{'location_info': {'pool_id': 30}}]
|
||||||
ret = self.driver.create_group_snapshot(
|
ret = self.driver.create_group_snapshot(
|
||||||
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
||||||
)
|
)
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
actual = (
|
actual = (
|
||||||
{'status': 'available'},
|
{'status': 'available'},
|
||||||
[{'id': TEST_SNAPSHOT[0]['id'],
|
[{'id': TEST_SNAPSHOT[0]['id'],
|
||||||
@ -1118,6 +1134,7 @@ class HPEXPRESTFCDriverTest(test.TestCase):
|
|||||||
self, is_group_a_cg_snapshot_type, volume_get, request):
|
self, is_group_a_cg_snapshot_type, volume_get, request):
|
||||||
is_group_a_cg_snapshot_type.return_value = True
|
is_group_a_cg_snapshot_type.return_value = True
|
||||||
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
@ -1128,7 +1145,7 @@ class HPEXPRESTFCDriverTest(test.TestCase):
|
|||||||
ret = self.driver.create_group_snapshot(
|
ret = self.driver.create_group_snapshot(
|
||||||
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
||||||
)
|
)
|
||||||
self.assertEqual(5, request.call_count)
|
self.assertEqual(6, request.call_count)
|
||||||
actual = (
|
actual = (
|
||||||
None,
|
None,
|
||||||
[{'id': TEST_SNAPSHOT[0]['id'],
|
[{'id': TEST_SNAPSHOT[0]['id'],
|
||||||
@ -1139,7 +1156,7 @@ class HPEXPRESTFCDriverTest(test.TestCase):
|
|||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_group_snapshot(self, request):
|
def test_delete_group_snapshot(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR_SNAP),
|
||||||
FakeResponse(200, NOTFOUND_RESULT),
|
FakeResponse(200, NOTFOUND_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
@ -190,6 +190,7 @@ GET_LDEV_RESULT = {
|
|||||||
"attributes": ["CVS", "THP"],
|
"attributes": ["CVS", "THP"],
|
||||||
"status": "NML",
|
"status": "NML",
|
||||||
"poolId": 30,
|
"poolId": 30,
|
||||||
|
"label": "00000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_LDEV_RESULT_MAPPED = {
|
GET_LDEV_RESULT_MAPPED = {
|
||||||
@ -207,11 +208,21 @@ GET_LDEV_RESULT_MAPPED = {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_SNAP = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "THP"],
|
||||||
|
"status": "NML",
|
||||||
|
"poolId": 30,
|
||||||
|
"label": "10000000000000000000000000000000",
|
||||||
|
}
|
||||||
|
|
||||||
GET_LDEV_RESULT_PAIR = {
|
GET_LDEV_RESULT_PAIR = {
|
||||||
"emulationType": "OPEN-V-CVS",
|
"emulationType": "OPEN-V-CVS",
|
||||||
"blockCapacity": 2097152,
|
"blockCapacity": 2097152,
|
||||||
"attributes": ["CVS", "THP", "FS"],
|
"attributes": ["CVS", "THP", "FS"],
|
||||||
"status": "NML",
|
"status": "NML",
|
||||||
|
"label": "10000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_POOLS_RESULT = {
|
GET_POOLS_RESULT = {
|
||||||
@ -548,17 +559,18 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.driver.common._stats = {}
|
self.driver.common._stats = {}
|
||||||
self.driver.common._stats['pools'] = [
|
self.driver.common._stats['pools'] = [
|
||||||
{'location_info': {'pool_id': 30}}]
|
{'location_info': {'pool_id': 30}}]
|
||||||
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
||||||
self.assertEqual('1', ret['provider_location'])
|
self.assertEqual('1', ret['provider_location'])
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_snapshot(self, request):
|
def test_delete_snapshot(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_SNAP),
|
||||||
FakeResponse(200, GET_LDEV_RESULT),
|
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)]
|
||||||
@ -761,17 +773,12 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
|
|||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_update_migrated_volume(self, request):
|
def test_update_migrated_volume(self, request):
|
||||||
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
|
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
|
||||||
self.assertRaises(
|
ret = self.driver.update_migrated_volume(
|
||||||
NotImplementedError,
|
self.ctxt, TEST_VOLUME[0], TEST_VOLUME[1], "available")
|
||||||
self.driver.update_migrated_volume,
|
self.assertEqual(1, request.call_count)
|
||||||
self.ctxt,
|
actual = ({'_name_id': TEST_VOLUME[1]['id'],
|
||||||
TEST_VOLUME[0],
|
'provider_location': TEST_VOLUME[1]['provider_location']})
|
||||||
TEST_VOLUME[1],
|
self.assertEqual(actual, ret)
|
||||||
"available")
|
|
||||||
self.assertEqual(2, request.call_count)
|
|
||||||
args, kwargs = request.call_args_list[1]
|
|
||||||
self.assertEqual(kwargs['json']['label'],
|
|
||||||
TEST_VOLUME[0]['id'].replace("-", ""))
|
|
||||||
|
|
||||||
def test_unmanage_snapshot(self):
|
def test_unmanage_snapshot(self):
|
||||||
"""The driver don't support unmange_snapshot."""
|
"""The driver don't support unmange_snapshot."""
|
||||||
@ -902,14 +909,15 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.driver.common._stats = {}
|
self.driver.common._stats = {}
|
||||||
self.driver.common._stats['pools'] = [
|
self.driver.common._stats['pools'] = [
|
||||||
{'location_info': {'pool_id': 30}}]
|
{'location_info': {'pool_id': 30}}]
|
||||||
ret = self.driver.create_group_snapshot(
|
ret = self.driver.create_group_snapshot(
|
||||||
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
||||||
)
|
)
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
actual = (
|
actual = (
|
||||||
{'status': 'available'},
|
{'status': 'available'},
|
||||||
[{'id': TEST_SNAPSHOT[0]['id'],
|
[{'id': TEST_SNAPSHOT[0]['id'],
|
||||||
@ -925,6 +933,7 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
|
|||||||
self, is_group_a_cg_snapshot_type, volume_get, request):
|
self, is_group_a_cg_snapshot_type, volume_get, request):
|
||||||
is_group_a_cg_snapshot_type.return_value = True
|
is_group_a_cg_snapshot_type.return_value = True
|
||||||
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
@ -935,7 +944,7 @@ class HPEXPRESTISCSIDriverTest(test.TestCase):
|
|||||||
ret = self.driver.create_group_snapshot(
|
ret = self.driver.create_group_snapshot(
|
||||||
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
||||||
)
|
)
|
||||||
self.assertEqual(5, request.call_count)
|
self.assertEqual(6, request.call_count)
|
||||||
actual = (
|
actual = (
|
||||||
None,
|
None,
|
||||||
[{'id': TEST_SNAPSHOT[0]['id'],
|
[{'id': TEST_SNAPSHOT[0]['id'],
|
||||||
|
@ -187,6 +187,7 @@ GET_LDEV_RESULT = {
|
|||||||
"attributes": ["CVS", "DP"],
|
"attributes": ["CVS", "DP"],
|
||||||
"status": "NML",
|
"status": "NML",
|
||||||
"poolId": 30,
|
"poolId": 30,
|
||||||
|
"label": "00000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_LDEV_RESULT_MAPPED = {
|
GET_LDEV_RESULT_MAPPED = {
|
||||||
@ -204,11 +205,29 @@ GET_LDEV_RESULT_MAPPED = {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_SNAP = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "DP"],
|
||||||
|
"status": "NML",
|
||||||
|
"poolId": 30,
|
||||||
|
"label": "10000000000000000000000000000000",
|
||||||
|
}
|
||||||
|
|
||||||
GET_LDEV_RESULT_PAIR = {
|
GET_LDEV_RESULT_PAIR = {
|
||||||
"emulationType": "OPEN-V-CVS",
|
"emulationType": "OPEN-V-CVS",
|
||||||
"blockCapacity": 2097152,
|
"blockCapacity": 2097152,
|
||||||
"attributes": ["CVS", "DP", "SS"],
|
"attributes": ["CVS", "DP", "SS"],
|
||||||
"status": "NML",
|
"status": "NML",
|
||||||
|
"label": "00000000000000000000000000000000",
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_PAIR_SNAP = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "DP", "SS"],
|
||||||
|
"status": "NML",
|
||||||
|
"label": "10000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_SNAPSHOTS_RESULT = {
|
GET_SNAPSHOTS_RESULT = {
|
||||||
@ -691,17 +710,18 @@ class VStorageRESTFCDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.driver.common._stats = {}
|
self.driver.common._stats = {}
|
||||||
self.driver.common._stats['pools'] = [
|
self.driver.common._stats['pools'] = [
|
||||||
{'location_info': {'pool_id': 30}}]
|
{'location_info': {'pool_id': 30}}]
|
||||||
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
||||||
self.assertEqual('1', ret['provider_location'])
|
self.assertEqual('1', ret['provider_location'])
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_snapshot(self, request):
|
def test_delete_snapshot(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR_SNAP),
|
||||||
FakeResponse(200, NOTFOUND_RESULT),
|
FakeResponse(200, NOTFOUND_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
@ -717,7 +737,7 @@ class VStorageRESTFCDriverTest(test.TestCase):
|
|||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_snapshot_no_pair(self, request):
|
def test_delete_snapshot_no_pair(self, request):
|
||||||
"""Normal case: Delete a snapshot without pair."""
|
"""Normal case: Delete a snapshot without pair."""
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_SNAP),
|
||||||
FakeResponse(200, GET_LDEV_RESULT),
|
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)]
|
||||||
@ -938,17 +958,12 @@ class VStorageRESTFCDriverTest(test.TestCase):
|
|||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_update_migrated_volume(self, request):
|
def test_update_migrated_volume(self, request):
|
||||||
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
|
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
|
||||||
self.assertRaises(
|
ret = self.driver.update_migrated_volume(
|
||||||
NotImplementedError,
|
self.ctxt, TEST_VOLUME[0], TEST_VOLUME[1], "available")
|
||||||
self.driver.update_migrated_volume,
|
self.assertEqual(1, request.call_count)
|
||||||
self.ctxt,
|
actual = ({'_name_id': TEST_VOLUME[1]['id'],
|
||||||
TEST_VOLUME[0],
|
'provider_location': TEST_VOLUME[1]['provider_location']})
|
||||||
TEST_VOLUME[1],
|
self.assertEqual(actual, ret)
|
||||||
"available")
|
|
||||||
self.assertEqual(2, request.call_count)
|
|
||||||
args, kwargs = request.call_args_list[1]
|
|
||||||
self.assertEqual(kwargs['json']['label'],
|
|
||||||
TEST_VOLUME[0]['id'].replace("-", ""))
|
|
||||||
|
|
||||||
def test_unmanage_snapshot(self):
|
def test_unmanage_snapshot(self):
|
||||||
"""The driver don't support unmange_snapshot."""
|
"""The driver don't support unmange_snapshot."""
|
||||||
@ -1085,14 +1100,15 @@ class VStorageRESTFCDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.driver.common._stats = {}
|
self.driver.common._stats = {}
|
||||||
self.driver.common._stats['pools'] = [
|
self.driver.common._stats['pools'] = [
|
||||||
{'location_info': {'pool_id': 30}}]
|
{'location_info': {'pool_id': 30}}]
|
||||||
ret = self.driver.create_group_snapshot(
|
ret = self.driver.create_group_snapshot(
|
||||||
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
||||||
)
|
)
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
actual = (
|
actual = (
|
||||||
{'status': 'available'},
|
{'status': 'available'},
|
||||||
[{'id': TEST_SNAPSHOT[0]['id'],
|
[{'id': TEST_SNAPSHOT[0]['id'],
|
||||||
@ -1108,6 +1124,7 @@ class VStorageRESTFCDriverTest(test.TestCase):
|
|||||||
self, is_group_a_cg_snapshot_type, volume_get, request):
|
self, is_group_a_cg_snapshot_type, volume_get, request):
|
||||||
is_group_a_cg_snapshot_type.return_value = True
|
is_group_a_cg_snapshot_type.return_value = True
|
||||||
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
@ -1118,7 +1135,7 @@ class VStorageRESTFCDriverTest(test.TestCase):
|
|||||||
ret = self.driver.create_group_snapshot(
|
ret = self.driver.create_group_snapshot(
|
||||||
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
||||||
)
|
)
|
||||||
self.assertEqual(5, request.call_count)
|
self.assertEqual(6, request.call_count)
|
||||||
actual = (
|
actual = (
|
||||||
None,
|
None,
|
||||||
[{'id': TEST_SNAPSHOT[0]['id'],
|
[{'id': TEST_SNAPSHOT[0]['id'],
|
||||||
@ -1129,7 +1146,7 @@ class VStorageRESTFCDriverTest(test.TestCase):
|
|||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_group_snapshot(self, request):
|
def test_delete_group_snapshot(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR_SNAP),
|
||||||
FakeResponse(200, NOTFOUND_RESULT),
|
FakeResponse(200, NOTFOUND_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
@ -191,6 +191,7 @@ GET_LDEV_RESULT = {
|
|||||||
"attributes": ["CVS", "DP"],
|
"attributes": ["CVS", "DP"],
|
||||||
"status": "NML",
|
"status": "NML",
|
||||||
"poolId": 30,
|
"poolId": 30,
|
||||||
|
"label": "00000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_LDEV_RESULT_MAPPED = {
|
GET_LDEV_RESULT_MAPPED = {
|
||||||
@ -208,11 +209,29 @@ GET_LDEV_RESULT_MAPPED = {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_SNAP = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "DP"],
|
||||||
|
"status": "NML",
|
||||||
|
"poolId": 30,
|
||||||
|
"label": "10000000000000000000000000000000",
|
||||||
|
}
|
||||||
|
|
||||||
GET_LDEV_RESULT_PAIR = {
|
GET_LDEV_RESULT_PAIR = {
|
||||||
"emulationType": "OPEN-V-CVS",
|
"emulationType": "OPEN-V-CVS",
|
||||||
"blockCapacity": 2097152,
|
"blockCapacity": 2097152,
|
||||||
"attributes": ["CVS", "DP", "SS"],
|
"attributes": ["CVS", "DP", "SS"],
|
||||||
"status": "NML",
|
"status": "NML",
|
||||||
|
"label": "00000000000000000000000000000000",
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_LDEV_RESULT_PAIR_SNAP = {
|
||||||
|
"emulationType": "OPEN-V-CVS",
|
||||||
|
"blockCapacity": 2097152,
|
||||||
|
"attributes": ["CVS", "DP", "SS"],
|
||||||
|
"status": "NML",
|
||||||
|
"label": "10000000000000000000000000000000",
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_POOLS_RESULT = {
|
GET_POOLS_RESULT = {
|
||||||
@ -584,17 +603,18 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.driver.common._stats = {}
|
self.driver.common._stats = {}
|
||||||
self.driver.common._stats['pools'] = [
|
self.driver.common._stats['pools'] = [
|
||||||
{'location_info': {'pool_id': 30}}]
|
{'location_info': {'pool_id': 30}}]
|
||||||
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
ret = self.driver.create_snapshot(TEST_SNAPSHOT[0])
|
||||||
self.assertEqual('1', ret['provider_location'])
|
self.assertEqual('1', ret['provider_location'])
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_snapshot(self, request):
|
def test_delete_snapshot(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_SNAP),
|
||||||
FakeResponse(200, GET_LDEV_RESULT),
|
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)]
|
||||||
@ -797,17 +817,12 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
|
|||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_update_migrated_volume(self, request):
|
def test_update_migrated_volume(self, request):
|
||||||
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
|
request.return_value = FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)
|
||||||
self.assertRaises(
|
ret = self.driver.update_migrated_volume(
|
||||||
NotImplementedError,
|
self.ctxt, TEST_VOLUME[0], TEST_VOLUME[1], "available")
|
||||||
self.driver.update_migrated_volume,
|
self.assertEqual(1, request.call_count)
|
||||||
self.ctxt,
|
actual = ({'_name_id': TEST_VOLUME[1]['id'],
|
||||||
TEST_VOLUME[0],
|
'provider_location': TEST_VOLUME[1]['provider_location']})
|
||||||
TEST_VOLUME[1],
|
self.assertEqual(actual, ret)
|
||||||
"available")
|
|
||||||
self.assertEqual(2, request.call_count)
|
|
||||||
args, kwargs = request.call_args_list[1]
|
|
||||||
self.assertEqual(kwargs['json']['label'],
|
|
||||||
TEST_VOLUME[0]['id'].replace("-", ""))
|
|
||||||
|
|
||||||
def test_unmanage_snapshot(self):
|
def test_unmanage_snapshot(self):
|
||||||
"""The driver don't support unmange_snapshot."""
|
"""The driver don't support unmange_snapshot."""
|
||||||
@ -938,14 +953,15 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
|
|||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT)]
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT)]
|
||||||
self.driver.common._stats = {}
|
self.driver.common._stats = {}
|
||||||
self.driver.common._stats['pools'] = [
|
self.driver.common._stats['pools'] = [
|
||||||
{'location_info': {'pool_id': 30}}]
|
{'location_info': {'pool_id': 30}}]
|
||||||
ret = self.driver.create_group_snapshot(
|
ret = self.driver.create_group_snapshot(
|
||||||
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
||||||
)
|
)
|
||||||
self.assertEqual(4, request.call_count)
|
self.assertEqual(5, request.call_count)
|
||||||
actual = (
|
actual = (
|
||||||
{'status': 'available'},
|
{'status': 'available'},
|
||||||
[{'id': TEST_SNAPSHOT[0]['id'],
|
[{'id': TEST_SNAPSHOT[0]['id'],
|
||||||
@ -961,6 +977,7 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
|
|||||||
self, is_group_a_cg_snapshot_type, volume_get, request):
|
self, is_group_a_cg_snapshot_type, volume_get, request):
|
||||||
is_group_a_cg_snapshot_type.return_value = True
|
is_group_a_cg_snapshot_type.return_value = True
|
||||||
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
request.side_effect = [FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT_PAIR),
|
||||||
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
FakeResponse(202, COMPLETED_SUCCEEDED_RESULT),
|
||||||
@ -971,7 +988,7 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
|
|||||||
ret = self.driver.create_group_snapshot(
|
ret = self.driver.create_group_snapshot(
|
||||||
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
self.ctxt, TEST_GROUP_SNAP[0], [TEST_SNAPSHOT[0]]
|
||||||
)
|
)
|
||||||
self.assertEqual(5, request.call_count)
|
self.assertEqual(6, request.call_count)
|
||||||
actual = (
|
actual = (
|
||||||
None,
|
None,
|
||||||
[{'id': TEST_SNAPSHOT[0]['id'],
|
[{'id': TEST_SNAPSHOT[0]['id'],
|
||||||
@ -982,7 +999,7 @@ class VStorageRESTISCSIDriverTest(test.TestCase):
|
|||||||
|
|
||||||
@mock.patch.object(requests.Session, "request")
|
@mock.patch.object(requests.Session, "request")
|
||||||
def test_delete_group_snapshot(self, request):
|
def test_delete_group_snapshot(self, request):
|
||||||
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR),
|
request.side_effect = [FakeResponse(200, GET_LDEV_RESULT_PAIR_SNAP),
|
||||||
FakeResponse(200, NOTFOUND_RESULT),
|
FakeResponse(200, NOTFOUND_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
FakeResponse(200, GET_SNAPSHOTS_RESULT),
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#
|
#
|
||||||
"""Common module for Hitachi HBSD Driver."""
|
"""Common module for Hitachi HBSD Driver."""
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@ -47,6 +48,8 @@ _GROUP_NAME_VAR_LEN = {GROUP_NAME_VAR_WWN: _GROUP_NAME_VAR_WWN_LEN,
|
|||||||
STR_VOLUME = 'volume'
|
STR_VOLUME = 'volume'
|
||||||
STR_SNAPSHOT = 'snapshot'
|
STR_SNAPSHOT = 'snapshot'
|
||||||
|
|
||||||
|
_UUID_PATTERN = re.compile(r'^[\da-f]{32}$')
|
||||||
|
|
||||||
_INHERITED_VOLUME_OPTS = [
|
_INHERITED_VOLUME_OPTS = [
|
||||||
'volume_backend_name',
|
'volume_backend_name',
|
||||||
'volume_driver',
|
'volume_driver',
|
||||||
@ -346,13 +349,19 @@ class HBSDCommon():
|
|||||||
"""Disconnect all volume pairs to which the specified S-VOL belongs."""
|
"""Disconnect all volume pairs to which the specified S-VOL belongs."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def get_pair_info(self, ldev):
|
def get_pair_info(self, ldev, ldev_info=None):
|
||||||
"""Return volume pair info(LDEV number, pair status and pair type)."""
|
"""Return volume pair info(LDEV number, pair status and pair type)."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def delete_pair(self, ldev):
|
def delete_pair(self, ldev, ldev_info=None):
|
||||||
"""Disconnect all volume pairs to which the specified LDEV belongs."""
|
"""Disconnect all volume pairs to which the specified LDEV belongs.
|
||||||
pair_info = self.get_pair_info(ldev)
|
|
||||||
|
:param int ldev: The ID of the LDEV whose TI pair needs be deleted
|
||||||
|
:param dict ldev_info: LDEV info
|
||||||
|
:return: None
|
||||||
|
:raises VolumeDriverException: if the LDEV is a P-VOL of a TI pair
|
||||||
|
"""
|
||||||
|
pair_info = self.get_pair_info(ldev, ldev_info)
|
||||||
if not pair_info:
|
if not pair_info:
|
||||||
return
|
return
|
||||||
if pair_info['pvol'] == ldev:
|
if pair_info['pvol'] == ldev:
|
||||||
@ -383,12 +392,57 @@ class HBSDCommon():
|
|||||||
"""Delete the specified LDEV from the storage."""
|
"""Delete the specified LDEV from the storage."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def delete_ldev(self, ldev):
|
def delete_ldev(self, ldev, ldev_info=None):
|
||||||
"""Delete the specified LDEV."""
|
"""Delete the specified LDEV.
|
||||||
self.delete_pair(ldev)
|
|
||||||
|
:param int ldev: The ID of the LDEV to be deleted
|
||||||
|
:param dict ldev_info: LDEV info
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.delete_pair(ldev, ldev_info)
|
||||||
self.unmap_ldev_from_storage(ldev)
|
self.unmap_ldev_from_storage(ldev)
|
||||||
self.delete_ldev_from_storage(ldev)
|
self.delete_ldev_from_storage(ldev)
|
||||||
|
|
||||||
|
def is_invalid_ldev(self, ldev, obj, ldev_info_):
|
||||||
|
"""Check if the specified LDEV corresponds to the specified object.
|
||||||
|
|
||||||
|
If the LDEV label and the object's id or name_id do not match, the LDEV
|
||||||
|
was deleted and another LDEV with the same ID was created for another
|
||||||
|
volume or snapshot. In this case, we say that the LDEV is invalid.
|
||||||
|
If the LDEV label is not set or its format is unexpected, we cannot
|
||||||
|
judge if the LDEV corresponds to the object. This can happen if the
|
||||||
|
LDEV was created in older versions of this product or if the user
|
||||||
|
overwrote the label. In this case, we just say that the LDEV is not
|
||||||
|
invalid, although we are not completely sure about it.
|
||||||
|
The reason for using name_id rather than id for volumes in comparison
|
||||||
|
is that id of the volume that corresponds to the LDEV changes by
|
||||||
|
host-assisted migration while that is not the case with name_id and
|
||||||
|
that the LDEV label is created from id of the volume when the LDEV is
|
||||||
|
created and is never changed after that.
|
||||||
|
Because Snapshot objects do not have name_id, we use id instead of
|
||||||
|
name_id if the object is a Snapshot. We assume that the object is a
|
||||||
|
Snapshot object if hasattr(obj, 'name_id') returns False.
|
||||||
|
This method returns False if the LDEV does not exist on the storage.
|
||||||
|
The absence of the LDEV on the storage is detected elsewhere.
|
||||||
|
:param int ldev: The ID of the LDEV to be checked
|
||||||
|
:param obj: The object to be checked
|
||||||
|
:type obj: Volume or Snapshot
|
||||||
|
:param dict ldev_info_: LDEV info. This is an output area. Data is
|
||||||
|
written by this method, but the area must be secured by the caller.
|
||||||
|
:return: True if the LDEV does not correspond to the object, False
|
||||||
|
otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
ldev_info = self.get_ldev_info(None, ldev)
|
||||||
|
# To avoid calling the same REST API multiple times, we pass the LDEV
|
||||||
|
# info to the caller.
|
||||||
|
ldev_info_.update(ldev_info)
|
||||||
|
return ('label' in ldev_info
|
||||||
|
and _UUID_PATTERN.match(ldev_info['label'])
|
||||||
|
and ldev_info['label'] != (
|
||||||
|
obj.name_id if hasattr(obj, 'name_id') else
|
||||||
|
obj.id).replace('-', ''))
|
||||||
|
|
||||||
def delete_volume(self, volume):
|
def delete_volume(self, volume):
|
||||||
"""Delete the specified volume."""
|
"""Delete the specified volume."""
|
||||||
ldev = self.get_ldev(volume)
|
ldev = self.get_ldev(volume)
|
||||||
@ -397,8 +451,18 @@ class HBSDCommon():
|
|||||||
MSG.INVALID_LDEV_FOR_DELETION,
|
MSG.INVALID_LDEV_FOR_DELETION,
|
||||||
method='delete_volume', id=volume['id'])
|
method='delete_volume', id=volume['id'])
|
||||||
return
|
return
|
||||||
|
# Check if the LDEV corresponds to the volume.
|
||||||
|
# To avoid KeyError when accessing a missing attribute, set the default
|
||||||
|
# value to None.
|
||||||
|
ldev_info = defaultdict(lambda: None)
|
||||||
|
if self.is_invalid_ldev(ldev, volume, ldev_info):
|
||||||
|
# If the LDEV is assigned to another object, skip deleting it.
|
||||||
|
self.output_log(MSG.SKIP_DELETING_LDEV, obj='volume',
|
||||||
|
obj_id=volume.id, ldev=ldev,
|
||||||
|
ldev_label=ldev_info['label'])
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
self.delete_ldev(ldev)
|
self.delete_ldev(ldev, ldev_info)
|
||||||
except exception.VolumeDriverException as ex:
|
except exception.VolumeDriverException as ex:
|
||||||
if utils.BUSY_MESSAGE in ex.msg:
|
if utils.BUSY_MESSAGE in ex.msg:
|
||||||
raise exception.VolumeIsBusy(volume_name=volume['name'])
|
raise exception.VolumeIsBusy(volume_name=volume['name'])
|
||||||
@ -422,6 +486,7 @@ class HBSDCommon():
|
|||||||
new_ldev = self.copy_on_storage(
|
new_ldev = self.copy_on_storage(
|
||||||
ldev, size, extra_specs, pool_id, snap_pool_id, ldev_range,
|
ldev, size, extra_specs, pool_id, snap_pool_id, ldev_range,
|
||||||
is_snapshot=True)
|
is_snapshot=True)
|
||||||
|
self.modify_ldev_name(new_ldev, snapshot.id.replace("-", ""))
|
||||||
return {
|
return {
|
||||||
'provider_location': str(new_ldev),
|
'provider_location': str(new_ldev),
|
||||||
}
|
}
|
||||||
@ -434,8 +499,18 @@ class HBSDCommon():
|
|||||||
MSG.INVALID_LDEV_FOR_DELETION, method='delete_snapshot',
|
MSG.INVALID_LDEV_FOR_DELETION, method='delete_snapshot',
|
||||||
id=snapshot['id'])
|
id=snapshot['id'])
|
||||||
return
|
return
|
||||||
|
# Check if the LDEV corresponds to the snapshot.
|
||||||
|
# To avoid KeyError when accessing a missing attribute, set the default
|
||||||
|
# value to None.
|
||||||
|
ldev_info = defaultdict(lambda: None)
|
||||||
|
if self.is_invalid_ldev(ldev, snapshot, ldev_info):
|
||||||
|
# If the LDEV is assigned to another object, skip deleting it.
|
||||||
|
self.output_log(MSG.SKIP_DELETING_LDEV, obj='snapshot',
|
||||||
|
obj_id=snapshot.id, ldev=ldev,
|
||||||
|
ldev_label=ldev_info['label'])
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
self.delete_ldev(ldev)
|
self.delete_ldev(ldev, ldev_info)
|
||||||
except exception.VolumeDriverException as ex:
|
except exception.VolumeDriverException as ex:
|
||||||
if utils.BUSY_MESSAGE in ex.msg:
|
if utils.BUSY_MESSAGE in ex.msg:
|
||||||
raise exception.SnapshotIsBusy(snapshot_name=snapshot['name'])
|
raise exception.SnapshotIsBusy(snapshot_name=snapshot['name'])
|
||||||
@ -1102,12 +1177,10 @@ class HBSDCommon():
|
|||||||
"""Migrate the specified volume."""
|
"""Migrate the specified volume."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def update_migrated_volume(self, volume, new_volume):
|
def update_migrated_volume(self, new_volume):
|
||||||
"""Update LDEV settings after generic volume migration."""
|
"""Return model update for migrated volume."""
|
||||||
ldev = self.get_ldev(new_volume)
|
return {'_name_id': new_volume.name_id,
|
||||||
# We do not need to check if ldev is not None because it is guaranteed
|
'provider_location': new_volume.provider_location}
|
||||||
# that ldev is not None because migration has been successful so far.
|
|
||||||
self.modify_ldev_name(ldev, volume['id'].replace("-", ""))
|
|
||||||
|
|
||||||
def retype(self, ctxt, volume, new_type, diff, host):
|
def retype(self, ctxt, volume, new_type, diff, host):
|
||||||
"""Retype the specified volume."""
|
"""Retype the specified volume."""
|
||||||
@ -1164,7 +1237,11 @@ class HBSDCommon():
|
|||||||
provider_location = obj.get('provider_location')
|
provider_location = obj.get('provider_location')
|
||||||
if not provider_location:
|
if not provider_location:
|
||||||
return None
|
return None
|
||||||
if provider_location.isdigit():
|
if provider_location.isdigit() and not getattr(self, 'is_secondary',
|
||||||
|
False):
|
||||||
|
# This format implies that the value is the ID of an LDEV in the
|
||||||
|
# primary storage. Therefore, the secondary instance should not
|
||||||
|
# retrieve this value.
|
||||||
return int(provider_location)
|
return int(provider_location)
|
||||||
if provider_location.startswith('{'):
|
if provider_location.startswith('{'):
|
||||||
loc = json.loads(provider_location)
|
loc = json.loads(provider_location)
|
||||||
|
@ -191,9 +191,7 @@ class HBSDFCDriver(driver.FibreChannelDriver):
|
|||||||
self, ctxt, volume, new_volume, original_volume_status):
|
self, ctxt, volume, new_volume, original_volume_status):
|
||||||
"""Do any remaining jobs after migration."""
|
"""Do any remaining jobs after migration."""
|
||||||
self.common.discard_zero_page(new_volume)
|
self.common.discard_zero_page(new_volume)
|
||||||
self.common.update_migrated_volume(volume, new_volume)
|
return self.common.update_migrated_volume(new_volume)
|
||||||
super(HBSDFCDriver, self).update_migrated_volume(
|
|
||||||
ctxt, volume, new_volume, original_volume_status)
|
|
||||||
|
|
||||||
@volume_utils.trace
|
@volume_utils.trace
|
||||||
def copy_image_to_volume(self, context, volume, image_service, image_id,
|
def copy_image_to_volume(self, context, volume, image_service, image_id,
|
||||||
|
@ -187,9 +187,7 @@ class HBSDISCSIDriver(driver.ISCSIDriver):
|
|||||||
self, ctxt, volume, new_volume, original_volume_status):
|
self, ctxt, volume, new_volume, original_volume_status):
|
||||||
"""Do any remaining jobs after migration."""
|
"""Do any remaining jobs after migration."""
|
||||||
self.common.discard_zero_page(new_volume)
|
self.common.discard_zero_page(new_volume)
|
||||||
self.common.update_migrated_volume(volume, new_volume)
|
return self.common.update_migrated_volume(new_volume)
|
||||||
super(HBSDISCSIDriver, self).update_migrated_volume(
|
|
||||||
ctxt, volume, new_volume, original_volume_status)
|
|
||||||
|
|
||||||
@volume_utils.trace
|
@volume_utils.trace
|
||||||
def copy_image_to_volume(self, context, volume, image_service, image_id,
|
def copy_image_to_volume(self, context, volume, image_service, image_id,
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#
|
#
|
||||||
"""replication module for Hitachi HBSD Driver."""
|
"""replication module for Hitachi HBSD Driver."""
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from eventlet import greenthread
|
from eventlet import greenthread
|
||||||
@ -562,16 +563,32 @@ class HBSDREPLICATION(rest.HBSDREST):
|
|||||||
}
|
}
|
||||||
return self.rep_primary.create_volume(volume)
|
return self.rep_primary.create_volume(volume)
|
||||||
|
|
||||||
def _has_rep_pair(self, ldev):
|
def _has_rep_pair(self, ldev, ldev_info=None):
|
||||||
ldev_info = self.rep_primary.get_ldev_info(
|
"""Return if the specified LDEV has a replication pair.
|
||||||
['status', 'attributes'], ldev)
|
|
||||||
|
:param int ldev: The LDEV ID
|
||||||
|
:param dict ldev_info: LDEV info
|
||||||
|
:return: True if the LDEV status is normal and the LDEV has a
|
||||||
|
replication pair, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
if ldev_info is None:
|
||||||
|
ldev_info = self.rep_primary.get_ldev_info(
|
||||||
|
['status', 'attributes'], ldev)
|
||||||
return (ldev_info['status'] == rest.NORMAL_STS and
|
return (ldev_info['status'] == rest.NORMAL_STS and
|
||||||
self.driver_info['mirror_attr'] in ldev_info['attributes'])
|
self.driver_info['mirror_attr'] in ldev_info['attributes'])
|
||||||
|
|
||||||
def _get_rep_pair_info(self, pldev):
|
def _get_rep_pair_info(self, pldev, ldev_info=None):
|
||||||
"""Return replication pair info."""
|
"""Return replication pair info.
|
||||||
|
|
||||||
|
:param int pldev: The ID of the LDEV(P-VOL in case of a pair)
|
||||||
|
:param dict ldev_info: LDEV info
|
||||||
|
:return: replication pair info. An empty dict if the LDEV does not
|
||||||
|
have a pair.
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
pair_info = {}
|
pair_info = {}
|
||||||
if not self._has_rep_pair(pldev):
|
if not self._has_rep_pair(pldev, ldev_info):
|
||||||
return pair_info
|
return pair_info
|
||||||
self._require_rep_secondary()
|
self._require_rep_secondary()
|
||||||
copy_group_name = self._create_rep_copy_group_name(pldev)
|
copy_group_name = self._create_rep_copy_group_name(pldev)
|
||||||
@ -609,6 +626,65 @@ class HBSDREPLICATION(rest.HBSDREST):
|
|||||||
self.rep_primary.client.delete_remote_copypair(
|
self.rep_primary.client.delete_remote_copypair(
|
||||||
self.rep_secondary.client, copy_group_name, pvol, svol)
|
self.rep_secondary.client, copy_group_name, pvol, svol)
|
||||||
|
|
||||||
|
def _delete_volume_pre_check(self, volume):
|
||||||
|
"""Pre-check for delete_volume().
|
||||||
|
|
||||||
|
:param Volume volume: The volume to be checked
|
||||||
|
:return: svol: The ID of the S-VOL
|
||||||
|
:rtype: int
|
||||||
|
:return: pvol_is_invalid: True if P-VOL is invalid, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
:return: svol_is_invalid: True if S-VOL is invalid, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
:return: pair_exists: True if the pair exists, False otherwise
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
# Check if the LDEV in the primary storage corresponds to the volume
|
||||||
|
pvol_is_invalid = True
|
||||||
|
# To avoid KeyError when accessing a missing attribute, set the default
|
||||||
|
# value to None.
|
||||||
|
pvol_info = defaultdict(lambda: None)
|
||||||
|
pvol = self.rep_primary.get_ldev(volume)
|
||||||
|
if pvol is not None:
|
||||||
|
if self.rep_primary.is_invalid_ldev(pvol, volume, pvol_info):
|
||||||
|
# If the LDEV is assigned to another object, skip deleting it.
|
||||||
|
self.rep_primary.output_log(
|
||||||
|
MSG.SKIP_DELETING_LDEV, obj='volume', obj_id=volume.id,
|
||||||
|
ldev=pvol, ldev_label=pvol_info['label'])
|
||||||
|
else:
|
||||||
|
pvol_is_invalid = False
|
||||||
|
# Check if the pair exists on the storage.
|
||||||
|
pair_exists = False
|
||||||
|
svol_is_invalid = True
|
||||||
|
svol = None
|
||||||
|
if not pvol_is_invalid:
|
||||||
|
pair_info = self._get_rep_pair_info(pvol, pvol_info)
|
||||||
|
if pair_info:
|
||||||
|
pair_exists = True
|
||||||
|
# Because this pair is a valid P-VOL's pair, we need to delete
|
||||||
|
# it and its LDEVs. The LDEV ID of the S-VOL to be deleted is
|
||||||
|
# uniquely determined from the pair info. Therefore, there is
|
||||||
|
# no need to get it from provider_location or to validate the
|
||||||
|
# S-VOL by comparing the volume ID with the S-VOL's label.
|
||||||
|
svol = pair_info['svol_info'][0]['ldev']
|
||||||
|
svol_is_invalid = False
|
||||||
|
# Check if the LDEV in the secondary storage corresponds to the volume
|
||||||
|
if svol_is_invalid:
|
||||||
|
svol = self.rep_secondary.get_ldev(volume)
|
||||||
|
if svol is not None:
|
||||||
|
# To avoid KeyError when accessing a missing attribute, set the
|
||||||
|
# default value to None.
|
||||||
|
svol_info = defaultdict(lambda: None)
|
||||||
|
if self.rep_secondary.is_invalid_ldev(svol, volume, svol_info):
|
||||||
|
# If the LDEV is assigned to another object, skip deleting
|
||||||
|
# it.
|
||||||
|
self.rep_secondary.output_log(
|
||||||
|
MSG.SKIP_DELETING_LDEV, obj='volume', obj_id=volume.id,
|
||||||
|
ldev=svol, ldev_label=svol_info['label'])
|
||||||
|
else:
|
||||||
|
svol_is_invalid = False
|
||||||
|
return svol, pvol_is_invalid, svol_is_invalid, pair_exists
|
||||||
|
|
||||||
def delete_volume(self, volume):
|
def delete_volume(self, volume):
|
||||||
"""Delete the specified volume."""
|
"""Delete the specified volume."""
|
||||||
self._require_rep_primary()
|
self._require_rep_primary()
|
||||||
@ -618,22 +694,34 @@ class HBSDREPLICATION(rest.HBSDREST):
|
|||||||
MSG.INVALID_LDEV_FOR_DELETION, method='delete_volume',
|
MSG.INVALID_LDEV_FOR_DELETION, method='delete_volume',
|
||||||
id=volume.id)
|
id=volume.id)
|
||||||
return
|
return
|
||||||
pair_info = self._get_rep_pair_info(ldev)
|
# Run pre-check.
|
||||||
if pair_info:
|
svol, pvol_is_invalid, svol_is_invalid, pair_exists = (
|
||||||
self._delete_rep_pair(
|
self._delete_volume_pre_check(volume))
|
||||||
pair_info['pvol'], pair_info['svol_info'][0]['ldev'])
|
# Delete the pair if it exists.
|
||||||
|
if pair_exists:
|
||||||
|
self._delete_rep_pair(ldev, svol)
|
||||||
|
# Delete LDEVs if they are valid.
|
||||||
|
thread = None
|
||||||
|
if not svol_is_invalid:
|
||||||
thread = greenthread.spawn(
|
thread = greenthread.spawn(
|
||||||
self.rep_secondary.delete_volume, volume)
|
self.rep_secondary.delete_volume, volume)
|
||||||
try:
|
try:
|
||||||
|
if not pvol_is_invalid:
|
||||||
self.rep_primary.delete_volume(volume)
|
self.rep_primary.delete_volume(volume)
|
||||||
finally:
|
finally:
|
||||||
|
if thread is not None:
|
||||||
thread.wait()
|
thread.wait()
|
||||||
else:
|
|
||||||
self.rep_primary.delete_volume(volume)
|
|
||||||
|
|
||||||
def delete_ldev(self, ldev):
|
def delete_ldev(self, ldev, ldev_info=None):
|
||||||
|
"""Delete the specified LDEV[s].
|
||||||
|
|
||||||
|
:param int ldev: The ID of the LDEV(P-VOL in case of a pair) to be
|
||||||
|
deleted
|
||||||
|
:param dict ldev_info: LDEV(P-VOL in case of a pair) info
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
self._require_rep_primary()
|
self._require_rep_primary()
|
||||||
pair_info = self._get_rep_pair_info(ldev)
|
pair_info = self._get_rep_pair_info(ldev, ldev_info)
|
||||||
if pair_info:
|
if pair_info:
|
||||||
self._delete_rep_pair(ldev, pair_info['svol_info'][0]['ldev'])
|
self._delete_rep_pair(ldev, pair_info['svol_info'][0]['ldev'])
|
||||||
th = greenthread.spawn(self.rep_secondary.delete_ldev,
|
th = greenthread.spawn(self.rep_secondary.delete_ldev,
|
||||||
@ -940,23 +1028,6 @@ class HBSDREPLICATION(rest.HBSDREST):
|
|||||||
else:
|
else:
|
||||||
return self.rep_primary.migrate_volume(volume, host)
|
return self.rep_primary.migrate_volume(volume, host)
|
||||||
|
|
||||||
def update_migrated_volume(self, volume, new_volume):
|
|
||||||
"""Update LDEV settings after generic volume migration."""
|
|
||||||
self._require_rep_primary()
|
|
||||||
ldev = self.rep_primary.get_ldev(new_volume)
|
|
||||||
# We do not need to check if ldev is not None because it is guaranteed
|
|
||||||
# that ldev is not None because migration has been successful so far.
|
|
||||||
if self._has_rep_pair(ldev):
|
|
||||||
self._require_rep_secondary()
|
|
||||||
thread = greenthread.spawn(
|
|
||||||
self.rep_secondary.update_migrated_volume, volume, new_volume)
|
|
||||||
try:
|
|
||||||
self.rep_primary.update_migrated_volume(volume, new_volume)
|
|
||||||
finally:
|
|
||||||
thread.wait()
|
|
||||||
else:
|
|
||||||
self.rep_primary.update_migrated_volume(volume, new_volume)
|
|
||||||
|
|
||||||
def _resync_rep_pair(self, pvol, svol):
|
def _resync_rep_pair(self, pvol, svol):
|
||||||
copy_group_name = self._create_rep_copy_group_name(pvol)
|
copy_group_name = self._create_rep_copy_group_name(pvol)
|
||||||
rep_type = self.driver_info['mirror_attr']
|
rep_type = self.driver_info['mirror_attr']
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2020, 2023, Hitachi, Ltd.
|
# Copyright (C) 2020, 2024, Hitachi, Ltd.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
@ -522,9 +522,22 @@ class HBSDREST(common.HBSDCommon):
|
|||||||
self._create_clone_pair(pvol, svol, snap_pool_id)
|
self._create_clone_pair(pvol, svol, snap_pool_id)
|
||||||
|
|
||||||
def get_ldev_info(self, keys, ldev, **kwargs):
|
def get_ldev_info(self, keys, ldev, **kwargs):
|
||||||
"""Return a dictionary of LDEV-related items."""
|
"""Return a dictionary of LDEV-related items.
|
||||||
|
|
||||||
|
:param keys: LDEV Attributes to be obtained. Specify None to obtain all
|
||||||
|
LDEV attributes.
|
||||||
|
:type keys: list or NoneType
|
||||||
|
:param int ldev: The LDEV ID
|
||||||
|
:param dict kwargs: REST API options
|
||||||
|
:return: LDEV info
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
d = {}
|
d = {}
|
||||||
result = self.client.get_ldev(ldev, **kwargs)
|
result = self.client.get_ldev(ldev, **kwargs)
|
||||||
|
if not keys:
|
||||||
|
# To avoid KeyError when accessing a missing attribute, set the
|
||||||
|
# default value to None.
|
||||||
|
return defaultdict(lambda: None, result)
|
||||||
for key in keys:
|
for key in keys:
|
||||||
d[key] = result.get(key)
|
d[key] = result.get(key)
|
||||||
return d
|
return d
|
||||||
@ -909,10 +922,17 @@ class HBSDREST(common.HBSDCommon):
|
|||||||
|
|
||||||
return pvol, [{'ldev': svol, 'is_psus': is_psus, 'status': status}]
|
return pvol, [{'ldev': svol, 'is_psus': is_psus, 'status': status}]
|
||||||
|
|
||||||
def get_pair_info(self, ldev):
|
def get_pair_info(self, ldev, ldev_info=None):
|
||||||
"""Return info of the volume pair."""
|
"""Return info of the volume pair.
|
||||||
|
|
||||||
|
:param int ldev: The LDEV ID
|
||||||
|
:param dict ldev_info: LDEV info
|
||||||
|
:return: TI pair info if the LDEV has TI pairs, None otherwise
|
||||||
|
:rtype: dict or NoneType
|
||||||
|
"""
|
||||||
pair_info = {}
|
pair_info = {}
|
||||||
ldev_info = self.get_ldev_info(['status', 'attributes'], ldev)
|
if ldev_info is None:
|
||||||
|
ldev_info = self.get_ldev_info(['status', 'attributes'], ldev)
|
||||||
if (ldev_info['status'] != NORMAL_STS or
|
if (ldev_info['status'] != NORMAL_STS or
|
||||||
self.driver_info['pair_attr'] not in ldev_info['attributes']):
|
self.driver_info['pair_attr'] not in ldev_info['attributes']):
|
||||||
return None
|
return None
|
||||||
@ -1278,6 +1298,8 @@ class HBSDREST(common.HBSDCommon):
|
|||||||
extra_specs = self.get_volume_extra_specs(snapshot.volume)
|
extra_specs = self.get_volume_extra_specs(snapshot.volume)
|
||||||
pair['svol'] = self.create_ldev(size, extra_specs,
|
pair['svol'] = self.create_ldev(size, extra_specs,
|
||||||
pool_id, ldev_range)
|
pool_id, ldev_range)
|
||||||
|
self.modify_ldev_name(pair['svol'],
|
||||||
|
snapshot.id.replace("-", ""))
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
pair['msg'] = utils.get_exception_msg(exc)
|
pair['msg'] = utils.get_exception_msg(exc)
|
||||||
raise loopingcall.LoopingCallDone(pair)
|
raise loopingcall.LoopingCallDone(pair)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2020, 2023, Hitachi, Ltd.
|
# Copyright (C) 2020, 2024, Hitachi, Ltd.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
@ -253,6 +253,14 @@ class HBSDMsg(enum.Enum):
|
|||||||
'to be active by the Fibre Channel Zone Manager.',
|
'to be active by the Fibre Channel Zone Manager.',
|
||||||
'suffix': WARNING_SUFFIX,
|
'suffix': WARNING_SUFFIX,
|
||||||
}
|
}
|
||||||
|
SKIP_DELETING_LDEV = {
|
||||||
|
'msg_id': 348,
|
||||||
|
'loglevel': base_logging.WARNING,
|
||||||
|
'msg': 'Skip deleting the LDEV and its LUNs and pairs because the '
|
||||||
|
'LDEV is used by another object. (%(obj)s: %(obj_id)s, LDEV: '
|
||||||
|
'%(ldev)s, LDEV label: %(ldev_label)s)',
|
||||||
|
'suffix': WARNING_SUFFIX,
|
||||||
|
}
|
||||||
STORAGE_COMMAND_FAILED = {
|
STORAGE_COMMAND_FAILED = {
|
||||||
'msg_id': 600,
|
'msg_id': 600,
|
||||||
'loglevel': base_logging.ERROR,
|
'loglevel': base_logging.ERROR,
|
||||||
|
@ -143,6 +143,12 @@ If you use iSCSI:
|
|||||||
1. ``Ports``
|
1. ``Ports``
|
||||||
Assign an IP address and a TCP port number to the port.
|
Assign an IP address and a TCP port number to the port.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
* Do not change LDEV nickname for the LDEVs created by Hitachi block
|
||||||
|
storage driver. The nickname is referred when deleting a volume or
|
||||||
|
a snapshot, to avoid data-loss risk. See details in `bug #2072317`_.
|
||||||
|
|
||||||
Set up Hitachi storage volume driver and volume operations
|
Set up Hitachi storage volume driver and volume operations
|
||||||
----------------------------------------------------------
|
----------------------------------------------------------
|
||||||
|
|
||||||
@ -508,3 +514,5 @@ attach operations for each volume type.
|
|||||||
https://docs.hitachivantara.com/r/en-us/svos/9.8.7/mk-97hm85026/
|
https://docs.hitachivantara.com/r/en-us/svos/9.8.7/mk-97hm85026/
|
||||||
about-adaptive-data-reduction/capacity-saving/
|
about-adaptive-data-reduction/capacity-saving/
|
||||||
capacity-saving-function-data-deduplication-and-compression
|
capacity-saving-function-data-deduplication-and-compression
|
||||||
|
.. _bug #2072317:
|
||||||
|
https://bugs.launchpad.net/cinder/+bug/2072317
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Hitachi driver `bug #2072317
|
||||||
|
<https://bugs.launchpad.net/cinder/+bug/2072317>`_: Fix potential
|
||||||
|
data-loss due to a network issue during a volume deletion.
|
Loading…
Reference in New Issue
Block a user