def _login(self): client = self._create_client() try: if self.configuration.hpelefthand_debug: client.debug_rest(True) client.login( self.configuration.hpelefthand_username, self.configuration.hpelefthand_password) cluster_info = client.getClusterByName( self.configuration.hpelefthand_clustername) self.cluster_id = cluster_info['id'] if len(cluster_info['virtualIPAddresses']) > 0: virtual_ips = cluster_info['virtualIPAddresses'] self.cluster_vip = virtual_ips[0]['ipV4Address'] else: # No VIP configured, so just use first storage node IP LOG.warning(_LW('VIP is not configured using node IP ')) ipAddrs = cluster_info['storageModuleIPAddresses'] self.cluster_vip = ipAddrs[0] return client except hpeexceptions.HTTPNotFound: raise exception.ConnectionError( _('LeftHand cluster not found')) except Exception as ex: raise exception.ConnectionError(ex)
def validate_cpg(self, cpg_name): try: self.client.getCPG(cpg_name) except hpeexceptions.HTTPNotFound: err = (_("CPG (%s) doesn't exist on array") % cpg_name) LOG.error(err) raise exception.InvalidInput(reason=err)
def client_login(self): try: LOG.debug("Connecting to 3PAR") self.client.login(self.config.hpe3par_username, self.config.hpe3par_password) except hpeexceptions.HTTPUnauthorized as ex: msg = (_("Failed to Login to 3PAR (%(url)s) because %(err)s") % { 'url': self.config.hpe3par_api_url, 'err': ex }) LOG.error(msg) raise exception.InvalidInput(reason=msg) 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 initialize_iscsi_ports(self, common): # map iscsi_ip-> ip_port # -> iqn # -> nsp self.iscsi_ips = {} temp_iscsi_ip = {} # use the 3PAR ip_addr list for iSCSI configuration if len(self.configuration.hpe3par_iscsi_ips) > 0: # add port values to ip_addr, if necessary for ip_addr in self.configuration.hpe3par_iscsi_ips: ip = ip_addr.split(':') if len(ip) == 1: temp_iscsi_ip[ip_addr] = {'ip_port': DEFAULT_ISCSI_PORT} elif len(ip) == 2: temp_iscsi_ip[ip[0]] = {'ip_port': ip[1]} else: LOG.warning(_LW("Invalid IP address format '%s'"), ip_addr) # add the single value iscsi_ip_address option to the IP dictionary. # This way we can see if it's a valid iSCSI IP. If it's not valid, # we won't use it and won't bother to report it, see below if (self.configuration.iscsi_ip_address not in temp_iscsi_ip): ip = self.configuration.iscsi_ip_address ip_port = self.configuration.iscsi_port temp_iscsi_ip[ip] = {'ip_port': ip_port} # 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 = common.get_active_iscsi_target_ports() for port in iscsi_ports: ip = port['IPAddr'] if ip in temp_iscsi_ip: ip_port = temp_iscsi_ip[ip]['ip_port'] self.iscsi_ips[ip] = { 'ip_port': ip_port, 'nsp': port['nsp'], 'iqn': port['iSCSIName'] } del temp_iscsi_ip[ip] # if the single value iscsi_ip_address option is still in the # temp dictionary it's because it defaults to $my_ip which doesn't # make sense in this context. So, if present, remove it and move on. if (self.configuration.iscsi_ip_address in temp_iscsi_ip): del temp_iscsi_ip[self.configuration.iscsi_ip_address] # lets see if there are invalid iSCSI IPs left in the temp dict if len(temp_iscsi_ip) > 0: LOG.warning( _LW("Found invalid iSCSI IP address(s) in " "configuration option(s) hpe3par_iscsi_ips or " "iscsi_ip_address '%s.'"), (", ".join(temp_iscsi_ip))) if not len(self.iscsi_ips) > 0: msg = _('At least one valid iSCSI IP address must be set.') LOG.error(msg) raise exception.InvalidInput(reason=msg)
def initialize_iscsi_ports(self, common): # map iscsi_ip-> ip_port # -> iqn # -> nsp self.iscsi_ips = {} temp_iscsi_ip = {} # use the 3PAR ip_addr list for iSCSI configuration if len(self.configuration.hpe3par_iscsi_ips) > 0: # add port values to ip_addr, if necessary for ip_addr in self.configuration.hpe3par_iscsi_ips: ip = ip_addr.split(':') if len(ip) == 1: temp_iscsi_ip[ip_addr] = {'ip_port': DEFAULT_ISCSI_PORT} elif len(ip) == 2: temp_iscsi_ip[ip[0]] = {'ip_port': ip[1]} else: LOG.warning(_LW("Invalid IP address format '%s'"), ip_addr) # add the single value iscsi_ip_address option to the IP dictionary. # This way we can see if it's a valid iSCSI IP. If it's not valid, # we won't use it and won't bother to report it, see below if (self.configuration.iscsi_ip_address not in temp_iscsi_ip): ip = self.configuration.iscsi_ip_address ip_port = self.configuration.iscsi_port temp_iscsi_ip[ip] = {'ip_port': ip_port} # 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 = common.get_active_iscsi_target_ports() for port in iscsi_ports: ip = port['IPAddr'] if ip in temp_iscsi_ip: ip_port = temp_iscsi_ip[ip]['ip_port'] self.iscsi_ips[ip] = {'ip_port': ip_port, 'nsp': port['nsp'], 'iqn': port['iSCSIName'] } del temp_iscsi_ip[ip] # if the single value iscsi_ip_address option is still in the # temp dictionary it's because it defaults to $my_ip which doesn't # make sense in this context. So, if present, remove it and move on. if (self.configuration.iscsi_ip_address in temp_iscsi_ip): del temp_iscsi_ip[self.configuration.iscsi_ip_address] # lets see if there are invalid iSCSI IPs left in the temp dict if len(temp_iscsi_ip) > 0: LOG.warning(_LW("Found invalid iSCSI IP address(s) in " "configuration option(s) hpe3par_iscsi_ips or " "iscsi_ip_address '%s.'"), (", ".join(temp_iscsi_ip))) if not len(self.iscsi_ips) > 0: msg = _('At least one valid iSCSI IP address must be set.') LOG.error(msg) raise exception.InvalidInput(reason=msg)
def mount_dir(src, tgt): try: mount("-t", "ext4", src, tgt) except Exception as ex: msg = (_('exception is : %s'), six.text_type(ex)) LOG.error(msg) raise exception.HPEPluginMountException(reason=msg) return True
def remove_dir(tgt): path = FilePath(tgt) if path.exists: try: rm("-rf", tgt) except Exception as ex: msg = (_('exception is : %s'), six.text_type(ex)) LOG.error(msg) raise exception.HPEPluginRemoveDirException(reason=msg) return True
def remove_file(tgt): path = FilePath(tgt) if path.exists: try: rm(tgt) except Exception as ex: msg = (_('exception is : %s'), six.text_type(ex)) LOG.error(msg) raise exception.HPEPluginRemoveDirException(reason=msg) return True
def create_filesystem(path): try: # Create filesystem without user intervention, -F # NEED to be extra careful here!!!! mkfs("-t", "ext4", "-F", path) except Exception as ex: msg = (_('create file system failed exception is : %s'), six.text_type(ex)) LOG.error(msg) raise exception.HPEPluginFileSystemException(reason=msg) return True
def create_volume(self, volume): """Creates a volume.""" # check for valid provisioning type prov_value = volume['provisioning'] if prov_value not in self.valid_prov_values: err = (_("Must specify a valid provisioning type %(valid)s, " "value '%(prov)s' is invalid.") % {'valid': self.valid_prov_values, 'prov': prov_value}) LOG.error(err) raise exception.InvalidInput(reason=err) thin_prov = True if prov_value == "full": thin_prov = False elif prov_value == "dedup": err = (_("Dedup is not supported in the StoreVirtual driver.")) LOG.error(err) raise exception.InvalidInput(reason=err) client = self._login() try: optional = {'isThinProvisioned': thin_prov, 'dataProtectionLevel': 0} clusterName = self.configuration.hpelefthand_clustername optional['clusterName'] = clusterName volume_info = client.createVolume( volume['name'], self.cluster_id, volume['size'] * units.Gi, optional) model_update = self._update_provider(volume_info) volume['provider_location'] = model_update['provider_location'] volume['provider_auth'] = '' except Exception as ex: raise exception.VolumeBackendAPIException(data=ex) finally: self._logout(client)
def get_domain(self, cpg_name): try: cpg = self.client.getCPG(cpg_name) except hpeexceptions.HTTPNotFound: err = (_("Failed to get domain because CPG (%s) doesn't " "exist on array.") % cpg_name) LOG.error(err) raise exception.InvalidInput(reason=err) if 'domain' in cpg: return cpg['domain'] return None
def do_setup(self): """Set up LeftHand client.""" if hpelefthandclient.version < MIN_CLIENT_VERSION: ex_msg = (_("Invalid hpelefthandclient version found (" "%(found)s). Version %(minimum)s or greater " "required. Run 'pip install --upgrade " "python-lefthandclient' to upgrade the " "hpelefthandclient.") % {'found': hpelefthandclient.version, 'minimum': MIN_CLIENT_VERSION}) LOG.error(ex_msg) raise exception.InvalidInput(reason=ex_msg)
def do_setup(self): if hpe3parclient is None: msg = _('You must install hpe3parclient before using 3PAR' ' drivers. Run "pip install python-3parclient" to' ' install the hpe3parclient.') raise exception.VolumeBackendAPIException(data=msg) try: self.client = self._create_client() wsapi_version = self.client.getWsApiVersion() self.API_VERSION = wsapi_version['build'] except hpeexceptions.UnsupportedVersion as ex: raise exception.InvalidInput(ex) if self.config.hpe3par_debug: self.client.debug_rest(True)
def umount_dir(tgt): # For some reason sh.mountpoint does not work, so # using subprocess instead. result = subprocess.Popen(["mountpoint", "-q", tgt]) # we must explictly wait for the process to finish. # Otherwise, we do not get the correct result result.wait() if result.returncode == 0: try: umount("-l", tgt) except Exception as ex: msg = (_('exception is : %s'), six.text_type(ex)) LOG.error(msg) raise exception.HPEPluginUMountException(reason=msg) return True
def mkdir_for_mounting(path): try: data = path.split("/") # TODO: Investigate what triggers OS Brick to return a # /dev/mapper vs. /dev/disk/by-path path if 'mapper' in data: uuid = data[3] else: uuid = data[4] directory = prefix + uuid mkdir("-p", directory) except Exception as ex: msg = (_('Make directory failed exception is : %s'), six.text_type(ex)) LOG.error(msg) raise exception.HPEPluginMakeDirException(reason=msg) return directory
def create_filesystem(path): try: # Create filesystem without user intervention, -F # NEED to be extra careful here!!!! # mkfs("-t", "ext4", "-F", path) # The containerized version of the plugin runs on Alpine # and there is no default mkfs. Therefore, we link # mkfs to mkfs.ext4 in our Dockerfile, so no need to # specify -t ext4. mkfs("-F", path) except Exception as ex: msg = (_('create file system failed exception is : %s'), six.text_type(ex)) LOG.error(msg) raise exception.HPEPluginFileSystemException(reason=msg) return True
def _create_client(self): cl = client.HPE3ParClient( self.config.hpe3par_api_url, suppress_ssl_warnings=CONF.suppress_requests_ssl_warnings) client_version = hpe3parclient.version if client_version < MIN_CLIENT_VERSION: ex_msg = (_('Invalid hpe3parclient version found (%(found)s). ' 'Version %(minimum)s or greater required. Run "pip' ' install --upgrade python-3parclient" to upgrade' ' the hpe3parclient.') % {'found': client_version, 'minimum': MIN_CLIENT_VERSION}) LOG.error(ex_msg) raise exception.InvalidInput(reason=ex_msg) return cl
def _create_client(self): cl = client.HPE3ParClient( self.config.hpe3par_api_url, suppress_ssl_warnings=CONF.suppress_requests_ssl_warnings) client_version = hpe3parclient.version if client_version < MIN_CLIENT_VERSION: ex_msg = (_('Invalid hpe3parclient version found (%(found)s). ' 'Version %(minimum)s or greater required. Run "pip' ' install --upgrade python-3parclient" to upgrade' ' the hpe3parclient.') % { 'found': client_version, 'minimum': MIN_CLIENT_VERSION }) LOG.error(ex_msg) raise exception.InvalidInput(reason=ex_msg) return cl
def get_flash_cache_policy(self, flash_cache): if flash_cache is not None: # If requested, see if supported on back end if self.API_VERSION < FLASH_CACHE_API_VERSION: err = (_("Flash Cache Policy requires " "WSAPI version '%(fcache_version)s' " "version '%(version)s' is installed.") % {'fcache_version': FLASH_CACHE_API_VERSION, 'version': self.API_VERSION}) LOG.error(err) raise exception.InvalidInput(reason=err) else: if flash_cache.lower() == 'true': return self.client.FLASH_CACHE_ENABLED else: return self.client.FLASH_CACHE_DISABLED return None
def get_flash_cache_policy(self, flash_cache): if flash_cache is not None: # If requested, see if supported on back end if self.API_VERSION < FLASH_CACHE_API_VERSION: err = (_("Flash Cache Policy requires " "WSAPI version '%(fcache_version)s' " "version '%(version)s' is installed.") % { 'fcache_version': FLASH_CACHE_API_VERSION, 'version': self.API_VERSION }) LOG.error(err) raise exception.InvalidInput(reason=err) else: if flash_cache.lower() == 'true': return self.client.FLASH_CACHE_ENABLED else: return self.client.FLASH_CACHE_DISABLED return None
def _login(self): client = self._create_client() try: if self.configuration.hpelefthand_debug: client.debug_rest(True) client.login( self.configuration.hpelefthand_username, self.configuration.hpelefthand_password) cluster_info = client.getClusterByName( self.configuration.hpelefthand_clustername) self.cluster_id = cluster_info['id'] virtual_ips = cluster_info['virtualIPAddresses'] self.cluster_vip = virtual_ips[0]['ipV4Address'] return client except hpeexceptions.HTTPNotFound: raise exception.DriverNotInitialized( _('LeftHand cluster not found')) except Exception as ex: raise exception.DriverNotInitialized(ex)
def client_login(self): try: LOG.debug("Connecting to 3PAR") self.client.login(self.config.hpe3par_username, self.config.hpe3par_password) except hpeexceptions.HTTPUnauthorized as ex: msg = (_("Failed to Login to 3PAR (%(url)s) because %(err)s") % {'url': self.config.hpe3par_api_url, 'err': ex}) LOG.error(msg) raise exception.InvalidInput(reason=msg) 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 get_version_string(self): return (_('REST %(proxy_ver)s hpelefthandclient %(rest_ver)s') % { 'proxy_ver': self.VERSION, 'rest_ver': hpelefthandclient.get_version_string()})
def delete_volume(self, volume): try: volume_name = self._get_3par_vol_name(volume['id']) # Try and delete the volume, it might fail here because # the volume is part of a volume set which will have the # volume set name in the error. try: self.client.deleteVolume(volume_name) except hpeexceptions.HTTPBadRequest as ex: if ex.get_code() == 29: if self.client.isOnlinePhysicalCopy(volume_name): LOG.debug("Found an online copy for %(volume)s", {'volume': volume_name}) # the volume is in process of being cloned. # stopOnlinePhysicalCopy will also delete # the volume once it stops the copy. self.client.stopOnlinePhysicalCopy(volume_name) else: LOG.error(_LE("Exception: %s"), ex) raise else: LOG.error(_LE("Exception: %s"), ex) raise except hpeexceptions.HTTPConflict as ex: if ex.get_code() == 34: # This is a special case which means the # volume is part of a volume set. vvset_name = self.client.findVolumeSet(volume_name) LOG.debug("Returned vvset_name = %s", vvset_name) if vvset_name is not None and \ vvset_name.startswith('vvs-'): # We have a single volume per volume set, so # remove the volume set. self.client.deleteVolumeSet( self._get_3par_vvs_name(volume['id'])) elif vvset_name is not None: # We have a pre-defined volume set just remove the # volume and leave the volume set. self.client.removeVolumeFromVolumeSet( vvset_name, volume_name) self.client.deleteVolume(volume_name) elif (ex.get_code() == 151 or ex.get_code() == 32): # the volume is being operated on in a background # task on the 3PAR. # TODO(walter-boring) do a retry a few times. # for now lets log a better message msg = _("The volume is currently busy on the 3PAR" " and cannot be deleted at this time. " "You can try again later.") LOG.error(msg) raise exception.VolumeIsBusy(message=msg) else: LOG.error(_LE("Exception: %s"), ex) raise exception.VolumeIsBusy(message=ex.get_description()) except hpeexceptions.HTTPNotFound as ex: LOG.warning(_LW("Delete volume id not found. Ex: %(msg)s"), {'msg': ex}) except hpeexceptions.HTTPForbidden as ex: LOG.error(_LE("Exception: %s"), ex) raise exception.NotAuthorized(ex.get_description()) except hpeexceptions.HTTPConflict as ex: LOG.error(_LE("Exception: %s"), ex) raise exception.VolumeIsBusy(message=ex.get_description()) except Exception as ex: LOG.error(_LE("Exception: %s"), ex) raise exception.PluginException(ex)
def check_flags(self, options, required_flags): for flag in required_flags: if not getattr(options, flag, None): msg = _('%s is not set') % flag LOG.error(msg) raise exception.InvalidInput(reason=msg)
def create_volume(self, volume): LOG.debug('CREATE VOLUME (%(disp_name)s: %(vol_name)s %(id)s on ' '%(host)s)', {'disp_name': volume['display_name'], 'vol_name': volume['name'], 'id': self._get_3par_vol_name(volume['id']), 'host': volume['host']}) try: comments = {'volume_id': volume['id'], 'name': volume['name'], 'type': 'Docker'} name = volume.get('display_name', None) if name: comments['display_name'] = name # TODO(leeantho): Choose the first CPG for now. In the future # support selecting different CPGs if multiple are provided. cpg = self.config.hpe3par_cpg[0] # check for valid provisioning type prov_value = volume['provisioning'] if prov_value not in self.valid_prov_values: err = (_("Must specify a valid provisioning type %(valid)s, " "value '%(prov)s' is invalid.") % {'valid': self.valid_prov_values, 'prov': prov_value}) LOG.error(err) raise exception.InvalidInput(reason=err) tpvv = True tdvv = False if prov_value == "full": tpvv = False elif prov_value == "dedup": tpvv = False tdvv = True if tdvv and (self.API_VERSION < DEDUP_API_VERSION): err = (_("Dedup is a valid provisioning type, " "but requires WSAPI version '%(dedup_version)s' " "version '%(version)s' is installed.") % {'dedup_version': DEDUP_API_VERSION, 'version': self.API_VERSION}) LOG.error(err) raise exception.InvalidInput(reason=err) extras = {'comment': json.dumps(comments), 'tpvv': tpvv, } # Only set the dedup option if the backend supports it. if self.API_VERSION >= DEDUP_API_VERSION: extras['tdvv'] = tdvv capacity = self._capacity_from_size(volume['size']) volume_name = self._get_3par_vol_name(volume['id']) self.client.createVolume(volume_name, cpg, capacity, extras) # Check if flash cache needs to be enabled flash_cache = self.get_flash_cache_policy(volume['flash_cache']) if flash_cache is not None: try: self._add_volume_to_volume_set(volume, volume_name, cpg, flash_cache) except exception.InvalidInput as ex: # Delete the volume if unable to add it to the volume set self.client.deleteVolume(volume_name) LOG.error(_LE("Exception: %s"), ex) raise exception.PluginException(ex) except hpeexceptions.HTTPConflict: msg = _("Volume (%s) already exists on array") % volume_name LOG.error(msg) raise exception.Duplicate(msg) except hpeexceptions.HTTPBadRequest as ex: LOG.error(_LE("Exception: %s"), ex) raise exception.Invalid(ex.get_description()) except exception.InvalidInput as ex: LOG.error(_LE("Exception: %s"), ex) raise except exception.PluginException as ex: LOG.error(_LE("Exception: %s"), ex) raise except Exception as ex: LOG.error(_LE("Exception: %s"), ex) raise exception.PluginException(ex)
def delete_volume(self, volume): try: volume_name = self._get_3par_vol_name(volume['id']) # Try and delete the volume, it might fail here because # the volume is part of a volume set which will have the # volume set name in the error. try: self.client.deleteVolume(volume_name) except hpeexceptions.HTTPBadRequest as ex: if ex.get_code() == 29: if self.client.isOnlinePhysicalCopy(volume_name): LOG.debug("Found an online copy for %(volume)s", {'volume': volume_name}) # the volume is in process of being cloned. # stopOnlinePhysicalCopy will also delete # the volume once it stops the copy. self.client.stopOnlinePhysicalCopy(volume_name) else: LOG.error(_LE("Exception: %s"), ex) raise else: LOG.error(_LE("Exception: %s"), ex) raise except hpeexceptions.HTTPConflict as ex: if ex.get_code() == 34: # This is a special case which means the # volume is part of a volume set. vvset_name = self.client.findVolumeSet(volume_name) LOG.debug("Returned vvset_name = %s", vvset_name) if vvset_name is not None and \ vvset_name.startswith('vvs-'): # We have a single volume per volume set, so # remove the volume set. self.client.deleteVolumeSet( self._get_3par_vvs_name(volume['id'])) elif vvset_name is not None: # We have a pre-defined volume set just remove the # volume and leave the volume set. self.client.removeVolumeFromVolumeSet(vvset_name, volume_name) self.client.deleteVolume(volume_name) elif (ex.get_code() == 151 or ex.get_code() == 32): # the volume is being operated on in a background # task on the 3PAR. # TODO(walter-boring) do a retry a few times. # for now lets log a better message msg = _("The volume is currently busy on the 3PAR" " and cannot be deleted at this time. " "You can try again later.") LOG.error(msg) raise exception.VolumeIsBusy(message=msg) else: LOG.error(_LE("Exception: %s"), ex) raise exception.VolumeIsBusy(message=ex.get_description()) except hpeexceptions.HTTPNotFound as ex: LOG.warning(_LW("Delete volume id not found. Ex: %(msg)s"), {'msg': ex}) except hpeexceptions.HTTPForbidden as ex: LOG.error(_LE("Exception: %s"), ex) raise exception.NotAuthorized(ex.get_description()) except hpeexceptions.HTTPConflict as ex: LOG.error(_LE("Exception: %s"), ex) raise exception.VolumeIsBusy(message=ex.get_description()) except Exception as ex: LOG.error(_LE("Exception: %s"), ex) raise exception.PluginException(ex)
def create_volume(self, volume): LOG.debug( 'CREATE VOLUME (%(disp_name)s: %(vol_name)s %(id)s on ' '%(host)s)', { 'disp_name': volume['display_name'], 'vol_name': volume['name'], 'id': self._get_3par_vol_name(volume['id']), 'host': volume['host'] }) try: comments = { 'volume_id': volume['id'], 'name': volume['name'], 'type': 'Docker' } name = volume.get('display_name', None) if name: comments['display_name'] = name # TODO(leeantho): Choose the first CPG for now. In the future # support selecting different CPGs if multiple are provided. cpg = self.config.hpe3par_cpg[0] # check for valid provisioning type prov_value = volume['provisioning'] if prov_value not in self.valid_prov_values: err = (_("Must specify a valid provisioning type %(valid)s, " "value '%(prov)s' is invalid.") % { 'valid': self.valid_prov_values, 'prov': prov_value }) LOG.error(err) raise exception.InvalidInput(reason=err) tpvv = True tdvv = False if prov_value == "full": tpvv = False elif prov_value == "dedup": tpvv = False tdvv = True if tdvv and (self.API_VERSION < DEDUP_API_VERSION): err = (_("Dedup is a valid provisioning type, " "but requires WSAPI version '%(dedup_version)s' " "version '%(version)s' is installed.") % { 'dedup_version': DEDUP_API_VERSION, 'version': self.API_VERSION }) LOG.error(err) raise exception.InvalidInput(reason=err) extras = { 'comment': json.dumps(comments), 'tpvv': tpvv, } # Only set the dedup option if the backend supports it. if self.API_VERSION >= DEDUP_API_VERSION: extras['tdvv'] = tdvv capacity = self._capacity_from_size(volume['size']) volume_name = self._get_3par_vol_name(volume['id']) self.client.createVolume(volume_name, cpg, capacity, extras) # Check if flash cache needs to be enabled flash_cache = self.get_flash_cache_policy(volume['flash_cache']) if flash_cache is not None: try: self._add_volume_to_volume_set(volume, volume_name, cpg, flash_cache) except exception.InvalidInput as ex: # Delete the volume if unable to add it to the volume set self.client.deleteVolume(volume_name) LOG.error(_LE("Exception: %s"), ex) raise exception.PluginException(ex) except hpeexceptions.HTTPConflict: msg = _("Volume (%s) already exists on array") % volume_name LOG.error(msg) raise exception.Duplicate(msg) except hpeexceptions.HTTPBadRequest as ex: LOG.error(_LE("Exception: %s"), ex) raise exception.Invalid(ex.get_description()) except exception.InvalidInput as ex: LOG.error(_LE("Exception: %s"), ex) raise except exception.PluginException as ex: LOG.error(_LE("Exception: %s"), ex) raise except Exception as ex: LOG.error(_LE("Exception: %s"), ex) raise exception.PluginException(ex)
def _get_iscsi_properties(volume, targetip): """Gets iscsi configuration We ideally get saved information in the volume entity, but fall back to discovery if need be. Discovery may be completely removed in future The properties are: :target_discovered: boolean indicating whether discovery was used :target_iqn: the IQN of the iSCSI target :target_portal: the portal of the iSCSI target :target_lun: the lun of the iSCSI target :volume_id: the id of the volume (currently used by xen) :auth_method:, :auth_username:, :auth_password: the authentication details. Right now, either auth_method is not present meaning no authentication, or auth_method == `CHAP` meaning use CHAP with the specified credentials. :access_mode: the volume access mode allow client used ('rw' or 'ro' currently supported) :discard: boolean indicating if discard is supported In some of drivers that support multiple connections (for multipath and for single path with failover on connection failure), it returns :target_iqns, :target_portals, :target_luns, which contain lists of multiple values. The main portal information is also returned in :target_iqn, :target_portal, :target_lun for backward compatibility. Note that some of drivers don't return :target_portals even if they support multipath. Then the connector should use sendtargets discovery to find the other portals if it supports multipath. """ properties = {} location = volume['provider_location'] if location: # provider_location is the same format as iSCSI discovery output properties['target_discovered'] = False else: location = _do_iscsi_discovery(volume, targetip) if not location: msg = (_("Could not find iSCSI export for volume %s") % (volume['name'])) raise msg print "ISCSI Discovery: Found ", location properties['target_discovered'] = True results = location.split(" ") portals = results[0].split(",")[0].split(";") iqn = results[1] nr_portals = len(portals) try: lun = int(results[2]) # TODO: Validate StoreVirtual LUN number is part of location details, # after target IP except (IndexError, ValueError): lun = 0 if nr_portals > 1: properties['target_portals'] = portals properties['target_iqns'] = [iqn] * nr_portals properties['target_luns'] = [lun] * nr_portals properties['target_portal'] = portals[0] properties['target_iqn'] = iqn properties['target_lun'] = lun properties['volume_id'] = volume['id'] auth = volume['provider_auth'] if auth: (auth_method, auth_username, auth_secret) = auth.split() properties['auth_method'] = auth_method properties['auth_username'] = auth_username properties['auth_password'] = auth_secret geometry = volume.get('provider_geometry', None) if geometry: (physical_block_size, logical_block_size) = geometry.split() properties['physical_block_size'] = physical_block_size properties['logical_block_size'] = logical_block_size encryption_key_id = volume.get('encryption_key_id', None) properties['encrypted'] = encryption_key_id is not None return properties