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.
29 # special constant that tells vserver to keep its existing settings
30 KEEP_LIMIT = vserver.VC_LIM_KEEP
32 class Sliver_VS(accounts.Account, vserver.VServer):
33 """This class wraps vserver.VServer to make its interface closer to what we need."""
36 TYPE = 'sliver.VServer'
37 _init_disk_info_sem = threading.Semaphore(1)
39 def __init__(self, rec):
41 vserver.VServer.__init__(self, rec['name'])
42 except Exception, err:
43 if not isinstance(err, vserver.NoSuchVServer):
44 # Probably a bad vserver or vserver configuration file
46 logger.log('%s: recreating bad vserver' % rec['name'])
47 self.destroy(rec['name'])
48 self.create(rec['name'], rec['vref'])
49 vserver.VServer.__init__(self, rec['name'])
54 self.disk_usage_initialized = False
58 def create(name, vref = None):
60 logger.log_call('/usr/sbin/vuseradd', '-t', vref, name)
62 logger.log_call('/usr/sbin/vuseradd', name)
63 open('/vservers/%s/etc/slicename' % name, 'w').write(name)
66 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
68 def configure(self, rec):
69 new_rspec = rec['_rspec']
70 if new_rspec != self.rspec:
71 self.rspec = new_rspec
74 new_initscript = rec['initscript']
75 if new_initscript != self.initscript:
76 self.initscript = new_initscript
77 logger.log('%s: installing initscript' % self.name)
78 def install_initscript():
79 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
80 fd = os.open('/etc/rc.vinit', flags, 0755)
81 os.write(fd, new_initscript)
83 try: self.chroot_call(install_initscript)
84 except: logger.log_exc()
86 accounts.Account.configure(self, rec) # install ssh keys
88 def start(self, delay=0):
89 if self.rspec['enabled'] > 0:
90 logger.log('%s: starting in %d seconds' % (self.name, delay))
94 # VServer.start calls fork() internally, so just close the nonstandard fds and fork once to avoid creating zombies
95 tools.close_nonstandard_fds()
96 vserver.VServer.start(self, True)
98 else: os.waitpid(child_pid, 0)
99 else: logger.log('%s: not starting, is not enabled' % self.name)
102 logger.log('%s: stopping' % self.name)
103 vserver.VServer.stop(self)
105 def set_resources(self):
106 disk_max = self.rspec['disk_max']
107 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
108 try: # if the sliver is over quota, .set_disk_limit will throw an exception
109 if not self.disk_usage_initialized:
110 self.vm_running = False
111 logger.log('%s: computing disk usage: beginning' % self.name)
112 Sliver_VS._init_disk_info_sem.acquire()
113 try: self.init_disk_info()
114 finally: Sliver_VS._init_disk_info_sem.release()
115 logger.log('%s: computing disk usage: ended' % self.name)
116 self.disk_usage_initialized = True
117 vserver.VServer.set_disklimit(self, disk_max)
119 logger.log('%s: failed to set max disk usage' % self.name)
122 # set min/soft/hard values for 'as', 'rss', 'nproc' and openfd
123 # Note that vserver currently only implements support for hard limits
125 as_min = self.rspec['as_min']
126 as_soft = self.rspec['as_soft']
127 as_hard = self.rspec['as_hard']
128 self.set_AS_config(as_hard, as_soft, as_min)
130 rss_min = self.rspec['rss_min']
131 rss_soft = self.rspec['rss_soft']
132 rss_hard = self.rspec['rss_hard']
133 self.set_RSS_config(rss_hard, rss_soft, rss_min)
135 nproc_min = self.rspec['nproc_min']
136 nproc_soft = self.rspec['nproc_soft']
137 nproc_hard = self.rspec['nproc_hard']
138 self.set_NPROC_config(nproc_hard, nproc_soft, nproc_min)
140 openfd_min = self.rspec['openfd_min']
141 openfd_soft = self.rspec['openfd_soft']
142 openfd_hard = self.rspec['openfd_hard']
143 self.set_OPENFD_config(openfd_hard, openfd_soft, openfd_min)
145 self.set_WHITELISTED_config(self.rspec['whitelist'])
147 if False: # this code was commented out before
148 # N.B. net_*_rate are in kbps because of XML-RPC maxint
149 # limitations, convert to bps which is what bwlimit.py expects.
150 net_limits = (self.rspec['net_min_rate'] * 1000,
151 self.rspec['net_max_rate'] * 1000,
152 self.rspec['net_i2_min_rate'] * 1000,
153 self.rspec['net_i2_max_rate'] * 1000,
154 self.rspec['net_share'])
155 logger.log('%s: setting net limits to %s bps' % (self.name, net_limits[:-1]))
156 logger.log('%s: setting net share to %d' % (self.name, net_limits[-1]))
157 self.set_bwlimit(*net_limits)
159 cpu_min = self.rspec['cpu_min']
160 cpu_share = self.rspec['cpu_share']
162 if self.rspec['enabled'] > 0 and self.rspec['whitelist'] == 1:
163 if cpu_min >= 50: # at least 5%: keep people from shooting themselves in the foot
164 logger.log('%s: setting cpu share to %d%% guaranteed' % (self.name, cpu_min/10.0))
165 self.set_sched_config(cpu_min, vserver.SCHED_CPU_GUARANTEED)
167 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
168 self.set_sched_config(cpu_share, 0)
170 if self.have_limits_changed():
171 logger.log('%s: limits have changed --- restarting' % self.name)
173 while self.isrunning() and stopcount > 0:
177 stopcount = stopcount - 1
180 else: # tell vsh to disable remote login by setting CPULIMIT to 0
181 logger.log('%s: disabling remote login' % self.name)
182 self.set_sched_config(0, 0)