Skip to content

Commit

Permalink
mgr/volumes: implement trash as a subvolume group
Browse files Browse the repository at this point in the history
Signed-off-by: Venky Shankar <vshankar@redhat.com>
  • Loading branch information
vshankar committed Jan 9, 2020
1 parent b30f0cb commit 0e3c48e
Showing 1 changed file with 132 additions and 0 deletions.
132 changes: 132 additions & 0 deletions src/pybind/mgr/volumes/fs/operations/trash.py
@@ -0,0 +1,132 @@
import os
import uuid
import logging
from contextlib import contextmanager

import cephfs

from .template import GroupTemplate
from ..fs_util import listdir
from ..exception import VolumeException

log = logging.getLogger(__name__)

class Trash(GroupTemplate):
GROUP_NAME = "_deleting"

def __init__(self, fs, vol_spec):
self.fs = fs
self.vol_spec = vol_spec
self.groupname = Trash.GROUP_NAME

@property
def path(self):
return os.path.join(self.vol_spec.base_dir.encode('utf-8'), self.groupname.encode('utf-8'))

@property
def unique_trash_path(self):
"""
return a unique trash directory entry path
"""
return os.path.join(self.path, str(uuid.uuid4()).encode('utf-8'))

def _get_single_dir_entry(self, exclude_list=[]):
exclude_list.extend((b".", b".."))
try:
with self.fs.opendir(self.path) as d:
entry = self.fs.readdir(d)
while entry:
if entry.d_name not in exclude_list and entry.is_dir():
return entry.d_name
entry = self.fs.readdir(d)
return None
except cephfs.Error as e:
raise VolumeException(-e.args[0], e.args[1])

def get_trash_entry(self, exclude_list):
"""
get a trash entry excluding entries provided.
:praram exclude_list: entries to exclude
:return: trash entry
"""
return self._get_single_dir_entry(exclude_list)

def purge(self, trash_entry, should_cancel):
"""
purge a trash entry.
:praram trash_entry: the trash entry to purge
:praram should_cancel: callback to check if the purge should be aborted
:return: None
"""
def rmtree(root_path):
log.debug("rmtree {0}".format(root_path))
try:
with self.fs.opendir(root_path) as dir_handle:
d = self.fs.readdir(dir_handle)
while d and not should_cancel():
if d.d_name not in (b".", b".."):
d_full = os.path.join(root_path, d.d_name)
if d.is_dir():
rmtree(d_full)
else:
self.fs.unlink(d_full)
d = self.fs.readdir(dir_handle)
except cephfs.ObjectNotFound:
return
except cephfs.Error as e:
raise VolumeException(-e.args[0], e.args[1])
# remove the directory only if we were not asked to cancel
# (else we would fail to remove this anyway)
if not should_cancel():
self.fs.rmdir(root_path)

trashpath = os.path.join(self.path, trash_entry)
# catch any unlink errors
try:
rmtree(trashpath)
except cephfs.Error as e:
raise VolumeException(-e.args[0], e.args[1])

def dump(self, path):
"""
move an filesystem entity to trash can.
:praram path: the filesystem path to be moved
:return: None
"""
try:
self.fs.rename(path, self.unique_trash_path)
except cephfs.Error as e:
raise VolumeException(-e.args[0], e.args[1])

def create_trashcan(fs, vol_spec):
"""
create a trash can.
:param fs: ceph filesystem handle
:param vol_spec: volume specification
:return: None
"""
trashcan = Trash(fs, vol_spec)
try:
fs.mkdirs(trashcan.path, 0o700)
except cephfs.Error as e:
raise VolumeException(-e.args[0], e.args[1])

@contextmanager
def open_trashcan(fs, vol_spec):
"""
open a trash can. This API is to be used as a context manager.
:param fs: ceph filesystem handle
:param vol_spec: volume specification
:return: yields a trash can object (subclass of GroupTemplate)
"""
trashcan = Trash(fs, vol_spec)
try:
fs.stat(trashcan.path)
except cephfs.Error as e:
raise VolumeException(-e.args[0], e.args[1])
yield trashcan

0 comments on commit 0e3c48e

Please sign in to comment.