Esempio n. 1
0
def prepare_lvm_storage(block_device, volume_group):
    '''
    Ensures block_device is initialized as a LVM PV and creates volume_group.
    Assumes block device is clean and will raise if storage is already
    initialized as a PV.

    :param block_device: str: Full path to block device to be prepared.
    :param volume_group: str: Name of volume group to be created with
                              block_device as backing PV.

    :returns: None or raises CinderCharmError if storage is unclean.
    '''
    e = None
    if is_lvm_physical_volume(block_device):
        juju_log('ERROR: Could not prepare LVM storage: %s is already '
                 'initialized as LVM physical device.' % block_device)
        raise CinderCharmError

    try:
        create_lvm_physical_volume(block_device)
        create_lvm_volume_group(volume_group, block_device)
    except Exception as e:
        juju_log('Could not prepare LVM storage on %s.' % block_device)
        juju_log(e)
        raise CinderCharmError
Esempio n. 2
0
def config_lvm(dev):
    if (is_lvm_physical_volume(dev) and
            list_lvm_volume_group(dev) == VG_NAME):
        log('Device already configured for LVM/LXD, skipping')
        return
    status_set('maintenance',
               'Configuring LVM container storage')

    cmd = ['systemctl', 'enable', 'lvm2-lvmetad']
    check_call(cmd)
    cmd = ['systemctl', 'start', 'lvm2-lvmetad']
    check_call(cmd)
    if has_storage():
        cmd = ['lxc', 'storage', 'create', LXD_POOL, 'lvm',
               'source={}'.format(dev), 'lvm.vg_name={}'.format(VG_NAME)]
        check_call(cmd)
    else:
        create_lvm_physical_volume(dev)
        create_lvm_volume_group(VG_NAME, dev)
        cmd = ['lxc', 'config', 'set', 'storage.lvm_vg_name', VG_NAME]
        check_call(cmd)

    # The LVM thinpool logical volume is lazily created, either on
    # image import or container creation. This will force LV creation.
    create_and_import_busybox_image()
Esempio n. 3
0
def config_lvm(dev):
    if (is_lvm_physical_volume(dev) and list_lvm_volume_group(dev) == VG_NAME):
        log('Device already configured for LVM/LXD, skipping')
        return
    status_set('maintenance', 'Configuring LVM container storage')

    cmd = ['systemctl', 'enable', 'lvm2-lvmetad']
    check_call(cmd)
    cmd = ['systemctl', 'start', 'lvm2-lvmetad']
    check_call(cmd)
    if has_storage():
        cmd = [
            'lxc', 'storage', 'create', LXD_POOL, 'lvm',
            'source={}'.format(dev), 'lvm.vg_name={}'.format(VG_NAME)
        ]
        check_call(cmd)
    else:
        create_lvm_physical_volume(dev)
        create_lvm_volume_group(VG_NAME, dev)
        cmd = ['lxc', 'config', 'set', 'storage.lvm_vg_name', VG_NAME]
        check_call(cmd)

    # The LVM thinpool logical volume is lazily created, either on
    # image import or container creation. This will force LV creation.
    create_and_import_busybox_image()
Esempio n. 4
0
def zap():
    if not hookenv.action_get('i-really-mean-it'):
        hookenv.action_fail('i-really-mean-it is a required parameter')
        return

    failed_devices = []
    not_block_devices = []
    lvm_devices = []
    try:
        devices = get_devices()
    except ZapDiskError as error:
        hookenv.action_fail("Failed due to: {}".format(error))
        return

    for device in devices:
        if is_lvm_physical_volume(device):
            lvm_devices.append(device)
        if not is_block_device(device):
            not_block_devices.append(device)
        if (is_device_mounted(device) or is_active_bluestore_device(device)
                or is_mapped_luks_device(device)):
            failed_devices.append(device)

    if lvm_devices or failed_devices or not_block_devices:
        message = ""
        if lvm_devices:
            log('Cannot zap a device used by lvm')
            message = "{} devices are lvm devices: {}".format(
                len(lvm_devices), ", ".join(lvm_devices))
        if failed_devices:
            message += "{} devices are mounted: {}".format(
                len(failed_devices), ", ".join(failed_devices))
        if not_block_devices:
            if len(message):
                message += "\n\n"
            message += "{} devices are not block devices: {}".format(
                len(not_block_devices), ", ".join(not_block_devices))
        hookenv.action_fail(message)
        return
    db = kv()
    used_devices = db.get('osd-devices', [])
    for device in devices:
        zap_disk(device)
        if device in used_devices:
            used_devices.remove(device)
    db.set('osd-devices', used_devices)
    db.flush()
    hookenv.action_set({
        'message':
        "{} disk(s) have been zapped, to use them as OSDs, run: \n"
        "juju run-action {} add-disk osd-devices=\"{}\"".format(
            len(devices), hookenv.local_unit(), " ".join(devices))
    })
Esempio n. 5
0
def clean_storage(block_device):
    '''Ensures a block device is clean.  That is:
        - unmounted
        - any lvm volume groups are deactivated
        - any lvm physical device signatures removed
        - partition table wiped

    :param block_device: str: Full path to block device to clean.
    '''
    for mp, d in mounts():
        if d == block_device:
            juju_log('clean_storage(): Found %s mounted @ %s, unmounting.' %
                     (d, mp))
            umount(mp, persist=True)

    if is_lvm_physical_volume(block_device):
        deactivate_lvm_volume_group(block_device)
        remove_lvm_physical_volume(block_device)

    zap_disk(block_device)
Esempio n. 6
0
def clean_storage(block_device):
    '''Ensures a block device is clean.  That is:
        - unmounted
        - any lvm volume groups are deactivated
        - any lvm physical device signatures removed
        - partition table wiped

    :param block_device: str: Full path to block device to clean.
    '''
    for mp, d in mounts():
        if d == block_device:
            log('clean_storage(): Found %s mounted @ %s, unmounting.' %
                (d, mp))
            umount(mp, persist=True)

    if is_lvm_physical_volume(block_device):
        deactivate_lvm_volume_group(block_device)
        remove_lvm_physical_volume(block_device)

    zap_disk(block_device)
Esempio n. 7
0
def manage_lvm(device, dirname):
    """Add the device to the VG corresponding to the dirname.

    In this case, logical volume name will be dirname with /
    replaced by _ symbols. If logical volume already exists,
    then, device is added as a new physical device and the entire
    logical volume is resized.

    If logical volume name does not exist, then prepare the
    physical device and add to the newly created volume.
    """
    lv_name = "lvm" + dirname.replace("/", "_")
    vg_name = "vg" + dirname.replace("/", "_")
    pv = device
    try:
        if list_lvm_volume_group(device):
            # PV already used, raise this as an issue
            pv = None
    except subprocess.CalledProcessError:
        # Device does not exist in the list, continue
        pass
    if not pv:
        raise DiskMapHelperPVAlreadyTakenForLVM(device, lv_name)
    if not is_lvm_physical_volume(device):
        # This is not a PV yet, create one
        create_lvm_physical_volume(device)
    if lv_name not in list_logical_volumes():
        # lv_name is always associated 1:1 with vg_name
        # If one does not exist, so we need to create both.
        create_lvm_volume_group(vg_name, pv)
        create_logical_volume(lv_name, vg_name)
    else:
        # Now extend the lv_name:
        extend_logical_volume_by_device(lv_name, pv)
        # TODO: find FS already present and resize it as well
    return "/dev/{}/{}".format(vg_name, lv_name)
Esempio n. 8
0
def configure_lvm_storage(block_devices,
                          volume_group,
                          overwrite=False,
                          remove_missing=False,
                          remove_missing_force=False):
    ''' Configure LVM storage on the list of block devices provided

    :param block_devices: list: List of whitelisted block devices to detect
                                and use if found
    :param overwrite: bool: Scrub any existing block data if block device is
                            not already in-use
    :param remove_missing: bool: Remove missing physical volumes from volume
                           group if logical volume not allocated on them
    :param remove_missing_force: bool: Remove missing physical volumes from
                           volume group even if logical volumes are allocated
                           on them. Overrides 'remove_missing' if set.
    '''
    log_lvm_info()
    devices = []
    for block_device in block_devices:
        (block_device, size) = _parse_block_device(block_device)

        if not is_device_mounted(block_device):
            if size == 0 and is_block_device(block_device):
                devices.append(block_device)
            elif size > 0:
                devices.append(ensure_loopback_device(block_device, str(size)))

    # NOTE(jamespage)
    # might need todo an initial one-time scrub on install if need be
    vg_found = False
    new_devices = []
    for device in devices:
        if not is_lvm_physical_volume(device):
            # Unused device
            if overwrite is True or not has_partition_table(device):
                prepare_volume(device)
                new_devices.append(device)
        elif (is_lvm_physical_volume(device)
              and list_lvm_volume_group(device) != volume_group):
            # Existing LVM but not part of required VG or new device
            if overwrite is True:
                prepare_volume(device)
                new_devices.append(device)
        elif (is_lvm_physical_volume(device)
              and list_lvm_volume_group(device) == volume_group):
            # Mark vg as found
            vg_found = True

    log_lvm_info()

    if vg_found is False and len(new_devices) > 0:
        if overwrite:
            ensure_lvm_volume_group_non_existent(volume_group)

        # Create new volume group from first device
        create_lvm_volume_group(volume_group, new_devices[0])
        new_devices.remove(new_devices[0])

    # Remove missing physical volumes from volume group
    try:
        if remove_missing_force:
            reduce_lvm_volume_group_missing(volume_group,
                                            extra_args=['--force'])
        elif remove_missing:
            reduce_lvm_volume_group_missing(volume_group)
    except subprocess.CalledProcessError as e:
        juju_log(
            "reduce_lvm_volume_group_missing() didn't complete."
            " LVM may not be fully configured yet.  Error was: '{}'.".format(
                str(e)))

    if len(new_devices) > 0:
        # Extend the volume group as required
        for new_device in new_devices:
            extend_lvm_volume_group(volume_group, new_device)
            thin_pools = list_thin_logical_volume_pools(path_mode=True)
            if len(thin_pools) == 0:
                juju_log("No thin pools found")
            elif len(thin_pools) == 1:
                juju_log("Thin pool {} found, extending with {}".format(
                    thin_pools[0], new_device))
                extend_logical_volume_by_device(thin_pools[0], new_device)
            else:
                juju_log("Multiple thin pools ({}) found, "
                         "skipping auto extending with {}".format(
                             ','.join(thin_pools), new_device))
    log_lvm_info()
Esempio n. 9
0
 def test_is_not_physical_volume(self):
     '''It properly reports block dev is an LVM PV'''
     with patch(STORAGE_LINUX_LVM + '.check_output') as check_output:
         check_output.side_effect = subprocess.CalledProcessError('cmd', 2)
         self.assertFalse(lvm.is_lvm_physical_volume('/dev/loop0'))
Esempio n. 10
0
 def test_is_physical_volume(self):
     '''It properly reports block dev is an LVM PV'''
     with patch(STORAGE_LINUX_LVM + '.check_output') as check_output:
         check_output.return_value = PVDISPLAY
         self.assertTrue(lvm.is_lvm_physical_volume('/dev/loop0'))
Esempio n. 11
0
def configure_lxd_block():
    '''Configure a block device for use by LXD for containers'''
    log('Configuring LXD container storage')
    if filesystem_mounted('/var/lib/lxd'):
        log('/var/lib/lxd already configured, skipping')
        return

    lxd_block_devices = get_block_devices()
    if len(lxd_block_devices) < 1:
        log('block devices not provided - skipping')
        return
    if len(lxd_block_devices) > 1:
        raise NotImplementedError('Multiple block devices are not supported.')
    lxd_block_device = lxd_block_devices[0]

    dev = None
    if lxd_block_device.startswith('/dev/'):
        dev = lxd_block_device
    elif lxd_block_device.startswith('/'):
        log('Configuring loopback device for use with LXD')
        _bd = lxd_block_device.split('|')
        if len(_bd) == 2:
            dev, size = _bd
        else:
            dev = lxd_block_device
            size = DEFAULT_LOOPBACK_SIZE
        dev = ensure_loopback_device(dev, size)

    if not dev or not is_block_device(dev):
        log('Invalid block device provided: %s' % lxd_block_device)
        return

    # NOTE: check overwrite and ensure its only execute once.
    db = kv()
    if config('overwrite') and not db.get('scrubbed', False):
        clean_storage(dev)
        db.set('scrubbed', True)
        db.flush()

    if not os.path.exists('/var/lib/lxd'):
        mkdir('/var/lib/lxd')

    if config('storage-type') == 'btrfs':
        status_set('maintenance',
                   'Configuring btrfs container storage')
        lxd_stop()
        cmd = ['mkfs.btrfs', '-f', dev]
        check_call(cmd)
        mount(dev,
              '/var/lib/lxd',
              options='user_subvol_rm_allowed',
              persist=True,
              filesystem='btrfs')
        cmd = ['btrfs', 'quota', 'enable', '/var/lib/lxd']
        check_call(cmd)
        lxd_start()
    elif config('storage-type') == 'lvm':
        if (is_lvm_physical_volume(dev) and
                list_lvm_volume_group(dev) == 'lxd_vg'):
            log('Device already configured for LVM/LXD, skipping')
            return
        status_set('maintenance',
                   'Configuring LVM container storage')
        # Enable and startup lvm2-lvmetad to avoid extra output
        # in lvm2 commands, which confused lxd.
        cmd = ['systemctl', 'enable', 'lvm2-lvmetad']
        check_call(cmd)
        cmd = ['systemctl', 'start', 'lvm2-lvmetad']
        check_call(cmd)
        create_lvm_physical_volume(dev)
        create_lvm_volume_group('lxd_vg', dev)
        cmd = ['lxc', 'config', 'set', 'storage.lvm_vg_name', 'lxd_vg']
        check_call(cmd)

        # The LVM thinpool logical volume is lazily created, either on
        # image import or container creation. This will force LV creation.
        create_and_import_busybox_image()
    elif config('storage-type') == 'zfs':
        status_set('maintenance',
                   'Configuring zfs container storage')
        if ZFS_POOL_NAME in zpools():
            log('ZFS pool already exist; skipping zfs configuration')
            return

        if config('overwrite'):
            cmd = ['zpool', 'create', '-f', ZFS_POOL_NAME, dev]
        else:
            cmd = ['zpool', 'create', ZFS_POOL_NAME, dev]
        check_call(cmd)

        cmd = ['lxc', 'config', 'set', 'storage.zfs_pool_name',
               ZFS_POOL_NAME]
        check_call(cmd)
Esempio n. 12
0
def configure_lvm_storage(block_devices, volume_group, overwrite=False,
                          remove_missing=False, remove_missing_force=False):
    ''' Configure LVM storage on the list of block devices provided

    :param block_devices: list: List of whitelisted block devices to detect
                                and use if found
    :param overwrite: bool: Scrub any existing block data if block device is
                            not already in-use
    :param remove_missing: bool: Remove missing physical volumes from volume
                           group if logical volume not allocated on them
    :param remove_missing_force: bool: Remove missing physical volumes from
                           volume group even if logical volumes are allocated
                           on them. Overrides 'remove_missing' if set.
    '''
    log_lvm_info()
    devices = []
    for block_device in block_devices:
        (block_device, size) = _parse_block_device(block_device)

        if not is_device_mounted(block_device):
            if size == 0 and is_block_device(block_device):
                devices.append(block_device)
            elif size > 0:
                devices.append(ensure_loopback_device(block_device, size))

    # NOTE(jamespage)
    # might need todo an initial one-time scrub on install if need be
    vg_found = False
    new_devices = []
    for device in devices:
        if not is_lvm_physical_volume(device):
            # Unused device
            if overwrite is True or not has_partition_table(device):
                prepare_volume(device)
                new_devices.append(device)
        elif (is_lvm_physical_volume(device) and
              list_lvm_volume_group(device) != volume_group):
            # Existing LVM but not part of required VG or new device
            if overwrite is True:
                prepare_volume(device)
                new_devices.append(device)
        elif (is_lvm_physical_volume(device) and
                list_lvm_volume_group(device) == volume_group):
            # Mark vg as found
            vg_found = True

    log_lvm_info()

    if vg_found is False and len(new_devices) > 0:
        if overwrite:
            ensure_lvm_volume_group_non_existent(volume_group)

        # Create new volume group from first device
        create_lvm_volume_group(volume_group, new_devices[0])
        new_devices.remove(new_devices[0])

    # Remove missing physical volumes from volume group
    if remove_missing_force:
        reduce_lvm_volume_group_missing(volume_group, extra_args=['--force'])
    elif remove_missing:
        reduce_lvm_volume_group_missing(volume_group)

    if len(new_devices) > 0:
        # Extend the volume group as required
        for new_device in new_devices:
            extend_lvm_volume_group(volume_group, new_device)

    log_lvm_info()
Esempio n. 13
0
def configure_lvm_storage(block_devices,
                          volume_group,
                          overwrite=False,
                          remove_missing=False,
                          remove_missing_force=False):
    ''' Configure LVM storage on the list of block devices provided

    :param block_devices: list: List of whitelisted block devices to detect
                                and use if found
    :param overwrite: bool: Scrub any existing block data if block device is
                            not already in-use
    :param remove_missing: bool: Remove missing physical volumes from volume
                           group if logical volume not allocated on them
    :param remove_missing_force: bool: Remove missing physical volumes from
                           volume group even if logical volumes are allocated
                           on them. Overrides 'remove_missing' if set.
    '''
    log_lvm_info()
    devices = []
    for block_device in block_devices:
        (block_device, size) = _parse_block_device(block_device)

        if not is_device_mounted(block_device):
            if size == 0 and is_block_device(block_device):
                devices.append(block_device)
            elif size > 0:
                devices.append(ensure_loopback_device(block_device, size))

    # NOTE(jamespage)
    # might need todo an initial one-time scrub on install if need be
    vg_found = False
    new_devices = []
    for device in devices:
        if not is_lvm_physical_volume(device):
            # Unused device
            if overwrite is True or not has_partition_table(device):
                prepare_volume(device)
                new_devices.append(device)
        elif (is_lvm_physical_volume(device)
              and list_lvm_volume_group(device) != volume_group):
            # Existing LVM but not part of required VG or new device
            if overwrite is True:
                prepare_volume(device)
                new_devices.append(device)
        elif (is_lvm_physical_volume(device)
              and list_lvm_volume_group(device) == volume_group):
            # Mark vg as found
            vg_found = True

    log_lvm_info()

    if vg_found is False and len(new_devices) > 0:
        if overwrite:
            ensure_lvm_volume_group_non_existent(volume_group)

        # Create new volume group from first device
        create_lvm_volume_group(volume_group, new_devices[0])
        new_devices.remove(new_devices[0])

    # Remove missing physical volumes from volume group
    if remove_missing_force:
        reduce_lvm_volume_group_missing(volume_group, extra_args=['--force'])
    elif remove_missing:
        reduce_lvm_volume_group_missing(volume_group)

    if len(new_devices) > 0:
        # Extend the volume group as required
        for new_device in new_devices:
            extend_lvm_volume_group(volume_group, new_device)

    log_lvm_info()
Esempio n. 14
0
def configure_lxd_block():
    '''Configure a block device for use by LXD for containers'''
    log('Configuring LXD container storage')
    if filesystem_mounted('/var/lib/lxd'):
        log('/var/lib/lxd already configured, skipping')
        return

    lxd_block_devices = get_block_devices()
    if len(lxd_block_devices) < 1:
        log('block devices not provided - skipping')
        return
    if len(lxd_block_devices) > 1:
        log("More than one block device is not supported yet, only"
            " using the first")
    lxd_block_device = lxd_block_devices[0]

    dev = None
    if lxd_block_device.startswith('/dev/'):
        dev = lxd_block_device
    elif lxd_block_device.startswith('/'):
        log('Configuring loopback device for use with LXD')
        _bd = lxd_block_device.split('|')
        if len(_bd) == 2:
            dev, size = _bd
        else:
            dev = lxd_block_device
            size = DEFAULT_LOOPBACK_SIZE
        dev = ensure_loopback_device(dev, size)

    if not dev or not is_block_device(dev):
        log('Invalid block device provided: %s' % lxd_block_device)
        return

    # NOTE: check overwrite and ensure its only execute once.
    db = kv()
    if config('overwrite') and not db.get('scrubbed', False):
        clean_storage(dev)
        db.set('scrubbed', True)
        db.flush()

    if not os.path.exists('/var/lib/lxd'):
        mkdir('/var/lib/lxd')

    if config('storage-type') == 'btrfs':
        status_set('maintenance', 'Configuring btrfs container storage')
        lxd_stop()
        cmd = ['mkfs.btrfs', '-f', dev]
        check_call(cmd)
        mount(dev,
              '/var/lib/lxd',
              options='user_subvol_rm_allowed',
              persist=True,
              filesystem='btrfs')
        cmd = ['btrfs', 'quota', 'enable', '/var/lib/lxd']
        check_call(cmd)
        lxd_start()
    elif config('storage-type') == 'lvm':
        if (is_lvm_physical_volume(dev)
                and list_lvm_volume_group(dev) == 'lxd_vg'):
            log('Device already configured for LVM/LXD, skipping')
            return
        status_set('maintenance', 'Configuring LVM container storage')
        # Enable and startup lvm2-lvmetad to avoid extra output
        # in lvm2 commands, which confused lxd.
        cmd = ['systemctl', 'enable', 'lvm2-lvmetad']
        check_call(cmd)
        cmd = ['systemctl', 'start', 'lvm2-lvmetad']
        check_call(cmd)
        create_lvm_physical_volume(dev)
        create_lvm_volume_group('lxd_vg', dev)
        cmd = ['lxc', 'config', 'set', 'storage.lvm_vg_name', 'lxd_vg']
        check_call(cmd)

        # The LVM thinpool logical volume is lazily created, either on
        # image import or container creation. This will force LV creation.
        create_and_import_busybox_image()
    elif config('storage-type') == 'zfs':
        status_set('maintenance', 'Configuring zfs container storage')
        if ZFS_POOL_NAME in zpools():
            log('ZFS pool already exist; skipping zfs configuration')
            return

        if config('overwrite'):
            cmd = ['zpool', 'create', '-f', ZFS_POOL_NAME, dev]
        else:
            cmd = ['zpool', 'create', ZFS_POOL_NAME, dev]
        check_call(cmd)

        cmd = ['lxc', 'config', 'set', 'storage.zfs_pool_name', ZFS_POOL_NAME]
        check_call(cmd)