Optimization in case the context is already running.
[util-vserver-pl.git] / python / vserver.py
index 7937022..7652b78 100644 (file)
@@ -137,7 +137,6 @@ class VServer:
     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)):
@@ -163,55 +162,30 @@ class VServer:
             except:
                 print '%s: (%s failed to open) %s'%(time.asctime(time.gmtime()),self.logfile,msg)
 
-    def have_limits_changed(self):
-        return self.rlimits_changed
-
-    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(): self.log("Unexpected error with getrlimit for running context %d" % self.ctx)
-
-        resource_type = RLIMITS[type]
-        try:
-            ret = vserverimpl.setrlimit(self.ctx,resource_type,hard,soft,minimum)
-        except OSError, e:
-            if self.is_running(): self.log("Unexpected error with setrlimit for running context %d" % self.ctx)
+        update = False
 
-    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:
-            self.log("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))
@@ -293,7 +267,6 @@ class VServer:
             block_usage = self.disk_blocks
             inode_usage = self.disk_inodes
 
-
         try:
             vserverimpl.setdlimit(self.dir,
                                   self.ctx,
@@ -335,7 +308,7 @@ class VServer:
         if cpu_share == 0:
             self.config.unset('sched/idle-time')
         
-        if self.vm_running:
+        if self.is_running():
             self.set_sched(cpu_min, cpu_share)
 
     def set_sched(self, cpu_min, cpu_share):
@@ -422,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()
 
@@ -458,25 +435,22 @@ class VServer:
                 # 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:
-                         print >>log, traceback.format_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:
                 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,
@@ -485,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,
@@ -507,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):
@@ -527,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
+