implement 'zfs destroy'

main
svalouch 4 years ago
parent 644bf8e6bc
commit 7d2e6e2a0d

@ -6,7 +6,7 @@ import stat
import subprocess
from typing import List, Optional
from .exceptions import PEHelperException, ExternalPEHelperException
from .exceptions import PEHelperException, ExternalPEHelperException, ValidationError
from .validation import validate_dataset_path, validate_pool_name
@ -39,6 +39,15 @@ class PEHelperBase:
'''
raise NotImplementedError(f'{self} has not implemented this function')
def zfs_destroy_dataset(self, dataset: str, recursive: bool, force_umount: bool):
'''
Destroy the given ``dataset``.
:raises ValidationError: If parameters do not validate.
:raises PEHelperException: If errors are encountered when running the helper.
'''
raise NotImplementedError(f'{self} has not implemented this function')
class ExternalPEHelper(PEHelperBase):
'''
@ -156,3 +165,17 @@ class SudoPEHelper(PEHelperBase):
# TODO validate mountpoint fs
self._execute_cmd(['zfs', 'set', f'mountpoint={mountpoint}', fileset])
def zfs_destroy_dataset(self, dataset: str, recursive: bool, force_umount: bool) -> None:
if '/' not in dataset:
raise ValidationError('Can\'t remove the pool itself')
validate_dataset_path(dataset)
args = ['zfs', 'destroy', '-p']
if recursive:
args.append('-r')
if force_umount:
args.append('-f')
args.append(dataset)
self._execute_cmd(args)

@ -651,7 +651,7 @@ class ZFS:
'''
raise NotImplementedError(f'{self} has not implemented this function')
def destroy_dataset(self, name: str, *, recursive: bool = False) -> None:
def destroy_dataset(self, dataset: str, *, recursive: bool = False, force_umount: bool = False) -> None:
'''
Destroy a dataset. This function tries to remove a dataset, optionally removing all children recursively if
``recursive`` is **True**. This function works on all types of datasets, ``fileset``, ``volume``, ``snapshot``
@ -666,7 +666,30 @@ class ZFS:
:note: This is a destructive process that can't be undone.
:param name: Name of the dataset to remove.
:param dataset: Name of the dataset to remove.
:param recursive: Whether to recursively delete child datasets such as snapshots.
:param force_umount: Forces umounting before destroying. Refer to ``ZFS(8)`` `zfs destroy` parameter ``-f``.
:raises ValidationError: If validating the parameters failed.
:raises DatasetNotFound: If the dataset can't be found.
'''
if '/' not in dataset:
raise ValidationError('Cannot destroy the pool using this function')
validate_dataset_path(dataset)
if not self.dataset_exists(dataset):
raise DatasetNotFound('The dataset could not be found')
self._destroy_dataset(dataset, recursive=recursive, force_umount=force_umount)
def _destroy_dataset(self, dataset: str, *, recursive: bool = False, force_umount: bool = False) -> None:
'''
Internal implementation of :func:`destroy_dataset`.
:param dataset: The name of the dataset to remove.
:param recursive: Whether to recursively delete child datasets such as snapshots.
:param force_umount: Forces umounting before destroying.
:raises ValidationError: If validating the parameters failed.
:raises DatasetNotFound: If the dataset can't be found.
'''
raise NotImplementedError(f'{self} has not implemented this function')

@ -323,3 +323,50 @@ class ZFSCli(ZFS):
def create_bookmark(self, snapshot: str, name: str) -> Dataset:
validate_dataset_path(snapshot)
raise NotImplementedError()
def _destroy_dataset(self, dataset: str, *, recursive: bool = False, force_umount: bool = False) -> None:
args = [self.__exe, 'destroy', '-p']
if recursive:
args.append('-r')
if force_umount:
args.append('-f')
args.append(dataset)
log.debug(f'executing: {args}')
proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8')
if proc.returncode != 0 or len(proc.stderr) > 0:
log.debug(f'destroy_dataset: command failed, code={proc.returncode}, stderr="{proc.stderr}"')
if 'has children' in proc.stderr:
if recursive:
log.error(f'Dataset {dataset} has children and recursive was given, please report this')
else:
log.warning(f'Dataset {dataset} has children and thus cannot be destroyed without recursive=True')
raise Exception
# two possible messaes: (zfs destroy -p -r [-f] $fileset_with_snapshots)
# * 'cannot destroy snapshots: permission denied'
# * 'umount: only root can use "--types" option'
# The latter seems to originate from having `destroy` and `mount` via `zfs allow`.
elif ('cannot destroy' in proc.stderr and 'permission denied' in proc.stderr) or \
'only root can' in proc.stderr:
log.debug('Command output indicates that we need to run the PE Helper')
if self.use_pe_helper:
if self.pe_helper is not None:
log.info(f'Using pe_helper to remove {dataset}')
self.pe_helper.zfs_destroy_dataset(dataset, recursive, force_umount)
log.info(f'Dataset {dataset} destroyed (using pe_helper)')
else:
msg = 'Cannot destroy: No pe_helper set'
log.error(msg)
raise PermissionError(msg)
else:
log.error(f'Dataset "{dataset}" can\'t be destroyed due to lack of permissions. Please set a'
' PE helper')
raise PermissionError(proc.stderr)
else:
try:
self.handle_command_error(proc)
except PermissionError:
log.error('Permission denied, please use "zfs allow"')
raise
else:
log.info('Dataset destroyed successfully')

Loading…
Cancel
Save