#!/usr/bin/python3 # Style Guide: https://www.python.org/dev/peps/pep-0008/ import argparse import configparser import libvirt from pathlib import Path import subprocess import sys import time import fcntoolbox.bscp as bscp log_function = lambda x : print(x) log_file = sys.stdout parser = argparse.ArgumentParser() parser.add_argument("selection", type=str, choices=["is-active", "is-volume-open", "vol-backup", "vol-serve"]) parser.add_argument("--dom", "-d", type=str) parser.add_argument("--vol", type=str) args = parser.parse_args() config = configparser.RawConfigParser() config.read("/etc/fcntoolbox/config.ini") confvirt = config['virt'] def is_active(dom_name): conn = libvirt.openReadOnly(None) if conn == None: print('Failed to open connection to the hypervisor') sys.exit(1) dom = conn.lookupByName(dom_name) return dom.isActive() def is_volume_open(volpath): if not Path(volpath).exists(): raise FileNotFoundError("Cannot find volume '{}'".format(volpath)) lvd_out = subprocess.check_output(['lvdisplay', volpath]).decode() # split lines v v remove header lvinfo = lvd_out.split("\n")[1:] lvinfo = [line.strip() for line in lvinfo] # Find line starting with "# open" lvopen = list(filter(lambda line : line.find("# open") == 0, lvinfo)) # Read after "# open" lvopen = lvopen[0][len("# open"):].strip() return lvopen != "0" def create_backup_snapshot(vol_str): snap_path = Path("{}_snap_{}".format(vol_str, time.strftime("%Y-%m-%d"))) snapshot_size = '10G' subprocess.call(['lvcreate', '--snapshot', '--size', snapshot_size, '--name', snap_path.name, vol_str], stdout = subprocess.DEVNULL, stderr = log_file) return snap_path def backup(dom_name, vol_str): vol_path = Path(vol_str) if not vol_path.exists(): raise FileNotFoundError("Cannot find volume '{}'".format(vol_str)) conn = libvirt.open(None) if conn == None: print('Failed to open connection to the hypervisor') sys.exit(1) dom = conn.lookupByName(dom_name) is_active = dom.isActive() if is_active: #try: # dom.fSTrim(None, 4096) #except libvirt.libvirtError as e: # print(e) dom.suspend() snap_path = create_backup_snapshot(vol_str) if is_active: dom.resume() remote_vol_str = '/dev/{}/{}'.format(confvirt['remote_vg'], vol_path.name) bscp.remote_command = confvirt['remote_command'] bscp.bscp(str(snap_path), confvirt['remote_host'], remote_vol_str, 64 * 1024, 'sha1') def bscp_device_function(filename): allowed_volumes = set(confvirt['allowed_volumes'].split(',')) if not filename in allowed_volumes: return None filename = str(create_backup_snapshot(filename)) if is_volume_open(filename): return None else: return filename if args.selection == 'is-active': if args.dom is None: parser.error("is-active requires --domain.") if is_active(args.dom): sys.exit(0) else: sys.exit(1) elif args.selection == 'is-volume-open': if args.vol is None: parser.error("is-volume-open requires --vol.") if is_volume_open(args.vol): sys.exit(0) else: sys.exit(1) elif args.selection == 'vol-backup': if args.dom is None or args.vol is None: parser.error("vol-backup requires --domain and --vol.") backup(args.dom, args.vol) elif args.selection == 'vol-serve': log_function = lambda x : None log_file = subprocess.DEVNULL bscp.device_function = bscp_device_function bscp.serve()