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.
28 # special constant that tells vserver to keep its existing settings
29 KEEP_LIMIT = vserver.VC_LIM_KEEP
31 # populate the sliver/vserver specific default allocations table,
32 # which is used to look for slice attributes
33 DEFAULT_ALLOCATION = {}
34 for rlimit in vserver.RLIMITS.keys():
36 DEFAULT_ALLOCATION["%s_min"%rlim]=KEEP_LIMIT
37 DEFAULT_ALLOCATION["%s_soft"%rlim]=KEEP_LIMIT
38 DEFAULT_ALLOCATION["%s_hard"%rlim]=KEEP_LIMIT
40 class Sliver_VS(accounts.Account, vserver.VServer):
41 """This class wraps vserver.VServer to make its interface closer to what we need."""
44 TYPE = 'sliver.VServer'
45 _init_disk_info_sem = tools.NMLock("/var/run/nm-disk-info.lock")
47 def __init__(self, rec):
48 logger.verbose ('initing Sliver_VS with name=%s'%rec['name'])
50 vserver.VServer.__init__(self, rec['name'],logfile='/var/log/nm')
51 except Exception, err:
52 if not isinstance(err, vserver.NoSuchVServer):
53 # Probably a bad vserver or vserver configuration file
54 logger.log_exc(self.name)
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'],logfile='/var/log/nm')
63 self.disk_usage_initialized = False
64 self.initscriptchanged = False
68 def create(name, vref = None):
69 logger.verbose('Sliver_VS:create - name=%s'%name)
73 ### locating the right slicefamily
74 # this is a first draft, and more a proof of concept thing
75 # the idea is to parse vref for dash-separated wishes,
76 # and to project these against the defaults
77 # however for cases like when vref is 'planetflow', if we do not understand
78 # any of the wishes we take vref as is
79 # this could be improved by having the vserver-reference init script be a bit smarter
80 # so we could take planetflow as the pldistro part here
83 default=file("/etc/planetlab/defaultvref").read().strip()
84 (pldistro,fcdistro,arch) = default.split("-")
85 # from the slice attribute: cut dashes and try to figure the meaning
86 slice_wishes = vref.split("-")
87 for wish in slice_wishes:
88 if wish == "i386" or wish == "x86_64":
90 elif wish == "planetlab" or wish == "onelab" or wish == "vini":
92 elif wish == "f8" or wish == "centos5" :
95 # if we find something like e.g. planetflow, use it as-is
101 refname="-".join( (pldistro,fcdistro,arch) )
102 # check the templates exists -- there's probably a better way..
103 if not os.path.isdir ("/vservers/.vref/%s"%refname):
104 logger.verbose("%s (%s) : vref %s not found, using default %s"%(
105 name,vref,refname,default))
108 # have not found defaultvref
109 logger.verbose("%s (%s): legacy node - using fallback vrefname 'default'"%(name,vref))
114 logger.log("%s (%s) : unexpected error follows - using 'default'"%(
116 logger.log(traceback.format_exc())
119 logger.log_call('/usr/sbin/vuseradd', '-t', refname, name)
120 open('/vservers/%s/etc/slicename' % name, 'w').write(name)
123 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
125 def configure(self, rec):
126 new_rspec = rec['_rspec']
127 if new_rspec != self.rspec:
128 self.rspec = new_rspec
131 new_initscript = rec['initscript']
132 if new_initscript != self.initscript:
133 self.initscript = new_initscript
134 logger.log('%s: installing initscript' % self.name)
135 def install_initscript():
136 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
137 fd = os.open('/etc/rc.vinit', flags, 0755)
138 os.write(fd, new_initscript)
141 self.chroot_call(install_initscript)
142 self.initscriptchanged = True
143 except: logger.log_exc(self.name)
145 accounts.Account.configure(self, rec) # install ssh keys
147 def start(self, delay=0):
148 if self.rspec['enabled'] > 0:
149 logger.log('%s: starting in %d seconds' % (self.name, delay))
151 child_pid = os.fork()
153 # VServer.start calls fork() internally, so just close the nonstandard fds and fork once to avoid creating zombies
154 tools.close_nonstandard_fds()
155 vserver.VServer.start(self, True)
157 else: os.waitpid(child_pid, 0)
158 else: logger.log('%s: not starting, is not enabled' % self.name)
159 self.initscriptchanged = False
162 logger.log('%s: stopping' % self.name)
163 vserver.VServer.stop(self)
165 def set_resources(self):
166 disk_max = self.rspec['disk_max']
167 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
168 try: # if the sliver is over quota, .set_disk_limit will throw an exception
169 if not self.disk_usage_initialized:
170 self.vm_running = False
171 logger.log('%s: computing disk usage: beginning' % self.name)
172 Sliver_VS._init_disk_info_sem.acquire()
173 try: self.init_disk_info()
174 finally: Sliver_VS._init_disk_info_sem.release()
175 logger.log('%s: computing disk usage: ended' % self.name)
176 self.disk_usage_initialized = True
177 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
179 logger.log('%s: failed to set max disk usage' % self.name)
180 logger.log_exc(self.name)
182 # get/set the min/soft/hard values for all of the vserver
183 # related RLIMITS. Note that vserver currently only
184 # implements support for hard limits.
185 for limit in vserver.RLIMITS.keys():
187 minimum = self.rspec['%s_min'%type]
188 soft = self.rspec['%s_soft'%type]
189 hard = self.rspec['%s_hard'%type]
190 update = self.set_rlimit(limit, hard, soft, minimum)
192 logger.log('%s: setting rlimit %s to (%d, %d, %d)'
193 % (self.name, type, hard, soft, minimum))
195 self.set_capabilities_config(self.rspec['capabilities'])
196 if self.rspec['capabilities']:
197 logger.log('%s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
199 if False: # this code was commented out before
200 # N.B. net_*_rate are in kbps because of XML-RPC maxint
201 # limitations, convert to bps which is what bwlimit.py expects.
202 net_limits = (self.rspec['net_min_rate'] * 1000,
203 self.rspec['net_max_rate'] * 1000,
204 self.rspec['net_i2_min_rate'] * 1000,
205 self.rspec['net_i2_max_rate'] * 1000,
206 self.rspec['net_share'])
207 logger.log('%s: setting net limits to %s bps' % (self.name, net_limits[:-1]))
208 logger.log('%s: setting net share to %d' % (self.name, net_limits[-1]))
209 self.set_bwlimit(*net_limits)
211 cpu_pct = self.rspec['cpu_pct']
212 cpu_share = self.rspec['cpu_share']
214 if self.rspec['enabled'] > 0:
216 logger.log('%s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
221 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
225 self.set_sched_config(cpu_pct, cpu_share)
226 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
227 if self.rspec['ip_addresses'] != '0.0.0.0':
228 logger.log('%s: setting IP address(es) to %s' % (self.name, self.rspec['ip_addresses']))
229 self.set_ipaddresses_config(self.rspec['ip_addresses'])
231 if False: # Does not work properly yet.
232 if self.have_limits_changed():
233 logger.log('%s: limits have changed --- restarting' % self.name)
235 while self.is_running() and stopcount > 0:
239 stopcount = stopcount - 1
242 else: # tell vsh to disable remote login by setting CPULIMIT to 0
243 logger.log('%s: disabling remote login' % self.name)
244 self.set_sched_config(0, 0)