- revert incorrect mangling of 'type' slice attribute
[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         try:
37             vserver.VServer.__init__(self, rec['name'])
38         except vserver.NoSuchVServer:
39             self.create(rec['name'], rec['type'])
40             vserver.VServer.__init__(self, rec['name'])
41
42         self.keys = ''
43         self.rspec = {}
44         self.initscript = ''
45         self.disk_usage_initialized = False
46         self.configure(rec)
47
48     @staticmethod
49     def create(name, vref = None):
50         if vref is not None:
51             logger.log_call('/usr/sbin/vuseradd', '-t', vref, name)
52         else:
53             logger.log_call('/usr/sbin/vuseradd', name)
54
55     @staticmethod
56     def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
57
58     def configure(self, rec):
59         new_rspec = rec['_rspec']
60         if new_rspec != self.rspec:
61             self.rspec = new_rspec
62             self.set_resources()
63
64         new_initscript = rec['initscript']
65         if new_initscript != self.initscript:
66             self.initscript = new_initscript
67             logger.log('%s: installing initscript' % self.name)
68             def install_initscript():
69                 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
70                 fd = os.open('/etc/rc.vinit', flags, 0755)
71                 os.write(fd, new_initscript)
72                 os.close(fd)
73             try: self.chroot_call(install_initscript)
74             except: logger.log_exc()
75
76         accounts.Account.configure(self, rec)  # install ssh keys
77
78     def start(self, delay=0):
79         if self.rspec['enabled']:
80             logger.log('%s: starting in %d seconds' % (self.name, delay))
81             child_pid = os.fork()
82             if child_pid == 0:
83                 # VServer.start calls fork() internally, so just close the nonstandard fds and fork once to avoid creating zombies
84                 tools.close_nonstandard_fds()
85                 time.sleep(delay)
86                 vserver.VServer.start(self, True)
87                 os._exit(0)
88             else: os.waitpid(child_pid, 0)
89         else: logger.log('%s: not starting, is not enabled' % self.name)
90
91     def stop(self):
92         logger.log('%s: stopping' % self.name)
93         vserver.VServer.stop(self)
94
95     def set_resources(self):
96         disk_max = self.rspec['disk_max']
97         logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
98         try:  # if the sliver is over quota, .set_disk_limit will throw an exception
99             if not self.disk_usage_initialized:
100                 self.vm_running = False
101                 logger.log('%s: computing disk usage' % self.name)
102                 self.init_disk_info()
103                 self.disk_usage_initialized = True
104             vserver.VServer.set_disklimit(self, disk_max)
105         except OSError:
106             logger.log('%s: failed to set max disk usage' % self.name)
107             logger.log_exc()
108
109         net_limits = (self.rspec['net_min'], self.rspec['net_max'], self.rspec['net2_min'], self.rspec['net2_max'], self.rspec['net_share'])
110         logger.log('%s: setting net limits to %s bps' % (self.name, net_limits[:-1]))
111         logger.log('%s: setting net share to %d' % (self.name, net_limits[-1]))
112         self.set_bwlimit(*net_limits)
113
114         cpu_min = self.rspec['cpu_min']
115         cpu_share = self.rspec['cpu_share']
116         if self.rspec['enabled']:
117             if cpu_min >= 50:  # at least 5%: keep people from shooting themselves in the foot
118                 logger.log('%s: setting cpu share to %d%% guaranteed' % (self.name, cpu_min/10.0))
119                 self.set_sched_config(cpu_min, vserver.SCHED_CPU_GUARANTEED)
120             else:
121                 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
122                 self.set_sched_config(cpu_share, 0)
123         else:  # tell vsh to disable remote login by setting CPULIMIT to 0
124             logger.log('%s: disabling remote login' % self.name)
125             self.set_sched_config(0, 0)
126             self.stop()