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")
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"})
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)
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
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")
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
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"})