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
74 def create(name, vref = None):
75 logger.verbose('Sliver_VS:create - name=%s'%name)
79 ### locating the right slicefamily
80 # this is a first draft, and more a proof of concept thing
81 # the idea is to parse vref for dash-separated wishes,
82 # and to project these against the defaults
83 # so e.g. if the default slice family (as found in /etc/planetlab/slicefamily)
84 # is planetlab-f8-i386, then here is what we get
85 # vref=x86_64 -> vuseradd -t planetlab-f8-x86_64
86 # vref=centos5 -> vuseradd -t planetlab-centos5-i386
87 # vref=centos5-onelab -> vuseradd -t onelab-centos5-i386
88 # vref=planetflow -> vuseradd -t planetflow-f8-i386
89 # vref=x86_64-planetflow -> vuseradd -t planetflow-f8-x86_64
92 default=file("/etc/planetlab/slicefamily").read().strip()
93 (pldistro,fcdistro,arch) = default.split("-")
95 known_archs = [ 'i386', 'x86_64' ]
96 known_fcdistros = [ 'f8', 'f9', 'centos5' ]
97 # from the slice attribute: cut dashes and try to figure the meaning
98 slice_wishes = vref.split("-")
99 for wish in slice_wishes:
100 if wish in known_archs:
102 elif wish in known_fcdistros:
108 refname="-".join( (pldistro,fcdistro,arch) )
110 # check the template exists -- there's probably a better way..
111 if not os.path.isdir ("/vservers/.vref/%s"%refname):
112 logger.log("%s (%s) : vref %s not found, using default %s"%(
113 name,vref,refname,default))
115 # reset so arch is right
116 (pldistro,fcdistro,arch) = default.split("-")
117 # could check again, but as we have /etc/slicefamily
118 # there's probably no /vservers/.vref/default
121 # have not found slicefamily
122 logger.log("%s (%s): legacy node - using fallback vrefname 'default'"%(name,vref))
127 logger.log("%s (%s) : unexpected error follows - using 'default'"%(name,vref))
128 logger.log(traceback.format_exc())
132 def personality (arch):
133 personality="linux32"
134 if arch.find("64")>=0:
135 personality="linux64"
138 logger.log_call('/usr/sbin/vuseradd', '-t', refname, name)
139 # export slicename to the slice in /etc/slicename
140 file('/vservers/%s/etc/slicename' % name, 'w').write(name)
141 # set personality: only if needed (if arch's differ)
142 if tools.root_context_arch() != arch:
143 file('/etc/vservers/%s/personality' % name, 'w').write(personality(arch))
144 logger.log('%s: set personality to %s'%(name,personality(arch)))
147 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
149 def configure(self, rec):
150 new_rspec = rec['_rspec']
151 if new_rspec != self.rspec:
152 self.rspec = new_rspec
155 new_initscript = rec['initscript']
156 if new_initscript != self.initscript:
157 self.initscript = new_initscript
158 self.initscriptchanged = True
160 accounts.Account.configure(self, rec) # install ssh keys
162 def start(self, delay=0):
163 if self.rspec['enabled'] > 0:
164 logger.log('%s: starting in %d seconds' % (self.name, delay))
166 # VServer.start calls fork() internally,
167 # so just close the nonstandard fds and fork once to avoid creating zombies
168 child_pid = os.fork()
170 if self.initscriptchanged:
171 logger.log('%s: installing initscript' % self.name)
172 def install_initscript():
173 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
174 fd = os.open('/etc/rc.vinit', flags, 0755)
175 os.write(fd, self.initscript)
178 self.chroot_call(install_initscript)
179 except: logger.log_exc(self.name)
180 tools.close_nonstandard_fds()
181 vserver.VServer.start(self)
184 os.waitpid(child_pid, 0)
185 self.initscriptchanged = False
186 else: logger.log('%s: not starting, is not enabled' % self.name)
189 logger.log('%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('%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('%s: computing disk usage: beginning' % self.name)
203 try: self.init_disk_info()
204 finally: Sliver_VS._init_disk_info_sem.release()
205 logger.log('%s: computing disk usage: ended' % self.name)
206 self.disk_usage_initialized = True
207 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
209 logger.log('%s: failed to set max disk usage' % self.name)
210 logger.log_exc(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('%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('%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("%s: opening %s"%(self.name,path))
240 fd = os.open(path, flags)
241 logger.log("%s: writing %s=%s"%(self.name,key,self.rspec[key]))
242 os.write(fd,self.rspec[key])
245 logger.log("%s: could not set %s=%s"%(self.name,key,self.rspec[key]))
246 logger.log("%s: error = %s"%(self.name,e))
249 if self.rspec['enabled'] > 0:
251 logger.log('%s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
256 logger.log('%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('%s: setting IP address(es) to %s' % \
264 (self.name, self.rspec['ip_addresses']))
265 self.set_ipaddresses_config(self.rspec['ip_addresses'])
267 if self.is_running():
268 logger.log("%s: Setting name to %s" % (self.name, self.slice_id),2)
269 self.setname(self.slice_id)
271 if False: # Does not work properly yet.
272 if self.have_limits_changed():
273 logger.log('%s: limits have changed --- restarting' % self.name)
275 while self.is_running() and stopcount > 0:
279 stopcount = stopcount - 1
282 else: # tell vsh to disable remote login by setting CPULIMIT to 0
283 logger.log('%s: disabling remote login' % self.name)
284 self.set_sched_config(0, 0)