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 # populate the sliver/vserver specific default allocations table,
33 # which is used to look for slice attributes
34 DEFAULT_ALLOCATION = {}
35 for rlimit in vserver.RLIMITS.keys():
37 DEFAULT_ALLOCATION["%s_min"%rlim]=KEEP_LIMIT
38 DEFAULT_ALLOCATION["%s_soft"%rlim]=KEEP_LIMIT
39 DEFAULT_ALLOCATION["%s_hard"%rlim]=KEEP_LIMIT
41 class Sliver_VS(accounts.Account, vserver.VServer):
42 """This class wraps vserver.VServer to make its interface closer to what we need."""
45 TYPE = 'sliver.VServer'
46 _init_disk_info_sem = threading.Semaphore(1)
48 def __init__(self, rec):
50 vserver.VServer.__init__(self, rec['name'])
51 except Exception, err:
52 if not isinstance(err, vserver.NoSuchVServer):
53 # Probably a bad vserver or vserver configuration file
55 logger.log('%s: recreating bad vserver' % rec['name'])
56 self.destroy(rec['name'])
57 self.create(rec['name'], rec['vref'])
58 vserver.VServer.__init__(self, rec['name'])
63 self.disk_usage_initialized = False
64 self.initscriptchanged = False
68 def create(name, vref = None):
70 logger.log_call('/usr/sbin/vuseradd', '-t', vref, name)
72 logger.log_call('/usr/sbin/vuseradd', name)
73 open('/vservers/%s/etc/slicename' % name, 'w').write(name)
76 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
78 def configure(self, rec):
79 new_rspec = rec['_rspec']
80 if new_rspec != self.rspec:
81 self.rspec = new_rspec
84 new_initscript = rec['initscript']
85 if new_initscript != self.initscript:
86 self.initscript = new_initscript
87 logger.log('%s: installing initscript' % self.name)
88 def install_initscript():
89 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
90 fd = os.open('/etc/rc.vinit', flags, 0755)
91 os.write(fd, new_initscript)
94 self.chroot_call(install_initscript)
95 self.initscriptchanged = True
96 except: logger.log_exc()
98 accounts.Account.configure(self, rec) # install ssh keys
100 def start(self, delay=0):
101 if self.rspec['enabled'] > 0:
102 logger.log('%s: starting in %d seconds' % (self.name, delay))
104 child_pid = os.fork()
106 # VServer.start calls fork() internally, so just close the nonstandard fds and fork once to avoid creating zombies
107 tools.close_nonstandard_fds()
108 vserver.VServer.start(self, True)
110 else: os.waitpid(child_pid, 0)
111 else: logger.log('%s: not starting, is not enabled' % self.name)
112 self.initscriptchanged = False
115 logger.log('%s: stopping' % self.name)
116 vserver.VServer.stop(self)
118 def set_resources(self):
119 disk_max = self.rspec['disk_max']
120 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
121 try: # if the sliver is over quota, .set_disk_limit will throw an exception
122 if not self.disk_usage_initialized:
123 self.vm_running = False
124 logger.log('%s: computing disk usage: beginning' % self.name)
125 Sliver_VS._init_disk_info_sem.acquire()
126 try: self.init_disk_info()
127 finally: Sliver_VS._init_disk_info_sem.release()
128 logger.log('%s: computing disk usage: ended' % self.name)
129 self.disk_usage_initialized = True
130 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
132 logger.log('%s: failed to set max disk usage' % self.name)
135 # get/set the min/soft/hard values for all of the vserver
136 # related RLIMITS. Note that vserver currently only
137 # implements support for hard limits.
138 for limit in vserver.RLIMITS.keys():
140 minimum = self.rspec['%s_min'%type]
141 soft = self.rspec['%s_soft'%type]
142 hard = self.rspec['%s_hard'%type]
143 self.set_rlimit_config(limit, hard, soft, minimum)
145 self.set_capabilities_config(self.rspec['capabilities'])
146 if self.rspec['capabilities']:
147 logger.log('%s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
149 if False: # this code was commented out before
150 # N.B. net_*_rate are in kbps because of XML-RPC maxint
151 # limitations, convert to bps which is what bwlimit.py expects.
152 net_limits = (self.rspec['net_min_rate'] * 1000,
153 self.rspec['net_max_rate'] * 1000,
154 self.rspec['net_i2_min_rate'] * 1000,
155 self.rspec['net_i2_max_rate'] * 1000,
156 self.rspec['net_share'])
157 logger.log('%s: setting net limits to %s bps' % (self.name, net_limits[:-1]))
158 logger.log('%s: setting net share to %d' % (self.name, net_limits[-1]))
159 self.set_bwlimit(*net_limits)
161 cpu_min = self.rspec['cpu_min']
162 cpu_share = self.rspec['cpu_share']
164 if self.rspec['enabled'] > 0:
165 if cpu_min >= 50: # at least 5%: keep people from shooting themselves in the foot
166 logger.log('%s: setting cpu share to %d%% guaranteed' % (self.name, cpu_min/10.0))
167 self.set_sched_config(cpu_min, vserver.SCHED_CPU_GUARANTEED)
169 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
170 self.set_sched_config(cpu_share, 0)
172 if self.rspec['ip_addresses'] != '0.0.0.0':
173 logger.log('%s: setting IP address(es) to %s' % (self.name, self.rspec['ip_addresses']))
174 self.set_ipaddresses_config(self.rspec['ip_addresses'])
176 if False: # Does not work properly yet.
177 if self.have_limits_changed():
178 logger.log('%s: limits have changed --- restarting' % self.name)
180 while self.is_running() and stopcount > 0:
184 stopcount = stopcount - 1
187 else: # tell vsh to disable remote login by setting CPULIMIT to 0
188 logger.log('%s: disabling remote login' % self.name)
189 self.set_sched_config(0, 0)