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

Add commands, argument parsing

parent 5601b740
No related branches found
No related tags found
No related merge requests found
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
# mark snapshots to keep, from oldest within period to new # mark snapshots to keep, from oldest within period to new
# delete unmarked snapshots # delete unmarked snapshots
import logging
import os import os
import re import re
from datetime import datetime, timedelta from datetime import datetime, timedelta
...@@ -40,6 +41,14 @@ PERIOD_KEYS = OrderedDict([ ...@@ -40,6 +41,14 @@ PERIOD_KEYS = OrderedDict([
("M", 60), ("M", 60),
]) ])
def run_process(command, check=True):
result = sp.run(command, check=check, stdout=sp.PIPE, stderr=sp.PIPE)
if result.stdout:
logging.info(result.stdout.decode("utf-8").strip())
if result.stderr:
logging.warn(result.stderr.decode("utf-8").strip())
return result
@contextmanager @contextmanager
def xfs_freeze(mountpoint): def xfs_freeze(mountpoint):
freeze_xfs(mountpoint, True) freeze_xfs(mountpoint, True)
...@@ -52,7 +61,7 @@ def freeze_xfs(mountpoint, freeze): ...@@ -52,7 +61,7 @@ def freeze_xfs(mountpoint, freeze):
"-f" if freeze else "-u", "-f" if freeze else "-u",
mountpoint mountpoint
] ]
sp.run(command, check=True) run_process(command, check=True)
class Volume: class Volume:
def __init__(self, volume_group, name): def __init__(self, volume_group, name):
...@@ -149,17 +158,19 @@ class Snapshot: ...@@ -149,17 +158,19 @@ class Snapshot:
"--permission", "r", "--permission", "r",
self.parent_volume.get_full_name() self.parent_volume.get_full_name()
] ]
sp.run(create_command, check=True) run_process(create_command, check=True)
self.mount() self.mount()
def mount(self): def mount(self):
if self.check_mount():
return
activate_command = [ activate_command = [
"/sbin/lvchange", "/sbin/lvchange",
"--activate", "y", "--activate", "y",
"--ignoreactivationskip", "--ignoreactivationskip",
self.get_full_volume() self.get_full_volume()
] ]
sp.run(activate_command, check=True) run_process(activate_command, check=True)
self.active = True self.active = True
self.create_mountpoint() self.create_mountpoint()
mount_command = [ mount_command = [
...@@ -168,7 +179,7 @@ class Snapshot: ...@@ -168,7 +179,7 @@ class Snapshot:
self.get_mountpoint(), self.get_mountpoint(),
"-o", self.get_mount_options() "-o", self.get_mount_options()
] ]
sp.run(mount_command, check=True) run_process(mount_command, check=True)
def check_mount(self): def check_mount(self):
check_command = [ check_command = [
...@@ -176,7 +187,7 @@ class Snapshot: ...@@ -176,7 +187,7 @@ class Snapshot:
"--source", self.get_mapper_device() "--source", self.get_mapper_device()
] ]
try: try:
sp.check_call(check_command) sp.check_call(check_command, stdout=sp.DEVNULL, stderr=sp.DEVNULL)
return True return True
except sp.CalledProcessError: except sp.CalledProcessError:
return False return False
...@@ -187,7 +198,7 @@ class Snapshot: ...@@ -187,7 +198,7 @@ class Snapshot:
"/sbin/lvremove", "/sbin/lvremove",
self.get_full_volume() self.get_full_volume()
] ]
sp.run(remove_command, check=True) run_process(remove_command, check=True)
def unmount(self): def unmount(self):
if self.check_mount(): if self.check_mount():
...@@ -195,14 +206,14 @@ class Snapshot: ...@@ -195,14 +206,14 @@ class Snapshot:
"/bin/umount", "/bin/umount",
self.get_mountpoint() self.get_mountpoint()
] ]
sp.run(unmount_command, check=True) run_process(unmount_command, check=True)
deactivate_command = [ deactivate_command = [
"/sbin/lvchange", "/sbin/lvchange",
"--activate", "n", "--activate", "n",
"--ignoreactivationskip", "--ignoreactivationskip",
self.get_full_volume() self.get_full_volume()
] ]
sp.run(deactivate_command, check=True) run_process(deactivate_command, check=True)
self.active = False self.active = False
os.rmdir(self.get_mountpoint()) os.rmdir(self.get_mountpoint())
...@@ -287,11 +298,12 @@ def load_config(): ...@@ -287,11 +298,12 @@ def load_config():
import sys import sys
import toml import toml
config_path = "config.toml" config_path = "config.toml"
global_config_path = "/etc/lvm-snapshot.toml"
ENV_VAR = "LVM_SNAPSHOT_CONFIG" ENV_VAR = "LVM_SNAPSHOT_CONFIG"
if not os.path.isfile(config_path) and os.path.isfile(global_config_path):
config_path = global_config_path
if ENV_VAR in os.environ: if ENV_VAR in os.environ:
config_path = os.environ[ENV_VAR] config_path = os.environ[ENV_VAR]
if len(sys.argv) > 1:
config_path = sys.argv[1]
with open(config_path, "r") as config_file: with open(config_path, "r") as config_file:
return toml.load(config_file) return toml.load(config_file)
...@@ -330,23 +342,59 @@ def mark_snapshots(snapshots, periods, min_interval): ...@@ -330,23 +342,59 @@ def mark_snapshots(snapshots, periods, min_interval):
unmarked_snapshots = all_snapshots - marked_snapshots unmarked_snapshots = all_snapshots - marked_snapshots
return unmarked_snapshots return unmarked_snapshots
def main(): def list_snapshots(**kwargs):
snapshots = Snapshot.list_snapshots()
for volume in snapshots:
print("volume: {}".format(str(volume)))
for snapshot in snapshots[volume]:
print(" {}".format(str(snapshot)))
def mount_snapshots(**kwargs):
snapshots = Snapshot.list_snapshots()
for volume in snapshots:
for snapshot in snapshots[volume]:
snapshot.mount()
def unmount_snapshots(**kwargs):
snapshots = Snapshot.list_snapshots()
for volume in snapshots:
for snapshot in snapshots[volume]:
snapshot.unmount()
def update_snapshots():
config = load_config() config = load_config()
snapshots = Snapshot.list_snapshots() snapshots = Snapshot.list_snapshots()
periods, min_interval = 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)) logging.warn("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)) logging.warn("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], min_interval) unmarked_snapshots = mark_snapshots(snapshots[volume], periods[volume], min_interval)
print("removing", unmarked_snapshots)
for snapshot in unmarked_snapshots: for snapshot in unmarked_snapshots:
snapshot.remove() snapshot.remove()
new_snapshot = Snapshot(volume, datetime.now()) new_snapshot = Snapshot(volume, datetime.now())
new_snapshot.create() new_snapshot.create()
operations = {
"list": list_snapshots,
"update": update_snapshots,
"mount": mount_snapshots,
"unmount": unmount_snapshots,
}
def main():
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("command", help="list|update|mount|unmount")
parser.add_argument("--verbose", action="store_true", help="do not redirect the command output to /dev/null")
args = parser.parse_args()
loglevel = logging.ERROR
if args.verbose:
loglevel = logging.INFO
logging.basicConfig(level=loglevel)
operations[args.command]()
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