vmmgr/usr/lib/python3.6/lxcmgr/lxcmgr.py
2019-09-20 10:10:25 +02:00

98 lines
4.3 KiB
Python

# -*- coding: utf-8 -*-
import fcntl
import os
import shutil
import subprocess
from . import flock
from .paths import HOSTS_FILE, HOSTS_LOCK, LXC_ROOT
from .templates import LXC_CONTAINER
def prepare_container(container, layers):
# Remove ephemeral layer data
clean_ephemeral_layer(container)
# Prepare and mount overlayfs
rootfs = os.path.join(LXC_ROOT, container, 'rootfs')
# Unmount rootfs in case it remained mounted for whatever reason
subprocess.run(['umount', rootfs])
layers = layers.split(',')
if len(layers) == 1:
# We have only single layer, no overlay needed
subprocess.run(['mount', '--bind', layers[0], rootfs])
else:
olwork = os.path.join(LXC_ROOT, container, 'olwork')
subprocess.run(['mount', '-t', 'overlay', '-o', 'upperdir={},lowerdir={},workdir={}'.format(layers[-1], ':'.join(layers[:-1]), olwork), 'none', rootfs])
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
# This is done early in the container start process, so the inode of the ephemeral directory must remain unchanged
ephemeral = os.path.join(LXC_ROOT, container, 'ephemeral')
for item in os.scandir(ephemeral):
shutil.rmtree(item.path) if item.is_dir() else os.unlink(item.path)
def cleanup_container(container):
# Unmount rootfs
rootfs = os.path.join(LXC_ROOT, container, 'rootfs')
subprocess.run(['umount', rootfs])
# Remove ephemeral layer data
clean_ephemeral_layer(container)
def create_container(container, image):
# Create directories after container installation
rootfs = os.path.join(LXC_ROOT, container, 'rootfs')
olwork = os.path.join(LXC_ROOT, container, 'olwork')
ephemeral = os.path.join(LXC_ROOT, container, 'ephemeral')
os.makedirs(rootfs, 0o755, True)
os.makedirs(olwork, 0o755, True)
os.makedirs(ephemeral, 0o755, True)
os.chown(ephemeral, 100000, 100000)
# Create container configuration file
layers = ','.join([os.path.join(LXC_STORAGE_DIR, layer) for layer in image['layers']])
if 'build' not in image:
layers = '{},{}'.format(layer, ephemeral)
mounts = '\n'.join(['lxc.mount.entry = {} {} none bind,create={} 0 0'.format(m[1], m[2].lstrip('/'), m[0].lower()) for m in image['mounts']]) if 'mounts' in image else ''
env = '\n'.join(['lxc.environment = {}={}'.format(e[0], e[1]) for e in image['env']]) if 'env' in image else ''
uid = image['uid'] if 'uid' in image else '0'
gid = image['gid'] if 'gid' in image else '0'
cmd = image['cmd'] if 'cmd' in image else '/bin/sh'
cwd = image['cwd'] if 'cwd' in image else '/'
halt = image['halt'] if 'halt' in image else 'SIGINT'
# Lease the first unused IP to the container
ipv4 = update_hosts_lease(container, True)
# Create the config file
with open(os.path.join(LXC_ROOT, container, 'config'), 'w') as f:
f.write(LXC_CONTAINER.format(name=container, ipv4=ipv4, layers=layers, mounts=mounts, env=env, uid=uid, gid=gid, cmd=cmd, cwd=cwd, halt=halt))
def destroy_container(container):
# Remove container configuration and directories
shutil.rmtree(os.path.join(LXC_ROOT, container))
# Release the IP address
update_hosts_lease(container, False)
@flock.flock_ex(HOSTS_LOCK)
def update_hosts_lease(container, is_request):
# This is a poor man's DHCP server which uses /etc/hosts as lease database
# Leases the first unused IP from range 172.17.0.0/16
# Uses file lock as interprocess mutex
ip = None
# Load all existing records
with open(HOSTS_FILE, 'r') as f:
leases = [l.strip().split(' ', 1) for l in f]
# If this call is a request for lease, find the first unassigned IP
if is_request:
used_ips = [l[0] for l in leases]
for i in range(2, 65278): # Reserve last /24 subnet for VPN
ip = '172.17.{}.{}'. format(i // 256, i % 256)
if ip not in used_ips:
leases.append([ip, container])
break
# Otherwise it is a release in which case we just delete the record
else:
leases = [l for l in leases if l[1] != container]
# Write the contents back to the file
with open(HOSTS_FILE, 'w') as f:
for lease in leases:
f.write('{} {}\n'.format(lease[0], lease[1]))
return ip