+"""VServer slivers.
+
+There are a couple of tricky things going on here. First, the kernel
+needs disk usage information in order to enforce the quota. However,
+determining disk usage redundantly strains the disks. Thus, the
+Sliver_VS.disk_usage_initialized flag is used to determine whether
+this initialization has been made.
+
+Second, it's not currently possible to set the scheduler parameters
+for a sliver unless that sliver has a running process. /bin/vsh helps
+us out by reading the configuration file so that it can set the
+appropriate limits after entering the sliver context. Making the
+syscall that actually sets the parameters gives a harmless error if no
+process is running. Thus we keep vm_running on when setting scheduler
+parameters so that set_sched_params() always makes the syscall, and we
+don't have to guess if there is a running process or not.
+"""
+
+import errno
+import os
+import vserver
+
+from bwlimit import bwmin, bwmax
+import accounts
+import logger
+import tools
+
+
+DEFAULTS = {'disk_max': 5000000,
+ 'net_min': bwmin,
+ 'net_max': bwmax,
+ 'net2_min': bwmin,
+ 'net2_max': bwmax,
+ 'net_share': 1,
+ 'enabled': 1,
+ 'cpu_min': 0,
+ 'cpu_share': 32,
+ 'keys': '',
+ 'initscript': ''}
+
+class Sliver_VS(vserver.VServer):
+ """This class wraps vserver.VServer to make its interface closer to what we need for the Node Manager."""
+
+ SHELL = '/bin/vsh'
+ TYPE = 'sliver.VServer'
+
+ def __init__(self, name):
+ vserver.VServer.__init__(self, name)
+ self.current_keys = ''
+ self.current_initscript = ''
+ self.disk_usage_initialized = False
+ self.rec = DEFAULTS.copy()
+
+
+ @staticmethod
+ def create(name): logger.log_call('/usr/sbin/vuseradd', name)
+
+ @staticmethod
+ def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
+
+
+ def configure(self, rec):
+ self.rec = DEFAULTS.copy()
+ self.rec.update(rec)
+
+ self.set_resources()
+
+ new_keys = self.rec['keys']
+ if new_keys != self.current_keys:
+ accounts.install_keys(rec)
+ self.current_keys = new_keys
+
+ new_initscript = self.rec['initscript']
+ if new_initscript != self.current_initscript:
+ logger.log('%s: installing initscript' % self.name)
+ def install_initscript():
+ flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
+ fd = os.open('/etc/rc.vinit', flags, 0755)
+ os.write(fd, new_initscript)
+ os.close(fd)
+ try: self.chroot_call(install_initscript)
+ except: logger.log_exc()
+ self.current_initscript = new_initscript
+
+
+ def start(self):
+ if self.rec['enabled']:
+ logger.log('%s: starting' % self.name)
+ child_pid = os.fork()
+ if child_pid == 0:
+ # VServer.start calls fork() internally, so just close the nonstandard fds and fork once to avoid creating zombies
+ tools.close_nonstandard_fds()
+ vserver.VServer.start(self, True)
+ os._exit(0)
+ else: os.waitpid(child_pid, 0)
+ else: logger.log('%s: not starting, is not enabled' % self.name)
+
+ def stop(self):
+ logger.log('%s: stopping' % self.name)
+ vserver.VServer.stop(self)
+
+
+ def set_resources(self):
+ disk_max = int(self.rec['disk_max'])
+ logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
+ try: # if the sliver is over quota, .set_disk_limit will throw an exception
+ if not self.disk_usage_initialized:
+ self.vm_running = False
+ logger.log('%s: computing disk usage' % self.name)
+ self.init_disk_info()
+ self.disk_usage_initialized = True
+ vserver.VServer.set_disklimit(self, disk_max_KiB)
+ except OSError: logger.log_exc()
+
+ net_limits = (int(self.rec['net_min']),
+ int(self.rec['net_max']),
+ int(self.rec['net2_min']),
+ int(self.rec['net2_max']),
+ int(self.rec['net_share']))
+ logger.log('%s: setting net limits to %s bps' % (self.name, net_limits[:-1]))
+ logger.log('%s: setting net share to %d' % (self.name, net_limits[-1]))
+ self.set_bwlimit(*net_limits)
+
+ cpu_min = int(self.rec['cpu_min'])
+ cpu_share = int(self.rec['cpu_share'])
+ if bool(self.rec['enabled']):
+ if cpu_min > 0:
+ logger.log('%s: setting cpu share to %d%% guaranteed' % (self.name, cpu_min/10.0))
+ self.set_sched_config(cpu_min, vserver.SCHED_CPU_GUARANTEED)
+ else:
+ logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
+ self.set_sched_config(cpu_share, 0)
+ else:
+ # tell vsh to disable remote login by setting CPULIMIT to 0
+ logger.log('%s: disabling remote login' % self.name)
+ self.set_sched_config(0, 0)
+ self.stop()