Exemplo n.º 1
0
def _update_client(**kwargs):
    """
    Handler function to apply the changes to a specific client definition
    :param args:
    """
    # convert the comma separated image_list string into a list for GWClient
    if kwargs['images']:
        image_list = str(kwargs['images']).split(',')
    else:
        image_list = []

    client = GWClient(logger, kwargs['client_iqn'], image_list, kwargs['chap'])

    if client.error:
        logger.error("Invalid client request - {}".format(client.error_msg))
        return 400, "Invalid client request"

    client.manage('present', committer=kwargs['committing_host'])
    if client.error:
        logger.error("client update failed on {} : "
                     "{}".format(kwargs['client_iqn'], client.error_msg))
        return 500, "Client update failed"
    else:
        config.refresh()
        return 200, "Client configured successfully"
Exemplo n.º 2
0
def define_clients():
    """
    define the clients (nodeACLs) to the gateway definition
    """

    # Client configurations (NodeACL's)
    for client_iqn in config.config['clients']:
        client_metadata = config.config['clients'][client_iqn]
        client_chap = CHAP(client_metadata['auth']['chap'])

        image_list = client_metadata['luns'].keys()

        chap_str = client_chap.chap_str
        if client_chap.error:
            logger.debug("Password decode issue : "
                         "{}".format(client_chap.error_msg))
            halt("Unable to decode password for "
                 "{}".format(client_iqn))

        client = GWClient(logger,
                          client_iqn,
                          image_list,
                          chap_str)

        client.manage('present')  # ensure the client exists
Exemplo n.º 3
0
def manage_client(client_iqn):
    """
    Manage a client definition to the local gateway
    Internal Use ONLY
    :param client_iqn: iscsi name for the client
    **RESTRICTED**
    """

    if request.method == 'GET':

        if client_iqn in config.config['clients']:
            return jsonify(config.config["clients"][client_iqn]), 200
        else:
            return jsonify(message="Client does not exist"), 404

    elif request.method == 'PUT':

        try:
            valid_iqn = normalize_wwn(['iqn'], client_iqn)
        except RTSLibError:
            return jsonify(message="'{}' is not a valid name for "
                                   "iSCSI".format(client_iqn)), 400

        committing_host = request.form['committing_host']

        image_list = request.form.get('image_list', '')

        chap = request.form.get('chap', '')

        status_code, status_text = _update_client(client_iqn=client_iqn,
                                                  images=image_list,
                                                  chap=chap,
                                                  committing_host=committing_host)

        logger.debug("client create: {}".format(status_code))
        logger.debug("client create: {}".format(status_text))
        return jsonify(message=status_text), status_code

    else:
        # DELETE request
        committing_host = request.form['committing_host']

        # Make sure the delete request is for a client we have defined
        if client_iqn in config.config['clients'].keys():
            client = GWClient(logger, client_iqn, '', '')
            client.manage('absent', committer=committing_host)

            if client.error:
                logger.error("Failed to remove client : "
                             "{}".format(client.error_msg))
                return jsonify(message="Failed to remove client"), 500

            else:
                if committing_host == this_host():
                    config.refresh()

                return jsonify(message="Client deleted ok"), 200
        else:
            logger.error("Delete request for non existent client!")
            return jsonify(message="Client does not exist!"), 404
Exemplo n.º 4
0
def ansible_main():

    fields = {
        "client_iqn": {
            "required": True,
            "type": "str"
        },
        "image_list": {
            "required": True,
            "type": "str"
        },
        "chap": {
            "required": True,
            "type": "str"
        },
        "state": {
            "required": True,
            "choices": ['present', 'absent'],
            "type": "str"
        },
    }

    module = AnsibleModule(argument_spec=fields, supports_check_mode=False)

    client_iqn = module.params['client_iqn']

    if module.params['image_list']:
        image_list = module.params['image_list'].split(',')
    else:
        image_list = []

    chap = module.params['chap']
    desired_state = module.params['state']

    logger.info("START - Client configuration started : {}".format(client_iqn))

    # The client is defined using the GWClient class. This class handles client attribute updates,
    # rados configuration object updates and LIO settings. Since the logic is external to this
    # custom module, clients can be created/deleted by other methods in the same manner.
    client = GWClient(logger, client_iqn, image_list, chap)
    if client.error:
        module.fail_json(msg=client.error_msg)

    client.manage(desired_state)
    if client.error:
        module.fail_json(msg=client.error_msg)

    logger.info(
        "END   - Client configuration complete - {} changes made".format(
            client.change_count))

    changes_made = True if client.change_count > 0 else False

    module.exit_json(changed=changes_made,
                     meta={
                         "msg":
                         "Client definition completed {} "
                         "changes made".format(client.change_count)
                     })
Exemplo n.º 5
0
def apply_config():
    """
    procesing logic that orchestrates the creation of the iSCSI gateway
    to LIO.
    """

    # access config_loading from the outer scope, for r/w
    global config_loading
    config_loading = True

    local_gw = this_host()

    logger.info("Reading the configuration object to update local LIO "
                "configuration")

    # first check to see if we have any entries to handle - if not, there is
    # no work to do..
    if "gateways" not in config.config:
        logger.info("Configuration is empty - nothing to define to LIO")
        config_loading = False
        return
    if local_gw not in config.config['gateways']:
        logger.info("Configuration does not have an entry for this host({}) - "
                    "nothing to define to LIO".format(local_gw))
        config_loading = False
        return

    # at this point we have a gateway entry that applies to the running host
    portals_already_active = portals_active()

    logger.info("Processing Gateway configuration")
    gateway = define_gateway()

    logger.info("Processing LUN configuration")
    try:
        LUN.define_luns(logger, config, gateway)
    except CephiSCSIError as err:
        halt("Could not define LUNs: {}".format(err))

    logger.info("Processing client configuration")
    try:
        GWClient.define_clients(logger, config)
    except CephiSCSIError as err:
        halt("Could not define clients: {}".format(err))

    if not portals_already_active:
        # The tpgs, luns and clients are all defined, but the active tpg
        # doesn't have an IP bound to it yet (due to the enable_portals=False
        # setting above)
        logger.info("Adding the IP to the enabled tpg, allowing iSCSI logins")
        gateway.enable_active_tpg(config)
        if gateway.error:
            halt("Error enabling the IP with the active TPG: {}".format(
                gateway.error_msg))

    config_loading = False

    logger.info("iSCSI configuration load complete")
Exemplo n.º 6
0
    def update_auth(self, tpg, username=None, password=None,
                    mutual_username=None, mutual_password=None):
        tpg.chap_userid = username
        tpg.chap_password = password
        tpg.chap_mutual_userid = mutual_username
        tpg.chap_mutual_password = mutual_password

        auth_enabled = (username and password)
        if auth_enabled:
            tpg.set_attribute('authentication', '1')
        else:
            GWClient.try_disable_auth(tpg)
Exemplo n.º 7
0
def ansible_main():

    fields = {
        "client_iqn": {"required": True, "type": "str"},
        "image_list": {"required": True, "type": "str"},
        "chap": {"required": True, "type": "str"},
        "state": {
            "required": True,
            "choices": ['present', 'absent'],
            "type": "str"
        },
    }

    module = AnsibleModule(argument_spec=fields,    # noqa F405
                           supports_check_mode=False)

    client_iqn = module.params['client_iqn']

    if module.params['image_list']:
        image_list = module.params['image_list'].split(',')
    else:
        image_list = []

    chap = module.params['chap']
    desired_state = module.params['state']

    logger.info("START - Client configuration started : {}".format(client_iqn))

    # The client is defined using the GWClient class. This class handles
    # client attribute updates, rados configuration object updates and LIO
    # settings. Since the logic is external to this custom module, clients
    # can be created/deleted by other methods in the same manner.
    client = GWClient(logger, client_iqn, image_list, chap)
    if client.error:
        module.fail_json(msg=client.error_msg)

    client.manage(desired_state)
    if client.error:
        module.fail_json(msg=client.error_msg)

    logger.info("END   - Client configuration complete - {} "
                "changes made".format(client.change_count))

    changes_made = True if client.change_count > 0 else False

    module.exit_json(changed=changes_made,
                     meta={"msg": "Client definition completed {} "
                                  "changes made".format(client.change_count)})
Exemplo n.º 8
0
    def update_client(self, client_iqn, image_list):

        client = GWClient(self.logger, client_iqn, image_list, '')
        client.define_client()                          # sets up tpg lun list

        # grab the client's metadata from the config (needed by setup_luns)
        client.metadata = self.config.config['clients'][client_iqn]
        client.setup_luns()

        if client.error:
            self._set_error(client.error_msg)
Exemplo n.º 9
0
    def update_client(self, client_iqn, image_list):

        client = GWClient(self.logger, client_iqn, image_list, '', '', '', '', self.target_iqn)
        client.manage('reconfigure')

        # grab the client's metadata from the config (needed by setup_luns)
        target_config = self.config.config['targets'][self.target_iqn]
        client.metadata = target_config['clients'][client_iqn]
        client.setup_luns(self.config.config['disks'])

        if client.error:
            self._set_error(client.error_msg)
Exemplo n.º 10
0
    def update_client(self, client_iqn, image_list):

        client = GWClient(self.logger, client_iqn, image_list, '')
        client.define_client()  # set up clients ACL

        # grab the metadata from the current definition
        client.metadata = self.config.config['clients'][client_iqn]
        client.setup_luns()

        if client.error:
            self._set_error(client.error_msg)
            return
        else:
            self.logger.info("Updating config object for "
                             "client '{}'".format(client_iqn))
            client.metadata['group_name'] = self.group_name
            self.config.update_item("clients", client_iqn, client.metadata)
Exemplo n.º 11
0
 def logged_in(self):
     target_iqn = self.parent.parent.name
     gateways = self.parent.parent.get_child('gateways')
     local_gw = this_host()
     is_local_target = len(
         [child
          for child in gateways.children if child.name == local_gw]) > 0
     if is_local_target:
         client_info = GWClient.get_client_info(target_iqn, self.client_iqn)
         self.alias = client_info['alias']
         self.ip_address = ','.join(client_info['ip_address'])
         return client_info['state']
     else:
         self.alias = ''
         self.ip_address = ''
         return ''
Exemplo n.º 12
0
    def define_target(self, target_iqn, gw_ip_list, target_only=False):
        """
        define the iSCSI target and tpgs
        :param target_iqn: (str) target iqn
        :param gw_ip_list: (list) gateway ip list
        :param target_only: (bool) if True only setup target
        :return: (object) GWTarget object
        """

        # GWTarget Definition : Handle the creation of the Target/TPG(s) and
        # Portals. Although we create the tpgs, we flick the enable_portal flag
        # off so the enabled tpg will not have an outside IP address. This
        # prevents clients from logging in too early, failing and giving up
        # because the nodeACL hasn't been defined yet (yes Windows I'm looking
        # at you!)

        # first check if there are tpgs already in LIO (True) - this would
        # indicate a restart or reload call has been made. If the tpg count is
        # 0, this is a boot time request
        self.logger.info("Setting up {}".format(target_iqn))

        target = GWTarget(self.logger, target_iqn, gw_ip_list,
                          enable_portal=self.portals_active(target_iqn))
        if target.error:
            raise CephiSCSIError("Error initializing iSCSI target: "
                                 "{}".format(target.error_msg))

        target.manage('target')
        if target.error:
            raise CephiSCSIError("Error creating the iSCSI target (target, "
                                 "TPGs, Portals): {}".format(target.error_msg))

        if not target_only:
            self.logger.info("Processing LUN configuration")
            try:
                LUN.define_luns(self.logger, self.config, target)
            except CephiSCSIError as err:
                self.logger.error("{} - Could not define LUNs: "
                                  "{}".format(target.iqn, err))
                raise

            self.logger.info("{} - Processing client configuration".
                             format(target.iqn))
            try:
                GWClient.define_clients(self.logger, self.config, target.iqn)
            except CephiSCSIError as err:
                self.logger.error("Could not define clients: {}".format(err))
                raise

        if not target.enable_portal:
            # The tpgs, luns and clients are all defined, but the active tpg
            # doesn't have an IP bound to it yet (due to the
            # enable_portals=False setting above)
            self.logger.info("{} - Adding the IP to the enabled tpg, "
                             "allowing iSCSI logins".format(target.iqn))
            target.enable_active_tpg(self.config)
            if target.error:
                raise CephiSCSIError("{} - Error enabling the IP with the "
                                     "active TPG: {}".format(target.iqn,
                                                             target.error_msg))
        return target
Exemplo n.º 13
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']
    target_iqn = kwargs['target_iqn']
    config = get_config()
    if not config:
        return "Unable to query the local API for the current config"
    target_config = config['targets'][target_iqn]

    if mode == 'create':
        # iqn must be valid
        try:
            normalize_wwn(['iqn'], client_iqn)
        except RTSLibError:
            return ("Invalid IQN name for iSCSI")

        # iqn must not already exist
        if client_iqn in target_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 target_config['clients']:
            return ("{} is not defined yet - nothing to "
                    "delete".format(client_iqn))

        this_client = target_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
        client_info = GWClient.get_client_info(target_iqn, client_iqn)
        if client_info['state'] == 'LOGGED_IN':
            return ("Client '{}' is logged in to {}- unable to delete until"
                    " it's logged out".format(client_iqn, target_iqn))

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

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

        username = kwargs['username']
        password = kwargs['password']
        mutual_username = kwargs['mutual_username']
        mutual_password = kwargs['mutual_password']

        error_msg = valid_credentials(username, password, mutual_username,
                                      mutual_password)
        if error_msg:
            return error_msg

        return 'ok'

    elif mode == 'disk':

        this_client = target_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(target_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'
Exemplo n.º 14
0
    def activate(self):
        disk = self.config.config['disks'].get(self.config_key, None)
        if not disk:
            raise CephiSCSIError("Image {} not found.".format(self.image))

        wwn = disk.get('wwn', None)
        if not wwn:
            raise CephiSCSIError("LUN {} missing wwn".format(self.image))

        # re-add backend storage object
        so = self.lio_stg_object()
        if not so:
            self.add_dev_to_lio(wwn)
            if self.error:
                raise CephiSCSIError("LUN activate failure - {}".format(
                    self.error_msg))

        # re-add LUN to target
        local_gw = this_host()
        targets_items = [
            item for item in self.config.config['targets'].items()
            if self.config_key in item[1]['disks']
            and local_gw in item[1]['portals']
        ]
        for target_iqn, target in targets_items:
            ip_list = target['ip_list']

            # Add the mapping for the lun to ensure the block device is
            # present on all TPG's
            gateway = GWTarget(self.logger, target_iqn, ip_list)

            gateway.manage('map')
            if gateway.error:
                raise CephiSCSIError("LUN mapping failed - {}".format(
                    gateway.error_msg))

            # re-map LUN to hosts
            client_err = ''
            for client_iqn in target['clients']:
                client_metadata = target['clients'][client_iqn]
                if client_metadata.get('group_name', ''):
                    continue

                image_list = list(client_metadata['luns'].keys())
                if self.config_key not in image_list:
                    continue

                client_chap = CHAP(client_metadata['auth']['chap'])
                chap_str = client_chap.chap_str
                if client_chap.error:
                    raise CephiSCSIError("Password decode issue : "
                                         "{}".format(client_chap.error_msg))

                client_chap_mutual = CHAP(
                    client_metadata['auth']['chap_mutual'])
                chap_mutual_str = client_chap_mutual.chap_str
                if client_chap_mutual.error:
                    raise CephiSCSIError("Password decode issue : "
                                         "{}".format(
                                             client_chap_mutual.error_msg))

                client = GWClient(self.logger, client_iqn, image_list,
                                  chap_str, chap_mutual_str, target_iqn)
                client.manage('present')
                if client.error:
                    client_err = "LUN mapping failed {} - {}".format(
                        client_iqn, client.error_msg)

            # re-map LUN to host groups
            for group_name in target['groups']:
                host_group = target['groups'][group_name]
                members = host_group.get('members')
                disks = host_group.get('disks').keys()
                if self.config_key not in disks:
                    continue

                group = Group(self.logger, target_iqn, group_name, members,
                              disks)
                group.apply()
                if group.error:
                    client_err = "LUN mapping failed {} - {}".format(
                        group_name, group.error_msg)

            if client_err:
                raise CephiSCSIError(client_err)
Exemplo n.º 15
0
 def logged_in(self):
     target_iqn = self.parent.parent.name
     client_info = GWClient.get_client_info(target_iqn, self.client_iqn)
     self.alias = client_info['alias']
     self.ip_address = ','.join(client_info['ip_address'])
     return client_info['state']