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.º 2
0
    def _create_server(self, connector, client):
        server_info = None
        chap_enabled = self.configuration.hpelefthand_iscsi_chap_enabled
        try:
            server_info = client.getServerByName(connector['host'])
            chap_secret = server_info['chapTargetSecret']
            if not chap_enabled and chap_secret:
                LOG.warning(_LW('CHAP secret exists for host %s but CHAP is '
                                'disabled'), connector['host'])
            if chap_enabled and chap_secret is None:
                LOG.warning(_LW('CHAP is enabled, but server secret not '
                                'configured on server %s'), connector['host'])
            return server_info
        except hpeexceptions.HTTPNotFound:
            # server does not exist, so create one
            pass

        optional = None
        if chap_enabled:
            chap_secret = volume_utils.generate_password()
            optional = {'chapName': connector['initiator'],
                        'chapTargetSecret': chap_secret,
                        'chapAuthenticationRequired': True
                        }

        server_info = client.createServer(connector['host'],
                                          connector['initiator'],
                                          optional)
        return server_info
    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.º 4
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 _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 = 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.hpe3par_iscsi_chap_enabled:
            vol_name = common._get_3par_vol_name(volume['id'])
            username = common.client.getVolumeMetaData(
                vol_name, CHAP_USER_KEY)['value']
            password = common.client.getVolumeMetaData(
                vol_name, CHAP_PASS_KEY)['value']

        try:
            host = common._get_3par_host(hostname)
        except hpeexceptions.HTTPNotFound:
            # get persona from the volume type extra specs
            persona_id = 2
            # host doesn't exist, we have to create it
            hostname = self._create_3par_iscsi_host(common,
                                                    hostname,
                                                    connector['initiator'],
                                                    domain,
                                                    persona_id)
            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(
                    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.hpe3par_iscsi_chap_enabled):
                LOG.warning(_LW("Host exists without CHAP credentials set and "
                                "has iSCSI attachments but CHAP is enabled. "
                                "Updating host with new CHAP credentials."))
                self._set_3par_chaps(
                    common,
                    hostname,
                    volume,
                    username,
                    password)

        return host, username, password
Exemplo n.º 6
0
    def check_for_setup_error(self):
        """Checks for incorrect LeftHand API being used on backend."""
        client = self._login()
        try:
            self.api_version = client.getApiVersion()

            LOG.info(_LI("HPELeftHand API version %s"), self.api_version)

            if self.api_version < MIN_API_VERSION:
                LOG.warning(_LW("HPELeftHand API is version %(current)s. "
                                "A minimum version of %(min)s is needed for "
                                "manage/unmanage support."),
                            {'current': self.api_version,
                             'min': MIN_API_VERSION})
        finally:
            self._logout(client)
    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 = 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.hpe3par_iscsi_chap_enabled:
            vol_name = common._get_3par_vol_name(volume['id'])
            username = common.client.getVolumeMetaData(vol_name,
                                                       CHAP_USER_KEY)['value']
            password = common.client.getVolumeMetaData(vol_name,
                                                       CHAP_PASS_KEY)['value']

        try:
            host = common._get_3par_host(hostname)
        except hpeexceptions.HTTPNotFound:
            # get persona from the volume type extra specs
            persona_id = 2
            # host doesn't exist, we have to create it
            hostname = self._create_3par_iscsi_host(common, hostname,
                                                    connector['initiator'],
                                                    domain, persona_id)
            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(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.hpe3par_iscsi_chap_enabled):
                LOG.warning(
                    _LW("Host exists without CHAP credentials set and "
                        "has iSCSI attachments but CHAP is enabled. "
                        "Updating host with new CHAP credentials."))
                self._set_3par_chaps(common, hostname, volume, username,
                                     password)

        return host, username, password
    def _do_export(self, common, volume, connector):
        """Gets the associated account, generates CHAP info and updates."""
        model_update = {}

        if not self.configuration.hpe3par_iscsi_chap_enabled:
            model_update['provider_auth'] = None
            return model_update

        # CHAP username will be the hostname
        chap_username = connector['host']

        chap_password = None
        try:
            # Get all active VLUNs for the host
            vluns = common.client.getHostVLUNs(chap_username)

            # Host has active VLUNs... is CHAP enabled on host?
            host_info = common.client.getHost(chap_username)

            if not host_info['initiatorChapEnabled']:
                LOG.warning(_LW("Host has no CHAP key, but CHAP is enabled."))

        except hpeexceptions.HTTPNotFound:
            chap_password = volume_utils.generate_password(16)
            LOG.warning(
                _LW("No host or VLUNs exist. Generating new "
                    "CHAP key."))
        else:
            # Get a list of all iSCSI VLUNs and see if there is already a CHAP
            # key assigned to one of them.  Use that CHAP key if present,
            # otherwise create a new one.  Skip any VLUNs that are missing
            # CHAP credentials in metadata.
            chap_exists = False
            active_vluns = 0

            for vlun in vluns:
                if not vlun['active']:
                    continue

                active_vluns += 1

                # iSCSI connections start with 'iqn'.
                if ('remoteName' in vlun
                        and re.match('iqn.*', vlun['remoteName'])):
                    try:
                        chap_password = common.client.getVolumeMetaData(
                            vlun['volumeName'], CHAP_PASS_KEY)['value']
                        chap_exists = True
                        break
                    except hpeexceptions.HTTPNotFound:
                        LOG.debug(
                            "The VLUN %s is missing CHAP credentials "
                            "but CHAP is enabled. Skipping.",
                            vlun['remoteName'])
                else:
                    LOG.warning(_LW("Non-iSCSI VLUN detected."))

            if not chap_exists:
                chap_password = volume_utils.generate_password(16)
                LOG.warning(
                    _LW("No VLUN contained CHAP credentials. "
                        "Generating new CHAP key."))

        # Add CHAP credentials to the volume metadata
        vol_name = common._get_3par_vol_name(volume['id'])
        common.client.setVolumeMetaData(vol_name, CHAP_USER_KEY, chap_username)
        common.client.setVolumeMetaData(vol_name, CHAP_PASS_KEY, chap_password)

        model_update['provider_auth'] = ('CHAP %s %s' %
                                         (chap_username, chap_password))

        return model_update
    def initialize_connection(self, volume, connector):
        """Assigns the volume to a server.

        Assign any created volume to a compute node/host so that it can be
        used from that host.

        This driver returns a driver_volume_type of 'iscsi'.
        The format of the driver data is defined in _get_iscsi_properties.
        Example return value:

            {
                'driver_volume_type': 'iscsi'
                'data': {
                    'encrypted': False,
                    'target_discovered': True,
                    'target_iqn': 'iqn.2010-10.org.openstack:volume-00000001',
                    'target_protal': '127.0.0.1:3260',
                    'volume_id': 1,
                }
            }

        Steps to export a volume on 3PAR
          * Get the 3PAR iSCSI iqn
          * Create a host on the 3par
          * create vlun on the 3par
        """
        common = self._login()
        try:
            # we have to make sure we have a host
            host, username, password = self._create_host(
                common, volume, connector)

            if connector['multipath']:
                ready_ports = common.client.getiSCSIPorts(
                    state=common.client.PORT_STATE_READY)

                target_portals = []
                target_iqns = []
                target_luns = []

                # Target portal ips are defined in hpe.conf.
                target_portal_ips = self.iscsi_ips.keys()

                # Collect all existing VLUNs for this volume/host combination.
                existing_vluns = common.find_existing_vluns(volume, host)

                # Cycle through each ready iSCSI port and determine if a new
                # VLUN should be created or an existing one used.
                for port in ready_ports:
                    iscsi_ip = port['IPAddr']
                    if iscsi_ip in target_portal_ips:
                        vlun = None
                        # check for an already existing VLUN matching the
                        # nsp for this iSCSI IP. If one is found, use it
                        # instead of creating a new VLUN.
                        for v in existing_vluns:
                            portPos = common.build_portPos(
                                self.iscsi_ips[iscsi_ip]['nsp'])
                            if v['portPos'] == portPos:
                                vlun = v
                                break
                        else:
                            vlun = common.create_vlun(
                                volume, host, self.iscsi_ips[iscsi_ip]['nsp'])
                        iscsi_ip_port = "%s:%s" % (
                            iscsi_ip, self.iscsi_ips[iscsi_ip]['ip_port'])
                        target_portals.append(iscsi_ip_port)
                        target_iqns.append(port['iSCSIName'])
                        target_luns.append(vlun['lun'])
                    else:
                        LOG.warning(
                            _LW("iSCSI IP: '%s' was not found in "
                                "hpe3par_iscsi_ips list defined in "
                                "hpe.conf."), iscsi_ip)

                info = {
                    'driver_volume_type': 'iscsi',
                    'data': {
                        'target_portals': target_portals,
                        'target_iqns': target_iqns,
                        'target_luns': target_luns,
                        'target_discovered': True
                    }
                }
            else:
                least_used_nsp = None

                # check if a VLUN already exists for this host
                existing_vlun = common.find_existing_vlun(volume, host)

                if existing_vlun:
                    # We override the nsp here on purpose to force the
                    # volume to be exported out the same IP as it already is.
                    # This happens during nova live-migration, we want to
                    # disable the picking of a different IP that we export
                    # the volume to, or nova complains.
                    least_used_nsp = common.build_nsp(existing_vlun['portPos'])

                if not least_used_nsp:
                    least_used_nsp = self._get_least_used_nsp_for_host(
                        common, host['name'])

                vlun = None
                if existing_vlun is None:
                    # now that we have a host, create the VLUN
                    vlun = common.create_vlun(volume, host, least_used_nsp)
                else:
                    vlun = existing_vlun

                if least_used_nsp is None:
                    LOG.warning(
                        _LW("Least busy iSCSI port not found, "
                            "using first iSCSI port in list."))
                    iscsi_ip = self.iscsi_ips.keys()[0]
                else:
                    iscsi_ip = self._get_ip_using_nsp(least_used_nsp)

                iscsi_ip_port = self.iscsi_ips[iscsi_ip]['ip_port']
                iscsi_target_iqn = self.iscsi_ips[iscsi_ip]['iqn']
                info = {
                    'driver_volume_type': 'iscsi',
                    'data': {
                        'target_portal': "%s:%s" % (iscsi_ip, iscsi_ip_port),
                        'target_iqn': iscsi_target_iqn,
                        'target_lun': vlun['lun'],
                        'target_discovered': True
                    }
                }

            if self.configuration.hpe3par_iscsi_chap_enabled:
                info['data']['auth_method'] = 'CHAP'
                info['data']['auth_username'] = username
                info['data']['auth_password'] = password

            encryption_key_id = volume.get('encryption_key_id', None)
            info['data']['encrypted'] = encryption_key_id is not None

            return info
        finally:
            self._logout(common)
    def _do_export(self, common, volume, connector):
        """Gets the associated account, generates CHAP info and updates."""
        model_update = {}

        if not self.configuration.hpe3par_iscsi_chap_enabled:
            model_update['provider_auth'] = None
            return model_update

        # CHAP username will be the hostname
        chap_username = connector['host']

        chap_password = None
        try:
            # Get all active VLUNs for the host
            vluns = common.client.getHostVLUNs(chap_username)

            # Host has active VLUNs... is CHAP enabled on host?
            host_info = common.client.getHost(chap_username)

            if not host_info['initiatorChapEnabled']:
                LOG.warning(_LW("Host has no CHAP key, but CHAP is enabled."))

        except hpeexceptions.HTTPNotFound:
            chap_password = volume_utils.generate_password(16)
            LOG.warning(_LW("No host or VLUNs exist. Generating new "
                            "CHAP key."))
        else:
            # Get a list of all iSCSI VLUNs and see if there is already a CHAP
            # key assigned to one of them.  Use that CHAP key if present,
            # otherwise create a new one.  Skip any VLUNs that are missing
            # CHAP credentials in metadata.
            chap_exists = False
            active_vluns = 0

            for vlun in vluns:
                if not vlun['active']:
                    continue

                active_vluns += 1

                # iSCSI connections start with 'iqn'.
                if ('remoteName' in vlun and
                        re.match('iqn.*', vlun['remoteName'])):
                    try:
                        chap_password = common.client.getVolumeMetaData(
                            vlun['volumeName'], CHAP_PASS_KEY)['value']
                        chap_exists = True
                        break
                    except hpeexceptions.HTTPNotFound:
                        LOG.debug("The VLUN %s is missing CHAP credentials "
                                  "but CHAP is enabled. Skipping.",
                                  vlun['remoteName'])
                else:
                    LOG.warning(_LW("Non-iSCSI VLUN detected."))

            if not chap_exists:
                chap_password = volume_utils.generate_password(16)
                LOG.warning(_LW("No VLUN contained CHAP credentials. "
                                "Generating new CHAP key."))

        # Add CHAP credentials to the volume metadata
        vol_name = common._get_3par_vol_name(volume['id'])
        common.client.setVolumeMetaData(
            vol_name, CHAP_USER_KEY, chap_username)
        common.client.setVolumeMetaData(
            vol_name, CHAP_PASS_KEY, chap_password)

        model_update['provider_auth'] = ('CHAP %s %s' %
                                         (chap_username, chap_password))

        return model_update
    def initialize_connection(self, volume, connector):
        """Assigns the volume to a server.

        Assign any created volume to a compute node/host so that it can be
        used from that host.

        This driver returns a driver_volume_type of 'iscsi'.
        The format of the driver data is defined in _get_iscsi_properties.
        Example return value:

            {
                'driver_volume_type': 'iscsi'
                'data': {
                    'encrypted': False,
                    'target_discovered': True,
                    'target_iqn': 'iqn.2010-10.org.openstack:volume-00000001',
                    'target_protal': '127.0.0.1:3260',
                    'volume_id': 1,
                }
            }

        Steps to export a volume on 3PAR
          * Get the 3PAR iSCSI iqn
          * Create a host on the 3par
          * create vlun on the 3par
        """
        common = self._login()
        try:
            # we have to make sure we have a host
            host, username, password = self._create_host(
                common,
                volume,
                connector)

            if connector['multipath']:
                ready_ports = common.client.getiSCSIPorts(
                    state=common.client.PORT_STATE_READY)

                target_portals = []
                target_iqns = []
                target_luns = []

                # Target portal ips are defined in hpe.conf.
                target_portal_ips = self.iscsi_ips.keys()

                # Collect all existing VLUNs for this volume/host combination.
                existing_vluns = common.find_existing_vluns(volume, host)

                # Cycle through each ready iSCSI port and determine if a new
                # VLUN should be created or an existing one used.
                for port in ready_ports:
                    iscsi_ip = port['IPAddr']
                    if iscsi_ip in target_portal_ips:
                        vlun = None
                        # check for an already existing VLUN matching the
                        # nsp for this iSCSI IP. If one is found, use it
                        # instead of creating a new VLUN.
                        for v in existing_vluns:
                            portPos = common.build_portPos(
                                self.iscsi_ips[iscsi_ip]['nsp'])
                            if v['portPos'] == portPos:
                                vlun = v
                                break
                        else:
                            vlun = common.create_vlun(
                                volume, host, self.iscsi_ips[iscsi_ip]['nsp'])
                        iscsi_ip_port = "%s:%s" % (
                            iscsi_ip, self.iscsi_ips[iscsi_ip]['ip_port'])
                        target_portals.append(iscsi_ip_port)
                        target_iqns.append(port['iSCSIName'])
                        target_luns.append(vlun['lun'])
                    else:
                        LOG.warning(_LW("iSCSI IP: '%s' was not found in "
                                        "hpe3par_iscsi_ips list defined in "
                                        "hpe.conf."), iscsi_ip)

                info = {'driver_volume_type': 'iscsi',
                        'data': {'target_portals': target_portals,
                                 'target_iqns': target_iqns,
                                 'target_luns': target_luns,
                                 'target_discovered': True
                                 }
                        }
            else:
                least_used_nsp = None

                # check if a VLUN already exists for this host
                existing_vlun = common.find_existing_vlun(volume, host)

                if existing_vlun:
                    # We override the nsp here on purpose to force the
                    # volume to be exported out the same IP as it already is.
                    # This happens during nova live-migration, we want to
                    # disable the picking of a different IP that we export
                    # the volume to, or nova complains.
                    least_used_nsp = common.build_nsp(existing_vlun['portPos'])

                if not least_used_nsp:
                    least_used_nsp = self._get_least_used_nsp_for_host(
                        common,
                        host['name'])

                vlun = None
                if existing_vlun is None:
                    # now that we have a host, create the VLUN
                    vlun = common.create_vlun(volume, host, least_used_nsp)
                else:
                    vlun = existing_vlun

                if least_used_nsp is None:
                    LOG.warning(_LW("Least busy iSCSI port not found, "
                                    "using first iSCSI port in list."))
                    iscsi_ip = self.iscsi_ips.keys()[0]
                else:
                    iscsi_ip = self._get_ip_using_nsp(least_used_nsp)

                iscsi_ip_port = self.iscsi_ips[iscsi_ip]['ip_port']
                iscsi_target_iqn = self.iscsi_ips[iscsi_ip]['iqn']
                info = {'driver_volume_type': 'iscsi',
                        'data': {'target_portal': "%s:%s" %
                                 (iscsi_ip, iscsi_ip_port),
                                 'target_iqn': iscsi_target_iqn,
                                 'target_lun': vlun['lun'],
                                 'target_discovered': True
                                 }
                        }

            if self.configuration.hpe3par_iscsi_chap_enabled:
                info['data']['auth_method'] = 'CHAP'
                info['data']['auth_username'] = username
                info['data']['auth_password'] = password

            encryption_key_id = volume.get('encryption_key_id', None)
            info['data']['encrypted'] = encryption_key_id is not None

            return info
        finally:
            self._logout(common)
    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 delete_vlun(self, volume, hostname):
        volume_name = self._get_3par_vol_name(volume['id'])
        vluns = self.client.getHostVLUNs(hostname)

        # Find all the VLUNs associated with the volume. The VLUNs will then
        # be split into groups based on the active status of the VLUN. If there
        # are active VLUNs detected a delete will be attempted on them. If
        # there are no active VLUNs but there are inactive VLUNs, then the
        # inactive VLUNs will be deleted. The inactive VLUNs are the templates
        # on the 3PAR backend.
        active_volume_vluns = []
        inactive_volume_vluns = []
        volume_vluns = []

        for vlun in vluns:
            if volume_name in vlun['volumeName']:
                if vlun['active']:
                    active_volume_vluns.append(vlun)
                else:
                    inactive_volume_vluns.append(vlun)
        if active_volume_vluns:
            volume_vluns = active_volume_vluns
        elif inactive_volume_vluns:
            volume_vluns = inactive_volume_vluns

        if not volume_vluns:
            msg = (
                _LW("3PAR vlun for volume %(name)s not found on "
                    "host %(host)s"), {'name': volume_name, 'host': hostname})
            LOG.warning(msg)
            return

        # VLUN Type of MATCHED_SET 4 requires the port to be provided
        removed_luns = []
        for vlun in volume_vluns:
            if self.VLUN_TYPE_MATCHED_SET == vlun['type']:
                self.client.deleteVLUN(volume_name, vlun['lun'], hostname,
                                       vlun['portPos'])
            else:
                # This is HOST_SEES or a type that is not MATCHED_SET.
                # By deleting one VLUN, all the others should be deleted, too.
                if vlun['lun'] not in removed_luns:
                    self.client.deleteVLUN(volume_name, vlun['lun'], hostname)
                    removed_luns.append(vlun['lun'])

        # Determine if there are other volumes attached to the host.
        # This will determine whether we should try removing host from host set
        # and deleting the host.
        vluns = []
        try:
            vluns = self.client.getHostVLUNs(hostname)
        except hpeexceptions.HTTPNotFound:
            LOG.debug("All VLUNs removed from host %s", hostname)
            pass

        for vlun in vluns:
            if volume_name not in vlun['volumeName']:
                # Found another volume
                break
        else:
            # We deleted the last vlun, so try to delete the host too.
            # This check avoids the old unnecessary try/fail when vluns exist
            # but adds a minor race condition if a vlun is manually deleted
            # externally at precisely the wrong time. Worst case is leftover
            # host, so it is worth the unlikely risk.

            try:
                self._delete_3par_host(hostname)
            except Exception as ex:
                # Any exception down here is only logged.  The vlun is deleted.

                # If the host is in a host set, the delete host will fail and
                # the host will remain in the host set.  This is desired
                # because docker was not responsible for the host set
                # assignment.  The host set could be used outside of docker
                # for future needs (e.g. export volume to host set).

                # The log info explains why the host was left alone.
                LOG.info(_LI("3PAR vlun for volume '%(name)s' was deleted, "
                             "but the host '%(host)s' was not deleted "
                             "because: %(reason)s"),
                         {'name': volume_name, 'host': hostname,
                          'reason': ex.get_description()})
Exemplo n.º 14
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.º 15
0
    def delete_vlun(self, volume, hostname):
        volume_name = self._get_3par_vol_name(volume['id'])
        vluns = self.client.getHostVLUNs(hostname)

        # Find all the VLUNs associated with the volume. The VLUNs will then
        # be split into groups based on the active status of the VLUN. If there
        # are active VLUNs detected a delete will be attempted on them. If
        # there are no active VLUNs but there are inactive VLUNs, then the
        # inactive VLUNs will be deleted. The inactive VLUNs are the templates
        # on the 3PAR backend.
        active_volume_vluns = []
        inactive_volume_vluns = []
        volume_vluns = []

        for vlun in vluns:
            if volume_name in vlun['volumeName']:
                if vlun['active']:
                    active_volume_vluns.append(vlun)
                else:
                    inactive_volume_vluns.append(vlun)
        if active_volume_vluns:
            volume_vluns = active_volume_vluns
        elif inactive_volume_vluns:
            volume_vluns = inactive_volume_vluns

        if not volume_vluns:
            msg = (_LW("3PAR vlun for volume %(name)s not found on "
                       "host %(host)s"), {
                           'name': volume_name,
                           'host': hostname
                       })
            LOG.warning(msg)
            return

        # VLUN Type of MATCHED_SET 4 requires the port to be provided
        removed_luns = []
        for vlun in volume_vluns:
            if self.VLUN_TYPE_MATCHED_SET == vlun['type']:
                self.client.deleteVLUN(volume_name, vlun['lun'], hostname,
                                       vlun['portPos'])
            else:
                # This is HOST_SEES or a type that is not MATCHED_SET.
                # By deleting one VLUN, all the others should be deleted, too.
                if vlun['lun'] not in removed_luns:
                    self.client.deleteVLUN(volume_name, vlun['lun'], hostname)
                    removed_luns.append(vlun['lun'])

        # Determine if there are other volumes attached to the host.
        # This will determine whether we should try removing host from host set
        # and deleting the host.
        vluns = []
        try:
            vluns = self.client.getHostVLUNs(hostname)
        except hpeexceptions.HTTPNotFound:
            LOG.debug("All VLUNs removed from host %s", hostname)
            pass

        for vlun in vluns:
            if volume_name not in vlun['volumeName']:
                # Found another volume
                break
        else:
            # We deleted the last vlun, so try to delete the host too.
            # This check avoids the old unnecessary try/fail when vluns exist
            # but adds a minor race condition if a vlun is manually deleted
            # externally at precisely the wrong time. Worst case is leftover
            # host, so it is worth the unlikely risk.

            try:
                self._delete_3par_host(hostname)
            except Exception as ex:
                # Any exception down here is only logged.  The vlun is deleted.

                # If the host is in a host set, the delete host will fail and
                # the host will remain in the host set.  This is desired
                # because docker was not responsible for the host set
                # assignment.  The host set could be used outside of docker
                # for future needs (e.g. export volume to host set).

                # The log info explains why the host was left alone.
                LOG.info(
                    _LI("3PAR vlun for volume '%(name)s' was deleted, "
                        "but the host '%(host)s' was not deleted "
                        "because: %(reason)s"), {
                            'name': volume_name,
                            'host': hostname,
                            'reason': ex.get_description()
                        })