2 Copyright (C) 2019 SUSE
4 LGPL2.1. See file COPYING.
9 from typing
import Sequence
, Optional
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',
27 def __init__(self
, *args
, **kwargs
):
28 super(Module
, self
).__init
__(*args
, **kwargs
)
29 self
._initialized
= Event()
30 self
.client
= SnapSchedClient(self
)
32 def resolve_subvolume_path(self
, fs
, subvol
, path
):
36 rc
, subvol_path
, err
= self
.remote('fs', 'subvolume', 'getpath',
39 # TODO custom exception?
40 raise Exception(f
'Could not resolve {path} in {fs}, {subvol}')
41 return subvol_path
+ path
45 fs_map
= self
.get('fs_map')
46 if fs_map
['filesystems']:
47 return fs_map
['filesystems'][0]['mdsmap']['fs_name']
49 self
.log
.error('No filesystem instance could be found.')
50 raise CephfsConnectionException(
51 -errno
.ENOENT
, "no filesystem found")
54 self
._initialized
.set()
56 def handle_command(self
, inbuf
, cmd
):
57 self
._initialized
.wait()
58 return -errno
.EINVAL
, "", "Unknown command"
60 @CLIReadCommand('fs snap-schedule status')
61 def snap_schedule_get(self
,
62 path
: Optional
[str] = '/',
63 subvol
: Optional
[str] = None,
64 fs
: Optional
[str] = None,
65 format
: Optional
[str] = 'plain'):
67 List current snapshot schedules
69 use_fs
= fs
if fs
else self
.default_fs
71 ret_scheds
= self
.client
.get_snap_schedules(use_fs
, path
)
72 except CephfsConnectionException
as e
:
75 json_report
= ','.join([ret_sched
.report_json() for ret_sched
in ret_scheds
])
76 return 0, f
'{json_report}', ''
77 return 0, '\n===\n'.join([ret_sched
.report() for ret_sched
in ret_scheds
]), ''
79 @CLIReadCommand('fs snap-schedule list')
80 def snap_schedule_list(self
, path
: str,
81 subvol
: Optional
[str] = None,
82 recursive
: Optional
[bool] = False,
83 fs
: Optional
[str] = None,
84 format
: Optional
[str] = 'plain'):
86 Get current snapshot schedule for <path>
89 use_fs
= fs
if fs
else self
.default_fs
90 scheds
= self
.client
.list_snap_schedules(use_fs
, path
, recursive
)
91 self
.log
.debug(f
'recursive is {recursive}')
92 except CephfsConnectionException
as e
:
95 return -errno
.ENOENT
, '', f
'SnapSchedule for {path} not found'
97 # json_list = ','.join([sched.json_list() for sched in scheds])
98 schedule_list
= [sched
.schedule
for sched
in scheds
]
99 retention_list
= [sched
.retention
for sched
in scheds
]
100 out
= {'path': path
, 'schedule': schedule_list
, 'retention': retention_list
}
101 return 0, json
.dumps(out
), ''
102 return 0, '\n'.join([str(sched
) for sched
in scheds
]), ''
104 @CLIWriteCommand('fs snap-schedule add')
105 def snap_schedule_add(self
,
107 snap_schedule
: Optional
[str],
108 start
: Optional
[str] = None,
109 fs
: Optional
[str] = None,
110 subvol
: Optional
[str] = None):
112 Set a snapshot schedule for <path>
115 use_fs
= fs
if fs
else self
.default_fs
116 abs_path
= self
.resolve_subvolume_path(fs
, subvol
, path
)
117 self
.client
.store_snap_schedule(use_fs
,
119 (abs_path
, snap_schedule
,
120 use_fs
, path
, start
, subvol
))
121 suc_msg
= f
'Schedule set for path {path}'
122 except sqlite3
.IntegrityError
:
123 existing_scheds
= self
.client
.get_snap_schedules(use_fs
, path
)
124 report
= [s
.report() for s
in existing_scheds
]
125 error_msg
= f
'Found existing schedule {report}'
126 self
.log
.error(error_msg
)
127 return -errno
.EEXIST
, '', error_msg
128 except ValueError as e
:
129 return -errno
.ENOENT
, '', str(e
)
130 except CephfsConnectionException
as e
:
132 return 0, suc_msg
, ''
134 @CLIWriteCommand('fs snap-schedule remove')
135 def snap_schedule_rm(self
,
137 repeat
: Optional
[str] = None,
138 start
: Optional
[str] = None,
139 subvol
: Optional
[str] = None,
140 fs
: Optional
[str] = None):
142 Remove a snapshot schedule for <path>
145 use_fs
= fs
if fs
else self
.default_fs
146 abs_path
= self
.resolve_subvolume_path(fs
, subvol
, path
)
147 self
.client
.rm_snap_schedule(use_fs
, abs_path
, repeat
, start
)
148 except CephfsConnectionException
as e
:
150 except ValueError as e
:
151 return -errno
.ENOENT
, '', str(e
)
152 return 0, 'Schedule removed for path {}'.format(path
), ''
154 @CLIWriteCommand('fs snap-schedule retention add')
155 def snap_schedule_retention_add(self
,
157 retention_spec_or_period
: str,
158 retention_count
: Optional
[str] = None,
159 fs
: Optional
[str] = None,
160 subvol
: Optional
[str] = None):
162 Set a retention specification for <path>
165 use_fs
= fs
if fs
else self
.default_fs
166 abs_path
= self
.resolve_subvolume_path(fs
, subvol
, path
)
167 self
.client
.add_retention_spec(use_fs
, abs_path
,
168 retention_spec_or_period
,
170 except CephfsConnectionException
as e
:
172 except ValueError as e
:
173 return -errno
.ENOENT
, '', str(e
)
174 return 0, 'Retention added to path {}'.format(path
), ''
176 @CLIWriteCommand('fs snap-schedule retention remove')
177 def snap_schedule_retention_rm(self
,
179 retention_spec_or_period
: str,
180 retention_count
: Optional
[str] = None,
181 fs
: Optional
[str] = None,
182 subvol
: Optional
[str] = None):
184 Remove a retention specification for <path>
187 use_fs
= fs
if fs
else self
.default_fs
188 abs_path
= self
.resolve_subvolume_path(fs
, subvol
, path
)
189 self
.client
.rm_retention_spec(use_fs
, abs_path
,
190 retention_spec_or_period
,
192 except CephfsConnectionException
as e
:
194 except ValueError as e
:
195 return -errno
.ENOENT
, '', str(e
)
196 return 0, 'Retention removed from path {}'.format(path
), ''
198 @CLIWriteCommand('fs snap-schedule activate')
199 def snap_schedule_activate(self
,
201 repeat
: Optional
[str] = None,
202 start
: Optional
[str] = None,
203 subvol
: Optional
[str] = None,
204 fs
: Optional
[str] = None):
206 Activate a snapshot schedule for <path>
209 use_fs
= fs
if fs
else self
.default_fs
210 abs_path
= self
.resolve_subvolume_path(fs
, subvol
, path
)
211 self
.client
.activate_snap_schedule(use_fs
, abs_path
, repeat
, start
)
212 except CephfsConnectionException
as e
:
214 except ValueError as e
:
215 return -errno
.ENOENT
, '', str(e
)
216 return 0, 'Schedule activated for path {}'.format(path
), ''
218 @CLIWriteCommand('fs snap-schedule deactivate')
219 def snap_schedule_deactivate(self
,
221 repeat
: Optional
[str] = None,
222 start
: Optional
[str] = None,
223 subvol
: Optional
[str] = None,
224 fs
: Optional
[str] = None):
226 Deactivate a snapshot schedule for <path>
229 use_fs
= fs
if fs
else self
.default_fs
230 abs_path
= self
.resolve_subvolume_path(fs
, subvol
, path
)
231 self
.client
.deactivate_snap_schedule(use_fs
, abs_path
, repeat
, start
)
232 except CephfsConnectionException
as e
:
234 except ValueError as e
:
235 return -errno
.ENOENT
, '', str(e
)
236 return 0, 'Schedule deactivated for path {}'.format(path
), ''