First version. Most definitely a work in progress.
[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.VServer.start(self, True)
55                 os._exit(0)
56             else: os.waitpid(child_pid, 0)
57         else: logger.log('%s: not starting, is not enabled' % self.name)
58
59     def stop(self):
60         logger.log('%s: stopping' % self.name)
61         vserver.VServer.stop(self)
62         # make sure we always make the syscalls when setting resource limits
63         self.vm_running = True
64
65     def set_resources(self):
66         """Set the resource limits of sliver <self.name>."""
67         # disk limits
68         disk_max_KiB = self.rspec['nm_disk_quota']
69         logger.log('%s: setting max disk usage to %d KiB' %
70                    (self.name, disk_max_KiB))
71         try:  # don't let slivers over quota escape other limits
72             if not self.disk_limit_has_been_set:
73                 self.vm_running = False
74                 logger.log('%s: computing disk usage' % self.name)
75                 self.init_disk_info()
76                 # even if set_disklimit() triggers an exception,
77                 # the kernel probably knows the disk usage
78                 self.disk_limit_has_been_set = True
79             vserver.VServer.set_disklimit(self, disk_max_KiB)
80             self.vm_running = True
81         except OSError: logger.log_exc()
82
83         # bw limits
84         bw_fields = ['nm_net_min_rate', 'nm_net_max_rate',
85                      'nm_net_exempt_min_rate', 'nm_net_exempt_max_rate',
86                      'nm_net_share']
87         args = tuple(map(self.rspec.__getitem__, bw_fields))
88         logger.log('%s: setting bw share to %d' % (self.name, args[-1]))
89         logger.log('%s: setting bw limits to %s bps' % (self.name, args[:-1]))
90         self.set_bwlimit(*args)
91
92         # cpu limits / remote login
93         cpu_guaranteed_shares = self.rspec['nm_cpu_guaranteed_share']
94         cpu_shares = self.rspec['nm_cpu_share']
95         if self.rspec['nm_enabled']:
96             if cpu_guaranteed_shares > 0:
97                 logger.log('%s: setting cpu share to %d%% guaranteed' %
98                            (self.name, cpu_guaranteed_shares/10.0))
99                 self.set_sched_config(cpu_guaranteed_shares,
100                                       vserver.SCHED_CPU_GUARANTEED)
101             else:
102                 logger.log('%s: setting cpu share to %d' %
103                            (self.name, cpu_shares))
104                 self.set_sched_config(cpu_shares, 0)
105         else:
106             # tell vsh to disable remote login by setting CPULIMIT to 0
107             logger.log('%s: disabling remote login' % self.name)
108             self.set_sched_config(0, 0)
109             self.stop()