From fe0a71b593fe0a89f9093ffb163d6e08f4a0f11d Mon Sep 17 00:00:00 2001 From: imacdonn Date: Thu, 20 Sep 2018 21:49:47 +0000 Subject: [PATCH] ZFSSA iSCSI implement get_manageable_volumes() Implement get_manageable_volumes() to augment existing implementation of volume manage/unmanage. Change-Id: I19d94c13288dd574f6eafaf3fb430c3452961ce5 Closes-Bug: #1793599 --- .../tests/unit/volume/drivers/test_zfssa.py | 60 ++++++++++++ cinder/volume/drivers/zfssa/zfssaiscsi.py | 40 +++++++- cinder/volume/drivers/zfssa/zfssarest.py | 91 ++++++++++++------- ...t-manageable-volumes-eb23a11570c813d7.yaml | 3 + 4 files changed, 159 insertions(+), 35 deletions(-) create mode 100644 releasenotes/notes/zfssa-iscsi-get-manageable-volumes-eb23a11570c813d7.yaml diff --git a/cinder/tests/unit/volume/drivers/test_zfssa.py b/cinder/tests/unit/volume/drivers/test_zfssa.py index 709e25b2d09..1520e7eaede 100644 --- a/cinder/tests/unit/volume/drivers/test_zfssa.py +++ b/cinder/tests/unit/volume/drivers/test_zfssa.py @@ -966,6 +966,66 @@ class TestZFSSAISCSIDriver(test.TestCase): lcfg.zfssa_cache_project, volname) + def test_get_manageable_volumes(self): + lcfg = self.configuration + + self.drv.zfssa.get_all_luns.return_value = [ + {'name': 'volume-11111111-1111-1111-1111-111111111111', + 'size': 111 * units.Gi, + 'cinder_managed': True}, + {'name': 'volume2', + 'size': 222 * units.Gi, + 'cinder_managed': False}, + {'name': 'volume-33333333-3333-3333-3333-333333333333', + 'size': 333 * units.Gi, + 'cinder_managed': True}, + {'name': 'volume4', + 'size': 444 * units.Gi} + ] + + cinder_vols = [{'id': '11111111-1111-1111-1111-111111111111'}] + args = (cinder_vols, None, 1000, 0, ['size'], ['asc']) + + lcfg.zfssa_manage_policy = 'strict' + expected = [ + {'reference': {'source-name': + 'volume-11111111-1111-1111-1111-111111111111'}, + 'size': 111, + 'safe_to_manage': False, + 'reason_not_safe': 'already managed', + 'cinder_id': '11111111-1111-1111-1111-111111111111', + 'extra_info': None}, + {'reference': {'source-name': 'volume2'}, + 'size': 222, + 'safe_to_manage': True, + 'reason_not_safe': None, + 'cinder_id': None, + 'extra_info': None}, + {'reference': {'source-name': + 'volume-33333333-3333-3333-3333-333333333333'}, + 'size': 333, + 'safe_to_manage': False, + 'reason_not_safe': 'managed by another cinder instance?', + 'cinder_id': None, + 'extra_info': None}, + {'reference': {'source-name': 'volume4'}, + 'size': 444, + 'safe_to_manage': False, + 'reason_not_safe': 'cinder_managed schema not present', + 'cinder_id': None, + 'extra_info': None}, + ] + + result = self.drv.get_manageable_volumes(*args) + self.assertEqual(expected, result) + + lcfg.zfssa_manage_policy = 'loose' + expected[3]['safe_to_manage'] = True + expected[3]['reason_not_safe'] = None + + result = self.drv.get_manageable_volumes(*args) + self.assertEqual(expected, result) + @mock.patch.object(iscsi.ZFSSAISCSIDriver, '_get_existing_vol') @mock.patch.object(iscsi.ZFSSAISCSIDriver, '_verify_volume_to_manage') def test_volume_manage(self, _get_existing_vol, _verify_volume_to_manage): diff --git a/cinder/volume/drivers/zfssa/zfssaiscsi.py b/cinder/volume/drivers/zfssa/zfssaiscsi.py index f900c36d1d3..e797c2dca45 100644 --- a/cinder/volume/drivers/zfssa/zfssaiscsi.py +++ b/cinder/volume/drivers/zfssa/zfssaiscsi.py @@ -32,6 +32,7 @@ from cinder.volume import configuration from cinder.volume import driver from cinder.volume.drivers.san import san from cinder.volume.drivers.zfssa import zfssarest +from cinder.volume import utils as volume_utils from cinder.volume import volume_types import taskflow.engines @@ -121,8 +122,10 @@ class ZFSSAISCSIDriver(driver.ISCSIDriver): Volume manage/unmanage support. 1.0.3: Fix multi-connect to enable live-migration (LP#1565051). + 1.0.4: + Implement get_manageable_volumes(). """ - VERSION = '1.0.3' + VERSION = '1.0.4' protocol = 'iSCSI' # ThirdPartySystems wiki page @@ -1088,6 +1091,41 @@ class ZFSSAISCSIDriver(driver.ISCSIDriver): LOG.warning("Volume %s exists but can't be deleted.", cache['share']) + def get_manageable_volumes(self, cinder_volumes, marker, limit, offset, + sort_keys, sort_dirs): + lcfg = self.configuration + manageable_volumes = [] + managed_vols = [vol['id'] for vol in cinder_volumes] + + for lun in self.zfssa.get_all_luns(lcfg.zfssa_pool, + lcfg.zfssa_project): + lun_info = { + 'reference': {'source-name': lun['name']}, + 'size': int(math.ceil(float(lun['size']) / units.Gi)), + 'cinder_id': None, + 'extra_info': None, + 'safe_to_manage': True, + 'reason_not_safe': None, + } + if ('cinder_managed' not in lun and + lcfg.zfssa_manage_policy != 'loose'): + lun_info['safe_to_manage'] = False + lun_info['reason_not_safe'] = 'cinder_managed schema ' \ + 'not present' + elif lun.get('cinder_managed', False): + lun_info['safe_to_manage'] = False + vol_id = volume_utils.extract_id_from_volume_name(lun['name']) + if vol_id in managed_vols: + lun_info['reason_not_safe'] = 'already managed' + lun_info['cinder_id'] = vol_id + else: + lun_info['reason_not_safe'] = \ + 'managed by another cinder instance?' + manageable_volumes.append(lun_info) + + return volume_utils.paginate_entries_list( + manageable_volumes, marker, limit, offset, sort_keys, sort_dirs) + def manage_existing(self, volume, existing_ref): """Manage an existing volume in the ZFSSA backend. diff --git a/cinder/volume/drivers/zfssa/zfssarest.py b/cinder/volume/drivers/zfssa/zfssarest.py index f62f234526d..f52fdde08c9 100644 --- a/cinder/volume/drivers/zfssa/zfssarest.py +++ b/cinder/volume/drivers/zfssa/zfssarest.py @@ -754,6 +754,62 @@ class ZFSSAApi(object): val = json.loads(ret.data) return val + def _canonify_lun_info(self, lun): + def _listify(item): + return item if isinstance(item, list) else [item] + + if 'com.sun.ms.vss.hg.maskAll' in lun['initiatorgroup']: + # Hide special maskAll value when LUN is not currently presented + # to any initiatorgroups: + initiatorgroup = [] + number = [] + else: + # For backward-compatibility with 2013.1.2.x, convert + # initiatorgroup and number to lists if they're not already + initiatorgroup = _listify(lun['initiatorgroup']) + number = _listify(lun['assignednumber']) + + canonical = { + 'name': lun['name'], + 'guid': lun['lunguid'], + 'number': number, + 'initiatorgroup': initiatorgroup, + 'size': lun['volsize'], + 'nodestroy': lun['nodestroy'], + 'targetgroup': lun['targetgroup'] + } + if 'origin' in lun: + canonical['origin'] = lun['origin'] + for custom in ['image_id', 'updated_at', 'cinder_managed']: + custom_value = lun.get('custom:' + custom) + if custom_value: + canonical[custom] = custom_value + + return canonical + + def get_all_luns(self, pool, project): + """return all luns in project.""" + svc = '/api/storage/v1/pools/' + pool + '/projects/' + \ + project + "/luns" + ret = self.rclient.get(svc) + if ret.status != restclient.Status.OK: + exception_msg = (_('Error Getting LUNs on ' + 'Pool: %(pool)s ' + 'Project: %(project)s ' + 'Return code: %(ret.status)d ' + 'Message: %(ret.data)s.') + % {'pool': pool, + 'project': project, + 'ret.status': ret.status, + 'ret.data': ret.data}) + LOG.error(exception_msg) + raise exception.VolumeBackendAPIException(data=exception_msg) + + canonical = [] + for lun in json.loads(ret.data)['luns']: + canonical.append(self._canonify_lun_info(lun)) + return canonical + def get_lun(self, pool, project, lun): """return iscsi lun properties.""" svc = '/api/storage/v1/pools/' + pool + '/projects/' + \ @@ -785,40 +841,7 @@ class ZFSSAApi(object): LOG.error(exception_msg) raise exception.VolumeBackendAPIException(data=exception_msg) - val = json.loads(ret.data) - - # For backward-compatibility with 2013.1.2.x, convert initiatorgroup - # and number to lists if they're not already - def _listify(item): - return item if isinstance(item, list) else [item] - - initiatorgroup = _listify(val['lun']['initiatorgroup']) - number = _listify(val['lun']['assignednumber']) - - # Hide special maskAll value when LUN is not currently presented to - # any initiatorgroups: - if 'com.sun.ms.vss.hg.maskAll' in initiatorgroup: - initiatorgroup = [] - number = [] - - ret = { - 'name': val['lun']['name'], - 'guid': val['lun']['lunguid'], - 'number': number, - 'initiatorgroup': initiatorgroup, - 'size': val['lun']['volsize'], - 'nodestroy': val['lun']['nodestroy'], - 'targetgroup': val['lun']['targetgroup'] - } - if 'origin' in val['lun']: - ret.update({'origin': val['lun']['origin']}) - if 'custom:image_id' in val['lun']: - ret.update({'image_id': val['lun']['custom:image_id']}) - ret.update({'updated_at': val['lun']['custom:updated_at']}) - if 'custom:cinder_managed' in val['lun']: - ret.update({'cinder_managed': val['lun']['custom:cinder_managed']}) - - return ret + return self._canonify_lun_info(json.loads(ret.data)['lun']) def get_lun_snapshot(self, pool, project, lun, snapshot): """Return iscsi lun snapshot properties.""" diff --git a/releasenotes/notes/zfssa-iscsi-get-manageable-volumes-eb23a11570c813d7.yaml b/releasenotes/notes/zfssa-iscsi-get-manageable-volumes-eb23a11570c813d7.yaml new file mode 100644 index 00000000000..b516bf95f5f --- /dev/null +++ b/releasenotes/notes/zfssa-iscsi-get-manageable-volumes-eb23a11570c813d7.yaml @@ -0,0 +1,3 @@ +--- +features: + - Oracle ZFSSA iSCSI volume driver implements ``get_manageable_volumes()``