def osdize_dev(dev, osd_format, osd_journal, reformat_osd=False): if not os.path.exists(dev): log('Path {} does not exist - bailing'.format(dev)) return if not is_block_device(dev): log('Path {} is not a block device - bailing'.format(dev)) return if (is_osd_disk(dev) and not reformat_osd): log('Looks like {} is already an OSD, skipping.'.format(dev)) return if device_mounted(dev): log('Looks like {} is in use, skipping.'.format(dev)) return cmd = ['ceph-disk-prepare'] # Later versions of ceph support more options if get_ceph_version() >= "0.48.3": if osd_format: cmd.append('--fs-type') cmd.append(osd_format) cmd.append(dev) if osd_journal and os.path.exists(osd_journal): cmd.append(osd_journal) else: # Just provide the device - no other options # for older versions of ceph cmd.append(dev) if reformat_osd: zap_disk(dev) subprocess.check_call(cmd)
def get_devices(): devices = [] if config('osd-devices'): for path in config('osd-devices').split(' '): path = path.strip() # Ensure that only block devices # are considered for evaluation as block devices. # This avoids issues with relative directories # being passed via configuration, and ensures that # the path to a block device provided by the user # is used, rather than its target which may change # between reboots in the case of bcache devices. if is_block_device(path): devices.append(path) # Make sure its a device which is specified using an # absolute path so that the current working directory # or any relative path under this directory is not used elif os.path.isabs(path): devices.append(os.path.realpath(path)) # List storage instances for the 'osd-devices' # store declared for this charm too, and add # their block device paths to the list. storage_ids = storage_list('osd-devices') devices.extend((storage_get('location', s) for s in storage_ids)) # Filter out any devices in the action managed unit-local device blacklist _blacklist = get_blacklist() return [device for device in devices if device not in _blacklist]
def ensure_block_device(block_device): ''' Confirm block_device, create as loopback if necessary. :param block_device: str: Full path of block device to ensure. :returns: str: Full path of ensured block device. ''' _none = ['None', 'none', None] if (block_device in _none): error_out('prepare_storage(): Missing required input: block_device=%s.' % block_device) if block_device.startswith('/dev/'): bdev = block_device elif block_device.startswith('/'): _bd = block_device.split('|') if len(_bd) == 2: bdev, size = _bd else: bdev = block_device size = DEFAULT_LOOPBACK_SIZE bdev = ensure_loopback_device(bdev, size) else: bdev = '/dev/%s' % block_device if not is_block_device(bdev): error_out('Failed to locate valid block device at %s' % bdev) return bdev
def test_is_block_device_does_not_exist(self, S_ISBLK, exists, stat): """It detects device node is block device""" class fake_stat: st_mode = True S_ISBLK.return_value = fake_stat() exists.return_value = False self.assertFalse(storage_utils.is_block_device('/dev/foo'))
def test_is_block_device(self, S_ISBLK, exists, stat): '''It detects device node is block device''' class fake_stat: st_mode = True S_ISBLK.return_value = fake_stat() exists.return_value = True self.assertTrue(storage_utils.is_block_device('/dev/foo'))
def osdize_dev(dev, osd_format, osd_journal, reformat_osd=False, ignore_errors=False, encrypt=False): if not os.path.exists(dev): log('Path {} does not exist - bailing'.format(dev)) return if not is_block_device(dev): log('Path {} is not a block device - bailing'.format(dev)) return if (is_osd_disk(dev) and not reformat_osd): log('Looks like {} is already an' ' OSD data or journal, skipping.'.format(dev)) return if is_device_mounted(dev): log('Looks like {} is in use, skipping.'.format(dev)) return status_set('maintenance', 'Initializing device {}'.format(dev)) cmd = ['ceph-disk', 'prepare'] # Later versions of ceph support more options if cmp_pkgrevno('ceph', '0.60') >= 0: if encrypt: cmd.append('--dmcrypt') if cmp_pkgrevno('ceph', '0.48.3') >= 0: if osd_format: cmd.append('--fs-type') cmd.append(osd_format) if reformat_osd: cmd.append('--zap-disk') cmd.append(dev) if osd_journal: least_used = find_least_used_journal(osd_journal) cmd.append(least_used) else: # Just provide the device - no other options # for older versions of ceph cmd.append(dev) if reformat_osd: zap_disk(dev) try: log("osdize cmd: {}".format(cmd)) subprocess.check_call(cmd) except subprocess.CalledProcessError as e: if ignore_errors: log('Unable to initialize device: {}'.format(dev), WARNING) else: log('Unable to initialize device: {}'.format(dev), ERROR) raise e
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)) })
def configure_lxd_block(): '''Configure a block device for use by LXD for containers''' log('Configuring LXD container storage if not configured') # determine if the lxd block has already been configured if has_storage(LXD_POOL): log("LXD storage pool {} already configured".format(LXD_POOL)) return elif 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': config_btrfs(dev) elif config('storage-type') == 'lvm': config_lvm(dev) elif config('storage-type') == 'zfs': config_zfs(dev)
def find_block_devices(): found = [] incl = ['sd[a-z]', 'vd[a-z]', 'cciss\/c[0-9]d[0-9]'] blacklist = ['sda', 'vda', 'cciss/c0d0'] with open('/proc/partitions') as proc: print proc partitions = [p.split() for p in proc.readlines()[2:]] for partition in [p[3] for p in partitions if p]: for inc in incl: _re = re.compile(r'^(%s)$' % inc) if _re.match(partition) and partition not in blacklist: found.append(os.path.join('/dev', partition)) return [f for f in found if is_block_device(f)]
def find_block_devices(include_mounted=False): found = [] incl = ['sd[a-z]', 'vd[a-z]', 'cciss\/c[0-9]d[0-9]'] with open('/proc/partitions') as proc: partitions = [p.split() for p in proc.readlines()[2:]] for partition in [p[3] for p in partitions if p]: for inc in incl: _re = re.compile(r'^(%s)$' % inc) if _re.match(partition): found.append(os.path.join('/dev', partition)) if include_mounted: devs = [f for f in found if is_block_device(f)] else: devs = [f for f in found if _is_storage_ready(f)] return devs
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 = [] devices = get_devices() for device in devices: 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 failed_devices or not_block_devices: message = "" if failed_devices: message = "{} devices are mounted: {}".format( len(failed_devices), ", ".join(failed_devices)) if not_block_devices: if message is not '': 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)) })
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 = [] devices = get_devices() for device in devices: 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 failed_devices or not_block_devices: message = "" 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)) })
def ensure_block_device(block_device): ''' Confirm block_device, create as loopback if necessary. :param block_device: str: Full path of block device to ensure. :returns: str: Full path of ensured block device. ''' if block_device.startswith("/"): # Resolve non-relative link(s) if device is a symlink to a real device. # This fixes/prevents bug #1577408. real_device = os.path.realpath(block_device) if real_device != block_device: log('Block device "{}" is a symlink to "{}". Using "{}".'.format( block_device, real_device, real_device), level=INFO) block_device = real_device _none = ['None', 'none', None] if (block_device in _none): log('prepare_storage(): Missing required input: ' 'block_device=%s.' % block_device, level=ERROR) raise if block_device.startswith('/dev/'): bdev = block_device elif block_device.startswith('/'): _bd = block_device.split('|') if len(_bd) == 2: bdev, size = _bd else: bdev = block_device size = DEFAULT_LOOPBACK_SIZE bdev = ensure_loopback_device(bdev, size) else: bdev = '/dev/%s' % block_device if not is_block_device(bdev): log('Failed to locate valid block device at %s' % bdev, level=ERROR) # ignore missing block devices return return bdev
def osdize_dev(dev, osd_format, osd_journal, reformat_osd=False, ignore_errors=False): if not os.path.exists(dev): log('Path {} does not exist - bailing'.format(dev)) return if not is_block_device(dev): log('Path {} is not a block device - bailing'.format(dev)) return if (is_osd_disk(dev) and not reformat_osd): log('Looks like {} is already an OSD, skipping.'.format(dev)) return if is_device_mounted(dev): log('Looks like {} is in use, skipping.'.format(dev)) return cmd = ['ceph-disk-prepare'] # Later versions of ceph support more options if cmp_pkgrevno('ceph', '0.48.3') >= 0: if osd_format: cmd.append('--fs-type') cmd.append(osd_format) if reformat_osd: cmd.append('--zap-disk') cmd.append(dev) if osd_journal and os.path.exists(osd_journal): cmd.append(osd_journal) else: # Just provide the device - no other options # for older versions of ceph cmd.append(dev) if reformat_osd: zap_disk(dev) try: subprocess.check_call(cmd) except subprocess.CalledProcessError as e: if ignore_errors: log('Unable to initialize device: {}'.format(dev), WARNING) else: log('Unable to initialize device: {}'.format(dev), ERROR) raise e
def encrypt_device(device, mountpoint=None, uuid=None): """ Set up encryption for the given block device, and optionally create and mount an XFS filesystem on the encrypted device. If ``mountpoint`` is not given, the device will not be formatted or mounted. When interacting with or mounting the device manually, the name returned by :func:`decrypted_device` called on the device name should be used in place of the raw device name. """ if not is_block_device(device): raise VaultLockerError('Cannot encrypt non-block device: {}', device) if is_device_mounted(device): raise VaultLockerError('Cannot encrypt mounted device: {}', device) hookenv.log('Encrypting device: {}'.format(device)) if uuid is None: uuid = str(uuid4()) try: check_call(['vaultlocker', 'encrypt', '--uuid', uuid, device]) unitdata.kv().set('layer.vaultlocker.uuids.{}'.format(device), uuid) if mountpoint: mapped_device = decrypted_device(device) hookenv.log('Creating filesystem on {} ({})'.format( mapped_device, device)) # If this fails, it's probalby due to the size of the loopback # backing file that is defined by the `dd`. mkfs_xfs(mapped_device) Path(mountpoint).mkdir(mode=0o755, parents=True, exist_ok=True) hookenv.log('Mounting filesystem for {} ({}) at {}' ''.format(mapped_device, device, mountpoint)) host.mount(mapped_device, mountpoint, filesystem='xfs') host.fstab_add( mapped_device, mountpoint, 'xfs', ','.join([ "defaults", "nofail", "x-systemd.requires=vaultlocker-decrypt@{uuid}.service". format(uuid=uuid, ), "comment=vaultlocker", ])) except (CalledProcessError, OSError) as e: raise VaultLockerError('Error configuring VaultLocker') from e
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()
def _is_storage_ready(partition): """ A small helper to determine if a given device is suitabe to be used as a storage device. """ return is_block_device(partition) and not is_device_mounted(partition)
def configure_local_ephemeral_storage(): """Configure local block device for use as ephemeral instance storage""" # Preflight check vault relation if encryption is enabled vault_kv = vaultlocker.VaultKVContext( secret_backend=vaultlocker.VAULTLOCKER_BACKEND) context = vault_kv() encrypt = config('encrypt') if encrypt and not vault_kv.complete: log("Encryption requested but vault relation not complete", level=DEBUG) return elif encrypt and vault_kv.complete: # NOTE: only write vaultlocker configuration once relation is complete # otherwise we run the chance of an empty configuration file # being installed on a machine with other vaultlocker based # services vaultlocker.write_vaultlocker_conf(context, priority=80) db = kv() storage_configured = db.get('storage-configured', False) if storage_configured: log("Ephemeral storage already configured, skipping", level=DEBUG) return dev = determine_block_device() if not dev: log('No block device configuration found, skipping', level=DEBUG) return if not is_block_device(dev): log("Device '{}' is not a block device, " "unable to configure storage".format(dev), level=DEBUG) return # NOTE: this deals with a dm-crypt'ed block device already in # use if is_device_mounted(dev): log("Device '{}' is already mounted, " "unable to configure storage".format(dev), level=DEBUG) return options = None if encrypt: dev_uuid = str(uuid.uuid4()) check_call(['vaultlocker', 'encrypt', '--uuid', dev_uuid, dev]) dev = '/dev/mapper/crypt-{}'.format(dev_uuid) options = ','.join([ "defaults", "nofail", ("x-systemd.requires=" "vaultlocker-decrypt@{uuid}.service".format(uuid=dev_uuid)), "comment=vaultlocker", ]) # If not cleaned and in use, mkfs should fail. mkfs_xfs(dev, force=True) mountpoint = '/var/lib/nova/instances' filesystem = "xfs" mount(dev, mountpoint, filesystem=filesystem) fstab_add(dev, mountpoint, filesystem, options=options) check_call(['chown', '-R', 'nova:nova', mountpoint]) check_call(['chmod', '-R', '0755', mountpoint]) # NOTE: record preparation of device - this ensures that ephemeral # storage is never reconfigured by mistake, losing instance disks db.set('storage-configured', True) db.flush()
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)
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()
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)