]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Add minimum features in HDS driver (for Havana & Icehouse)
authorLakhinder Walia <lakhinder.walia@hds.com>
Thu, 1 Aug 2013 23:43:45 +0000 (16:43 -0700)
committerLakhinder Walia <lakhinder.walia@hds.com>
Wed, 7 Aug 2013 23:09:58 +0000 (16:09 -0700)
* Add create_cloned_volume() api.
* Add extend_volume() api.
* Reorganized some connection state keeping into _loc_info() function.
  Earlier this logic was spread out in various calls.
* New self tests for #1 and #2 above.

Change-Id: I6a88164ee0a427519c7fab6f6a852d46ec46176c
Fixes: bug #1207560
cinder/tests/test_hds.py
cinder/volume/drivers/hds/hds.py
cinder/volume/drivers/hds/hus_backend.py

index 20365eb8d1b3fd1398ed0229fbf0312c963aa245..7491d60c089e2a81be27c667461bcfcfcb4792ab 100644 (file)
@@ -72,6 +72,7 @@ class SimulatedHusBackend:
     init_index = 0              # initiator index
     target_index = 0            # target index
     hlun = 0                    # hlun index
+    out = ''
 
     def __init__(self):
         self.start_lun = 0
@@ -103,6 +104,11 @@ class SimulatedHusBackend:
         self.start_lun += 1
         return out
 
+    def extend_vol(self, cmd, ver, ip0, ip1, user, pw, id, lu, size):
+        out = ("LUN: %s successfully extended to %s MB" % (lu, size))
+        SimulatedHusBackend.out = out
+        return out
+
     def delete_lu(self, cmd, ver, ip0, ip1, user, pw, id, lun):
         out = ""
         if lun in self.alloc_lun:
@@ -132,7 +138,7 @@ class SimulatedHusBackend:
         return out
 
     def del_iscsi_conn(self, cmd, ver, ip0, ip1, user, pw, id, lun, ctl, port,
-                       iqn, initiator, force):
+                       iqn, initiator):
         conn = ()
         for connection in SimulatedHusBackend.connections:
             if (connection[1] == lun):
@@ -165,6 +171,7 @@ class HUSiSCSIDriverTest(test.TestCase):
         os.close(handle)
         SimulatedHusBackend.alloc_lun = []
         SimulatedHusBackend.connections = []
+        SimulatedHusBackend.out = ''
         self.mox = mox.Mox()
         self.mox.StubOutWithMock(hds, 'factory_bend')
         hds.factory_bend().AndReturn(SimulatedHusBackend())
@@ -209,6 +216,13 @@ class HUSiSCSIDriverTest(test.TestCase):
         num_luns_after = len(SimulatedHusBackend.alloc_lun)
         self.assertTrue(num_luns_before > num_luns_after)
 
+    def test_extend_volume(self):
+        vol = self.test_create_volume()
+        new_size = _VOLUME['size'] * 2
+        self.driver.extend_volume(vol, new_size)
+        self.assertTrue(str(new_size * 1024) in
+                        SimulatedHusBackend.out)
+
     def test_create_snapshot(self):
         vol = self.test_create_volume()
         self.mox.StubOutWithMock(self.driver, '_id_to_vol')
@@ -221,6 +235,18 @@ class HUSiSCSIDriverTest(test.TestCase):
         svol['provider_location'] = loc['provider_location']
         return svol
 
+    def test_create_clone(self):
+        vol = self.test_create_volume()
+        self.mox.StubOutWithMock(self.driver, '_id_to_vol')
+        self.driver._id_to_vol(vol['volume_id']).AndReturn(vol)
+        self.mox.ReplayAll()
+        svol = vol.copy()
+        svol['volume_size'] = svol['size']
+        loc = self.driver.create_snapshot(svol)
+        self.assertNotEqual(loc, None)
+        svol['provider_location'] = loc['provider_location']
+        return svol
+
     def test_delete_snapshot(self):
         """Delete a snapshot (test).
 
index 13efb0bb592bf1ad58b90ccd7d703d114a94a6a1..f26051d6f3f2846ad6acbd1761de3b6eede97ef1 100644 (file)
@@ -29,7 +29,7 @@ from cinder import utils
 from cinder.volume import driver
 from cinder.volume.drivers.hds.hus_backend import HusBackend
 
-HDS_VERSION = '1.0.1'
+HDS_VERSION = '1.0.2'
 
 LOG = logging.getLogger(__name__)
 
@@ -53,6 +53,18 @@ def factory_bend():
     return HusBackend()
 
 
+def _loc_info(loc):
+    """Parse info from location string."""
+    info = {}
+    tup = loc.split(',')
+    if len(tup) < 5:
+        info['id_lu'] = tup[0].split('.')
+        return info
+    info['id_lu'] = tup[2].split('.')
+    info['tgt'] = tup
+    return info
+
+
 def _do_lu_range_check(start, end, maxlun):
     """Validate array allocation range."""
     LOG.debug(_("Range: start LU: %(start)s, end LU: %(end)s")
@@ -315,20 +327,68 @@ class HUSDriver(driver.ISCSIDriver):
                      'sz': sz})
         return {'provider_location': lun}
 
+    @utils.synchronized('hds_hus', external=True)
+    def create_cloned_volume(self, dst, src):
+        """Create a clone of a volume."""
+        if src['size'] != dst['size']:
+            msg = 'clone volume size mismatch'
+            raise exception.VolumeBackendAPIException(data=msg)
+        service = self._get_service(dst)
+        (_ip, _ipp, _ctl, _port, hdp) = service
+        size = int(src['size']) * 1024
+        source_vol = self._id_to_vol(src['id'])
+        (arid, slun) = _loc_info(source_vol['provider_location'])['id_lu']
+        out = self.bend.create_dup(self.config['hus_cmd'],
+                                   HDS_VERSION,
+                                   self.config['mgmt_ip0'],
+                                   self.config['mgmt_ip1'],
+                                   self.config['username'],
+                                   self.config['password'],
+                                   arid, slun,
+                                   hdp,
+                                   self.start, self.end,
+                                   '%s' % (size))
+        lun = self.arid + '.' + out.split()[1]
+        size = int(out.split()[5])
+        LOG.debug(_("LUN %(lun)s of size %(size)s MB is cloned.")
+                  % {'lun': lun,
+                     'size': size})
+        return {'provider_location': lun}
+
+    @utils.synchronized('hds_hus', external=True)
+    def extend_volume(self, volume, new_size):
+        """Extend an existing volume."""
+        (arid, lun) = _loc_info(volume['provider_location'])['id_lu']
+        out = self.bend.extend_vol(self.config['hus_cmd'],
+                                   HDS_VERSION,
+                                   self.config['mgmt_ip0'],
+                                   self.config['mgmt_ip1'],
+                                   self.config['username'],
+                                   self.config['password'],
+                                   arid, lun,
+                                   '%s' % (new_size * 1024))
+        LOG.debug(_("LUN %(lun)s extended to %(size)s GB.")
+                  % {'lun': lun,
+                     'size': new_size})
+
     @utils.synchronized('hds_hus', external=True)
     def delete_volume(self, volume):
         """Delete an LU on HUS."""
-        loc = volume['provider_location']
-        if loc is None:         # to take care of spurious input
-            return              # which could cause exception.
-        (arid, lun) = loc.split('.')
-        myid = self.arid
-        if arid != myid:
-            LOG.error(_("Array Mismatch %(myid)s vs %(arid)s")
-                      % {'myid': myid,
-                         'arid': arid})
-            msg = 'Array id mismatch in volume delete'
-            raise exception.VolumeBackendAPIException(data=msg)
+        prov_loc = volume['provider_location']
+        if prov_loc is None:
+            return
+        info = _loc_info(prov_loc)
+        (arid, lun) = info['id_lu']
+        if 'tgt' in info.keys():  # connected?
+            (_portal, iqn, loc, ctl, port) = info['tgt']
+            _out = self.bend.del_iscsi_conn(self.config['hus_cmd'],
+                                            HDS_VERSION,
+                                            self.config['mgmt_ip0'],
+                                            self.config['mgmt_ip1'],
+                                            self.config['username'],
+                                            self.config['password'],
+                                            arid, lun, ctl, port, iqn,
+                                            '')
         name = self.hus_name
         LOG.debug(_("delete lun %(lun)s on %(name)s")
                   % {'lun': lun,
@@ -339,7 +399,7 @@ class HUSDriver(driver.ISCSIDriver):
                                    self.config['mgmt_ip1'],
                                    self.config['username'],
                                    self.config['password'],
-                                   self.arid, lun)
+                                   arid, lun)
 
     def remove_export(self, context, volume):
         """Disconnect a volume from an attached instance."""
@@ -350,8 +410,11 @@ class HUSDriver(driver.ISCSIDriver):
         """Map the created volume to connector['initiator']."""
         service = self._get_service(volume)
         (ip, ipp, ctl, port, _hdp) = service
-        loc = volume['provider_location']
-        (_array_id, lun) = loc.split('.')
+        info = _loc_info(volume['provider_location'])
+        if 'tgt' in info.keys():  # spurious repeat connection
+            return
+        (arid, lun) = info['id_lu']
+        loc = arid + '.' + lun
         iqn = HI_IQN + connector['host']
         out = self.bend.add_iscsi_conn(self.config['hus_cmd'],
                                        HDS_VERSION,
@@ -359,7 +422,7 @@ class HUSDriver(driver.ISCSIDriver):
                                        self.config['mgmt_ip1'],
                                        self.config['username'],
                                        self.config['password'],
-                                       self.arid, lun, ctl, port, iqn,
+                                       arid, lun, ctl, port, iqn,
                                        connector['initiator'])
         hus_portal = ip + ':' + ipp
         tgt = hus_portal + ',' + iqn + ',' + loc + ',' + ctl + ',' + port
@@ -377,19 +440,20 @@ class HUSDriver(driver.ISCSIDriver):
     @utils.synchronized('hds_hus', external=True)
     def terminate_connection(self, volume, connector, **kwargs):
         """Terminate a connection to a volume."""
-        info = volume['provider_location'].split(',')
-        if len(info) < 5:      # connection not setup properly. bail out
+        info = _loc_info(volume['provider_location'])
+        if 'tgt' not in info.keys():  # spurious disconnection
             return
-        (_portal, iqn, loc, ctl, port) = info
-        (_array_id, lun) = loc.split('.')
+        (arid, lun) = info['id_lu']
+        (_portal, iqn, loc, ctl, port) = info['tgt']
+
         _out = self.bend.del_iscsi_conn(self.config['hus_cmd'],
                                         HDS_VERSION,
                                         self.config['mgmt_ip0'],
                                         self.config['mgmt_ip1'],
                                         self.config['username'],
                                         self.config['password'],
-                                        self.arid, lun, ctl, port, iqn,
-                                        connector['initiator'], 1)
+                                        arid, lun, ctl, port, iqn,
+                                        connector['initiator'])
         self._update_vol_location(volume['id'], loc)
         return {'provider_location': loc}
 
@@ -397,7 +461,7 @@ class HUSDriver(driver.ISCSIDriver):
     def create_volume_from_snapshot(self, volume, snapshot):
         """Create a volume from a snapshot."""
         size = int(snapshot['volume_size']) * 1024
-        (_arid, slun) = snapshot['provider_location'].split('.')
+        (arid, slun) = _loc_info(snapshot['provider_location'])['id_lu']
         service = self._get_service(volume)
         (_ip, _ipp, _ctl, _port, hdp) = service
         out = self.bend.create_dup(self.config['hus_cmd'],
@@ -406,7 +470,7 @@ class HUSDriver(driver.ISCSIDriver):
                                    self.config['mgmt_ip1'],
                                    self.config['username'],
                                    self.config['password'],
-                                   self.arid, slun, hdp,
+                                   arid, slun, hdp,
                                    self.start, self.end,
                                    '%s' % (size))
         lun = self.arid + '.' + out.split()[1]
@@ -421,20 +485,20 @@ class HUSDriver(driver.ISCSIDriver):
         """Create a snapshot."""
         source_vol = self._id_to_vol(snapshot['volume_id'])
         size = int(snapshot['volume_size']) * 1024
-        (_arid, slun) = source_vol['provider_location'].split('.')
+        (arid, slun) = _loc_info(source_vol['provider_location'])['id_lu']
         out = self.bend.create_dup(self.config['hus_cmd'],
                                    HDS_VERSION,
                                    self.config['mgmt_ip0'],
                                    self.config['mgmt_ip1'],
                                    self.config['username'],
                                    self.config['password'],
-                                   self.arid, slun,
+                                   arid, slun,
                                    self.config['snapshot_hdp'],
                                    self.start, self.end,
                                    '%s' % (size))
         lun = self.arid + '.' + out.split()[1]
         size = int(out.split()[5])
-        LOG.debug(_("LUN %(lun)s of size %(size)s MB is created.")
+        LOG.debug(_("LUN %(lun)s of size %(size)s MB is created as snapshot.")
                   % {'lun': lun,
                      'size': size})
         return {'provider_location': lun}
@@ -446,20 +510,13 @@ class HUSDriver(driver.ISCSIDriver):
         if loc is None:         # to take care of spurious input
             return              # which could cause exception.
         (arid, lun) = loc.split('.')
-        myid = self.arid
-        if arid != myid:
-            LOG.error(_('Array mismatch %(myid)s vs %(arid)s')
-                      % {'myid': myid,
-                         'arid': arid})
-            msg = 'Array id mismatch in delete snapshot'
-            raise exception.VolumeBackendAPIException(data=msg)
         _out = self.bend.delete_lu(self.config['hus_cmd'],
                                    HDS_VERSION,
                                    self.config['mgmt_ip0'],
                                    self.config['mgmt_ip1'],
                                    self.config['username'],
                                    self.config['password'],
-                                   self.arid, lun)
+                                   arid, lun)
         LOG.debug(_("LUN %s is deleted.") % lun)
         return
 
index 860e908eefcccc955248e9461a4b86ff6e298609..fd8753d08783e66721841ae9175e6ba18d734e54 100644 (file)
@@ -92,6 +92,7 @@ class HusBackend:
                                  '--delete-lun', '1',
                                  '--array-id', id,
                                  '--lun', lun,
+                                 '--force', 1,
                                  check_exit_code=True)
         LOG.debug('delete_lu: ' + out + ' -- ' + err)
         return out
@@ -115,6 +116,21 @@ class HusBackend:
         LOG.debug('create_dup: ' + out + ' -- ' + err)
         return out
 
+    def extend_vol(self, cmd, ver, ip0, ip1, user, pw, id, lun, new_size):
+        out, err = utils.execute(cmd,
+                                 '--driver-version', ver,
+                                 '--ip0', ip0,
+                                 '--ip1', ip1,
+                                 '--user', user,
+                                 '--password', pw,
+                                 '--extend-lun', '1',
+                                 '--array-id', id,
+                                 '--lun', lun,
+                                 '--size', new_size,
+                                 check_exit_code=True)
+        LOG.debug('extend_vol: ' + out + ' -- ' + err)
+        return out
+
     def add_iscsi_conn(self, cmd, ver, ip0, ip1, user, pw, id, lun, ctl, port,
                        iqn, initiator):
         out, err = utils.execute(cmd,
@@ -135,7 +151,7 @@ class HusBackend:
         return out
 
     def del_iscsi_conn(self, cmd, ver, ip0, ip1, user, pw, id, lun, ctl, port,
-                       iqn, initiator, force):
+                       iqn, initiator):
         out, err = utils.execute(cmd,
                                  '--driver-version', ver,
                                  '--ip0', ip0,
@@ -149,7 +165,7 @@ class HusBackend:
                                  '--port', port,
                                  '--target', iqn,
                                  '--initiator', initiator,
-                                 '--force', force,
+                                 '--force', 1,
                                  check_exit_code=True)
         LOG.debug('del_iscsi_conn: ' + out + ' -- ' + err)
         return out