Skip to content

Commit

Permalink
Add minimum features in HDS driver (for Havana & Icehouse)
Browse files Browse the repository at this point in the history
* 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 rnirmal#1 and #2 above.

Change-Id: I6a88164ee0a427519c7fab6f6a852d46ec46176c
Fixes: bug #1207560
  • Loading branch information
Lakhinder Walia committed Aug 7, 2013
1 parent a942b9f commit 758a4be
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 38 deletions.
28 changes: 27 additions & 1 deletion cinder/tests/test_hds.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -103,6 +104,11 @@ def create_lu(self, cmd, ver, ip0, ip1, user, pw, id, hdp, start,
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:
Expand Down Expand Up @@ -132,7 +138,7 @@ def add_iscsi_conn(self, cmd, ver, ip0, ip1, user, pw, id, lun, ctl, port,
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):
Expand Down Expand Up @@ -165,6 +171,7 @@ def setUp(self):
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())
Expand Down Expand Up @@ -209,6 +216,13 @@ def test_delete_volume(self):
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')
Expand All @@ -221,6 +235,18 @@ def test_create_snapshot(self):
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).
Expand Down
127 changes: 92 additions & 35 deletions cinder/volume/drivers/hds/hds.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
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__)

Expand All @@ -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")
Expand Down Expand Up @@ -315,20 +327,68 @@ def create_volume(self, volume):
'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,
Expand All @@ -339,7 +399,7 @@ def delete_volume(self, volume):
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."""
Expand All @@ -350,16 +410,19 @@ def initialize_connection(self, volume, connector):
"""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,
self.config['mgmt_ip0'],
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
Expand All @@ -377,27 +440,28 @@ def initialize_connection(self, volume, connector):
@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}

@utils.synchronized('hds_hus', external=True)
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'],
Expand All @@ -406,7 +470,7 @@ def create_volume_from_snapshot(self, volume, snapshot):
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]
Expand All @@ -421,20 +485,20 @@ def create_snapshot(self, snapshot):
"""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}
Expand All @@ -446,20 +510,13 @@ def delete_snapshot(self, snapshot):
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

Expand Down
20 changes: 18 additions & 2 deletions cinder/volume/drivers/hds/hus_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def delete_lu(self, cmd, ver, ip0, ip1, user, pw, id, lun):
'--delete-lun', '1',
'--array-id', id,
'--lun', lun,
'--force', 1,
check_exit_code=True)
LOG.debug('delete_lu: ' + out + ' -- ' + err)
return out
Expand All @@ -115,6 +116,21 @@ def create_dup(self, cmd, ver, ip0, ip1, user, pw, id, src_lun,
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,
Expand All @@ -135,7 +151,7 @@ def add_iscsi_conn(self, cmd, ver, ip0, ip1, user, pw, id, lun, ctl, port,
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,
Expand All @@ -149,7 +165,7 @@ def del_iscsi_conn(self, cmd, ver, ip0, ip1, user, pw, id, lun, ctl, port,
'--port', port,
'--target', iqn,
'--initiator', initiator,
'--force', force,
'--force', 1,
check_exit_code=True)
LOG.debug('del_iscsi_conn: ' + out + ' -- ' + err)
return out

0 comments on commit 758a4be

Please sign in to comment.