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.
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.
30 class Sliver_VS(accounts.Account, vserver.VServer):
31 """This class wraps vserver.VServer to make its interface closer to what we need."""
34 TYPE = 'sliver.VServer'
35 _init_disk_info_sem = threading.Semaphore(1)
37 def __init__(self, rec):
39 vserver.VServer.__init__(self, rec['name'])
40 except Exception, err:
41 if not isinstance(err, vserver.NoSuchVServer):
42 # Probably a bad vserver or vserver configuration file
44 logger.log('%s: recreating bad vserver' % rec['name'])
45 self.destroy(rec['name'])
46 self.create(rec['name'], rec['vref'])
47 vserver.VServer.__init__(self, rec['name'])
52 self.disk_usage_initialized = False
56 def create(name, vref = None):
58 logger.log_call('/usr/sbin/vuseradd', '-t', vref, name)
60 logger.log_call('/usr/sbin/vuseradd', name)
63 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
65 def configure(self, rec):
66 new_rspec = rec['_rspec']
67 if new_rspec != self.rspec:
68 self.rspec = new_rspec
71 new_initscript = rec['initscript']
72 if new_initscript != self.initscript:
73 self.initscript = new_initscript
74 logger.log('%s: installing initscript' % self.name)
75 def install_initscript():
76 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
77 fd = os.open('/etc/rc.vinit', flags, 0755)
78 os.write(fd, new_initscript)
80 try: self.chroot_call(install_initscript)
81 except: logger.log_exc()
83 accounts.Account.configure(self, rec) # install ssh keys
85 def start(self, delay=0):
86 if self.rspec['enabled']:
87 logger.log('%s: starting in %d seconds' % (self.name, delay))
90 # VServer.start calls fork() internally, so just close the nonstandard fds and fork once to avoid creating zombies
91 tools.close_nonstandard_fds()
93 vserver.VServer.start(self, True)
95 else: os.waitpid(child_pid, 0)
96 else: logger.log('%s: not starting, is not enabled' % self.name)
99 logger.log('%s: stopping' % self.name)
100 vserver.VServer.stop(self)
102 def set_resources(self):
103 disk_max = self.rspec['disk_max']
104 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
105 try: # if the sliver is over quota, .set_disk_limit will throw an exception
106 if not self.disk_usage_initialized:
107 self.vm_running = False
108 logger.log('%s: computing disk usage: beginning' % self.name)
109 Sliver_VS._init_disk_info_sem.acquire()
110 try: self.init_disk_info()
111 finally: Sliver_VS._init_disk_info_sem.release()
112 logger.log('%s: computing disk usage: ended' % self.name)
113 self.disk_usage_initialized = True
114 vserver.VServer.set_disklimit(self, disk_max)
116 logger.log('%s: failed to set max disk usage' % self.name)
119 # N.B. net_*_rate are in kbps because of XML-RPC maxint
120 # limitations, convert to bps which is what bwlimit.py expects.
121 # net_limits = (self.rspec['net_min_rate'] * 1000,
122 # self.rspec['net_max_rate'] * 1000,
123 # self.rspec['net_i2_min_rate'] * 1000,
124 # self.rspec['net_i2_max_rate'] * 1000,
125 # self.rspec['net_share'])
126 # logger.log('%s: setting net limits to %s bps' % (self.name, net_limits[:-1]))
127 # logger.log('%s: setting net share to %d' % (self.name, net_limits[-1]))
128 # self.set_bwlimit(*net_limits)
130 cpu_min = self.rspec['cpu_min']
131 cpu_share = self.rspec['cpu_share']
132 if self.rspec['enabled'] > 0:
133 if cpu_min >= 50: # at least 5%: keep people from shooting themselves in the foot
134 logger.log('%s: setting cpu share to %d%% guaranteed' % (self.name, cpu_min/10.0))
135 self.set_sched_config(cpu_min, vserver.SCHED_CPU_GUARANTEED)
137 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
138 self.set_sched_config(cpu_share, 0)
139 else: # tell vsh to disable remote login by setting CPULIMIT to 0
140 logger.log('%s: disabling remote login' % self.name)
141 self.set_sched_config(0, 0)