Ejemplo n.º 1
0
    def _get_ips_iqns_luns(self, connection_properties, discover=True):
        """Build a list of ips, iqns, and luns.

        Used only when doing multipath, we have 3 cases:

        - All information is in the connection properties
        - We have to do an iSCSI discovery to get the information
        - We don't want to do another discovery and we query the discoverydb

        :param connection_properties: The dictionary that describes all
                                      of the target volume attributes.
        :type connection_properties: dict
        :param discover: Wheter doing an iSCSI discovery is acceptable.
        :type discover: bool
        :returns: list of tuples of (ip, iqn, lun)
        """
        try:
            if ('target_portals' in connection_properties and
                    'target_iqns' in connection_properties):
                # Use targets specified by connection_properties
                ips_iqns_luns = list(
                    zip(connection_properties['target_portals'],
                        connection_properties['target_iqns'],
                        self._get_luns(connection_properties)))
            else:
                method = (self._discover_iscsi_portals if discover
                          else self._get_discoverydb_portals)
                ips_iqns_luns = method(connection_properties)
        except exception.TargetPortalsNotFound:
            raise
        except Exception:
            if 'target_portals' in connection_properties:
                raise exception.TargetPortalsNotFound(
                    target_portals=connection_properties['target_portals'])
            if 'target_portal' in connection_properties:
                raise exception.TargetPortalNotFound(
                    target_portal=connection_properties['target_portal'])
            raise

        if not connection_properties.get('target_iqns'):
            # There are two types of iSCSI multipath devices. One which
            # shares the same iqn between multiple portals, and the other
            # which use different iqns on different portals.
            # Try to identify the type by checking the iscsiadm output
            # if the iqn is used by multiple portals. If it is, it's
            # the former, so use the supplied iqn. Otherwise, it's the
            # latter, so try the ip,iqn combinations to find the targets
            # which constitutes the multipath device.
            main_iqn = connection_properties['target_iqn']
            all_portals = {(ip, lun) for ip, iqn, lun in ips_iqns_luns}
            match_portals = {(ip, lun) for ip, iqn, lun in ips_iqns_luns
                             if iqn == main_iqn}
            if len(all_portals) == len(match_portals):
                ips_iqns_luns = [(p[0], main_iqn, p[1])
                                 for p in all_portals]

        return ips_iqns_luns
Ejemplo n.º 2
0
    def _get_potential_volume_paths(self,
                                    connection_properties,
                                    connect_to_portal=True,
                                    use_rescan=True):
        """Build a list of potential volume paths that exist.

        Given a list of target_portals in the connection_properties,
        a list of paths might exist on the system during discovery.
        This method's job is to build that list of potential paths
        for a volume that might show up.

        This is used during connect_volume time, in which case we want
        to connect to the iSCSI target portal.

        During get_volume_paths time, we are looking to
        find a list of existing volume paths for the connection_properties.
        In this case, we don't want to connect to the portal.  If we
        blindly try and connect to a portal, it could create a new iSCSI
        session that didn't exist previously, and then leave it stale.

        :param connection_properties: The dictionary that describes all
                                      of the target volume attributes.
        :type connection_properties: dict
        :param connect_to_portal: Do we want to try a new connection to the
                                  target portal(s)?  Set this to False if you
                                  want to search for existing volumes, not
                                  discover new volumes.
        :param connect_to_portal: bool
        :param use_rescan: Issue iSCSI rescan during discovery?
        :type use_rescan: bool
        :returns: dict
        """

        target_props = None
        connected_to_portal = False
        if self.use_multipath:
            LOG.info("Multipath discovery for iSCSI enabled")
            # Multipath installed, discovering other targets if available
            try:
                ips_iqns_luns = self._discover_iscsi_portals(
                    connection_properties)
            except Exception:
                if 'target_portals' in connection_properties:
                    raise exception.TargetPortalsNotFound(
                        target_portals=connection_properties['target_portals'])
                elif 'target_portal' in connection_properties:
                    raise exception.TargetPortalNotFound(
                        target_portal=connection_properties['target_portal'])
                else:
                    raise

            if not connection_properties.get('target_iqns'):
                # There are two types of iSCSI multipath devices. One which
                # shares the same iqn between multiple portals, and the other
                # which use different iqns on different portals.
                # Try to identify the type by checking the iscsiadm output
                # if the iqn is used by multiple portals. If it is, it's
                # the former, so use the supplied iqn. Otherwise, it's the
                # latter, so try the ip,iqn combinations to find the targets
                # which constitutes the multipath device.
                main_iqn = connection_properties['target_iqn']
                all_portals = {(ip, lun) for ip, iqn, lun in ips_iqns_luns}
                match_portals = {(ip, lun)
                                 for ip, iqn, lun in ips_iqns_luns
                                 if iqn == main_iqn}
                if len(all_portals) == len(match_portals):
                    ips_iqns_luns = [(p[0], main_iqn, p[1])
                                     for p in all_portals]

            for ip, iqn, lun in ips_iqns_luns:
                props = copy.deepcopy(connection_properties)
                props['target_portal'] = ip
                props['target_iqn'] = iqn
                if connect_to_portal:
                    if self._connect_to_iscsi_portal(props):
                        connected_to_portal = True

            if use_rescan:
                self._rescan_iscsi(ips_iqns_luns)

            host_devices = self._get_device_path(connection_properties)
        else:
            LOG.info("Multipath discovery for iSCSI not enabled.")
            iscsi_sessions = []
            if not connect_to_portal:
                iscsi_sessions = self._get_iscsi_sessions()

            host_devices = []
            target_props = connection_properties
            for props in self._iterate_all_targets(connection_properties):
                if connect_to_portal:
                    if self._connect_to_iscsi_portal(props):
                        target_props = props
                        connected_to_portal = True
                        host_devices = self._get_device_path(props)
                        break
                    else:
                        LOG.warning(
                            'Failed to connect to iSCSI portal %(portal)s.',
                            {'portal': props['target_portal']})
                else:
                    # If we aren't trying to connect to the portal, we
                    # want to find ALL possible paths from all of the
                    # alternate portals
                    if props['target_portal'] in iscsi_sessions:
                        paths = self._get_device_path(props)
                        host_devices = list(set(paths + host_devices))

        if connect_to_portal and not connected_to_portal:
            msg = _("Could not login to any iSCSI portal.")
            LOG.error(msg)
            raise exception.FailedISCSITargetPortalLogin(message=msg)

        return host_devices, target_props
Ejemplo n.º 3
0
    def _get_discoverydb_portals(self, connection_properties):
        """Retrieve iscsi portals information from the discoverydb.

        Example of discoverydb command output:

        SENDTARGETS:
        DiscoveryAddress: 192.168.1.33,3260
        DiscoveryAddress: 192.168.1.2,3260
        Target: iqn.2004-04.com.qnap:ts-831x:iscsi.cinder-20170531114245.9eff88
            Portal: 192.168.1.3:3260,1
                Iface Name: default
            Portal: 192.168.1.2:3260,1
                Iface Name: default
        Target: iqn.2004-04.com.qnap:ts-831x:iscsi.cinder-20170531114447.9eff88
            Portal: 192.168.1.3:3260,1
                Iface Name: default
            Portal: 192.168.1.2:3260,1
                Iface Name: default
        DiscoveryAddress: 192.168.1.38,3260
        iSNS:
        No targets found.
        STATIC:
        No targets found.
        FIRMWARE:
        No targets found.

        :param connection_properties: The dictionary that describes all
                                      of the target volume attributes.
        :type connection_properties: dict
        :returns: list of tuples of (ip, iqn, lun)
        """
        ip, port = connection_properties['target_portal'].rsplit(':', 1)
        # NOTE(geguileo): I don't know if IPv6 will be reported with []
        # or not, so we'll make them optional.
        ip = ip.replace('[', '\[?').replace(']', '\]?')
        out = self._run_iscsiadm_bare(['-m', 'discoverydb',
                                       '-o', 'show',
                                       '-P', 1])[0] or ""
        regex = ''.join(('^SENDTARGETS:\n.*?^DiscoveryAddress: ',
                         ip, ',', port,
                         '.*?\n(.*?)^(?:DiscoveryAddress|iSNS):.*'))
        LOG.debug('Regex to get portals from discoverydb: %s', regex)

        info = re.search(regex, out, re.DOTALL | re.MULTILINE)

        ips = []
        iqns = []

        if info:
            iscsi_transport = ('iser' if self._get_transport() == 'iser'
                               else 'default')
            iface = 'Iface Name: ' + iscsi_transport
            current_iqn = ''
            current_ip = ''
            for line in info.group(1).splitlines():
                line = line.strip()
                if line.startswith('Target:'):
                    current_iqn = line[8:]
                elif line.startswith('Portal:'):
                    current_ip = line[8:].split(',')[0]
                elif line.startswith(iface):
                    if current_iqn and current_ip:
                        iqns.append(current_iqn)
                        ips.append(current_ip)
                    current_ip = ''

        if not iqns:
            raise exception.TargetPortalsNotFound(
                _('Unable to find target portals information on discoverydb.'))

        luns = self._get_luns(connection_properties, iqns)
        return list(zip(ips, iqns, luns))