Exemplo n.º 1
0
    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)
Exemplo n.º 3
0
 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)
Exemplo n.º 4
0
    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)
Exemplo n.º 7
0
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
Exemplo n.º 8
0
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
Exemplo n.º 9
0
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
Exemplo n.º 10
0
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
Exemplo n.º 11
0
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
Exemplo n.º 12
0
    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
Exemplo n.º 14
0
    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
Exemplo n.º 15
0
 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)
Exemplo n.º 16
0
    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 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)
Exemplo n.º 18
0
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
Exemplo n.º 19
0
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
Exemplo n.º 20
0
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
Exemplo n.º 21
0
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
Exemplo n.º 22
0
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
Exemplo n.º 24
0
    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
Exemplo n.º 26
0
    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)
Exemplo n.º 29
0
 def get_version_string(self):
     return (_('REST %(proxy_ver)s hpelefthandclient %(rest_ver)s') % {
         'proxy_ver': self.VERSION,
         'rest_ver': hpelefthandclient.get_version_string()})
Exemplo n.º 30
0
    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)
Exemplo n.º 31
0
 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 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)
Exemplo n.º 35
0
    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)
Exemplo n.º 36
0
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
Exemplo n.º 37
0
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