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 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()
|
list_installed()
|
||||||
sys.exit(1)
|
elif args.action == 'list-online':
|
||||||
|
list_online()
|
||||||
|
elif args.action == 'list-updates':
|
||||||
|
list_updates()
|
||||||
|
elif args.action == 'install':
|
||||||
|
install_app(args.app)
|
||||||
|
elif args.action == 'update':
|
||||||
|
update_app(args.app)
|
||||||
|
elif args.action == 'uninstall':
|
||||||
|
uninstall_app(args.app)
|
||||||
|
|
||||||
if args.action == 'list-installed':
|
parser = argparse.ArgumentParser(description='LXC container and package manager')
|
||||||
list_installed()
|
subparsers = parser.add_subparsers()
|
||||||
elif args.action == 'list-online':
|
|
||||||
list_online()
|
parser_list = subparsers.add_parser('list')
|
||||||
elif args.action == 'list-updates':
|
subparsers_list = parser_list.add_subparsers()
|
||||||
list_updates()
|
parser_list_installed = subparsers_list.add_parser('installed')
|
||||||
elif args.action == 'install':
|
parser_list_installed.set_defaults(action='list-installed')
|
||||||
install_app(args.app)
|
parser_list_online = subparsers_list.add_parser('online')
|
||||||
elif args.action == 'update':
|
parser_list_online.set_defaults(action='list-online')
|
||||||
update_app(args.app)
|
parser_list_updates = subparsers_list.add_parser('updates')
|
||||||
elif args.action == 'uninstall':
|
parser_list_updates.set_defaults(action='list-updates')
|
||||||
uninstall_app(args.app)
|
|
||||||
elif args.action == 'container-prepare':
|
parser_install = subparsers.add_parser('install')
|
||||||
# Used with LXC hooks on container startup
|
parser_install.set_defaults(action='install')
|
||||||
lxcmgr.prepare_container(args.container, args.layers)
|
parser_install.add_argument('app', help='Application to install')
|
||||||
elif args.action == 'container-cleanup':
|
|
||||||
# Used with LXC hooks on container stop
|
parser_update = subparsers.add_parser('update')
|
||||||
lxcmgr.cleanup_container(args.container)
|
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
|
# 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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user