Skip to content
Snippets Groups Projects
Commit b17feee7 authored by Administrator's avatar Administrator
Browse files

Create, mount, remove, umount works

parent 7ba866d8
No related branches found
No related tags found
No related merge requests found
...@@ -42,9 +42,9 @@ PERIOD_KEYS = OrderedDict([ ...@@ -42,9 +42,9 @@ PERIOD_KEYS = OrderedDict([
@contextmanager @contextmanager
def xfs_freeze(mountpoint): def xfs_freeze(mountpoint):
freeze_xfs(True) #freeze_xfs(mountpoint, True)
yield yield
freeze_xfs(False) #freeze_xfs(mountpoint, False)
def freeze_xfs(mountpoint, freeze): def freeze_xfs(mountpoint, freeze):
command = [ command = [
...@@ -113,19 +113,24 @@ class Snapshot: ...@@ -113,19 +113,24 @@ class Snapshot:
return parent_name, timestamp return parent_name, timestamp
def get_full_volume(self): def get_full_volume(self):
return "{}/{}".format(self.volume_group, self.get_name()) return "{}/{}".format(self.parent_volume.volume_group, self.get_name())
def get_mountpoint(self): def get_mountpoint(self):
return os.path.join([ return os.path.join(
SNAPSHOT_BASE_DIR, self.parent_volume.name, self.get_timestamp_str() SNAPSHOT_BASE_DIR, self.parent_volume.name, self.get_timestamp_str()
]) )
def create_mountpoint(self):
os.makedirs(self.get_mountpoint(), mode=0o755, exist_ok=True)
def get_mapper_device(self): def get_mapper_device(self):
return "/dev/mapper/{}-{}".format(self.volume_group, self.get_name()) return "/dev/mapper/{}-{}".format(
self.parent_volume.volume_group,
self.get_name().replace("-", "--"))
def create(self): def create(self):
parent_mountpoint = self.parent_volume.get_mountpoint() parent_mountpoint = self.parent_volume.get_mountpoint()
with xfs_freeze(mountpoint): with xfs_freeze(parent_mountpoint):
create_command = [ create_command = [
"/sbin/lvcreate", "/sbin/lvcreate",
"--snapshot", "--snapshot",
...@@ -145,11 +150,12 @@ class Snapshot: ...@@ -145,11 +150,12 @@ class Snapshot:
] ]
sp.run(activate_command, check=True) sp.run(activate_command, check=True)
self.active = True self.active = True
self.create_mountpoint()
mount_command = [ mount_command = [
"/bin/mount", "/bin/mount",
self.get_mapper_device(), self.get_mapper_device(),
self.get_mountpoint(), self.get_mountpoint(),
"-onouuid,ro" # nouuid is necessary for xfs snapshots "-onouuid,ro,norecovery" # nouuid is necessary for xfs snapshots
] ]
sp.run(mount_command, check=True) sp.run(mount_command, check=True)
...@@ -175,6 +181,7 @@ class Snapshot: ...@@ -175,6 +181,7 @@ class Snapshot:
] ]
sp.run(deactivate_command, check=True) sp.run(deactivate_command, check=True)
self.active = False self.active = False
os.rmdir(self.get_mountpoint())
@staticmethod @staticmethod
def list_snapshots(): def list_snapshots():
...@@ -267,6 +274,7 @@ def load_config(): ...@@ -267,6 +274,7 @@ def load_config():
def parse_config(config): def parse_config(config):
periods = {} periods = {}
min_interval = None
for volume_conf in config["volume"]: for volume_conf in config["volume"]:
name = volume_conf["name"] name = volume_conf["name"]
volume_group = volume_conf["volume_group"] volume_group = volume_conf["volume_group"]
...@@ -274,20 +282,27 @@ def parse_config(config): ...@@ -274,20 +282,27 @@ def parse_config(config):
periods[volume] = [] periods[volume] = []
for raw_period in volume_conf["keep"]: for raw_period in volume_conf["keep"]:
interval = Period.parse_interval(raw_period["interval"]) interval = Period.parse_interval(raw_period["interval"])
if min_interval is None or interval < min_interval:
min_interval = interval
number = int(raw_period["number"]) number = int(raw_period["number"])
periods[volume].append( periods[volume].append(
Period(target_number=number, interval=interval)) Period(target_number=number, interval=interval))
return periods return periods, min_interval
def mark_snapshots(snapshots, periods): def mark_snapshots(snapshots, periods, min_interval):
all_snapshots = set(snapshots) all_snapshots = set(snapshots)
marked_snapshots = set() marked_snapshots = set()
budgets = {period: period.target_number for period in periods} budgets = {period: period.target_number for period in periods}
last_snapshot = {period: None for period in periods}
for snapshot in sorted(snapshots, key=lambda s: s.timestamp): for snapshot in sorted(snapshots, key=lambda s: s.timestamp):
for period in sorted(periods, key=Period.interval): for period in sorted(periods, key=lambda p: p.interval):
required_distance = period.interval - min_interval/2
if (budgets[period] > 0 if (budgets[period] > 0
and period.get_start() < snapshot.timestamp): and period.get_start() < snapshot.timestamp):
if (last_snapshot[period] is None
or abs(snapshot.timestamp - last_snapshot[period].timestamp) > required_distance):
marked_snapshots.add(snapshot) marked_snapshots.add(snapshot)
last_snapshot[period] = snapshot
budgets[period] -= 1 budgets[period] -= 1
unmarked_snapshots = all_snapshots - marked_snapshots unmarked_snapshots = all_snapshots - marked_snapshots
return unmarked_snapshots return unmarked_snapshots
...@@ -295,15 +310,20 @@ def mark_snapshots(snapshots, periods): ...@@ -295,15 +310,20 @@ def mark_snapshots(snapshots, periods):
def main(): def main():
config = load_config() config = load_config()
snapshots = Snapshot.list_snapshots() snapshots = Snapshot.list_snapshots()
periods = parse_config(config) periods, min_interval = parse_config(config)
for volume in set(periods.keys()) - set(snapshots.keys()): for volume in set(periods.keys()) - set(snapshots.keys()):
print("Warning: Volume {} is configured but does not exist or has no snapshots.".format(volume)) print("Warning: Volume {} is configured but does not exist or has no snapshots.".format(volume))
for volume in set(snapshots.keys()) - set(periods.keys()): for volume in set(snapshots.keys()) - set(periods.keys()):
print("Warning: Volume {} does exist but is not configured.".format(volume)) print("Warning: Volume {} does exist but is not configured.".format(volume))
snapshots.pop(volume) snapshots.pop(volume)
for volume in snapshots: for volume in snapshots:
unmarked_snapshots = mark_snapshots(snapshots[volume], periods[volume]) unmarked_snapshots = mark_snapshots(snapshots[volume], periods[volume], min_interval)
print(unmarked_snapshots) print("removing", unmarked_snapshots)
for snapshot in unmarked_snapshots:
snapshot.remove()
new_snapshot = Snapshot(volume, datetime.now())
new_snapshot.create()
if __name__ == "__main__": if __name__ == "__main__":
main() main()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment