def summary(self): total_bytes = 0 total_disks = 0 for pool in self.children: total_disks += len(pool.children) for disk in pool.children: total_bytes += disk.size return '{}, Disks: {}'.format(human_size(total_bytes), total_disks), None
def __init__(self, parent, image_id, image_config, size=None, features=None, snapshots=None): """ Create a disk entry under the Disks subtree :param parent: parent object (instance of the Disks class) :param image_id: key used in the config object for this rbd image (pool.image_name) - str :param image_config: meta data for this image :return: """ self.pool, self.rbd_image = image_id.split('.', 1) UINode.__init__(self, image_id, parent) self.image_id = image_id self.size = 0 self.size_h = '' self.features = 0 self.feature_list = [] self.snapshots = [] self.backstore = image_config['backstore'] self.controls = {} self.control_values = {} self.ceph_cluster = self.parent.parent.ceph.local_ceph.name disk_map = self.parent.disk_info if image_id not in disk_map: disk_map[image_id] = {} if image_id not in self.parent.disk_lookup: self.parent.disk_lookup[image_id] = self self._apply_config(image_config) if not size: # Size/features are not stored in the config, since it can be changed # outside of this tool-chain, so we get them dynamically self._refresh_config() else: # size and features have been passed in from the Disks.refresh # method self.exists = True self.size = size self.size_h = human_size(self.size) self.features = features self.feature_list = self._get_features() self._parse_snapshots(snapshots) # update the parent's disk info map disk_map = self.parent.disk_info disk_map[self.image_id]['size'] = self.size disk_map[self.image_id]['size_h'] = self.size_h
def _get_meta_data_tcmu(self): """ query the rbd to get the features and size of the rbd :return: """ self.logger.debug("Refreshing image metadata") with rados.Rados(conffile=settings.config.cephconf) as cluster: with cluster.open_ioctx(self.pool) as ioctx: with rbd.Image(ioctx, self.image) as rbd_image: self.size = rbd_image.size() self.size_h = human_size(self.size) self.features = rbd_image.features() self.feature_list = self._get_features() self._parse_snapshots(list(rbd_image.list_snaps()))
def ui_command_info(self): color = { "HEALTH_OK": "green", "HEALTH_WARN": "yellow", "HEALTH_ERR": "red" } status = self.ceph_status # backward compatibility (< octopus) if 'osdmap' in status['osdmap']: osdmap = status['osdmap']['osdmap'] else: osdmap = status['osdmap'] output = "Cluster name: {}\n".format(self.cluster_name) output += "Ceph version: {}\n".format(self.version) output += "Health : {}\n".format(self.health_status) if self.health_status != 'HEALTH_OK': output += " - {}\n".format(','.join(self.health_list)) # backward compatibility (< octopus) if 'mons' in status['monmap']: num_mons = len(status['monmap']['mons']) else: num_mons = status['monmap']['num_mons'] num_quorum = len(status['quorum_names']) num_out_of_quorum = num_mons - num_quorum q_str = "quorum {}, out of quorum: {}".format(num_quorum, num_out_of_quorum) output += "\nMONs : {:>4} ({})\n".format(self.topology.num_mons, q_str) output += "OSDs : {:>4} ({} up, {} in)\n".format( self.topology.num_osds, osdmap['num_up_osds'], osdmap['num_in_osds']) output += "Pools: {:>4}\n".format(self.num_pools) raw = status['pgmap']['bytes_total'] output += "Raw capacity: {}\n".format(human_size(raw)) output += "\nConfig : {}\n".format(self.conf) output += "Client Name: {}\n".format(self.client_name) console_message(output, color=color[self.health_status])
def summary(self): total_bytes = 0 for disk in self.children: total_bytes += disk.size return '{} ({})'.format(self.name, human_size(total_bytes)), None
def valid_disk(ceph_iscsi_config, logger, **kwargs): """ determine whether the given image info is valid for a disk operation :param ceph_iscsi_config: Config object :param logger: logger object :param image_id: (str) <pool>.<image> format :return: (str) either 'ok' or an error description """ # create can also pass optional controls dict mode_vars = { "create": ['pool', 'image', 'size', 'count'], "resize": ['pool', 'image', 'size'], "reconfigure": ['pool', 'image', 'controls'], "delete": ['pool', 'image'] } if 'mode' in kwargs.keys(): mode = kwargs['mode'] else: mode = None backstore = kwargs['backstore'] if backstore not in LUN.BACKSTORES: return "Invalid '{}' backstore - Supported backstores: " \ "{}".format(backstore, ','.join(LUN.BACKSTORES)) if mode in mode_vars: if not all(x in kwargs for x in mode_vars[mode]): return ("{} request must contain the following " "variables: ".format(mode, ','.join(mode_vars[mode]))) else: return "disk operation mode '{}' is invalid".format(mode) config = ceph_iscsi_config.config disk_key = "{}.{}".format(kwargs['pool'], kwargs['image']) if mode in ['create', 'resize']: if kwargs['pool'] not in get_pools(): return "pool name is invalid" if mode == 'create': if kwargs['size'] and not valid_size(kwargs['size']): return "Size is invalid" if len(config['disks']) >= 256: return "Disk limit of 256 reached." disk_regex = re.compile(r"^[a-zA-Z0-9\-_]+$") if not disk_regex.search(kwargs['pool']): return "Invalid pool name (use alphanumeric, '_', or '-' characters)" if not disk_regex.search(kwargs['image']): return "Invalid image name (use alphanumeric, '_', or '-' characters)" if kwargs['count'].isdigit(): if not 1 <= int(kwargs['count']) <= 10: return "invalid count specified, must be an integer (1-10)" else: return "invalid count specified, must be an integer (1-10)" if kwargs['count'] == '1': new_disks = {disk_key} else: limit = int(kwargs['count']) + 1 new_disks = set( ['{}{}'.format(disk_key, ctr) for ctr in range(1, limit)]) if any(new_disk in config['disks'] for new_disk in new_disks): return ("at least one rbd image(s) with that name/prefix is " "already defined") if mode in ["resize", "delete", "reconfigure"]: # disk must exist in the config if disk_key not in config['disks']: return ("rbd {}/{} is not defined to the " "configuration".format(kwargs['pool'], kwargs['image'])) if mode == 'resize': if not valid_size(kwargs['size']): return "Size is invalid" size = kwargs['size'].upper() current_size = get_rbd_size(kwargs['pool'], kwargs['image']) if convert_2_bytes(size) <= current_size: return ("resize value must be larger than the " "current size ({}/{})".format(human_size(current_size), current_size)) if mode in ['create', 'reconfigure']: try: settings.Settings.normalize_controls(kwargs['controls'], LUN.SETTINGS[backstore]) except ValueError as err: return (err) if mode == 'delete': # disk must *not* be allocated to a client in the config mapped_list = [] allocation_list = [] for target_iqn, target in config['targets'].items(): if disk_key in target['disks']: mapped_list.append(target_iqn) for client_iqn in target['clients']: client_metadata = target['clients'][client_iqn] if disk_key in client_metadata['luns']: allocation_list.append(client_iqn) if allocation_list: return ("Unable to delete {}. Allocated " "to: {}".format(disk_key, ','.join(allocation_list))) if mapped_list: return ("Unable to delete {}. Mapped " "to: {}".format(disk_key, ','.join(mapped_list))) return 'ok'
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 valid_disk(ceph_iscsi_config, logger, **kwargs): """ determine whether the given image info is valid for a disk operation :param ceph_iscsi_config: Config object :param logger: logger object :param image_id: (str) <pool>.<image> format :return: (str) either 'ok' or an error description """ # create can also pass optional controls dict mode_vars = { "create": ['pool', 'image', 'size', 'count'], "resize": ['pool', 'image', 'size'], "reconfigure": ['pool', 'image', 'controls'], "delete": ['pool', 'image'] } if 'mode' in kwargs.keys(): mode = kwargs['mode'] else: mode = None if mode in mode_vars: if not all(x in kwargs for x in mode_vars[mode]): return ("{} request must contain the following " "variables: ".format(mode, ','.join(mode_vars[mode]))) else: return "disk operation mode '{}' is invalid".format(mode) config = ceph_iscsi_config.config disk_key = "{}.{}".format(kwargs['pool'], kwargs['image']) if mode in ['create', 'resize']: if not valid_size(kwargs['size']): return "Size is invalid" elif kwargs['pool'] not in get_pools(): return "pool name is invalid" if mode == 'create': if len(config['disks']) >= 256: return "Disk limit of 256 reached." disk_regex = re.compile(r"^[a-zA-Z0-9\-_]+$") if not disk_regex.search(kwargs['pool']): return "Invalid pool name (use alphanumeric, '_', or '-' characters)" if not disk_regex.search(kwargs['image']): return "Invalid image name (use alphanumeric, '_', or '-' characters)" if kwargs['count'].isdigit(): if not 1 <= int(kwargs['count']) <= 10: return "invalid count specified, must be an integer (1-10)" else: return "invalid count specified, must be an integer (1-10)" if kwargs['count'] == '1': new_disks = {disk_key} else: limit = int(kwargs['count']) + 1 new_disks = set( ['{}{}'.format(disk_key, ctr) for ctr in range(1, limit)]) if any(new_disk in config['disks'] for new_disk in new_disks): return ("at least one rbd image(s) with that name/prefix is " "already defined") gateways_defined = len([ key for key in config['gateways'] if isinstance(config['gateways'][key], dict) ]) if gateways_defined < settings.config.minimum_gateways: return ("disks can not be added until at least {} gateways " "are defined".format(settings.config.minimum_gateways)) if mode in ["resize", "delete", "reconfigure"]: # disk must exist in the config if disk_key not in config['disks']: return ("rbd {}/{} is not defined to the " "configuration".format(kwargs['pool'], kwargs['image'])) if mode == 'resize': size = kwargs['size'].upper() current_size = get_rbd_size(kwargs['pool'], kwargs['image']) if convert_2_bytes(size) <= current_size: return ("resize value must be larger than the " "current size ({}/{})".format(human_size(current_size), current_size)) if mode in ['create', 'reconfigure']: try: settings.Settings.normalize_controls(kwargs['controls'], LUN.SETTINGS) except ValueError as err: return (err) if mode == 'delete': # disk must *not* be allocated to a client in the config allocation_list = [] for client_iqn in config['clients']: client_metadata = config['clients'][client_iqn] if disk_key in client_metadata['luns']: allocation_list.append(client_iqn) if allocation_list: return ("Unable to delete {}. Allocated " "to: {}".format(disk_key, ','.join(allocation_list))) try: with rados.Rados(conffile=settings.config.cephconf) as cluster: with cluster.open_ioctx(kwargs['pool']) as ioctx: with rbd.Image(ioctx, kwargs['image']) as rbd_image: if list(rbd_image.list_snaps()): return ("Unable to delete {}. Snapshots must " "be removed first.".format(disk_key)) except rbd.ImageNotFound: pass except Exception as e: return "Unable to query {}: {}".format(disk_key, e) return 'ok'
def summary(self): total_bytes = 0 for disk in self.children: total_bytes += disk.size return '{}, Disks: {}'.format(human_size(total_bytes), len(self.children)), None
def _parse_snapshots(self, snapshots): self.snapshots = ["{name} ({size})".format(name=s['name'], size=human_size(s['size'])) for s in snapshots] self.snapshot_names = [s['name'] for s in snapshots]