]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/volumes/fs/operations/trash.py
import 15.2.9
[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:
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, trashpath, 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 # catch any unlink errors
86 try:
87 rmtree(trashpath)
88 except cephfs.Error as e:
89 raise VolumeException(-e.args[0], e.args[1])
90
91 def dump(self, path):
92 """
93 move an filesystem entity to trash can.
94
95 :praram path: the filesystem path to be moved
96 :return: None
97 """
98 try:
99 self.fs.rename(path, self.unique_trash_path)
100 except cephfs.Error as e:
101 raise VolumeException(-e.args[0], e.args[1])
102
103 def link(self, path, bname):
104 pth = os.path.join(self.path, bname)
105 try:
106 self.fs.symlink(path, pth)
107 except cephfs.Error as e:
108 raise VolumeException(-e.args[0], e.args[1])
109
110 def delink(self, bname):
111 pth = os.path.join(self.path, bname)
112 try:
113 self.fs.unlink(pth)
114 except cephfs.Error as e:
115 raise VolumeException(-e.args[0], e.args[1])
116
117 def create_trashcan(fs, vol_spec):
118 """
119 create a trash can.
120
121 :param fs: ceph filesystem handle
122 :param vol_spec: volume specification
123 :return: None
124 """
125 trashcan = Trash(fs, vol_spec)
126 try:
127 fs.mkdirs(trashcan.path, 0o700)
128 except cephfs.Error as e:
129 raise VolumeException(-e.args[0], e.args[1])
130
131 @contextmanager
132 def open_trashcan(fs, vol_spec):
133 """
134 open a trash can. This API is to be used as a context manager.
135
136 :param fs: ceph filesystem handle
137 :param vol_spec: volume specification
138 :return: yields a trash can object (subclass of GroupTemplate)
139 """
140 trashcan = Trash(fs, vol_spec)
141 try:
142 fs.stat(trashcan.path)
143 except cephfs.Error as e:
144 raise VolumeException(-e.args[0], e.args[1])
145 yield trashcan