5 There are a couple of tricky things going on here. First, the kernel
6 needs disk usage information in order to enforce the quota. However,
7 determining disk usage redundantly strains the disks. Thus, the
8 Sliver_VS.disk_usage_initialized flag is used to determine whether
9 this initialization has been made.
11 Second, it's not currently possible to set the scheduler parameters
12 for a sliver unless that sliver has a running process. /bin/vsh helps
13 us out by reading the configuration file so that it can set the
14 appropriate limits after entering the sliver context. Making the
15 syscall that actually sets the parameters gives a harmless error if no
16 process is running. Thus we keep vm_running on when setting scheduler
17 parameters so that set_sched_params() always makes the syscall, and we
18 don't have to guess if there is a running process or not.
25 from threading import BoundedSemaphore
27 # the util-vserver-pl module
34 # special constant that tells vserver to keep its existing settings
35 KEEP_LIMIT = vserver.VC_LIM_KEEP
37 # populate the sliver/vserver specific default allocations table,
38 # which is used to look for slice attributes
39 DEFAULT_ALLOCATION = {}
40 for rlimit in vserver.RLIMITS.keys():
42 DEFAULT_ALLOCATION["%s_min"%rlim]=KEEP_LIMIT
43 DEFAULT_ALLOCATION["%s_soft"%rlim]=KEEP_LIMIT
44 DEFAULT_ALLOCATION["%s_hard"%rlim]=KEEP_LIMIT
46 class Sliver_VS(accounts.Account, vserver.VServer):
47 """This class wraps vserver.VServer to make its interface closer to what we need."""
50 TYPE = 'sliver.VServer'
51 _init_disk_info_sem = BoundedSemaphore()
53 def __init__(self, rec):
55 logger.verbose ('sliver_vs: %s init'%name)
57 logger.log("sliver_vs: %s: first chance..."%name)
58 vserver.VServer.__init__(self, name,logfile='/var/log/nodemanager')
59 except Exception, err:
60 if not isinstance(err, vserver.NoSuchVServer):
61 # Probably a bad vserver or vserver configuration file
62 logger.log_exc("sliver_vs:__init__ (first chance) %s",name=name)
63 logger.log('sliver_vs: %s: recreating bad vserver' % name)
65 self.create(name, rec['vref'])
66 logger.log("sliver_vs: %s: second chance..."%name)
67 vserver.VServer.__init__(self, name,logfile='/var/log/nodemanager')
71 self.slice_id = rec['slice_id']
72 self.disk_usage_initialized = False
78 def create(name, vref = None):
79 logger.verbose('sliver_vs: %s: create'%name)
81 logger.log("sliver_vs: %s: ERROR - no vref attached, this is unexpected"%(name))
83 # band-aid for short period as old API doesn't have GetSliceFamily function
85 vref = "planetlab-f8-i386"
87 # used to look in /etc/planetlab/family,
88 # now relies on the 'GetSliceFamily' extra attribute in GetSlivers()
89 # which for legacy is still exposed here as the 'vref' key
91 # check the template exists -- there's probably a better way..
92 if not os.path.isdir ("/vservers/.vref/%s"%vref):
93 logger.log ("sliver_vs: %s: ERROR Could not create sliver - vreference image %s not found"%(name,vref))
98 (x,y,arch)=vref.split('-')
99 # mh, this of course applies when 'vref' is e.g. 'netflow'
100 # and that's not quite right
104 def personality (arch):
105 personality="linux32"
106 if arch.find("64")>=0:
107 personality="linux64"
110 # logger.log_call(['/usr/sbin/vuseradd', '-t', vref, name, ], timeout=15*60)
111 logger.log_call(['/bin/bash','-x','/usr/sbin/vuseradd', '-t', vref, name, ], timeout=15*60)
112 # export slicename to the slice in /etc/slicename
113 file('/vservers/%s/etc/slicename' % name, 'w').write(name)
114 file('/vservers/%s/etc/slicefamily' % name, 'w').write(vref)
115 # set personality: only if needed (if arch's differ)
116 if tools.root_context_arch() != arch:
117 file('/etc/vservers/%s/personality' % name, 'w').write(personality(arch)+"\n")
118 logger.log('sliver_vs: %s: set personality to %s'%(name,personality(arch)))
122 # logger.log_call(['/usr/sbin/vuserdel', name, ])
123 logger.log_call(['/bin/bash','-x','/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 # not used anymore, we always check against the installed script
135 #self.initscriptchanged = True
136 self.refresh_slice_vinit()
138 accounts.Account.configure(self, rec) # install ssh keys
140 # unconditionnally install and enable the generic vinit script
141 # mimicking chkconfig for enabling the generic vinit script
142 # this is hardwired for runlevel 3
143 def install_and_enable_vinit (self):
144 vinit_source="/usr/share/NodeManager/sliver-initscripts/vinit"
145 vinit_script="/vservers/%s/etc/rc.d/init.d/vinit"%self.name
146 rc3_link="/vservers/%s/etc/rc.d/rc3.d/S99vinit"%self.name
147 rc3_target="../init.d/vinit"
149 body=file(vinit_source).read()
150 if tools.replace_file_with_string(vinit_script,body,chmod=0755):
151 logger.log("vsliver_vs: %s: installed generic vinit rc script"%self.name)
152 # create symlink for runlevel 3
153 if not os.path.islink(rc3_link):
155 logger.log("vsliver_vs: %s: creating runlevel3 symlink %s"%(self.name,rc3_link))
156 os.symlink(rc3_target,rc3_link)
158 logger.log_exc("vsliver_vs: %s: failed to create runlevel3 symlink %s"%rc3_link)
160 # this one checks for the existence of the slice initscript
161 # install or remove the slice inistscript, as instructed by the initscript tag
162 def refresh_slice_vinit(self):
164 sliver_initscript="/vservers/%s/etc/rc.d/init.d/vinit.slice"%self.name
165 if tools.replace_file_with_string(sliver_initscript,body,remove_if_empty=True,chmod=0755):
167 logger.log("vsliver_vs: %s: Installed new initscript in %s"%(self.name,sliver_initscript))
169 logger.log("vsliver_vs: %s: Removed obsolete initscript %s"%(self.name,sliver_initscript))
171 # bind mount root side dir to sliver side
172 # needs to be done before sliver starts
173 def expose_ssh_dir (self):
175 root_ssh="/home/%s/.ssh"
176 sliver_ssh="/vservers/%s/home/%s/.ssh"%(self.name,self.name)
177 # any of both might not exist yet
178 for path in [root_ssh,sliver_ssh]:
179 if not os.path.exists (path):
181 if not os.path.isdir (path):
183 mounts=file('/proc/mounts').read()
184 if mounts.find(sliver_ssh)<0:
186 subprocess.call("mount --bind -o ro %s %s"%(root_ssh,sliver_ssh),shell=True)
188 logger.log("expose_ssh_dir with slice %s failed"%self.name)
190 def start(self, delay=0):
191 if self.rspec['enabled'] <= 0:
192 logger.log('sliver_vs: not starting %s, is not enabled'%self.name)
194 logger.log('sliver_vs: %s: starting in %d seconds' % (self.name, delay))
196 # the generic /etc/init.d/vinit script is permanently refreshed, and enabled
197 self.install_and_enable_vinit()
198 self.expose_ssh_dir()
199 # if a change has occured in the slice initscript, reflect this in /etc/init.d/vinit.slice
200 self.refresh_slice_vinit()
201 child_pid = os.fork()
203 # VServer.start calls fork() internally,
204 # so just close the nonstandard fds and fork once to avoid creating zombies
205 tools.close_nonstandard_fds()
206 vserver.VServer.start(self)
209 os.waitpid(child_pid, 0)
212 logger.log('sliver_vs: %s: stopping' % self.name)
213 vserver.VServer.stop(self)
215 def is_running(self):
216 return vserver.VServer.is_running(self)
218 def set_resources(self):
219 disk_max = self.rspec['disk_max']
220 logger.log('sliver_vs: %s: setting max disk usage to %d KiB' % (self.name, disk_max))
221 try: # if the sliver is over quota, .set_disk_limit will throw an exception
222 if not self.disk_usage_initialized:
223 self.vm_running = False
224 Sliver_VS._init_disk_info_sem.acquire()
225 logger.log('sliver_vs: %s: computing disk usage: beginning' % self.name)
226 # init_disk_info is inherited from VServer
227 try: self.init_disk_info()
228 finally: Sliver_VS._init_disk_info_sem.release()
229 logger.log('sliver_vs: %s: computing disk usage: ended' % self.name)
230 self.disk_usage_initialized = True
231 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
233 logger.log_exc('sliver_vs: failed to set max disk usage',name=self.name)
235 # get/set the min/soft/hard values for all of the vserver
236 # related RLIMITS. Note that vserver currently only
237 # implements support for hard limits.
238 for limit in vserver.RLIMITS.keys():
240 minimum = self.rspec['%s_min'%type]
241 soft = self.rspec['%s_soft'%type]
242 hard = self.rspec['%s_hard'%type]
243 update = self.set_rlimit(limit, hard, soft, minimum)
245 logger.log('sliver_vs: %s: setting rlimit %s to (%d, %d, %d)'
246 % (self.name, type, hard, soft, minimum))
248 self.set_capabilities_config(self.rspec['capabilities'])
249 if self.rspec['capabilities']:
250 logger.log('sliver_vs: %s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
252 cpu_pct = self.rspec['cpu_pct']
253 cpu_share = self.rspec['cpu_share']
256 for key in self.rspec.keys():
257 if key.find('sysctl.') == 0:
258 sysctl=key.split('.')
260 # /etc/vservers/<guest>/sysctl/<id>/
261 dirname = "/etc/vservers/%s/sysctl/%s" % (self.name, count)
263 os.makedirs(dirname, 0755)
266 setting = open("%s/setting" % dirname, "w")
267 setting.write("%s\n" % key.lstrip("sysctl."))
269 value = open("%s/value" % dirname, "w")
270 value.write("%s\n" % self.rspec[key])
274 logger.log("sliver_vs: %s: writing %s=%s"%(self.name,key,self.rspec[key]))
276 logger.log("sliver_vs: %s: could not set %s=%s"%(self.name,key,self.rspec[key]))
277 logger.log("sliver_vs: %s: error = %s"%(self.name,e))
280 if self.rspec['enabled'] > 0:
282 logger.log('sliver_vs: %s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
287 logger.log('sliver_vs: %s: setting cpu share to %d' % (self.name, cpu_share))
291 self.set_sched_config(cpu_pct, cpu_share)
292 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
293 if self.rspec['ip_addresses'] != '0.0.0.0':
294 logger.log('sliver_vs: %s: setting IP address(es) to %s' % \
295 (self.name, self.rspec['ip_addresses']))
296 self.set_ipaddresses_config(self.rspec['ip_addresses'])
298 #logger.log("sliver_vs: %s: Setting name to %s" % (self.name, self.slice_id))
299 #self.setname(self.slice_id)
300 #logger.log("sliver_vs: %s: Storing slice id of %s for PlanetFlow" % (self.name, self.slice_id))
302 vserver_config_path = '/etc/vservers/%s'%self.name
303 if not os.path.exists (vserver_config_path):
304 os.makedirs (vserver_config_path)
305 file('%s/slice_id'%vserver_config_path, 'w').write("%d\n"%self.slice_id)
306 logger.log("sliver_vs: Recorded slice id %d for slice %s"%(self.slice_id,self.name))
308 logger.log("sliver_vs: Could not record slice_id for slice %s. Error: %s"%(self.name,str(e)))
310 logger.log_exc("sliver_vs: Error recording slice id: %s"%str(e),name=self.name)
313 if self.enabled == False:
317 if False: # Does not work properly yet.
318 if self.have_limits_changed():
319 logger.log('sliver_vs: %s: limits have changed --- restarting' % self.name)
321 while self.is_running() and stopcount > 0:
325 stopcount = stopcount - 1
328 else: # tell vsh to disable remote login by setting CPULIMIT to 0
329 logger.log('sliver_vs: %s: disabling remote login' % self.name)
330 self.set_sched_config(0, 0)