6 There are a couple of tricky things going on here. First, the kernel
7 needs disk usage information in order to enforce the quota. However,
8 determining disk usage redundantly strains the disks. Thus, the
9 Sliver_VS.disk_usage_initialized flag is used to determine whether
10 this initialization has been made.
12 Second, it's not currently possible to set the scheduler parameters
13 for a sliver unless that sliver has a running process. /bin/vsh helps
14 us out by reading the configuration file so that it can set the
15 appropriate limits after entering the sliver context. Making the
16 syscall that actually sets the parameters gives a harmless error if no
17 process is running. Thus we keep vm_running on when setting scheduler
18 parameters so that set_sched_params() always makes the syscall, and we
19 don't have to guess if there is a running process or not.
26 from threading import BoundedSemaphore
28 # the util-vserver-pl module
35 # special constant that tells vserver to keep its existing settings
36 KEEP_LIMIT = vserver.VC_LIM_KEEP
38 # populate the sliver/vserver specific default allocations table,
39 # which is used to look for slice attributes
40 DEFAULT_ALLOCATION = {}
41 for rlimit in vserver.RLIMITS.keys():
43 DEFAULT_ALLOCATION["%s_min"%rlim]=KEEP_LIMIT
44 DEFAULT_ALLOCATION["%s_soft"%rlim]=KEEP_LIMIT
45 DEFAULT_ALLOCATION["%s_hard"%rlim]=KEEP_LIMIT
47 class Sliver_VS(accounts.Account, vserver.VServer):
48 """This class wraps vserver.VServer to make its interface closer to what we need."""
51 TYPE = 'sliver.VServer'
52 _init_disk_info_sem = BoundedSemaphore()
54 def __init__(self, rec):
56 logger.verbose ('sliver_vs: %s init'%name)
58 logger.log("sliver_vs: %s: first chance..."%name)
59 vserver.VServer.__init__(self, name,logfile='/var/log/nodemanager')
60 except Exception, err:
61 if not isinstance(err, vserver.NoSuchVServer):
62 # Probably a bad vserver or vserver configuration file
63 logger.log_exc("sliver_vs:__init__ (first chance) %s",name=name)
64 logger.log('sliver_vs: %s: recreating bad vserver' % name)
66 self.create(name, rec['vref'])
67 logger.log("sliver_vs: %s: second chance..."%name)
68 vserver.VServer.__init__(self, name,logfile='/var/log/nodemanager')
73 self.slice_id = rec['slice_id']
74 self.disk_usage_initialized = False
75 self.initscriptchanged = False
80 def create(name, vref = None):
81 logger.verbose('sliver_vs: %s: create'%name)
83 logger.log("sliver_vs: %s: ERROR - no vref attached, this is unexpected"%(name))
85 # used to look in /etc/planetlab/family,
86 # now relies on the 'GetSliceFamily' extra attribute in GetSlivers()
87 # which for legacy is still exposed here as the 'vref' key
89 # check the template exists -- there's probably a better way..
90 if not os.path.isdir ("/vservers/.vref/%s"%vref):
91 logger.log ("sliver_vs: %s: ERROR Could not create sliver - vreference image %s not found"%(name,vref))
96 (x,y,arch)=vref.split('-')
97 # mh, this of course applies when 'vref' is e.g. 'netflow'
98 # and that's not quite right
102 def personality (arch):
103 personality="linux32"
104 if arch.find("64")>=0:
105 personality="linux64"
108 # logger.log_call(['/usr/sbin/vuseradd', '-t', vref, name, ], timeout=15*60)
109 logger.log_call(['/bin/bash','-x','/usr/sbin/vuseradd', '-t', vref, name, ], timeout=15*60)
110 # export slicename to the slice in /etc/slicename
111 file('/vservers/%s/etc/slicename' % name, 'w').write(name)
112 file('/vservers/%s/etc/slicefamily' % name, 'w').write(vref)
113 # set personality: only if needed (if arch's differ)
114 if tools.root_context_arch() != arch:
115 file('/etc/vservers/%s/personality' % name, 'w').write(personality(arch)+"\n")
116 logger.log('sliver_vs: %s: set personality to %s'%(name,personality(arch)))
120 # logger.log_call(['/usr/sbin/vuserdel', name, ])
121 logger.log_call(['/bin/bash','-x','/usr/sbin/vuserdel', name, ])
123 def configure(self, rec):
124 new_rspec = rec['_rspec']
125 if new_rspec != self.rspec:
126 self.rspec = new_rspec
129 new_initscript = rec['initscript']
130 if new_initscript != self.initscript:
131 self.initscript = new_initscript
132 self.initscriptchanged = True
134 accounts.Account.configure(self, rec) # install ssh keys
136 # mimicking chkconfig for enabling the generic vinit script
137 # this is hardwired for runlevel 3
138 def install_and_enable_vinit (self):
139 vinit_source="/usr/share/NodeManager/sliver-initscripts/vinit"
140 vinit_script="/vservers/%s/etc/rc.d/init.d/vinit"%self.name
141 rc3_link="/vservers/%s/etc/rc.d/rc3.d/S99vinit"%self.name
142 rc3_target="../init.d/vinit"
145 logger.log("vsliver_vs: %s: installing generic vinit rc script"%self.name)
146 body=file(vinit_source).read()
147 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
148 fd = os.open(vinit_script, flags, 0755)
152 logger.log_exc("vsliver_vs: %s: could not install generic vinit script"%self.name)
153 # create symlink for runlevel 3
154 if not os.path.islink(rc3_link):
156 logger.log("vsliver_vs: %s: installing generic vinit rc script"%self.name)
157 os.symlink(rc3_target,rc3_link)
159 logger.log_exc("vsliver_vs: %s: failed to install runlevel3 link")
162 def start(self, delay=0):
163 if self.rspec['enabled'] <= 0:
164 logger.log('sliver_vs: not starting %s, is not enabled'%self.name)
166 logger.log('sliver_vs: %s: starting in %d seconds' % (self.name, delay))
168 # VServer.start calls fork() internally,
169 # so just close the nonstandard fds and fork once to avoid creating zombies
170 child_pid = os.fork()
172 if self.initscriptchanged:
173 # unconditionnally install and enable the generic vinit script
174 # this one checks for the existence of the slice initscript
175 self.install_and_enable_vinit()
176 # install or remove the slice inistscript, as instructed by the initscript tag
177 sliver_initscript="/vservers/%s/etc/rc.d/init.d/vinit.slice"%self.name
178 if not self.initscript:
179 logger.log("sliver_vs: %s: unlinking initscript %s"%(self.name,sliver_initscript))
180 os.unlink(sliver_initscript)
182 logger.log("sliver_vs: %s: installing new initscript %s"%(self.name,sliver_initscript))
184 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
185 fd = os.open(sliver_initscript, flags, 0755)
186 os.write(fd, self.initscript)
189 logger.log_exc("sliver_vs: %s - could not install initscript"%self.name)
190 tools.close_nonstandard_fds()
191 vserver.VServer.start(self)
194 os.waitpid(child_pid, 0)
195 self.initscriptchanged = False
198 logger.log('sliver_vs: %s: stopping' % self.name)
199 vserver.VServer.stop(self)
201 def is_running(self):
202 return vserver.VServer.is_running(self)
204 def set_resources(self,setup=False):
205 disk_max = self.rspec['disk_max']
206 logger.log('sliver_vs: %s: setting max disk usage to %d KiB' % (self.name, disk_max))
207 try: # if the sliver is over quota, .set_disk_limit will throw an exception
208 if not self.disk_usage_initialized:
209 self.vm_running = False
210 Sliver_VS._init_disk_info_sem.acquire()
211 logger.log('sliver_vs: %s: computing disk usage: beginning' % self.name)
212 # init_disk_info is inherited from VServer
213 try: self.init_disk_info()
214 finally: Sliver_VS._init_disk_info_sem.release()
215 logger.log('sliver_vs: %s: computing disk usage: ended' % self.name)
216 self.disk_usage_initialized = True
217 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
219 logger.log_exc('sliver_vs: failed to set max disk usage',name=self.name)
221 # get/set the min/soft/hard values for all of the vserver
222 # related RLIMITS. Note that vserver currently only
223 # implements support for hard limits.
224 for limit in vserver.RLIMITS.keys():
226 minimum = self.rspec['%s_min'%type]
227 soft = self.rspec['%s_soft'%type]
228 hard = self.rspec['%s_hard'%type]
229 update = self.set_rlimit(limit, hard, soft, minimum)
231 logger.log('sliver_vs: %s: setting rlimit %s to (%d, %d, %d)'
232 % (self.name, type, hard, soft, minimum))
234 self.set_capabilities_config(self.rspec['capabilities'])
235 if self.rspec['capabilities']:
236 logger.log('sliver_vs: %s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
238 cpu_pct = self.rspec['cpu_pct']
239 cpu_share = self.rspec['cpu_share']
242 for key in self.rspec.keys():
243 if key.find('sysctl.') == 0:
244 sysctl=key.split('.')
246 path="/proc/sys/%s" % ("/".join(sysctl[1:]))
247 logger.log("sliver_vs: %s: opening %s"%(self.name,path))
249 fd = os.open(path, flags)
250 logger.log("sliver_vs: %s: writing %s=%s"%(self.name,key,self.rspec[key]))
251 os.write(fd,self.rspec[key])
254 logger.log("sliver_vs: %s: could not set %s=%s"%(self.name,key,self.rspec[key]))
255 logger.log("sliver_vs: %s: error = %s"%(self.name,e))
258 if self.rspec['enabled'] > 0:
260 logger.log('sliver_vs: %s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
265 logger.log('sliver_vs: %s: setting cpu share to %d' % (self.name, cpu_share))
269 self.set_sched_config(cpu_pct, cpu_share)
270 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
271 if self.rspec['ip_addresses'] != '0.0.0.0':
272 logger.log('sliver_vs: %s: setting IP address(es) to %s' % \
273 (self.name, self.rspec['ip_addresses']))
274 self.set_ipaddresses_config(self.rspec['ip_addresses'])
276 #logger.log("sliver_vs: %s: Setting name to %s" % (self.name, self.slice_id))
277 #self.setname(self.slice_id)
278 #logger.log("sliver_vs: %s: Storing slice id of %s for PlanetFlow" % (self.name, self.slice_id))
280 vserver_config_path = '/etc/vservers/%s'%self.name
281 if not os.path.exists (vserver_config_path):
282 os.makedirs (vserver_config_path)
283 file('%s/slice_id'%vserver_config_path, 'w').write("%d\n"%self.slice_id)
284 logger.log("sliver_vs: Recorded slice id %d for slice %s"%(self.slice_id,self.name))
286 logger.log("sliver_vs: Could not record slice_id for slice %s. Error: %s"%(self.name,str(e)))
288 logger.log_exc("sliver_vs: Error recording slice id: %s"%str(e),name=self.name)
291 if self.enabled == False:
295 if False: # Does not work properly yet.
296 if self.have_limits_changed():
297 logger.log('sliver_vs: %s: limits have changed --- restarting' % self.name)
299 while self.is_running() and stopcount > 0:
303 stopcount = stopcount - 1
306 else: # tell vsh to disable remote login by setting CPULIMIT to 0
307 logger.log('sliver_vs: %s: disabling remote login' % self.name)
308 self.set_sched_config(0, 0)