From 389020e14594e4894e28d1eb9103c210b142509e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 23 May 2024 18:45:13 +0200 Subject: Adding upstream version 18.2.3. Signed-off-by: Daniel Baumann --- src/pybind/mgr/snap_schedule/fs/schedule.py | 39 +++++++++++++--- src/pybind/mgr/snap_schedule/fs/schedule_client.py | 53 +++++++++++++++++++--- 2 files changed, 78 insertions(+), 14 deletions(-) (limited to 'src/pybind/mgr/snap_schedule/fs') diff --git a/src/pybind/mgr/snap_schedule/fs/schedule.py b/src/pybind/mgr/snap_schedule/fs/schedule.py index 95e43b7e0..ba4390939 100644 --- a/src/pybind/mgr/snap_schedule/fs/schedule.py +++ b/src/pybind/mgr/snap_schedule/fs/schedule.py @@ -65,7 +65,7 @@ def parse_retention(retention: str) -> Dict[str, int]: return ret -RETENTION_MULTIPLIERS = ['n', 'M', 'h', 'd', 'w', 'm', 'y'] +RETENTION_MULTIPLIERS = ['n', 'm', 'h', 'd', 'w', 'M', 'Y'] TableRowT = Dict[str, Union[int, str]] @@ -89,6 +89,7 @@ class Schedule(object): rel_path: str, start: Optional[str] = None, subvol: Optional[str] = None, + group: Optional[str] = None, retention_policy: str = '{}', created: Optional[str] = None, first: Optional[str] = None, @@ -100,9 +101,15 @@ class Schedule(object): ) -> None: self.fs = fs_name self.subvol = subvol + self.group = group self.path = path self.rel_path = rel_path self.schedule = schedule + # test to see if period and spec are valid + # this test will throw a ValueError exception if + # period is negative or zero + # spec is empty or other than n,m,h,d,w,M,Y + rep = self.repeat self.retention = json.loads(retention_policy) if start is None: now = datetime.now(timezone.utc) @@ -140,6 +147,7 @@ class Schedule(object): cast(str, table_row['rel_path']), cast(str, table_row['start']), cast(str, table_row['subvol']), + cast(str, table_row['group_name']), cast(str, table_row['retention']), cast(str, table_row['created']), cast(str, table_row['first']), @@ -195,7 +203,7 @@ class Schedule(object): ORDER BY until;''' PROTO_GET_SCHEDULES = '''SELECT - s.path, s.subvol, s.rel_path, sm.active, + s.path, s.subvol, s.group_name, s.rel_path, sm.active, sm.schedule, s.retention, sm.start, sm.first, sm.last, sm.last_pruned, sm.created, sm.created_count, sm.pruned_count FROM schedules s @@ -250,8 +258,8 @@ class Schedule(object): return [cls._from_db_row(row, fs) for row in c.fetchall()] INSERT_SCHEDULE = '''INSERT INTO - schedules(path, subvol, retention, rel_path) - Values(?, ?, ?, ?);''' + schedules(path, subvol, group_name, retention, rel_path) + Values(?, ?, ?, ?, ?);''' INSERT_SCHEDULE_META = '''INSERT INTO schedules_meta(schedule_id, start, created, repeat, schedule, active) @@ -265,6 +273,7 @@ class Schedule(object): c = db.execute(self.INSERT_SCHEDULE, (self.path, self.subvol, + self.group, json.dumps(self.retention), self.rel_path,)) sched_id = c.lastrowid @@ -396,8 +405,19 @@ class Schedule(object): @property def repeat(self) -> int: - period, mult = self.parse_schedule(self.schedule) - if mult == 'M': + period = -1 + mult = "" + try: + period, mult = self.parse_schedule(self.schedule) + except ValueError: + raise ValueError('invalid schedule specified - period should be ' + 'non-zero positive value and multiplier should ' + 'be one of h,d,w,M,Y e.g. 1h or 4d etc.') + if period <= 0: + raise ValueError('invalid schedule specified - period must be a ' + 'non-zero positive value e.g. 1h or 4d etc.') + # 'm' is only for developer testing of minute level snapshots + if mult == 'm': return period * 60 elif mult == 'h': return period * 60 * 60 @@ -405,8 +425,13 @@ class Schedule(object): return period * 60 * 60 * 24 elif mult == 'w': return period * 60 * 60 * 24 * 7 + elif mult == 'M': + return period * 60 * 60 * 24 * 30 + elif mult == 'Y': + return period * 60 * 60 * 24 * 365 else: - raise ValueError(f'schedule multiplier "{mult}" not recognized') + raise ValueError('invalid schedule specified - multiplier should ' + 'be one of h,d,w,M,Y') UPDATE_LAST = '''UPDATE schedules_meta SET diff --git a/src/pybind/mgr/snap_schedule/fs/schedule_client.py b/src/pybind/mgr/snap_schedule/fs/schedule_client.py index 28d54639a..8b199b632 100644 --- a/src/pybind/mgr/snap_schedule/fs/schedule_client.py +++ b/src/pybind/mgr/snap_schedule/fs/schedule_client.py @@ -79,12 +79,12 @@ def get_prune_set(candidates: Set[Tuple[cephfs.DirEntry, datetime]], # NOTE: prune set has tz suffix stripped out. ("n", SNAPSHOT_TS_FORMAT), # TODO remove M for release - ("M", '%Y-%m-%d-%H_%M'), + ("m", '%Y-%m-%d-%H_%M'), ("h", '%Y-%m-%d-%H'), ("d", '%Y-%m-%d'), ("w", '%G-%V'), - ("m", '%Y-%m'), - ("y", '%Y'), + ("M", '%Y-%m'), + ("Y", '%Y'), ]) keep = [] if not retention: @@ -180,6 +180,41 @@ class SnapSchedClient(CephfsClient): def dump_on_update(self) -> None: return self.mgr.get_module_option('dump_on_update') + def _create_snap_schedule_kv_db(self, db: sqlite3.Connection) -> None: + SQL = """ + CREATE TABLE IF NOT EXISTS SnapScheduleModuleKV ( + key TEXT PRIMARY KEY, + value NOT NULL + ) WITHOUT ROWID; + INSERT OR IGNORE INTO SnapScheduleModuleKV (key, value) VALUES ('__snap_schedule_db_version', 1); + """ + db.executescript(SQL) + + def _get_snap_schedule_db_version(self, db: sqlite3.Connection) -> int: + SQL = """ + SELECT value + FROM SnapScheduleModuleKV + WHERE key = '__snap_schedule_db_version'; + """ + cur = db.execute(SQL) + row = cur.fetchone() + assert row is not None + return int(row[0]) + + # add all upgrades here + def _upgrade_snap_schedule_db_schema(self, db: sqlite3.Connection) -> None: + # add a column to hold the subvolume group name + if self._get_snap_schedule_db_version(db) < 2: + SQL = """ + ALTER TABLE schedules + ADD COLUMN group_name TEXT; + """ + db.executescript(SQL) + + # bump up the snap-schedule db version to 2 + SQL = "UPDATE OR ROLLBACK SnapScheduleModuleKV SET value = ? WHERE key = '__snap_schedule_db_version';" + db.execute(SQL, (2,)) + def get_schedule_db(self, fs: str) -> DBConnectionManager: dbinfo = None self.conn_lock.acquire() @@ -206,13 +241,15 @@ class SnapSchedClient(CephfsClient): except rados.ObjectNotFound: log.debug(f'No legacy schedule DB found in {fs}') db.executescript(Schedule.CREATE_TABLES) + self._create_snap_schedule_kv_db(db) + self._upgrade_snap_schedule_db_schema(db) self.sqlite_connections[fs] = DBInfo(fs, db) dbinfo = self.sqlite_connections[fs] self.conn_lock.release() return DBConnectionManager(dbinfo) def _is_allowed_repeat(self, exec_row: Dict[str, str], path: str) -> bool: - if Schedule.parse_schedule(exec_row['schedule'])[1] == 'M': + if Schedule.parse_schedule(exec_row['schedule'])[1] == 'm': if self.allow_minute_snaps: log.debug(('Minute repeats allowed, ' f'scheduling snapshot on path {path}')) @@ -370,12 +407,14 @@ class SnapSchedClient(CephfsClient): def store_snap_schedule(self, fs: str, path_: str, args: Tuple[str, str, str, str, - Optional[str], Optional[str]]) -> None: + Optional[str], Optional[str], + Optional[str]]) -> None: sched = Schedule(*args) log.debug(f'repeat is {sched.repeat}') - if sched.parse_schedule(sched.schedule)[1] == 'M' and not self.allow_minute_snaps: + if sched.parse_schedule(sched.schedule)[1] == 'm' and not self.allow_minute_snaps: log.error('not allowed') - raise ValueError('no minute snaps allowed') + raise ValueError('invalid schedule specified - multiplier should ' + 'be one of h,d,w,M,Y') log.debug(f'attempting to add schedule {sched}') with self.get_schedule_db(fs) as conn_mgr: db = conn_mgr.dbinfo.db -- cgit v1.2.3