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')
72 self.slice_id = rec['slice_id']
73 self.disk_usage_initialized = False
79 def create(name, vref = None):
80 logger.verbose('sliver_vs: %s: create'%name)
82 logger.log("sliver_vs: %s: ERROR - no vref attached, this is unexpected"%(name))
84 # used to look in /etc/planetlab/family,
85 # now relies on the 'GetSliceFamily' extra attribute in GetSlivers()
86 # which for legacy is still exposed here as the 'vref' key
88 # check the template exists -- there's probably a better way..
89 if not os.path.isdir ("/vservers/.vref/%s"%vref):
90 logger.log ("sliver_vs: %s: ERROR Could not create sliver - vreference image %s not found"%(name,vref))
95 (x,y,arch)=vref.split('-')
96 # mh, this of course applies when 'vref' is e.g. 'netflow'
97 # and that's not quite right
101 def personality (arch):
102 personality="linux32"
103 if arch.find("64")>=0:
104 personality="linux64"
107 # logger.log_call(['/usr/sbin/vuseradd', '-t', vref, name, ], timeout=15*60)
108 logger.log_call(['/bin/bash','-x','/usr/sbin/vuseradd', '-t', vref, name, ], timeout=15*60)
109 # export slicename to the slice in /etc/slicename
110 file('/vservers/%s/etc/slicename' % name, 'w').write(name)
111 file('/vservers/%s/etc/slicefamily' % name, 'w').write(vref)
112 # set personality: only if needed (if arch's differ)
113 if tools.root_context_arch() != arch:
114 file('/etc/vservers/%s/personality' % name, 'w').write(personality(arch)+"\n")
115 logger.log('sliver_vs: %s: set personality to %s'%(name,personality(arch)))
119 # logger.log_call(['/usr/sbin/vuserdel', name, ])
120 logger.log_call(['/bin/bash','-x','/usr/sbin/vuserdel', name, ])
122 def configure(self, rec):
123 new_rspec = rec['_rspec']
124 if new_rspec != self.rspec:
125 self.rspec = new_rspec
128 new_initscript = rec['initscript']
129 if new_initscript != self.initscript:
130 self.initscript = new_initscript
131 # not used anymore, we always check against the installed script
132 #self.initscriptchanged = True
133 self.refresh_slice_vinit()
135 accounts.Account.configure(self, rec) # install ssh keys
137 # unconditionnally install and enable the generic vinit script
138 # mimicking chkconfig for enabling the generic vinit script
139 # this is hardwired for runlevel 3
140 def install_and_enable_vinit (self):
141 vinit_source="/usr/share/NodeManager/sliver-initscripts/vinit"
142 vinit_script="/vservers/%s/etc/rc.d/init.d/vinit"%self.name
143 rc3_link="/vservers/%s/etc/rc.d/rc3.d/S99vinit"%self.name
144 rc3_target="../init.d/vinit"
146 body=file(vinit_source).read()
147 if tools.replace_file_with_string(vinit_script,body,chmod=0755):
148 logger.log("vsliver_vs: %s: installed generic vinit rc script"%self.name)
149 # create symlink for runlevel 3
150 if not os.path.islink(rc3_link):
152 logger.log("vsliver_vs: %s: creating runlevel3 symlink %s"%(self.name,rc3_link))
153 os.symlink(rc3_target,rc3_link)
155 logger.log_exc("vsliver_vs: %s: failed to create runlevel3 symlink %s"%rc3_link)
157 # this one checks for the existence of the slice initscript
158 # install or remove the slice inistscript, as instructed by the initscript tag
159 def refresh_slice_vinit(self):
161 sliver_initscript="/vservers/%s/etc/rc.d/init.d/vinit.slice"%self.name
162 if tools.replace_file_with_string(sliver_initscript,body,remove_if_empty=True,chmod=0755):
164 logger.log("vsliver_vs: %s: Installed new initscript in %s"%(self.name,sliver_initscript))
166 logger.log("vsliver_vs: %s: Removed obsolete initscript %s"%(self.name,sliver_initscript))
168 def start(self, delay=0):
169 if self.rspec['enabled'] <= 0:
170 logger.log('sliver_vs: not starting %s, is not enabled'%self.name)
172 logger.log('sliver_vs: %s: starting in %d seconds' % (self.name, delay))
174 # the generic /etc/init.d/vinit script is permanently refreshed, and enabled
175 self.install_and_enable_vinit()
176 # if a change has occured in the slice initscript, reflect this in /etc/init.d/vinit.slice
177 self.refresh_slice_vinit()
178 child_pid = os.fork()
180 # VServer.start calls fork() internally,
181 # so just close the nonstandard fds and fork once to avoid creating zombies
182 tools.close_nonstandard_fds()
183 vserver.VServer.start(self)
186 os.waitpid(child_pid, 0)
189 logger.log('sliver_vs: %s: stopping' % self.name)
190 vserver.VServer.stop(self)
192 def is_running(self):
193 return vserver.VServer.is_running(self)
195 def set_resources(self,setup=False):
196 disk_max = self.rspec['disk_max']
197 logger.log('sliver_vs: %s: setting max disk usage to %d KiB' % (self.name, disk_max))
198 try: # if the sliver is over quota, .set_disk_limit will throw an exception
199 if not self.disk_usage_initialized:
200 self.vm_running = False
201 Sliver_VS._init_disk_info_sem.acquire()
202 logger.log('sliver_vs: %s: computing disk usage: beginning' % self.name)
203 # init_disk_info is inherited from VServer
204 try: self.init_disk_info()
205 finally: Sliver_VS._init_disk_info_sem.release()
206 logger.log('sliver_vs: %s: computing disk usage: ended' % self.name)
207 self.disk_usage_initialized = True
208 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
210 logger.log_exc('sliver_vs: failed to set max disk usage',name=self.name)
212 # get/set the min/soft/hard values for all of the vserver
213 # related RLIMITS. Note that vserver currently only
214 # implements support for hard limits.
215 for limit in vserver.RLIMITS.keys():
217 minimum = self.rspec['%s_min'%type]
218 soft = self.rspec['%s_soft'%type]
219 hard = self.rspec['%s_hard'%type]
220 update = self.set_rlimit(limit, hard, soft, minimum)
222 logger.log('sliver_vs: %s: setting rlimit %s to (%d, %d, %d)'
223 % (self.name, type, hard, soft, minimum))
225 self.set_capabilities_config(self.rspec['capabilities'])
226 if self.rspec['capabilities']:
227 logger.log('sliver_vs: %s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
229 cpu_pct = self.rspec['cpu_pct']
230 cpu_share = self.rspec['cpu_share']
233 for key in self.rspec.keys():
234 if key.find('sysctl.') == 0:
235 sysctl=key.split('.')
237 path="/proc/sys/%s" % ("/".join(sysctl[1:]))
238 logger.log("sliver_vs: %s: opening %s"%(self.name,path))
240 fd = os.open(path, flags)
241 logger.log("sliver_vs: %s: writing %s=%s"%(self.name,key,self.rspec[key]))
242 os.write(fd,self.rspec[key])
245 logger.log("sliver_vs: %s: could not set %s=%s"%(self.name,key,self.rspec[key]))
246 logger.log("sliver_vs: %s: error = %s"%(self.name,e))
249 if self.rspec['enabled'] > 0:
251 logger.log('sliver_vs: %s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
256 logger.log('sliver_vs: %s: setting cpu share to %d' % (self.name, cpu_share))
260 self.set_sched_config(cpu_pct, cpu_share)
261 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
262 if self.rspec['ip_addresses'] != '0.0.0.0':
263 logger.log('sliver_vs: %s: setting IP address(es) to %s' % \
264 (self.name, self.rspec['ip_addresses']))
265 self.set_ipaddresses_config(self.rspec['ip_addresses'])
267 #logger.log("sliver_vs: %s: Setting name to %s" % (self.name, self.slice_id))
268 #self.setname(self.slice_id)
269 #logger.log("sliver_vs: %s: Storing slice id of %s for PlanetFlow" % (self.name, self.slice_id))
271 vserver_config_path = '/etc/vservers/%s'%self.name
272 if not os.path.exists (vserver_config_path):
273 os.makedirs (vserver_config_path)
274 file('%s/slice_id'%vserver_config_path, 'w').write("%d\n"%self.slice_id)
275 logger.log("sliver_vs: Recorded slice id %d for slice %s"%(self.slice_id,self.name))
277 logger.log("sliver_vs: Could not record slice_id for slice %s. Error: %s"%(self.name,str(e)))
279 logger.log_exc("sliver_vs: Error recording slice id: %s"%str(e),name=self.name)
282 if self.enabled == False:
286 if False: # Does not work properly yet.
287 if self.have_limits_changed():
288 logger.log('sliver_vs: %s: limits have changed --- restarting' % self.name)
290 while self.is_running() and stopcount > 0:
294 stopcount = stopcount - 1
297 else: # tell vsh to disable remote login by setting CPULIMIT to 0
298 logger.log('sliver_vs: %s: disabling remote login' % self.name)
299 self.set_sched_config(0, 0)