示例#1
0
    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))
示例#2
0
 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))
示例#3
0
 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)
示例#4
0
 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)
示例#5
0
    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))
示例#6
0
 def list_asds():
     """
     List all ASDs
     """
     asds = {}
     mountpoints = FSTab.read()
     for disk, mountpoint in mountpoints.iteritems():
         asds[disk] = ASDController.list_asds(mountpoint)
     return asds
示例#7
0
 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))
示例#8
0
 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)
示例#9
0
 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)
示例#10
0
 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]))
示例#11
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]))
示例#12
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))
示例#13
0
 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]
示例#14
0
 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))
示例#15
0
    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)
示例#16
0
 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)
示例#17
0
 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)
示例#18
0
 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))
示例#19
0
 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
示例#20
0
 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)
示例#21
0
    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
示例#22
0
    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
示例#23
0
    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
示例#24
0
    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))
示例#25
0
    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)
示例#26
0
 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())