Exemple #1
0
    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)
Exemple #2
0
    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
Exemple #3
0
    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
Exemple #4
0
    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
Exemple #5
0
    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}')
Exemple #6
0
    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
Exemple #7
0
    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
Exemple #8
0
 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))
Exemple #9
0
    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
Exemple #10
0
    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)
Exemple #11
0
    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)
Exemple #12
0
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()
Exemple #13
0
    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)
Exemple #14
0
    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
Exemple #15
0
    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
            )
Exemple #16
0
    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}')
Exemple #17
0
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.")
Exemple #18
0
 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))
Exemple #19
0
    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}')
Exemple #20
0
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
Exemple #21
0
    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
Exemple #22
0
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)))
Exemple #23
0
    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)
Exemple #24
0
    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
Exemple #25
0
    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