def activate(self, zpool): """Activates the zpool for iocage usage""" pools = list(self.zfs.pools) prop = "org.freebsd.ioc:active" match = False for pool in pools: if pool.name == zpool: match = True if not match: ioc_common.logit( { "level": "EXCEPTION", "message": f"ZFS pool '{zpool}' not found!" }, _callback=self.callback, silent=self.silent) for pool in pools: ds = self.zfs.get_dataset(pool.name) if pool.name == zpool: ds.properties[prop] = libzfs.ZFSUserProperty("yes") else: ds.properties[prop] = libzfs.ZFSUserProperty("no") self.__remove_activate_comment(pool)
def activate(self, pool): """Activates a pool for iocage usage, and deactivates the rest.""" zfs = libzfs.ZFS(history=True, history_prefix="<iocage>") pools = zfs.pools prop = "org.freebsd.ioc:active" for _pool in pools: if _pool.name == pool: ds = zfs.get_dataset(_pool.name) ds.properties[prop] = libzfs.ZFSUserProperty("yes") else: ds = zfs.get_dataset(_pool.name) ds.properties[prop] = libzfs.ZFSUserProperty("no") return True
def _get_or_create_dataset(self, name, root_name=None, pool=None, mountpoint=None): if not libiocage.lib.helpers.validate_name(name): raise NameError(f"Invalid 'name' for Dataset: {name}") try: return self.datasets[name] except (AttributeError, KeyError): pass if root_name is None: root_name = self.root.name if pool is None: pool = self.root.pool name = f"{root_name}/{name}" try: dataset = self.zfs.get_dataset(name) except: pool.create(name, {}) dataset = self.zfs.get_dataset(name) if mountpoint is not None: mountpoint_property = libzfs.ZFSUserProperty(mountpoint) dataset.properties["mountpoint"] = mountpoint_property dataset.mount() self._datasets[name] = dataset return dataset
def zfs_set_property(self, identifier, key, value): ds = self._zfs_get_properties(identifier) if ":" in key: ds[key] = libzfs.ZFSUserProperty(value) else: ds[key].value = value
def do_update(self, id, data): try: zfs = libzfs.ZFS() dataset = zfs.get_dataset(id) if 'properties' in data: for k, v in data['properties'].items(): # If prop already exists we just update it, # otherwise create a user property prop = dataset.properties.get(k) if prop: if v.get('source') == 'INHERIT': prop.inherit() elif 'value' in v and prop.value != v['value']: prop.value = v['value'] elif 'parsed' in v and prop.parsed != v['parsed']: prop.parsed = v['parsed'] else: if 'value' not in v: raise ValidationError( 'properties', f'properties.{k} needs a "value" attribute') if ':' not in k: raise ValidationError( 'properties', f'User property needs a colon (:) in its name`' ) prop = libzfs.ZFSUserProperty(v['value']) dataset.properties[k] = prop except libzfs.ZFSException as e: self.logger.error('Failed to update dataset', exc_info=True) raise CallError(f'Failed to update dataset: {e}')
def activate_pool( self, pool: libzfs.ZFSPool, mountpoint: typing.Optional[iocage.lib.Types.AbsolutePath]=None ) -> None: if self._is_pool_active(pool): msg = f"ZFS pool '{pool.name}' is already active" self.logger.warn(msg) if not isinstance(pool, libzfs.ZFSPool): raise iocage.lib.errors.ZFSPoolInvalid("cannot activate") if pool.status == "UNAVAIL": raise iocage.lib.errors.ZFSPoolUnavailable(pool.name) other_pools = filter(lambda x: x.name != pool.name, self.zfs.pools) for other_pool in other_pools: self._deactivate_pool(other_pool) self._activate_pool(pool) if (mountpoint is None) and (os.path.ismount('/iocage') is False): self.logger.spam( "Claiming /iocage as mountpoint of the activated zpool" ) mountpoint = iocage.lib.Types.AbsolutePath('/iocage') if self.root.mountpoint != mountpoint: zfs_property = libzfs.ZFSUserProperty(mountpoint) self.root.properties["mountpoint"] = zfs_property
async def do_create(self, data): """ Take a snapshot from a given dataset. Returns: bool: True if succeed otherwise False. """ dataset = data.get('dataset', '') name = data.get('name', '') recursive = data.get('recursive', False) vmsnaps_count = data.get('vmsnaps_count', 0) properties = data.get('properties', None) if not dataset or not name: return False try: with libzfs.ZFS() as zfs: ds = zfs.get_dataset(dataset) ds.snapshot(f'{dataset}@{name}', recursive=recursive, fsopts=properties) if vmsnaps_count > 0: ds.properties['freenas:vmsynced'] = libzfs.ZFSUserProperty( 'Y') self.logger.info(f"Snapshot taken: {dataset}@{name}") return True except libzfs.ZFSException as err: self.logger.error(f"{err}") return False
def update_zfs_object_props(self, properties, zfs_object): for k, v in properties.items(): # If prop already exists we just update it, # otherwise create a user property prop = zfs_object.properties.get(k) try: if prop: if v.get('source') == 'INHERIT': prop.inherit(recursive=v.get('recursive', False)) elif 'value' in v and (prop.value != v['value'] or prop.source.name == 'INHERITED'): prop.value = v['value'] elif 'parsed' in v and (prop.parsed != v['parsed'] or prop.source.name == 'INHERITED'): prop.parsed = v['parsed'] else: if v.get('source') == 'INHERIT': pass else: if 'value' not in v: raise ValidationError( 'properties', f'properties.{k} needs a "value" attribute') if ':' not in k: raise ValidationError( 'properties', f'User property needs a colon (:) in its name`' ) prop = libzfs.ZFSUserProperty(v['value']) zfs_object.properties[k] = prop except libzfs.ZFSException as e: raise ZFSSetPropertyError(k, str(e))
def write(self, data: dict) -> None: """ Writes changes to the config file """ output_data = {} for key, value in data.items(): output_data[key] = self._to_string(value) # ToDo: Delete unnecessary ZFS options # existing_property_names = list( # map(lambda x: get_iocage_property_name(x), # filter( # lambda name: is_iocage_property(name), # self.jail.dataset.properties # ) # ) # ) # data_keys = list(self.data) # for existing_property_name in existing_property_names: # if not existing_property_name in data_keys: # pass for key, value in output_data.items(): prop_name = f"{ZFS_PROPERTY_PREFIX}{key}" prop = libzfs.ZFSUserProperty(self._to_string(value)) self.dataset.properties[prop_name] = prop
def _set_zfs_property(self, dataset: libzfs.ZFSDataset, name: str, value: str) -> None: current_value = self._get_dataset_property(dataset, name) if current_value != value: self.logger.verbose(f"Set ZFS property {name}='{value}'" f" on dataset '{dataset.name}'") dataset.properties[name] = libzfs.ZFSUserProperty(value)
def do_create(self, data): """ Take a snapshot from a given dataset. """ dataset = data['dataset'] recursive = data['recursive'] properties = data['properties'] verrors = ValidationErrors() if 'name' in data and 'naming_schema' in data: verrors.add( 'snapshot_create.naming_schema', 'You can\'t specify name and naming schema at the same time') elif 'name' in data: name = data['name'] elif 'naming_schema' in data: # We can't do `strftime` here because we are in the process pool and `TZ` environment variable update # is not propagated here. name = self.middleware.call_sync('replication.new_snapshot_name', data['naming_schema']) else: verrors.add('snapshot_create.naming_schema', 'You must specify either name or naming schema') if verrors: raise verrors vmware_context = None if data['vmware_sync']: vmware_context = self.middleware.call_sync('vmware.snapshot_begin', dataset, recursive) try: with libzfs.ZFS() as zfs: ds = zfs.get_dataset(dataset) ds.snapshot(f'{dataset}@{name}', recursive=recursive, fsopts=properties) if vmware_context and vmware_context['vmsynced']: ds.properties['freenas:vmsynced'] = libzfs.ZFSUserProperty( 'Y') self.logger.info(f"Snapshot taken: {dataset}@{name}") except libzfs.ZFSException as err: self.logger.error(f'Failed to snapshot {dataset}@{name}: {err}') raise CallError(f'Failed to snapshot {dataset}@{name}: {err}') else: return self.middleware.call_sync('zfs.snapshot.get_instance', f'{dataset}@{name}') finally: if vmware_context: self.middleware.call_sync('vmware.snapshot_end', vmware_context)
def shared_zfs_dataset( root_dataset: libzfs.ZFSDataset, zfs: libzfs.ZFS ) -> libzfs.ZFSDataset: name = f"{root_dataset.name}/shared-" + str(random.randint(1, 32768)) root_dataset.pool.create(name, {}) dataset = zfs.get_dataset(name) dataset.properties["jailed"] = libzfs.ZFSUserProperty("on") yield dataset dataset.delete()
def do_create(self, data): """ Take a snapshot from a given dataset. Returns: bool: True if succeed otherwise False. """ dataset = data['dataset'] recursive = data['recursive'] properties = data['properties'] verrors = ValidationErrors() if 'name' in data and 'naming_schema' in data: verrors.add( 'snapshot_create.naming_schema', 'You can\'t specify name and naming schema at the same time') elif 'name' in data: name = data['name'] elif 'naming_schema' in data: name = datetime.now().strftime(data['naming_schema']) else: verrors.add('snapshot_create.naming_schema', 'You must specify either name or naming schema') if verrors: raise verrors vmware_context = None if data['vmware_sync']: vmware_context = self.middleware.call_sync('vmware.snapshot_begin', dataset, recursive) try: with libzfs.ZFS() as zfs: ds = zfs.get_dataset(dataset) ds.snapshot(f'{dataset}@{name}', recursive=recursive, fsopts=properties) if vmware_context and vmware_context['vmsynced']: ds.properties['freenas:vmsynced'] = libzfs.ZFSUserProperty( 'Y') self.logger.info(f"Snapshot taken: {dataset}@{name}") return True except libzfs.ZFSException as err: self.logger.error(f"{err}") return False finally: if vmware_context: self.middleware.call_sync('vmware.snapshot_end', vmware_context)
def activate_pool( self, pool: libzfs.ZFSPool, mountpoint: typing.Optional[libioc.Types.AbsolutePath]=None ) -> None: """ Activate the given pool and set its mountpoint. Pool activation follows the traditional way of setting a ZFS property on the pool that other iocage variants will detect. The mechanism cannot be combined with iocage datasets defined in /etc/rc.conf, so that using the Multi-Pool feature is not possible. When attemptig to activate a pool on a system with such configuration an ActivationFailed error is raised. Args: pool: Target of the iocage activation on which an iocage dataset is created on the top level (e.g. zfs create <pool>/iocage) mountpoint: The desired mountpoint for the iocage dataset. """ if self._rc_conf_enabled is True: raise libioc.errors.ActivationFailed( "iocage ZFS source datasets are managed in /etc/rc.conf", logger=self.logger ) if self.is_pool_active(pool): msg = f"ZFS pool '{pool.name}' is already active" self.logger.warn(msg) if not isinstance(pool, libzfs.ZFSPool): raise libioc.errors.ZFSPoolInvalid("cannot activate") if pool.status == "UNAVAIL": raise libioc.errors.ZFSPoolUnavailable(pool.name) other_pools = filter(lambda x: x.name != pool.name, self.zfs.pools) for other_pool in other_pools: self._set_pool_activation(other_pool, False) self._set_pool_activation(pool, True) self.attach_source("iocage", f"{pool.name}/iocage") if self.main.root.mountpoint != mountpoint: zfs_property = libzfs.ZFSUserProperty(mountpoint) self.main.root.properties["mountpoint"] = zfs_property
def __init__( self, root_dataset: typing.Union[libzfs.ZFSDataset, str], zfs: typing.Optional['libioc.ZFS.ZFS']=None, logger: typing.Optional['libioc.Logger.Logger']=None ) -> None: self.logger = libioc.helpers_object.init_logger(self, logger) self.zfs = libioc.helpers_object.init_zfs(self, zfs) self._datasets = {} if isinstance(root_dataset, libzfs.ZFSDataset): self.root = root_dataset elif isinstance(root_dataset, str): try: self.root = self.zfs.get_dataset(root_dataset) _create = False except libzfs.ZFSException: _create = True if _create is True: pool_mountpoint = self.zfs.get_dataset( self.zfs.get_pool(root_dataset).name ).mountpoint preferred_mountpoint = "/iocage" preferred_mountpoint_inuse = os.path.ismount( preferred_mountpoint ) is True if pool_mountpoint is None: if preferred_mountpoint_inuse: raise libioc.errors.ZFSSourceMountpoint( dataset_name=root_dataset, logger=self.logger ) self.root = self.zfs.create_dataset(root_dataset) if preferred_mountpoint_inuse is False: self.logger.spam( "Claiming mountpoint /iocage" ) mountpoint = libioc.Types.AbsolutePath( preferred_mountpoint ) zfs_property = libzfs.ZFSUserProperty(mountpoint) self.root.properties["mountpoint"] = zfs_property if self.root.mountpoint is None: raise libioc.errors.ZFSSourceMountpoint( dataset_name=self.root.name, logger=self.logger )
def do_update(self, id, data): try: with libzfs.ZFS() as zfs: dataset = zfs.get_dataset(id) if 'properties' in data: properties = data['properties'].copy() # Set these after reservations for k in ['quota', 'refquota']: if k in properties: properties[k] = properties.pop(k) # Set them last for k, v in properties.items(): # If prop already exists we just update it, # otherwise create a user property prop = dataset.properties.get(k) try: if prop: if v.get('source') == 'INHERIT': prop.inherit( recursive=v.get('recursive', False)) elif 'value' in v and (prop.value != v['value'] or prop.source.name == 'INHERITED'): prop.value = v['value'] elif 'parsed' in v and ( prop.parsed != v['parsed'] or prop.source.name == 'INHERITED'): prop.parsed = v['parsed'] else: if v.get('source') == 'INHERIT': pass else: if 'value' not in v: raise ValidationError( 'properties', f'properties.{k} needs a "value" attribute' ) if ':' not in k: raise ValidationError( 'properties', f'User property needs a colon (:) in its name`' ) prop = libzfs.ZFSUserProperty(v['value']) dataset.properties[k] = prop except libzfs.ZFSException as e: raise ZFSSetPropertyError(k, str(e)) except libzfs.ZFSException as e: self.logger.error('Failed to update dataset', exc_info=True) raise CallError(f'Failed to update dataset: {e}')
def activate_cmd(zpool): """Calls ZFS set to change the property org.freebsd.ioc:active to yes.""" lgr = ioc_logger.Logger('ioc_cli_activate').getLogger() zfs = libzfs.ZFS(history=True, history_prefix="<iocage>") pools = zfs.pools prop = "org.freebsd.ioc:active" for _pool in pools: if _pool.name == zpool: ds = zfs.get_dataset(_pool.name) ds.properties[prop] = libzfs.ZFSUserProperty("yes") else: ds = zfs.get_dataset(_pool.name) ds.properties[prop] = libzfs.ZFSUserProperty("no") # Check and clean if necessary iocage_legacy way # to mark a ZFS pool as usable (now replaced by ZFS property) comment = zfs.get(_pool.name).properties["comment"] if comment.value == "iocage": comment.value = "-" lgr.info(f"ZFS pool '{zpool}' successfully activated.")
def run(self, pool_name, name, properties): try: zfs = libzfs.ZFS() dataset = zfs.get_dataset(name) for k, v in properties.items(): if k in dataset.properties: if v['value'] is None: dataset.properties[k].inherit() else: dataset.properties[k].value = v['value'] else: prop = libzfs.ZFSUserProperty(v['value']) dataset.properties[k] = prop except libzfs.ZFSException, err: raise TaskException(errno.EFAULT, str(err))
def do_update(self, id, data): try: with libzfs.ZFS() as zfs: dataset = zfs.get_dataset(id) if 'properties' in data: for k, v in data['properties'].items(): # If prop already exists we just update it, # otherwise create a user property prop = dataset.properties.get(k) if prop: if v.get('source') == 'INHERIT': if isinstance(prop, libzfs.ZFSUserProperty): # Workaround because libzfs crashes when trying to inherit user property subprocess.check_call( ["zfs", "inherit", k, id]) else: prop.inherit() elif 'value' in v and (prop.value != v['value'] or prop.source.name == 'INHERITED'): prop.value = v['value'] elif 'parsed' in v and (prop.parsed != v['parsed'] or prop.source.name == 'INHERITED'): prop.parsed = v['parsed'] else: if v.get('source') == 'INHERIT': pass else: if 'value' not in v: raise ValidationError( 'properties', f'properties.{k} needs a "value" attribute' ) if ':' not in k: raise ValidationError( 'properties', f'User property needs a colon (:) in its name`' ) prop = libzfs.ZFSUserProperty(v['value']) dataset.properties[k] = prop except libzfs.ZFSException as e: self.logger.error('Failed to update dataset', exc_info=True) raise CallError(f'Failed to update dataset: {e}')
def root_dataset(zfs: libzfs.ZFS, pool: libzfs.ZFSPool) -> libzfs.ZFSDataset: """Return the root dataset for tests.""" dataset_name = f"{pool.name}/libioc-test" try: pool.create(dataset_name, {}) except libzfs.ZFSException: pass dataset = zfs.get_dataset(dataset_name) if dataset.properties["mountpoint"].value == "none": mountpoint = libzfs.ZFSUserProperty("/.libioc-test") dataset.properties["mountpoint"] = mountpoint if not dataset.mountpoint: dataset.mount() return dataset
def _get_or_create_dataset( self, name: str, root_name: typing.Optional[str] = None, pool: typing.Optional[libzfs.ZFSPool] = None, mountpoint: typing.Optional[iocage.lib.Types.AbsolutePath] = None ) -> libzfs.ZFSDataset: if not iocage.lib.helpers.validate_name(name): raise NameError(f"Invalid 'name' for Dataset: {name}") try: return self._datasets[name] except (AttributeError, KeyError): pass if root_name is not None: root_dataset_name = root_name else: root_dataset_name = self.root.name target_pool: libzfs.ZFSPool if pool is not None: target_pool = pool else: target_pool = self.root.pool dataset: libzfs.ZFSDataset dataset_name = f"{root_dataset_name}/{name}" try: dataset = self.zfs.get_dataset(dataset_name) except libzfs.ZFSException: target_pool.create(dataset_name, {}) dataset = self.zfs.get_dataset(dataset_name) if mountpoint is not None: mountpoint_property = libzfs.ZFSUserProperty(mountpoint) dataset.properties["mountpoint"] = mountpoint_property dataset.mount() self._datasets[name] = dataset return dataset
def create_system_dataset(dispatcher, dsid, pool): logger.warning('Creating system dataset on pool {0}'.format(pool)) zfs = libzfs.ZFS() pool = zfs.get(pool) try: ds = zfs.get_dataset('{0}/.system-{1}'.format(pool.name, dsid)) except libzfs.ZFSException: pool.create('{0}/.system-{1}'.format(pool.name, dsid), { 'mountpoint': 'none', 'sharenfs': 'off' }) ds = zfs.get_dataset('{0}/.system-{1}'.format(pool.name, dsid)) try: ds.properties['org.freenas:hidden'] = libzfs.ZFSUserProperty('yes') ds.properties['canmount'].value = 'noauto' ds.properties['mountpoint'].value = SYSTEM_DIR except libzfs.ZFSException as err: logger.warning('Cannot set properties on .system dataset: {0}'.format(str(err)))
def do_create(self, data): """ Take a snapshot from a given dataset. Returns: bool: True if succeed otherwise False. """ dataset = data.get('dataset', '') name = data.get('name', '') recursive = data.get('recursive', False) properties = data.get('properties', None) if not dataset or not name: return False vmware_context = None if data['vmware_sync']: vmware_context = self.middleware.call_sync('vmware.snapshot_begin', dataset, recursive) try: with libzfs.ZFS() as zfs: ds = zfs.get_dataset(dataset) ds.snapshot(f'{dataset}@{name}', recursive=recursive, fsopts=properties) if vmware_context and vmware_context['vmsynced']: ds.properties['freenas:vmsynced'] = libzfs.ZFSUserProperty( 'Y') self.logger.info(f"Snapshot taken: {dataset}@{name}") return True except libzfs.ZFSException as err: self.logger.error(f"{err}") return False finally: if vmware_context: self.middleware.call_sync('vmware.snapshot_end', vmware_context)
def save(self): # ToDo: Delete unnecessary ZFS options # existing_property_names = list( # map(lambda x: JailConfigZFS._get_iocage_property_name(self, x), # filter( # lambda name: JailConfigZFS._is_iocage_property(self, name), # self.jail.dataset.properties # ) # ) # ) # data_keys = list(self.data) # for existing_property_name in existing_property_names: # if not existing_property_name in data_keys: # pass for zfs_property_name in self.data: zfs_property = libzfs.ZFSUserProperty( str(self.data[zfs_property_name]) ) self.jail.dataset.property[zfs_property_name] = zfs_property
async def do_create(self, data): """ Take a snapshot from a given dataset. Returns: bool: True if succeed otherwise False. """ zfs = libzfs.ZFS() dataset = data.get('dataset', '') name = data.get('name', '') recursive = data.get('recursive', False) vmsnaps_count = data.get('vmsnaps_count', 0) if not dataset or not name: return False try: ds = zfs.get_dataset(dataset) except libzfs.ZFSException as err: self.logger.error("{0}".format(err)) return False try: if recursive: ds.snapshots('{0}@{1}'.format(dataset, name, recursive=True)) else: ds.snapshot('{0}@{1}'.format(dataset, name)) if vmsnaps_count > 0: ds.properties['freenas:vmsynced'] = libzfs.ZFSUserProperty('Y') self.logger.info("Snapshot taken: {0}@{1}".format(dataset, name)) return True except libzfs.ZFSException as err: self.logger.error("{0}".format(err)) return False