diff --git a/hpedockerplugin/hpe/hpe_3par_common.py b/hpedockerplugin/hpe/hpe_3par_common.py index 255e9d2b..002bf000 100644 --- a/hpedockerplugin/hpe/hpe_3par_common.py +++ b/hpedockerplugin/hpe/hpe_3par_common.py @@ -20,7 +20,6 @@ from oslo_utils import importutils from oslo_config import cfg from oslo_log import log as logging -from oslo_service import loopingcall from oslo_utils import units from hpedockerplugin import exception @@ -1336,8 +1335,8 @@ def create_cloned_volume(self, dst_volume, src_vref): optional = {'priority': 1} self.client.copyVolume(src_3par_vol_name, - dst_3par_vol_name, None, - optional=optional) + dst_3par_vol_name, None, + optional=optional) comments = {'volume_id': dst_volume['id'], 'name': dst_volume['name'], diff --git a/hpedockerplugin/hpe/volume.py b/hpedockerplugin/hpe/volume.py index 2c2a8bd4..8500aa4d 100644 --- a/hpedockerplugin/hpe/volume.py +++ b/hpedockerplugin/hpe/volume.py @@ -1,4 +1,5 @@ import uuid +from hpedockerplugin.hpe import utils DEFAULT_SIZE = 100 DEFAULT_PROV = "thin" @@ -25,6 +26,8 @@ def createvol(name, size=DEFAULT_SIZE, prov=DEFAULT_PROV, volume = {} volume['id'] = str(uuid.uuid4()) volume['name'] = volume['id'] + volume['3par_vol_name'] = utils.get_3par_name(volume['id'], + is_snap) volume['host'] = '' volume['size'] = size volume['availability_zone'] = '' diff --git a/hpedockerplugin/volume_manager.py b/hpedockerplugin/volume_manager.py index 01ced677..a777bce3 100644 --- a/hpedockerplugin/volume_manager.py +++ b/hpedockerplugin/volume_manager.py @@ -242,8 +242,10 @@ def create_volume(self, volname, vol_size, vol_prov, vol_flash, compression_val, vol_qos, mount_conflict_delay, False, cpg, snap_cpg, False, current_backend) + + bkend_vol_name = "" try: - self._create_volume(vol, undo_steps) + bkend_vol_name = self._create_volume(vol, undo_steps) self._apply_volume_specs(vol, undo_steps) if rcg_name: # bkend_rcg_name = self._get_3par_rcg_name(rcg_name) @@ -260,6 +262,7 @@ def create_volume(self, volname, vol_size, vol_prov, # This will make get_vol_byname more efficient vol['fsOwner'] = fs_owner vol['fsMode'] = fs_mode + vol['3par_vol_name'] = bkend_vol_name self._etcd.save_vol(vol) except Exception as ex: @@ -508,14 +511,14 @@ def clone_volume(self, src_vol_name, clone_name, # add prefix '*' because offline copy task name have pattern like # e.g. dcv-m0o5ZAwPReaZVoymnLTrMA->dcv-N.9ikeA.RiaxPP4LzecaEQ # this will check both offline as well as online copy task - if self._hpeplugin_driver.is_vol_having_active_task("*%s" % volume_3par): + if self._hpeplugin_driver.is_vol_having_active_task( + "*%s" % volume_3par): msg = 'source volume: %s / %s is having some active task ' \ 'running on array' % (src_vol_name, volume_3par) LOG.debug(msg) response = json.dumps({u"Err": msg}) return response - if not size: size = src_vol['size'] if not cpg: @@ -602,13 +605,13 @@ def _create_snapshot(self, src_vol_name, schedName, snapshot_name, # add prefix '*' because offline copy task name have pattern like # e.g. dcv-m0o5ZAwPReaZVoymnLTrMA->dcv-N.9ikeA.RiaxPP4LzecaEQ # this will check both offline as well as online copy task - if self._hpeplugin_driver.is_vol_having_active_task("*%s" % volume_3par): + if self._hpeplugin_driver.is_vol_having_active_task( + "*%s" % volume_3par): msg = 'source volume: %s / %s is having some active task ' \ 'running on array' % (src_vol_name, volume_3par) LOG.debug(msg) response = json.dumps({u"Err": msg}) return response - # Check if this is an old volume type. If yes, add is_snap flag to it if 'is_snap' not in vol: @@ -677,6 +680,7 @@ def _create_snapshot(self, src_vol_name, schedName, snapshot_name, 'display_description': 'snapshot of volume %s' % src_vol_name} undo_steps = [] + bkend_snap_name = "" try: bkend_snap_name = self._hpeplugin_driver.create_snapshot( snapshot) @@ -714,9 +718,12 @@ def _create_snapshot(self, src_vol_name, schedName, snapshot_name, vol['snapshots'].append(db_snapshot) snap_vol['snap_metadata'] = db_snapshot snap_vol['backend'] = current_backend + snap_vol['3par_vol_name'] = bkend_snap_name try: - self._create_snapshot_record(snap_vol, snapshot_name, undo_steps) + self._create_snapshot_record(snap_vol, + snapshot_name, + undo_steps) # For now just track volume to uuid mapping internally # TODO: Save volume name and uuid mapping in etcd as well @@ -853,7 +860,9 @@ def _clone_volume(self, clone_name, src_vol, size, cpg, False, cpg, snap_cpg, False, current_backend) try: - self.__clone_volume__(src_vol, clone_vol, undo_steps) + bkend_clone_name = self.__clone_volume__(src_vol, + clone_vol, + undo_steps) self._apply_volume_specs(clone_vol, undo_steps) # For now just track volume to uuid mapping internally # TODO: Save volume name and uuid mapping in etcd as well @@ -861,6 +870,7 @@ def _clone_volume(self, clone_name, src_vol, size, cpg, clone_vol['fsOwner'] = src_vol.get('fsOwner') clone_vol['fsMode'] = src_vol.get('fsMode') clone_vol['backend'] = src_vol.get('backend') + clone_vol['3par_vol_name'] = bkend_clone_name self._etcd.save_vol(clone_vol) except Exception as ex: @@ -940,6 +950,14 @@ def _get_snapshot_response(self, snapinfo, snapname): if 'snap_schedule' in metadata: snap_detail['snap_schedule'] = metadata['snap_schedule'] + LOG.info('_get_snapshot_response: adding 3par vol info') + + if '3par_vol_name' in snapinfo: + snap_detail['3par_vol_name'] = snapinfo.get('3par_vol_name') + else: + snap_detail['3par_vol_name'] = utils.get_3par_name(snapinfo['id'], + True) + snapshot['Status'].update({'snap_detail': snap_detail}) response = json.dumps({u"Err": err, u"Volume": snapshot}) @@ -1076,6 +1094,15 @@ def get_volume_snap_details(self, volname, snapname, qualified_name): 'mount_conflict_delay') vol_detail['cpg'] = volinfo.get('cpg') vol_detail['snap_cpg'] = volinfo.get('snap_cpg') + + LOG.info(' get_volume_snap_details : adding 3par vol info') + if '3par_vol_name' in volinfo: + vol_detail['3par_vol_name'] = volinfo['3par_vol_name'] + else: + vol_detail['3par_vol_name'] = \ + utils.get_3par_name(volinfo['id'], + False) + if volinfo.get('rcg_info'): vol_detail['secondary_cpg'] = \ self.tgt_bkend_config.hpe3par_cpg[0] diff --git a/test/clonevolume_tester.py b/test/clonevolume_tester.py index ad771ff0..640bd232 100644 --- a/test/clonevolume_tester.py +++ b/test/clonevolume_tester.py @@ -28,6 +28,7 @@ def setup_mock_objects(self): mock_3parclient = self.mock_objects['mock_3parclient'] mock_3parclient.copyVolume.return_value = {'taskid': data.TASK_ID} + mock_3parclient.isOnlinePhysicalCopy.return_value = False mock_3parclient.getCPG.return_value = {} @@ -44,6 +45,7 @@ def setup_mock_objects(self): mock_3parclient = self.mock_objects['mock_3parclient'] mock_3parclient.copyVolume.return_value = {'taskid': data.TASK_ID} + mock_3parclient.isOnlinePhysicalCopy.return_value = False mock_3parclient.getCPG.return_value = {} def check_response(self, resp): @@ -64,7 +66,6 @@ def check_response(self, resp): mock_3parclient = self.mock_objects['mock_3parclient'] mock_3parclient.createVolume.assert_called() mock_3parclient.copyVolume.assert_called() - mock_3parclient.getTask.assert_called() mock_3parclient.modifyVolume.assert_called() def get_request_params(self): @@ -80,42 +81,11 @@ def setup_mock_objects(self): mock_3parclient = self.mock_objects['mock_3parclient'] mock_3parclient.copyVolume.return_value = {'taskid': data.TASK_ID} + mock_3parclient.isOnlinePhysicalCopy.return_value = False mock_3parclient.getCPG.return_value = {} mock_3parclient.getTask.return_value = {'status': data.TASK_DONE} -# Make copyVolume operation fail -class TestCloneOfflineCopyFails(CloneVolumeUnitTest): - def check_response(self, resp): - # Match error substring with returned error string - err_received = resp['Err'] - err_expected = 'copy volume task failed: create_cloned_volume' - self._test_case.assertIn(err_expected, err_received) - - # Check following 3PAR APIs were invoked - mock_3parclient = self.mock_objects['mock_3parclient'] - mock_3parclient.createVolume.assert_called() - mock_3parclient.copyVolume.assert_called() - mock_3parclient.getTask.assert_called() - - def get_request_params(self): - return {"Name": "clone-vol-001", - "Opts": {"cloneOf": data.VOLUME_NAME, - # Difference in size of source and cloned volume - # triggers offline copy. Src size is 2. - "size": 20}} - - def setup_mock_objects(self): - mock_etcd = self.mock_objects['mock_etcd'] - mock_etcd.get_vol_byname.return_value = data.volume - - mock_3parclient = self.mock_objects['mock_3parclient'] - mock_3parclient.getCPG.return_value = {} - mock_3parclient.copyVolume.return_value = {'taskid': data.TASK_ID} - # TASK_FAILED simulates failure of copyVolume() operation - mock_3parclient.getTask.return_value = {'status': data.TASK_FAILED} - - class TestCloneInvalidSourceVolume(CloneVolumeUnitTest): def check_response(self, resp): expected_msg = "source volume: %s does not exist" % data.VOLUME_NAME @@ -147,6 +117,8 @@ def setup_mock_objects(self): mock_etcd = self.mock_objects['mock_etcd'] # Source volume that is to be cloned mock_etcd.get_vol_byname.return_value = data.volume + mock_3parclient = self.mock_objects['mock_3parclient'] + mock_3parclient.isOnlinePhysicalCopy.return_value = False def check_response(self, resp): expected_msg = "clone volume size 1 is less than source volume size 2" @@ -174,6 +146,7 @@ def setup_mock_objects(self): mock_3parclient = self.mock_objects['mock_3parclient'] mock_3parclient.copyVolume.return_value = {'taskid': data.TASK_ID} + mock_3parclient.isOnlinePhysicalCopy.return_value = False mock_3parclient.getCPG.return_value = {} @@ -198,6 +171,7 @@ def setup_mock_objects(self): mock_3parclient = self.mock_objects['mock_3parclient'] mock_3parclient.copyVolume.return_value = {'taskid': data.TASK_ID} + mock_3parclient.isOnlinePhysicalCopy.return_value = False mock_3parclient.getCPG.return_value = {} @@ -220,6 +194,7 @@ def setup_mock_objects(self): mock_3parclient = self.mock_objects['mock_3parclient'] mock_3parclient.copyVolume.return_value = {'taskid': data.TASK_ID} + mock_3parclient.isOnlinePhysicalCopy.return_value = False mock_3parclient.getCPG.return_value = {} @@ -251,6 +226,7 @@ def setup_mock_objects(self): mock_3parclient = self.mock_objects['mock_3parclient'] mock_3parclient.copyVolume.return_value = {'taskid': data.TASK_ID} mock_3parclient.getCPG.return_value = {} + mock_3parclient.isOnlinePhysicalCopy.return_value = False # Make addVolumeToVolumeSet fail by throwing exception mock_3parclient.addVolumeToVolumeSet.side_effect = \ [exceptions.HTTPNotFound('fake')] @@ -286,6 +262,7 @@ def setup_mock_objects(self): mock_3parclient = self.mock_objects['mock_3parclient'] mock_3parclient.copyVolume.return_value = {'taskid': data.TASK_ID} + mock_3parclient.isOnlinePhysicalCopy.return_value = False mock_3parclient.getCPG.return_value = {} @@ -315,6 +292,7 @@ def setup_mock_objects(self): mock_3parclient = self.mock_objects['mock_3parclient'] mock_3parclient.copyVolume.return_value = {'taskid': data.TASK_ID} mock_3parclient.getCPG.return_value = {} + mock_3parclient.isOnlinePhysicalCopy.return_value = False # Make addVolumeToVolumeSet fail by throwing exception mock_3parclient.modifyVolumeSet.side_effect = [ exceptions.HTTPInternalServerError("Internal server error") @@ -350,6 +328,7 @@ def setup_mock_objects(self): mock_3parclient = self.mock_objects['mock_3parclient'] mock_3parclient.copyVolume.return_value = {'taskid': data.TASK_ID} + mock_3parclient.isOnlinePhysicalCopy.return_value = False mock_3parclient.getCPG.return_value = {} @@ -381,6 +360,7 @@ def setup_mock_objects(self): mock_3parclient.copyVolume.return_value = {'taskid': data.TASK_ID} mock_3parclient.getCPG.return_value = {} mock_3parclient.getVolumeMetaData.return_value = {'value': True} + mock_3parclient.isOnlinePhysicalCopy.return_value = False mock_3parclient.getTask.return_value = {'status': data.TASK_DONE} @@ -411,5 +391,6 @@ def setup_mock_objects(self): 'revision': 0} mock_3parclient.copyVolume.return_value = {'taskid': data.TASK_ID} mock_3parclient.getCPG.return_value = {} + mock_3parclient.isOnlinePhysicalCopy.return_value = False mock_3parclient.getStorageSystemInfo.return_value = \ {'licenseInfo': {'licenses': [{'name': 'Compression'}]}} diff --git a/test/createsnapshot_tester.py b/test/createsnapshot_tester.py index 12c690bf..d7cfe19f 100644 --- a/test/createsnapshot_tester.py +++ b/test/createsnapshot_tester.py @@ -29,6 +29,8 @@ def setup_mock_objects(self): copy.deepcopy(data.volume), None ] + mock_3parclient = self.mock_objects['mock_3parclient'] + mock_3parclient.isOnlinePhysicalCopy.return_value = False def check_response(self, resp): self._test_case.assertEqual(resp, {u"Err": ''}) @@ -52,6 +54,8 @@ def setup_mock_objects(self): None, copy.deepcopy(data.volume) ] + mock_3parclient = self.mock_objects['mock_3parclient'] + mock_3parclient.isOnlinePhysicalCopy.return_value = False def check_response(self, resp): self._test_case.assertEqual(resp, {u"Err": ''}) @@ -124,6 +128,8 @@ def setup_mock_objects(self): ] mock_etcd.save_vol.side_effect = \ [hpe_exc.HPEPluginSaveFailed(obj='snap-001')] + mock_3parclient = self.mock_objects['mock_3parclient'] + mock_3parclient.isOnlinePhysicalCopy.return_value = False def check_response(self, resp): expected = "ETCD data save failed: snap-001" diff --git a/test/fake_3par_data.py b/test/fake_3par_data.py index 12c613a2..a682db03 100644 --- a/test/fake_3par_data.py +++ b/test/fake_3par_data.py @@ -39,6 +39,7 @@ SNAPSHOT_ID3 = 'f5d9e226-2995-4d66-a5bd-3e373f4ff772' SNAPSHOT_NAME3 = 'snapshot-3' VOLUME_3PAR_NAME = 'dcv-0DM4qZEVSKON-DXN-NwVpw' +SNAPSHOT_3PAR_NAME1 = 'dcs-0DM4qZEVSKON-DXN-NwVpw' SNAPSHOT_3PAR_NAME = 'dcs-L4I73ONuTci9Fd4ceij-MQ' TARGET_IQN = 'iqn.2000-05.com.3pardata:21810002ac00383d' TARGET_LUN = 90 diff --git a/test/getvolume_tester.py b/test/getvolume_tester.py index bdf79359..dd8f05d8 100644 --- a/test/getvolume_tester.py +++ b/test/getvolume_tester.py @@ -50,6 +50,7 @@ def check_response(self, resp): u'vvset_name': u'vvk_vvset' }, u'volume_detail': { + u'3par_vol_name': data.VOLUME_3PAR_NAME, u'compression': None, u'flash_cache': None, u'fsMode': None, @@ -94,6 +95,7 @@ def check_response(self, resp): u'Devicename': u'', u'Status': { u'volume_detail': { + u'3par_vol_name': data.VOLUME_3PAR_NAME, u'compression': None, u'flash_cache': None, u'provisioning': u'dedup', @@ -151,6 +153,7 @@ def setup_mock_objects(self): def check_response(self, resp): snap_detail = { + u'3par_vol_name': data.SNAPSHOT_3PAR_NAME, u'compression': None, u'is_snap': True, u'parent_id': data.VOLUME_ID, diff --git a/test/test_hpe_plugin_v2.py b/test/test_hpe_plugin_v2.py index e42d74b5..402f45bb 100644 --- a/test/test_hpe_plugin_v2.py +++ b/test/test_hpe_plugin_v2.py @@ -218,11 +218,6 @@ def test_clone_offline_copy(self): test = clonevolume_tester.TestCloneOfflineCopy() test.run_test(self) - @tc_banner_decorator - def test_clone_offline_copy_fails(self): - test = clonevolume_tester.TestCloneOfflineCopyFails() - test.run_test(self) - @tc_banner_decorator def test_clone_invalid_source_volume(self): test = clonevolume_tester.TestCloneInvalidSourceVolume()