Removing locks from 3PAR FC and iSCSI drivers
Removed locks from the 3PAR FC and iSCSI drivers. In high load environments where many simultaneous volume creations/deletions and attaches/detaches are happening errors occur periodically. By changing the drivers to create a new connection to the 3PAR backend whenever a volume request is made the errors are avoided and performance is improved. Closes-Bug: 1381190 Change-Id: Ie588a1d87cf5a22ddf2e890c440582e1fe67f2cb
This commit is contained in:
parent
5c203bb3d3
commit
9ef47c280b
File diff suppressed because it is too large
Load Diff
@ -158,10 +158,11 @@ class HP3PARCommon(object):
|
||||
2.0.25 - Migrate without losing type settings bug #1356608
|
||||
2.0.26 - Don't ignore extra-specs snap_cpg when missing cpg #1368972
|
||||
2.0.27 - Fixing manage source-id error bug #1357075
|
||||
2.0.28 - Removing locks bug #1381190
|
||||
|
||||
"""
|
||||
|
||||
VERSION = "2.0.27"
|
||||
VERSION = "2.0.28"
|
||||
|
||||
stats = {}
|
||||
|
||||
@ -199,6 +200,7 @@ class HP3PARCommon(object):
|
||||
self.config = config
|
||||
self.hosts_naming_dict = dict()
|
||||
self.client = None
|
||||
self.uuid = uuid.uuid4()
|
||||
|
||||
def get_version(self):
|
||||
return self.VERSION
|
||||
@ -222,27 +224,6 @@ class HP3PARCommon(object):
|
||||
LOG.error(ex_msg)
|
||||
raise exception.InvalidInput(reason=ex_msg)
|
||||
|
||||
if client_version < MIN_CLIENT_SSH_ARGS_VERSION:
|
||||
cl.setSSHOptions(self.config.san_ip,
|
||||
self.config.san_login,
|
||||
self.config.san_password,
|
||||
port=self.config.san_ssh_port,
|
||||
conn_timeout=self.config.ssh_conn_timeout,
|
||||
privatekey=self.config.san_private_key)
|
||||
else:
|
||||
known_hosts_file = CONF.ssh_hosts_key_file
|
||||
policy = "AutoAddPolicy"
|
||||
if CONF.strict_ssh_host_key_policy:
|
||||
policy = "RejectPolicy"
|
||||
cl.setSSHOptions(self.config.san_ip,
|
||||
self.config.san_login,
|
||||
self.config.san_password,
|
||||
port=self.config.san_ssh_port,
|
||||
conn_timeout=self.config.ssh_conn_timeout,
|
||||
privatekey=self.config.san_private_key,
|
||||
missing_key_policy=policy,
|
||||
known_hosts_file=known_hosts_file)
|
||||
|
||||
return cl
|
||||
|
||||
def client_login(self):
|
||||
@ -256,9 +237,35 @@ class HP3PARCommon(object):
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
client_version = hp3parclient.version
|
||||
|
||||
if client_version < MIN_CLIENT_SSH_ARGS_VERSION:
|
||||
self.client.setSSHOptions(
|
||||
self.config.san_ip,
|
||||
self.config.san_login,
|
||||
self.config.san_password,
|
||||
port=self.config.san_ssh_port,
|
||||
conn_timeout=self.config.ssh_conn_timeout,
|
||||
privatekey=self.config.san_private_key)
|
||||
else:
|
||||
known_hosts_file = CONF.ssh_hosts_key_file
|
||||
policy = "AutoAddPolicy"
|
||||
if CONF.strict_ssh_host_key_policy:
|
||||
policy = "RejectPolicy"
|
||||
self.client.setSSHOptions(
|
||||
self.config.san_ip,
|
||||
self.config.san_login,
|
||||
self.config.san_password,
|
||||
port=self.config.san_ssh_port,
|
||||
conn_timeout=self.config.ssh_conn_timeout,
|
||||
privatekey=self.config.san_private_key,
|
||||
missing_key_policy=policy,
|
||||
known_hosts_file=known_hosts_file)
|
||||
|
||||
def client_logout(self):
|
||||
LOG.info(_LI("Disconnect from 3PAR REST and SSH %s") % self.uuid)
|
||||
self.client.logout()
|
||||
LOG.debug("Disconnect from 3PAR")
|
||||
LOG.info(_LI("logout Done %s") % self.uuid)
|
||||
|
||||
def do_setup(self, context):
|
||||
if hp3parclient is None:
|
||||
@ -274,8 +281,8 @@ class HP3PARCommon(object):
|
||||
if self.config.hp3par_debug:
|
||||
self.client.debug_rest(True)
|
||||
|
||||
def check_for_setup_error(self):
|
||||
self.client_login()
|
||||
|
||||
try:
|
||||
cpg_names = self.config.hp3par_cpg
|
||||
for cpg_name in cpg_names:
|
||||
|
@ -34,10 +34,11 @@ try:
|
||||
except ImportError:
|
||||
hpexceptions = None
|
||||
|
||||
import pprint
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _, _LI
|
||||
from cinder.i18n import _, _LI, _LW
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder import utils
|
||||
import cinder.volume.driver
|
||||
from cinder.volume.drivers.san.hp import hp_3par_common as hpcommon
|
||||
from cinder.volume.drivers.san import san
|
||||
@ -72,14 +73,14 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
2.0.8 - Fixing missing login/logout around attach/detach bug #1367429
|
||||
2.0.9 - Add support for pools with model update
|
||||
2.0.10 - Migrate without losing type settings bug #1356608
|
||||
2.0.11 - Removing locks bug #1381190
|
||||
|
||||
"""
|
||||
|
||||
VERSION = "2.0.10"
|
||||
VERSION = "2.0.11"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HP3PARFCDriver, self).__init__(*args, **kwargs)
|
||||
self.common = None
|
||||
self.configuration.append_config_values(hpcommon.hp3par_opts)
|
||||
self.configuration.append_config_values(san.san_opts)
|
||||
self.lookup_service = fczm_utils.create_lookup_service()
|
||||
@ -87,18 +88,26 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
def _init_common(self):
|
||||
return hpcommon.HP3PARCommon(self.configuration)
|
||||
|
||||
def _check_flags(self):
|
||||
def _login(self):
|
||||
common = self._init_common()
|
||||
common.do_setup(None)
|
||||
common.client_login()
|
||||
return common
|
||||
|
||||
def _logout(self, common):
|
||||
common.client_logout()
|
||||
|
||||
def _check_flags(self, common):
|
||||
"""Sanity check to ensure we have required options set."""
|
||||
required_flags = ['hp3par_api_url', 'hp3par_username',
|
||||
'hp3par_password',
|
||||
'san_ip', 'san_login', 'san_password']
|
||||
self.common.check_flags(self.configuration, required_flags)
|
||||
common.check_flags(self.configuration, required_flags)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def get_volume_stats(self, refresh):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
stats = self.common.get_volume_stats(refresh)
|
||||
stats = common.get_volume_stats(refresh)
|
||||
stats['storage_protocol'] = 'FC'
|
||||
stats['driver_version'] = self.VERSION
|
||||
backend_name = self.configuration.safe_get('volume_backend_name')
|
||||
@ -106,72 +115,65 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
self.__class__.__name__)
|
||||
return stats
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
def do_setup(self, context):
|
||||
self.common = self._init_common()
|
||||
self._check_flags()
|
||||
self.common.do_setup(context)
|
||||
common = self._init_common()
|
||||
common.do_setup(context)
|
||||
self._check_flags(common)
|
||||
common.check_for_setup_error()
|
||||
|
||||
def check_for_setup_error(self):
|
||||
"""Returns an error if prerequisites aren't met."""
|
||||
self._check_flags()
|
||||
"""Setup errors are already checked for in do_setup so return pass."""
|
||||
pass
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def create_volume(self, volume):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.create_volume(volume)
|
||||
return common.create_volume(volume)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def create_cloned_volume(self, volume, src_vref):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.create_cloned_volume(volume, src_vref)
|
||||
return common.create_cloned_volume(volume, src_vref)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def delete_volume(self, volume):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.delete_volume(volume)
|
||||
common.delete_volume(volume)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def create_volume_from_snapshot(self, volume, snapshot):
|
||||
"""Create a volume from a snapshot.
|
||||
|
||||
TODO: support using the size from the user.
|
||||
"""
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.create_volume_from_snapshot(volume,
|
||||
snapshot)
|
||||
return common.create_volume_from_snapshot(volume, snapshot)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def create_snapshot(self, snapshot):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.create_snapshot(snapshot)
|
||||
common.create_snapshot(snapshot)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def delete_snapshot(self, snapshot):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.delete_snapshot(snapshot)
|
||||
common.delete_snapshot(snapshot)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@fczm_utils.AddFCZone
|
||||
@utils.synchronized('3par', external=True)
|
||||
def initialize_connection(self, volume, connector):
|
||||
"""Assigns the volume to a server.
|
||||
|
||||
@ -209,25 +211,25 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
* Create a VLUN for that HOST with the volume we want to export.
|
||||
|
||||
"""
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
# we have to make sure we have a host
|
||||
host = self._create_host(volume, connector)
|
||||
host = self._create_host(common, volume, connector)
|
||||
|
||||
target_wwns, init_targ_map, numPaths = \
|
||||
self._build_initiator_target_map(connector)
|
||||
self._build_initiator_target_map(common, connector)
|
||||
|
||||
# now that we have a host, create the VLUN
|
||||
if self.lookup_service is not None and numPaths == 1:
|
||||
nsp = None
|
||||
active_fc_port_list = self.common.get_active_fc_target_ports()
|
||||
active_fc_port_list = common.get_active_fc_target_ports()
|
||||
for port in active_fc_port_list:
|
||||
if port['portWWN'].lower() == target_wwns[0].lower():
|
||||
nsp = port['nsp']
|
||||
break
|
||||
vlun = self.common.create_vlun(volume, host, nsp)
|
||||
vlun = common.create_vlun(volume, host, nsp)
|
||||
else:
|
||||
vlun = self.common.create_vlun(volume, host)
|
||||
vlun = common.create_vlun(volume, host)
|
||||
|
||||
info = {'driver_volume_type': 'fibre_channel',
|
||||
'data': {'target_lun': vlun['lun'],
|
||||
@ -236,42 +238,41 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
'initiator_target_map': init_targ_map}}
|
||||
return info
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@fczm_utils.RemoveFCZone
|
||||
@utils.synchronized('3par', external=True)
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
"""Driver entry point to unattach a volume from an instance."""
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
hostname = self.common._safe_hostname(connector['host'])
|
||||
self.common.terminate_connection(volume, hostname,
|
||||
wwn=connector['wwpns'])
|
||||
hostname = common._safe_hostname(connector['host'])
|
||||
common.terminate_connection(volume, hostname,
|
||||
wwn=connector['wwpns'])
|
||||
|
||||
info = {'driver_volume_type': 'fibre_channel',
|
||||
'data': {}}
|
||||
|
||||
try:
|
||||
self.common.client.getHostVLUNs(hostname)
|
||||
common.client.getHostVLUNs(hostname)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
# No more exports for this host.
|
||||
LOG.info(_LI("Need to remove FC Zone, building initiator "
|
||||
"target map"))
|
||||
|
||||
target_wwns, init_targ_map, _numPaths = \
|
||||
self._build_initiator_target_map(connector)
|
||||
self._build_initiator_target_map(common, connector)
|
||||
|
||||
info['data'] = {'target_wwn': target_wwns,
|
||||
'initiator_target_map': init_targ_map}
|
||||
return info
|
||||
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
def _build_initiator_target_map(self, connector):
|
||||
def _build_initiator_target_map(self, common, connector):
|
||||
"""Build the target_wwns and the initiator target map."""
|
||||
|
||||
fc_ports = self.common.get_active_fc_target_ports()
|
||||
fc_ports = common.get_active_fc_target_ports()
|
||||
all_target_wwns = []
|
||||
target_wwns = []
|
||||
init_targ_map = {}
|
||||
@ -308,7 +309,8 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
|
||||
return target_wwns, init_targ_map, numPaths
|
||||
|
||||
def _create_3par_fibrechan_host(self, hostname, wwns, domain, persona_id):
|
||||
def _create_3par_fibrechan_host(self, common, hostname, wwns,
|
||||
domain, persona_id):
|
||||
"""Create a 3PAR host.
|
||||
|
||||
Create a 3PAR host, if there is already a host on the 3par using
|
||||
@ -317,48 +319,50 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
"""
|
||||
# first search for an existing host
|
||||
host_found = None
|
||||
for wwn in wwns:
|
||||
host_found = self.common.client.findHost(wwn=wwn)
|
||||
if host_found is not None:
|
||||
break
|
||||
hosts = common.client.queryHost(wwns)
|
||||
|
||||
LOG.warn(_LW("Found HOSTS %s") % pprint.pformat(hosts))
|
||||
if hosts and hosts['members']:
|
||||
host_found = hosts['members'][0]['name']
|
||||
|
||||
if host_found is not None:
|
||||
self.common.hosts_naming_dict[hostname] = host_found
|
||||
common.hosts_naming_dict[hostname] = host_found
|
||||
return host_found
|
||||
else:
|
||||
persona_id = int(persona_id)
|
||||
self.common.client.createHost(hostname, FCWwns=wwns,
|
||||
optional={'domain': domain,
|
||||
'persona': persona_id})
|
||||
common.client.createHost(hostname, FCWwns=wwns,
|
||||
optional={'domain': domain,
|
||||
'persona': persona_id})
|
||||
return hostname
|
||||
|
||||
def _modify_3par_fibrechan_host(self, hostname, wwn):
|
||||
mod_request = {'pathOperation': self.common.client.HOST_EDIT_ADD,
|
||||
def _modify_3par_fibrechan_host(self, common, hostname, wwn):
|
||||
mod_request = {'pathOperation': common.client.HOST_EDIT_ADD,
|
||||
'FCWWNs': wwn}
|
||||
|
||||
self.common.client.modifyHost(hostname, mod_request)
|
||||
common.client.modifyHost(hostname, mod_request)
|
||||
|
||||
def _create_host(self, volume, connector):
|
||||
def _create_host(self, common, volume, connector):
|
||||
"""Creates or modifies existing 3PAR host."""
|
||||
host = None
|
||||
hostname = self.common._safe_hostname(connector['host'])
|
||||
cpg = self.common.get_cpg(volume, allowSnap=True)
|
||||
domain = self.common.get_domain(cpg)
|
||||
hostname = common._safe_hostname(connector['host'])
|
||||
cpg = common.get_cpg(volume, allowSnap=True)
|
||||
domain = common.get_domain(cpg)
|
||||
try:
|
||||
host = self.common._get_3par_host(hostname)
|
||||
host = common._get_3par_host(hostname)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
# get persona from the volume type extra specs
|
||||
persona_id = self.common.get_persona_type(volume)
|
||||
persona_id = common.get_persona_type(volume)
|
||||
# host doesn't exist, we have to create it
|
||||
hostname = self._create_3par_fibrechan_host(hostname,
|
||||
hostname = self._create_3par_fibrechan_host(common,
|
||||
hostname,
|
||||
connector['wwpns'],
|
||||
domain,
|
||||
persona_id)
|
||||
host = self.common._get_3par_host(hostname)
|
||||
host = common._get_3par_host(hostname)
|
||||
|
||||
return self._add_new_wwn_to_host(host, connector['wwpns'])
|
||||
return self._add_new_wwn_to_host(common, host, connector['wwpns'])
|
||||
|
||||
def _add_new_wwn_to_host(self, host, wwns):
|
||||
def _add_new_wwn_to_host(self, common, host, wwns):
|
||||
"""Add wwns to a host if one or more don't exist.
|
||||
|
||||
Identify if argument wwns contains any world wide names
|
||||
@ -383,85 +387,71 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
# if any wwns found that were not in host list,
|
||||
# add them to the host
|
||||
if (len(new_wwns) > 0):
|
||||
self._modify_3par_fibrechan_host(host['name'], new_wwns)
|
||||
host = self.common._get_3par_host(host['name'])
|
||||
self._modify_3par_fibrechan_host(common, host['name'], new_wwns)
|
||||
host = common._get_3par_host(host['name'])
|
||||
return host
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def create_export(self, context, volume):
|
||||
pass
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def ensure_export(self, context, volume):
|
||||
pass
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def remove_export(self, context, volume):
|
||||
pass
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def extend_volume(self, volume, new_size):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.extend_volume(volume, new_size)
|
||||
common.extend_volume(volume, new_size)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def manage_existing(self, volume, existing_ref):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.manage_existing(volume, existing_ref)
|
||||
return common.manage_existing(volume, existing_ref)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def manage_existing_get_size(self, volume, existing_ref):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
size = self.common.manage_existing_get_size(volume, existing_ref)
|
||||
return common.manage_existing_get_size(volume, existing_ref)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
return size
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def unmanage(self, volume):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.unmanage(volume)
|
||||
common.unmanage(volume)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def attach_volume(self, context, volume, instance_uuid, host_name,
|
||||
mountpoint):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.attach_volume(volume, instance_uuid)
|
||||
common.attach_volume(volume, instance_uuid)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def detach_volume(self, context, volume):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.detach_volume(volume)
|
||||
common.detach_volume(volume)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def retype(self, context, volume, new_type, diff, host):
|
||||
"""Convert the volume to be of the new type."""
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.retype(volume, new_type, diff, host)
|
||||
return common.retype(volume, new_type, diff, host)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def migrate_volume(self, context, volume, host):
|
||||
|
||||
if volume['status'] == 'in-use':
|
||||
protocol = host['capabilities']['storage_protocol']
|
||||
if protocol != 'FC':
|
||||
@ -469,19 +459,19 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
|
||||
"to a host with storage_protocol=%s." % protocol)
|
||||
return False, None
|
||||
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.migrate_volume(volume, host)
|
||||
return common.migrate_volume(volume, host)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
def get_pool(self, volume):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.get_cpg(volume)
|
||||
return common.get_cpg(volume)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
reason = (_("Volume %s doesn't exist on array.") % volume)
|
||||
LOG.error(reason)
|
||||
raise exception.InvalidVolume(reason)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
@ -38,7 +38,6 @@ except ImportError:
|
||||
from cinder import exception
|
||||
from cinder.i18n import _, _LE
|
||||
from cinder.openstack.common import log as logging
|
||||
from cinder import utils
|
||||
import cinder.volume.driver
|
||||
from cinder.volume.drivers.san.hp import hp_3par_common as hpcommon
|
||||
from cinder.volume.drivers.san import san
|
||||
@ -76,32 +75,40 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
2.0.6 - Fixing missing login/logout around attach/detach bug #1367429
|
||||
2.0.7 - Add support for pools with model update
|
||||
2.0.8 - Migrate without losing type settings bug #1356608
|
||||
2.0.9 - Removing locks bug #1381190
|
||||
|
||||
"""
|
||||
|
||||
VERSION = "2.0.8"
|
||||
VERSION = "2.0.9"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HP3PARISCSIDriver, self).__init__(*args, **kwargs)
|
||||
self.common = None
|
||||
self.configuration.append_config_values(hpcommon.hp3par_opts)
|
||||
self.configuration.append_config_values(san.san_opts)
|
||||
|
||||
def _init_common(self):
|
||||
return hpcommon.HP3PARCommon(self.configuration)
|
||||
|
||||
def _check_flags(self):
|
||||
def _login(self):
|
||||
common = self._init_common()
|
||||
common.do_setup(None)
|
||||
common.client_login()
|
||||
return common
|
||||
|
||||
def _logout(self, common):
|
||||
common.client_logout()
|
||||
|
||||
def _check_flags(self, common):
|
||||
"""Sanity check to ensure we have required options set."""
|
||||
required_flags = ['hp3par_api_url', 'hp3par_username',
|
||||
'hp3par_password', 'san_ip', 'san_login',
|
||||
'san_password']
|
||||
self.common.check_flags(self.configuration, required_flags)
|
||||
common.check_flags(self.configuration, required_flags)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def get_volume_stats(self, refresh):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
stats = self.common.get_volume_stats(refresh)
|
||||
stats = common.get_volume_stats(refresh)
|
||||
stats['storage_protocol'] = 'iSCSI'
|
||||
stats['driver_version'] = self.VERSION
|
||||
backend_name = self.configuration.safe_get('volume_backend_name')
|
||||
@ -109,20 +116,21 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
self.__class__.__name__)
|
||||
return stats
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
def do_setup(self, context):
|
||||
self.common = self._init_common()
|
||||
self._check_flags()
|
||||
self.common.do_setup(context)
|
||||
common = self._init_common()
|
||||
common.do_setup(context)
|
||||
self._check_flags(common)
|
||||
common.check_for_setup_error()
|
||||
|
||||
self.common.client_login()
|
||||
common.client_login()
|
||||
try:
|
||||
self.initialize_iscsi_ports()
|
||||
self.initialize_iscsi_ports(common)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
def initialize_iscsi_ports(self):
|
||||
def initialize_iscsi_ports(self, common):
|
||||
# map iscsi_ip-> ip_port
|
||||
# -> iqn
|
||||
# -> nsp
|
||||
@ -153,7 +161,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
# get all the valid iSCSI ports from 3PAR
|
||||
# when found, add the valid iSCSI ip, ip port, iqn and nsp
|
||||
# to the iSCSI IP dictionary
|
||||
iscsi_ports = self.common.get_active_iscsi_target_ports()
|
||||
iscsi_ports = common.get_active_iscsi_target_ports()
|
||||
|
||||
for port in iscsi_ports:
|
||||
ip = port['IPAddr']
|
||||
@ -184,64 +192,56 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
raise exception.InvalidInput(reason=(msg))
|
||||
|
||||
def check_for_setup_error(self):
|
||||
"""Returns an error if prerequisites aren't met."""
|
||||
self._check_flags()
|
||||
"""Setup errors are already checked for in do_setup so return pass."""
|
||||
pass
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def create_volume(self, volume):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.create_volume(volume)
|
||||
return common.create_volume(volume)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def create_cloned_volume(self, volume, src_vref):
|
||||
"""Clone an existing volume."""
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.create_cloned_volume(volume, src_vref)
|
||||
return common.create_cloned_volume(volume, src_vref)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def delete_volume(self, volume):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.delete_volume(volume)
|
||||
common.delete_volume(volume)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@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.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.create_volume_from_snapshot(volume,
|
||||
snapshot)
|
||||
return common.create_volume_from_snapshot(volume, snapshot)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def create_snapshot(self, snapshot):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.create_snapshot(snapshot)
|
||||
common.create_snapshot(snapshot)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def delete_snapshot(self, snapshot):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.delete_snapshot(snapshot)
|
||||
common.delete_snapshot(snapshot)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def initialize_connection(self, volume, connector):
|
||||
"""Assigns the volume to a server.
|
||||
|
||||
@ -267,14 +267,19 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
* Create a host on the 3par
|
||||
* create vlun on the 3par
|
||||
"""
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
# we have to make sure we have a host
|
||||
host, username, password = self._create_host(volume, connector)
|
||||
least_used_nsp = self._get_least_used_nsp_for_host(host['name'])
|
||||
host, username, password = self._create_host(
|
||||
common,
|
||||
volume,
|
||||
connector)
|
||||
least_used_nsp = self._get_least_used_nsp_for_host(
|
||||
common,
|
||||
host['name'])
|
||||
|
||||
# now that we have a host, create the VLUN
|
||||
vlun = self.common.create_vlun(volume, host, least_used_nsp)
|
||||
vlun = common.create_vlun(volume, host, least_used_nsp)
|
||||
|
||||
if least_used_nsp is None:
|
||||
msg = _("Least busy iSCSI port not found, "
|
||||
@ -302,42 +307,44 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
|
||||
return info
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
"""Driver entry point to unattach a volume from an instance."""
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
hostname = self.common._safe_hostname(connector['host'])
|
||||
self.common.terminate_connection(volume, hostname,
|
||||
iqn=connector['initiator'])
|
||||
self._clear_chap_3par(volume)
|
||||
hostname = common._safe_hostname(connector['host'])
|
||||
common.terminate_connection(
|
||||
volume,
|
||||
hostname,
|
||||
iqn=connector['initiator'])
|
||||
self._clear_chap_3par(common, volume)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
def _clear_chap_3par(self, volume):
|
||||
def _clear_chap_3par(self, common, volume):
|
||||
"""Clears CHAP credentials on a 3par volume.
|
||||
|
||||
Ignore exceptions caused by the keys not being present on a volume.
|
||||
"""
|
||||
vol_name = self.common._get_3par_vol_name(volume['id'])
|
||||
vol_name = common._get_3par_vol_name(volume['id'])
|
||||
|
||||
try:
|
||||
self.common.client.removeVolumeMetaData(vol_name, CHAP_USER_KEY)
|
||||
common.client.removeVolumeMetaData(vol_name, CHAP_USER_KEY)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
pass
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
try:
|
||||
self.common.client.removeVolumeMetaData(vol_name, CHAP_PASS_KEY)
|
||||
common.client.removeVolumeMetaData(vol_name, CHAP_PASS_KEY)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
pass
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
def _create_3par_iscsi_host(self, hostname, iscsi_iqn, domain, persona_id):
|
||||
def _create_3par_iscsi_host(self, common, hostname, iscsi_iqn, domain,
|
||||
persona_id):
|
||||
"""Create a 3PAR host.
|
||||
|
||||
Create a 3PAR host, if there is already a host on the 3par using
|
||||
@ -345,9 +352,9 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
used by 3PAR.
|
||||
"""
|
||||
# first search for an existing host
|
||||
host_found = self.common.client.findHost(iqn=iscsi_iqn)
|
||||
host_found = common.client.findHost(iqn=iscsi_iqn)
|
||||
if host_found is not None:
|
||||
self.common.hosts_naming_dict[hostname] = host_found
|
||||
common.hosts_naming_dict[hostname] = host_found
|
||||
return host_found
|
||||
else:
|
||||
if isinstance(iscsi_iqn, str) or isinstance(iscsi_iqn, unicode):
|
||||
@ -355,73 +362,86 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
else:
|
||||
iqn = iscsi_iqn
|
||||
persona_id = int(persona_id)
|
||||
self.common.client.createHost(hostname, iscsiNames=iqn,
|
||||
optional={'domain': domain,
|
||||
'persona': persona_id})
|
||||
common.client.createHost(hostname, iscsiNames=iqn,
|
||||
optional={'domain': domain,
|
||||
'persona': persona_id})
|
||||
return hostname
|
||||
|
||||
def _modify_3par_iscsi_host(self, hostname, iscsi_iqn):
|
||||
mod_request = {'pathOperation': self.common.client.HOST_EDIT_ADD,
|
||||
def _modify_3par_iscsi_host(self, common, hostname, iscsi_iqn):
|
||||
mod_request = {'pathOperation': common.client.HOST_EDIT_ADD,
|
||||
'iSCSINames': [iscsi_iqn]}
|
||||
|
||||
self.common.client.modifyHost(hostname, mod_request)
|
||||
common.client.modifyHost(hostname, mod_request)
|
||||
|
||||
def _set_3par_chaps(self, hostname, volume, username, password):
|
||||
def _set_3par_chaps(self, common, hostname, volume, username, password):
|
||||
"""Sets a 3PAR host's CHAP credentials."""
|
||||
if not self.configuration.hp3par_iscsi_chap_enabled:
|
||||
return
|
||||
|
||||
mod_request = {'chapOperation': self.common.client.HOST_EDIT_ADD,
|
||||
'chapOperationMode': self.common.client.CHAP_INITIATOR,
|
||||
mod_request = {'chapOperation': common.client.HOST_EDIT_ADD,
|
||||
'chapOperationMode': common.client.CHAP_INITIATOR,
|
||||
'chapName': username,
|
||||
'chapSecret': password}
|
||||
self.common.client.modifyHost(hostname, mod_request)
|
||||
common.client.modifyHost(hostname, mod_request)
|
||||
|
||||
def _create_host(self, volume, connector):
|
||||
def _create_host(self, common, volume, connector):
|
||||
"""Creates or modifies existing 3PAR host."""
|
||||
# make sure we don't have the host already
|
||||
host = None
|
||||
username = None
|
||||
password = None
|
||||
hostname = self.common._safe_hostname(connector['host'])
|
||||
cpg = self.common.get_cpg(volume, allowSnap=True)
|
||||
domain = self.common.get_domain(cpg)
|
||||
hostname = common._safe_hostname(connector['host'])
|
||||
cpg = common.get_cpg(volume, allowSnap=True)
|
||||
domain = common.get_domain(cpg)
|
||||
|
||||
# Get the CHAP secret if CHAP is enabled
|
||||
if self.configuration.hp3par_iscsi_chap_enabled:
|
||||
vol_name = self.common._get_3par_vol_name(volume['id'])
|
||||
username = self.common.client.getVolumeMetaData(
|
||||
vol_name = common._get_3par_vol_name(volume['id'])
|
||||
username = common.client.getVolumeMetaData(
|
||||
vol_name, CHAP_USER_KEY)['value']
|
||||
password = self.common.client.getVolumeMetaData(
|
||||
password = common.client.getVolumeMetaData(
|
||||
vol_name, CHAP_PASS_KEY)['value']
|
||||
|
||||
try:
|
||||
host = self.common._get_3par_host(hostname)
|
||||
host = common._get_3par_host(hostname)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
# get persona from the volume type extra specs
|
||||
persona_id = self.common.get_persona_type(volume)
|
||||
persona_id = common.get_persona_type(volume)
|
||||
# host doesn't exist, we have to create it
|
||||
hostname = self._create_3par_iscsi_host(hostname,
|
||||
hostname = self._create_3par_iscsi_host(common,
|
||||
hostname,
|
||||
connector['initiator'],
|
||||
domain,
|
||||
persona_id)
|
||||
self._set_3par_chaps(hostname, volume, username, password)
|
||||
host = self.common._get_3par_host(hostname)
|
||||
self._set_3par_chaps(common, hostname, volume, username, password)
|
||||
host = common._get_3par_host(hostname)
|
||||
else:
|
||||
if 'iSCSIPaths' not in host or len(host['iSCSIPaths']) < 1:
|
||||
self._modify_3par_iscsi_host(hostname, connector['initiator'])
|
||||
self._set_3par_chaps(hostname, volume, username, password)
|
||||
host = self.common._get_3par_host(hostname)
|
||||
self._modify_3par_iscsi_host(
|
||||
common, hostname,
|
||||
connector['initiator'])
|
||||
self._set_3par_chaps(
|
||||
common,
|
||||
hostname,
|
||||
volume,
|
||||
username,
|
||||
password)
|
||||
host = common._get_3par_host(hostname)
|
||||
elif (not host['initiatorChapEnabled'] and
|
||||
self.configuration.hp3par_iscsi_chap_enabled):
|
||||
LOG.warn(_("Host exists without CHAP credentials set and has "
|
||||
"iSCSI attachments but CHAP is enabled. Updating "
|
||||
"host with new CHAP credentials."))
|
||||
self._set_3par_chaps(hostname, volume, username, password)
|
||||
self._set_3par_chaps(
|
||||
common,
|
||||
hostname,
|
||||
volume,
|
||||
username,
|
||||
password)
|
||||
|
||||
return host, username, password
|
||||
|
||||
def _do_export(self, volume):
|
||||
def _do_export(self, common, volume):
|
||||
"""Gets the associated account, generates CHAP info and updates."""
|
||||
model_update = {}
|
||||
|
||||
@ -435,10 +455,10 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
chap_password = None
|
||||
try:
|
||||
# Get all active VLUNs for the host
|
||||
vluns = self.common.client.getHostVLUNs(chap_username)
|
||||
vluns = common.client.getHostVLUNs(chap_username)
|
||||
|
||||
# Host has active VLUNs... is CHAP enabled on host?
|
||||
host_info = self.common.client.getHost(chap_username)
|
||||
host_info = common.client.getHost(chap_username)
|
||||
|
||||
if not host_info['initiatorChapEnabled']:
|
||||
LOG.warn(_("Host has no CHAP key, but CHAP is enabled."))
|
||||
@ -464,7 +484,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
if ('remoteName' in vlun and
|
||||
re.match('iqn.*', vlun['remoteName'])):
|
||||
try:
|
||||
chap_password = self.common.client.getVolumeMetaData(
|
||||
chap_password = common.client.getVolumeMetaData(
|
||||
vlun['volumeName'], CHAP_PASS_KEY)['value']
|
||||
chap_exists = True
|
||||
break
|
||||
@ -481,10 +501,10 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
"Generating new CHAP key."))
|
||||
|
||||
# Add CHAP credentials to the volume metadata
|
||||
vol_name = self.common._get_3par_vol_name(volume['id'])
|
||||
self.common.client.setVolumeMetaData(
|
||||
vol_name = common._get_3par_vol_name(volume['id'])
|
||||
common.client.setVolumeMetaData(
|
||||
vol_name, CHAP_USER_KEY, chap_username)
|
||||
self.common.client.setVolumeMetaData(
|
||||
common.client.setVolumeMetaData(
|
||||
vol_name, CHAP_PASS_KEY, chap_password)
|
||||
|
||||
model_update['provider_auth'] = ('CHAP %s %s' %
|
||||
@ -492,28 +512,26 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
|
||||
return model_update
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def create_export(self, context, volume):
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.client_login()
|
||||
return self._do_export(volume)
|
||||
return self._do_export(common, volume)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def ensure_export(self, context, volume):
|
||||
"""Ensure the volume still exists on the 3PAR.
|
||||
|
||||
Also retrieves CHAP credentials, if present on the volume
|
||||
"""
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.client_login()
|
||||
vol_name = self.common._get_3par_vol_name(volume['id'])
|
||||
self.common.client.getVolume(vol_name)
|
||||
vol_name = common._get_3par_vol_name(volume['id'])
|
||||
common.client.getVolume(vol_name)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
LOG.error(_LE("Volume %s doesn't exist on array.") % vol_name)
|
||||
else:
|
||||
metadata = self.common.client.getAllVolumeMetaData(vol_name)
|
||||
metadata = common.client.getAllVolumeMetaData(vol_name)
|
||||
|
||||
username = None
|
||||
password = None
|
||||
@ -532,13 +550,12 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
|
||||
return model_update
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def remove_export(self, context, volume):
|
||||
pass
|
||||
|
||||
def _get_least_used_nsp_for_host(self, hostname):
|
||||
def _get_least_used_nsp_for_host(self, common, hostname):
|
||||
"""Get the least used NSP for the current host.
|
||||
|
||||
Steps to determine which NSP to use.
|
||||
@ -553,17 +570,18 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
return iscsi_nsps[0]
|
||||
|
||||
# Try to reuse an existing iscsi path to the host
|
||||
vluns = self.common.client.getVLUNs()
|
||||
vluns = common.client.getVLUNs()
|
||||
for vlun in vluns['members']:
|
||||
if vlun['active']:
|
||||
if vlun['hostname'] == hostname:
|
||||
temp_nsp = self.common.build_nsp(vlun['portPos'])
|
||||
temp_nsp = common.build_nsp(vlun['portPos'])
|
||||
if temp_nsp in iscsi_nsps:
|
||||
# this host already has an iscsi path, so use it
|
||||
return temp_nsp
|
||||
|
||||
# Calculate the least used iscsi nsp
|
||||
least_used_nsp = self._get_least_used_nsp(vluns['members'],
|
||||
least_used_nsp = self._get_least_used_nsp(common,
|
||||
vluns['members'],
|
||||
self._get_iscsi_nsps())
|
||||
return least_used_nsp
|
||||
|
||||
@ -580,7 +598,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
if value['nsp'] == nsp:
|
||||
return key
|
||||
|
||||
def _get_least_used_nsp(self, vluns, nspss):
|
||||
def _get_least_used_nsp(self, common, vluns, nspss):
|
||||
""""Return the nsp that has the fewest active vluns."""
|
||||
# return only the nsp (node:server:port)
|
||||
# count the number of nsps
|
||||
@ -593,7 +611,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
|
||||
for vlun in vluns:
|
||||
if vlun['active']:
|
||||
nsp = self.common.build_nsp(vlun['portPos'])
|
||||
nsp = common.build_nsp(vlun['portPos'])
|
||||
if nsp in nsp_counts:
|
||||
nsp_counts[nsp] = nsp_counts[nsp] + 1
|
||||
|
||||
@ -606,69 +624,58 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
|
||||
return current_least_used_nsp
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def extend_volume(self, volume, new_size):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.extend_volume(volume, new_size)
|
||||
common.extend_volume(volume, new_size)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def manage_existing(self, volume, existing_ref):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.manage_existing(volume, existing_ref)
|
||||
return common.manage_existing(volume, existing_ref)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def manage_existing_get_size(self, volume, existing_ref):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
size = self.common.manage_existing_get_size(volume, existing_ref)
|
||||
return common.manage_existing_get_size(volume, existing_ref)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
return size
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def unmanage(self, volume):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.unmanage(volume)
|
||||
common.unmanage(volume)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def attach_volume(self, context, volume, instance_uuid, host_name,
|
||||
mountpoint):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.attach_volume(volume, instance_uuid)
|
||||
common.attach_volume(volume, instance_uuid)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def detach_volume(self, context, volume):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
self.common.detach_volume(volume)
|
||||
common.detach_volume(volume)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def retype(self, context, volume, new_type, diff, host):
|
||||
"""Convert the volume to be of the new type."""
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.retype(volume, new_type, diff, host)
|
||||
return common.retype(volume, new_type, diff, host)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
@utils.synchronized('3par', external=True)
|
||||
def migrate_volume(self, context, volume, host):
|
||||
|
||||
if volume['status'] == 'in-use':
|
||||
protocol = host['capabilities']['storage_protocol']
|
||||
if protocol != 'iSCSI':
|
||||
@ -676,19 +683,19 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
|
||||
"to a host with storage_protocol=%s." % protocol)
|
||||
return False, None
|
||||
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.migrate_volume(volume, host)
|
||||
return common.migrate_volume(volume, host)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
||||
def get_pool(self, volume):
|
||||
self.common.client_login()
|
||||
common = self._login()
|
||||
try:
|
||||
return self.common.get_cpg(volume)
|
||||
return common.get_cpg(volume)
|
||||
except hpexceptions.HTTPNotFound:
|
||||
reason = (_("Volume %s doesn't exist on array.") % volume)
|
||||
LOG.error(reason)
|
||||
raise exception.InvalidVolume(reason)
|
||||
finally:
|
||||
self.common.client_logout()
|
||||
self._logout(common)
|
||||
|
Loading…
Reference in New Issue
Block a user