From 51cc582a1c9d48621044a27a0d8560bd44ef9e1f Mon Sep 17 00:00:00 2001 From: Stanislaw Adaszewski Date: Tue, 21 Apr 2020 12:41:42 +0200 Subject: [PATCH] Initial commit. --- Fockerfile | 14 +++++++++ focker.py | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ hash.py | 0 snapshot.py | 18 ++++++++++++ zfs.py | 20 +++++++++++++ 5 files changed, 137 insertions(+) create mode 100644 Fockerfile create mode 100644 focker.py create mode 100644 hash.py create mode 100644 snapshot.py create mode 100644 zfs.py diff --git a/Fockerfile b/Fockerfile new file mode 100644 index 0000000..31fee49 --- /dev/null +++ b/Fockerfile @@ -0,0 +1,14 @@ +from: freebsd-12.1 + +steps: + - run: | + pkg install python3 && \ + pkg install py37-pip + +jail: + name: xxx + mount.devfs: true + ip4.address: 127.0.0.2 + interface: lo1 + exec.start: /bin/sh /etc/rc + exec.stop: /bin/sh /etc/rc.shutdown diff --git a/focker.py b/focker.py new file mode 100644 index 0000000..77a7911 --- /dev/null +++ b/focker.py @@ -0,0 +1,85 @@ +from argparse import ArgumentParser +import yaml +import os +from weir import zfs, process + + +def clone(self, name, props={}, force=False): + url = zfs._urlsplit(self.name) + url_1 = zfs._urlsplit(name) + if url.netloc != url_1.netloc: + raise ValueError('Clone has to happen on the same host') + cmd = ['zfs', 'clone'] + for prop, value in props.items(): + cmd.append('-o') + cmd.append(prop + '=' + str(value)) + cmd.append(url.path) + cmd.append(url_1.path) + process.check_call(cmd, netloc=url.netloc) + +zfs.ZFSSnapshot.clone = clone + + +def process_step(step, name): + cmd=['jail', '-c'] + cmd.append('path=' + '/focker/' + name) + + +def process_steps(steps, name): + if isinstance(steps, list): + for step in steps: + process_step(step, name) + else: + process_step(steps, name) + + +def build(args): + fname = os.path.join(args.focker_dir, 'Fockerfile.yml') + print('fname:', fname) + if not os.path.exists(fname): + raise ValueError('No Fockerfile.yml could be found in the specified directory') + with open(fname, 'r') as f: + spec = yaml.safe_load(f) + print('spec:', spec) + if 'from' not in spec: + raise ValueError('Missing base specification') + from_ = zfs.findprops('/', props=['focker:tags']) + from_ = filter(lambda a: a['value'] == spec['from'] \ + and '@' in a['name'], from_) + from_ = list(from_) + if len(from_) == 0: + raise ValueError('Requested base not found') + if len(from_) > 1: + raise ValueError('Ambiguous base specification') + base = from_[0]['name'] + root = '/'.join(base.split('/')[:-1]) + print('base:', base) + print('root:', root) + base = zfs.open(base) + name = '/'.join([root, 'x y z']) + base.clone(name) + process_steps(args['steps'], name) + + +def run(args): + pass + + +def create_parser(): + parser = ArgumentParser() + subparsers = parser.add_subparsers() + parser_build = subparsers.add_parser('build') + parser_build.set_defaults(func=build) + parser_build.add_argument('focker_dir', type=str) + parser_run = subparsers.add_parser('run') + parser_run.set_defaults(func=run) + parser_rm = subparsers.add_parser('rm') + parser_rmi = subparsers.add_parser('rmi') + parser_ps = subparsers.add_parser('ps') + parser_images = subparsers.add_parser('images') + return parser + + +parser = create_parser() +args = parser.parse_args() +args.func(args) diff --git a/hash.py b/hash.py new file mode 100644 index 0000000..e69de29 diff --git a/snapshot.py b/snapshot.py new file mode 100644 index 0000000..a6115c6 --- /dev/null +++ b/snapshot.py @@ -0,0 +1,18 @@ +from zfs import * + + +def new_snapshot(base, fun, name): + type_ = zfs_get_type(base) + if type_ != 'snapshot': + raise ValueError('Provided base dataset is not a snapshot') + if '/' not in name: + root = '/'.join(base.split('/')[:-1]) + name = root + '/' + name + zfs_run(['zfs', 'clone', base, name]) + try: + fun() + except: + zfs_run(['zfs', 'destroy', name]) + raise + zfs_run(['zfs', 'set', 'readonly=on', name]) + zfs_run(['zfs', 'snapshot', name + '@1']) diff --git a/zfs.py b/zfs.py new file mode 100644 index 0000000..9660e98 --- /dev/null +++ b/zfs.py @@ -0,0 +1,20 @@ +import subprocess +import csv +import io + + +def zfs_run(command): + out = subprocess.check_output(command) + return out + + +def zfs_parse_output(command): + out = zfs_run(command) + s = io.StringIO(out.decode('utf-8')) + r = csv.reader(s, delimiter='\t') + return [a for a in r] + + +def zfs_get_type(name): + lst = zfs_parse_output(['zfs', 'list', '-o', 'name,type', '-H', name]) + return lst[0][1]