X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=python%2Fvserver.py;h=7652b784c5b1d99c06db7d7c3839958a73345c32;hb=5d32782d7fd7141309464b18678ee16b92af6686;hp=590ec1b9a18e1051dc881c70795c81bd0b6f45b8;hpb=a24a238a20efb2b58e8f90ee21932622c2ce64ab;p=util-vserver-pl.git diff --git a/python/vserver.py b/python/vserver.py index 590ec1b..7652b78 100644 --- a/python/vserver.py +++ b/python/vserver.py @@ -17,7 +17,6 @@ import resource 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 VLIMIT_NSOCK @@ -135,10 +134,9 @@ class VServer: INITSCRIPTS = [('/etc/rc.vinit', 'start'), ('/etc/rc.d/rc', '%(runlevel)d')] - def __init__(self, name, vm_id = None, vm_running = None): + def __init__(self, name, vm_id = None, vm_running = None, logfile=None): self.name = name - self.rlimits_changed = False self.dir = "%s/%s" % (vserverimpl.VSERVER_BASEDIR, name) if not (os.path.isdir(self.dir) and os.access(self.dir, os.R_OK | os.W_OK | os.X_OK)): @@ -151,56 +149,43 @@ class VServer: if vm_running == None: vm_running = self.is_running() self.vm_running = vm_running + self.logfile = logfile - def have_limits_changed(self): - return self.rlimits_changed + # inspired from nodemanager's logger + def log(self,msg): + if self.logfile: + try: + fd = os.open(self.logfile,os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0600) + if not msg.endswith('\n'): msg += '\n' + os.write(fd, '%s: %s' % (time.asctime(time.gmtime()), msg)) + os.close(fd) + except: + print '%s: (%s failed to open) %s'%(time.asctime(time.gmtime()),self.logfile,msg) - def set_rlimit_limit(self,type,hard,soft,minimum): + def set_rlimit(self, type, hard, soft, min): """Generic set resource limit function for vserver""" global RLIMITS - changed = False - try: - old_hard, old_soft, old_minimum = self.get_rlimit_limit(type) - if old_hard != VC_LIM_KEEP and old_hard <> hard: changed = True - if old_soft != VC_LIM_KEEP and old_soft <> soft: changed = True - if old_minimum != VC_LIM_KEEP and old_minimum <> minimum: changed = True - self.rlimits_changed = self.rlimits_changed or changed - except OSError, e: - if self.is_running(): print "Unexpected error with getrlimit for running context %d" % self.ctx + update = False - resource_type = RLIMITS[type] - try: - ret = vserverimpl.setrlimit(self.ctx,resource_type,hard,soft,minimum) - except OSError, e: - if self.is_running(): print "Unexpected error with setrlimit for running context %d" % self.ctx - - def set_rlimit_config(self,type,hard,soft,minimum): - """Generic set resource limit function for vserver""" if hard <> VC_LIM_KEEP: self.config.update('rlimits/%s.hard' % type.lower(), hard) + update = True if soft <> VC_LIM_KEEP: self.config.update('rlimits/%s.soft' % type.lower(), soft) - if minimum <> VC_LIM_KEEP: - self.config.update('rlimits/%s.min' % type.lower(), minimum) - self.set_rlimit_limit(type,hard,soft,minimum) + update = True + if min <> VC_LIM_KEEP: + self.config.update('rlimits/%s.min' % type.lower(), min) + update = True - def get_rlimit_limit(self,type): - """Generic get resource configuration function for vserver""" - global RLIMITS - resource_type = RLIMITS[type] - try: - ret = vserverimpl.getrlimit(self.ctx,resource_type) - except OSError, e: - print "Unexpected error with getrlimit for context %d" % self.ctx - ret = self.get_rlimit_config(type) - return ret + if self.is_running() and update: + resource_type = RLIMITS[type] + try: + vserverimpl.setrlimit(self.ctx, resource_type, hard, soft, min) + except OSError, e: + self.log("Error: setrlimit(%d, %s, %d, %d, %d): %s" + % (self.ctx, type.lower(), hard, soft, min)) - def get_rlimit_config(self,type): - """Generic get resource configuration function for vserver""" - hard = int(self.config.get("rlimits/%s.hard"%type.lower(),VC_LIM_KEEP)) - soft = int(self.config.get("rlimits/%s.soft"%type.lower(),VC_LIM_KEEP)) - minimum = int(self.config.get("rlimits/%s.min"%type.lower(),VC_LIM_KEEP)) - return (hard,soft,minimum) + return update def set_capabilities(self, capabilities): return vserverimpl.setbcaps(self.ctx, vserverimpl.text2bcaps(capabilities)) @@ -271,7 +256,7 @@ class VServer: try: vserverimpl.unsetdlimit(self.dir, self.ctx) except OSError, e: - print "Unexpected error with unsetdlimit for context %d" % self.ctx + self.log("Unexpected error with unsetdlimit for context %d" % self.ctx) return if self.vm_running: @@ -282,7 +267,6 @@ class VServer: block_usage = self.disk_blocks inode_usage = self.disk_inodes - try: vserverimpl.setdlimit(self.dir, self.ctx, @@ -292,7 +276,7 @@ class VServer: vserverimpl.DLIMIT_INF, # inode limit 2) # %age reserved for root except OSError, e: - print "Unexpected error with setdlimit for context %d" % self.ctx + self.log("Unexpected error with setdlimit for context %d" % self.ctx) self.config.update('dlimits/0/space_total', block_limit) @@ -313,25 +297,23 @@ class VServer: return block_limit - def set_sched_config(self, cpu_share, sched_flags): + def set_sched_config(self, cpu_min, cpu_share): """ Write current CPU scheduler parameters to the vserver configuration file. This method does not modify the kernel CPU scheduling parameters for this context. """ - if sched_flags & SCHED_CPU_GUARANTEED: - cpu_guaranteed = cpu_share - else: - cpu_guaranteed = 0 + self.config.update('sched/fill-rate', cpu_min) self.config.update('sched/fill-rate2', cpu_share) - self.config.update('sched/fill-rate', cpu_guaranteed) - - if self.vm_running: - self.set_sched(cpu_share, sched_flags) + if cpu_share == 0: + self.config.unset('sched/idle-time') + + if self.is_running(): + self.set_sched(cpu_min, cpu_share) - def set_sched(self, cpu_share, sched_flags = 0): + def set_sched(self, cpu_min, cpu_share): """ Update kernel CPU scheduling parameters for this context. """ - vserverimpl.setsched(self.ctx, cpu_share, sched_flags) + vserverimpl.setsched(self.ctx, cpu_min, cpu_share) def get_sched(self): # have no way of querying scheduler right now on a per vserver basis @@ -369,7 +351,7 @@ class VServer: self.set_resources() vserverimpl.setup_done(self.ctx) - def __prep(self, runlevel, log): + def __prep(self, runlevel): """ Perform all the crap that the vserver script does before actually executing the startup scripts. """ @@ -413,14 +395,18 @@ class VServer: self.__do_chroot() self.__do_chcontext(None) - def start(self, wait, runlevel = 3): - self.vm_running = True - self.rlimits_changed = False + def start(self, runlevel = 3): - child_pid = os.fork() - if child_pid == 0: + if (os.fork() != 0): + # Parent should just return. + self.vm_running = True + return + else: # child process try: + # so we don't chcontext with priv'ed fds + close_nonstandard_fds() + # get a new session os.setsid() @@ -432,42 +418,39 @@ class VServer: if fd != 0: os.dup2(fd, 0) os.close(fd) + + # perform pre-init cleanup + self.__prep(runlevel) + self.config.cache_it() self.__do_chroot() - log = open("/var/log/boot.log", "w", 0) + log = open("/var/log/boot.log", "a", 0) if log.fileno() != 1: os.dup2(log.fileno(), 1) os.dup2(1, 2) print >>log, ("%s: starting the virtual server %s" % (time.asctime(time.gmtime()), self.name)) - - # perform pre-init cleanup - self.__prep(runlevel, log) - # 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: - try: - # enter vserver context - arg_subst = { 'runlevel': runlevel } - cmd_args = [cmd[0]] + map(lambda x: x % arg_subst, + 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) + if os.path.isfile(cmd[0]): + print >>log, "executing '%s'" % " ".join(cmd_args) + os.spawnvp(os.P_NOWAIT,cmd[0],cmd_args) + except: + print >>log, traceback.format_exc() # we get here due to an exception in the top-level child process except Exception, ex: - traceback.print_exc() + self.log(traceback.format_exc()) os._exit(0) - # parent process - return child_pid - def set_resources(self): """ Called when vserver context is entered for first time, @@ -476,6 +459,13 @@ class VServer: pass def init_disk_info(self): + try: + dlimit = vserver.getdlimit(self.dir, self.ctx) + self.disk_blocks = dlimit[0] + self.disk_inodes = dlimit[2] + return self.disk_blocks * 1024 + except Exception, e: + pass cmd = "/usr/sbin/vdu --script --space --inodes --blocksize 1024 --xid %d %s" % (self.ctx, self.dir) p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -498,8 +488,14 @@ class VServer: def stop(self, signal = signal.SIGKILL): vserverimpl.killall(self.ctx, signal) self.vm_running = False - self.rlimits_changed = False + def setname(self, slice_id): + '''Set vcVHI_CONTEXT field in kernel to slice_id''' + vserverimpl.setname(self.ctx, slice_id) + + def getname(self): + '''Get vcVHI_CONTEXT field in kernel''' + return vserverimpl.getname(self.ctx) def create(vm_name, static = False, ctor = VServer): @@ -518,3 +514,12 @@ def create(vm_name, static = False, ctor = VServer): vm_id = pwd.getpwnam(vm_name)[2] return ctor(vm_name, vm_id) + + +def close_nonstandard_fds(): + """Close all open file descriptors other than 0, 1, and 2.""" + _SC_OPEN_MAX = 4 + for fd in range(3, os.sysconf(_SC_OPEN_MAX)): + try: os.close(fd) + except OSError: pass # most likely an fd that isn't open +