Create lxchelper for prepare, cleanup and extract

This commit is contained in:
Disassembler 2019-12-07 15:45:43 +01:00
parent 539a61662d
commit 7794ada45e
No known key found for this signature in database
GPG Key ID: 524BD33A0EE29499
4 changed files with 118 additions and 66 deletions

68
usr/bin/lxchelper Executable file
View File

@ -0,0 +1,68 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import argparse
import os
import shutil
import sys
import tempfile
from lxcmgr import lxcmgr
from lxcmgr.paths import LXC_ROOT
def get_layers(container):
with open(os.path.join(LXC_ROOT, container, 'config')) as f:
for line in f.read().splitlines():
if line.startswith('lxc.hook.pre-start'):
return line.split()[-1].split(',')
def copy(source, destination):
if os.path.isdir(source):
shutil.copytree(source, destination, True)
else:
shutil.copy2(source, destination)
def extract(args):
with tempfile.TemporaryDirectory() as tmp_rootfs:
layers = get_layers(args.container)
lxcmgr.mount_rootfs(args.container, layers, tmp_rootfs)
source = os.path.join(tmp_rootfs, args.source.lstrip('/'))
try:
copy(source, args.destination)
except:
lxcmgr.unmount_rootfs(tmp_rootfs)
raise
lxcmgr.unmount_rootfs(tmp_rootfs)
def main(args):
if args.action == 'prepare':
# Used with LXC hooks on container startup
lxcmgr.prepare_container(args.container, args.layers)
elif args.action == 'cleanup':
# Used with LXC hooks on container stop
lxcmgr.cleanup_container(args.container)
elif args.action == 'extract':
# Used in install.sh scripts to get files or directories from containers rootfs (excluding persistent mounts)
extract(args)
parser = argparse.ArgumentParser(description='Collection of auxiliary LXC tools')
subparsers = parser.add_subparsers()
parser_prepare = subparsers.add_parser('prepare', help='Perform pre-start steps for LXC')
parser_prepare.set_defaults(action='prepare')
parser_prepare.add_argument('layers', help='OverlayFS LXC rootfs layers')
parser_prepare.add_argument('container', help='Container name')
parser_prepare.add_argument('lxc', nargs=argparse.REMAINDER)
parser_cleanup = subparsers.add_parser('cleanup', help='Perform post-stop steps for LXC')
parser_cleanup.set_defaults(action='cleanup')
parser_cleanup.add_argument('container', help='Container name')
parser_cleanup.add_argument('lxc', nargs=argparse.REMAINDER)
parser_extract = subparsers.add_parser('extract', help='Extracts files or directories from containers rootfs (excluding persistent mounts)')
parser_cleanup.set_defaults(action='extract')
parser_extract.add_argument('container', help='Container name')
parser_extract.add_argument('source', help='Source file or directory within the container')
parser_extract.add_argument('destination', help='Destination file or directory on the host')
main(parser.parse_args())

View File

@ -10,44 +10,6 @@ from concurrent.futures import ThreadPoolExecutor
from lxcmgr import lxcmgr from lxcmgr import lxcmgr
from lxcmgr.pkgmgr import App, Stage, PkgMgr from lxcmgr.pkgmgr import App, Stage, PkgMgr
parser = argparse.ArgumentParser(description='LXC container and package manager')
subparsers = parser.add_subparsers()
parser_list = subparsers.add_parser('list')
subparsers_list = parser_list.add_subparsers()
parser_list_installed = subparsers_list.add_parser('installed')
parser_list_installed.set_defaults(action='list-installed')
parser_list_online = subparsers_list.add_parser('online')
parser_list_online.set_defaults(action='list-online')
parser_list_updates = subparsers_list.add_parser('updates')
parser_list_updates.set_defaults(action='list-updates')
parser_install = subparsers.add_parser('install')
parser_install.set_defaults(action='install')
parser_install.add_argument('app', help='Application to install')
parser_update = subparsers.add_parser('update')
parser_update.set_defaults(action='update')
parser_update.add_argument('app', help='Application to update')
parser_uninstall = subparsers.add_parser('uninstall')
parser_uninstall.set_defaults(action='uninstall')
parser_uninstall.add_argument('app', help='Application to uninstall')
parser_container = subparsers.add_parser('container')
subparsers_container = parser_container.add_subparsers()
parser_container_prepare = subparsers_container.add_parser('prepare')
parser_container_prepare.set_defaults(action='container-prepare')
parser_container_prepare.add_argument('layers', help='OverlayFS LXC rootfs layers')
parser_container_prepare.add_argument('container', help='Container name')
parser_container_prepare.add_argument('lxc', nargs=argparse.REMAINDER)
parser_container_cleanup = subparsers_container.add_parser('cleanup')
parser_container_cleanup.set_defaults(action='container-cleanup')
parser_container_cleanup.add_argument('container', help='Container name')
parser_container_cleanup.add_argument('lxc', nargs=argparse.REMAINDER)
def print_apps(packages): def print_apps(packages):
for app, meta in packages.items(): for app, meta in packages.items():
print(app, meta['version']) print(app, meta['version'])
@ -125,26 +87,42 @@ def uninstall_app(app):
app = App(app) app = App(app)
run_install_action(pm.uninstall_app, app) run_install_action(pm.uninstall_app, app)
args = parser.parse_args() def main(args):
if not hasattr(args, 'action'): if args.action == 'list-installed':
parser.print_usage()
sys.exit(1)
if args.action == 'list-installed':
list_installed() list_installed()
elif args.action == 'list-online': elif args.action == 'list-online':
list_online() list_online()
elif args.action == 'list-updates': elif args.action == 'list-updates':
list_updates() list_updates()
elif args.action == 'install': elif args.action == 'install':
install_app(args.app) install_app(args.app)
elif args.action == 'update': elif args.action == 'update':
update_app(args.app) update_app(args.app)
elif args.action == 'uninstall': elif args.action == 'uninstall':
uninstall_app(args.app) uninstall_app(args.app)
elif args.action == 'container-prepare':
# Used with LXC hooks on container startup parser = argparse.ArgumentParser(description='LXC container and package manager')
lxcmgr.prepare_container(args.container, args.layers) subparsers = parser.add_subparsers()
elif args.action == 'container-cleanup':
# Used with LXC hooks on container stop parser_list = subparsers.add_parser('list')
lxcmgr.cleanup_container(args.container) subparsers_list = parser_list.add_subparsers()
parser_list_installed = subparsers_list.add_parser('installed')
parser_list_installed.set_defaults(action='list-installed')
parser_list_online = subparsers_list.add_parser('online')
parser_list_online.set_defaults(action='list-online')
parser_list_updates = subparsers_list.add_parser('updates')
parser_list_updates.set_defaults(action='list-updates')
parser_install = subparsers.add_parser('install')
parser_install.set_defaults(action='install')
parser_install.add_argument('app', help='Application to install')
parser_update = subparsers.add_parser('update')
parser_update.set_defaults(action='update')
parser_update.add_argument('app', help='Application to update')
parser_uninstall = subparsers.add_parser('uninstall')
parser_uninstall.set_defaults(action='uninstall')
parser_uninstall.add_argument('app', help='Application to uninstall')
main(parser.parse_args())

View File

@ -16,14 +16,20 @@ def prepare_container(container, layers):
# which don't have the capability to mount overlays - https://www.spinics.net/lists/linux-fsdevel/msg105877.html # which don't have the capability to mount overlays - https://www.spinics.net/lists/linux-fsdevel/msg105877.html
rootfs = os.path.join(LXC_ROOT, container, 'rootfs') rootfs = os.path.join(LXC_ROOT, container, 'rootfs')
# Unmount rootfs in case it remained mounted for whatever reason # Unmount rootfs in case it remained mounted for whatever reason
subprocess.run(['umount', rootfs]) unmount_rootfs(rootfs)
layers = layers.split(',') layers = layers.split(',')
mount_rootfs(container, layers, rootfs)
def mount_rootfs(container, layers, mountpoint)
if len(layers) == 1: if len(layers) == 1:
# We have only single layer, no overlay needed # We have only single layer, no overlay needed
subprocess.run(['mount', '--bind', layers[0], rootfs]) subprocess.run(['mount', '--bind', layers[0], mountpoint])
else: else:
olwork = os.path.join(LXC_ROOT, container, 'olwork') olwork = os.path.join(LXC_ROOT, container, 'olwork')
subprocess.run(['mount', '-t', 'overlay', '-o', 'upperdir={},lowerdir={},workdir={}'.format(layers[0], ':'.join(layers[1:]), olwork), 'none', rootfs]) subprocess.run(['mount', '-t', 'overlay', '-o', 'upperdir={},lowerdir={},workdir={}'.format(layers[0], ':'.join(layers[1:]), olwork), 'none', mountpoint])
def unmount_rootfs(mountpoint):
subprocess.run(['umount', '--quiet', mountpoint])
def clean_ephemeral_layer(container): def clean_ephemeral_layer(container):
# Cleans containers ephemeral layer. Called in lxc.hook.post-stop and lxc.hook.pre-start in case of unclean shutdown # Cleans containers ephemeral layer. Called in lxc.hook.post-stop and lxc.hook.pre-start in case of unclean shutdown
@ -35,7 +41,7 @@ def clean_ephemeral_layer(container):
def cleanup_container(container): def cleanup_container(container):
# Unmount rootfs # Unmount rootfs
rootfs = os.path.join(LXC_ROOT, container, 'rootfs') rootfs = os.path.join(LXC_ROOT, container, 'rootfs')
subprocess.run(['umount', rootfs]) unmount_rootfs(rootfs)
# Remove ephemeral layer data # Remove ephemeral layer data
clean_ephemeral_layer(container) clean_ephemeral_layer(container)

View File

@ -39,8 +39,8 @@ lxc.idmap = u 0 100000 65536
lxc.idmap = g 0 100000 65536 lxc.idmap = g 0 100000 65536
# Hooks # Hooks
lxc.hook.pre-start = /usr/bin/lxcmgr container prepare {layers} lxc.hook.pre-start = /usr/bin/lxchelper prepare {layers}
lxc.hook.post-stop = /usr/bin/lxcmgr container cleanup lxc.hook.post-stop = /usr/bin/lxchelper cleanup
# Other # Other
lxc.arch = linux64 lxc.arch = linux64