From 1b38fc26bcfbe29cae040c9a6be2b632972f775c Mon Sep 17 00:00:00 2001 From: Jan Fajerski Date: Thu, 17 Sep 2020 02:09:03 -0400 Subject: [PATCH] pybind/snap_schedule: config option to allow minute granularity snaps Thsi allows setting and scheduling snapshots with minute granularity. If the option is unset, adding these will fail and scheduling for them will be skipped. Signed-off-by: Jan Fajerski --- src/pybind/mgr/snap_schedule/fs/schedule.py | 12 ++++++---- .../mgr/snap_schedule/fs/schedule_client.py | 23 ++++++++++++++++++- src/pybind/mgr/snap_schedule/module.py | 9 ++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/pybind/mgr/snap_schedule/fs/schedule.py b/src/pybind/mgr/snap_schedule/fs/schedule.py index d9246c1a333..225bbf08975 100644 --- a/src/pybind/mgr/snap_schedule/fs/schedule.py +++ b/src/pybind/mgr/snap_schedule/fs/schedule.py @@ -146,7 +146,8 @@ class Schedule(object): table_row['last_pruned'], table_row['created_count'], table_row['pruned_count'], - table_row['active']) + table_row['active'], + ) def __str__(self): return f'''{self.path} {self.schedule} {dump_retention(self.retention)}''' @@ -183,7 +184,7 @@ class Schedule(object): s.retention, sm.repeat - (strftime("%s", "now") - strftime("%s", sm.start)) % sm.repeat "until", - sm.start, sm.repeat + sm.start, sm.repeat, sm.schedule FROM schedules s INNER JOIN schedules_meta sm ON sm.schedule_id = s.id WHERE @@ -355,10 +356,13 @@ class Schedule(object): return json.dumps(dict(self.__dict__), default=lambda o: o.strftime(SNAP_DB_TS_FORMAT)) + @classmethod + def parse_schedule(cls, schedule): + return int(schedule[0:-1]), schedule[-1] + @property def repeat(self): - mult = self.schedule[-1] - period = int(self.schedule[0:-1]) + period, mult = self.parse_schedule(self.schedule) if mult == 'M': return period * 60 elif mult == 'h': diff --git a/src/pybind/mgr/snap_schedule/fs/schedule_client.py b/src/pybind/mgr/snap_schedule/fs/schedule_client.py index 12298c1f97e..b7fffab09bd 100644 --- a/src/pybind/mgr/snap_schedule/fs/schedule_client.py +++ b/src/pybind/mgr/snap_schedule/fs/schedule_client.py @@ -104,6 +104,10 @@ class SnapSchedClient(CephfsClient): self.sqlite_connections = {} self.active_timers = {} + @property + def allow_minute_snaps(self): + return self.mgr.get_module_option('allow_m_granularity') + def get_schedule_db(self, fs): if fs not in self.sqlite_connections: self.sqlite_connections[fs] = sqlite3.connect( @@ -140,6 +144,18 @@ class SnapSchedClient(CephfsClient): ioctx.write_full(SNAP_DB_OBJECT_NAME, '\n'.join(db_content).encode('utf-8')) + def _is_allowed_repeat(self, exec_row, path): + if Schedule.parse_schedule(exec_row['schedule'])[1] == 'M': + if self.allow_minute_snaps: + log.debug(f'Minute repeats allowed, scheduling snapshot on path {path}') + return True + else: + log.info(f'Minute repeats disabled, skipping snapshot on path {path}') + return False + else: + return True + + def refresh_snap_timers(self, fs, path): try: log.debug(f'SnapDB on {fs} changed for {path}, updating next Timer') @@ -147,7 +163,8 @@ class SnapSchedClient(CephfsClient): rows = [] with db: cur = db.execute(Schedule.EXEC_QUERY, (path,)) - rows = cur.fetchmany(1) + all_rows = cur.fetchall() + rows = [r for r in all_rows if self._is_allowed_repeat(r, path)][0:1] timers = self.active_timers.get((fs, path), []) for timer in timers: timer.cancel() @@ -238,6 +255,10 @@ class SnapSchedClient(CephfsClient): # TODO improve interface def store_snap_schedule(self, fs, path_, args): sched = Schedule(*args) + log.debug(f'repeat is {sched.repeat}') + if sched.parse_schedule(sched.schedule)[1] == 'M' and not self.allow_minute_snaps: + log.error('not allowed') + raise ValueError('no minute snaps allowed') log.debug(f'attempting to add schedule {sched}') db = self.get_schedule_db(fs) sched.store_schedule(db) diff --git a/src/pybind/mgr/snap_schedule/module.py b/src/pybind/mgr/snap_schedule/module.py index 917aa5b78d2..83112c4cc32 100644 --- a/src/pybind/mgr/snap_schedule/module.py +++ b/src/pybind/mgr/snap_schedule/module.py @@ -12,6 +12,15 @@ from threading import Event class Module(MgrModule): + MODULE_OPTIONS = [ + { + 'name': 'allow_m_granularity', + 'type': 'bool', + 'default': False, + 'desc': 'allow minute scheduled snapshots', + 'runtime': True, + }, + ] def __init__(self, *args, **kwargs): super(Module, self).__init__(*args, **kwargs) -- 2.39.5