Support for -s. More comments. Removed out of date documentation and bwcap.
[nodemanager.git] / sliver_vs.py
1 """VServer slivers.
2
3 There are a couple of tricky things going on here.  First, the kernel
4 needs disk usage information in order to enforce the quota.  However,
5 determining disk usage redundantly strains the disks.  Thus, the
6 Sliver_VS.disk_usage_initialized flag is used to determine whether
7 this initialization has been made.
8
9 Second, it's not currently possible to set the scheduler parameters
10 for a sliver unless that sliver has a running process.  /bin/vsh helps
11 us out by reading the configuration file so that it can set the
12 appropriate limits after entering the sliver context.  Making the
13 syscall that actually sets the parameters gives a harmless error if no
14 process is running.  Thus we keep vm_running on when setting scheduler
15 parameters so that set_sched_params() always makes the syscall, and we
16 don't have to guess if there is a running process or not.
17 """
18
19 import errno
20 import os
21 import time
22 import vserver
23
24 import accounts
25 import logger
26 import tools
27
28
29 class Sliver_VS(accounts.Account, vserver.VServer):
30     """This class wraps vserver.VServer to make its interface closer to what we need."""
31
32     SHELL = '/bin/vsh'
33     TYPE = 'sliver.VServer'
34
35     def __init__(self, rec):
36         vserver.VServer.__init__(self, rec['name'])
37         self.keys = ''
38         self.rspec = {}
39         self.initscript = ''
40         self.disk_usage_initialized = False
41         self.configure(rec)
42
43     @staticmethod
44     def create(name): logger.log_call('/usr/sbin/vuseradd', name)
45
46     @staticmethod
47     def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
48
49     def configure(self, rec):
50         new_rspec = rec['_rspec']
51         if new_rspec != self.rspec:
52             self.rspec = new_rspec
53             self.set_resources()
54
55         new_initscript = rec['initscript']
56         if new_initscript != self.initscript:
57             self.initscript = new_initscript
58             logger.log('%s: installing initscript' % self.name)
59             def install_initscript():
60                 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
61                 fd = os.open('/etc/rc.vinit', flags, 0755)
62                 os.write(fd, new_initscript)
63                 os.close(fd)
64             try: self.chroot_call(install_initscript)
65             except: logger.log_exc()
66
67         accounts.Account.configure(self, rec)  # install ssh keys
68
69     def start(self, delay=0):
70         if self.rspec['enabled']:
71             logger.log('%s: starting in %d seconds' % (self.name, delay))
72             child_pid = os.fork()
73             if child_pid == 0:
74                 # VServer.start calls fork() internally, so just close the nonstandard fds and fork once to avoid creating zombies
75                 tools.close_nonstandard_fds()
76                 time.sleep(delay)
77                 vserver.VServer.start(self, True)
78                 os._exit(0)
79             else: os.waitpid(child_pid, 0)
80         else: logger.log('%s: not starting, is not enabled' % self.name)
81
82     def stop(self):
83         logger.log('%s: stopping' % self.name)
84         vserver.VServer.stop(self)
85
86     def set_resources(self):
87         disk_max = self.rspec['disk_max']
88         logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
89         try:  # if the sliver is over quota, .set_disk_limit will throw an exception
90             if not self.disk_usage_initialized:
91                 self.vm_running = False
92                 logger.log('%s: computing disk usage' % self.name)
93                 self.init_disk_info()
94                 self.disk_usage_initialized = True
95             vserver.VServer.set_disklimit(self, disk_max)
96         except OSError: logger.log_exc()
97
98         net_limits = (self.rspec['net_min'], self.rspec['net_max'], self.rspec['net2_min'], self.rspec['net2_max'], self.rspec['net_share'])
99         logger.log('%s: setting net limits to %s bps' % (self.name, net_limits[:-1]))
100         logger.log('%s: setting net share to %d' % (self.name, net_limits[-1]))
101         self.set_bwlimit(*net_limits)
102
103         cpu_min = self.rspec['cpu_min']
104         cpu_share = self.rspec['cpu_share']
105         if self.rspec['enabled']:
106             if cpu_min > 0:
107                 logger.log('%s: setting cpu share to %d%% guaranteed' % (self.name, cpu_min/10.0))
108                 self.set_sched_config(cpu_min, vserver.SCHED_CPU_GUARANTEED)
109             else:
110                 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
111                 self.set_sched_config(cpu_share, 0)
112         else:  # tell vsh to disable remote login by setting CPULIMIT to 0
113             logger.log('%s: disabling remote login' % self.name)
114             self.set_sched_config(0, 0)
115             self.stop()