File no-libexec.patch of Package virt-sandbox
Index: libvirt-sandbox-0.5.1/bin/virt-sandbox-service
===================================================================
--- libvirt-sandbox-0.5.1.orig/bin/virt-sandbox-service
+++ /dev/null
@@ -1,1279 +0,0 @@
-#!/usr/bin/python -Es
-#
-# Authors: Dan Walsh <dwalsh@redhat.com>
-#
-# Copyright (C) 2012-2013 Red Hat, Inc.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-#
-
-from gi.repository import LibvirtGObject
-from gi.repository import LibvirtSandbox
-from gi.repository import GLib
-import gi
-import re
-import os, sys, shutil, errno, stat
-import exceptions
-import rpm
-from subprocess import Popen, PIPE, STDOUT
-import gettext
-import pwd
-
-if os.path.exists("/sys/fs/selinux"):
- import selinux
-else:
- selinux = None
-
-LibvirtGObject.init_object_check(None)
-LibvirtSandbox.init_check(None)
-
-gettext.bindtextdomain("libvirt-sandbox", "/usr/share/locale")
-gettext.textdomain("libvirt-sandbox")
-try:
- gettext.install("libvirt-sandbox",
- localedir="/usr/share/locale",
- unicode=False,
- codeset = 'utf-8')
-except IOError:
- import __builtin__
- __builtin__.__dict__['_'] = unicode
-
-CONFIG_PATH = "/etc/libvirt-sandbox/services/"
-def get_config_path(name):
- return CONFIG_PATH + name + "/config/sandbox.cfg"
-
-def get_legacy_config_path(name):
- return CONFIG_PATH + name + ".sandbox"
-
-def read_config(name):
- path = get_config_path(name)
- if not os.path.exists(path):
- return None
- return LibvirtSandbox.Config.load_from_path(path)
-
-# shutil.copytree throws a fit if it finds sockets
-# or fifos, and has really bad behaviour on block
-# and character devices too.
-def copydirtree(src, dst):
- filenames = os.listdir(src)
- os.makedirs(dst)
-
- for filename in filenames:
- srcfilepath = os.path.join(src, filename)
- dstfilepath = os.path.join(dst, filename)
-
- st = os.lstat(srcfilepath)
- if stat.S_ISDIR(st.st_mode):
- copydirtree(srcfilepath, dstfilepath)
-
- os.utime(dstfilepath, (st.st_atime, st.st_mtime))
- os.chmod(dstfilepath, stat.S_IMODE(st.st_mode))
- elif stat.S_ISREG(st.st_mode):
- with open(srcfilepath, 'rb') as fsrc:
- with open(dstfilepath, 'wb') as fdst:
- while 1:
- buf = fsrc.read(1024*32)
- if not buf:
- break
- fdst.write(buf)
-
- os.utime(dstfilepath, (st.st_atime, st.st_mtime))
- os.chmod(dstfilepath, stat.S_IMODE(st.st_mode))
- elif stat.S_ISLNK(st.st_mode):
- linkdst = os.readlink(srcfilepath)
- os.symlink(linkdst, dstfilepath)
- else:
- # Ignore all other special files (block/char/sock/fifo)
- pass
-
-class Container:
- DEFAULT_PATH = "/var/lib/libvirt/filesystems"
- DEFAULT_IMAGE = "/var/lib/libvirt/images/%s.raw"
- SELINUX_FILE_TYPE = "svirt_lxc_file_t"
-
- def __init__(self, name=None, uri = "lxc:///", path = DEFAULT_PATH, config=None, create=False):
- self.uri = uri
- self.use_image = False
- self.size = 10 * MB
- self.path = path
- self.config = config
- if self.config:
- self.name = self.config.get_name()
- else:
- self.name = name
- self.dest = "%s/%s" % (self.path, self.name)
- self.file_type = self.SELINUX_FILE_TYPE
- self.conn = None
- self.image = None
- self.uid = 0
- self.mounts = []
-
- def get_file_type(self):
- return self.file_type
-
- def set_file_type(self, file_type):
- self.file_type = file_type
-
- def set_uid(self, uid):
- self.config.set_userid(uid)
-
- def get_uid(self):
- return self.config.get_userid(uid)
-
- def set_gid(self, gid):
- self.config.set_groupid(gid)
-
- def get_gid(self):
- return self.config.get_groupid(gid)
-
- def set_username(self, username):
- self.config.set_username(username)
-
- def get_username(self):
- return self.config.get_username()
-
- def set_homedir(self, homedir):
- self.config.set_homedir(homedir)
-
- def get_homedir(self):
- return self.config.get_homedir()
-
- def set_mounts(self, mounts):
- self.mounts = mounts
-
- def get_mounts(self):
- return self.mounts
-
- def add_mounts(self):
- self.config.add_mount_strv(self.mounts)
-
- def get_config_path(self, name = None):
- if not name:
- name = self.name
- return get_config_path(name)
-
- def get_filesystem_path(self, name = None):
- if not name:
- name = self.get_name()
- return "%s/%s" % (self.path, name)
-
- def get_image_path(self, name = None):
- if not name:
- name = self.get_name()
- return self.DEFAULT_IMAGE % name
-
- def set_image(self, size):
- self.use_image = True
- self.size = size * MB
-
- def set_path(self, path):
- self.path = path
- self.dest = "%s/%s" % (self.path, self.name)
-
- def get_name(self):
- return self.name
-
- def set_name(self, name):
- if self.config:
- raise ValueError([_("Cannot modify Name")])
- self.name = name
- self.dest = "%s/%s" % (self.path, self.name)
-
- def set_security(self, val):
- self.config.set_security_opts(val)
-
- def add_network(self, val):
- self.config.add_network_opts(val)
-
- def get_security_dynamic(self):
- return self.config.get_security_dynamic()
-
- def get_security_label(self):
- return self.config.get_security_label()
-
- def set_security_label(self):
- if selinux is None:
- return
-
- if self.image or self.get_security_dynamic():
- return
-
- selabel = self.get_security_label()
- if selabel is None:
- raise ValueError([_("Missing security label configuration")])
- parts = selabel.split(":")
- selinux.chcon(self.dest, "system_u:object_r:%s:%s" % (
- self.get_file_type(), ":".join(parts[3:])), True)
-
- def gen_filesystems(self):
- if self.use_image:
- self.image = self.DEFAULT_IMAGE % self.get_name()
- mount = LibvirtSandbox.ConfigMountHostImage.new(self.image, self.dest)
- self.config.add_mount(mount)
-
- def fix_stat(self, f):
- try:
- s = os.stat(f)
- path = "%s%s" % (self.dest, f)
- os.chown(path, s.st_uid, s.st_gid)
- os.chmod(path, s.st_mode)
- except OSError, e:
- if not e.errno == errno.ENOENT:
- raise
-
- def fix_protection(self):
- l = len(self.dest)
- for root, dirs, files in os.walk(self.dest):
- for f in files:
- dest = root + "/" + f
- self.fix_stat(dest[l:])
- for d in dirs:
- dest = root + "/" + d
- self.fix_stat(dest[l:])
-
- def makedirs(self, d):
- try:
- path = "%s%s" % (self.dest, d)
- os.makedirs(path)
- except OSError, e:
- if not e.errno == errno.EEXIST:
- raise
-
- def makefile(self, f):
- self.makedirs(os.path.dirname(f))
- try:
- path = "%s%s" % (self.dest, f)
- fd=open(path, "w")
- fd.close()
- except OSError, e:
- if not e.errno == errno.EEXIST:
- raise
-
- def umount(self):
- p = Popen(["/bin/umount", self.dest])
- p.communicate()
- if p.returncode and p.returncode != 0:
- raise OSError(_("Failed to unmount image %s from %s") % (self.image, self.dest))
-
- def create_image(self):
- fd = open(self.image, "w")
- fd.truncate(self.size)
- fd.close()
- p = Popen(["/sbin/mkfs","-F", "-t", "ext4", self.image],stdout=PIPE, stderr=PIPE)
- p.communicate()
- if p.returncode and p.returncode != 0:
- raise OSError(_("Failed to build image %s") % self.image )
-
- p = Popen(["/bin/mount", self.image, self.dest])
- p.communicate()
- if p.returncode and p.returncode != 0:
- raise OSError(_("Failed to mount image %s on %s") % (self.image, self.dest))
-
- def save_config(self):
- self.connect()
- context = self.context()
- context.define()
- sys.stdout.write(_("Created sandbox config %s\n") % get_config_path(self.name))
-
- def delete(self):
- self.connect()
- self.conn.fetch_domains(None)
- dom = self.conn.find_domain_by_name(self.name)
- if dom is not None:
- info = dom.get_info()
- if info.state == LibvirtGObject.DomainState.RUNNING:
- raise ValueError([_("Cannot delete running container")])
-
- # Not sure we should remove content
- if os.path.exists(self.dest):
- shutil.rmtree(self.dest)
-
- image = self.get_image_path()
- if os.path.exists(image):
- os.remove(image)
-
- context = self.context()
- context.undefine()
-
- def get_security_model(self):
- # XXX selinux is the default for the while, needs to be configurable someday
- model = "selinux"
- supported = False
-
- # Make sure we have a connection
- self.connect()
-
- # Loop over the security models from the host capabilities
- configCaps = self.conn.get_capabilities()
- hostCaps = configCaps.get_host()
- secmodels = hostCaps.get_secmodels()
- for secmodel in secmodels:
- if secmodel.get_model() == model:
- supported = True
- break
-
- if not supported:
- model = None
- return model
-
-
- def create(self):
- self.connect()
- if self.get_security_model() is not None and \
- self.config.get_security_dynamic() and not self.use_image:
- raise ValueError([_("Dynamic security label only supported for image based containers")])
- if self.uri != "lxc:///":
- self.config.set_shell(True)
- if not os.path.exists(self.dest):
- os.mkdir(self.dest)
-
- def connect(self):
- if not self.conn:
- self.conn=LibvirtGObject.Connection.new(self.uri)
- self.conn.open(None)
-
- def disconnect(self):
- if self.conn:
- self.conn.close()
- self.conn = None
-
- def context(self):
- return LibvirtSandbox.ContextService.new(self.conn, self.config)
-
- def add_bind_mount(self, source, dest):
- if self.image is None:
- mount = LibvirtSandbox.ConfigMountHostBind.new(source, dest)
- else:
- mount = LibvirtSandbox.ConfigMountGuestBind.new(source, dest)
- self.config.add_mount(mount)
-
- def add_ram_mount(self, dest, size):
- mount = LibvirtSandbox.ConfigMountRam.new(dest, size);
- self.config.add_mount(mount)
-
-class GenericContainer(Container):
- def __init__(self, name=None, uri = "lxc:///", path = Container.DEFAULT_PATH, config=None, create=False):
- Container.__init__(self, name, uri, path, config, create)
-
- if create:
- self.config = LibvirtSandbox.ConfigServiceGeneric.new(name)
-
- def gen_filesystems(self):
- Container.gen_filesystems(self)
- self.add_bind_mount(self.dest, self.path)
- self.add_mounts()
-
- def create_generic(self):
- Container.create(self)
- self.gen_filesystems()
-
- if self.image:
- self.create_image()
- self.umount()
- sys.stdout.write(_("Created sandbox container image %s\n") % self.image)
- else:
- sys.stdout.write(_("Created sandbox container dir %s\n") % self.dest)
- self.save_config()
-
- def create(self):
- try:
- self.create_generic()
- except Exception, e:
- try:
- self.delete()
- except Exception, e2:
- pass
- raise e
-
- def set_command(self, command):
- self.config.set_command(command)
-
-
-def is_template_unit(unit):
- return '@' in unit
-
-class SystemdContainer(Container):
- IGNORE_DIRS = [ "/var/run/", "/etc/logrotate.d/", "/etc/pam.d" ]
- DEFAULT_DIRS = [ "/etc", "/var" ]
- PROFILE_FILES = [ ".bashrc", ".bash_profile", ".profile" ]
- MACHINE_ID = "/etc/machine-id"
- HOSTNAME = "/etc/hostname"
- SYSVINIT_PATH = "/etc/rc.d"
- ANACONDA_WANTS_PATH = "/usr/lib/systemd/system/anaconda.target.wants"
- MULTI_USER_WANTS_PATH = "/usr/lib/systemd/system/multi-user.target.wants"
- SYSINIT_WANTS_PATH = "/usr/lib/systemd/system/sysinit.target.wants"
- SOCKET_WANTS_PATH = "/usr/lib/systemd/system/sockets.target.wants"
- MAKE_SYSTEM_DIRS = [ "/var/lib/dhclient", "/var/lib/dbus", "/var/log", "/var/spool", "/var/cache", "/var/tmp", "/var/lib/nfs/rpc_pipefs", SYSVINIT_PATH, "/lib/lsb" ]
- BIND_SYSTEM_DIRS = [ "/var", "/home", "/root", "/etc/systemd/system", "/etc/rc.d", "/usr/lib/systemd/system/basic.target.wants", "/usr/lib/systemd/system/local-fs.target.wants", ANACONDA_WANTS_PATH, MULTI_USER_WANTS_PATH, SYSINIT_WANTS_PATH, SOCKET_WANTS_PATH ]
- BIND_SYSTEM_FILES = [ MACHINE_ID, "/etc/fstab", HOSTNAME ]
- LOCAL_LINK_FILES = { SYSINIT_WANTS_PATH : [ "systemd-tmpfiles-setup.service" ] , SOCKET_WANTS_PATH : [ "dbus.socket", "systemd-journald.socket", "systemd-shutdownd.socket", "systemd-initctl.socket" ] }
-
- DEFAULT_UNIT = "/etc/systemd/system/%s_sandbox.service"
-
- def __init__(self, name=None, uri = "lxc:///", path = Container.DEFAULT_PATH, config=None, create=False, packages=[]):
- Container.__init__(self, name, uri, path, config, create)
- self.copy = False
- self.unit_file_list = []
- self.packages = packages
- if create:
- self.config = LibvirtSandbox.ConfigServiceSystemd.new(name)
- self.unitfile = None
- else:
- self.unitfile = self.get_unit_path()
-
- def follow_units(self):
- unitst=""
- for i, src in self.unit_file_list:
- unitst += "ReloadPropagatedFrom=%s\n" % i
-
- return unitst
-
- def get_unit_path(self, name = None):
- if not name:
- name = self.get_name()
- return self.DEFAULT_UNIT % name
-
- def set_unit_file_list(self, unit_file_list):
- self.unit_file_list = unit_file_list
-
- def get_sandboxed_service(self):
- return self.unit_file_list[0][0].split(".")[0]
-
- def create_system_unit(self):
- self.unitfile = self.get_unit_path()
- unit = r"""
-[Unit]
-Description=Secure Sandbox Container %(NAME)s
-Requires=libvirtd.service
-After=libvirtd.service
-%(FOLLOW)s
-[Service]
-Type=simple
-ExecStart=/usr/libexec/virt-sandbox-service-util -c %(URI)s -s %(NAME)s
-ExecReload=/usr/bin/virt-sandbox-service -c %(URI)s reload -u %(RELOAD)s %(NAME)s
-ExecStop=/usr/bin/virsh -c %(URI)s destroy %(NAME)s
-
-[Install]
-WantedBy=multi-user.target
-""" % { 'NAME':self.name,
- 'FOLLOW':self.follow_units(),
- 'RELOAD': " -u ".join(map(lambda x: x[0], self.unit_file_list)),
- 'URI': self.uri,
- }
-
- fd = open(self.unitfile, "w")
- fd.write(unit)
- fd.close()
- if selinux is not None:
- selinux.restorecon(self.unitfile)
- sys.stdout.write(_("Created unit file %s\n") % self.unitfile)
-
- def add_dir(self, newd):
- if newd in self.all_dirs:
- return
- for ignd in self.IGNORE_DIRS:
- if newd.startswith(ignd):
- return
- for defd in self.DEFAULT_DIRS:
- if newd.startswith(defd):
- self.all_dirs.append(newd)
- tmp_dirs = []
- for d in self.dirs:
- if newd.startswith(d):
- return
- if not d.startswith(newd):
- tmp_dirs.append(d)
- self.dirs = tmp_dirs
- self.dirs.append(newd)
- break;
-
- def add_file(self, newf):
- if newf in self.files:
- return
- for d in self.IGNORE_DIRS:
- if newf.startswith(d):
- return
- for d in self.DEFAULT_DIRS:
- if newf.startswith(d):
- self.files.append(newf)
- break;
-
- def get_name(self):
- if self.config:
- return self.config.get_name()
- raise ValueError([_("Name not configured")])
-
- def set_copy(self, copy):
- self.copy = copy
-
- def get_security_dynamic(self):
- return self.config.get_security_dynamic()
-
- def extract_rpms(self):
- self.all_dirs = []
- self.dirs = []
- self.files = []
-
- self.ts = rpm.ts()
-
- nb_packages = 0
- for u, src in self.unit_file_list:
- rpm_name = self.get_rpm_for_unit(src)
- if rpm_name:
- self.extract_rpm(rpm_name)
- nb_packages += 1
-
- for package in self.packages:
- self.extract_rpm(package)
- nb_packages += 1
-
- if nb_packages == 0:
- raise ValueError([_("Cannot autodetect the package for unit files, please use --package")])
-
- def split_filename(self, filename):
- if filename[-4:] == '.rpm':
- filename = filename[:-4]
-
- archIndex = filename.rfind('.')
- arch = filename[archIndex+1:]
-
- relIndex = filename[:archIndex].rfind('-')
- rel = filename[relIndex+1:archIndex]
-
- verIndex = filename[:relIndex].rfind('-')
- ver = filename[verIndex+1:relIndex]
-
- epochIndex = filename.find(':')
- if epochIndex == -1:
- epoch = ''
- else:
- epoch = filename[:epochIndex]
-
- name = filename[epochIndex + 1:verIndex]
- return name, ver, rel, epoch, arch
-
- def get_rpm_for_unit(self, unitfile):
- mi = self.ts.dbMatch(rpm.RPMTAG_BASENAMES, unitfile)
- try:
- h = mi.next();
- except exceptions.StopIteration:
- return None
- return h['name']
-
-
- def extract_rpm(self, rpm_name):
- mi = self.ts.dbMatch('name', rpm_name)
- try:
- h = mi.next();
- except exceptions.StopIteration:
- raise ValueError([_("Cannot find package named %s") % rpm_name])
-
- for fentry in h.fiFromHeader():
- fname = fentry[0]
-
- if os.path.isdir(fname):
- self.add_dir(fname)
- if os.path.isfile(fname):
- self.add_file(fname)
-
- srcrpm = h[rpm.RPMTAG_SOURCERPM]
- srcrpmbits = self.split_filename(srcrpm)
-
- if srcrpmbits[0] == h[rpm.RPMTAG_NAME]:
- return
-
- mi = self.ts.dbMatch(rpm.RPMTAG_NAME, srcrpmbits[0])
- try:
- h = mi.next();
- except exceptions.StopIteration:
- raise ValueError([_("Cannot find base package %s") % srcrpmbits[0]])
-
- for fentry in h.fiFromHeader():
- fname = fentry[0]
-
- if os.path.isdir(fname):
- self.add_dir(fname)
- if os.path.isfile(fname):
- self.add_file(fname)
-
- def gen_hostname(self):
- fd=open(self.dest + self.HOSTNAME, "w")
- fd.write("%s\n" % self.name )
- fd.close()
-
- def gen_machine_id(self):
- uuid_fd = open("/proc/sys/kernel/random/uuid")
- uuid = uuid_fd.read().replace("-","").rstrip()
- uuid_fd.close()
- self.config.set_uuid(uuid)
- fd=open(self.dest + self.MACHINE_ID, "w")
- fd.write("%s\n" % uuid)
- fd.close()
-
- if not self.use_image:
- # Link /var/log/journal within the container to /var/log/journal/UUID
- # on host. This will allow the hosts journalctl to easily read
- # containers journal information.
- jdir = "/var/log/journal/"
- jpath = jdir + uuid
- if not os.path.exists(self.dest + jpath):
- os.makedirs(self.dest + jpath)
- if not os.path.exists(jdir):
- os.makedirs(jdir)
-
- os.symlink(self.dest + jpath, jpath)
-
- def gen_filesystems(self):
- Container.gen_filesystems(self)
- # 10 MB /run
- mount = LibvirtSandbox.ConfigMountRam.new("/run", 10 * 1024 * 1024);
- self.config.add_mount(mount)
-
- # 100 MB /tmp
- mount = LibvirtSandbox.ConfigMountRam.new("/tmp", 100 * 1024 * 1024);
- self.config.add_mount(mount)
-
- # 100 MB /tmp
- mount = LibvirtSandbox.ConfigMountRam.new("/dev/shm", 100 * 1024 * 1024);
- self.config.add_mount(mount)
-
- for d in self.BIND_SYSTEM_DIRS:
- if os.path.exists(d):
- source = "%s%s" % ( self.dest, d)
- self.add_bind_mount(source, d)
-
- for f in self.BIND_SYSTEM_FILES:
- if os.path.exists(f):
- source = "%s%s" % ( self.dest, f)
- self.add_bind_mount(source, f)
-
- for d in self.dirs:
- found = False
- # Dont add dirs whos parents are in SYSTEM_DIRS
- for s in self.BIND_SYSTEM_DIRS:
- if d.startswith(s):
- found = True
- break
- if not found:
- source = "%s%s" % ( self.dest, d)
- self.add_bind_mount(source, d)
- self.add_mounts()
-
- def get_expanded_unit_template(self, unit):
- return unit.replace('@', '@' + self.name)
-
- def create_container_unit(self, src, dest, unit):
- if is_template_unit(unit):
- shutil.copy(src, dest + "/" + unit)
- unit = self.get_expanded_unit_template(unit)
- os.symlink(src, dest + "/" + unit)
-
- dropin_dir = "%s/%s.d" % (dest, unit)
- if not os.path.exists(dropin_dir):
- os.mkdir(dropin_dir)
-
- fd = open(dropin_dir + "/virt-sandbox.conf", "w")
- fd.write("""; file placed here by virt-sandbox-service
-[Service]
-PrivateTmp=false
-PrivateNetwork=false
-""" )
- fd.close()
-
- def gen_content(self):
- if self.copy:
- for d in self.dirs:
- copydirtree(d, "%s%s" % (self.dest, d))
- for f in self.files:
- self.makedirs(os.path.dirname(f))
- shutil.copy(f, "%s%s" % (self.dest, f))
- else:
- for d in self.all_dirs:
- self.makedirs(d)
- for f in self.files:
- self.makedirs(os.path.dirname(f))
- self.makefile(f)
-
- for d in self.BIND_SYSTEM_DIRS + self.MAKE_SYSTEM_DIRS:
- self.makedirs(d)
-
- for f in self.BIND_SYSTEM_FILES:
- self.makefile(f)
-
- destpath = self.dest + self.SYSVINIT_PATH
- for i in range(7):
- os.mkdir(destpath+("/rc%s.d" % i))
-
- # Copy both /etc/rc.d/init.d/functions and /lib/lsb/init-functions, even
- # though the latter is the one recommended
- if os.path.exists(self.SYSVINIT_PATH + "/init.d/functions"):
- os.mkdir(destpath+"/init.d")
- shutil.copy(self.SYSVINIT_PATH + "/init.d/functions" , destpath + "/init.d")
-
- if os.path.exists("/lib/lsb/init-functions"):
- shutil.copy("/lib/lsb/init-functions" , self.dest + "/lib/lsb/")
-
- self.gen_machine_id()
- self.gen_hostname()
-
- for k in self.LOCAL_LINK_FILES:
- for d in self.LOCAL_LINK_FILES[k]:
- src = "../%s" % ( d)
- dest = "%s%s/%s" % ( self.dest, k, d)
- os.symlink(src,dest)
-
- unitdir = "/etc/systemd/system"
- tgtdir = unitdir + "/multi-user.target.wants"
-
- self.makedirs(unitdir)
- self.makedirs(tgtdir)
- os.symlink("/run", self.dest + "/var/run")
-
- for i, src in self.unit_file_list:
- self.create_container_unit(src, self.dest + unitdir, i)
- if is_template_unit(i):
- i = self.get_expanded_unit_template(i)
- os.symlink(src, self.dest + tgtdir + "/" + i)
-
- tgtfile = unitdir + "/multi-user.target"
- try:
- fd = open(self.dest + tgtfile, "w")
- fd.write("[Unit]\n")
- fd.write("Description=Sandbox multi-user target\n")
- fd.close()
- except OSError, e:
- if not e.errno == errno.EEXIST:
- raise
-
- for p in self.PROFILE_FILES:
- profile = "/etc/skel/" + p
- if os.path.exists(profile):
- shutil.copy(profile, self.dest + "/root/")
-
- self.fix_protection()
-
- def delete(self):
- try:
- uuid = self.config.get_uuid()
- if uuid is not None:
- jpath = "/var/log/journal/" + uuid
- if os.path.lexists(jpath):
- os.remove(jpath)
- except Exception, e:
- sys.stderr.write("%s: %s\n" % (sys.argv[0], e))
- sys.stderr.flush()
-
- Container.delete(self)
-
- if self.unitfile and os.path.exists(self.unitfile):
- p = Popen(["/usr/bin/systemctl","disable", self.unitfile],stdout=PIPE, stderr=PIPE)
- p.communicate()
- if p.returncode and p.returncode != 0:
- raise OSError(_("Failed to disable %s unit file") % self.unitfile)
- os.remove(self.unitfile)
-
- def create_systemd(self):
- self.extract_rpms()
- Container.create(self)
- self.gen_filesystems()
- if self.image:
- self.create_image()
- self.gen_content()
- self.umount()
- sys.stdout.write(_("Created sandbox container image %s\n") % self.image)
- else:
- self.gen_content()
- sys.stdout.write(_("Created sandbox container dir %s\n") % self.dest)
- self.set_security_label()
- self.create_system_unit()
- self.config.set_boot_target("multi-user.target")
- self.save_config()
-
- def create(self):
- if os.path.exists(self.dest):
- raise OSError(_("%s already exists") % self.dest)
-
- try:
- self.create_systemd()
- except Exception, e:
- try:
- self.delete()
- except Exception, e2:
- sys.stderr.write("Cleanup failed: %s\n" % str(e2))
- raise
-
- def reload(self, unitfiles):
- class Args:
- command = []
- noseclabel = None
- name = self.name
- uri = self.uri
- args = Args()
- args.command = [ "systemctl", "reload" ] + map(lambda x: x[0], unitfiles)
- execute(args)
-
-MB = int(1000000)
-
-def delete(args):
- config = read_config(args.name)
- if config is None:
- sys.stderr.write("Sandbox '%s' does not exist\n" % args.name)
- sys.exit(1)
-
- if isinstance(config, gi.repository.LibvirtSandbox.ConfigServiceGeneric):
- container = GenericContainer(uri=args.uri, config = config)
- else:
- container = SystemdContainer(uri=args.uri, config = config)
- container.set_path(args.path)
- container.delete()
-
-def create(args):
- if len(args.command) > 0 and len(args.unitfiles) > 0:
- raise ValueError([_("Commands cannot be specified with unit files")])
-
- if len(args.command) == 0 and len(args.unitfiles) == 0:
- raise ValueError([_("You must specify a command or a unit file")])
-
- if args.packages and len(args.unitfiles) != 1:
- raise ValueError([_("Option --package cannot be used without a unit file")])
-
- if len(args.command) > 0:
- container = GenericContainer(name = args.name, uri=args.uri, create = True)
- container.set_command(args.command)
- else:
- container = SystemdContainer(name = args.name, uri=args.uri, create = True, packages = args.packages)
- container.set_copy(args.copy)
- container.set_unit_file_list(args.unitfiles)
- for net in args.network:
- container.add_network(net)
- if args.security:
- container.set_security(args.security)
- container.set_uid(args.uid)
- if not args.homedir:
- args.homedir = pwd.getpwuid(args.uid).pw_dir
- container.set_homedir(args.homedir)
- if not args.username:
- args.username = pwd.getpwuid(args.uid).pw_name
- container.set_username(args.username)
- if not args.gid:
- args.gid = pwd.getpwuid(args.uid).pw_gid
- container.set_gid(args.gid)
- container.set_path(args.path)
- container.set_file_type(args.file_type)
- container.set_mounts(args.mounts)
- if args.imagesize:
- container.set_image(args.imagesize)
-
- container.create()
-
-def usage(parser, msg):
- parser.print_help()
-
- sys.stderr.write("\n%s\n" % msg)
- sys.stderr.flush()
- sys.exit(1)
-
-def sandbox_reload(args):
- config = read_config(args.name)
- if isinstance(config, gi.repository.LibvirtSandbox.ConfigServiceGeneric):
- raise ValueError([_("Generic Containers do not support reload")])
- container = SystemdContainer(uri = args.uri, config = config)
- container.reload(args.unitfiles)
-
-def connect(args):
- if args.uri == "lxc:///":
- class Args:
- command = []
- noseclabel = None
- name = args.name
- uri = args.uri
-
- args = Args()
- args.command = [ "/bin/sh" ]
- execute(args)
- return
-
- print """\
-Connected to %s.
-Type 'Ctrl + ]' to detach from the console.
-""" % ( args.name )
- os.execl("/usr/libexec/virt-sandbox-service-util",
- "virt-sandbox-service-util",
- "-c", args.uri,
- "-a", args.name)
-
-#
-# Search Path for command to execute within the container.
-#
-def fullpath(cmd):
- for i in [ "/", "./", "../" ]:
- if cmd.startswith(i):
- return cmd
- for i in os.environ["PATH"].split(':'):
- f = "%s/%s" % (i, cmd)
- if os.access(f, os.X_OK):
- return f
- return cmd
-
-def execute(args):
- if args.uri != "lxc:///":
- raise ValueError([_("Can only execute commands inside of linux containers.")])
-
- myexec = [ "virsh", "-c", args.uri, "lxc-enter-namespace" ]
- if args.noseclabel:
- myexec.append("--noseclabel")
- myexec.extend([ args.name, "--", fullpath(args.command[0])] + args.command[1:])
- os.execv("/usr/bin/virsh", myexec)
-
-def clone(args):
- config = read_config(args.source)
- if isinstance(config, gi.repository.LibvirtSandbox.ConfigServiceGeneric):
- container = GenericContainer(uri=args.uri, config=config)
- else:
- container = SystemdContainer(uri=args.uri, config=config)
- newcontainer = None
-
- container.set_path(args.path)
-
- old_path = container.get_filesystem_path()
- new_path = container.get_filesystem_path(args.dest)
-
- if os.path.exists(new_path):
- raise OSError(_("%s already exists") % new_path)
-
- try:
- fd = open(container.get_config_path(),"r")
- recs = fd.read()
- fd.close()
-
- newrec = recs.replace(old_path + "/", new_path + "/")
- newrec = newrec.replace("name=" + args.source, "name=" + args.dest)
- old_image_path = container.get_image_path()
- if os.path.exists(old_image_path):
- new_image_path = container.get_image_path(args.dest)
- newrec = newrec.replace(old_image_path, new_image_path)
- shutil.copy(old_image_path, new_image_path)
- sys.stdout.write(_("Created sandbox container image %s\n") % new_image_path)
- os.mkdir(new_path)
- else:
- copydirtree(old_path, new_path)
- sys.stdout.write(_("Created sandbox container dir %s\n") % new_path)
-
- if isinstance(config, gi.repository.LibvirtSandbox.ConfigServiceGeneric):
- newcontainer = GenericContainer(name=args.dest, uri=args.uri, create=True)
- newcontainer.set_path(args.path)
- else:
- fd = open(container.get_unit_path())
- recs = fd.read()
- fd.close()
-
- new_unit = container.get_unit_path(args.dest)
- fd = open(new_unit, "wx")
- fd.write(recs.replace(args.source, args.dest))
- fd.close()
-
- sys.stdout.write(_("Created unit file %s\n") % new_unit)
-
- config = LibvirtSandbox.Config.load_from_data(newrec)
- newcontainer = SystemdContainer(config=config, uri=args.uri)
- newcontainer.set_path(args.path)
- newcontainer.gen_machine_id()
- newcontainer.gen_hostname()
-
- if args.security:
- newcontainer.set_security(args.security)
- newcontainer.set_security_label()
- newcontainer.save_config()
- except Exception, e:
- if newcontainer is not None:
- newcontainer.delete()
- raise
-
-
-def upgrade_config_legacy(path):
- config = LibvirtSandbox.Config.load_from_path(path)
-
- if isinstance(config, gi.repository.LibvirtSandbox.ConfigServiceGeneric):
- container = GenericContainer(uri=args.uri, config=config)
- else:
- container = SystemdContainer(uri=args.uri, config=config)
-
- fd = open(container.get_unit_path())
- unitfile = fd.read()
- fd.close()
-
- unitfile = unitfile.replace("/usr/bin/virt-sandbox-service start",
- "/usr/libexec/virt-sandbox-service-util -c lxc:/// -s")
- unitfile = unitfile.replace("/usr/bin/virt-sandbox-service reload",
- "/usr/bin/virt-sandbox-service -c lxc:/// reload")
- unitfile = unitfile.replace("/usr/bin/virt-sandbox-service stop",
- "/usr/bin/virsh -c lxc:/// destroy")
-
- unitfile = re.sub("WantedBy=.*\.target",
- "WantedBy=multi-user.target",
- unitfile)
-
- os.remove(container.get_unit_path())
- fd = open(container.get_unit_path(), "wx")
- fd.write(unitfile)
- fd.close()
-
- sys.stdout.write(_("Created unit file %s\n") %
- container.get_unit_path())
-
- # Create new config file + libvirt persistent XML config
- container.save_config()
- # Kill legacy config file
- os.remove(path)
-
-
-def upgrade_config(args):
- configfile = get_legacy_config_path(args.name)
- if os.path.exists(configfile):
- upgrade_config_legacy(configfile)
-
-
-def upgrade_filesystem(args):
- # This is where we'd look at RPM DB and upgrade the
- # filesystem with new info for the unit files
- pass
-
-# This function must be capable of reading configs created by
-# old releases and "fixing" them to work with the new release
-def upgrade(args):
- upgrade_config(args)
- upgrade_filesystem(args)
-
-import argparse
-class AddMount(argparse.Action):
- def __call__(self, parser, namespace, values, option_string=None):
- newval = getattr(namespace, self.dest)
- if not newval:
- newval = []
- for v in values:
- newval.append(v)
- setattr(namespace, self.dest, newval)
-
-class SizeAction(argparse.Action):
- def __call__(self, parser, namespace, values, option_string=None):
- setattr(namespace, self.dest, int(values))
-
-class CheckUnit(argparse.Action):
- def __call__(self, parser, namespace, value, option_string=None):
- def check_unit(unit):
- src = "/etc/systemd/system/" + unit
- if os.path.exists(src):
- return src
- src = "/usr/lib/systemd/system/" + unit
- if os.path.exists(src):
- return src
- return None
- src = check_unit(value)
- if not src:
- src = check_unit(value + ".service")
- if src:
- value = value + ".service"
- else:
- raise OSError(_("Requested unit %s does not exist") % value)
-
- unitfiles = getattr(namespace, self.dest)
- if unitfiles:
- unitfiles.append((value, src))
- else:
- unitfiles = [ (value, src) ]
- setattr(namespace, self.dest, unitfiles)
-
-class SetNet(argparse.Action):
- def __call__(self, parser, namespace, values, option_string=None):
- nets = getattr(namespace, self.dest)
- if nets:
- nets.append(values)
- else:
- nets = [values]
- setattr(namespace, self.dest, nets)
-
-class CheckPackage(argparse.Action):
- def __call__(self, parser, namespace, value, option_string=None):
- nb_rpm = len(rpm.TransactionSet().dbMatch('name', value))
- if nb_rpm == 0:
- raise OSError(_("Cannot find %s rpm") % value)
- elif nb_rpm > 1:
- raise OSError(_("%s rpm is installed more than once") % value)
- packages = getattr(namespace, self.dest)
- if packages:
- packages.append(value)
- else:
- packages = [ value ]
- setattr(namespace, self.dest, packages)
-
-def requires_name(parser):
- parser.add_argument("name",
- help=_("name of the sandbox container"))
-
-def default_security_opts():
- if selinux is None:
- return None
-
- # XXX vary per URI for kvm/qemu/lxc.
- # XXX generate a random category
- return "static,label=system_u:system_r:svirt_lxc_net_t:s0"
-
-def gen_create_args(subparser):
- parser = subparser.add_parser("create",
- help=_("Create a sandbox container."))
-
- parser.add_argument("-C", "--copy", default=False,
- action="store_true",
- help=_("copy content from the hosts /etc and /var directories that will be mounted within the sandbox"))
-
- parser.add_argument("-f", "--filetype", dest="file_type",
- default=c.get_file_type(),
- help=_("SELinux file type to assign to content within the sandbox. Default: %s") % c.get_file_type())
- parser.add_argument("--homedir", dest="homedir",
- help=_("Specify the homedir for the container. Default: UID homedir."))
- parser.add_argument("-G", "--gid", dest="gid",
- default=None, type=int,
- help=_("Specify the login gid for the container. Default: login GID of the UID."))
- parser.add_argument("-i", "--imagesize", dest="imagesize", default = None,
- action=SizeAction,
- help=_("create image of this many megabytes."))
- parser.add_argument("-m", "--mount", dest="mounts",default=[], nargs="*", action=AddMount,
- help=_("Mount a filesytem in the sandbox"))
- parser.add_argument("-N", "--network", dest="network",
- action=SetNet, default=[],
- help=_("Specify the network configuration"))
- parser.add_argument("-p", "--path", dest="path", default=c.DEFAULT_PATH,
- help=_("select path to store sandbox content. Default: %s") % c.DEFAULT_PATH)
- parser.add_argument("-s", "--security", dest="security",
- default=default_security_opts(),
- help=_("Specify the security model configuration for the sandbox: Defaults to dynamic"))
- parser.add_argument("-u", "--unitfile",
- action=CheckUnit,
- dest="unitfiles", default=[],
- help=_("Systemd Unit file to run within the systemd sandbox container. Commands cannot be specified with unit files."))
- parser.add_argument("-P", "--package",
- action=CheckPackage,
- dest="packages", default=[],
- help=_("RPM package to be used in the container. Default: autodetected from unit files."))
- parser.add_argument("--username", dest="username",
- help=_("Specify the username for the container. Default: UID username."))
- parser.add_argument("-U", "--uid", dest="uid",
- default=os.getuid(),type=int,
- help=_("Specify the uid for the container: Default to current UID."))
-
- requires_name(parser)
- parser.add_argument("command", default=[], nargs="*",
- help=_("Command to run within the generic sandbox container. Commands cannot be specified with unit files."))
-
- parser.set_defaults(func=create)
-
-def gen_connect_args(subparser):
- parser = subparser.add_parser("connect",
- help=_("Connect to a sandbox container"))
- requires_name(parser)
- parser.set_defaults(func=connect)
-
-def gen_execute_args(subparser):
- parser = subparser.add_parser("execute",
- help=_("Execute a command within a sandbox container. Only available for lxc:///"))
- parser.add_argument("-N", "--noseclabel", dest="noseclabel",
- default=False, action="store_true",
- help=_("do not modify the label of the executable process. By default all commands execute with the label of the sandbox"))
- requires_name(parser)
- parser.add_argument("command", nargs="+",
- help=_("command to execute within the container"))
- parser.set_defaults(func=execute)
-
-def gen_reload_args(subparser):
- parser = subparser.add_parser("reload",
- help=_("Reload a running sandbox container"))
- parser.add_argument("-u", "--unitfile", required=True,
- action=CheckUnit, dest="unitfiles",
- help=_("Systemd Unit file to reload within the sandbox container"))
- requires_name(parser)
- parser.set_defaults(func=sandbox_reload)
-
-def gen_clone_args(subparser):
- parser = subparser.add_parser("clone",
- help=_("Clone an existing sandbox container"))
- parser.set_defaults(func=clone)
- parser.add_argument("-p", "--path", dest="path", default=c.DEFAULT_PATH,
- help=_("select path to copy sandbox content from/to. Default: %s") % c.DEFAULT_PATH)
- parser.add_argument("-s", "--security", dest="security",
- default=default_security_opts(),
- help=_("Specify the security model configuration for the sandbox: Defaults to dynamic"))
-
- parser.add_argument("source",
- help=_("source sandbox container name"))
- parser.add_argument("dest",
- help=_("dest name of the new sandbox container"))
-
-def gen_delete_args(subparser):
- parser = subparser.add_parser("delete",
- help=_("Delete a sandbox container"))
- parser.add_argument("-p", "--path", dest="path", default=c.DEFAULT_PATH,
- help=_("select path to delete sandbox content from. Default: %s") % c.DEFAULT_PATH)
- requires_name(parser)
- parser.set_defaults(func=delete)
-
-def gen_upgrade_args(subparser):
- parser = subparser.add_parser("upgrade",
- help=_("Upgrade the sandbox container"))
- requires_name(parser)
- parser.set_defaults(func=upgrade)
-
-if __name__ == '__main__':
- c = Container()
-
- parser = argparse.ArgumentParser(description='Sandbox Container Tool')
- parser.add_argument("-c", "--connect", required=False, dest="uri", default="lxc:///",
- help=_("libvirt connection URI to use (lxc:/// [default] or qemu:///session)"))
-
- subparser = parser.add_subparsers(help=_("commands"))
- gen_create_args(subparser)
- gen_clone_args(subparser)
- gen_connect_args(subparser)
- gen_delete_args(subparser)
- gen_execute_args(subparser)
- gen_reload_args(subparser)
- gen_upgrade_args(subparser)
-
- try:
- args = parser.parse_args()
- if args.uri[0:3] != "lxc":
- sys.stderr.write("%s: only lxc:/// URIs are currently supported\n" % sys.argv[0])
- sys.exit(1)
- args.func(args)
- sys.exit(0)
- except KeyboardInterrupt, e:
- sys.exit(0)
- except ValueError, e:
- for line in e:
- for l in line:
- sys.stderr.write("%s: %s\n" % (sys.argv[0], l))
- sys.stderr.flush()
- sys.exit(1)
- except IOError, e:
- sys.stderr.write("%s: %s: %s\n" % (sys.argv[0], e.filename, e.strerror))
- sys.stderr.flush()
- sys.exit(1)
- except OSError, e:
- sys.stderr.write("%s: %s\n" % (sys.argv[0], e))
- sys.stderr.flush()
- sys.exit(1)
- except GLib.GError, e:
- sys.stderr.write("%s: %s\n" % (sys.argv[0], e))
- sys.stderr.flush()
- sys.exit(1)
Index: libvirt-sandbox-0.5.1/bin/Makefile.am
===================================================================
--- libvirt-sandbox-0.5.1.orig/bin/Makefile.am
+++ libvirt-sandbox-0.5.1/bin/Makefile.am
@@ -66,6 +66,11 @@ virt-sandbox-service-upgrade.1: virt-san
CLEANFILES = $(man1_MANS)
+virt-sandbox-service: virt-sandbox-service.in
+ sed \
+ -e 's!/usr/libexec/!$(libexecdir)/!g' \
+ < $< > $@
+
virt_sandbox_SOURCES = virt-sandbox.c
virt_sandbox_CFLAGS = \
$(GIO_UNIX_CFLAGS) \
Index: libvirt-sandbox-0.5.1/bin/virt-sandbox-service.in
===================================================================
--- /dev/null
+++ libvirt-sandbox-0.5.1/bin/virt-sandbox-service.in
@@ -0,0 +1,1279 @@
+#!/usr/bin/python -Es
+#
+# Authors: Dan Walsh <dwalsh@redhat.com>
+#
+# Copyright (C) 2012-2013 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+from gi.repository import LibvirtGObject
+from gi.repository import LibvirtSandbox
+from gi.repository import GLib
+import gi
+import re
+import os, sys, shutil, errno, stat
+import exceptions
+import rpm
+from subprocess import Popen, PIPE, STDOUT
+import gettext
+import pwd
+
+if os.path.exists("/sys/fs/selinux"):
+ import selinux
+else:
+ selinux = None
+
+LibvirtGObject.init_object_check(None)
+LibvirtSandbox.init_check(None)
+
+gettext.bindtextdomain("libvirt-sandbox", "/usr/share/locale")
+gettext.textdomain("libvirt-sandbox")
+try:
+ gettext.install("libvirt-sandbox",
+ localedir="/usr/share/locale",
+ unicode=False,
+ codeset = 'utf-8')
+except IOError:
+ import __builtin__
+ __builtin__.__dict__['_'] = unicode
+
+CONFIG_PATH = "/etc/libvirt-sandbox/services/"
+def get_config_path(name):
+ return CONFIG_PATH + name + "/config/sandbox.cfg"
+
+def get_legacy_config_path(name):
+ return CONFIG_PATH + name + ".sandbox"
+
+def read_config(name):
+ path = get_config_path(name)
+ if not os.path.exists(path):
+ return None
+ return LibvirtSandbox.Config.load_from_path(path)
+
+# shutil.copytree throws a fit if it finds sockets
+# or fifos, and has really bad behaviour on block
+# and character devices too.
+def copydirtree(src, dst):
+ filenames = os.listdir(src)
+ os.makedirs(dst)
+
+ for filename in filenames:
+ srcfilepath = os.path.join(src, filename)
+ dstfilepath = os.path.join(dst, filename)
+
+ st = os.lstat(srcfilepath)
+ if stat.S_ISDIR(st.st_mode):
+ copydirtree(srcfilepath, dstfilepath)
+
+ os.utime(dstfilepath, (st.st_atime, st.st_mtime))
+ os.chmod(dstfilepath, stat.S_IMODE(st.st_mode))
+ elif stat.S_ISREG(st.st_mode):
+ with open(srcfilepath, 'rb') as fsrc:
+ with open(dstfilepath, 'wb') as fdst:
+ while 1:
+ buf = fsrc.read(1024*32)
+ if not buf:
+ break
+ fdst.write(buf)
+
+ os.utime(dstfilepath, (st.st_atime, st.st_mtime))
+ os.chmod(dstfilepath, stat.S_IMODE(st.st_mode))
+ elif stat.S_ISLNK(st.st_mode):
+ linkdst = os.readlink(srcfilepath)
+ os.symlink(linkdst, dstfilepath)
+ else:
+ # Ignore all other special files (block/char/sock/fifo)
+ pass
+
+class Container:
+ DEFAULT_PATH = "/var/lib/libvirt/filesystems"
+ DEFAULT_IMAGE = "/var/lib/libvirt/images/%s.raw"
+ SELINUX_FILE_TYPE = "svirt_lxc_file_t"
+
+ def __init__(self, name=None, uri = "lxc:///", path = DEFAULT_PATH, config=None, create=False):
+ self.uri = uri
+ self.use_image = False
+ self.size = 10 * MB
+ self.path = path
+ self.config = config
+ if self.config:
+ self.name = self.config.get_name()
+ else:
+ self.name = name
+ self.dest = "%s/%s" % (self.path, self.name)
+ self.file_type = self.SELINUX_FILE_TYPE
+ self.conn = None
+ self.image = None
+ self.uid = 0
+ self.mounts = []
+
+ def get_file_type(self):
+ return self.file_type
+
+ def set_file_type(self, file_type):
+ self.file_type = file_type
+
+ def set_uid(self, uid):
+ self.config.set_userid(uid)
+
+ def get_uid(self):
+ return self.config.get_userid(uid)
+
+ def set_gid(self, gid):
+ self.config.set_groupid(gid)
+
+ def get_gid(self):
+ return self.config.get_groupid(gid)
+
+ def set_username(self, username):
+ self.config.set_username(username)
+
+ def get_username(self):
+ return self.config.get_username()
+
+ def set_homedir(self, homedir):
+ self.config.set_homedir(homedir)
+
+ def get_homedir(self):
+ return self.config.get_homedir()
+
+ def set_mounts(self, mounts):
+ self.mounts = mounts
+
+ def get_mounts(self):
+ return self.mounts
+
+ def add_mounts(self):
+ self.config.add_mount_strv(self.mounts)
+
+ def get_config_path(self, name = None):
+ if not name:
+ name = self.name
+ return get_config_path(name)
+
+ def get_filesystem_path(self, name = None):
+ if not name:
+ name = self.get_name()
+ return "%s/%s" % (self.path, name)
+
+ def get_image_path(self, name = None):
+ if not name:
+ name = self.get_name()
+ return self.DEFAULT_IMAGE % name
+
+ def set_image(self, size):
+ self.use_image = True
+ self.size = size * MB
+
+ def set_path(self, path):
+ self.path = path
+ self.dest = "%s/%s" % (self.path, self.name)
+
+ def get_name(self):
+ return self.name
+
+ def set_name(self, name):
+ if self.config:
+ raise ValueError([_("Cannot modify Name")])
+ self.name = name
+ self.dest = "%s/%s" % (self.path, self.name)
+
+ def set_security(self, val):
+ self.config.set_security_opts(val)
+
+ def add_network(self, val):
+ self.config.add_network_opts(val)
+
+ def get_security_dynamic(self):
+ return self.config.get_security_dynamic()
+
+ def get_security_label(self):
+ return self.config.get_security_label()
+
+ def set_security_label(self):
+ if selinux is None:
+ return
+
+ if self.image or self.get_security_dynamic():
+ return
+
+ selabel = self.get_security_label()
+ if selabel is None:
+ raise ValueError([_("Missing security label configuration")])
+ parts = selabel.split(":")
+ selinux.chcon(self.dest, "system_u:object_r:%s:%s" % (
+ self.get_file_type(), ":".join(parts[3:])), True)
+
+ def gen_filesystems(self):
+ if self.use_image:
+ self.image = self.DEFAULT_IMAGE % self.get_name()
+ mount = LibvirtSandbox.ConfigMountHostImage.new(self.image, self.dest)
+ self.config.add_mount(mount)
+
+ def fix_stat(self, f):
+ try:
+ s = os.stat(f)
+ path = "%s%s" % (self.dest, f)
+ os.chown(path, s.st_uid, s.st_gid)
+ os.chmod(path, s.st_mode)
+ except OSError, e:
+ if not e.errno == errno.ENOENT:
+ raise
+
+ def fix_protection(self):
+ l = len(self.dest)
+ for root, dirs, files in os.walk(self.dest):
+ for f in files:
+ dest = root + "/" + f
+ self.fix_stat(dest[l:])
+ for d in dirs:
+ dest = root + "/" + d
+ self.fix_stat(dest[l:])
+
+ def makedirs(self, d):
+ try:
+ path = "%s%s" % (self.dest, d)
+ os.makedirs(path)
+ except OSError, e:
+ if not e.errno == errno.EEXIST:
+ raise
+
+ def makefile(self, f):
+ self.makedirs(os.path.dirname(f))
+ try:
+ path = "%s%s" % (self.dest, f)
+ fd=open(path, "w")
+ fd.close()
+ except OSError, e:
+ if not e.errno == errno.EEXIST:
+ raise
+
+ def umount(self):
+ p = Popen(["/bin/umount", self.dest])
+ p.communicate()
+ if p.returncode and p.returncode != 0:
+ raise OSError(_("Failed to unmount image %s from %s") % (self.image, self.dest))
+
+ def create_image(self):
+ fd = open(self.image, "w")
+ fd.truncate(self.size)
+ fd.close()
+ p = Popen(["/sbin/mkfs","-F", "-t", "ext4", self.image],stdout=PIPE, stderr=PIPE)
+ p.communicate()
+ if p.returncode and p.returncode != 0:
+ raise OSError(_("Failed to build image %s") % self.image )
+
+ p = Popen(["/bin/mount", self.image, self.dest])
+ p.communicate()
+ if p.returncode and p.returncode != 0:
+ raise OSError(_("Failed to mount image %s on %s") % (self.image, self.dest))
+
+ def save_config(self):
+ self.connect()
+ context = self.context()
+ context.define()
+ sys.stdout.write(_("Created sandbox config %s\n") % get_config_path(self.name))
+
+ def delete(self):
+ self.connect()
+ self.conn.fetch_domains(None)
+ dom = self.conn.find_domain_by_name(self.name)
+ if dom is not None:
+ info = dom.get_info()
+ if info.state == LibvirtGObject.DomainState.RUNNING:
+ raise ValueError([_("Cannot delete running container")])
+
+ # Not sure we should remove content
+ if os.path.exists(self.dest):
+ shutil.rmtree(self.dest)
+
+ image = self.get_image_path()
+ if os.path.exists(image):
+ os.remove(image)
+
+ context = self.context()
+ context.undefine()
+
+ def get_security_model(self):
+ # XXX selinux is the default for the while, needs to be configurable someday
+ model = "selinux"
+ supported = False
+
+ # Make sure we have a connection
+ self.connect()
+
+ # Loop over the security models from the host capabilities
+ configCaps = self.conn.get_capabilities()
+ hostCaps = configCaps.get_host()
+ secmodels = hostCaps.get_secmodels()
+ for secmodel in secmodels:
+ if secmodel.get_model() == model:
+ supported = True
+ break
+
+ if not supported:
+ model = None
+ return model
+
+
+ def create(self):
+ self.connect()
+ if self.get_security_model() is not None and \
+ self.config.get_security_dynamic() and not self.use_image:
+ raise ValueError([_("Dynamic security label only supported for image based containers")])
+ if self.uri != "lxc:///":
+ self.config.set_shell(True)
+ if not os.path.exists(self.dest):
+ os.mkdir(self.dest)
+
+ def connect(self):
+ if not self.conn:
+ self.conn=LibvirtGObject.Connection.new(self.uri)
+ self.conn.open(None)
+
+ def disconnect(self):
+ if self.conn:
+ self.conn.close()
+ self.conn = None
+
+ def context(self):
+ return LibvirtSandbox.ContextService.new(self.conn, self.config)
+
+ def add_bind_mount(self, source, dest):
+ if self.image is None:
+ mount = LibvirtSandbox.ConfigMountHostBind.new(source, dest)
+ else:
+ mount = LibvirtSandbox.ConfigMountGuestBind.new(source, dest)
+ self.config.add_mount(mount)
+
+ def add_ram_mount(self, dest, size):
+ mount = LibvirtSandbox.ConfigMountRam.new(dest, size);
+ self.config.add_mount(mount)
+
+class GenericContainer(Container):
+ def __init__(self, name=None, uri = "lxc:///", path = Container.DEFAULT_PATH, config=None, create=False):
+ Container.__init__(self, name, uri, path, config, create)
+
+ if create:
+ self.config = LibvirtSandbox.ConfigServiceGeneric.new(name)
+
+ def gen_filesystems(self):
+ Container.gen_filesystems(self)
+ self.add_bind_mount(self.dest, self.path)
+ self.add_mounts()
+
+ def create_generic(self):
+ Container.create(self)
+ self.gen_filesystems()
+
+ if self.image:
+ self.create_image()
+ self.umount()
+ sys.stdout.write(_("Created sandbox container image %s\n") % self.image)
+ else:
+ sys.stdout.write(_("Created sandbox container dir %s\n") % self.dest)
+ self.save_config()
+
+ def create(self):
+ try:
+ self.create_generic()
+ except Exception, e:
+ try:
+ self.delete()
+ except Exception, e2:
+ pass
+ raise e
+
+ def set_command(self, command):
+ self.config.set_command(command)
+
+
+def is_template_unit(unit):
+ return '@' in unit
+
+class SystemdContainer(Container):
+ IGNORE_DIRS = [ "/var/run/", "/etc/logrotate.d/", "/etc/pam.d" ]
+ DEFAULT_DIRS = [ "/etc", "/var" ]
+ PROFILE_FILES = [ ".bashrc", ".bash_profile", ".profile" ]
+ MACHINE_ID = "/etc/machine-id"
+ HOSTNAME = "/etc/hostname"
+ SYSVINIT_PATH = "/etc/rc.d"
+ ANACONDA_WANTS_PATH = "/usr/lib/systemd/system/anaconda.target.wants"
+ MULTI_USER_WANTS_PATH = "/usr/lib/systemd/system/multi-user.target.wants"
+ SYSINIT_WANTS_PATH = "/usr/lib/systemd/system/sysinit.target.wants"
+ SOCKET_WANTS_PATH = "/usr/lib/systemd/system/sockets.target.wants"
+ MAKE_SYSTEM_DIRS = [ "/var/lib/dhclient", "/var/lib/dbus", "/var/log", "/var/spool", "/var/cache", "/var/tmp", "/var/lib/nfs/rpc_pipefs", SYSVINIT_PATH, "/lib/lsb" ]
+ BIND_SYSTEM_DIRS = [ "/var", "/home", "/root", "/etc/systemd/system", "/etc/rc.d", "/usr/lib/systemd/system/basic.target.wants", "/usr/lib/systemd/system/local-fs.target.wants", ANACONDA_WANTS_PATH, MULTI_USER_WANTS_PATH, SYSINIT_WANTS_PATH, SOCKET_WANTS_PATH ]
+ BIND_SYSTEM_FILES = [ MACHINE_ID, "/etc/fstab", HOSTNAME ]
+ LOCAL_LINK_FILES = { SYSINIT_WANTS_PATH : [ "systemd-tmpfiles-setup.service" ] , SOCKET_WANTS_PATH : [ "dbus.socket", "systemd-journald.socket", "systemd-shutdownd.socket", "systemd-initctl.socket" ] }
+
+ DEFAULT_UNIT = "/etc/systemd/system/%s_sandbox.service"
+
+ def __init__(self, name=None, uri = "lxc:///", path = Container.DEFAULT_PATH, config=None, create=False, packages=[]):
+ Container.__init__(self, name, uri, path, config, create)
+ self.copy = False
+ self.unit_file_list = []
+ self.packages = packages
+ if create:
+ self.config = LibvirtSandbox.ConfigServiceSystemd.new(name)
+ self.unitfile = None
+ else:
+ self.unitfile = self.get_unit_path()
+
+ def follow_units(self):
+ unitst=""
+ for i, src in self.unit_file_list:
+ unitst += "ReloadPropagatedFrom=%s\n" % i
+
+ return unitst
+
+ def get_unit_path(self, name = None):
+ if not name:
+ name = self.get_name()
+ return self.DEFAULT_UNIT % name
+
+ def set_unit_file_list(self, unit_file_list):
+ self.unit_file_list = unit_file_list
+
+ def get_sandboxed_service(self):
+ return self.unit_file_list[0][0].split(".")[0]
+
+ def create_system_unit(self):
+ self.unitfile = self.get_unit_path()
+ unit = r"""
+[Unit]
+Description=Secure Sandbox Container %(NAME)s
+Requires=libvirtd.service
+After=libvirtd.service
+%(FOLLOW)s
+[Service]
+Type=simple
+ExecStart=/usr/libexec/virt-sandbox-service-util -c %(URI)s -s %(NAME)s
+ExecReload=/usr/bin/virt-sandbox-service -c %(URI)s reload -u %(RELOAD)s %(NAME)s
+ExecStop=/usr/bin/virsh -c %(URI)s destroy %(NAME)s
+
+[Install]
+WantedBy=multi-user.target
+""" % { 'NAME':self.name,
+ 'FOLLOW':self.follow_units(),
+ 'RELOAD': " -u ".join(map(lambda x: x[0], self.unit_file_list)),
+ 'URI': self.uri,
+ }
+
+ fd = open(self.unitfile, "w")
+ fd.write(unit)
+ fd.close()
+ if selinux is not None:
+ selinux.restorecon(self.unitfile)
+ sys.stdout.write(_("Created unit file %s\n") % self.unitfile)
+
+ def add_dir(self, newd):
+ if newd in self.all_dirs:
+ return
+ for ignd in self.IGNORE_DIRS:
+ if newd.startswith(ignd):
+ return
+ for defd in self.DEFAULT_DIRS:
+ if newd.startswith(defd):
+ self.all_dirs.append(newd)
+ tmp_dirs = []
+ for d in self.dirs:
+ if newd.startswith(d):
+ return
+ if not d.startswith(newd):
+ tmp_dirs.append(d)
+ self.dirs = tmp_dirs
+ self.dirs.append(newd)
+ break;
+
+ def add_file(self, newf):
+ if newf in self.files:
+ return
+ for d in self.IGNORE_DIRS:
+ if newf.startswith(d):
+ return
+ for d in self.DEFAULT_DIRS:
+ if newf.startswith(d):
+ self.files.append(newf)
+ break;
+
+ def get_name(self):
+ if self.config:
+ return self.config.get_name()
+ raise ValueError([_("Name not configured")])
+
+ def set_copy(self, copy):
+ self.copy = copy
+
+ def get_security_dynamic(self):
+ return self.config.get_security_dynamic()
+
+ def extract_rpms(self):
+ self.all_dirs = []
+ self.dirs = []
+ self.files = []
+
+ self.ts = rpm.ts()
+
+ nb_packages = 0
+ for u, src in self.unit_file_list:
+ rpm_name = self.get_rpm_for_unit(src)
+ if rpm_name:
+ self.extract_rpm(rpm_name)
+ nb_packages += 1
+
+ for package in self.packages:
+ self.extract_rpm(package)
+ nb_packages += 1
+
+ if nb_packages == 0:
+ raise ValueError([_("Cannot autodetect the package for unit files, please use --package")])
+
+ def split_filename(self, filename):
+ if filename[-4:] == '.rpm':
+ filename = filename[:-4]
+
+ archIndex = filename.rfind('.')
+ arch = filename[archIndex+1:]
+
+ relIndex = filename[:archIndex].rfind('-')
+ rel = filename[relIndex+1:archIndex]
+
+ verIndex = filename[:relIndex].rfind('-')
+ ver = filename[verIndex+1:relIndex]
+
+ epochIndex = filename.find(':')
+ if epochIndex == -1:
+ epoch = ''
+ else:
+ epoch = filename[:epochIndex]
+
+ name = filename[epochIndex + 1:verIndex]
+ return name, ver, rel, epoch, arch
+
+ def get_rpm_for_unit(self, unitfile):
+ mi = self.ts.dbMatch(rpm.RPMTAG_BASENAMES, unitfile)
+ try:
+ h = mi.next();
+ except exceptions.StopIteration:
+ return None
+ return h['name']
+
+
+ def extract_rpm(self, rpm_name):
+ mi = self.ts.dbMatch('name', rpm_name)
+ try:
+ h = mi.next();
+ except exceptions.StopIteration:
+ raise ValueError([_("Cannot find package named %s") % rpm_name])
+
+ for fentry in h.fiFromHeader():
+ fname = fentry[0]
+
+ if os.path.isdir(fname):
+ self.add_dir(fname)
+ if os.path.isfile(fname):
+ self.add_file(fname)
+
+ srcrpm = h[rpm.RPMTAG_SOURCERPM]
+ srcrpmbits = self.split_filename(srcrpm)
+
+ if srcrpmbits[0] == h[rpm.RPMTAG_NAME]:
+ return
+
+ mi = self.ts.dbMatch(rpm.RPMTAG_NAME, srcrpmbits[0])
+ try:
+ h = mi.next();
+ except exceptions.StopIteration:
+ raise ValueError([_("Cannot find base package %s") % srcrpmbits[0]])
+
+ for fentry in h.fiFromHeader():
+ fname = fentry[0]
+
+ if os.path.isdir(fname):
+ self.add_dir(fname)
+ if os.path.isfile(fname):
+ self.add_file(fname)
+
+ def gen_hostname(self):
+ fd=open(self.dest + self.HOSTNAME, "w")
+ fd.write("%s\n" % self.name )
+ fd.close()
+
+ def gen_machine_id(self):
+ uuid_fd = open("/proc/sys/kernel/random/uuid")
+ uuid = uuid_fd.read().replace("-","").rstrip()
+ uuid_fd.close()
+ self.config.set_uuid(uuid)
+ fd=open(self.dest + self.MACHINE_ID, "w")
+ fd.write("%s\n" % uuid)
+ fd.close()
+
+ if not self.use_image:
+ # Link /var/log/journal within the container to /var/log/journal/UUID
+ # on host. This will allow the hosts journalctl to easily read
+ # containers journal information.
+ jdir = "/var/log/journal/"
+ jpath = jdir + uuid
+ if not os.path.exists(self.dest + jpath):
+ os.makedirs(self.dest + jpath)
+ if not os.path.exists(jdir):
+ os.makedirs(jdir)
+
+ os.symlink(self.dest + jpath, jpath)
+
+ def gen_filesystems(self):
+ Container.gen_filesystems(self)
+ # 10 MB /run
+ mount = LibvirtSandbox.ConfigMountRam.new("/run", 10 * 1024 * 1024);
+ self.config.add_mount(mount)
+
+ # 100 MB /tmp
+ mount = LibvirtSandbox.ConfigMountRam.new("/tmp", 100 * 1024 * 1024);
+ self.config.add_mount(mount)
+
+ # 100 MB /tmp
+ mount = LibvirtSandbox.ConfigMountRam.new("/dev/shm", 100 * 1024 * 1024);
+ self.config.add_mount(mount)
+
+ for d in self.BIND_SYSTEM_DIRS:
+ if os.path.exists(d):
+ source = "%s%s" % ( self.dest, d)
+ self.add_bind_mount(source, d)
+
+ for f in self.BIND_SYSTEM_FILES:
+ if os.path.exists(f):
+ source = "%s%s" % ( self.dest, f)
+ self.add_bind_mount(source, f)
+
+ for d in self.dirs:
+ found = False
+ # Dont add dirs whos parents are in SYSTEM_DIRS
+ for s in self.BIND_SYSTEM_DIRS:
+ if d.startswith(s):
+ found = True
+ break
+ if not found:
+ source = "%s%s" % ( self.dest, d)
+ self.add_bind_mount(source, d)
+ self.add_mounts()
+
+ def get_expanded_unit_template(self, unit):
+ return unit.replace('@', '@' + self.name)
+
+ def create_container_unit(self, src, dest, unit):
+ if is_template_unit(unit):
+ shutil.copy(src, dest + "/" + unit)
+ unit = self.get_expanded_unit_template(unit)
+ os.symlink(src, dest + "/" + unit)
+
+ dropin_dir = "%s/%s.d" % (dest, unit)
+ if not os.path.exists(dropin_dir):
+ os.mkdir(dropin_dir)
+
+ fd = open(dropin_dir + "/virt-sandbox.conf", "w")
+ fd.write("""; file placed here by virt-sandbox-service
+[Service]
+PrivateTmp=false
+PrivateNetwork=false
+""" )
+ fd.close()
+
+ def gen_content(self):
+ if self.copy:
+ for d in self.dirs:
+ copydirtree(d, "%s%s" % (self.dest, d))
+ for f in self.files:
+ self.makedirs(os.path.dirname(f))
+ shutil.copy(f, "%s%s" % (self.dest, f))
+ else:
+ for d in self.all_dirs:
+ self.makedirs(d)
+ for f in self.files:
+ self.makedirs(os.path.dirname(f))
+ self.makefile(f)
+
+ for d in self.BIND_SYSTEM_DIRS + self.MAKE_SYSTEM_DIRS:
+ self.makedirs(d)
+
+ for f in self.BIND_SYSTEM_FILES:
+ self.makefile(f)
+
+ destpath = self.dest + self.SYSVINIT_PATH
+ for i in range(7):
+ os.mkdir(destpath+("/rc%s.d" % i))
+
+ # Copy both /etc/rc.d/init.d/functions and /lib/lsb/init-functions, even
+ # though the latter is the one recommended
+ if os.path.exists(self.SYSVINIT_PATH + "/init.d/functions"):
+ os.mkdir(destpath+"/init.d")
+ shutil.copy(self.SYSVINIT_PATH + "/init.d/functions" , destpath + "/init.d")
+
+ if os.path.exists("/lib/lsb/init-functions"):
+ shutil.copy("/lib/lsb/init-functions" , self.dest + "/lib/lsb/")
+
+ self.gen_machine_id()
+ self.gen_hostname()
+
+ for k in self.LOCAL_LINK_FILES:
+ for d in self.LOCAL_LINK_FILES[k]:
+ src = "../%s" % ( d)
+ dest = "%s%s/%s" % ( self.dest, k, d)
+ os.symlink(src,dest)
+
+ unitdir = "/etc/systemd/system"
+ tgtdir = unitdir + "/multi-user.target.wants"
+
+ self.makedirs(unitdir)
+ self.makedirs(tgtdir)
+ os.symlink("/run", self.dest + "/var/run")
+
+ for i, src in self.unit_file_list:
+ self.create_container_unit(src, self.dest + unitdir, i)
+ if is_template_unit(i):
+ i = self.get_expanded_unit_template(i)
+ os.symlink(src, self.dest + tgtdir + "/" + i)
+
+ tgtfile = unitdir + "/multi-user.target"
+ try:
+ fd = open(self.dest + tgtfile, "w")
+ fd.write("[Unit]\n")
+ fd.write("Description=Sandbox multi-user target\n")
+ fd.close()
+ except OSError, e:
+ if not e.errno == errno.EEXIST:
+ raise
+
+ for p in self.PROFILE_FILES:
+ profile = "/etc/skel/" + p
+ if os.path.exists(profile):
+ shutil.copy(profile, self.dest + "/root/")
+
+ self.fix_protection()
+
+ def delete(self):
+ try:
+ uuid = self.config.get_uuid()
+ if uuid is not None:
+ jpath = "/var/log/journal/" + uuid
+ if os.path.lexists(jpath):
+ os.remove(jpath)
+ except Exception, e:
+ sys.stderr.write("%s: %s\n" % (sys.argv[0], e))
+ sys.stderr.flush()
+
+ Container.delete(self)
+
+ if self.unitfile and os.path.exists(self.unitfile):
+ p = Popen(["/usr/bin/systemctl","disable", self.unitfile],stdout=PIPE, stderr=PIPE)
+ p.communicate()
+ if p.returncode and p.returncode != 0:
+ raise OSError(_("Failed to disable %s unit file") % self.unitfile)
+ os.remove(self.unitfile)
+
+ def create_systemd(self):
+ self.extract_rpms()
+ Container.create(self)
+ self.gen_filesystems()
+ if self.image:
+ self.create_image()
+ self.gen_content()
+ self.umount()
+ sys.stdout.write(_("Created sandbox container image %s\n") % self.image)
+ else:
+ self.gen_content()
+ sys.stdout.write(_("Created sandbox container dir %s\n") % self.dest)
+ self.set_security_label()
+ self.create_system_unit()
+ self.config.set_boot_target("multi-user.target")
+ self.save_config()
+
+ def create(self):
+ if os.path.exists(self.dest):
+ raise OSError(_("%s already exists") % self.dest)
+
+ try:
+ self.create_systemd()
+ except Exception, e:
+ try:
+ self.delete()
+ except Exception, e2:
+ sys.stderr.write("Cleanup failed: %s\n" % str(e2))
+ raise
+
+ def reload(self, unitfiles):
+ class Args:
+ command = []
+ noseclabel = None
+ name = self.name
+ uri = self.uri
+ args = Args()
+ args.command = [ "systemctl", "reload" ] + map(lambda x: x[0], unitfiles)
+ execute(args)
+
+MB = int(1000000)
+
+def delete(args):
+ config = read_config(args.name)
+ if config is None:
+ sys.stderr.write("Sandbox '%s' does not exist\n" % args.name)
+ sys.exit(1)
+
+ if isinstance(config, gi.repository.LibvirtSandbox.ConfigServiceGeneric):
+ container = GenericContainer(uri=args.uri, config = config)
+ else:
+ container = SystemdContainer(uri=args.uri, config = config)
+ container.set_path(args.path)
+ container.delete()
+
+def create(args):
+ if len(args.command) > 0 and len(args.unitfiles) > 0:
+ raise ValueError([_("Commands cannot be specified with unit files")])
+
+ if len(args.command) == 0 and len(args.unitfiles) == 0:
+ raise ValueError([_("You must specify a command or a unit file")])
+
+ if args.packages and len(args.unitfiles) != 1:
+ raise ValueError([_("Option --package cannot be used without a unit file")])
+
+ if len(args.command) > 0:
+ container = GenericContainer(name = args.name, uri=args.uri, create = True)
+ container.set_command(args.command)
+ else:
+ container = SystemdContainer(name = args.name, uri=args.uri, create = True, packages = args.packages)
+ container.set_copy(args.copy)
+ container.set_unit_file_list(args.unitfiles)
+ for net in args.network:
+ container.add_network(net)
+ if args.security:
+ container.set_security(args.security)
+ container.set_uid(args.uid)
+ if not args.homedir:
+ args.homedir = pwd.getpwuid(args.uid).pw_dir
+ container.set_homedir(args.homedir)
+ if not args.username:
+ args.username = pwd.getpwuid(args.uid).pw_name
+ container.set_username(args.username)
+ if not args.gid:
+ args.gid = pwd.getpwuid(args.uid).pw_gid
+ container.set_gid(args.gid)
+ container.set_path(args.path)
+ container.set_file_type(args.file_type)
+ container.set_mounts(args.mounts)
+ if args.imagesize:
+ container.set_image(args.imagesize)
+
+ container.create()
+
+def usage(parser, msg):
+ parser.print_help()
+
+ sys.stderr.write("\n%s\n" % msg)
+ sys.stderr.flush()
+ sys.exit(1)
+
+def sandbox_reload(args):
+ config = read_config(args.name)
+ if isinstance(config, gi.repository.LibvirtSandbox.ConfigServiceGeneric):
+ raise ValueError([_("Generic Containers do not support reload")])
+ container = SystemdContainer(uri = args.uri, config = config)
+ container.reload(args.unitfiles)
+
+def connect(args):
+ if args.uri == "lxc:///":
+ class Args:
+ command = []
+ noseclabel = None
+ name = args.name
+ uri = args.uri
+
+ args = Args()
+ args.command = [ "/bin/sh" ]
+ execute(args)
+ return
+
+ print """\
+Connected to %s.
+Type 'Ctrl + ]' to detach from the console.
+""" % ( args.name )
+ os.execl("/usr/libexec/virt-sandbox-service-util",
+ "virt-sandbox-service-util",
+ "-c", args.uri,
+ "-a", args.name)
+
+#
+# Search Path for command to execute within the container.
+#
+def fullpath(cmd):
+ for i in [ "/", "./", "../" ]:
+ if cmd.startswith(i):
+ return cmd
+ for i in os.environ["PATH"].split(':'):
+ f = "%s/%s" % (i, cmd)
+ if os.access(f, os.X_OK):
+ return f
+ return cmd
+
+def execute(args):
+ if args.uri != "lxc:///":
+ raise ValueError([_("Can only execute commands inside of linux containers.")])
+
+ myexec = [ "virsh", "-c", args.uri, "lxc-enter-namespace" ]
+ if args.noseclabel:
+ myexec.append("--noseclabel")
+ myexec.extend([ args.name, "--", fullpath(args.command[0])] + args.command[1:])
+ os.execv("/usr/bin/virsh", myexec)
+
+def clone(args):
+ config = read_config(args.source)
+ if isinstance(config, gi.repository.LibvirtSandbox.ConfigServiceGeneric):
+ container = GenericContainer(uri=args.uri, config=config)
+ else:
+ container = SystemdContainer(uri=args.uri, config=config)
+ newcontainer = None
+
+ container.set_path(args.path)
+
+ old_path = container.get_filesystem_path()
+ new_path = container.get_filesystem_path(args.dest)
+
+ if os.path.exists(new_path):
+ raise OSError(_("%s already exists") % new_path)
+
+ try:
+ fd = open(container.get_config_path(),"r")
+ recs = fd.read()
+ fd.close()
+
+ newrec = recs.replace(old_path + "/", new_path + "/")
+ newrec = newrec.replace("name=" + args.source, "name=" + args.dest)
+ old_image_path = container.get_image_path()
+ if os.path.exists(old_image_path):
+ new_image_path = container.get_image_path(args.dest)
+ newrec = newrec.replace(old_image_path, new_image_path)
+ shutil.copy(old_image_path, new_image_path)
+ sys.stdout.write(_("Created sandbox container image %s\n") % new_image_path)
+ os.mkdir(new_path)
+ else:
+ copydirtree(old_path, new_path)
+ sys.stdout.write(_("Created sandbox container dir %s\n") % new_path)
+
+ if isinstance(config, gi.repository.LibvirtSandbox.ConfigServiceGeneric):
+ newcontainer = GenericContainer(name=args.dest, uri=args.uri, create=True)
+ newcontainer.set_path(args.path)
+ else:
+ fd = open(container.get_unit_path())
+ recs = fd.read()
+ fd.close()
+
+ new_unit = container.get_unit_path(args.dest)
+ fd = open(new_unit, "wx")
+ fd.write(recs.replace(args.source, args.dest))
+ fd.close()
+
+ sys.stdout.write(_("Created unit file %s\n") % new_unit)
+
+ config = LibvirtSandbox.Config.load_from_data(newrec)
+ newcontainer = SystemdContainer(config=config, uri=args.uri)
+ newcontainer.set_path(args.path)
+ newcontainer.gen_machine_id()
+ newcontainer.gen_hostname()
+
+ if args.security:
+ newcontainer.set_security(args.security)
+ newcontainer.set_security_label()
+ newcontainer.save_config()
+ except Exception, e:
+ if newcontainer is not None:
+ newcontainer.delete()
+ raise
+
+
+def upgrade_config_legacy(path):
+ config = LibvirtSandbox.Config.load_from_path(path)
+
+ if isinstance(config, gi.repository.LibvirtSandbox.ConfigServiceGeneric):
+ container = GenericContainer(uri=args.uri, config=config)
+ else:
+ container = SystemdContainer(uri=args.uri, config=config)
+
+ fd = open(container.get_unit_path())
+ unitfile = fd.read()
+ fd.close()
+
+ unitfile = unitfile.replace("/usr/bin/virt-sandbox-service start",
+ "/usr/libexec/virt-sandbox-service-util -c lxc:/// -s")
+ unitfile = unitfile.replace("/usr/bin/virt-sandbox-service reload",
+ "/usr/bin/virt-sandbox-service -c lxc:/// reload")
+ unitfile = unitfile.replace("/usr/bin/virt-sandbox-service stop",
+ "/usr/bin/virsh -c lxc:/// destroy")
+
+ unitfile = re.sub("WantedBy=.*\.target",
+ "WantedBy=multi-user.target",
+ unitfile)
+
+ os.remove(container.get_unit_path())
+ fd = open(container.get_unit_path(), "wx")
+ fd.write(unitfile)
+ fd.close()
+
+ sys.stdout.write(_("Created unit file %s\n") %
+ container.get_unit_path())
+
+ # Create new config file + libvirt persistent XML config
+ container.save_config()
+ # Kill legacy config file
+ os.remove(path)
+
+
+def upgrade_config(args):
+ configfile = get_legacy_config_path(args.name)
+ if os.path.exists(configfile):
+ upgrade_config_legacy(configfile)
+
+
+def upgrade_filesystem(args):
+ # This is where we'd look at RPM DB and upgrade the
+ # filesystem with new info for the unit files
+ pass
+
+# This function must be capable of reading configs created by
+# old releases and "fixing" them to work with the new release
+def upgrade(args):
+ upgrade_config(args)
+ upgrade_filesystem(args)
+
+import argparse
+class AddMount(argparse.Action):
+ def __call__(self, parser, namespace, values, option_string=None):
+ newval = getattr(namespace, self.dest)
+ if not newval:
+ newval = []
+ for v in values:
+ newval.append(v)
+ setattr(namespace, self.dest, newval)
+
+class SizeAction(argparse.Action):
+ def __call__(self, parser, namespace, values, option_string=None):
+ setattr(namespace, self.dest, int(values))
+
+class CheckUnit(argparse.Action):
+ def __call__(self, parser, namespace, value, option_string=None):
+ def check_unit(unit):
+ src = "/etc/systemd/system/" + unit
+ if os.path.exists(src):
+ return src
+ src = "/usr/lib/systemd/system/" + unit
+ if os.path.exists(src):
+ return src
+ return None
+ src = check_unit(value)
+ if not src:
+ src = check_unit(value + ".service")
+ if src:
+ value = value + ".service"
+ else:
+ raise OSError(_("Requested unit %s does not exist") % value)
+
+ unitfiles = getattr(namespace, self.dest)
+ if unitfiles:
+ unitfiles.append((value, src))
+ else:
+ unitfiles = [ (value, src) ]
+ setattr(namespace, self.dest, unitfiles)
+
+class SetNet(argparse.Action):
+ def __call__(self, parser, namespace, values, option_string=None):
+ nets = getattr(namespace, self.dest)
+ if nets:
+ nets.append(values)
+ else:
+ nets = [values]
+ setattr(namespace, self.dest, nets)
+
+class CheckPackage(argparse.Action):
+ def __call__(self, parser, namespace, value, option_string=None):
+ nb_rpm = len(rpm.TransactionSet().dbMatch('name', value))
+ if nb_rpm == 0:
+ raise OSError(_("Cannot find %s rpm") % value)
+ elif nb_rpm > 1:
+ raise OSError(_("%s rpm is installed more than once") % value)
+ packages = getattr(namespace, self.dest)
+ if packages:
+ packages.append(value)
+ else:
+ packages = [ value ]
+ setattr(namespace, self.dest, packages)
+
+def requires_name(parser):
+ parser.add_argument("name",
+ help=_("name of the sandbox container"))
+
+def default_security_opts():
+ if selinux is None:
+ return None
+
+ # XXX vary per URI for kvm/qemu/lxc.
+ # XXX generate a random category
+ return "static,label=system_u:system_r:svirt_lxc_net_t:s0"
+
+def gen_create_args(subparser):
+ parser = subparser.add_parser("create",
+ help=_("Create a sandbox container."))
+
+ parser.add_argument("-C", "--copy", default=False,
+ action="store_true",
+ help=_("copy content from the hosts /etc and /var directories that will be mounted within the sandbox"))
+
+ parser.add_argument("-f", "--filetype", dest="file_type",
+ default=c.get_file_type(),
+ help=_("SELinux file type to assign to content within the sandbox. Default: %s") % c.get_file_type())
+ parser.add_argument("--homedir", dest="homedir",
+ help=_("Specify the homedir for the container. Default: UID homedir."))
+ parser.add_argument("-G", "--gid", dest="gid",
+ default=None, type=int,
+ help=_("Specify the login gid for the container. Default: login GID of the UID."))
+ parser.add_argument("-i", "--imagesize", dest="imagesize", default = None,
+ action=SizeAction,
+ help=_("create image of this many megabytes."))
+ parser.add_argument("-m", "--mount", dest="mounts",default=[], nargs="*", action=AddMount,
+ help=_("Mount a filesytem in the sandbox"))
+ parser.add_argument("-N", "--network", dest="network",
+ action=SetNet, default=[],
+ help=_("Specify the network configuration"))
+ parser.add_argument("-p", "--path", dest="path", default=c.DEFAULT_PATH,
+ help=_("select path to store sandbox content. Default: %s") % c.DEFAULT_PATH)
+ parser.add_argument("-s", "--security", dest="security",
+ default=default_security_opts(),
+ help=_("Specify the security model configuration for the sandbox: Defaults to dynamic"))
+ parser.add_argument("-u", "--unitfile",
+ action=CheckUnit,
+ dest="unitfiles", default=[],
+ help=_("Systemd Unit file to run within the systemd sandbox container. Commands cannot be specified with unit files."))
+ parser.add_argument("-P", "--package",
+ action=CheckPackage,
+ dest="packages", default=[],
+ help=_("RPM package to be used in the container. Default: autodetected from unit files."))
+ parser.add_argument("--username", dest="username",
+ help=_("Specify the username for the container. Default: UID username."))
+ parser.add_argument("-U", "--uid", dest="uid",
+ default=os.getuid(),type=int,
+ help=_("Specify the uid for the container: Default to current UID."))
+
+ requires_name(parser)
+ parser.add_argument("command", default=[], nargs="*",
+ help=_("Command to run within the generic sandbox container. Commands cannot be specified with unit files."))
+
+ parser.set_defaults(func=create)
+
+def gen_connect_args(subparser):
+ parser = subparser.add_parser("connect",
+ help=_("Connect to a sandbox container"))
+ requires_name(parser)
+ parser.set_defaults(func=connect)
+
+def gen_execute_args(subparser):
+ parser = subparser.add_parser("execute",
+ help=_("Execute a command within a sandbox container. Only available for lxc:///"))
+ parser.add_argument("-N", "--noseclabel", dest="noseclabel",
+ default=False, action="store_true",
+ help=_("do not modify the label of the executable process. By default all commands execute with the label of the sandbox"))
+ requires_name(parser)
+ parser.add_argument("command", nargs="+",
+ help=_("command to execute within the container"))
+ parser.set_defaults(func=execute)
+
+def gen_reload_args(subparser):
+ parser = subparser.add_parser("reload",
+ help=_("Reload a running sandbox container"))
+ parser.add_argument("-u", "--unitfile", required=True,
+ action=CheckUnit, dest="unitfiles",
+ help=_("Systemd Unit file to reload within the sandbox container"))
+ requires_name(parser)
+ parser.set_defaults(func=sandbox_reload)
+
+def gen_clone_args(subparser):
+ parser = subparser.add_parser("clone",
+ help=_("Clone an existing sandbox container"))
+ parser.set_defaults(func=clone)
+ parser.add_argument("-p", "--path", dest="path", default=c.DEFAULT_PATH,
+ help=_("select path to copy sandbox content from/to. Default: %s") % c.DEFAULT_PATH)
+ parser.add_argument("-s", "--security", dest="security",
+ default=default_security_opts(),
+ help=_("Specify the security model configuration for the sandbox: Defaults to dynamic"))
+
+ parser.add_argument("source",
+ help=_("source sandbox container name"))
+ parser.add_argument("dest",
+ help=_("dest name of the new sandbox container"))
+
+def gen_delete_args(subparser):
+ parser = subparser.add_parser("delete",
+ help=_("Delete a sandbox container"))
+ parser.add_argument("-p", "--path", dest="path", default=c.DEFAULT_PATH,
+ help=_("select path to delete sandbox content from. Default: %s") % c.DEFAULT_PATH)
+ requires_name(parser)
+ parser.set_defaults(func=delete)
+
+def gen_upgrade_args(subparser):
+ parser = subparser.add_parser("upgrade",
+ help=_("Upgrade the sandbox container"))
+ requires_name(parser)
+ parser.set_defaults(func=upgrade)
+
+if __name__ == '__main__':
+ c = Container()
+
+ parser = argparse.ArgumentParser(description='Sandbox Container Tool')
+ parser.add_argument("-c", "--connect", required=False, dest="uri", default="lxc:///",
+ help=_("libvirt connection URI to use (lxc:/// [default] or qemu:///session)"))
+
+ subparser = parser.add_subparsers(help=_("commands"))
+ gen_create_args(subparser)
+ gen_clone_args(subparser)
+ gen_connect_args(subparser)
+ gen_delete_args(subparser)
+ gen_execute_args(subparser)
+ gen_reload_args(subparser)
+ gen_upgrade_args(subparser)
+
+ try:
+ args = parser.parse_args()
+ if args.uri[0:3] != "lxc":
+ sys.stderr.write("%s: only lxc:/// URIs are currently supported\n" % sys.argv[0])
+ sys.exit(1)
+ args.func(args)
+ sys.exit(0)
+ except KeyboardInterrupt, e:
+ sys.exit(0)
+ except ValueError, e:
+ for line in e:
+ for l in line:
+ sys.stderr.write("%s: %s\n" % (sys.argv[0], l))
+ sys.stderr.flush()
+ sys.exit(1)
+ except IOError, e:
+ sys.stderr.write("%s: %s: %s\n" % (sys.argv[0], e.filename, e.strerror))
+ sys.stderr.flush()
+ sys.exit(1)
+ except OSError, e:
+ sys.stderr.write("%s: %s\n" % (sys.argv[0], e))
+ sys.stderr.flush()
+ sys.exit(1)
+ except GLib.GError, e:
+ sys.stderr.write("%s: %s\n" % (sys.argv[0], e))
+ sys.stderr.flush()
+ sys.exit(1)