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
)
40 def resolve_subvolume_path(self
, fs
: str, subvol
: Optional
[str], path
: str) -> str:
44 rc
, subvol_path
, err
= self
.remote('fs', 'subvolume', 'getpath',
47 # TODO custom exception?
48 raise Exception(f
'Could not resolve {path} in {fs}, {subvol}')
49 return subvol_path
+ path
52 def default_fs(self
) -> str:
53 fs_map
= self
.get('fs_map')
54 if fs_map
['filesystems']:
55 return fs_map
['filesystems'][0]['mdsmap']['fs_name']
57 self
.log
.error('No filesystem instance could be found.')
58 raise CephfsConnectionException(
59 -errno
.ENOENT
, "no filesystem found")
61 def serve(self
) -> None:
62 self
._initialized
.set()
64 def handle_command(self
, inbuf
: str, cmd
: Dict
[str, str]) -> Tuple
[int, str, str]:
65 self
._initialized
.wait()
66 return -errno
.EINVAL
, "", "Unknown command"
68 @CLIReadCommand('fs snap-schedule status')
69 def snap_schedule_get(self
,
71 subvol
: Optional
[str] = None,
72 fs
: Optional
[str] = None,
73 format
: Optional
[str] = 'plain') -> Tuple
[int, str, str]:
75 List current snapshot schedules
77 use_fs
= fs
if fs
else self
.default_fs
79 ret_scheds
= self
.client
.get_snap_schedules(use_fs
, path
)
80 except CephfsConnectionException
as e
:
83 json_report
= ','.join([ret_sched
.report_json() for ret_sched
in ret_scheds
])
84 return 0, f
'[{json_report}]', ''
85 return 0, '\n===\n'.join([ret_sched
.report() for ret_sched
in ret_scheds
]), ''
87 @CLIReadCommand('fs snap-schedule list')
88 def snap_schedule_list(self
, path
: str,
89 subvol
: Optional
[str] = None,
90 recursive
: bool = False,
91 fs
: Optional
[str] = None,
92 format
: Optional
[str] = 'plain') -> Tuple
[int, str, str]:
94 Get current snapshot schedule for <path>
97 use_fs
= fs
if fs
else self
.default_fs
98 scheds
= self
.client
.list_snap_schedules(use_fs
, path
, recursive
)
99 self
.log
.debug(f
'recursive is {recursive}')
100 except CephfsConnectionException
as e
:
104 output
: Dict
[str, str] = {}
105 return 0, json
.dumps(output
), ''
106 return -errno
.ENOENT
, '', f
'SnapSchedule for {path} not found'
108 # json_list = ','.join([sched.json_list() for sched in scheds])
109 schedule_list
= [sched
.schedule
for sched
in scheds
]
110 retention_list
= [sched
.retention
for sched
in scheds
]
111 out
= {'path': path
, 'schedule': schedule_list
, 'retention': retention_list
}
112 return 0, json
.dumps(out
), ''
113 return 0, '\n'.join([str(sched
) for sched
in scheds
]), ''
115 @CLIWriteCommand('fs snap-schedule add')
116 def snap_schedule_add(self
,
119 start
: Optional
[str] = None,
120 fs
: Optional
[str] = None,
121 subvol
: Optional
[str] = None) -> Tuple
[int, str, str]:
123 Set a snapshot schedule for <path>
126 use_fs
= fs
if fs
else self
.default_fs
127 abs_path
= self
.resolve_subvolume_path(use_fs
, subvol
, path
)
128 self
.client
.store_snap_schedule(use_fs
,
130 (abs_path
, snap_schedule
,
131 use_fs
, path
, start
, subvol
))
132 suc_msg
= f
'Schedule set for path {path}'
133 except sqlite3
.IntegrityError
:
134 existing_scheds
= self
.client
.get_snap_schedules(use_fs
, path
)
135 report
= [s
.report() for s
in existing_scheds
]
136 error_msg
= f
'Found existing schedule {report}'
137 self
.log
.error(error_msg
)
138 return -errno
.EEXIST
, '', error_msg
139 except ValueError as e
:
140 return -errno
.ENOENT
, '', str(e
)
141 except CephfsConnectionException
as e
:
143 return 0, suc_msg
, ''
145 @CLIWriteCommand('fs snap-schedule remove')
146 def snap_schedule_rm(self
,
148 repeat
: Optional
[str] = None,
149 start
: Optional
[str] = None,
150 subvol
: Optional
[str] = None,
151 fs
: Optional
[str] = None) -> Tuple
[int, str, str]:
153 Remove a snapshot schedule for <path>
156 use_fs
= fs
if fs
else self
.default_fs
157 abs_path
= self
.resolve_subvolume_path(use_fs
, subvol
, path
)
158 self
.client
.rm_snap_schedule(use_fs
, abs_path
, repeat
, start
)
159 except CephfsConnectionException
as e
:
161 except ValueError as e
:
162 return -errno
.ENOENT
, '', str(e
)
163 return 0, 'Schedule removed for path {}'.format(path
), ''
165 @CLIWriteCommand('fs snap-schedule retention add')
166 def snap_schedule_retention_add(self
,
168 retention_spec_or_period
: str,
169 retention_count
: Optional
[str] = None,
170 fs
: Optional
[str] = None,
171 subvol
: Optional
[str] = None) -> Tuple
[int, str, str]:
173 Set a retention specification for <path>
176 use_fs
= fs
if fs
else self
.default_fs
177 abs_path
= self
.resolve_subvolume_path(use_fs
, subvol
, path
)
178 self
.client
.add_retention_spec(use_fs
, abs_path
,
179 retention_spec_or_period
,
181 except CephfsConnectionException
as e
:
183 except ValueError as e
:
184 return -errno
.ENOENT
, '', str(e
)
185 return 0, 'Retention added to path {}'.format(path
), ''
187 @CLIWriteCommand('fs snap-schedule retention remove')
188 def snap_schedule_retention_rm(self
,
190 retention_spec_or_period
: str,
191 retention_count
: Optional
[str] = None,
192 fs
: Optional
[str] = None,
193 subvol
: Optional
[str] = None) -> Tuple
[int, str, str]:
195 Remove a retention specification for <path>
198 use_fs
= fs
if fs
else self
.default_fs
199 abs_path
= self
.resolve_subvolume_path(use_fs
, subvol
, path
)
200 self
.client
.rm_retention_spec(use_fs
, abs_path
,
201 retention_spec_or_period
,
203 except CephfsConnectionException
as e
:
205 except ValueError as e
:
206 return -errno
.ENOENT
, '', str(e
)
207 return 0, 'Retention removed from path {}'.format(path
), ''
209 @CLIWriteCommand('fs snap-schedule activate')
210 def snap_schedule_activate(self
,
212 repeat
: Optional
[str] = None,
213 start
: Optional
[str] = None,
214 subvol
: Optional
[str] = None,
215 fs
: Optional
[str] = None) -> Tuple
[int, str, str]:
217 Activate a snapshot schedule for <path>
220 use_fs
= fs
if fs
else self
.default_fs
221 abs_path
= self
.resolve_subvolume_path(use_fs
, subvol
, path
)
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 subvol
: Optional
[str] = None,
235 fs
: Optional
[str] = None) -> Tuple
[int, str, str]:
237 Deactivate a snapshot schedule for <path>
240 use_fs
= fs
if fs
else self
.default_fs
241 abs_path
= self
.resolve_subvolume_path(use_fs
, subvol
, path
)
242 self
.client
.deactivate_snap_schedule(use_fs
, abs_path
, repeat
, start
)
243 except CephfsConnectionException
as e
:
245 except ValueError as e
:
246 return -errno
.ENOENT
, '', str(e
)
247 return 0, 'Schedule deactivated for path {}'.format(path
), ''