]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/services/cephfs.py
1 # -*- coding: utf-8 -*-
2 from __future__
import absolute_import
4 from contextlib
import contextmanager
15 logger
= logging
.getLogger('cephfs')
20 def list_filesystems(cls
):
21 fsmap
= mgr
.get("fs_map")
22 return [{'id': fs
['id'], 'name': fs
['mdsmap']['fs_name']}
23 for fs
in fsmap
['filesystems']]
26 def fs_name_from_id(cls
, fs_id
):
28 Get the filesystem name from ID.
29 :param fs_id: The filesystem ID.
30 :type fs_id: int | str
31 :return: The filesystem name or None.
34 fs_map
= mgr
.get("fs_map")
35 fs_info
= list(filter(lambda x
: str(x
['id']) == str(fs_id
),
36 fs_map
['filesystems']))
39 return fs_info
[0]['mdsmap']['fs_name']
41 def __init__(self
, fs_name
=None):
42 logger
.debug("initializing cephfs connection")
43 self
.cfs
= cephfs
.LibCephFS(rados_inst
=mgr
.rados
)
44 logger
.debug("mounting cephfs filesystem: %s", fs_name
)
46 self
.cfs
.mount(filesystem_name
=fs_name
)
49 logger
.debug("mounted cephfs filesystem")
52 logger
.debug("shutting down cephfs filesystem")
56 def opendir(self
, dirpath
):
59 d
= self
.cfs
.opendir(dirpath
)
65 def ls_dir(self
, path
, depth
):
67 List directories of specified path with additional information.
68 :param path: The root directory path.
69 :type path: str | bytes
70 :param depth: The number of steps to go down the directory tree.
71 :type depth: int | str
72 :return: A list of directory dicts which consist of name, path,
73 parent, snapshots and quotas.
76 paths
= self
._ls
_dir
(path
, int(depth
))
77 # Convert (bytes => string), prettify paths (strip slashes)
78 # and append additional information.
79 return [self
.get_directory(p
) for p
in paths
if p
!= path
.encode()]
81 def _ls_dir(self
, path
, depth
):
83 List directories of specified path.
84 :param path: The root directory path.
85 :type path: str | bytes
86 :param depth: The number of steps to go down the directory tree.
88 :return: A list of directory paths (bytes encoded).
90 ls_dir('/photos', 1) => [
91 b'/photos/flowers', b'/photos/cars'
95 if isinstance(path
, six
.string_types
):
97 logger
.debug("get_dir_list dirpath=%s depth=%s", path
,
101 logger
.debug("opening dirpath=%s", path
)
102 with self
.opendir(path
) as d
:
103 dent
= self
.cfs
.readdir(d
)
106 logger
.debug("found entry=%s", dent
.d_name
)
107 if dent
.d_name
in [b
'.', b
'..']:
108 dent
= self
.cfs
.readdir(d
)
111 logger
.debug("found dir=%s", dent
.d_name
)
112 subdir_path
= os
.path
.join(path
, dent
.d_name
)
113 paths
.extend(self
._ls
_dir
(subdir_path
, depth
- 1))
114 dent
= self
.cfs
.readdir(d
)
117 def get_directory(self
, path
):
119 Transforms path of directory into a meaningful dictionary.
120 :param path: The root directory path.
121 :type path: str | bytes
122 :return: Dict consists of name, path, parent, snapshots and quotas.
126 not_root
= path
!= os
.sep
128 'name': os
.path
.basename(path
) if not_root
else path
,
130 'parent': os
.path
.dirname(path
) if not_root
else None,
131 'snapshots': self
.ls_snapshots(path
),
132 'quotas': self
.get_quotas(path
) if not_root
else None
135 def dir_exists(self
, path
):
137 with self
.opendir(path
):
139 except cephfs
.ObjectNotFound
:
142 def mk_dirs(self
, path
):
145 :param path: The path of the directory.
148 raise Exception('Cannot create root directory "/"')
149 if self
.dir_exists(path
):
151 logger
.info("Creating directory: %s", path
)
152 self
.cfs
.mkdirs(path
, 0o755)
154 def rm_dir(self
, path
):
157 :param path: The path of the directory.
160 raise Exception('Cannot remove root directory "/"')
161 if not self
.dir_exists(path
):
163 logger
.info("Removing directory: %s", path
)
166 def mk_snapshot(self
, path
, name
=None, mode
=0o755):
169 :param path: The path of the directory.
171 :param name: The name of the snapshot. If not specified,
172 a name using the current time in RFC3339 UTC format
174 :type name: str | None
175 :param mode: The permissions the directory should have
178 :return: Returns the name of the snapshot.
182 now
= datetime
.datetime
.now()
183 tz
= now
.astimezone().tzinfo
184 name
= now
.replace(tzinfo
=tz
).isoformat('T')
185 client_snapdir
= self
.cfs
.conf_get('client_snapdir')
186 snapshot_path
= os
.path
.join(path
, client_snapdir
, name
)
187 logger
.info("Creating snapshot: %s", snapshot_path
)
188 self
.cfs
.mkdir(snapshot_path
, mode
)
191 def ls_snapshots(self
, path
):
193 List snapshots for the specified path.
194 :param path: The path of the directory.
196 :return: A list of dictionaries containing the name and the
197 creation time of the snapshot.
201 client_snapdir
= self
.cfs
.conf_get('client_snapdir')
202 path
= os
.path
.join(path
, client_snapdir
).encode()
203 with self
.opendir(path
) as d
:
204 dent
= self
.cfs
.readdir(d
)
207 if dent
.d_name
not in [b
'.', b
'..'] and not dent
.d_name
.startswith(b
'_'):
208 snapshot_path
= os
.path
.join(path
, dent
.d_name
)
209 stat
= self
.cfs
.stat(snapshot_path
)
211 'name': dent
.d_name
.decode(),
212 'path': snapshot_path
.decode(),
213 'created': '{}Z'.format(stat
.st_ctime
.isoformat('T'))
215 dent
= self
.cfs
.readdir(d
)
218 def rm_snapshot(self
, path
, name
):
221 :param path: The path of the directory.
223 :param name: The name of the snapshot.
226 client_snapdir
= self
.cfs
.conf_get('client_snapdir')
227 snapshot_path
= os
.path
.join(path
, client_snapdir
, name
)
228 logger
.info("Removing snapshot: %s", snapshot_path
)
229 self
.cfs
.rmdir(snapshot_path
)
231 def get_quotas(self
, path
):
233 Get the quotas of the specified path.
234 :param path: The path of the directory/file.
236 :return: Returns a dictionary containing 'max_bytes'
241 max_bytes
= int(self
.cfs
.getxattr(path
, 'ceph.quota.max_bytes'))
242 except cephfs
.NoData
:
245 max_files
= int(self
.cfs
.getxattr(path
, 'ceph.quota.max_files'))
246 except cephfs
.NoData
:
248 return {'max_bytes': max_bytes
, 'max_files': max_files
}
250 def set_quotas(self
, path
, max_bytes
=None, max_files
=None):
252 Set the quotas of the specified path.
253 :param path: The path of the directory/file.
255 :param max_bytes: The byte limit.
256 :type max_bytes: int | None
257 :param max_files: The file limit.
258 :type max_files: int | None
260 if max_bytes
is not None:
261 self
.cfs
.setxattr(path
, 'ceph.quota.max_bytes',
262 str(max_bytes
).encode(), 0)
263 if max_files
is not None:
264 self
.cfs
.setxattr(path
, 'ceph.quota.max_files',
265 str(max_files
).encode(), 0)