Example #1
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")
Example #2
0
def ansible_main():

    # Define the fields needs to create/map rbd's the the host(s)
    # NB. features and state are reserved/unused
    fields = {
        "pool": {"required": False, "default": "rbd", "type": "str"},
        "image": {"required": True, "type": "str"},
        "size": {"required": True, "type": "str"},
        "host": {"required": True, "type": "str"},
        "features": {"required": False, "type": "str"},
        "state": {
            "required": False,
            "default": "present",
            "choices": ['present', 'absent'],
            "type": "str"
        },
    }

    # not supporting check mode currently
    module = AnsibleModule(argument_spec=fields,
                           supports_check_mode=False)

    pool = module.params["pool"]
    image = module.params['image']
    size = module.params['size']
    allocating_host = module.params['host']
    desired_state = module.params['state']

    ################################################
    # Validate the parameters passed from Ansible  #
    ################################################
    if not valid_size(size):
        logger.critical("image '{}' has an invalid size specification '{}' in the ansible configuration".format(image,
                                                                                                         size))
        module.fail_json(msg="(main) Unable to use the size parameter '{}' for image '{}' from the playbook - "
                             "must be a number suffixed by M, G or T".format(size, image))

    # define a lun object and perform some initial parameter validation
    lun = LUN(logger, pool, image, size, allocating_host)
    if lun.error:
        module.fail_json(msg=lun.error_msg)

    logger.info("START - LUN configuration started for {}/{}".format(pool, image))

    # attempt to create/allocate the LUN for LIO
    lun.manage(desired_state)
    if lun.error:
        module.fail_json(msg=lun.error_msg)

    if lun.num_changes == 0:
        logger.info("END   - No changes needed")
    else:
        logger.info("END   - {} configuration changes made".format(lun.num_changes))

    module.exit_json(changed=(lun.num_changes > 0), meta={"msg": "Configuration updated"})
Example #3
0
    def remove_from_config(self, target_iqn):
        has_changed = False

        target_config = self.config.config["targets"].get(target_iqn)
        if target_config:
            local_gw = target_config['portals'].get(self.hostname)
            if local_gw:
                local_gw_ips = local_gw['portal_ip_addresses']

                target_config['portals'].pop(self.hostname)

                ip_list = target_config['ip_list']
                for local_gw_ip in local_gw_ips:
                    ip_list.remove(local_gw_ip)

                for _, remote_gw_config in target_config['portals'].items():
                    for local_gw_ip in local_gw_ips:
                        remote_gw_config["gateway_ip_list"].remove(local_gw_ip)
                        remote_gw_config["inactive_portal_ips"].remove(
                            local_gw_ip)
                    tpg_count = remote_gw_config["tpgs"]
                    remote_gw_config["tpgs"] = tpg_count - 1

                if not target_config['portals']:
                    # Last gw for the target so delete everything that lives
                    # under the tpg in LIO since we can't create it
                    target_config['disks'] = {}
                    target_config['clients'] = {}
                    target_config['controls'] = {}
                    target_config['groups'] = {}

                has_changed = True
                self.config.update_item('targets', target_iqn, target_config)

        remove_gateway = True
        for _, target in self.config.config["targets"].items():
            if self.hostname in target['portals']:
                remove_gateway = False
                break

        if remove_gateway:
            # gateway is no longer used, so delete it
            has_changed = True
            self.config.del_item('gateways', self.hostname)

        LUN.reassign_owners(self.logger, self.config)

        if has_changed:
            self.config.commit("retain")
            if self.config.error:
                raise CephiSCSIError(self.config.error_msg)
Example #4
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
Example #5
0
def define_luns(gateway):
    """
    define the disks in the config to LIO
    :param gateway: (object) gateway object - used for mapping
    :return: None
    """

    local_gw = this_host()

    # sort the disks dict keys, so the disks are registered in a specific
    # sequence
    disks = config.config['disks']
    srtd_disks = sorted(disks)
    pools = {disks[disk_key]['pool'] for disk_key in srtd_disks}

    if pools:
        with rados.Rados(conffile=settings.config.cephconf) as cluster:

            for pool in pools:

                logger.debug("Processing rbd's in '{}' pool".format(pool))

                with cluster.open_ioctx(pool) as ioctx:

                    pool_disks = [disk_key for disk_key in srtd_disks
                                  if disk_key.startswith(pool)]
                    for disk_key in pool_disks:

                        pool, image_name = disk_key.split('.')

                        try:
                            with rbd.Image(ioctx, image_name) as rbd_image:
                                image_bytes = rbd_image.size()
                                image_size_h = human_size(image_bytes)

                                lun = LUN(logger, pool, image_name,
                                          image_size_h, local_gw)
                                if lun.error:
                                    halt("Error defining rbd image "
                                         "{}".format(disk_key))

                                lun.allocate()
                                if lun.error:
                                    halt("Error unable to register {} with "
                                         "LIO - {}".format(disk_key,
                                                           lun.error_msg))

                        except rbd.ImageNotFound:
                            halt("Disk '{}' defined to the config, but image "
                                 "'{}' can not be found in "
                                 "'{}' pool".format(disk_key,
                                                    image_name,
                                                    pool))

        # Gateway Mapping : Map the LUN's registered to all tpg's within the
        # LIO target
        gateway.manage('map')
        if gateway.error:
            halt("Error mapping the LUNs to the tpg's within the iscsi Target")

    else:
        logger.info("No LUNs to export")
Example #6
0
def manage_disk(image_id):
    """
    Manage a disk definition on the local gateway
    Internal Use ONLY
    Disks can be created and added to each gateway, or deleted through this
    call
    :param image_id: (str) of the form pool.image_name
    **RESTRICTED**
    """

    if request.method == 'GET':

        if image_id in config.config['disks']:
            return jsonify(config.config["disks"][image_id]), 200

        else:
            return jsonify(message="rbd image {} not "
                           "found".format(image_id)), 404

    elif request.method == 'PUT':
        # A put is for either a create or a resize
        # put('http://127.0.0.1:5000/api/disk/rbd.ansible3',data={'pool': 'rbd','size': '3G','owner':'ceph-1'})

        rqst_fields = set(request.form.keys())
        if rqst_fields.issuperset(("pool", "size", "owner", "mode")):

            image_name = str(image_id.split('.', 1)[1])
            lun = LUN(logger, str(request.form['pool']), image_name,
                      str(request.form['size']), str(request.form['owner']))
            if lun.error:
                logger.error("Unable to create a LUN instance"
                             " : {}".format(lun.error_msg))
                return jsonify(message="Unable to establish LUN instance"), 500

            lun.allocate()
            if lun.error:
                logger.error("LUN alloc problem - {}".format(lun.error_msg))
                return jsonify(message="LUN allocation failure"), 500

            if request.form['mode'] == 'create':
                # new disk is allocated, so refresh the local config object
                config.refresh()

                iqn = config.config['gateways']['iqn']
                ip_list = config.config['gateways']['ip_list']

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

                gateway.manage('map')
                if gateway.error:
                    logger.error("LUN mapping failed : "
                                 "".format(gateway.error_msg))
                    return jsonify(message="LUN map failed"), 500

                return jsonify(message="LUN created"), 200

            elif request.form['mode'] == 'resize':

                return jsonify(message="LUN resized"), 200

        else:

            # this is an invalid request
            return jsonify(message="Invalid Request - need to provide"
                           "pool, size and owner"), 400

    else:
        # DELETE request
        # let's assume that the request has been validated by the caller

        # if valid_request(request.remote_addr):
        purge_host = request.form['purge_host']

        pool, image = image_id.split('.', 1)

        lun = LUN(logger, pool, image, '0G', purge_host)

        if lun.error:
            # problem defining the LUN instance
            logger.error("Error initialising the LUN : "
                         "{}".format(lun.error_msg))
            return jsonify(message="Error establishing LUN instance"), 500

        lun.remove_lun()
        if lun.error:
            if 'allocated to' in lun.error_msg:
                # attempted to remove rbd that is still allocated to a client
                status_code = 400
            else:
                status_code = 500

            logger.error("LUN remove failed : {}".format(lun.error_msg))
            return jsonify(message="Failed to remove the LUN"), status_code

        config.refresh()

        return jsonify(message="LUN removed"), 200
Example #7
0
def ansible_main():

    # Define the fields needs to create/map rbd's the the host(s)
    # NB. features and state are reserved/unused
    fields = {
        "pool": {"required": False, "default": "rbd", "type": "str"},
        "image": {"required": True, "type": "str"},
        "size": {"required": True, "type": "str"},
        "host": {"required": True, "type": "str"},
        "features": {"required": False, "type": "str"},
        "state": {
            "required": False,
            "default": "present",
            "choices": ['present', 'absent'],
            "type": "str"
        },
    }

    # not supporting check mode currently
    module = AnsibleModule(argument_spec=fields,
                           supports_check_mode=False)

    pool = module.params["pool"]
    image = module.params['image']
    size = module.params['size']
    allocating_host = module.params['host']
    desired_state = module.params['state']

    ################################################
    # Validate the parameters passed from Ansible  #
    ################################################
    if not valid_size(size):
        logger.critical("image '{}' has an invalid size specification '{}' "
                        "in the ansible configuration".format(image,
                                                              size))
        module.fail_json(msg="(main) Unable to use the size parameter '{}' "
                             "for image '{}' from the playbook - "
                             "must be a number suffixed by M,G "
                             "or T".format(size,
                                           image))

    # define a lun object and perform some initial parameter validation
    lun = LUN(logger, pool, image, size, allocating_host)
    if lun.error:
        module.fail_json(msg=lun.error_msg)

    logger.info("START - LUN configuration started for {}/{}".format(pool,
                                                                     image))

    # attempt to create/allocate the LUN for LIO
    lun.manage(desired_state)
    if lun.error:
        module.fail_json(msg=lun.error_msg)

    if lun.num_changes == 0:
        logger.info("END   - No changes needed")
    else:
        logger.info("END   - {} configuration changes "
                    "made".format(lun.num_changes))

    module.exit_json(changed=(lun.num_changes > 0),
                     meta={"msg": "Configuration updated"})