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.
27 from threading import BoundedSemaphore
29 globalsem = BoundedSemaphore()
31 # special constant that tells vserver to keep its existing settings
32 KEEP_LIMIT = vserver.VC_LIM_KEEP
34 # populate the sliver/vserver specific default allocations table,
35 # which is used to look for slice attributes
36 DEFAULT_ALLOCATION = {}
37 for rlimit in vserver.RLIMITS.keys():
39 DEFAULT_ALLOCATION["%s_min"%rlim]=KEEP_LIMIT
40 DEFAULT_ALLOCATION["%s_soft"%rlim]=KEEP_LIMIT
41 DEFAULT_ALLOCATION["%s_hard"%rlim]=KEEP_LIMIT
43 class Sliver_VS(accounts.Account, vserver.VServer):
44 """This class wraps vserver.VServer to make its interface closer to what we need."""
47 TYPE = 'sliver.VServer'
48 _init_disk_info_sem = globalsem
50 def __init__(self, rec):
51 logger.verbose ('initing Sliver_VS with name=%s'%rec['name'])
53 vserver.VServer.__init__(self, rec['name'],logfile='/var/log/nm')
54 except Exception, err:
55 if not isinstance(err, vserver.NoSuchVServer):
56 # Probably a bad vserver or vserver configuration file
57 logger.log_exc(self.name)
58 logger.log('%s: recreating bad vserver' % rec['name'])
59 self.destroy(rec['name'])
60 self.create(rec['name'], rec['vref'])
61 vserver.VServer.__init__(self, rec['name'],logfile='/var/log/nm')
66 self.slice_id = rec['slice_id']
67 self.disk_usage_initialized = False
68 self.initscriptchanged = False
72 def create(name, vref = None):
73 logger.verbose('Sliver_VS:create - name=%s'%name)
77 ### locating the right slicefamily
78 # this is a first draft, and more a proof of concept thing
79 # the idea is to parse vref for dash-separated wishes,
80 # and to project these against the defaults
81 # so e.g. if the default slice family (as found in /etc/planetlab/slicefamily)
82 # is planetlab-f8-i386, then here is what we get
83 # vref=x86_64 -> vuseradd -t planetlab-f8-x86_64
84 # vref=centos5 -> vuseradd -t planetlab-centos5-i386
85 # vref=centos5-onelab -> vuseradd -t onelab-centos5-i386
86 # vref=planetflow -> vuseradd -t planetflow-f8-i386
87 # vref=x86_64-planetflow -> vuseradd -t planetflow-f8-x86_64
90 default=file("/etc/planetlab/slicefamily").read().strip()
91 (pldistro,fcdistro,arch) = default.split("-")
93 known_archs = [ 'i386', 'x86_64' ]
94 known_fcdistros = [ 'f8', 'f9', 'centos5' ]
95 # from the slice attribute: cut dashes and try to figure the meaning
96 slice_wishes = vref.split("-")
97 for wish in slice_wishes:
98 if wish in known_archs:
100 elif wish in known_fcdistros:
106 refname="-".join( (pldistro,fcdistro,arch) )
108 # check the templates exists -- there's probably a better way..
109 if os.path.isdir ("/vservers/.vref/%s"% vref): refname = vref
111 if not os.path.isdir ("/vservers/.vref/%s"% refname):
112 logger.verbose("%s (%s) : vref %s not found, using default %s"%(
113 name,vref,refname,default))
115 # could check again, but as we have /etc/slicefamily
116 # there's probably no /vservers/.vref/default
119 # have not found slicefamily
120 logger.verbose("%s (%s): legacy node - using fallback vrefname 'default'"%(name,vref))
125 logger.log("%s (%s) : unexpected error follows - using 'default'"%(
127 logger.log(traceback.format_exc())
130 logger.log_call('/usr/sbin/vuseradd', '-t', refname, name)
131 open('/vservers/%s/etc/slicename' % name, 'w').write(name)
134 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
136 def configure(self, rec):
137 new_rspec = rec['_rspec']
138 if new_rspec != self.rspec:
139 self.rspec = new_rspec
142 new_initscript = rec['initscript']
143 if new_initscript != self.initscript:
144 self.initscript = new_initscript
145 logger.log('%s: installing initscript' % self.name)
146 def install_initscript():
147 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
148 fd = os.open('/etc/rc.vinit', flags, 0755)
149 os.write(fd, new_initscript)
152 self.chroot_call(install_initscript)
153 self.initscriptchanged = True
154 except: logger.log_exc(self.name)
156 accounts.Account.configure(self, rec) # install ssh keys
158 def start(self, delay=0):
159 if self.rspec['enabled'] > 0:
160 logger.log('%s: starting in %d seconds' % (self.name, delay))
162 child_pid = os.fork()
164 # VServer.start calls fork() internally,
165 # so just close the nonstandard fds and fork once to avoid creating zombies
166 tools.close_nonstandard_fds()
167 vserver.VServer.start(self)
169 else: os.waitpid(child_pid, 0)
170 else: logger.log('%s: not starting, is not enabled' % self.name)
171 self.initscriptchanged = False
174 logger.log('%s: stopping' % self.name)
175 vserver.VServer.stop(self)
177 def set_resources(self):
178 disk_max = self.rspec['disk_max']
179 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
180 try: # if the sliver is over quota, .set_disk_limit will throw an exception
181 if not self.disk_usage_initialized:
182 self.vm_running = False
183 logger.log('%s: computing disk usage: beginning' % self.name)
184 Sliver_VS._init_disk_info_sem.acquire()
185 try: self.init_disk_info()
186 finally: Sliver_VS._init_disk_info_sem.release()
187 logger.log('%s: computing disk usage: ended' % self.name)
188 self.disk_usage_initialized = True
189 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
191 logger.log('%s: failed to set max disk usage' % self.name)
192 logger.log_exc(self.name)
194 # get/set the min/soft/hard values for all of the vserver
195 # related RLIMITS. Note that vserver currently only
196 # implements support for hard limits.
197 for limit in vserver.RLIMITS.keys():
199 minimum = self.rspec['%s_min'%type]
200 soft = self.rspec['%s_soft'%type]
201 hard = self.rspec['%s_hard'%type]
202 update = self.set_rlimit(limit, hard, soft, minimum)
204 logger.log('%s: setting rlimit %s to (%d, %d, %d)'
205 % (self.name, type, hard, soft, minimum))
207 self.set_capabilities_config(self.rspec['capabilities'])
208 if self.rspec['capabilities']:
209 logger.log('%s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
211 cpu_pct = self.rspec['cpu_pct']
212 cpu_share = self.rspec['cpu_share']
214 if self.rspec['enabled'] > 0:
216 logger.log('%s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
221 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
225 self.set_sched_config(cpu_pct, cpu_share)
226 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
227 if self.rspec['ip_addresses'] != '0.0.0.0':
228 logger.log('%s: setting IP address(es) to %s' % \
229 (self.name, self.rspec['ip_addresses']))
230 self.set_ipaddresses_config(self.rspec['ip_addresses'])
232 if self.is_running():
233 logger.log("%s: Setting name to %s" % (self.name, self.slice_id),2)
234 self.setname(self.slice_id)
236 if False: # Does not work properly yet.
237 if self.have_limits_changed():
238 logger.log('%s: limits have changed --- restarting' % self.name)
240 while self.is_running() and stopcount > 0:
244 stopcount = stopcount - 1
247 else: # tell vsh to disable remote login by setting CPULIMIT to 0
248 logger.log('%s: disabling remote login' % self.name)
249 self.set_sched_config(0, 0)