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 from threading import BoundedSemaphore
31 globalsem = BoundedSemaphore()
33 # special constant that tells vserver to keep its existing settings
34 KEEP_LIMIT = vserver.VC_LIM_KEEP
36 # populate the sliver/vserver specific default allocations table,
37 # which is used to look for slice attributes
38 DEFAULT_ALLOCATION = {}
39 for rlimit in vserver.RLIMITS.keys():
41 DEFAULT_ALLOCATION["%s_min"%rlim]=KEEP_LIMIT
42 DEFAULT_ALLOCATION["%s_soft"%rlim]=KEEP_LIMIT
43 DEFAULT_ALLOCATION["%s_hard"%rlim]=KEEP_LIMIT
45 class Sliver_VS(accounts.Account, vserver.VServer):
46 """This class wraps vserver.VServer to make its interface closer to what we need."""
49 TYPE = 'sliver.VServer'
50 _init_disk_info_sem = globalsem
52 def __init__(self, rec):
53 logger.verbose ('initing Sliver_VS with name=%s'%rec['name'])
55 vserver.VServer.__init__(self, rec['name'],logfile='/var/log/nm')
56 except Exception, err:
57 if not isinstance(err, vserver.NoSuchVServer):
58 # Probably a bad vserver or vserver configuration file
59 logger.log_exc(self.name)
60 logger.log('%s: recreating bad vserver' % rec['name'])
61 self.destroy(rec['name'])
62 self.create(rec['name'], rec['vref'])
63 vserver.VServer.__init__(self, rec['name'],logfile='/var/log/nm')
68 self.slice_id = rec['slice_id']
69 self.disk_usage_initialized = False
70 self.initscriptchanged = False
75 def create(name, vref = None):
76 logger.verbose('Sliver_VS:create - name=%s'%name)
80 ### locating the right slicefamily
81 # this is a first draft, and more a proof of concept thing
82 # the idea is to parse vref for dash-separated wishes,
83 # and to project these against the defaults
84 # so e.g. if the default slice family (as found in /etc/planetlab/slicefamily)
85 # is planetlab-f8-i386, then here is what we get
86 # vref=x86_64 -> vuseradd -t planetlab-f8-x86_64
87 # vref=centos5 -> vuseradd -t planetlab-centos5-i386
88 # vref=centos5-onelab -> vuseradd -t onelab-centos5-i386
89 # vref=planetflow -> vuseradd -t planetflow-f8-i386
90 # vref=x86_64-planetflow -> vuseradd -t planetflow-f8-x86_64
93 default=file("/etc/planetlab/slicefamily").read().strip()
94 (pldistro,fcdistro,arch) = default.split("-")
96 known_archs = [ 'i386', 'x86_64' ]
97 known_fcdistros = [ 'centos5', 'f8', 'f9', 'f10', 'f11', 'f12' ]
98 # from the slice attribute: cut dashes and try to figure the meaning
99 slice_wishes = vref.split("-")
100 for wish in slice_wishes:
101 if wish in known_archs:
103 elif wish in known_fcdistros:
109 refname="-".join( (pldistro,fcdistro,arch) )
111 # check the template exists -- there's probably a better way..
112 if not os.path.isdir ("/vservers/.vref/%s"%refname):
113 logger.log("%s (%s) : vref %s not found, using default %s"%(
114 name,vref,refname,default))
116 # reset so arch is right
117 (pldistro,fcdistro,arch) = default.split("-")
118 # could check again, but as we have /etc/slicefamily
119 # there's probably no /vservers/.vref/default
122 # have not found slicefamily
123 logger.log("%s (%s): legacy node - using fallback vrefname 'default'"%(name,vref))
128 logger.log("%s (%s) : unexpected error follows - using 'default'"%(name,vref))
129 logger.log(traceback.format_exc())
133 def personality (arch):
134 personality="linux32"
135 if arch.find("64")>=0:
136 personality="linux64"
139 logger.log_call('/usr/sbin/vuseradd', '-t', refname, name)
140 # export slicename to the slice in /etc/slicename
141 file('/vservers/%s/etc/slicename' % name, 'w').write(name)
142 # set personality: only if needed (if arch's differ)
143 if tools.root_context_arch() != arch:
144 file('/etc/vservers/%s/personality' % name, 'w').write(personality(arch))
145 logger.log('%s: set personality to %s'%(name,personality(arch)))
148 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
150 def configure(self, rec):
151 new_rspec = rec['_rspec']
152 if new_rspec != self.rspec:
153 self.rspec = new_rspec
156 new_initscript = rec['initscript']
157 if new_initscript != self.initscript:
158 self.initscript = new_initscript
159 # not used anymore, we always check against the installed script
160 #self.initscriptchanged = True
161 self.refresh_slice_vinit()
163 accounts.Account.configure(self, rec) # install ssh keys
165 # unconditionnally install and enable the generic vinit script
166 # mimicking chkconfig for enabling the generic vinit script
167 # this is hardwired for runlevel 3
168 def install_and_enable_vinit (self):
169 vinit_source="/usr/share/NodeManager/sliver-initscripts/vinit"
170 vinit_script="/vservers/%s/etc/rc.d/init.d/vinit"%self.name
171 rc3_link="/vservers/%s/etc/rc.d/rc3.d/S99vinit"%self.name
172 rc3_target="../init.d/vinit"
174 body=file(vinit_source).read()
175 if tools.replace_file_with_string(vinit_script,body,chmod=0755):
176 logger.log("vsliver_vs: %s: installed generic vinit rc script"%self.name)
177 # create symlink for runlevel 3
178 if not os.path.islink(rc3_link):
180 logger.log("vsliver_vs: %s: creating runlevel3 symlink %s"%(self.name,rc3_link))
181 os.symlink(rc3_target,rc3_link)
183 logger.log_exc("vsliver_vs: %s: failed to create runlevel3 symlink %s"%rc3_link)
185 # this one checks for the existence of the slice initscript
186 # install or remove the slice inistscript, as instructed by the initscript tag
187 def refresh_slice_vinit(self):
189 sliver_initscript="/vservers/%s/etc/rc.d/init.d/vinit.slice"%self.name
190 if tools.replace_file_with_string(sliver_initscript,body,remove_if_empty=True,chmod=0755):
192 logger.log("vsliver_vs: %s: Installed new initscript in %s"%(self.name,sliver_initscript))
194 logger.log("vsliver_vs: %s: Removed obsolete initscript %s"%(self.name,sliver_initscript))
196 def start(self, delay=0):
197 if self.rspec['enabled'] <= 0:
198 logger.log('sliver_vs: not starting %s, is not enabled'%self.name)
200 logger.log('sliver_vs: %s: starting in %d seconds' % (self.name, delay))
202 # the generic /etc/init.d/vinit script is permanently refreshed, and enabled
203 self.install_and_enable_vinit()
204 # if a change has occured in the slice initscript, reflect this in /etc/init.d/vinit.slice
205 self.refresh_slice_vinit()
206 child_pid = os.fork()
208 # VServer.start calls fork() internally,
209 # so just close the nonstandard fds and fork once to avoid creating zombies
210 tools.close_nonstandard_fds()
211 vserver.VServer.start(self)
214 os.waitpid(child_pid, 0)
217 logger.log('%s: stopping' % self.name)
218 vserver.VServer.stop(self)
220 def is_running(self):
221 return vserver.VServer.is_running(self)
223 def set_resources(self,setup=False):
224 disk_max = self.rspec['disk_max']
225 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
226 try: # if the sliver is over quota, .set_disk_limit will throw an exception
227 if not self.disk_usage_initialized:
228 self.vm_running = False
229 Sliver_VS._init_disk_info_sem.acquire()
230 logger.log('%s: computing disk usage: beginning' % self.name)
231 try: self.init_disk_info()
232 finally: Sliver_VS._init_disk_info_sem.release()
233 logger.log('%s: computing disk usage: ended' % self.name)
234 self.disk_usage_initialized = True
235 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
237 logger.log('%s: failed to set max disk usage' % self.name)
238 logger.log_exc(self.name)
240 # get/set the min/soft/hard values for all of the vserver
241 # related RLIMITS. Note that vserver currently only
242 # implements support for hard limits.
243 for limit in vserver.RLIMITS.keys():
245 minimum = self.rspec['%s_min'%type]
246 soft = self.rspec['%s_soft'%type]
247 hard = self.rspec['%s_hard'%type]
248 update = self.set_rlimit(limit, hard, soft, minimum)
250 logger.log('%s: setting rlimit %s to (%d, %d, %d)'
251 % (self.name, type, hard, soft, minimum))
253 self.set_capabilities_config(self.rspec['capabilities'])
254 if self.rspec['capabilities']:
255 logger.log('%s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
257 cpu_pct = self.rspec['cpu_pct']
258 cpu_share = self.rspec['cpu_share']
261 for key in self.rspec.keys():
262 if key.find('sysctl.') == 0:
263 sysctl=key.split('.')
265 path="/proc/sys/%s" % ("/".join(sysctl[1:]))
266 logger.log("%s: opening %s"%(self.name,path))
268 fd = os.open(path, flags)
269 logger.log("%s: writing %s=%s"%(self.name,key,self.rspec[key]))
270 os.write(fd,self.rspec[key])
273 logger.log("%s: could not set %s=%s"%(self.name,key,self.rspec[key]))
274 logger.log("%s: error = %s"%(self.name,e))
277 if self.rspec['enabled'] > 0:
279 logger.log('%s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
284 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
288 self.set_sched_config(cpu_pct, cpu_share)
289 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
290 if self.rspec['ip_addresses'] != '0.0.0.0':
291 logger.log('%s: setting IP address(es) to %s' % \
292 (self.name, self.rspec['ip_addresses']))
293 self.set_ipaddresses_config(self.rspec['ip_addresses'])
296 vserver_config_path = '/etc/vservers/%s'%self.name
297 if not os.path.exists (vserver_config_path):
298 os.makedirs (vserver_config_path)
299 file('%s/slice_id'%vserver_config_path, 'w').write("%d"%self.slice_id)
300 logger.log("sliver_vs: Recorded slice id %d for slice %s"%(self.slice_id,self.name))
302 logger.log("sliver_vs: Could not record slice_id for slice %s. Error: %s"%(self.name,str(e)))
304 logger.log_exc("sliver_vs: Error recording slice id: %s"%str(e),name=self.name)
306 if self.enabled == False:
310 if False: # Does not work properly yet.
311 if self.have_limits_changed():
312 logger.log('%s: limits have changed --- restarting' % self.name)
314 while self.is_running() and stopcount > 0:
318 stopcount = stopcount - 1
321 else: # tell vsh to disable remote login by setting CPULIMIT to 0
322 logger.log('%s: disabling remote login' % self.name)
323 self.set_sched_config(0, 0)