]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
copy_image_to_volume for Nexenta volume driver
authorVictor Rodionov <vito.ordaz@gmail.com>
Fri, 23 Aug 2013 21:59:27 +0000 (01:59 +0400)
committerVictor Rodionov <vito.ordaz@gmail.com>
Fri, 30 Aug 2013 17:02:32 +0000 (21:02 +0400)
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
cinder/volume/drivers/nexenta/volume.py

index 208a45da614fcb8c296cb0516dec49efcf1e49fb..9631319a9c35429ec8980827c9595910ea220298 100644 (file)
@@ -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)
index 586856fe14a6b5664e25fdcdc016bb42af070bc7..219df2647689000b7da3b5dc40e7a30938e9c707 100644 (file)
@@ -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.