From 7ad2f80d05fdf6d0b225292030c27e85e66bd254 Mon Sep 17 00:00:00 2001 From: "Walter A. Boring IV" Date: Thu, 30 May 2013 12:05:26 -0700 Subject: [PATCH] Update 3PAR driver session management The 3PAR REST API server has a limited number of active sessions. This change to the driver removes the ability of keeping the REST API session alive for the life of the driver. Now on every driver entry point, which is synchronized, we login to the 3PAR and when the work is complete, we log out of the 3PAR. This has the side affect of more overhead to the 3PAR but helps eliminate failed commands due to maximum sessions reached to the 3PAR. This patch also changes the locking to be the same lock for all driver entry points and adds locking around the volume stats. This patch also migrates all of the client code access to the common class, which simplifies the drivers. Change-Id: Ibcec4cf1781262521ccbdf99c4ba4167634a09c4 --- cinder/tests/test_hp3par.py | 46 ++++----- .../volume/drivers/san/hp/hp_3par_common.py | 97 ++++++++++++++----- cinder/volume/drivers/san/hp/hp_3par_fc.py | 95 ++++++++---------- cinder/volume/drivers/san/hp/hp_3par_iscsi.py | 96 ++++++++---------- 4 files changed, 175 insertions(+), 159 deletions(-) diff --git a/cinder/tests/test_hp3par.py b/cinder/tests/test_hp3par.py index 1fa0b3efe..2b90fd143 100644 --- a/cinder/tests/test_hp3par.py +++ b/cinder/tests/test_hp3par.py @@ -350,10 +350,10 @@ class HP3PARBaseDriver(): 'desc': "HOST '%s' was not found" % hostname} raise hpexceptions.HTTPNotFound(msg) else: - self._hosts[hostname] = None + del self._hosts[hostname] def fake_create_3par_vlun(self, volume, hostname): - self.driver.client.createVLUN(volume, 19, hostname) + self.driver.common.client.createVLUN(volume, 19, hostname) def fake_get_ports(self): return {'FC': self.FAKE_FC_PORTS, 'iSCSI': self.FAKE_ISCSI_PORTS} @@ -368,7 +368,7 @@ class HP3PARBaseDriver(): self.flags(lock_path=self.tempdir) self.driver.delete_volume(self.volume) self.assertRaises(hpexceptions.HTTPNotFound, - self.driver.client.getVolume, + self.driver.common.client.getVolume, self.VOLUME_ID) def test_create_snapshot(self): @@ -376,7 +376,7 @@ class HP3PARBaseDriver(): self.driver.create_snapshot(self.snapshot) # check to see if the snapshot was created - snap_vol = self.driver.client.getVolume(self.SNAPSHOT_3PAR_NAME) + snap_vol = self.driver.common.client.getVolume(self.SNAPSHOT_3PAR_NAME) self.assertEqual(snap_vol['name'], self.SNAPSHOT_3PAR_NAME) def test_delete_snapshot(self): @@ -384,20 +384,20 @@ class HP3PARBaseDriver(): self.driver.create_snapshot(self.snapshot) #make sure it exists first - vol = self.driver.client.getVolume(self.SNAPSHOT_3PAR_NAME) + vol = self.driver.common.client.getVolume(self.SNAPSHOT_3PAR_NAME) self.assertEqual(vol['name'], self.SNAPSHOT_3PAR_NAME) self.driver.delete_snapshot(self.snapshot) # the snapshot should be deleted now self.assertRaises(hpexceptions.HTTPNotFound, - self.driver.client.getVolume, + self.driver.common.client.getVolume, self.SNAPSHOT_3PAR_NAME) def test_create_volume_from_snapshot(self): self.flags(lock_path=self.tempdir) self.driver.create_volume_from_snapshot(self.volume, self.snapshot) - snap_vol = self.driver.client.getVolume(self.VOLUME_3PAR_NAME) + snap_vol = self.driver.common.client.getVolume(self.VOLUME_3PAR_NAME) self.assertEqual(snap_vol['name'], self.VOLUME_3PAR_NAME) volume = self.volume.copy() @@ -410,12 +410,12 @@ class HP3PARBaseDriver(): self.flags(lock_path=self.tempdir) #setup the connections self.driver.initialize_connection(self.volume, self.connector) - vlun = self.driver.client.getVLUN(self.VOLUME_3PAR_NAME) + vlun = self.driver.common.client.getVLUN(self.VOLUME_3PAR_NAME) self.assertEqual(vlun['volumeName'], self.VOLUME_3PAR_NAME) self.driver.terminate_connection(self.volume, self.connector, True) # vlun should be gone. self.assertRaises(hpexceptions.HTTPNotFound, - self.driver.client.getVLUN, + self.driver.common.client.getVLUN, self.VOLUME_3PAR_NAME) @@ -442,7 +442,7 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): configuration.san_password = 'test' configuration.hp3par_snapshot_expiration = "" configuration.hp3par_snapshot_retention = "" - self.stubs.Set(hpfcdriver.HP3PARFCDriver, "_create_client", + self.stubs.Set(hpfcdriver.hpcommon.HP3PARCommon, "_create_client", self.fake_create_client) self.stubs.Set(hpfcdriver.HP3PARFCDriver, "_create_3par_fibrechan_host", @@ -515,7 +515,7 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): host = self.fake_get_3par_host(self.FAKE_HOST) self.assertEquals(self.FAKE_HOST, host['name']) self.assertEquals(HP3PAR_DOMAIN, host['domain']) - vlun = self.driver.client.getVLUN(self.VOLUME_3PAR_NAME) + vlun = self.driver.common.client.getVLUN(self.VOLUME_3PAR_NAME) self.assertEquals(self.VOLUME_3PAR_NAME, vlun['volumeName']) self.assertEquals(self.FAKE_HOST, vlun['hostname']) @@ -554,10 +554,10 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): self.assertEquals(stats['free_capacity_gb'], 'infinite') #modify the CPG to have a limit - old_cpg = self.driver.client.getCPG(HP3PAR_CPG) + old_cpg = self.driver.common.client.getCPG(HP3PAR_CPG) options = {'SDGrowth': {'limitMiB': 8192}} - self.driver.client.deleteCPG(HP3PAR_CPG) - self.driver.client.createCPG(HP3PAR_CPG, options) + self.driver.common.client.deleteCPG(HP3PAR_CPG) + self.driver.common.client.createCPG(HP3PAR_CPG, options) const = 0.0009765625 stats = self.driver.get_volume_stats(True) @@ -566,8 +566,8 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): self.assertEquals(stats['total_capacity_gb'], total_capacity_gb) free_capacity_gb = int((8192 - old_cpg['UsrUsage']['usedMiB']) * const) self.assertEquals(stats['free_capacity_gb'], free_capacity_gb) - self.driver.client.deleteCPG(HP3PAR_CPG) - self.driver.client.createCPG(HP3PAR_CPG, {}) + self.driver.common.client.deleteCPG(HP3PAR_CPG) + self.driver.common.client.createCPG(HP3PAR_CPG, {}) def test_create_host(self): self.flags(lock_path=self.tempdir) @@ -664,7 +664,7 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): configuration.hp3par_snapshot_expiration = "" configuration.hp3par_snapshot_retention = "" - self.stubs.Set(hpdriver.HP3PARISCSIDriver, "_create_client", + self.stubs.Set(hpdriver.hpcommon.HP3PARCommon, "_create_client", self.fake_create_client) self.stubs.Set(hpdriver.HP3PARISCSIDriver, "_iscsi_discover_target_iqn", @@ -743,7 +743,7 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): host = self.fake_get_3par_host(self.FAKE_HOST) self.assertEquals(self.FAKE_HOST, host['name']) self.assertEquals(HP3PAR_DOMAIN, host['domain']) - vlun = self.driver.client.getVLUN(self.VOLUME_3PAR_NAME) + vlun = self.driver.common.client.getVLUN(self.VOLUME_3PAR_NAME) self.assertEquals(self.VOLUME_3PAR_NAME, vlun['volumeName']) self.assertEquals(self.FAKE_HOST, vlun['hostname']) @@ -782,10 +782,10 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): self.assertEquals(stats['free_capacity_gb'], 'infinite') #modify the CPG to have a limit - old_cpg = self.driver.client.getCPG(HP3PAR_CPG) + old_cpg = self.driver.common.client.getCPG(HP3PAR_CPG) options = {'SDGrowth': {'limitMiB': 8192}} - self.driver.client.deleteCPG(HP3PAR_CPG) - self.driver.client.createCPG(HP3PAR_CPG, options) + self.driver.common.client.deleteCPG(HP3PAR_CPG) + self.driver.common.client.createCPG(HP3PAR_CPG, options) const = 0.0009765625 stats = self.driver.get_volume_stats(True) @@ -794,8 +794,8 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): self.assertEquals(stats['total_capacity_gb'], total_capacity_gb) free_capacity_gb = int((8192 - old_cpg['UsrUsage']['usedMiB']) * const) self.assertEquals(stats['free_capacity_gb'], free_capacity_gb) - self.driver.client.deleteCPG(HP3PAR_CPG) - self.driver.client.createCPG(HP3PAR_CPG, {}) + self.driver.common.client.deleteCPG(HP3PAR_CPG) + self.driver.common.client.createCPG(HP3PAR_CPG, {}) def test_create_host(self): self.flags(lock_path=self.tempdir) diff --git a/cinder/volume/drivers/san/hp/hp_3par_common.py b/cinder/volume/drivers/san/hp/hp_3par_common.py index abfb74135..01b19a18e 100644 --- a/cinder/volume/drivers/san/hp/hp_3par_common.py +++ b/cinder/volume/drivers/san/hp/hp_3par_common.py @@ -46,6 +46,7 @@ import time import uuid from eventlet import greenthread +from hp3parclient import client from hp3parclient import exceptions as hpexceptions from oslo.config import cfg @@ -93,7 +94,7 @@ hp3par_opts = [ ] -class HP3PARCommon(): +class HP3PARCommon(object): stats = {} @@ -113,12 +114,57 @@ class HP3PARCommon(): self.sshpool = None self.config = config self.hosts_naming_dict = dict() + self.client = None def check_flags(self, options, required_flags): for flag in required_flags: if not getattr(options, flag, None): raise exception.InvalidInput(reason=_('%s is not set') % flag) + def _create_client(self): + return client.HP3ParClient(self.config.hp3par_api_url) + + def client_login(self): + try: + LOG.debug("Connecting to 3PAR") + self.client.login(self.config.hp3par_username, + self.config.hp3par_password) + except hpexceptions.HTTPUnauthorized as ex: + LOG.warning("Failed to connect to 3PAR (%s) because %s" % + (self.config.hp3par_api_url, str(ex))) + msg = _("Login to 3PAR array invalid") + raise exception.InvalidInput(reason=msg) + + def client_logout(self): + self.client.logout() + LOG.debug("Disconnect from 3PAR") + + def do_setup(self, context): + self.client = self._create_client() + if self.config.hp3par_debug: + self.client.debug_rest(True) + + self.client_login() + + # make sure the CPG exists + try: + cpg = self.client.getCPG(self.config.hp3par_cpg) + except hpexceptions.HTTPNotFound as ex: + err = (_("CPG (%s) doesn't exist on array") + % self.config.hp3par_cpg) + LOG.error(err) + raise exception.InvalidInput(reason=err) + + if ('domain' not in cpg + and cpg['domain'] != self.config.hp3par_domain): + err = ("CPG's domain '%s' and config option hp3par_domain '%s'" + " must be the same" % + (cpg['domain'], self.config.hp3par_domain)) + LOG.error(err) + raise exception.InvalidInput(reason=err) + + self.client_logout() + def _get_3par_vol_name(self, volume_id): """ Converts the openstack volume id from @@ -412,13 +458,13 @@ exit LOG.debug("PORTS = %s" % pprint.pformat(ports)) return ports - def get_volume_stats(self, refresh, client): + def get_volume_stats(self, refresh): if refresh: - self._update_volume_stats(client) + self._update_volume_stats() return self.stats - def _update_volume_stats(self, client): + def _update_volume_stats(self): # const to convert MiB to GB const = 0.0009765625 @@ -433,7 +479,7 @@ exit 'volume_backend_name': None} try: - cpg = client.getCPG(self.config.hp3par_cpg) + cpg = self.client.getCPG(self.config.hp3par_cpg) if 'limitMiB' not in cpg['SDGrowth']: total_capacity = 'infinite' free_capacity = 'infinite' @@ -452,19 +498,19 @@ exit self.stats = stats - def create_vlun(self, volume, host, client): + def create_vlun(self, volume, host): """ In order to export a volume on a 3PAR box, we have to create a VLUN. """ volume_name = self._get_3par_vol_name(volume['id']) self._create_3par_vlun(volume_name, host['name']) - return client.getVLUN(volume_name) + return self.client.getVLUN(volume_name) - def delete_vlun(self, volume, hostname, client): + def delete_vlun(self, volume, hostname): volume_name = self._get_3par_vol_name(volume['id']) - vlun = client.getVLUN(volume_name) - client.deleteVLUN(volume_name, vlun['lun'], hostname) + vlun = self.client.getVLUN(volume_name) + self.client.deleteVLUN(volume_name, vlun['lun'], hostname) self._delete_3par_host(hostname) def _get_volume_type(self, type_id): @@ -500,7 +546,7 @@ exit persona_id = persona_value.split(' ') return persona_id[0] - def create_volume(self, volume, client): + def create_volume(self, volume): LOG.debug("CREATE VOLUME (%s : %s %s)" % (volume['display_name'], volume['name'], self._get_3par_vol_name(volume['id']))) @@ -563,7 +609,7 @@ exit capacity = self._capacity_from_size(volume['size']) volume_name = self._get_3par_vol_name(volume['id']) - client.createVolume(volume_name, cpg, capacity, extras) + self.client.createVolume(volume_name, cpg, capacity, extras) except hpexceptions.HTTPConflict: raise exception.Duplicate(_("Volume (%s) already exists on array") @@ -605,15 +651,14 @@ exit word = re.search(search_string.strip(' ') + ' ([^ ]*)', s) return word.groups()[0].strip(' ') - @utils.synchronized('3parclone', external=True) - def create_cloned_volume(self, volume, src_vref, client): + def create_cloned_volume(self, volume, src_vref): try: orig_name = self._get_3par_vol_name(volume['source_volid']) vol_name = self._get_3par_vol_name(volume['id']) # We need to create a new volume first. Otherwise you # can't delete the original - new_vol = self.create_volume(volume, client) + new_vol = self.create_volume(volume) # make the 3PAR copy the contents. # can't delete the original until the copy is done. @@ -648,10 +693,10 @@ exit return None - def delete_volume(self, volume, client): + def delete_volume(self, volume): try: volume_name = self._get_3par_vol_name(volume['id']) - client.deleteVolume(volume_name) + self.client.deleteVolume(volume_name) except hpexceptions.HTTPNotFound as ex: # We'll let this act as if it worked # it helps clean up the cinder entries. @@ -663,7 +708,7 @@ exit LOG.error(str(ex)) raise exception.CinderException(ex.get_description()) - def create_volume_from_snapshot(self, volume, snapshot, client): + def create_volume_from_snapshot(self, volume, snapshot): """ Creates a volume from a snapshot. @@ -696,13 +741,13 @@ exit optional = {'comment': json.dumps(extra), 'readOnly': False} - client.createSnapshot(vol_name, snap_name, optional) + self.client.createSnapshot(vol_name, snap_name, optional) except hpexceptions.HTTPForbidden: raise exception.NotAuthorized() except hpexceptions.HTTPNotFound: raise exception.NotFound() - def create_snapshot(self, snapshot, client): + def create_snapshot(self, snapshot): LOG.debug("Create Snapshot\n%s" % pprint.pformat(snapshot)) try: @@ -734,18 +779,18 @@ exit optional['retentionHours'] = ( self.config.hp3par_snapshot_retention) - client.createSnapshot(snap_name, vol_name, optional) + self.client.createSnapshot(snap_name, vol_name, optional) except hpexceptions.HTTPForbidden: raise exception.NotAuthorized() except hpexceptions.HTTPNotFound: raise exception.NotFound() - def delete_snapshot(self, snapshot, client): + def delete_snapshot(self, snapshot): LOG.debug("Delete Snapshot\n%s" % pprint.pformat(snapshot)) try: snap_name = self._get_3par_snap_name(snapshot['id']) - client.deleteVolume(snap_name) + self.client.deleteVolume(snap_name) except hpexceptions.HTTPForbidden: raise exception.NotAuthorized() except hpexceptions.HTTPNotFound as ex: @@ -765,13 +810,13 @@ exit if (wwn_iqn.upper() in showhost.upper()): return showhost.split(',')[1] - def terminate_connection(self, volume, hostname, wwn_iqn, client): + def terminate_connection(self, volume, hostname, wwn_iqn): """ Driver entry point to unattach a volume from an instance.""" try: # does 3par know this host by a different name? if hostname in self.hosts_naming_dict: hostname = self.hosts_naming_dict.get(hostname) - self.delete_vlun(volume, hostname, client) + self.delete_vlun(volume, hostname) return except hpexceptions.HTTPNotFound as e: if 'host does not exist' in e.get_description(): @@ -785,7 +830,7 @@ exit raise #try again with name retrieved from 3par - self.delete_vlun(volume, hostname, client) + self.delete_vlun(volume, hostname) def parse_create_host_error(self, hostname, out): search_str = "already used by host " diff --git a/cinder/volume/drivers/san/hp/hp_3par_fc.py b/cinder/volume/drivers/san/hp/hp_3par_fc.py index 0479e083a..cc55b0a3e 100644 --- a/cinder/volume/drivers/san/hp/hp_3par_fc.py +++ b/cinder/volume/drivers/san/hp/hp_3par_fc.py @@ -30,7 +30,6 @@ Set the following in the cinder.conf file to enable the volume_driver=cinder.volume.drivers.san.hp.hp_3par_fc.HP3PARFCDriver """ -from hp3parclient import client from hp3parclient import exceptions as hpexceptions from oslo.config import cfg @@ -55,7 +54,6 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver): def __init__(self, *args, **kwargs): super(HP3PARFCDriver, self).__init__(*args, **kwargs) - self.client = None self.common = None self.configuration.append_config_values(hpcommon.hp3par_opts) self.configuration.append_config_values(san.san_opts) @@ -70,85 +68,69 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver): 'san_ip', 'san_login', 'san_password'] self.common.check_flags(self.configuration, required_flags) - def _create_client(self): - return client.HP3ParClient(self.configuration.hp3par_api_url) - + @utils.synchronized('3par', external=True) def get_volume_stats(self, refresh): - stats = self.common.get_volume_stats(refresh, self.client) + self.common.client_login() + stats = self.common.get_volume_stats(refresh) stats['storage_protocol'] = 'FC' backend_name = self.configuration.safe_get('volume_backend_name') stats['volume_backend_name'] = backend_name or self.__class__.__name__ + self.common.client_logout() return stats def do_setup(self, context): self.common = self._init_common() self._check_flags() - self.client = self._create_client() - if self.configuration.hp3par_debug: - self.client.debug_rest(True) - - try: - LOG.debug("Connecting to 3PAR") - self.client.login(self.configuration.hp3par_username, - self.configuration.hp3par_password) - except hpexceptions.HTTPUnauthorized as ex: - LOG.warning("Failed to connect to 3PAR (%s) because %s" % - (self.configuration.hp3par_api_url, str(ex))) - msg = _("Login to 3PAR array invalid") - raise exception.InvalidInput(reason=msg) - - # make sure the CPG exists - try: - cpg = self.client.getCPG(self.configuration.hp3par_cpg) - except hpexceptions.HTTPNotFound as ex: - err = (_("CPG (%s) doesn't exist on array") - % self.configuration.hp3par_cpg) - LOG.error(err) - raise exception.InvalidInput(reason=err) - - if ('domain' not in cpg - and cpg['domain'] != self.configuration.hp3par_domain): - err = "CPG's domain '%s' and config option hp3par_domain '%s' \ -must be the same" % (cpg['domain'], self.configuration.hp3par_domain) - LOG.error(err) - raise exception.InvalidInput(reason=err) + self.common.do_setup(context) def check_for_setup_error(self): """Returns an error if prerequisites aren't met.""" self._check_flags() - @utils.synchronized('3par-vol', external=True) + @utils.synchronized('3par', external=True) def create_volume(self, volume): - metadata = self.common.create_volume(volume, self.client) + self.common.client_login() + metadata = self.common.create_volume(volume) + self.common.client_logout() return {'metadata': metadata} + @utils.synchronized('3par', external=True) def create_cloned_volume(self, volume, src_vref): - new_vol = self.common.create_cloned_volume(volume, src_vref, - self.client) + self.common.client_login() + new_vol = self.common.create_cloned_volume(volume, src_vref) + self.common.client_logout() return {'metadata': new_vol} - @utils.synchronized('3par-vol', external=True) + @utils.synchronized('3par', external=True) def delete_volume(self, volume): - self.common.delete_volume(volume, self.client) + self.common.client_login() + self.common.delete_volume(volume) + self.common.client_logout() - @utils.synchronized('3par-vol', external=True) + @utils.synchronized('3par', external=True) def create_volume_from_snapshot(self, volume, snapshot): """ Creates a volume from a snapshot. TODO: support using the size from the user. """ - self.common.create_volume_from_snapshot(volume, snapshot, self.client) + self.common.client_login() + self.common.create_volume_from_snapshot(volume, snapshot) + self.common.client_logout() - @utils.synchronized('3par-snap', external=True) + @utils.synchronized('3par', external=True) def create_snapshot(self, snapshot): - self.common.create_snapshot(snapshot, self.client) + self.common.client_login() + self.common.create_snapshot(snapshot) + self.common.client_logout() - @utils.synchronized('3par-snap', external=True) + @utils.synchronized('3par', external=True) def delete_snapshot(self, snapshot): - self.common.delete_snapshot(snapshot, self.client) + self.common.client_login() + self.common.delete_snapshot(snapshot) + self.common.client_logout() - @utils.synchronized('3par-attach', external=True) + @utils.synchronized('3par', external=True) def initialize_connection(self, volume, connector): """Assigns the volume to a server. @@ -186,27 +168,30 @@ must be the same" % (cpg['domain'], self.configuration.hp3par_domain) * Create a VLUN for that HOST with the volume we want to export. """ + self.common.client_login() # we have to make sure we have a host host = self._create_host(volume, connector) # now that we have a host, create the VLUN - vlun = self.common.create_vlun(volume, host, self.client) + vlun = self.common.create_vlun(volume, host) ports = self.common.get_ports() + self.common.client_logout() info = {'driver_volume_type': 'fibre_channel', 'data': {'target_lun': vlun['lun'], 'target_discovered': True, 'target_wwn': ports['FC']}} return info - @utils.synchronized('3par-attach', external=True) + @utils.synchronized('3par', external=True) def terminate_connection(self, volume, connector, force): """Driver entry point to unattach a volume from an instance.""" + self.common.client_login() self.common.terminate_connection(volume, connector['host'], - connector['wwpns'], - self.client) + connector['wwpns']) + self.common.client_logout() def _create_3par_fibrechan_host(self, hostname, wwn, domain, persona_id): """Create a 3PAR host. @@ -250,14 +235,14 @@ must be the same" % (cpg['domain'], self.configuration.hp3par_domain) return host - @utils.synchronized('3par-exp', external=True) + @utils.synchronized('3par', external=True) def create_export(self, context, volume): pass - @utils.synchronized('3par-exp', external=True) + @utils.synchronized('3par', external=True) def ensure_export(self, context, volume): pass - @utils.synchronized('3par-exp', external=True) + @utils.synchronized('3par', external=True) def remove_export(self, context, volume): pass diff --git a/cinder/volume/drivers/san/hp/hp_3par_iscsi.py b/cinder/volume/drivers/san/hp/hp_3par_iscsi.py index bf14c8791..4fddb8103 100644 --- a/cinder/volume/drivers/san/hp/hp_3par_iscsi.py +++ b/cinder/volume/drivers/san/hp/hp_3par_iscsi.py @@ -30,7 +30,6 @@ Set the following in the cinder.conf file to enable the volume_driver=cinder.volume.drivers.san.hp.hp_3par_iscsi.HP3PARISCSIDriver """ -from hp3parclient import client from hp3parclient import exceptions as hpexceptions from cinder import exception @@ -53,7 +52,6 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): """ def __init__(self, *args, **kwargs): super(HP3PARISCSIDriver, self).__init__(*args, **kwargs) - self.client = None self.common = None self.configuration.append_config_values(hpcommon.hp3par_opts) self.configuration.append_config_values(san.san_opts) @@ -69,48 +67,20 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): 'san_password'] self.common.check_flags(self.configuration, required_flags) - def _create_client(self): - return client.HP3ParClient(self.configuration.hp3par_api_url) - + @utils.synchronized('3par', external=True) def get_volume_stats(self, refresh): - stats = self.common.get_volume_stats(refresh, self.client) + self.common.client_login() + stats = self.common.get_volume_stats(refresh) stats['storage_protocol'] = 'iSCSI' backend_name = self.configuration.safe_get('volume_backend_name') stats['volume_backend_name'] = backend_name or self.__class__.__name__ + self.common.client_logout() return stats def do_setup(self, context): self.common = self._init_common() self._check_flags() - self.client = self._create_client() - if self.configuration.hp3par_debug: - self.client.debug_rest(True) - - try: - LOG.debug("Connecting to 3PAR") - self.client.login(self.configuration.hp3par_username, - self.configuration.hp3par_password) - except hpexceptions.HTTPUnauthorized as ex: - LOG.warning("Failed to connect to 3PAR (%s) because %s" % - (self.configuration.hp3par_api_url, str(ex))) - msg = _("Login to 3PAR array invalid") - raise exception.InvalidInput(reason=msg) - - # make sure the CPG exists - try: - cpg = self.client.getCPG(self.configuration.hp3par_cpg) - except hpexceptions.HTTPNotFound as ex: - err = (_("CPG (%s) doesn't exist on array") - % self.configuration.hp3par_cpg) - LOG.error(err) - raise exception.InvalidInput(reason=err) - - if ('domain' not in cpg and - cpg['domain'] != self.configuration.hp3par_domain): - err = "CPG's domain '%s' and config option hp3par_domain '%s' \ -must be the same" % (cpg['domain'], self.configuration.hp3par_domain) - LOG.error(err) - raise exception.InvalidInput(reason=err) + self.common.do_setup(context) # make sure ssh works. self._iscsi_discover_target_iqn(self.configuration.iscsi_ip_address) @@ -119,46 +89,59 @@ must be the same" % (cpg['domain'], self.configuration.hp3par_domain) """Returns an error if prerequisites aren't met.""" self._check_flags() - @utils.synchronized('3par-vol', external=True) + @utils.synchronized('3par', external=True) def create_volume(self, volume): - metadata = self.common.create_volume(volume, self.client) + self.common.client_login() + metadata = self.common.create_volume(volume) + self.common.client_logout() return {'provider_location': "%s:%s" % (self.configuration.iscsi_ip_address, self.configuration.iscsi_port), 'metadata': metadata} + @utils.synchronized('3par', external=True) def create_cloned_volume(self, volume, src_vref): """ Clone an existing volume. """ - new_vol = self.common.create_cloned_volume(volume, src_vref, - self.client) + self.common.client_login() + new_vol = self.common.create_cloned_volume(volume, src_vref) + self.common.client_logout() + return {'provider_location': "%s:%s" % (self.configuration.iscsi_ip_address, self.configuration.iscsi_port), 'metadata': new_vol} - @utils.synchronized('3par-vol', external=True) + @utils.synchronized('3par', external=True) def delete_volume(self, volume): - self.common.delete_volume(volume, self.client) + self.common.client_login() + self.common.delete_volume(volume) + self.common.client_logout() - @utils.synchronized('3par-vol', external=True) + @utils.synchronized('3par', external=True) def create_volume_from_snapshot(self, volume, snapshot): """ Creates a volume from a snapshot. TODO: support using the size from the user. """ - self.common.create_volume_from_snapshot(volume, snapshot, self.client) + self.common.client_login() + self.common.create_volume_from_snapshot(volume, snapshot) + self.common.client_logout() - @utils.synchronized('3par-snap', external=True) + @utils.synchronized('3par', external=True) def create_snapshot(self, snapshot): - self.common.create_snapshot(snapshot, self.client) + self.common.client_login() + self.common.create_snapshot(snapshot) + self.common.client_logout() - @utils.synchronized('3par-snap', external=True) + @utils.synchronized('3par', external=True) def delete_snapshot(self, snapshot): - self.common.delete_snapshot(snapshot, self.client) + self.common.client_login() + self.common.delete_snapshot(snapshot) + self.common.client_logout() - @utils.synchronized('3par-attach', external=True) + @utils.synchronized('3par', external=True) def initialize_connection(self, volume, connector): """Assigns the volume to a server. @@ -184,6 +167,7 @@ must be the same" % (cpg['domain'], self.configuration.hp3par_domain) * Create a host on the 3par * create vlun on the 3par """ + self.common.client_login() # get the target_iqn on the 3par interface. target_iqn = self._iscsi_discover_target_iqn( self.configuration.iscsi_ip_address) @@ -192,8 +176,9 @@ must be the same" % (cpg['domain'], self.configuration.hp3par_domain) host = self._create_host(volume, connector) # now that we have a host, create the VLUN - vlun = self.common.create_vlun(volume, host, self.client) + vlun = self.common.create_vlun(volume, host) + self.common.client_logout() info = {'driver_volume_type': 'iscsi', 'data': {'target_portal': "%s:%s" % (self.configuration.iscsi_ip_address, @@ -205,13 +190,14 @@ must be the same" % (cpg['domain'], self.configuration.hp3par_domain) } return info - @utils.synchronized('3par-attach', external=True) + @utils.synchronized('3par', external=True) def terminate_connection(self, volume, connector, force): """Driver entry point to unattach a volume from an instance.""" + self.common.client_login() self.common.terminate_connection(volume, connector['host'], - connector['initiator'], - self.client) + connector['initiator']) + self.common.client_logout() def _iscsi_discover_target_iqn(self, remote_ip): result = self.common._cli_run('showport -ids', None) @@ -270,14 +256,14 @@ must be the same" % (cpg['domain'], self.configuration.hp3par_domain) return host - @utils.synchronized('3par-exp', external=True) + @utils.synchronized('3par', external=True) def create_export(self, context, volume): pass - @utils.synchronized('3par-exp', external=True) + @utils.synchronized('3par', external=True) def ensure_export(self, context, volume): pass - @utils.synchronized('3par-exp', external=True) + @utils.synchronized('3par', external=True) def remove_export(self, context, volume): pass -- 2.45.2