simpler yet, no need to trigger rc at all now that we use vserver .. start
[util-vserver-pl.git] / python / vserver.py
index 8aa410e..315edbb 100644 (file)
@@ -12,6 +12,7 @@ import sys
 import time
 import traceback
 import subprocess
 import time
 import traceback
 import subprocess
+import commands
 import resource
 
 import vserverimpl
 import resource
 
 import vserverimpl
@@ -41,6 +42,8 @@ RLIMITS = { "NSOCK": VLIMIT_NSOCK,
             "ANON": VLIMIT_ANON,
             "SHMEM": VLIMIT_SHMEM}
 
             "ANON": VLIMIT_ANON,
             "SHMEM": VLIMIT_SHMEM}
 
+CPU_SHARE_MULT = 1024
+
 # add in the platform supported rlimits
 for entry in resource.__dict__.keys():
     if entry.find("RLIMIT_")==0:
 # add in the platform supported rlimits
 for entry in resource.__dict__.keys():
     if entry.find("RLIMIT_")==0:
@@ -52,7 +55,6 @@ for entry in resource.__dict__.keys():
 
 class NoSuchVServer(Exception): pass
 
 
 class NoSuchVServer(Exception): pass
 
-
 class VServerConfig:
     def __init__(self, name, directory):
         self.name = name
 class VServerConfig:
     def __init__(self, name, directory):
         self.name = name
@@ -131,9 +133,6 @@ class VServerConfig:
 
 class VServer:
 
 
 class VServer:
 
-    INITSCRIPTS = [('/etc/rc.vinit', 'start'),
-                   ('/etc/rc.d/rc', '%(runlevel)d')]
-
     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
@@ -142,7 +141,7 @@ class VServer:
                 os.access(self.dir, os.R_OK | os.W_OK | os.X_OK)):
             raise NoSuchVServer, "no such vserver: " + name
         self.config = VServerConfig(name, "/etc/vservers/%s" % name)
                 os.access(self.dir, os.R_OK | os.W_OK | os.X_OK)):
             raise NoSuchVServer, "no such vserver: " + name
         self.config = VServerConfig(name, "/etc/vservers/%s" % name)
-        self.remove_caps = ~vserverimpl.CAP_SAFE;
+        #self.remove_caps = ~vserverimpl.CAP_SAFE;
         if vm_id == None:
             vm_id = int(self.config.get('context'))
         self.ctx = vm_id
         if vm_id == None:
             vm_id = int(self.config.get('context'))
         self.ctx = vm_id
@@ -152,12 +151,16 @@ class VServer:
         self.logfile = logfile
 
     # inspired from nodemanager's logger
         self.logfile = logfile
 
     # inspired from nodemanager's logger
+    def log_in_file (self, fd, msg):
+        if not msg: msg="\n"
+        if not msg.endswith('\n'): msg += '\n'
+        os.write(fd, '%s: %s' % (time.asctime(time.gmtime()), msg))
+
     def log(self,msg):
         if self.logfile:
             try:
                 fd = os.open(self.logfile,os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0600)
     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))
+                self.log_in_file(fd,msg)
                 os.close(fd)
             except:
                 print '%s: (%s failed to open) %s'%(time.asctime(time.gmtime()),self.logfile,msg)
                 os.close(fd)
             except:
                 print '%s: (%s failed to open) %s'%(time.asctime(time.gmtime()),self.logfile,msg)
@@ -183,7 +186,7 @@ class VServer:
                 vserverimpl.setrlimit(self.ctx, resource_type, hard, soft, min)
             except OSError, e:
                 self.log("Error: setrlimit(%d, %s, %d, %d, %d): %s"
                 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))
+                         % (self.ctx, type.lower(), hard, soft, min, e))
 
         return update
 
 
         return update
 
@@ -200,6 +203,9 @@ class VServer:
     def set_capabilities_config(self, capabilities):
         bcaps = self.get_bcaps_from_capabilities(capabilities)
         ccaps = self.get_ccaps_from_capabilities(capabilities)
     def set_capabilities_config(self, capabilities):
         bcaps = self.get_bcaps_from_capabilities(capabilities)
         ccaps = self.get_ccaps_from_capabilities(capabilities)
+        if len(bcaps) > 0:
+            bcaps += ","
+        bcaps += "CAP_NET_RAW"
         self.config.update('bcapabilities', bcaps)
         self.config.update('ccapabilities', ccaps)
         ret = vserverimpl.setbcaps(self.ctx, vserverimpl.text2bcaps(bcaps))
         self.config.update('bcapabilities', bcaps)
         self.config.update('ccapabilities', ccaps)
         ret = vserverimpl.setbcaps(self.ctx, vserverimpl.text2bcaps(bcaps))
@@ -227,6 +233,7 @@ class VServer:
             vserverimpl.netadd(self.ctx, a)
 
     def set_ipaddresses_config(self, addresses):
             vserverimpl.netadd(self.ctx, a)
 
     def set_ipaddresses_config(self, addresses):
+        return # acb
         i = 0
         for a in addresses.split(","):
             self.config.update("interfaces/%d/ip" % i, a)
         i = 0
         for a in addresses.split(","):
             self.config.update("interfaces/%d/ip" % i, a)
@@ -248,20 +255,20 @@ class VServer:
 
     def get_ipaddresses(self):
         # No clean way to do this right now.
 
     def get_ipaddresses(self):
         # No clean way to do this right now.
+        self.log("Calling Vserver.get_ipaddresses for slice %s" % self.name)
         return None
 
     def __do_chroot(self):
         os.chroot(self.dir)
         os.chdir("/")
 
         return None
 
     def __do_chroot(self):
         os.chroot(self.dir)
         os.chdir("/")
 
-    def chroot_call(self, fn, *args):
-
+    def chroot_call(self, fn, *args, **kwargs):
         cwd_fd = os.open(".", os.O_RDONLY)
         try:
             root_fd = os.open("/", os.O_RDONLY)
             try:
                 self.__do_chroot()
         cwd_fd = os.open(".", os.O_RDONLY)
         try:
             root_fd = os.open("/", os.O_RDONLY)
             try:
                 self.__do_chroot()
-                result = fn(*args)
+                result = fn(*args, **kwargs)
             finally:
                 os.fchdir(root_fd)
                 os.chroot(".")
             finally:
                 os.fchdir(root_fd)
                 os.chroot(".")
@@ -299,14 +306,13 @@ class VServer:
         except OSError, e:
             self.log("Unexpected error with setdlimit for context %d" % self.ctx)
 
         except OSError, e:
             self.log("Unexpected error with setdlimit for context %d" % self.ctx)
 
-
         self.config.update('dlimits/0/space_total', block_limit)
 
     def is_running(self):
         self.config.update('dlimits/0/space_total', block_limit)
 
     def is_running(self):
-        return vserverimpl.isrunning(self.ctx)
+        status = subprocess.call(["/usr/sbin/vserver", self.name, "running"], shell=False)
+        return not status
     
     def get_disklimit(self):
     
     def get_disklimit(self):
-
         try:
             (self.disk_blocks, block_limit, self.disk_inodes, inode_limit,
              reserved) = vserverimpl.getdlimit(self.dir, self.ctx)
         try:
             (self.disk_blocks, block_limit, self.disk_inodes, inode_limit,
              reserved) = vserverimpl.getdlimit(self.dir, self.ctx)
@@ -319,26 +325,28 @@ class VServer:
         return block_limit
 
     def set_sched_config(self, cpu_min, cpu_share):
         return block_limit
 
     def set_sched_config(self, cpu_min, cpu_share):
-
         """ Write current CPU scheduler parameters to the vserver
         """ Write current CPU scheduler parameters to the vserver
-        configuration file. This method does not modify the kernel CPU
-        scheduling parameters for this context. """
-
-        self.config.update('sched/fill-rate', cpu_min)
-        self.config.update('sched/fill-rate2', cpu_share)
-        if cpu_share == 0:
-            self.config.unset('sched/idle-time')
-        
+        configuration file. Currently, 'cpu_min' is not supported. """
+        self.config.update('cgroup/cpu.shares', cpu_share * CPU_SHARE_MULT)
         if self.is_running():
             self.set_sched(cpu_min, cpu_share)
 
     def set_sched(self, cpu_min, cpu_share):
         if self.is_running():
             self.set_sched(cpu_min, cpu_share)
 
     def set_sched(self, cpu_min, cpu_share):
-        """ Update kernel CPU scheduling parameters for this context. """
-        vserverimpl.setsched(self.ctx, cpu_min, cpu_share)
+        """ Update kernel CPU scheduling parameters for this context.
+        Currently, 'cpu_min' is not supported. """
+        try:
+            cgroup = open('/dev/cgroup/%s/cpu.shares' % name, 'w')
+            cgroup.write('%s' % (cpu_share * CPU_SHARE_MULT))
+            cgroup.close()
+        except:
+            pass
 
     def get_sched(self):
 
     def get_sched(self):
-        # have no way of querying scheduler right now on a per vserver basis
-        return (-1, False)
+        try:
+            cpu_share = int(int(self.config.get('cgroup/cpu.shares')) / CPU_SHARE_MULT)
+        except:
+            cpu_share = False
+        return (-1, cpu_share)
 
     def set_bwlimit(self, minrate = bwlimit.bwmin, maxrate = None,
                     exempt_min = None, exempt_max = None,
 
     def set_bwlimit(self, minrate = bwlimit.bwmin, maxrate = None,
                     exempt_min = None, exempt_max = None,
@@ -362,130 +370,36 @@ class VServer:
 
         return self.chroot_call(open, filename, mode, bufsize)
 
 
         return self.chroot_call(open, filename, mode, bufsize)
 
-    def __do_chcontext(self, state_file):
-
-        if state_file:
-            print >>state_file, "%u" % self.ctx
-            state_file.close()
-
-        if vserverimpl.chcontext(self.ctx, vserverimpl.text2bcaps(self.get_capabilities_config())):
-            self.set_resources(True)
-            vserverimpl.setup_done(self.ctx)
-
-
-    def __prep(self, runlevel):
-
-        """ Perform all the crap that the vserver script does before
-        actually executing the startup scripts. """
-
-
-        # set the initial runlevel
-        vserverimpl.setrunlevel(self.dir + "/var/run/utmp", runlevel)
-
-        # mount /proc and /dev/pts
-        self.__do_mount("none", self.dir, "/proc", "proc")
-        # XXX - magic mount options
-        self.__do_mount("none", self.dir, "/dev/pts", "devpts", 0, "gid=5,mode=0620")
-
-
-    def __cleanvar(self):
-        """
-        Clean the /var/ directory so RH startup scripts can run
-        """ 
-
-        RUNDIR = "/var/run"
-        LOCKDIR = "/var/lock/subsys"
-
-        filter = ["utmp"]
-        garbage = []
-        for topdir in [RUNDIR, LOCKDIR]:
-            #os.walk() = (dirpath, dirnames, filenames)
-            for root, dirs, files in os.walk(topdir):
-                for file in files:
-                    if not file in filter:
-                        garbage.append(root + "/" + file)
-
-        for f in garbage: os.unlink(f)
-        return garbage
-
-
-    def __do_mount(self, *mount_args):
-        try:
-            vserverimpl.mount(*mount_args)
-        except OSError, ex:
-            if ex.errno == errno.EBUSY:
-                # assume already mounted
-                return
-            raise ex
-
-
     def enter(self):
     def enter(self):
-        self.config.cache_it()
-        self.__do_chroot()
-        self.__do_chcontext(None)
-
+        subprocess.call("/usr/sbin/vserver %s enter" % self.name, shell=True)
 
 
+    # 2010 June 21 - Thierry 
+    # the slice initscript now gets invoked through rc - see sliver_vs.py in nodemanager
+    # and, rc is triggered as part of vserver .. start 
+    # so we don't have to worry about initscripts at all anymore here
     def start(self, runlevel = 3):
     def start(self, runlevel = 3):
-
-        if (os.fork() != 0):
+        if os.fork() != 0:
             # Parent should just return.
             self.vm_running = True
             return
         else:
             # Parent should just return.
             self.vm_running = True
             return
         else:
-            # child process
+            os.setsid()
+            # first child process: fork again
+            if os.fork() != 0:
+                os._exit(0)    # Exit parent (the first child) of the second child.
+            # the grandson is the working one
+            os.chdir('/')
+            os.umask(0)
             try:
             try:
-                # so we don't chcontext with priv'ed fds
-                close_nonstandard_fds()
-
-                # get a new session
-                os.setsid()
+                # start the vserver
+                subprocess.call(["/usr/sbin/vserver",self.name,"start"])
 
 
-                # open state file to record vserver info
-                state_file = open("/var/run/vservers/%s" % self.name, "w")
-
-                # use /dev/null for stdin, /var/log/boot.log for stdout/err
-                fd = os.open("/dev/null", os.O_RDONLY)
-                if fd != 0:
-                    os.dup2(fd, 0)
-                    os.close(fd)
-                # perform pre-init cleanup
-                self.__prep(runlevel)
-
-                self.config.cache_it()
-                self.__do_chroot()
-                removed = self.__cleanvar()
-
-                log = open("/var/log/boot.log", "a", 0)
-                if log.fileno() != 1:
-                    os.dup2(log.fileno(), 1)
-                os.dup2(1, 2)
-
-                print >>log, ("%s: removing %s" % 
-                                (time.asctime(time.gmtime()), removed))
-                print >>log, ("%s: starting the virtual server %s" %
-                                (time.asctime(time.gmtime()), self.name))
-                # 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,
-                                                   cmd[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
+            # we get here due to an exception in the grandson process
             except Exception, ex:
                 self.log(traceback.format_exc())
             os._exit(0)
 
             except Exception, ex:
                 self.log(traceback.format_exc())
             os._exit(0)
 
-    def set_resources(self,setup=False):
+    def set_resources(self):
 
         """ 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. """
@@ -515,17 +429,15 @@ class VServer:
         (space, inodes) = line.split()
         self.disk_inodes = int(inodes)
         self.disk_blocks = int(space)
         (space, inodes) = line.split()
         self.disk_inodes = int(inodes)
         self.disk_blocks = int(space)
-        #(self.disk_inodes, self.disk_blocks) = vduimpl.vdu(self.dir)
 
         return self.disk_blocks * 1024
 
     def stop(self, signal = signal.SIGKILL):
 
         return self.disk_blocks * 1024
 
     def stop(self, signal = signal.SIGKILL):
-        vserverimpl.killall(self.ctx, signal)
         self.vm_running = False
         self.vm_running = False
+        subprocess.call("/usr/sbin/vserver %s stop" % self.name, shell=True)
 
     def setname(self, slice_id):
 
     def setname(self, slice_id):
-        '''Set vcVHI_CONTEXT field in kernel to slice_id'''
-        vserverimpl.setname(self.ctx, slice_id)
+        pass
 
     def getname(self):
         '''Get vcVHI_CONTEXT field in kernel'''
 
     def getname(self):
         '''Get vcVHI_CONTEXT field in kernel'''