Create lxchelper for prepare, cleanup and extract
This commit is contained in:
parent
539a61662d
commit
7794ada45e
68
usr/bin/lxchelper
Executable file
68
usr/bin/lxchelper
Executable 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())
|
@ -10,44 +10,6 @@ from concurrent.futures import ThreadPoolExecutor
|
||||
from lxcmgr import lxcmgr
|
||||
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):
|
||||
for app, meta in packages.items():
|
||||
print(app, meta['version'])
|
||||
@ -125,26 +87,42 @@ def uninstall_app(app):
|
||||
app = App(app)
|
||||
run_install_action(pm.uninstall_app, app)
|
||||
|
||||
args = parser.parse_args()
|
||||
if not hasattr(args, 'action'):
|
||||
parser.print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
if args.action == 'list-installed':
|
||||
def main(args):
|
||||
if args.action == 'list-installed':
|
||||
list_installed()
|
||||
elif args.action == 'list-online':
|
||||
elif args.action == 'list-online':
|
||||
list_online()
|
||||
elif args.action == 'list-updates':
|
||||
elif args.action == 'list-updates':
|
||||
list_updates()
|
||||
elif args.action == 'install':
|
||||
elif args.action == 'install':
|
||||
install_app(args.app)
|
||||
elif args.action == 'update':
|
||||
elif args.action == 'update':
|
||||
update_app(args.app)
|
||||
elif args.action == 'uninstall':
|
||||
elif args.action == 'uninstall':
|
||||
uninstall_app(args.app)
|
||||
elif args.action == 'container-prepare':
|
||||
# Used with LXC hooks on container startup
|
||||
lxcmgr.prepare_container(args.container, args.layers)
|
||||
elif args.action == 'container-cleanup':
|
||||
# Used with LXC hooks on container stop
|
||||
lxcmgr.cleanup_container(args.container)
|
||||
|
||||
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')
|
||||
|
||||
main(parser.parse_args())
|
||||
|
@ -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
|
||||
rootfs = os.path.join(LXC_ROOT, container, 'rootfs')
|
||||
# Unmount rootfs in case it remained mounted for whatever reason
|
||||
subprocess.run(['umount', rootfs])
|
||||
unmount_rootfs(rootfs)
|
||||
layers = layers.split(',')
|
||||
mount_rootfs(container, layers, rootfs)
|
||||
|
||||
def mount_rootfs(container, layers, mountpoint)
|
||||
if len(layers) == 1:
|
||||
# We have only single layer, no overlay needed
|
||||
subprocess.run(['mount', '--bind', layers[0], rootfs])
|
||||
subprocess.run(['mount', '--bind', layers[0], mountpoint])
|
||||
else:
|
||||
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):
|
||||
# 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):
|
||||
# Unmount rootfs
|
||||
rootfs = os.path.join(LXC_ROOT, container, 'rootfs')
|
||||
subprocess.run(['umount', rootfs])
|
||||
unmount_rootfs(rootfs)
|
||||
# Remove ephemeral layer data
|
||||
clean_ephemeral_layer(container)
|
||||
|
||||
|
@ -39,8 +39,8 @@ lxc.idmap = u 0 100000 65536
|
||||
lxc.idmap = g 0 100000 65536
|
||||
|
||||
# Hooks
|
||||
lxc.hook.pre-start = /usr/bin/lxcmgr container prepare {layers}
|
||||
lxc.hook.post-stop = /usr/bin/lxcmgr container cleanup
|
||||
lxc.hook.pre-start = /usr/bin/lxchelper prepare {layers}
|
||||
lxc.hook.post-stop = /usr/bin/lxchelper cleanup
|
||||
|
||||
# Other
|
||||
lxc.arch = linux64
|
||||
|
Loading…
Reference in New Issue
Block a user