]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/volumes/fs/operations/trash.py
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / pybind / mgr / volumes / fs / operations / trash.py
1 import os
2 import uuid
3 import logging
4 from contextlib import contextmanager
5
6 import cephfs
7
8 from .template import GroupTemplate
9 from ..fs_util import listdir
10 from ..exception import VolumeException
11
12 log = logging.getLogger(__name__)
13
14 class Trash(GroupTemplate):
15 GROUP_NAME = "_deleting"
16
17 def __init__(self, fs, vol_spec):
18 self.fs = fs
19 self.vol_spec = vol_spec
20 self.groupname = Trash.GROUP_NAME
21
22 @property
23 def path(self):
24 return os.path.join(self.vol_spec.base_dir.encode('utf-8'), self.groupname.encode('utf-8'))
25
26 @property
27 def unique_trash_path(self):
28 """
29 return a unique trash directory entry path
30 """
31 return os.path.join(self.path, str(uuid.uuid4()).encode('utf-8'))
32
33 def _get_single_dir_entry(self, exclude_list=[]):
34 exclude_list.extend((b".", b".."))
35 try:
36 with self.fs.opendir(self.path) as d:
37 entry = self.fs.readdir(d)
38 while entry:
39 if entry.d_name not in exclude_list and entry.is_dir():
40 return entry.d_name
41 entry = self.fs.readdir(d)
42 return None
43 except cephfs.Error as e:
44 raise VolumeException(-e.args[0], e.args[1])
45
46 def get_trash_entry(self, exclude_list):
47 """
48 get a trash entry excluding entries provided.
49
50 :praram exclude_list: entries to exclude
51 :return: trash entry
52 """
53 return self._get_single_dir_entry(exclude_list)
54
55 def purge(self, trash_entry, should_cancel):
56 """
57 purge a trash entry.
58
59 :praram trash_entry: the trash entry to purge
60 :praram should_cancel: callback to check if the purge should be aborted
61 :return: None
62 """
63 def rmtree(root_path):
64 log.debug("rmtree {0}".format(root_path))
65 try:
66 with self.fs.opendir(root_path) as dir_handle:
67 d = self.fs.readdir(dir_handle)
68 while d and not should_cancel():
69 if d.d_name not in (b".", b".."):
70 d_full = os.path.join(root_path, d.d_name)
71 if d.is_dir():
72 rmtree(d_full)
73 else:
74 self.fs.unlink(d_full)
75 d = self.fs.readdir(dir_handle)
76 except cephfs.ObjectNotFound:
77 return
78 except cephfs.Error as e:
79 raise VolumeException(-e.args[0], e.args[1])
80 # remove the directory only if we were not asked to cancel
81 # (else we would fail to remove this anyway)
82 if not should_cancel():
83 self.fs.rmdir(root_path)
84
85 trashpath = os.path.join(self.path, trash_entry)
86 # catch any unlink errors
87 try:
88 rmtree(trashpath)
89 except cephfs.Error as e:
90 raise VolumeException(-e.args[0], e.args[1])
91
92 def dump(self, path):
93 """
94 move an filesystem entity to trash can.
95
96 :praram path: the filesystem path to be moved
97 :return: None
98 """
99 try:
100 self.fs.rename(path, self.unique_trash_path)
101 except cephfs.Error as e:
102 raise VolumeException(-e.args[0], e.args[1])
103
104 def create_trashcan(fs, vol_spec):
105 """
106 create a trash can.
107
108 :param fs: ceph filesystem handle
109 :param vol_spec: volume specification
110 :return: None
111 """
112 trashcan = Trash(fs, vol_spec)
113 try:
114 fs.mkdirs(trashcan.path, 0o700)
115 except cephfs.Error as e:
116 raise VolumeException(-e.args[0], e.args[1])
117
118 @contextmanager
119 def open_trashcan(fs, vol_spec):
120 """
121 open a trash can. This API is to be used as a context manager.
122
123 :param fs: ceph filesystem handle
124 :param vol_spec: volume specification
125 :return: yields a trash can object (subclass of GroupTemplate)
126 """
127 trashcan = Trash(fs, vol_spec)
128 try:
129 fs.stat(trashcan.path)
130 except cephfs.Error as e:
131 raise VolumeException(-e.args[0], e.args[1])
132 yield trashcan