Refactoring almost complete, with a stubbed out version of GetSlivers().
[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 vserver
22
23 import accounts
24 import logger
25 import tools
26
27
28 class Sliver_VS(accounts.Account, vserver.VServer):
29     """This class wraps vserver.VServer to make its interface closer to what we need for the Node Manager."""
30
31     SHELL = '/bin/vsh'
32     TYPE = 'sliver.VServer'
33
34     def __init__(self, rec):
35         vserver.VServer.__init__(self, rec['name'])
36         self.keys = ''
37         self.rspec = {}
38         self.initscript = ''
39         self.disk_usage_initialized = False
40         self.configure(rec)
41
42     @staticmethod
43     def create(name): logger.log_call('/usr/sbin/vuseradd', name)
44
45     @staticmethod
46     def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
47
48     def configure(self, rec):
49         new_rspec = rec['_rspec']
50         if new_rspec != self.rspec:
51             self.rspec = new_rspec
52             self.set_resources()
53
54         new_initscript = rec['initscript']
55         if new_initscript != self.initscript:
56             self.initscript = new_initscript
57             logger.log('%s: installing initscript' % self.name)
58             def install_initscript():
59                 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
60                 fd = os.open('/etc/rc.vinit', flags, 0755)
61                 os.write(fd, new_initscript)
62                 os.close(fd)
63             try: self.chroot_call(install_initscript)
64             except: logger.log_exc()
65
66         accounts.Account.configure(self, rec)  # install ssh keys
67
68     def start(self):
69         if self.rspec['enabled']:
70             logger.log('%s: starting' % self.name)
71             child_pid = os.fork()
72             if child_pid == 0:
73                 # VServer.start calls fork() internally, so just close the nonstandard fds and fork once to avoid creating zombies
74                 tools.close_nonstandard_fds()
75                 vserver.VServer.start(self, True)
76                 os._exit(0)
77             else: os.waitpid(child_pid, 0)
78         else: logger.log('%s: not starting, is not enabled' % self.name)
79
80     def stop(self):
81         logger.log('%s: stopping' % self.name)
82         vserver.VServer.stop(self)
83
84     def set_resources(self):
85         disk_max = self.rspec['disk_max']
86         logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
87         try:  # if the sliver is over quota, .set_disk_limit will throw an exception
88             if not self.disk_usage_initialized:
89                 self.vm_running = False
90                 logger.log('%s: computing disk usage' % self.name)
91                 self.init_disk_info()
92                 self.disk_usage_initialized = True
93             vserver.VServer.set_disklimit(self, disk_max)
94         except OSError: logger.log_exc()
95
96         net_limits = (self.rspec['net_min'], self.rspec['net_max'], self.rspec['net2_min'], self.rspec['net2_max'], self.rspec['net_share'])
97         logger.log('%s: setting net limits to %s bps' % (self.name, net_limits[:-1]))
98         logger.log('%s: setting net share to %d' % (self.name, net_limits[-1]))
99         self.set_bwlimit(*net_limits)
100
101         cpu_min = self.rspec['cpu_min']
102         cpu_share = self.rspec['cpu_share']
103         if self.rspec['enabled']:
104             if cpu_min > 0:
105                 logger.log('%s: setting cpu share to %d%% guaranteed' % (self.name, cpu_min/10.0))
106                 self.set_sched_config(cpu_min, vserver.SCHED_CPU_GUARANTEED)
107             else:
108                 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
109                 self.set_sched_config(cpu_share, 0)
110         else:  # tell vsh to disable remote login by setting CPULIMIT to 0
111             logger.log('%s: disabling remote login' % self.name)
112             self.set_sched_config(0, 0)
113             self.stop()