Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mgr/snap_schedule: support subvol and group arguments #53999

2 changes: 2 additions & 0 deletions PendingReleaseNotes
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ CephFS: Disallow delegating preallocated inode ranges to clients. Config
module is now available. Users may run `ceph balancer status detail` to see more
details about which PGs were updated in the balancer's last optimization.
See https://docs.ceph.com/en/latest/rados/operations/balancer/ for more information.
* CephFS: Full support for subvolumes and subvolume groups is now available
for snap_schedule Manager module.

>=18.0.0

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
overrides:
subvolume_version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
overrides:
subvolume_version: 2
1 change: 1 addition & 0 deletions qa/suites/fs/functional/subvol_versions/.qa
481 changes: 459 additions & 22 deletions qa/tasks/cephfs/test_snap_schedules.py

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions src/pybind/mgr/snap_schedule/fs/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def __init__(self,
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,
Expand All @@ -100,6 +101,7 @@ def __init__(self,
) -> None:
self.fs = fs_name
self.subvol = subvol
self.group = group
self.path = path
self.rel_path = rel_path
self.schedule = schedule
Expand Down Expand Up @@ -145,6 +147,7 @@ def _from_db_row(cls, table_row: TableRowT, fs: str) -> 'Schedule':
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']),
Expand Down Expand Up @@ -200,7 +203,7 @@ def json_list(self) -> str:
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
Expand Down Expand Up @@ -255,8 +258,8 @@ def list_all_schedules(cls,
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)
Expand All @@ -270,6 +273,7 @@ def store_schedule(self, db: sqlite3.Connection) -> None:
c = db.execute(self.INSERT_SCHEDULE,
(self.path,
self.subvol,
self.group,
json.dumps(self.retention),
self.rel_path,))
sched_id = c.lastrowid
Expand Down
40 changes: 39 additions & 1 deletion src/pybind/mgr/snap_schedule/fs/schedule_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,41 @@ def allow_minute_snaps(self) -> None:
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);
vshankar marked this conversation as resolved.
Show resolved Hide resolved
"""
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()
Expand All @@ -206,6 +241,8 @@ def get_schedule_db(self, fs: str) -> DBConnectionManager:
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()
Expand Down Expand Up @@ -370,7 +407,8 @@ def list_snap_schedules(self,
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:
Expand Down