From 2e168341dac6a21a6e421ee9b111d3b27e944f47 Mon Sep 17 00:00:00 2001 From: Victor Rodionov Date: Sat, 24 Aug 2013 01:59:27 +0400 Subject: [PATCH] copy_image_to_volume for Nexenta volume driver This patch implements copy_image_to_volume in Nexenta volume driver, because volume must be exported before copying image to it. Change-Id: I9dc725095944d166fee415f3f605268c7fcbbd3a --- cinder/tests/test_nexenta.py | 40 ++++- cinder/volume/drivers/nexenta/volume.py | 188 +++++++++++++++++------- 2 files changed, 167 insertions(+), 61 deletions(-) diff --git a/cinder/tests/test_nexenta.py b/cinder/tests/test_nexenta.py index 208a45da6..9631319a9 100644 --- a/cinder/tests/test_nexenta.py +++ b/cinder/tests/test_nexenta.py @@ -91,6 +91,18 @@ class TestNexentaDriver(test.TestCase): def test_create_volume(self): self.nms_mock.zvol.create('cinder/volume1', '1G', '8K', True) + self.nms_mock.stmf.list_targets() + self.nms_mock.iscsitarget.create_target({'target_name': 'iqn:volume1'}) + self.nms_mock.stmf.list_targetgroups() + self.nms_mock.stmf.create_targetgroup('cinder/volume1') + self.nms_mock.stmf.list_targetgroup_members('cinder/volume1') + self.nms_mock.stmf.add_targetgroup_member('cinder/volume1', + 'iqn:volume1') + self.nms_mock.scsidisk.lu_exists('cinder/volume1') + self.nms_mock.scsidisk.create_lu('cinder/volume1', {}) + self.nms_mock.scsidisk.lu_shared('cinder/volume1') + self.nms_mock.scsidisk.add_lun_mapping_entry( + 'cinder/volume1', {'target_group': 'cinder/volume1', 'lun': '0'}) self.mox.ReplayAll() self.drv.create_volume(self.TEST_VOLUME_REF) @@ -138,32 +150,43 @@ class TestNexentaDriver(test.TestCase): self.drv.delete_snapshot(self.TEST_SNAPSHOT_REF) _CREATE_EXPORT_METHODS = [ + ('stmf', 'list_targets', tuple(), [], False, ), ('iscsitarget', 'create_target', ({'target_name': 'iqn:volume1'},), u'Unable to create iscsi target\n' u' iSCSI target iqn.1986-03.com.sun:02:cinder-volume1 already' u' configured\n' - u' itadm create-target failed with error 17\n', ), + u' itadm create-target failed with error 17\n', True, ), + ('stmf', 'list_targetgroups', tuple(), [], False, ), ('stmf', 'create_targetgroup', ('cinder/volume1',), u'Unable to create targetgroup: stmfadm: cinder/volume1:' - u' already exists\n', ), + u' already exists\n', True, ), + ('stmf', 'list_targetgroup_members', ('cinder/volume1', ), [], + False, ), ('stmf', 'add_targetgroup_member', ('cinder/volume1', 'iqn:volume1'), u'Unable to add member to targetgroup: stmfadm:' - u' iqn.1986-03.com.sun:02:cinder-volume1: already exists\n', ), + u' iqn.1986-03.com.sun:02:cinder-volume1: already exists\n', + True, ), + ('scsidisk', 'lu_exists', ('cinder/volume1', ), 0, False, ), ('scsidisk', 'create_lu', ('cinder/volume1', {}), u"Unable to create lu with zvol 'cinder/volume1':\n" - u" sbdadm: filename /dev/zvol/rdsk/cinder/volume1: in use\n", ), + u" sbdadm: filename /dev/zvol/rdsk/cinder/volume1: in use\n", + True, ), + ('scsidisk', 'lu_shared', ('cinder/volume1', ), 0, False, ), ('scsidisk', 'add_lun_mapping_entry', ('cinder/volume1', { 'target_group': 'cinder/volume1', 'lun': '0'}), u"Unable to add view to zvol 'cinder/volume1' (LUNs in use: ):\n" - u" stmfadm: view entry exists\n", ), + u" stmfadm: view entry exists\n", True, ), ] - def _stub_export_method(self, module, method, args, error, fail=False): + def _stub_export_method(self, module, method, args, error, raise_exception, + fail=False): m = getattr(self.nms_mock, module) m = getattr(m, method) mock = m(*args) - if fail: + if raise_exception and fail: mock.AndRaise(nexenta.NexentaException(error)) + else: + mock.AndReturn(error) def _stub_all_export_methods(self, fail=False): for params in self._CREATE_EXPORT_METHODS: @@ -195,7 +218,8 @@ class TestNexentaDriver(test.TestCase): return _test_create_export_fail for i in range(len(_CREATE_EXPORT_METHODS)): - locals()['test_create_export_fail_%d' % i] = __get_test(i) + if i % 2: + locals()['test_create_export_fail_%d' % i] = __get_test(i) def test_ensure_export(self): self._stub_all_export_methods(fail=True) diff --git a/cinder/volume/drivers/nexenta/volume.py b/cinder/volume/drivers/nexenta/volume.py index 586856fe1..219df2647 100644 --- a/cinder/volume/drivers/nexenta/volume.py +++ b/cinder/volume/drivers/nexenta/volume.py @@ -101,12 +101,14 @@ class NexentaDriver(driver.ISCSIDriver): # pylint: disable=R0921 """Create a zvol on appliance. :param volume: volume reference + :return: model update dict for volume reference """ self.nms.zvol.create( self._get_zvol_name(volume['name']), '%sG' % (volume['size'],), self.configuration.nexenta_blocksize, self.configuration.nexenta_sparse) + return self.create_export(None, volume) def extend_volume(self, volume, new_size): """Extend an existing volume. @@ -220,70 +222,150 @@ class NexentaDriver(driver.ISCSIDriver): # pylint: disable=R0921 """ raise NotImplementedError + def _target_exists(self, target): + """Check if iSCSI target exist. + + :param target: target name + :return: True if target exist, else False + """ + targets = self.nms.stmf.list_targets() + if not targets: + return False + return target in self.nms.stmf.list_targets() + + def _target_group_exists(self, target_group): + """Check if target group exist. + + :param target_group: target group + :return: True if target group exist, else False + """ + groups = self.nms.stmf.list_targetgroups() + if not groups: + return False + return target_group in groups + + def _target_member_in_target_group(self, target_group, target_member): + """Check if target member in target group. + + :param target_group: target group + :param target_member: target member + :return: True if target member in target group, else False + :raises: NexentaException if target group doesn't exist + """ + members = self.nms.stmf.list_targetgroup_members(target_group) + if not members: + return False + return target_member in members + + def _lu_exists(self, zvol_name): + """Check if LU exists on appliance. + + :param zvol_name: Zvol name + :raises: NexentaException if zvol not exists + :return: True if LU exists, else False + """ + return bool(self.nms.scsidisk.lu_exists(zvol_name)) + + def _is_lu_shared(self, zvol_name): + """Check if LU exists on appliance and shared. + + :param zvol_name: Zvol name + :raises: NexentaException if Zvol not exist + :return: True if LU exists and shared, else False + """ + try: + shared = self.nms.scsidisk.lu_shared(zvol_name) > 0 + except nexenta.NexentaException as exc: + if 'does not exist for zvol' not in exc.args[0]: + raise # Zvol does not exists + shared = False # LU does not exist + return shared + + def _is_volume_exported(self, volume): + """Check if volume exported. + + :param volume: volume object + :return: True if volume exported, else False + """ + zvol_name = self._get_zvol_name(volume['name']) + target_name = self._get_target_name(volume['name']) + target_group_name = self._get_target_group_name(volume['name']) + return (self._target_exists(target_name) and + self._target_group_exists(target_group_name) and + self._target_member_in_target_group(target_group_name, + target_name) and + self._lu_exists(zvol_name) and + self._is_lu_shared(zvol_name)) + + def _get_provider_location(self, volume): + """Returns volume iscsiadm-formatted provider location string.""" + return '%(host)s:%(port)s,1 %(name)s 0' % { + 'host': self.configuration.nexenta_host, + 'port': self.configuration.nexenta_iscsi_target_portal_port, + 'name': self._get_target_name(volume['name']) + } + def _do_export(self, _ctx, volume, ensure=False): """Do all steps to get zvol exported as LUN 0 at separate target. :param volume: reference of volume to be exported :param ensure: if True, ignore errors caused by already existing resources - :return: iscsiadm-formatted provider location string """ zvol_name = self._get_zvol_name(volume['name']) target_name = self._get_target_name(volume['name']) target_group_name = self._get_target_group_name(volume['name']) - target_already_configured = False - try: - self.nms.iscsitarget.create_target({'target_name': target_name}) - except nexenta.NexentaException as exc: - if ensure and 'already configured' in exc.args[0]: - target_already_configured = True - LOG.info(_('Ignored target creation error "%s" while ensuring ' + if not self._target_exists(target_name): + try: + self.nms.iscsitarget.create_target({ + 'target_name': target_name}) + except nexenta.NexentaException as exc: + if ensure and 'already configured' in exc.args[0]: + LOG.info(_('Ignored target creation error "%s" while ' + 'ensuring export'), exc) + else: + raise + if not self._target_group_exists(target_group_name): + try: + self.nms.stmf.create_targetgroup(target_group_name) + except nexenta.NexentaException as exc: + if ((ensure and 'already exists' in exc.args[0]) or + 'target must be offline' in exc.args[0]): + LOG.info(_('Ignored target group creation error "%s" ' + 'while ensuring export'), exc) + else: + raise + if not self._target_member_in_target_group(target_group_name, + target_name): + try: + self.nms.stmf.add_targetgroup_member(target_group_name, + target_name) + except nexenta.NexentaException as exc: + if ((ensure and 'already exists' in exc.args[0]) or + 'target must be offline' in exc.args[0]): + LOG.info(_('Ignored target group member addition error ' + '"%s" while ensuring export'), exc) + else: + raise + if not self._lu_exists(zvol_name): + try: + self.nms.scsidisk.create_lu(zvol_name, {}) + except nexenta.NexentaException as exc: + if not ensure or 'in use' not in exc.args[0]: + raise + LOG.info(_('Ignored LU creation error "%s" while ensuring ' 'export'), exc) - else: - raise - try: - self.nms.stmf.create_targetgroup(target_group_name) - except nexenta.NexentaException as exc: - if ((ensure and 'already exists' in exc.args[0]) or - (target_already_configured and - 'target must be offline' in exc.args[0])): - LOG.info(_('Ignored target group creation error "%s" while ' - 'ensuring export'), exc) - else: - raise - try: - self.nms.stmf.add_targetgroup_member(target_group_name, - target_name) - except nexenta.NexentaException as exc: - if ensure and ('already exists' in exc.args[0] or - 'target must be offline' in exc.args[0]): - LOG.info(_('Ignored target group member addition error "%s" ' + if not self._is_lu_shared(zvol_name): + try: + self.nms.scsidisk.add_lun_mapping_entry(zvol_name, { + 'target_group': target_group_name, + 'lun': '0'}) + except nexenta.NexentaException as exc: + if not ensure or 'view entry exists' not in exc.args[0]: + raise + LOG.info(_('Ignored LUN mapping entry addition error "%s" ' 'while ensuring export'), exc) - else: - raise - try: - self.nms.scsidisk.create_lu(zvol_name, {}) - except nexenta.NexentaException as exc: - if not ensure or 'in use' not in exc.args[0]: - raise - LOG.info(_('Ignored LU creation error "%s" while ensuring export'), - exc) - try: - self.nms.scsidisk.add_lun_mapping_entry(zvol_name, { - 'target_group': target_group_name, - 'lun': '0' - }) - except nexenta.NexentaException as exc: - if not ensure or 'view entry exists' not in exc.args[0]: - raise - LOG.info(_('Ignored LUN mapping entry addition error "%s" while ' - 'ensuring export'), exc) - return '%(host)s:%(port)s,1 %(name)s 0' % { - 'host': self.configuration.nexenta_host, - 'port': self.configuration.nexenta_iscsi_target_portal_port, - 'name': target_name - } def create_export(self, _ctx, volume): """Create new export for zvol. @@ -291,8 +373,8 @@ class NexentaDriver(driver.ISCSIDriver): # pylint: disable=R0921 :param volume: reference of volume to be exported :return: iscsiadm-formatted provider location string """ - loc = self._do_export(_ctx, volume, ensure=False) - return {'provider_location': loc} + self._do_export(_ctx, volume, ensure=False) + return {'provider_location': self._get_provider_location(volume)} def ensure_export(self, _ctx, volume): """Recreate parts of export if necessary. -- 2.45.2