# Copyright 2005 Princeton University
-#$Id: vserver.py,v 1.59 2007/07/17 17:56:04 faiyaza Exp $
+#$Id: vserver.py,v 1.72 2007/08/02 16:01:59 dhozac Exp $
import errno
import fcntl
import sys
import time
import traceback
+import subprocess
+import resource
-import mountimpl
-import runcmd
-import utmp
import vserverimpl
import cpulimit, bwlimit
from vserverimpl import VS_SCHED_CPU_GUARANTEED as SCHED_CPU_GUARANTEED
from vserverimpl import DLIMIT_INF
from vserverimpl import VC_LIM_KEEP
-
-from vserverimpl import RLIMIT_CPU
-from vserverimpl import RLIMIT_RSS
-from vserverimpl import RLIMIT_NPROC
-from vserverimpl import RLIMIT_NOFILE
-from vserverimpl import RLIMIT_MEMLOCK
-from vserverimpl import RLIMIT_AS
-from vserverimpl import RLIMIT_LOCKS
-from vserverimpl import RLIMIT_SIGPENDING
-from vserverimpl import RLIMIT_MSGQUEUE
from vserverimpl import VLIMIT_NSOCK
from vserverimpl import VLIMIT_OPENFD
from vserverimpl import VLIMIT_ANON
FLAGS_ULIMIT = 64
FLAGS_NAMESPACE = 128
-RLIMITS = {"CPU": RLIMIT_CPU,
- "RSS": RLIMIT_RSS,
- "NPROC": RLIMIT_NPROC,
- "NOFILE": RLIMIT_NOFILE,
- "MEMLOCK": RLIMIT_MEMLOCK,
- "AS": RLIMIT_AS,
- "LOCKS": RLIMIT_LOCKS,
- "SIGPENDING": RLIMIT_SIGPENDING,
- "MSGQUEUE": RLIMIT_MSGQUEUE,
- "NSOCK": VLIMIT_NSOCK,
- "OPENFD": VLIMIT_OPENFD,
- "ANON": VLIMIT_ANON,
- "SHMEM": VLIMIT_SHMEM}
+RLIMITS = { "NSOCK": VLIMIT_NSOCK,
+ "OPENFD": VLIMIT_OPENFD,
+ "ANON": VLIMIT_ANON,
+ "SHMEM": VLIMIT_SHMEM}
+
+# add in the platform supported rlimits
+for entry in resource.__dict__.keys():
+ if entry.find("RLIMIT_")==0:
+ k = entry[len("RLIMIT_"):]
+ if not RLIMITS.has_key(k):
+ RLIMITS[k]=resource.__dict__[entry]
+ else:
+ print "WARNING: duplicate RLIMITS key %s" % k
class NoSuchVServer(Exception): pass
def __init__(self, name, directory):
self.name = name
self.dir = directory
+ self.cache = None
+ if not (os.path.isdir(self.dir) and
+ os.access(self.dir, os.R_OK | os.W_OK | os.X_OK)):
+ raise NoSuchVServer, "%s does not exist" % self.dir
def get(self, option, default = None):
try:
- f = open(os.path.join(self.dir, option), "r")
- buf = f.readline().rstrip()
- f.close()
- return buf
- except IOError, e:
+ if self.cache:
+ return self.cache[option]
+ else:
+ f = open(os.path.join(self.dir, option), "r")
+ buf = f.read().rstrip()
+ f.close()
+ return buf
+ except:
if default is not None:
return default
else:
raise KeyError, "Key %s is not set for %s" % (option, self.name)
def update(self, option, value):
+ if self.cache:
+ return
+
try:
old_umask = os.umask(0022)
filename = os.path.join(self.dir, option)
f.write("%s\n" % value)
f.close()
os.umask(old_umask)
- except KeyError, e:
- raise KeyError, "Don't know how to handle %s, sorry" % option
+ except:
+ raise
+
+ def unset(self, option):
+ if self.cache:
+ return
+
+ try:
+ filename = os.path.join(self.dir, option)
+ os.unlink(filename)
+ try:
+ os.removedirs(os.path.dirname(filename))
+ except:
+ pass
+ return True
+ except:
+ return False
+
+ def cache_it(self):
+ self.cache = {}
+ def add_to_cache(cache, dirname, fnames):
+ for file in fnames:
+ full_name = os.path.join(dirname, file)
+ if os.path.islink(full_name):
+ fnames.remove(file)
+ elif (os.path.isfile(full_name) and
+ os.access(full_name, os.R_OK)):
+ f = open(full_name, "r")
+ cache[full_name.replace(os.path.join(self.dir, ''),
+ '')] = f.read().rstrip()
+ f.close()
+ os.path.walk(self.dir, add_to_cache, self.cache)
class VServer:
minimum = int(self.config.get("rlimits/%s.min"%type.lower(),VC_LIM_KEEP))
return (hard,soft,minimum)
- def set_WHITELISTED_config(self,whitelisted):
- self.config.update('whitelisted', whitelisted)
-
def set_capabilities(self, capabilities):
return vserverimpl.setbcaps(self.ctx, vserverimpl.text2bcaps(capabilities))
def set_capabilities_config(self, capabilities):
self.config.update('bcapabilities', capabilities)
- self.set_capabilities(capabilities)
+ self.set_capabilities(capabilities)
def get_capabilities(self):
return vserverimpl.bcaps2text(vserverimpl.getbcaps(self.ctx))
def get_capabilities_config(self):
return self.config.get('bcapabilities', '')
- def __do_chroot(self):
+ def set_ipaddresses(self, addresses):
+ vserverimpl.netremove(self.ctx, "all")
+ for a in addresses.split(","):
+ vserverimpl.netadd(self.ctx, a)
+
+ def set_ipaddresses_config(self, addresses):
+ i = 0
+ for a in addresses.split(","):
+ self.config.update("interfaces/%d/ip" % i, a)
+ i += 1
+ while self.config.unset("interfaces/%d/ip" % i):
+ i += 1
+ self.set_ipaddresses(addresses)
+
+ def get_ipaddresses_config(self):
+ i = 0
+ ret = []
+ while True:
+ r = self.config.get("interfaces/%d/ip" % i, '')
+ if r == '':
+ break
+ ret += [r]
+ i += 1
+ return ",".join(ret)
+
+ def get_ipaddresses(self):
+ # No clean way to do this right now.
+ return None
+ def __do_chroot(self):
+ self.config.cache_it()
os.chroot(self.dir)
os.chdir("/")
([], filter_fn))[0]
garbage += filter(os.path.isfile, map((LOCKDIR + "/").__add__,
os.listdir(LOCKDIR)))
- for f in garbage:
- os.unlink(f)
+ if False:
+ for f in garbage:
+ os.unlink(f)
# set the initial runlevel
f = open(RUNDIR + "/utmp", "w")
- utmp.set_runlevel(f, runlevel)
+ vserverimpl.setrunlevel(f, runlevel)
f.close()
# mount /proc and /dev/pts
- self.__do_mount("none", "/proc", "proc")
+ self.__do_mount("none", self.dir, "/proc", "proc")
# XXX - magic mount options
- self.__do_mount("none", "/dev/pts", "devpts", 0, "gid=5,mode=0620")
+ self.__do_mount("none", self.dir, "/dev/pts", "devpts", 0, "gid=5,mode=0620")
def __do_mount(self, *mount_args):
try:
- mountimpl.mount(*mount_args)
+ vserverimpl.mount(*mount_args)
except OSError, ex:
if ex.errno == errno.EBUSY:
# assume already mounted
state_file = open("/var/run/vservers/%s" % self.name, "w")
# use /dev/null for stdin, /var/log/boot.log for stdout/err
- os.close(0)
- os.close(1)
- os.open("/dev/null", os.O_RDONLY)
+ fd = os.open("/dev/null", os.O_RDONLY)
+ if fd != 0:
+ os.dup2(fd, 0)
+ os.close(fd)
self.__do_chroot()
log = open("/var/log/boot.log", "w", 0)
+ if log.fileno() != 1:
+ os.dup2(log.fileno(), 1)
os.dup2(1, 2)
print >>log, ("%s: starting the virtual server %s" %
# execute each init script in turn
# XXX - we don't support all scripts that vserver script does
self.__do_chcontext(state_file)
- for cmd in self.INITSCRIPTS + [None]:
- try:
- # enter vserver context
- arg_subst = { 'runlevel': runlevel }
- cmd_args = [cmd[0]] + map(lambda x: x % arg_subst,
- cmd[1:])
- print >>log, "executing '%s'" % " ".join(cmd_args)
- os.spawnvp(os.P_WAIT,cmd[0],*cmd_args)
- except:
- traceback.print_exc()
- os._exit(1)
+ for cmd in self.INITSCRIPTS:
+ try:
+ # enter vserver context
+ arg_subst = { 'runlevel': runlevel }
+ cmd_args = [cmd[0]] + map(lambda x: x % arg_subst,
+ cmd[1:])
+ print >>log, "executing '%s'" % " ".join(cmd_args)
+ os.spawnvp(os.P_NOWAIT,cmd[0],cmd_args)
+ except:
+ traceback.print_exc()
+ os._exit(1)
# we get here due to an exception in the top-level child process
except Exception, ex:
def init_disk_info(self):
cmd = "/usr/sbin/vdu --script --space --inodes --blocksize 1024 --xid %d %s" % (self.ctx, self.dir)
- (child_stdin, child_stdout, child_stderr) = os.popen3(cmd)
- child_stdin.close()
- line = child_stdout.readline()
+ p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ close_fds=True)
+ p.stdin.close()
+ line = p.stdout.readline()
if not line:
- sys.stderr.write(child_stderr.readline())
- child_stdout.close()
- child_stderr.close()
+ sys.stderr.write(p.stderr.read())
+ p.stdout.close()
+ p.stderr.close()
+ ret = p.wait()
+
(space, inodes) = line.split()
self.disk_inodes = int(inodes)
self.disk_blocks = int(space)
def create(vm_name, static = False, ctor = VServer):
- options = []
+ options = ['vuseradd']
if static:
options += ['--static']
- runcmd.run('vuseradd', options + [vm_name])
+ ret = os.spawnvp(os.P_WAIT, 'vuseradd', options + [vm_name])
+ if not os.WIFEXITED(ret) or os.WEXITSTATUS(ret) != 0:
+ out = "system command ('%s') " % options
+ if os.WIFEXITED(ret):
+ out += "failed, rc = %d" % os.WEXITSTATUS(ret)
+ else:
+ out += "killed by signal %d" % os.WTERMSIG(ret)
+ raise SystemError, out
vm_id = pwd.getpwnam(vm_name)[2]
return ctor(vm_name, vm_id)