Exemple #1
0
    def map_luns(self, config):
        """
        LIO will have blockstorage objects already defined by the igw_lun module, so this
        method, brings those objects into the gateways TPG
        """

        lio_root = root.RTSRoot()

        # process each storage object added to the gateway, and map to the tpg
        for stg_object in lio_root.storage_objects:

            for tpg in self.tpg_list:

                if not self.lun_mapped(tpg, stg_object):

                    # use the iblock number for the lun id - /sys/kernel/config/target/core/iblock_1/ansible4
                    #                                                                              ^
                    lun_id = int(stg_object._path.split('/')[-2].split('_')[1])

                    try:
                        mapped_lun = LUN(tpg,
                                         lun=lun_id,
                                         storage_object=stg_object)
                        self.changes_made = True
                    except RTSLibError as err:
                        self.error = True
                        self.error_msg = err
                        break

                    self.bind_alua_group_to_lun(config, mapped_lun)
Exemple #2
0
    def remove_dev_from_lio(self):
        lio_root = root.RTSRoot()

        # remove the device from all tpgs
        for t in lio_root.tpgs:
            for lun in t.luns:
                if lun.storage_object.name == self.backstore_object_name:
                    try:
                        lun.delete()
                    except Exception as e:
                        self.error = True
                        self.error_msg = ("Delete from LIO/TPG failed - "
                                          "{}".format(e))
                        return
                    else:
                        break  # continue to the next tpg

        so = self.lio_stg_object()
        if not so:
            self.error = True
            self.error_msg = ("Removal failed. Could not find LIO object.")
            return

        try:
            so.delete()
        except Exception as err:
            self.error = True
            self.error_msg = ("Delete from LIO/backstores failed - "
                              "{}".format(err))
            return
Exemple #3
0
    def define_client(self):
        """
        Establish the links for this object to the corresponding ACL and TPG objects from LIO
        :return:
        """

        r = lio_root.RTSRoot()

        # NB. this will check all tpg's for a matching iqn
        for client in r.node_acls:
            if client.node_wwn == self.iqn:
                self.acl = client
                self.tpg = client.parent_tpg
                logger.debug("(Client.define_client) - {} already defined".format(self.iqn))
                return

        # at this point the client does not exist, so create it
        # NB. The solution supports only a single tpg definition, so simply grabbing the
        # first tpg is fine. If multiple tpgs are required this will need more work
        self.tpg = r.tpgs.next()

        try:
            self.acl = NodeACL(self.tpg, self.iqn)
        except RTSLibError as err:
            logger.error("(Client.define_client) FAILED to define {}".format(self.iqn))
            logger.debug("(Client.define_client) failure msg {}".format(err))
            self.error = True
            self.error_msg = err
        else:
            self.change_count += 1
            logger.info("(Client.define_client) {} added successfully".format(self.iqn))
Exemple #4
0
    def _get_mapped_disks(self):
        '''
        return a dict indexed by a pool/image name that points to the client
        that has this device mapped to it
        :return: dict
        '''

        map = {}
        pools = {}
        lio_root = root.RTSRoot()

        # get a list of active sessions on this host indexed by the iqn
        connections = {}
        for con in lio_root.sessions:
            nodeacl = con['parent_nodeacl']
            connections[nodeacl.node_wwn] = con['state']

        for tpg in lio_root.tpgs:
            if tpg._get_enable():
                # this tpg is enabled, so let's walk the luns
                # and process all luns mapped to this tpg
                for lun in tpg.luns:

                    dm_id = os.path.basename(lun.storage_object.udev_path)
                    dm_num = dm_id.split('-')[0]

                    # dev_path = m_lun.storage_object.path
                    # '/sys/kernel/config/target/core/iblock_0/ansible3'
                    # iblock_name = dev_path.split('/')[-2]

                    image_name = lun.storage_object.name
                    if dm_num in pools:
                        pool_name = pools[dm_num]
                    else:
                        pools[dm_num] = get_pool_name(pool_id=int(dm_num))
                        pool_name = pools[dm_num]

                    key = "{}/{}".format(pool_name, image_name)

                    # udev_path = m_lun.storage_object.udev_path
                    # dev_id = get_devid(udev_path)

                    client_iqns = []

                    for mapping in lun.mapped_luns:
                        client_iqns.append(mapping.node_wwn)

                    suffix = ''
                    client_shortname = ''

                    if len(client_iqns) == 1:
                        client_shortname = client_iqns[0].split(':')[-1]
                        suffix = '(CON)' if client_iqns[
                            0] in connections else ''
                    elif len(client_iqns) > 1:
                        client_shortname = '- multi -'

                    map[key] = client_shortname + suffix

        return map
Exemple #5
0
    def load_config(self):
        """
        Grab the target, tpg and portal objects from LIO and store in this
        Gateway object
        """

        try:

            lio_root = root.RTSRoot()
            self.target = [
                tgt for tgt in lio_root.targets if tgt.wwn == self.iqn
            ][0]

            self.tpg_list = []
            # there could/should be multiple tpg's for the target
            for tpg in self.target.tpgs:
                self.tpg_list.append(tpg)

            # self.portal = self.tpg.network_portals.next()

        except RTSLibError as err:
            self.error_msg = err
            self.error = True

        self.logger.info("(Gateway.load_config) successfully loaded existing "
                         "target definition")
Exemple #6
0
    def logged_in(self):

        r = root.RTSRoot()
        for sess in r.sessions:
            if sess['parent_nodeacl'].node_wwn == self.client_iqn:
                return sess['state']
        return ''
    def map_luns(self, config):
        """
        LIO will have objects already defined by the lun module,
        so this method, brings those objects into the gateways TPG
        """

        lio_root = root.RTSRoot()

        # process each storage object added to the gateway, and map to the tpg
        for stg_object in lio_root.storage_objects:

            for tpg in self.tpg_list:
                self.logger.debug("processing tpg{}".format(tpg.tag))

                if not self.lun_mapped(tpg, stg_object):
                    self.logger.debug("{} needed mapping to "
                                      "tpg{}".format(stg_object.name,
                                                     tpg.tag))

                    lun_id = int(stg_object.path.split('/')[-2].split('_')[1])

                    try:
                        mapped_lun = LUN(tpg, lun=lun_id, storage_object=stg_object)
                        self.changes_made = True
                    except RTSLibError as err:
                        self.logger.error("LUN mapping failed: {}".format(err))
                        self.error = True
                        self.error_msg = err
                        return

                    self.bind_alua_group_to_lun(config, mapped_lun)
def get_tpgs():
    """
    determine the number of tpgs in the current LIO environment
    :return: count of the defined tpgs
    """

    return len([tpg.tag for tpg in root.RTSRoot().tpgs])
    def clear_config(self):
        """
        Remove the target definition form LIO
        :return: None
        """
        # check that there aren't any disks or clients in the configuration
        lio_root = root.RTSRoot()

        disk_count = len([disk for disk in lio_root.storage_objects])
        clients = []
        for tpg in self.tpg_list:
            tpg_clients = [node for node in tpg._list_node_acls()]
            clients += tpg_clients
        client_count = len(clients)

        if disk_count > 0 or client_count > 0:
            self.error = True
            self.error_msg = ("Clients({}) and disks({}) must be removed"
                              "before the gateways".format(client_count,
                                                           disk_count))
            return

        self.logger.debug("Clients defined :{}".format(client_count))
        self.logger.debug("Disks defined :{}".format(disk_count))
        self.logger.info("Removing target configuration")

        try:
            self.delete()
        except RTSLibError as err:
            self.error = True
            self.error_msg = "Unable to delete target - {}".format(err)
Exemple #10
0
    def remove_dev_from_lio(self):
        lio_root = root.RTSRoot()

        # remove the device from all tpgs
        for t in lio_root.tpgs:
            for lun in t.luns:
                if lun.storage_object.name == self.config_key:
                    try:
                        lun.delete()
                    except Exception as e:
                        self.error = True
                        self.error_msg = ("Delete from LIO/TPG failed - "
                                          "{}".format(e))
                        return
                    else:
                        break  # continue to the next tpg

        for stg_object in lio_root.storage_objects:
            if stg_object.name == self.config_key:
                try:
                    stg_object.delete()
                except Exception as e:
                    self.error = True
                    self.error_msg = ("Delete from LIO/backstores failed - "
                                      "{}".format(e))
                    return

                break
Exemple #11
0
    def drop_target(self, this_host):
        iqn = self.config.config['gateways'][this_host]['iqn']

        lio_root = root.RTSRoot()
        for tgt in lio_root.targets:
            if tgt.wwn == iqn:
                tgt.delete()
                self.changed = True
Exemple #12
0
    def exists(self):
        """
        This function determines whether this instances iqn is already defined to LIO
        :return: Boolean
        """

        r = lio_root.RTSRoot()
        client_list = [client.node_wwn for client in r.node_acls]
        return self.iqn in client_list
Exemple #13
0
    def drop_target(self, this_host):
        if this_host in self.config.config['gateways']:

            lio_root = root.RTSRoot()
            for tgt in lio_root.targets:
                if tgt.wwn in self.config.config['targets'] \
                        and this_host in self.config.config['targets'][tgt.wwn]['portals']:
                    tgt.delete()
                    self.changed = True
Exemple #14
0
def lun_in_lio(image):
    found_it = False
    rtsroot = root.RTSRoot()
    for stg_object in rtsroot.storage_objects:
        if stg_object.name == image:
            found_it = True
            break

    return stg_object if found_it else None
Exemple #15
0
    def drop_target(self, this_host):
        iqn = self.config.config['gateways'][this_host]['iqn']

        lio_root = root.RTSRoot()
        for tgt in lio_root.targets:
            if tgt.wwn == iqn:
                tgt.delete()
                self.changed = True
                # remove the gateway from the config dict
                self.config.del_item('gateways', this_host)
Exemple #16
0
    def _get_mapped_disks(self):
        '''
        return a dict indexed by a pool/image name that points to the client
        that has this device mapped to it. If the client mapped is currently
        connected the name used is the alias (dns) of the client from LIO
        session information - if not, we just use the last qualifier of the
        iqn
        :return: dict <pool>.<image> --> <client_name> | '- multi -'
        '''

        map = {}
        pools = {}
        lio_root = root.RTSRoot()

        # get a list of active sessions on this host indexed by the iqn
        connections = {}
        for con in lio_root.sessions:
            nodeacl = con['parent_nodeacl']
            connections[nodeacl.node_wwn] = {
                "state": con['state'],
                "alias": con['alias'].split('.')[0]
            }

        # seed the map dict with an entry for each storage object
        for so in lio_root.storage_objects:
            map[so.name] = ''

        # process each client
        for node in lio_root.node_acls:

            # for each client, look at it's luns
            for m_lun in node.mapped_luns:

                tpg_lun = m_lun._get_tpg_lun()
                disk_name = tpg_lun.storage_object.name

                if map[disk_name]:
                    map[disk_name] = '- multi -'
                else:
                    # if this node is connected, try and use it's alias
                    if node.node_wwn in connections:
                        suffix = "(CON)"
                        alias_name = connections[node.node_wwn]["alias"]
                        if alias_name:
                            map[disk_name] = "{}(CON)".format(alias_name)
                        else:
                            map[disk_name] = "{}(CON)".format(
                                node.node_wwn.split(':')[-1])
                    else:
                        map[disk_name] = "{}".format(
                            node.node_wwn.split(':')[-1])

        return map
Exemple #17
0
    def lun_in_lio(self):
        found_it = False
        rtsroot = root.RTSRoot()
        for stg_object in rtsroot.storage_objects:

            # First match on name, but then check the pool incase the same name exists in multiple pools
            if stg_object.name == self.config_key:

                found_it = True
                break

        return stg_object if found_it else None
Exemple #18
0
    def map_luns(self, config):
        """
        LIO will have objects already defined by the lun module,
        so this method, brings those objects into the gateways TPG
        """

        lio_root = root.RTSRoot()
        target_config = config.config["targets"][self.iqn]
        target_stg_object = [
            stg_object for stg_object in lio_root.storage_objects
            if stg_object.name in target_config['disks']
        ]

        # a dict, key with tpg and storage object name
        tpg_stg_object_dict = {}
        for tpg in self.tpg_list:

            for l in tpg.luns:
                key = str(tpg.tag) + "-" + l.storage_object.name
                tpg_stg_object_dict[key] = l.storage_object.name

        # process each storage object added to the gateway, and map to the tpg
        for stg_object in target_stg_object:

            for tpg in self.tpg_list:
                self.logger.debug("processing tpg{}".format(tpg.tag))

                if str(tpg.tag
                       ) + "-" + stg_object.name not in tpg_stg_object_dict:
                    self.logger.debug("{} needed mapping to "
                                      "tpg{}".format(stg_object.name, tpg.tag))

                    lun_id = int(stg_object.path.split('/')[-2].split('_')[1])

                    try:
                        mapped_lun = LUN(tpg,
                                         lun=lun_id,
                                         storage_object=stg_object)
                        self.changes_made = True
                    except RTSLibError as err:
                        self.logger.error("LUN mapping failed: {}".format(err))
                        self.error = True
                        self.error_msg = err
                        return

                    try:
                        self.bind_alua_group_to_lun(config, mapped_lun)
                    except CephiSCSIInval as err:
                        self.logger.error("Could not bind LUN to ALUA group: "
                                          "{}".format(err))
                        self.error = True
                        self.error_msg = err
                        return
Exemple #19
0
    def ui_command_create(self, target_iqn):
        """
        Create an iSCSI target. This target is defined across all gateway nodes,
        providing the client with a single 'image' for iscsi discovery.

        Only ONE iSCSI target is supported, at this time.
        """

        self.logger.debug("CMD: /iscsi create {}".format(target_iqn))

        defined_targets = [tgt.name for tgt in self.children]
        if len(defined_targets) > 0:
            self.logger.error("Only ONE iscsi target image is supported")
            return

        # We need LIO to be empty, so check there aren't any targets defined
        local_lio = root.RTSRoot()
        current_target_names = [tgt.wwn for tgt in local_lio.targets]
        if current_target_names:
            self.logger.error("Local LIO instance already has LIO configured "
                              "with a target - unable to continue")
            return

        # OK - this request is valid, but is the IQN usable?
        if not valid_iqn(target_iqn):
            self.logger.error("IQN name '{}' is not valid for "
                              "iSCSI".format(target_iqn))
            return


        # 'safe' to continue with the definition
        self.logger.debug("Create an iscsi target definition in the UI")

        local_api = ('{}://127.0.0.1:{}/api/'
                     'target/{}'.format(self.http_mode,
                                        settings.config.api_port,
                                        target_iqn))

        api = APIRequest(local_api)
        api.put()

        if api.response.status_code == 200:
            self.logger.info('ok')
            # create the target entry in the UI tree
            Target(target_iqn, self)
        else:
            self.logger.error("Failed to create the target on the local node")

            raise GatewayAPIError("iSCSI target creation failed - "
                                  "{}".format(response_message(api.response,
                                                               self.logger)))
Exemple #20
0
    def define_client(self):
        """
        Establish the links for this object to the corresponding ACL and TPG
        objects from LIO
        :return:
        """

        r = lio_root.RTSRoot()

        # NB. this will check all tpg's for a matching iqn
        for client in r.node_acls:
            if client.node_wwn == self.iqn:
                self.acl = client
                self.tpg = client.parent_tpg
                self.logger.debug("(Client.define_client) - {} already "
                                  "defined".format(self.iqn))
                return

        # at this point the client does not exist, so create it
        # The configuration only has one active tpg, so pick that one for any
        # acl definitions
        for tpg in r.tpgs:
            if tpg.enable:
                self.tpg = tpg

        try:
            self.acl = NodeACL(self.tpg, self.iqn)
            # Try to detect network problems so we can kill connections
            # and cleanup before the initiator has begun recovery and
            # failed over.
            self.acl.set_attribute('dataout_timeout', '20')  # default  3
            # LIO default 30
            self.acl.set_attribute(
                'nopin_response_timeout',
                '{}'.format(settings.config.nopin_response_timeout))
            # LIO default 15
            self.acl.set_attribute('nopin_timeout',
                                   '{}'.format(settings.config.nopin_timeout))
        except RTSLibError as err:
            self.logger.error("(Client.define_client) FAILED to define "
                              "{}".format(self.iqn))
            self.logger.debug("(Client.define_client) failure msg "
                              "{}".format(err))
            self.error = True
            self.error_msg = err
        else:
            self.logger.info("(Client.define_client) {} added "
                             "successfully".format(self.iqn))
            self.change_count += 1
Exemple #21
0
def get_lio_devices():
    """
    LIO uses the kernel's configfs feature to store and manage configuration
    data, so use rtslib to get a list of the devices

    :return: dict of dicts describing the rbd devices mapped to LIO
    """

    device_data = {}
    pools = {}

    lio_root = root.RTSRoot()
    # iterate over all luns - this will pick up the same lun mapped to
    # multiple tpgs - so we look out for that
    for lun in lio_root.luns:

        dm_id = os.path.basename(lun.storage_object.udev_path)
        dm_num = dm_id.split('-')[0]

        dev_path = lun.storage_object.path
        # '/sys/kernel/config/target/core/iblock_0/ansible3'
        iblock_name = dev_path.split('/')[-2]

        image_name = lun.storage_object.name
        if dm_num in pools:
            pool_name = pools[dm_num]
        else:
            pools[dm_num] = get_pool_name(pool_id=int(dm_num))
            pool_name = pools[dm_num]

        if '.' in image_name:
            # assume new format names that have the pool name in already
            key = image_name
        else:
            key = "{}.{}".format(pool_name, image_name)

        if key not in device_data:

            rbd_name = 'rbd{}'.format(iblock_name.split('_')[1])
            image_size = lun.storage_object.size
            wwn = lun.storage_object.wwn

            device_data[key] = {"size": image_size,
                                "wwn": wwn,
                                "rbd_name": rbd_name,
                                "pool": pool_name,
                                "image_name": image_name}

    return device_data
Exemple #22
0
    def define_client(self):
        """
        Establish the links for this object to the corresponding ACL and TPG
        objects from LIO
        :return:
        """

        r = lio_root.RTSRoot()

        # NB. this will check all tpg's for a matching iqn
        for client in r.node_acls:
            if client.node_wwn == self.iqn:
                self.acl = client
                self.tpg = client.parent_tpg
                try:
                    self.update_acl_controls()
                except RTSLibError as err:
                    self.logger.error(
                        "(Client.define_client) FAILED to update "
                        "{}".format(self.iqn))
                    self.error = True
                    self.error_msg = err
                self.logger.debug("(Client.define_client) - {} already "
                                  "defined".format(self.iqn))
                return

        # at this point the client does not exist, so create it
        # The configuration only has one active tpg, so pick that one for any
        # acl definitions
        for tpg in r.tpgs:
            if tpg.enable:
                self.tpg = tpg

        try:
            self.acl = NodeACL(self.tpg, self.iqn)
            self.update_acl_controls()
        except RTSLibError as err:
            self.logger.error("(Client.define_client) FAILED to define "
                              "{}".format(self.iqn))
            self.logger.debug("(Client.define_client) failure msg "
                              "{}".format(err))
            self.error = True
            self.error_msg = err
        else:
            self.logger.info("(Client.define_client) {} added "
                             "successfully".format(self.iqn))
            self.change_count += 1
Exemple #23
0
    def logged_in(self):

        r = root.RTSRoot()
        for sess in r.sessions:
            if sess['parent_nodeacl'].node_wwn == self.client_iqn:
                self.alias = sess.get('alias')
                state = sess.get('state').upper()
                ips = set()
                if state == 'LOGGED_IN':
                    for conn in sess.get('connections'):
                        ips.add(conn.get('address'))
                    self.ip_address = ','.join(list(ips))
                else:
                    self.ip_address = ''

                return state
        return ''
Exemple #24
0
    def remove_dev_from_lio(self):
        lio_root = root.RTSRoot()

        # remove the device from all tpgs
        for t in lio_root.tpgs:
            for lun in t.luns:
                if lun.storage_object.name == self.config_key:
                    try:
                        lun.delete()
                    except RTSLibError as e:
                        self.error = True
                        self.error_msg = "Delete from LIO/TPG failed - {}".format(
                            e)
                        return
                    else:
                        break  # continue to the next tpg

        for stg_object in lio_root.storage_objects:
            if stg_object.name == self.config_key:

                alua_dir = os.path.join(stg_object.path, "alua")

                # remove the alua directories (future versions will handle this
                # natively within rtslib_fb
                for dirname in next(os.walk(alua_dir))[1]:
                    if dirname != "default_tg_pt_gp":
                        try:
                            alua_tpg = ALUATargetPortGroup(stg_object, dirname)
                            alua_tpg.delete()
                        except (RTSLibError, RTSLibNotInCFS) as err:
                            self.error = True
                            self.error_msg = "Delete of ALUA directories failed - {}".format(
                                err)
                            return

                try:
                    stg_object.delete()
                except RTSLibError as e:
                    self.error = True
                    self.error_msg = "Delete from LIO/backstores failed - {}".format(
                        e)
                    return

                break
Exemple #25
0
    def load_config(self):
        """
        Grab the target, tpg and portal objects from LIO and store in this Gateway object
        """

        try:
            # since we only support one target/TPG, we just grab the first iterable
            lio_root = root.RTSRoot()
            self.target = lio_root.targets.next()
            self.tpg = self.target.tpgs.next()
            self.portal = self.tpg.network_portals.next()

        except RTSLibError as err:
            self.error_msg = err
            self.error = True

        logger.info(
            "(Gateway.load_config) successfully loaded existing target definition"
        )
Exemple #26
0
    def load_config(self):
        """
        Grab the target, tpg and portal objects from LIO and store in this Gateway object
        """

        try:

            lio_root = root.RTSRoot()
            # since we only support one target, we just grab the first iterable
            self.target = lio_root.targets.next()
            # but there could/should be multiple tpg's for the target
            for tpg in self.target.tpgs:
                self.tpg_list.append(tpg)

            # self.portal = self.tpg.network_portals.next()

        except RTSLibError as err:
            self.error_msg = err
            self.error = True

        self.logger.info(
            "(Gateway.load_config) successfully loaded existing target definition"
        )
Exemple #27
0
 def __init__(self):
     self.lio_root = root.RTSRoot()
     self.error = False
     self.error_msg = ''
     self.changed = False
Exemple #28
0
 def _get_client_count(self):
     r = root.RTSRoot()
     nodeACL_list = [client.node_wwn for client in r.node_acls]
     return len(nodeACL_list)
Exemple #29
0
def valid_client(**kwargs):
    """
    validate a client create or update request, based on mode.
    :param kwargs: 'mode' is the key field used to determine process flow
    :return: 'ok' or an error description (str)
    """

    valid_modes = ['create', 'delete', 'auth', 'disk']
    parms_passed = set(kwargs.keys())

    if 'mode' in kwargs:
        if kwargs['mode'] not in valid_modes:
            return ("Invalid client validation mode request - "
                    "asked for {}, available {}".format(kwargs['mode'],
                                                        valid_modes))
    else:
        return "Invalid call to valid_client - mode is needed"

    # at this point we have a mode to work with

    mode = kwargs['mode']
    client_iqn = kwargs['client_iqn']
    config = get_config()
    if not config:
        return "Unable to query the local API for the current config"

    if mode == 'create':
        # iqn must be valid
        if not valid_iqn(client_iqn):
            return ("Invalid IQN name for iSCSI")

        # iqn must not already exist
        if client_iqn in config['clients']:
            return ("A client with the name '{}' is "
                    "already defined".format(client_iqn))

        # Creates can only be done with a minimum number of gw's in place
        num_gws = len([gw_name for gw_name in config['gateways']
                       if isinstance(config['gateways'][gw_name], dict)])
        if num_gws < settings.config.minimum_gateways:
            return ("Clients can not be defined until a HA configuration "
                    "has been defined "
                    "(>{} gateways)".format(settings.config.minimum_gateways))

        # at this point pre-req's look good
        return 'ok'

    elif mode == 'delete':

        # client must exist in the configuration
        if client_iqn not in config['clients']:
            return ("{} is not defined yet - nothing to "
                    "delete".format(client_iqn))

        this_client = config['clients'].get(client_iqn)
        if this_client.get('group_name', None):
            return ("Unable to delete '{}' - it belongs to "
                    "group {}".format(client_iqn,
                                      this_client.get('group_name')))

        # client to delete must not be logged in - we're just checking locally,
        # since *all* nodes are set up the same, and a client login request
        # would normally login to each gateway
        lio_root = root.RTSRoot()
        clients_logged_in = [session['parent_nodeacl'].node_wwn
                             for session in lio_root.sessions
                             if session['state'] == 'LOGGED_IN']

        if client_iqn in clients_logged_in:
            return ("Client '{}' is logged in - unable to delete until"
                    " it's logged out".format(client_iqn))

        # at this point, the client looks ok for a DELETE operation
        return 'ok'

    elif mode == 'auth':
        chap = kwargs['chap']
        # client iqn must exist
        if client_iqn not in config['clients']:
            return ("Client '{}' does not exist".format(client_iqn))

        # must provide chap as either '' or a user/password string
        if 'chap' not in kwargs:
            return ("Client auth needs 'chap' defined")

        # credentials string must be valid
        if chap:
            if not valid_credentials(chap):
                return ("Invalid format for CHAP credentials. Refer to 'help' "
                        "or documentation for the correct format")

        return 'ok'

    elif mode == 'disk':

        this_client = config['clients'].get(client_iqn)
        if this_client.get('group_name', None):
            return ("Unable to manage disks for '{}' - it belongs to "
                    "group {}".format(client_iqn,
                                      this_client.get('group_name')))

        if 'image_list' not in parms_passed:
            return ("Disk changes require 'image_list' to be set, containing"
                    " a comma separated str of rbd images (pool.image)")

        rqst_disks = set(kwargs['image_list'].split(','))
        mapped_disks = set(config['clients'][client_iqn]['luns'].keys())
        current_disks = set(config['disks'].keys())

        if len(rqst_disks) > len(mapped_disks):
            # this is an add operation

            # ensure the image list is 'complete' not just a single disk
            if not mapped_disks.issubset(rqst_disks):
                return ("Invalid image list - it must contain existing "
                        "disks AND any additions")

            # ensure new disk(s) exist - must yield a result since rqst>mapped
            new_disks = rqst_disks.difference(mapped_disks)
            if not new_disks.issubset(current_disks):
                # disks provided are not currently defined
                return ("Invalid image list - it defines new disks that do "
                        "not current exist")

            return 'ok'

        else:

            # this is a disk removal operation
            if kwargs['image_list']:
                if not rqst_disks.issubset(mapped_disks):
                    return ("Invalid image list ({})".format(rqst_disks))

            return 'ok'

    return 'Unknown error in valid_client function'
Exemple #30
0
    def add_dev_to_lio(self, in_wwn=None):
        """
        Add an rbd device to the LIO configuration
        :param in_wwn: optional wwn identifying the rbd image to clients
        (must match across gateways)
        :return: LIO LUN object
        """
        self.logger.info("(LUN.add_dev_to_lio) Adding image "
                         "'{}' to LIO".format(self.config_key))

        # extract control parameter overrides (if any) or use default
        controls = {}
        for k in ['max_data_area_mb', 'hw_max_sectors']:
            controls[k] = getattr(self, k)

        control_string = gen_control_string(controls)
        if control_string:
            self.logger.debug("control=\"{}\"".format(control_string))

        new_lun = None
        try:
            # config string = rbd identifier / config_key (pool/image) /
            # optional osd timeout
            cfgstring = "rbd/{}/{};osd_op_timeout={}".format(
                self.pool, self.image, self.osd_op_timeout)
            if (settings.config.cephconf != '/etc/ceph/ceph.conf'):
                cfgstring += ";conf={}".format(settings.config.cephconf)

            # Another app might have created/removed the dev. Force the cache
            # to update itself.
            lio_root = root.RTSRoot()
            lio_root.invalidate_caches()
            new_lun = UserBackedStorageObject(name=self.config_key,
                                              config=cfgstring,
                                              size=self.size_bytes,
                                              wwn=in_wwn,
                                              control=control_string)
        except (RTSLibError, IOError) as err:
            self.error = True
            self.error_msg = ("failed to add {} to LIO - "
                              "error({})".format(self.config_key, str(err)))
            self.logger.error(self.error_msg)
            return None

        try:
            new_lun.set_attribute("cmd_time_out", 0)
            new_lun.set_attribute("qfull_time_out", self.qfull_timeout)
        except RTSLibError as err:
            self.error = True
            self.error_msg = ("Could not set LIO device attribute "
                              "cmd_time_out/qfull_time_out for device: {}. "
                              "Kernel not supported. - "
                              "error({})".format(self.config_key, str(err)))
            self.logger.error(self.error_msg)
            new_lun.delete()
            return None

        self.logger.info("(LUN.add_dev_to_lio) Successfully added {}"
                         " to LIO".format(self.config_key))

        return new_lun