More documentation.
[nodemanager.git] / sliver.py
1 import base64
2 import errno
3 import os
4 import vserver
5
6 from config import DEFAULT_RSPEC
7 import accounts
8 import logger
9 import tools
10
11
12 class Sliver(vserver.VServer):
13     """This class wraps vserver.VServer to make its interface closer to what we need for the Node Manager."""
14
15     SHELL = '/bin/vsh'
16     TYPE = 'sliver'
17
18     def __init__(self, name):
19         vserver.VServer.__init__(self, name, vm_running=True)
20         self.disk_limit_has_been_set = False
21         self.rspec = DEFAULT_RSPEC.copy()
22         self.ssh_keys = None
23         self.initscript = ''
24
25     @staticmethod
26     def create(name): logger.log_call('/usr/sbin/vuseradd', name)
27
28     @staticmethod
29     def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
30
31     def configure(self, rec):
32         self.rspec.update(rec['eff_rspec'])
33         self.set_resources()
34         if rec['ssh_keys'] != self.ssh_keys:
35             accounts.install_ssh_keys(rec)
36             self.ssh_keys = rec['ssh_keys']
37         if rec['initscript'] != self.initscript:
38             logger.log('%s: installing initscript' % self.name)
39             def install_initscript():
40                 flags = os.O_WRONLY|os.O_CREAT|os.O_TRUNC
41                 fd = os.open('/etc/rc.vinit', flags, 0755)
42                 os.write(fd, base64.b64decode(rec['initscript']))
43                 os.close(fd)
44             try: self.chroot_call(install_initscript)
45             except OSError, e:
46                 if e.errno != errno.EEXIST: logger.log_exc()
47             self.initscript = rec['initscript']
48
49     def start(self):
50         if self.rspec['nm_enabled']:
51             logger.log('%s: starting' % self.name)
52             child_pid = os.fork()
53             if child_pid == 0:
54                 # VServer.start calls fork() internally, so we don't need all of fork_as()
55                 tools.close_nonstandard_fds()
56                 vserver.VServer.start(self, True)
57                 os._exit(0)
58             else: os.waitpid(child_pid, 0)
59         else: logger.log('%s: not starting, is not enabled' % self.name)
60
61     def stop(self):
62         logger.log('%s: stopping' % self.name)
63         vserver.VServer.stop(self)
64         # make sure we always make the syscalls when setting resource limits
65         self.vm_running = True
66
67     def set_resources(self):
68         """Set the resource limits of sliver <self.name>."""
69         # disk limits
70         disk_max_KiB = self.rspec['nm_disk_quota']
71         logger.log('%s: setting max disk usage to %d KiB' %
72                    (self.name, disk_max_KiB))
73         try:  # don't let slivers over quota escape other limits
74             if not self.disk_limit_has_been_set:
75                 self.vm_running = False
76                 logger.log('%s: computing disk usage' % self.name)
77                 self.init_disk_info()
78                 # even if set_disklimit() triggers an exception,
79                 # the kernel probably knows the disk usage
80                 self.disk_limit_has_been_set = True
81             vserver.VServer.set_disklimit(self, disk_max_KiB)
82             self.vm_running = True
83         except OSError: logger.log_exc()
84
85         # bw limits
86         bw_fields = ['nm_net_min_rate', 'nm_net_max_rate',
87                      'nm_net_exempt_min_rate', 'nm_net_exempt_max_rate',
88                      'nm_net_share']
89         args = tuple(map(self.rspec.__getitem__, bw_fields))
90         logger.log('%s: setting bw share to %d' % (self.name, args[-1]))
91         logger.log('%s: setting bw limits to %s bps' % (self.name, args[:-1]))
92         self.set_bwlimit(*args)
93
94         # cpu limits / remote login
95         cpu_guaranteed_shares = self.rspec['nm_cpu_guaranteed_share']
96         cpu_shares = self.rspec['nm_cpu_share']
97         if self.rspec['nm_enabled']:
98             if cpu_guaranteed_shares > 0:
99                 logger.log('%s: setting cpu share to %d%% guaranteed' %
100                            (self.name, cpu_guaranteed_shares/10.0))
101                 self.set_sched_config(cpu_guaranteed_shares,
102                                       vserver.SCHED_CPU_GUARANTEED)
103             else:
104                 logger.log('%s: setting cpu share to %d' %
105                            (self.name, cpu_shares))
106                 self.set_sched_config(cpu_shares, 0)
107         else:
108             # tell vsh to disable remote login by setting CPULIMIT to 0
109             logger.log('%s: disabling remote login' % self.name)
110             self.set_sched_config(0, 0)
111             self.stop()