def _get_map(self): """ Convert dm devices into their pool/rbd_image name. All gateway nodes should see the same rbds, and each rbd when mapped through device mapper will have the same name, so this gives us a common reference point. """ dm_devices = glob.glob('/dev/mapper/[0-255]-*') if dm_devices: for dm_path in dm_devices: key = os.path.basename(dm_path) # e.g. 0-198baf12200854 dm_id = os.path.realpath(dm_path).split('/')[-1] # e.g. dm-4 rbd_device = os.listdir('/sys/class/block/{}/slaves'.format(dm_id))[0] # rbdX rbd_num = rbd_device[3:] # X pool = fread('/sys/devices/rbd/{}/pool'.format(rbd_num)) image = fread('/sys/devices/rbd/{}/name'.format(rbd_num)) self.map[key] = {"rbd_name": "{}.{}".format(pool, image), "rbd_dev": rbd_device}
def _get_size_bytes(self): """ return the current size of the rbd from sysfs query :return: """ rbd_bytes = 0 if self.rbd_map: rbd_id = self.rbd_map.split('/')[-1] rbd_sysfs_path = "/sys/class/block/{}/size".format(rbd_id) # size is defined in 512b sectors rbd_bytes = int(fread(rbd_sysfs_path)) * 512 return rbd_bytes
def set_alua(lun, desired_state='standby'): """ Sets the ALUA state of a LUN (active/standby) :param lun: LIO LUN object :param desired_state: active or standby state :return: None """ alua_state_options = { "active": '0', "active/unoptimized": '1', "standby": '2' } configfs_path = lun.path lun_name = lun.name alua_access_state = 'alua/default_tg_pt_gp/alua_access_state' alua_access_type = 'alua/default_tg_pt_gp/alua_access_type' type_fullpath = os.path.join(configfs_path, alua_access_type) if fread(type_fullpath) != 'Implicit': logger.info( "(set_alua) Switching device alua access type to Implicit - i.e. active path set by gateways" ) fwrite(type_fullpath, '1') else: logger.debug( "(set_alua) lun alua_access_type already set to Implicit - no change needed" ) state_fullpath = os.path.join(configfs_path, alua_access_state) if fread(state_fullpath) != alua_state_options[desired_state]: logger.debug( "(set_alua) Updating alua_access_state for {} to {}".format( lun_name, desired_state)) fwrite(state_fullpath, alua_state_options[desired_state]) else: logger.debug( "(set_alua) Skipping alua update - already set to desired state '{}'" .format(desired_state))
def dm_device_name_from_rbd_map(map_device): """ take a mapped device name /dev/rbdX to determine the /dev/mapper/X equivalent by reading the devices attribute files in sysfs :param map_device: device path of the form /dev/rbdX :return: device mapper name for the rbd device /dev/mapper/<pool>-<image-id> """ rbd_bus_id = map_device[8:] dm_uid = None # TODO - could fread encounter an IOerror? rbd_path = os.path.join('/sys/bus/rbd/devices', rbd_bus_id) if os.path.exists(rbd_path): pool_id = fread(os.path.join(rbd_path, "pool_id")) image_id = fread(os.path.join(rbd_path, "image_id")) current_snap = fread(os.path.join(rbd_path, "current_snap")) dm_uid = "/dev/mapper/{}-{}".format(pool_id, image_id) if current_snap != "-": dm_uid += "-{}".format(fread(os.path.join(rbd_path, "snap_id"))) return dm_uid
def _get_lun_stats(self): iops = Metric("IOPS per LUN per client", "counter") read_bytes = Metric("read bytes per LUN per client", "counter") write_bytes = Metric("write bytes per LUN client", "counter") for node_acl in self._root.node_acls: for lun in node_acl.mapped_luns: lun_path = lun.path lun_name = lun.tpg_lun.storage_object.name perf_labels = { "gw_name": self.gw_name, "client_iqn": node_acl.node_wwn, "lun_name": lun_name } lun_iops = int( fread( os.path.join(lun_path, "statistics/scsi_auth_intr/num_cmds"))) mbytes_read = int( fread( os.path.join(lun_path, "statistics/scsi_auth_intr/read_mbytes"))) mbytes_write = int( fread( os.path.join( lun_path, "statistics/scsi_auth_intr/write_mbytes"))) iops.add(perf_labels, lun_iops) read_bytes.add(perf_labels, mbytes_read * (1024**2)) write_bytes.add(perf_labels, mbytes_write * (1024**2)) self.metrics["ceph_iscsi_lun_iops"] = iops self.metrics["ceph_iscsi_lun_read_bytes"] = read_bytes self.metrics["ceph_iscsi_lun_write_bytes"] = write_bytes
def dm_size_ok(self, rbd_object): """ Check that the dm device matches the request. if the size request is lower than current size, just return since resizing down is not support and problematic for client filesystems anyway :return boolean indicating whether the size matches """ target_bytes = convert_2_bytes(self.size) if rbd_object.size_bytes > target_bytes: return True tmr = 0 size_ok = False rbd_size_ok = False dm_path_found = False # we have to wait for the rbd size to match, since the rbd could have been # resized on another gateway host when this is called from Ansible while tmr < settings.config.time_out: if rbd_object.size_bytes == target_bytes: rbd_size_ok = True break sleep(settings.config.loop_delay) tmr += settings.config.loop_delay # since the size matches underneath device mapper, now we ensure the size # matches with device mapper - if not issue a resize map request if rbd_size_ok: # find the dm-X device dm_devices = glob.glob('/sys/class/block/dm-*/') # convert the full dm_device path to just the name (last component of path dm_name = os.path.basename(self.dm_device) for dm_dev in dm_devices: if fread(os.path.join(dm_dev, 'dm/name')) == dm_name: dm_path_found = True break if dm_path_found: # size is in sectors, so read it and * 512 = bytes dm_size_bytes = int(fread(os.path.join(dm_dev, 'size'))) * 512 if dm_size_bytes != target_bytes: self.logger.info( "Issuing a resize map for {}".format(dm_name)) response = shellcommand( 'multipathd resize map {}'.format(dm_name)) self.logger.debug("resize result : {}".format(response)) dm_size_bytes = int(fread(os.path.join(dm_dev, 'size'))) * 512 if response.lower().startswith( 'ok') and dm_size_bytes == target_bytes: size_ok = True else: self.logger.critical( "multipathd resize map for {} failed".format( dm_name)) else: # size matches size_ok = True else: self.logger.critical( "Unable to locate a dm-X device for this rbd image - {}". format(self.image)) return size_ok
def _get_preferred(self): self._check_self() path = "%s/preferred" % self.path return int(fread(path))
def _get_alua_access_type(self): self._check_self() path = "%s/alua_access_type" % self.path return int(fread(path))
def _get_nonop_delay_msecs(self): self._check_self() path = "%s/nonop_delay_msecs" % self.path return int(fread(path))
def _get_tpg_id(self): self._check_self() path = "%s/tg_pt_gp_id" % self.path return int(fread(path))
def _get_alua_support_standby(self): self._check_self() path = "%s/alua_support_standby" % self.path return int(fread(path))
def _get_alua_support_unavailable(self): self._check_self() path = "%s/alua_support_unavailable" % self.path return int(fread(path))
def _get_alua_support_offline(self): self._check_self() path = "%s/alua_support_offline" % self.path return int(fread(path))
def _get_alua_support_active_optimized(self): self._check_self() path = "%s/alua_support_active_optimized" % self.path return int(fread(path))
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: # plugin will show user or block lun_type = lun.storage_object.plugin if lun_type == 'block': 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] rbd_name = 'rbd{}'.format(iblock_name.split('_')[1]) 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] storage_type = 'rbd' elif lun_type == 'user': rbd_name = '' rbd_info = fread(os.path.join(lun.storage_object.path, 'info')) cfg_ptr = rbd_info.find('Config:') # cfg_data looks something like # rbd/iscsiTest/gprfc095-iscsiTest-20[;option1=value1] cfg_data = rbd_info[cfg_ptr:].split()[1] storage_type, pool_name, image_name = cfg_data.split('/') image_name = image_name.split(';')[0] else: raise ValueError("Unknown LUN type encountered with " "{}".format(lun.storage_object.name)) image_name = lun.storage_object.name image_size = lun.storage_object.size wwn = lun.storage_object.wwn # Each image name is assumed to be unique if image_name not in device_data: device_data[image_name] = { "size": image_size, "wwn": wwn, "rbd_name": rbd_name, "pool": pool_name, "image_name": image_name, "stg_type": storage_type, "lun_type": lun_type } return device_data