Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12:GA
virt-sandbox
no-libexec.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
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)
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor