2 Copyright (C) 2019 SUSE
4 LGPL2.1. See file COPYING.
9 from typing
import Any
, Dict
, Optional
, Tuple
10 from .fs
.schedule_client
import SnapSchedClient
11 from mgr_module
import MgrModule
, CLIReadCommand
, CLIWriteCommand
, Option
12 from mgr_util
import CephfsConnectionException
13 from threading
import Event
16 class Module(MgrModule
):
19 'allow_m_granularity',
22 desc
='allow minute scheduled snapshots',
29 desc
='dump database to debug log on update',
35 def __init__(self
, *args
: Any
, **kwargs
: Any
) -> None:
36 super(Module
, self
).__init
__(*args
, **kwargs
)
37 self
._initialized
= Event()
38 self
.client
= SnapSchedClient(self
)
41 def default_fs(self
) -> str:
42 fs_map
= self
.get('fs_map')
43 if fs_map
['filesystems']:
44 return fs_map
['filesystems'][0]['mdsmap']['fs_name']
46 self
.log
.error('No filesystem instance could be found.')
47 raise CephfsConnectionException(
48 -errno
.ENOENT
, "no filesystem found")
50 def has_fs(self
, fs_name
: str) -> bool:
51 return fs_name
in self
.client
.get_all_filesystems()
53 def serve(self
) -> None:
54 self
._initialized
.set()
56 def handle_command(self
, inbuf
: str, cmd
: Dict
[str, str]) -> Tuple
[int, str, str]:
57 self
._initialized
.wait()
58 return -errno
.EINVAL
, "", "Unknown command"
60 @CLIReadCommand('fs snap-schedule status')
61 def snap_schedule_get(self
,
63 fs
: Optional
[str] = None,
64 format
: Optional
[str] = 'plain') -> Tuple
[int, str, str]:
66 List current snapshot schedules
68 use_fs
= fs
if fs
else self
.default_fs
69 if not self
.has_fs(use_fs
):
70 return -errno
.EINVAL
, '', f
"no such filesystem: {use_fs}"
72 ret_scheds
= self
.client
.get_snap_schedules(use_fs
, path
)
73 except CephfsConnectionException
as e
:
76 json_report
= ','.join([ret_sched
.report_json() for ret_sched
in ret_scheds
])
77 return 0, f
'[{json_report}]', ''
78 return 0, '\n===\n'.join([ret_sched
.report() for ret_sched
in ret_scheds
]), ''
80 @CLIReadCommand('fs snap-schedule list')
81 def snap_schedule_list(self
, path
: str,
82 recursive
: bool = False,
83 fs
: Optional
[str] = None,
84 format
: Optional
[str] = 'plain') -> Tuple
[int, str, str]:
86 Get current snapshot schedule for <path>
89 use_fs
= fs
if fs
else self
.default_fs
90 if not self
.has_fs(use_fs
):
91 return -errno
.EINVAL
, '', f
"no such filesystem: {use_fs}"
92 scheds
= self
.client
.list_snap_schedules(use_fs
, path
, recursive
)
93 self
.log
.debug(f
'recursive is {recursive}')
94 except CephfsConnectionException
as e
:
98 output
: Dict
[str, str] = {}
99 return 0, json
.dumps(output
), ''
100 return -errno
.ENOENT
, '', f
'SnapSchedule for {path} not found'
102 # json_list = ','.join([sched.json_list() for sched in scheds])
103 schedule_list
= [sched
.schedule
for sched
in scheds
]
104 retention_list
= [sched
.retention
for sched
in scheds
]
105 out
= {'path': path
, 'schedule': schedule_list
, 'retention': retention_list
}
106 return 0, json
.dumps(out
), ''
107 return 0, '\n'.join([str(sched
) for sched
in scheds
]), ''
109 @CLIWriteCommand('fs snap-schedule add')
110 def snap_schedule_add(self
,
113 start
: Optional
[str] = None,
114 fs
: Optional
[str] = None) -> Tuple
[int, str, str]:
116 Set a snapshot schedule for <path>
119 use_fs
= fs
if fs
else self
.default_fs
120 if not self
.has_fs(use_fs
):
121 return -errno
.EINVAL
, '', f
"no such filesystem: {use_fs}"
124 self
.client
.store_snap_schedule(use_fs
,
126 (abs_path
, snap_schedule
,
127 use_fs
, path
, start
, subvol
))
128 suc_msg
= f
'Schedule set for path {path}'
129 except sqlite3
.IntegrityError
:
130 existing_scheds
= self
.client
.get_snap_schedules(use_fs
, path
)
131 report
= [s
.report() for s
in existing_scheds
]
132 error_msg
= f
'Found existing schedule {report}'
133 self
.log
.error(error_msg
)
134 return -errno
.EEXIST
, '', error_msg
135 except ValueError as e
:
136 return -errno
.ENOENT
, '', str(e
)
137 except CephfsConnectionException
as e
:
139 return 0, suc_msg
, ''
141 @CLIWriteCommand('fs snap-schedule remove')
142 def snap_schedule_rm(self
,
144 repeat
: Optional
[str] = None,
145 start
: Optional
[str] = None,
146 fs
: Optional
[str] = None) -> Tuple
[int, str, str]:
148 Remove a snapshot schedule for <path>
151 use_fs
= fs
if fs
else self
.default_fs
152 if not self
.has_fs(use_fs
):
153 return -errno
.EINVAL
, '', f
"no such filesystem: {use_fs}"
155 self
.client
.rm_snap_schedule(use_fs
, abs_path
, repeat
, start
)
156 except CephfsConnectionException
as e
:
158 except ValueError as e
:
159 return -errno
.ENOENT
, '', str(e
)
160 return 0, 'Schedule removed for path {}'.format(path
), ''
162 @CLIWriteCommand('fs snap-schedule retention add')
163 def snap_schedule_retention_add(self
,
165 retention_spec_or_period
: str,
166 retention_count
: Optional
[str] = None,
167 fs
: Optional
[str] = None) -> Tuple
[int, str, str]:
169 Set a retention specification for <path>
172 use_fs
= fs
if fs
else self
.default_fs
173 if not self
.has_fs(use_fs
):
174 return -errno
.EINVAL
, '', f
"no such filesystem: {use_fs}"
176 self
.client
.add_retention_spec(use_fs
, abs_path
,
177 retention_spec_or_period
,
179 except CephfsConnectionException
as e
:
181 except ValueError as e
:
182 return -errno
.ENOENT
, '', str(e
)
183 return 0, 'Retention added to path {}'.format(path
), ''
185 @CLIWriteCommand('fs snap-schedule retention remove')
186 def snap_schedule_retention_rm(self
,
188 retention_spec_or_period
: str,
189 retention_count
: Optional
[str] = None,
190 fs
: Optional
[str] = None) -> Tuple
[int, str, str]:
192 Remove a retention specification for <path>
195 use_fs
= fs
if fs
else self
.default_fs
196 if not self
.has_fs(use_fs
):
197 return -errno
.EINVAL
, '', f
"no such filesystem: {use_fs}"
199 self
.client
.rm_retention_spec(use_fs
, abs_path
,
200 retention_spec_or_period
,
202 except CephfsConnectionException
as e
:
204 except ValueError as e
:
205 return -errno
.ENOENT
, '', str(e
)
206 return 0, 'Retention removed from path {}'.format(path
), ''
208 @CLIWriteCommand('fs snap-schedule activate')
209 def snap_schedule_activate(self
,
211 repeat
: Optional
[str] = None,
212 start
: Optional
[str] = None,
213 fs
: Optional
[str] = None) -> Tuple
[int, str, str]:
215 Activate a snapshot schedule for <path>
218 use_fs
= fs
if fs
else self
.default_fs
219 if not self
.has_fs(use_fs
):
220 return -errno
.EINVAL
, '', f
"no such filesystem: {use_fs}"
222 self
.client
.activate_snap_schedule(use_fs
, abs_path
, repeat
, start
)
223 except CephfsConnectionException
as e
:
225 except ValueError as e
:
226 return -errno
.ENOENT
, '', str(e
)
227 return 0, 'Schedule activated for path {}'.format(path
), ''
229 @CLIWriteCommand('fs snap-schedule deactivate')
230 def snap_schedule_deactivate(self
,
232 repeat
: Optional
[str] = None,
233 start
: Optional
[str] = None,
234 fs
: Optional
[str] = None) -> Tuple
[int, str, str]:
236 Deactivate a snapshot schedule for <path>
239 use_fs
= fs
if fs
else self
.default_fs
240 if not self
.has_fs(use_fs
):
241 return -errno
.EINVAL
, '', f
"no such filesystem: {use_fs}"
243 self
.client
.deactivate_snap_schedule(use_fs
, abs_path
, repeat
, start
)
244 except CephfsConnectionException
as e
:
246 except ValueError as e
:
247 return -errno
.ENOENT
, '', str(e
)
248 return 0, 'Schedule deactivated for path {}'.format(path
), ''