added in a 'setup' boolean argument to indicate that set_resources is being called...
[util-vserver-pl.git] / python / vserver.py
index 39275de..577a44a 100644 (file)
@@ -137,7 +137,6 @@ class VServer:
     def __init__(self, name, vm_id = None, vm_running = None, logfile=None):
 
         self.name = name
     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)):
         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,68 +162,64 @@ class VServer:
             except:
                 print '%s: (%s failed to open) %s'%(time.asctime(time.gmtime()),self.logfile,msg)
 
             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
         """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)
         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 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))
+    def get_prefix_from_capabilities(self, capabilities, prefix):
+        split_caps = capabilities.split(',')
+        return ",".join(["%s" % (c) for c in split_caps if c.startswith(prefix.upper()) or c.startswith(prefix.lower())])
+
+    def get_bcaps_from_capabilities(self, capabilities):
+        return self.get_prefix_from_capabilities(capabilities, "cap_")
+
+    def get_ccaps_from_capabilities(self, capabilities):
+        return self.get_prefix_from_capabilities(capabilities, "vxc_")
 
     def set_capabilities_config(self, capabilities):
 
     def set_capabilities_config(self, capabilities):
-        self.config.update('bcapabilities', capabilities)
-        self.set_capabilities(capabilities)
+        bcaps = self.get_bcaps_from_capabilities(capabilities)
+        ccaps = self.get_ccaps_from_capabilities(capabilities)
+        self.config.update('bcapabilities', bcaps)
+        self.config.update('ccapabilities', ccaps)
+        ret = vserverimpl.setbcaps(self.ctx, vserverimpl.text2bcaps(bcaps))
+        if ret > 0:
+            return ret
+        return vserverimpl.setccaps(self.ctx, vserverimpl.text2ccaps(ccaps))
 
     def get_capabilities(self):
 
     def get_capabilities(self):
-        return vserverimpl.bcaps2text(vserverimpl.getbcaps(self.ctx))
+        bcaps = vserverimpl.bcaps2text(vserverimpl.getbcaps(self.ctx))
+        ccaps = vserverimpl.ccaps2text(vserverimpl.getccaps(self.ctx))
+        if bcaps and ccaps:
+            ccaps = "," + ccaps
+        return (bcaps + ccaps)
  
     def get_capabilities_config(self):
  
     def get_capabilities_config(self):
-        return self.config.get('bcapabilities', '')
+        bcaps = self.config.get('bcapabilities', '')
+        ccaps = self.config.get('ccapabilities', '')
+        if bcaps and ccaps:
+            ccaps = "," + ccaps
+        return (bcaps + ccaps)
 
     def set_ipaddresses(self, addresses):
         vserverimpl.netremove(self.ctx, "all")
 
     def set_ipaddresses(self, addresses):
         vserverimpl.netremove(self.ctx, "all")
@@ -293,7 +288,6 @@ class VServer:
             block_usage = self.disk_blocks
             inode_usage = self.disk_inodes
 
             block_usage = self.disk_blocks
             inode_usage = self.disk_inodes
 
-
         try:
             vserverimpl.setdlimit(self.dir,
                                   self.ctx,
         try:
             vserverimpl.setdlimit(self.dir,
                                   self.ctx,
@@ -375,7 +369,7 @@ class VServer:
             state_file.close()
 
         if vserverimpl.chcontext(self.ctx, vserverimpl.text2bcaps(self.get_capabilities_config())):
             state_file.close()
 
         if vserverimpl.chcontext(self.ctx, vserverimpl.text2bcaps(self.get_capabilities_config())):
-            self.set_resources()
+            self.set_resources(True)
             vserverimpl.setup_done(self.ctx)
 
     def __prep(self, runlevel):
             vserverimpl.setup_done(self.ctx)
 
     def __prep(self, runlevel):
@@ -422,14 +416,18 @@ class VServer:
         self.__do_chroot()
         self.__do_chcontext(None)
 
         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:
             # child process
             try:
+                # so we don't chcontext with priv'ed fds
+                close_nonstandard_fds()
+
                 # get a new session
                 os.setsid()
 
                 # get a new session
                 os.setsid()
 
@@ -458,26 +456,23 @@ class VServer:
                 # XXX - we don't support all scripts that vserver script does
                 self.__do_chcontext(state_file)
                 for cmd in self.INITSCRIPTS:
                 # 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:])
                                                    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)
 
 
             # 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):
+    def set_resources(self,setup=False):
 
         """ Called when vserver context is entered for first time,
         should be overridden by subclass. """
 
         """ Called when vserver context is entered for first time,
         should be overridden by subclass. """
@@ -485,6 +480,13 @@ class VServer:
         pass
 
     def init_disk_info(self):
         pass
 
     def init_disk_info(self):
+        try:
+            dlimit = vserverimpl.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,
         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 +509,14 @@ class VServer:
     def stop(self, signal = signal.SIGKILL):
         vserverimpl.killall(self.ctx, signal)
         self.vm_running = False
     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):
 
 
 def create(vm_name, static = False, ctor = VServer):
@@ -527,3 +535,12 @@ def create(vm_name, static = False, ctor = VServer):
     vm_id = pwd.getpwnam(vm_name)[2]
 
     return ctor(vm_name, vm_id)
     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
+