def terminate_connection(self, volume, hostname, wwn=None, iqn=None): """Driver entry point to unattach a volume from an instance.""" # does 3par know this host by a different name? hosts = None if wwn: hosts = self.client.queryHost(wwns=wwn) elif iqn: hosts = self.client.queryHost(iqns=[iqn]) if hosts and hosts['members'] and 'name' in hosts['members'][0]: hostname = hosts['members'][0]['name'] try: self.delete_vlun(volume, hostname) return except hpeexceptions.HTTPNotFound as e: if 'host does not exist' in e.get_description(): # use the wwn to see if we can find the hostname hostname = self._get_3par_hostname_from_wwn_iqn(wwn, iqn) # no 3par host, re-throw if hostname is None: LOG.error(_LE("Exception: %s"), e) raise else: # not a 'host does not exist' HTTPNotFound exception, re-throw LOG.error(_LE("Exception: %s"), e) raise # try again with name retrieved from 3par self.delete_vlun(volume, hostname)
def _set_flash_cache_policy_in_vvs(self, flash_cache, vvs_name): # Update virtual volume set if flash_cache: try: self.client.modifyVolumeSet(vvs_name, flashCachePolicy=flash_cache) LOG.info(_LI("Flash Cache policy set to %s"), flash_cache) except Exception as ex: LOG.error(_LE("Error setting Flash Cache policy " "to %s - exception"), flash_cache) exception.PluginException(ex)
def delete_volume(self, volume): """Deletes a volume.""" client = self._login() try: volume_info = client.getVolumeByName(volume['name']) client.deleteVolume(volume_info['id']) except hpeexceptions.HTTPNotFound: LOG.error(_LE("Volume did not exist. It will not be deleted")) except Exception as ex: raise exception.VolumeBackendAPIException(ex) finally: self._logout(client)
def _set_flash_cache_policy_in_vvs(self, flash_cache, vvs_name): # Update virtual volume set if flash_cache: try: self.client.modifyVolumeSet(vvs_name, flashCachePolicy=flash_cache) LOG.info(_LI("Flash Cache policy set to %s"), flash_cache) except Exception as ex: LOG.error( _LE("Error setting Flash Cache policy " "to %s - exception"), flash_cache) 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 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)