def clean_disk(cls, disk): """ Removes the given disk :param disk: Disk object to clean :type disk: source.dal.objects.disk.Disk :return: None """ if disk.usable is False: raise RuntimeError('Cannot clean disk {0}'.format(disk.name)) cls._logger.info('Cleaning disk {0}'.format(disk.name)) FSTab.remove(disk.partition_aliases) if disk.mountpoint is not None: umount_cmd = ['umount', disk.mountpoint] try: cls._local_client.run(umount_cmd) cls._local_client.dir_delete(disk.mountpoint) except Exception: cls._logger.exception( 'Failure to umount or delete the mountpoint') raise try: cls._local_client.run( ['parted', disk.aliases[0], '-s', 'mklabel', 'gpt']) except CalledProcessError: # Wiping the partition is a nice-to-have and might fail when a disk is e.g. unavailable pass cls.sync_disks() cls._locate(device_alias=disk.aliases[0], start=True) cls._logger.info('Clean disk {0} complete'.format(disk.name))
def prepare_disk(disk_id): """ Prepare a disk for use with ALBA :param disk_id: Disk identifier :type disk_id: str """ DiskController._log('Preparing disk {0}'.format(disk_id)) mountpoint = '/mnt/alba-asd/{0}'.format(''.join(random.choice(string.ascii_letters + string.digits) for _ in range(16))) disk_by_id = '/dev/disk/by-id/{0}'.format(disk_id) DiskController.locate(disk_id, start=False) check_output('umount {0} || true'.format(mountpoint), shell=True) check_output('parted {0} -s mklabel gpt'.format(disk_by_id), shell=True) check_output('parted {0} -s mkpart {1} 2MB 100%'.format(disk_by_id, disk_id), shell=True) check_output('partprobe {0}'.format(disk_by_id), shell=True) counter = 0 partition_name = '{0}-part1'.format(disk_by_id) while not os.path.exists(partition_name): print('Partition {0} not ready yet'.format(partition_name)) time.sleep(0.2) counter += 1 if counter > 10: raise RuntimeError('Partition {0} not ready in 2 seconds'.format(partition_name)) check_output('mkfs.xfs -qf {0}-part1'.format(disk_by_id), shell=True) check_output('mkdir -p {0}'.format(mountpoint), shell=True) FSTab.add('{0}-part1'.format(disk_by_id), mountpoint) check_output('mount {0}'.format(mountpoint), shell=True) check_output('chown -R alba:alba {0}'.format(mountpoint), shell=True) DiskController._log('Prepare disk {0} complete'.format(disk_id))
def clean_disk(disk, asd_id): print 'Cleaning disk {0}/{1}'.format(disk, asd_id) FSTab.remove('/dev/disk/by-id/{0}-part1'.format(disk)) check_output('umount /mnt/alba-asd/{0} || true'.format(asd_id), shell=True) check_output('rm -rf /mnt/alba-asd/{0} || true'.format(asd_id), shell=True) try: check_output('parted /dev/disk/by-id/{0} -s mklabel gpt'.format(disk), shell=True) except CalledProcessError: # Wiping the parition is a nice-to-have and might fail when a disk is e.g. unavailable pass Disks.locate(disk, start=True) print 'Clean disk {0}/{1} complete'.format(disk, asd_id)
def prepare_disk(disk, asd_id): print 'Preparing disk {0}/{1}'.format(disk, asd_id) Disks.locate(disk, start=False) check_output('umount /mnt/alba-asd/{0} || true'.format(asd_id), shell=True) check_output('parted /dev/disk/by-id/{0} -s mklabel gpt'.format(disk), shell=True) check_output('parted /dev/disk/by-id/{0} -s mkpart {0} 2MB 100%'.format(disk), shell=True) check_output('mkfs.xfs -qf /dev/disk/by-id/{0}-part1'.format(disk), shell=True) check_output('mkdir -p /mnt/alba-asd/{0}'.format(asd_id), shell=True) FSTab.add('/dev/disk/by-id/{0}-part1'.format(disk), '/mnt/alba-asd/{0}'.format(asd_id)) check_output('mount /mnt/alba-asd/{0}'.format(asd_id), shell=True) check_output('mkdir /mnt/alba-asd/{0}/data'.format(asd_id), shell=True) check_output('chown -R alba:alba /mnt/alba-asd/{0}'.format(asd_id), shell=True) print 'Prepare disk {0}/{1} complete'.format(disk, asd_id)
def prepare_disk(device_alias): """ Prepare a disk for use with ALBA :param device_alias: Alias of the device (eg: '/dev/disk/by-path/pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0') :type device_alias: str :return: None """ # Create partition DiskController._logger.info('Preparing disk {0}'.format(device_alias)) mountpoint = '/mnt/alba-asd/{0}'.format(''.join(random.choice(string.ascii_letters + string.digits) for _ in range(16))) DiskController._locate(device_alias=device_alias, start=False) DiskController._local_client.run(['umount', mountpoint], allow_nonzero=True) DiskController._local_client.run(['parted', device_alias, '-s', 'mklabel', 'gpt']) DiskController._local_client.run(['parted', device_alias, '-s', 'mkpart', device_alias.split('/')[-1], '2MB', '100%']) DiskController._local_client.run(['udevadm', 'settle']) # Waits for all udev rules to have finished # Wait for partition to be ready by attempting to add filesystem counter = 0 already_mounted = False partition_aliases = [] while True: disk_info = DiskController.get_disk_data_by_alias(device_alias=device_alias) if disk_info.get('partition_amount', 0) == 1: partition_aliases = disk_info['partition_aliases'] try: DiskController._local_client.run(['mkfs.xfs', '-qf', partition_aliases[0]]) break except CalledProcessError: if disk_info['mountpoint'] and disk_info['mountpoint'] in DiskController._local_client.run(['mount']): # Some OSes have auto-mount functionality making mkfs.xfs to fail when the mountpoint has already been mounted # This can occur when the exact same partition gets created on the device mountpoint = disk_info['mountpoint'] already_mounted = True if mountpoint.startswith('/mnt/alba-asd'): DiskController._local_client.run('rm -rf {0}/*'.format(mountpoint), allow_insecure=True) DiskController._logger.warning('Device has already been used by ALBA, re-using mountpoint {0}'.format(mountpoint)) break DiskController._logger.info('Partition for disk {0} not ready yet'.format(device_alias)) time.sleep(0.2) counter += 1 if counter > 10: raise RuntimeError('Partition for disk {0} not ready in 2 seconds'.format(device_alias)) # Create mountpoint and mount DiskController._local_client.run(['mkdir', '-p', mountpoint]) FSTab.add(partition_aliases=partition_aliases, mountpoint=mountpoint) if already_mounted is False: DiskController._local_client.run(['mount', mountpoint]) DiskController._local_client.run(['chown', '-R', 'alba:alba', mountpoint]) DiskController._logger.info('Prepare disk {0} complete'.format(device_alias))
def list_asds(): """ List all ASDs """ asds = {} mountpoints = FSTab.read() for disk, mountpoint in mountpoints.iteritems(): asds[disk] = ASDController.list_asds(mountpoint) return asds
def clean_disk(disk_id, mountpoint): """ Removes the given disk :param disk_id: Disk identifier :type disk_id: str :param mountpoint: Mountpoint of the disk :type mountpoint: str """ DiskController._log('Cleaning disk {0}'.format(disk_id)) FSTab.remove('/dev/disk/by-id/{0}-part1'.format(disk_id)) check_output('umount {0} || true'.format(mountpoint), shell=True) DiskController._local_client.dir_delete(mountpoint) try: check_output('parted /dev/disk/by-id/{0} -s mklabel gpt'.format(disk_id), shell=True) except CalledProcessError: # Wiping the partition is a nice-to-have and might fail when a disk is e.g. unavailable pass DiskController.locate(disk_id, start=True) DiskController._log('Clean disk {0} complete'.format(disk_id))
def add_asd_disk(disk_id): """ Adds an ASD to a disk :param disk_id: Identifier of the disk :type disk_id: str """ mountpoints = FSTab.read() if disk_id not in mountpoints: raise BadRequest('Disk {0} is not yet initialized'.format(disk_id)) with file_mutex('add_asd'): ASDController.create_asd(disk_id)
def list_asds_disk(disk_id): """ Lists all ASDs on a given disk :param disk_id: Identifier of the disk :type disk_id: str """ mountpoints = FSTab.read() if disk_id not in mountpoints: raise BadRequest('Disk {0} is not yet initialized'.format(disk_id)) mountpoint = mountpoints[disk_id] return ASDController.list_asds(mountpoint)
def list_asds_disk(disk_id): """ Lists all ASDs on a given disk :param disk_id: Identifier of the disk (eg: '/dev/disk/by-path/pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0' or 'pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0') :type disk_id: str :return: ASD information for the specified disk :rtype: dict """ disk_data = DiskController.get_disk_data_by_alias(device_alias=disk_id) for partition_alias, mountpoint in FSTab.read().iteritems(): if partition_alias in disk_data['partition_aliases']: return ASDController.list_asds(mountpoint=mountpoint) raise BadRequest('Disk {0} is not yet initialized'.format(disk_data['aliases'][0]))
def add_asd_disk(disk_id): """ Adds an ASD to a disk :param disk_id: Identifier of the disk (eg: '/dev/disk/by-path/pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0' or 'pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0') :type disk_id: str :return: None """ disk_data = DiskController.get_disk_data_by_alias(device_alias=disk_id) for partition_alias, mountpoint in FSTab.read().iteritems(): if partition_alias in disk_data['partition_aliases']: with file_mutex('add_asd'): ASDController.create_asd(partition_alias=partition_alias) return raise BadRequest('Disk {0} is not yet initialized'.format(disk_data['aliases'][0]))
def clean_disk(device_alias, mountpoint, partition_aliases): """ Removes the given disk :param device_alias: Alias for the device (eg: '/dev/disk/by-path/pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0') :type device_alias: str :param mountpoint: Mountpoint of the disk :type mountpoint: str :param partition_aliases: Aliases of the partition of the device :type partition_aliases: list :return: None """ if device_alias is None: DiskController._logger.warning('Deleting unknown disk with partition aliases {0}'.format(partition_aliases)) else: DiskController._logger.info('Cleaning disk {0}'.format(device_alias)) FSTab.remove(partition_aliases) if mountpoint is not None: if device_alias is None: umount_cmd = ['umount', '-l', mountpoint] else: umount_cmd = ['umount', mountpoint] try: DiskController._local_client.run(umount_cmd) DiskController._local_client.dir_delete(mountpoint) except Exception: DiskController._logger.exception('Failure to umount or delete the mountpoint') raise try: DiskController._local_client.run(['parted', device_alias, '-s', 'mklabel', 'gpt']) except CalledProcessError: # Wiping the partition is a nice-to-have and might fail when a disk is e.g. unavailable pass DiskController._locate(device_alias=device_alias, start=True) DiskController._logger.info('Clean disk {0} complete'.format(device_alias))
def get_asd(disk_id, asd_id): """ Gets an ASD :param disk_id: Identifier of the disk :type disk_id: str :param asd_id: Identifier of the ASD :type asd_id: str """ mountpoints = FSTab.read() if disk_id not in mountpoints: raise BadRequest('Disk {0} is not yet initialized'.format(disk_id)) mountpoint = mountpoints[disk_id] asds = ASDController.list_asds(mountpoint) if asd_id not in asds: raise BadRequest('ASD {0} could not be found on disk'.format(disk_id)) return asds[asd_id]
def get_asd(disk_id, asd_id): """ Gets an ASD :param disk_id: Identifier of the disk (eg: '/dev/disk/by-path/pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0' or 'pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0') :type disk_id: str :param asd_id: Identifier of the ASD (eg: bnAWEXuPHN5YJceCeZo7KxaQW86ixXd4, found under /mnt/alba-asd/WDCztMxmRqi6Hx21/) :type asd_id: str :return: ASD information :rtype: dict """ disk_data = DiskController.get_disk_data_by_alias(device_alias=disk_id) alias = disk_data['aliases'][0] for partition_alias, mountpoint in FSTab.read().iteritems(): if partition_alias in disk_data['partition_aliases']: asds = ASDController.list_asds(mountpoint=mountpoint) if asd_id not in asds: raise BadRequest('ASD {0} could not be found on disk'.format(alias)) return asds[asd_id] raise BadRequest('Disk {0} is not yet initialized'.format(alias))
def delete_disk(disk_id): """ Delete a disk :param disk_id: Identifier of the disk (eg: '/dev/disk/by-path/pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0' or 'pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0') :type disk_id: str :return: None """ try: disk_data = DiskController.get_disk_data_by_alias(device_alias=disk_id) except DiskNotFoundError: API._logger.warning('Disk with ID {0} is no longer detected on the filesystem'.format(disk_id)) disk_data = {} if disk_data.get('available') is True: raise BadRequest('Disk not yet configured') if disk_data: alias = disk_data['aliases'][0] mountpoint = disk_data['mountpoint'] or None partition_aliases = disk_data['partition_aliases'] API._logger.info('Deleting disk {0}'.format(alias)) else: # Disk is most likely missing from filesystem alias = None mountpoint = None partition_aliases = json.loads(request.form['partition_aliases']) API._logger.info('Deleting unknown disk with partition aliases "{0}"'.format('", "'.join(partition_aliases))) if mountpoint is None: # 'lsblk' did not return mountpoint for the device, but perhaps it's still mounted according to FSTab for partition_alias, mtpt in FSTab.read().iteritems(): if partition_alias in partition_aliases: API._logger.warning('Disk with ID {0} is still mounted on {1} according to FSTab'.format(disk_id, mountpoint)) mountpoint = mtpt break with file_mutex('disk_{0}'.format(disk_id)): if mountpoint is not None: for asd_id in ASDController.list_asds(mountpoint=mountpoint): ASDController.remove_asd(asd_id=asd_id, mountpoint=mountpoint) DiskController.clean_disk(device_alias=alias, mountpoint=mountpoint, partition_aliases=partition_aliases)
def asd_delete(disk_id, asd_id): """ Deletes an ASD on a given Disk :param disk_id: Identifier of the Disk :type disk_id: str :param asd_id: The ASD ID of the ASD to be removed :type asd_id: str """ # Stop and remove service API._log('Removing services for disk {0}'.format(disk_id)) mountpoints = FSTab.read() if disk_id not in mountpoints: raise BadRequest('Disk {0} is not yet initialized'.format(disk_id)) all_asds = {} for mountpoint in mountpoints.values(): all_asds.update(ASDController.list_asds(mountpoint)) if asd_id not in all_asds: raise BadRequest('Could not find ASD {0} on disk {1}'.format(asd_id, disk_id)) mountpoint = mountpoints[disk_id] ASDController.remove_asd(asd_id, mountpoint)
def delete_disk(disk_id): """ Delete a disk :param disk_id: Identifier of the disk :type disk_id: str """ API._log('Deleting disk {0}'.format(disk_id)) all_disks = DiskController.list_disks() if disk_id not in all_disks: raise BadRequest('Disk not available') if all_disks[disk_id]['available'] is True: raise BadRequest('Disk not yet configured') with file_mutex('disk_{0}'.format(disk_id)): mountpoints = FSTab.read() if disk_id in mountpoints: mountpoint = mountpoints[disk_id] asds = ASDController.list_asds(mountpoint) for asd_id in asds: ASDController.remove_asd(asd_id, mountpoint) DiskController.clean_disk(disk_id, mountpoint)
def asd_delete(disk_id, asd_id): """ Deletes an ASD on a given disk :param disk_id: Identifier of the Disk (eg: '/dev/disk/by-path/pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0' or 'pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0') :type disk_id: str :param asd_id: Identifier of the ASD (eg: bnAWEXuPHN5YJceCeZo7KxaQW86ixXd4, found under /mnt/alba-asd/WDCztMxmRqi6Hx21/) :type asd_id: str :return: None """ disk_data = DiskController.get_disk_data_by_alias(device_alias=disk_id) alias = disk_data['aliases'][0] API._logger.info('Removing services for disk {0}'.format(alias)) for partition_alias, mountpoint in FSTab.read().iteritems(): if partition_alias in disk_data['partition_aliases']: if asd_id not in ASDController.list_asds(mountpoint=mountpoint): raise BadRequest('Could not find ASD {0} on disk {1}'.format(asd_id, alias)) ASDController.remove_asd(asd_id=asd_id, mountpoint=mountpoint) return raise BadRequest('Disk {0} is not yet initialized'.format(alias))
def restart_disk(disk_id): """ Restart a disk :param disk_id: Identifier of the disk (eg: '/dev/disk/by-path/pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0' or 'pci-0000:03:00.0-sas-0x5000c29f4cf04566-lun-0') :type disk_id: str :return: None """ API._logger.info('Restarting disk {0}'.format(disk_id)) disk_data = DiskController.get_disk_data_by_alias(device_alias=disk_id) alias = disk_data['aliases'][0] with file_mutex('disk_{0}'.format(disk_id)): API._logger.info('Got lock for restarting disk {0}'.format(alias)) for partition_alias, mountpoint in FSTab.read().iteritems(): if partition_alias in disk_data['partition_aliases']: asds = ASDController.list_asds(mountpoint=mountpoint) for asd_id in asds: ASDController.stop_asd(asd_id=asd_id) DiskController.remount_disk(device_alias=alias, mountpoint=mountpoint) asds = ASDController.list_asds(mountpoint=mountpoint) for asd_id in asds: ASDController.start_asd(asd_id=asd_id) break
def restart_disk(disk_id): """ Restart a disk :param disk_id: Identifier of the disk :type disk_id: str """ API._log('Restarting disk {0}'.format(disk_id)) all_disks = DiskController.list_disks() if disk_id not in all_disks: raise BadRequest('Disk not available') if all_disks[disk_id]['available'] is False: raise BadRequest('Disk already configured') with file_mutex('disk_{0}'.format(disk_id)): API._log('Got lock for restarting disk {0}'.format(disk_id)) mountpoints = FSTab.read() if disk_id in mountpoints: mountpoint = mountpoints[disk_id] asds = ASDController.list_asds(mountpoint) for asd_id in asds: ASDController.stop_asd(asd_id) DiskController.remount_disk(disk_id, mountpoint) asds = ASDController.list_asds(mountpoint) for asd_id in asds: ASDController.start_asd(asd_id)
def list_disks(): """ List the disks :return: Information about the disks """ disks = {} # Find used disks # 1. Mounted disks all_mounts = check_output('mount', shell=True).splitlines() all_mounted_asds = [] used_disks = [] for mount in all_mounts: mount = mount.strip() match = re.search('/dev/(.+?) on (/.*?) type.*', mount) if match is not None: if not match.groups()[1].startswith('/mnt/alba-asd/'): used_disks.append(match.groups()[0]) else: all_mounted_asds.append(match.groups()[0]) # 2. Disks used in a software raid mdstat = check_output('cat /proc/mdstat', shell=True) for md_match in re.findall('([a-z]+\d+ : (in)?active raid\d+(( [a-z]+\d?\[\d+\])+))', mdstat): for disk_match in re.findall('( ([a-z]+\d?)\[\d+\])', md_match[2]): used_disks.append(disk_match[1].strip()) # Find all disks all_disks = check_output('ls -al /dev/disk/by-id/', shell=True).split('\n') for disk in all_disks: disk = disk.strip() match = re.search('.+?(((scsi-)|(ata-)|(virtio-)).+?) -> ../../([sv]d.+)', disk) if match is not None: disk_id, disk_name = match.groups()[0], match.groups()[-1] if disk_name in all_mounted_asds: all_mounted_asds.remove(disk_name) all_mounted_asds.append(disk_id.replace('-part1', '')) if re.search('-part\d+', disk_id) is None: if not any(used_disk for used_disk in used_disks if disk_name in used_disk): disks[disk_id] = {'device': '/dev/disk/by-id/{0}'.format(disk_id), 'available': True, 'state': 'ok'} # Load information about mount configuration (detect whether the disks are configured) fstab_disks = FSTab.read() for device in fstab_disks.keys(): for disk_id in disks: if disk_id == device: disks[disk_id].update({'available': False, 'mountpoint': fstab_disks[device]}) del fstab_disks[device] if device not in all_mounted_asds: disks[device].update({'state': 'error', 'state_detail': 'notmounted'}) for device in fstab_disks.keys(): disks[device] = {'device': '/dev/disk/by-id/{0}'.format(device), 'available': False, 'mountpoint': fstab_disks[device], 'state': 'error', 'state_detail': 'missing'} # Load statistical information about the disk root_directory = '/mnt/alba-asd' if DiskController._local_client.dir_exists(root_directory) and DiskController._local_client.dir_list(root_directory): df_info = check_output('df -B 1 --output=size,used,avail,target /mnt/alba-asd/*', shell=True).strip().splitlines()[1:] for disk_id in disks: if disks[disk_id]['available'] is False and disks[disk_id]['state'] == 'ok': for df in df_info: params = df.split() if params[-1] == disks[disk_id]['mountpoint']: disks[disk_id].update({'usage': {'size': int(params[0]), 'used': int(params[1]), 'available': int(params[2])}}) # Execute some checkups on the disks for disk_id in disks: if disks[disk_id]['available'] is False and disks[disk_id]['state'] == 'ok': output = check_output('ls {0}/ 2>&1 || true'.format(disks[disk_id]['mountpoint']), shell=True) if 'Input/output error' in output: disks[disk_id].update({'state': 'error', 'state_detail': 'ioerror'}) # Extra information for disk_id, disk in disks.iteritems(): disk['name'] = disk_id disk['node_id'] = DiskController.NODE_ID return disks
def list_disks(): """ List the disks CHANGES MADE TO THIS CODE SHOULD BE REFLECTED IN THE FRAMEWORK sync_with_reality CALL TOO. :return: Information about the disks :rtype: dict """ # Retrieve all symlinks for all devices # Example of name_alias_mapping: # {'/dev/md0': ['/dev/disk/by-id/md-uuid-ad2de634:26d97253:5eda0a23:96986b76', '/dev/disk/by-id/md-name-OVS-1:0'], # '/dev/sda': ['/dev/disk/by-path/pci-0000:03:00.0-sas-0x5000c295fe2ff771-lun-0'], # '/dev/sda1': ['/dev/disk/by-uuid/e3e0bc62-4edc-4c6b-a6ce-1f39e8f27e41', '/dev/disk/by-path/pci-0000:03:00.0-sas-0x5000c295fe2ff771-lun-0-part1']} name_alias_mapping = {} alias_name_mapping = {} partition_device_map = {} for path_type in DiskController._local_client.dir_list(directory='/dev/disk'): if path_type in ['by-uuid', 'by-partuuid']: # UUIDs can change after creating a filesystem on a partition continue directory = '/dev/disk/{0}'.format(path_type) for symlink in DiskController._local_client.dir_list(directory=directory): symlink_path = '{0}/{1}'.format(directory, symlink) link = DiskController._local_client.file_read_link(path=symlink_path) if link not in name_alias_mapping: name_alias_mapping[link] = [] name_alias_mapping[link].append(symlink_path) alias_name_mapping[symlink_path] = link # Parse 'lsblk' output # --exclude 1 for RAM devices, 2 for floppy devices, 11 for CD-ROM devices (See https://www.kernel.org/doc/Documentation/devices.txt) devices = DiskController._local_client.run(['lsblk', '--pairs', '--bytes', '--noheadings', '--exclude', '1,2,11', '--output=KNAME,FSTYPE,TYPE,MOUNTPOINT']).splitlines() device_regex = re.compile('^KNAME="(?P<name>.*)" FSTYPE="(?P<fstype>.*)" TYPE="(?P<type>.*)" MOUNTPOINT="(?P<mtpt>.*)"$') configuration = {} parsed_devices = [] for device in devices: match = re.match(device_regex, device) if match is None: DiskController._logger.error('Device regex did not match for {0}. Please investigate'.format(device)) raise Exception('Failed to parse \'lsblk\' output') groupdict = match.groupdict() name = groupdict['name'].strip() fs_type = groupdict['fstype'].strip() dev_type = groupdict['type'].strip() mount_point = groupdict['mtpt'].strip() if dev_type == 'rom': continue link = DiskController._local_client.file_read_link(path='/sys/block/{0}'.format(name)) friendly_path = '/dev/{0}'.format(name) system_aliases = sorted(name_alias_mapping.get(friendly_path, [friendly_path])) device_is_also_partition = False if link is not None: # If this returns, it means its a device and not a partition device_is_also_partition = mount_point != '' # LVM, RAID1, ... have the tendency to be a device with a partition on it, but the partition is not reported by 'lsblk' parsed_devices.append(name) configuration[name] = {'name': name, 'aliases': system_aliases, 'partitions': []} if link is None or device_is_also_partition is True: current_device = None if device_is_also_partition is True: current_device = name else: for device_name in reversed(parsed_devices): try: current_device = device_name DiskController._local_client.file_read(filename='/sys/block/{0}/{1}/start'.format(current_device, name)) break except Exception: pass if current_device is None: raise RuntimeError('Failed to retrieve the device information for current partition') mount_point = mount_point if mount_point != '' else None partition_device_map[friendly_path] = current_device configuration[current_device]['partitions'].append({'aliases': system_aliases, 'filesystem': fs_type if fs_type != '' else None, 'mountpoint': mount_point}) # Parse 'configuration' to see which devices can be used as ASD disks = {} for device_name, device_info in configuration.iteritems(): availability = True usable_device = True partition_mtpt = None partition_aliases = [] for partition in device_info['partitions']: partition_mtpt = partition['mountpoint'] partition_filesystem = partition['filesystem'] partition_aliases.extend(partition['aliases']) if partition_mtpt is not None: if partition_mtpt.startswith('/mnt/alba-asd/'): availability = False else: usable_device = False break if partition_filesystem in ['swap', 'linux_raid_member', 'LVM2_member']: usable_device = False break if usable_device is True: usage = {} state = 'ok' state_detail = '' if availability is False: # Check partition usage information df_info = DiskController._local_client.run("df -B 1 --output=size,used,avail '{0}' | tail -1 || true".format(partition_mtpt.replace(r"'", r"'\''")), allow_insecure=True).strip().splitlines() if len(df_info) != 1: DiskController._logger.warning('Verifying usage information for mountpoint {0} failed. Information retrieved: {1}'.format(partition_mtpt, df_info)) continue size, used, available = df_info[0].split() usage = {'size': int(size), 'used': int(used), 'available': int(available)} # Check mountpoint validation output, error = DiskController._local_client.run(['ls', '{0}/'.format(partition_mtpt)], allow_nonzero=True, return_stderr=True) output += error if 'Input/output error' in output: state = 'error' state_detail = 'io_error' aliases = device_info['aliases'] disks[aliases[0]] = {'usage': usage, 'state': state, 'device': '/dev/{0}'.format(device_name), 'aliases': aliases, 'node_id': DiskController.NODE_ID, 'available': availability, 'mountpoint': partition_mtpt, 'state_detail': state_detail, 'partition_amount': len(device_info['partitions']), 'partition_aliases': partition_aliases} # Verify FStab entries are present in 'disks' fstab_disks = FSTab.read() for device_info in disks.itervalues(): for partition_alias, mountpoint in fstab_disks.items(): if partition_alias in device_info['partition_aliases']: fstab_disks.pop(partition_alias) # FSTab entries which are not present in disks (missing disks) are not returned. return disks
def list_disks(): disks = {} # Find used disks # 1. Mounted disks all_mounts = check_output('mount', shell=True).splitlines() used_disks = [] for mount in all_mounts: mount = mount.strip() match = re.search('/dev/(.+?) on (/.*?) type.*', mount) if match is not None and not match.groups()[1].startswith('/mnt/alba-asd/'): used_disks.append(match.groups()[0]) # 2. Disks used in a software raid mdstat = check_output('cat /proc/mdstat', shell=True) for md_match in re.findall('([a-z]+\d+ : (in)?active raid\d+(( [a-z]+\d?\[\d+\])+))', mdstat): for disk_match in re.findall('( ([a-z]+\d?)\[\d+\])', md_match[2]): used_disks.append(disk_match[1].strip()) # Find all disks all_disks = check_output('ls -al /dev/disk/by-id/', shell=True).split('\n') for disk in all_disks: disk = disk.strip() match = re.search('.+?(((scsi-)|(ata-)).+?) -> ../../(sd.+)', disk) if match is not None: disk_id, disk_name = match.groups()[0], match.groups()[-1] if re.search('-part\d+', disk_id) is None: if not any(used_disk for used_disk in used_disks if disk_name in used_disk): disks[disk_id] = {'device': '/dev/disk/by-id/{0}'.format(disk_id), 'available': True, 'state': {'state': 'ok'}} # Load information about mount configuration (detect whether the disks are configured) fstab_disks = FSTab.read() for device in fstab_disks.keys(): for disk_id in disks: if disks[disk_id]['device'] == '/dev/disk/by-id/{0}'.format(device): disks[disk_id].update({'available': False, 'mountpoint': fstab_disks[device], 'asd_id': fstab_disks[device].split('/')[-1]}) del fstab_disks[device] for device in fstab_disks.keys(): disks[device] = {'device': '/dev/disk/by-id/{0}'.format(device), 'available': False, 'mountpoint': fstab_disks[device], 'asd_id': fstab_disks[device].split('/')[-1], 'state': {'state': 'error', 'detail': 'missing'}} # Load statistical information about the disk df_info = check_output('df -k /mnt/alba-asd/* || true', shell=True).strip().split('\n') for disk_id in disks: if 'asd_id' in disks[disk_id]: for df in df_info: match = re.search('\S+?\s+?(\d+?)\s+?(\d+?)\s+?(\d+?)\s.+?/mnt/alba-asd/{0}'.format(disks[disk_id]['asd_id']), df) if match is not None: disks[disk_id].update({'usage': {'size': int(match.groups()[0]) * 1024, 'used': int(match.groups()[1]) * 1024, 'available': int(match.groups()[2]) * 1024}}) # Execute some checkups on the disks for disk_id in disks: if disks[disk_id]['available'] is False and disks[disk_id]['state']['state'] == 'ok': output = check_output('ls {0}/ 2>&1 || true'.format(disks[disk_id]['mountpoint']), shell=True) if 'Input/output error' in output: disks[disk_id]['state'] = {'state': 'error', 'detail': 'ioerror'} return disks
def prepare_disk(cls, disk): """ Prepare a disk for use with ALBA :param disk: Disk object to prepare :type disk: source.dal.objects.disk.Disk :return: None """ if disk.usable is False: raise RuntimeError('Cannot prepare disk {0}'.format(disk.name)) cls._logger.info('Preparing disk {0}'.format(disk.name)) # Create partition mountpoint = '/mnt/alba-asd/{0}'.format(''.join( random.choice(string.ascii_letters + string.digits) for _ in range(16))) alias = disk.aliases[0] cls._locate(device_alias=alias, start=False) cls._local_client.run(['umount', disk.mountpoint], allow_nonzero=True) cls._local_client.run(['parted', alias, '-s', 'mklabel', 'gpt']) cls._local_client.run([ 'parted', alias, '-s', 'mkpart', alias.split('/')[-1], '2MB', '100%' ]) cls._local_client.run(['udevadm', 'settle' ]) # Waits for all udev rules to have finished # Wait for partition to be ready by attempting to add filesystem counter = 0 already_mounted = False while True: disk = Disk(disk.id) if len(disk.partitions) == 1: try: cls._local_client.run( ['mkfs.xfs', '-qf', disk.partition_aliases[0]]) break except CalledProcessError: mountpoint = disk.mountpoint if mountpoint and mountpoint in cls._local_client.run( ['mount']): # Some OSes have auto-mount functionality making mkfs.xfs to fail when the mountpoint has already been mounted # This can occur when the exact same partition gets created on the device already_mounted = True if mountpoint.startswith('/mnt/alba-asd'): cls._local_client.run( 'rm -rf {0}/*'.format(mountpoint), allow_insecure=True) cls._logger.warning( 'Device has already been used by ALBA, re-using mountpoint {0}' .format(mountpoint)) break cls._logger.info('Partition for disk {0} not ready yet'.format( disk.name)) cls.sync_disks() time.sleep(0.2) counter += 1 if counter > 10: raise RuntimeError( 'Partition for disk {0} not ready in 2 seconds'.format( disk.name)) # Create mountpoint and mount cls._local_client.run(['mkdir', '-p', mountpoint]) FSTab.add(partition_aliases=[disk.partition_aliases[0]], mountpoint=mountpoint) if already_mounted is False: cls._local_client.run(['mount', mountpoint]) cls.sync_disks() cls._local_client.run(['chown', '-R', 'alba:alba', mountpoint]) cls._logger.info('Prepare disk {0} complete'.format(disk.name))
def create_asd(partition_alias): """ Creates and starts an ASD on a given disk :param partition_alias: Alias of the partition of a disk (eg: /dev/disk/by-id/scsi-1ATA_TOSHIBA_MK2002TSKB_92M1KDMHF-part1) :type partition_alias: str :return: None """ all_asds = {} mountpoint = None for alias, mtpt in FSTab.read().iteritems(): all_asds.update(ASDController.list_asds(mtpt)) if alias == partition_alias: mountpoint = mtpt if mountpoint is None: raise RuntimeError('Failed to retrieve the mountpoint for partition with alias: {0}'.format(partition_alias)) # Fetch disk information disk_size = int(ASDController._local_client.run(['df', '-B', '1', '--output=size', mountpoint]).splitlines()[1]) # Find out appropriate disk size asds = 1.0 for asd_id in os.listdir(mountpoint): if os.path.isdir('/'.join([mountpoint, asd_id])) and Configuration.exists(ASDController.ASD_CONFIG.format(asd_id)): asds += 1 asd_size = int(math.floor(disk_size / asds)) for asd_id in os.listdir(mountpoint): if os.path.isdir('/'.join([mountpoint, asd_id])) and Configuration.exists(ASDController.ASD_CONFIG.format(asd_id)): config = json.loads(Configuration.get(ASDController.ASD_CONFIG.format(asd_id), raw=True)) config['capacity'] = asd_size config['rocksdb_block_cache_size'] = int(asd_size / 1024 / 4) Configuration.set(ASDController.ASD_CONFIG.format(asd_id), json.dumps(config, indent=4), raw=True) try: ServiceManager.send_signal(ASDController.ASD_SERVICE_PREFIX.format(asd_id), signal.SIGUSR1, ASDController._local_client) except Exception as ex: ASDController._logger.info('Could not send signal to ASD for reloading the quota: {0}'.format(ex)) # Prepare & start service asd_id = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32)) ASDController._logger.info('Setting up service for disk {0}'.format(partition_alias)) homedir = '{0}/{1}'.format(mountpoint, asd_id) base_port = Configuration.get('{0}/network|port'.format(ASDController.CONFIG_ROOT)) ips = Configuration.get('{0}/network|ips'.format(ASDController.CONFIG_ROOT)) used_ports = [] for asd in all_asds.itervalues(): used_ports.append(asd['port']) if 'rora_port' in asd: used_ports.append(asd['rora_port']) asd_port = base_port rora_port = base_port + 1 while asd_port in used_ports: asd_port += 1 used_ports.append(asd_port) while rora_port in used_ports: rora_port += 1 asd_config = {'home': homedir, 'node_id': ASDController.NODE_ID, 'asd_id': asd_id, 'capacity': asd_size, 'log_level': 'info', 'port': asd_port, 'transport': 'tcp', 'rocksdb_block_cache_size': int(asd_size / 1024 / 4)} if Configuration.get('/ovs/framework/rdma'): asd_config['rora_port'] = rora_port asd_config['rora_transport'] = 'rdma' if ips is not None and len(ips) > 0: asd_config['ips'] = ips if Configuration.exists('{0}/extra'.format(ASDController.CONFIG_ROOT)): data = Configuration.get('{0}/extra'.format(ASDController.CONFIG_ROOT)) asd_config.update(data) Configuration.set(ASDController.ASD_CONFIG.format(asd_id), json.dumps(asd_config, indent=4), raw=True) service_name = ASDController.ASD_SERVICE_PREFIX.format(asd_id) params = {'CONFIG_PATH': Configuration.get_configuration_path('/ovs/alba/asds/{0}/config'.format(asd_id)), 'SERVICE_NAME': service_name, 'LOG_SINK': LogHandler.get_sink_path('alba_asd')} os.mkdir(homedir) ASDController._local_client.run(['chown', '-R', 'alba:alba', homedir]) ServiceManager.add_service('alba-asd', ASDController._local_client, params, service_name) ASDController.start_asd(asd_id)
def list_asds(): """ List all ASDs :return: Information about all ASDs on local node :rtype: dict """ return dict((partition_alias, ASDController.list_asds(mountpoint=mountpoint)) for partition_alias, mountpoint in FSTab.read().iteritems())