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("-")
92 # from the slice attribute: cut dashes and try to figure the meaning
93 slice_wishes = vref.split("-")
94 for wish in slice_wishes:
95 if wish == "i386" or wish == "x86_64":
97 elif wish == "f8" or wish == "centos5" :
103 refname="-".join( (pldistro,fcdistro,arch) )
105 # check the templates exists -- there's probably a better way..
106 if not os.path.isdir ("/vservers/.vref/%s"%refname):
107 logger.verbose("%s (%s) : vref %s not found, using default %s"%(
108 name,vref,refname,default))
110 # could check again, but as we have /etc/slicefamily
111 # there's probably no /vservers/.vref/default
114 # have not found slicefamily
115 logger.verbose("%s (%s): legacy node - using fallback vrefname 'default'"%(name,vref))
120 logger.log("%s (%s) : unexpected error follows - using 'default'"%(
122 logger.log(traceback.format_exc())
125 logger.log_call('/usr/sbin/vuseradd', '-t', refname, name)
126 open('/vservers/%s/etc/slicename' % name, 'w').write(name)
129 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
131 def configure(self, rec):
132 new_rspec = rec['_rspec']
133 if new_rspec != self.rspec:
134 self.rspec = new_rspec
137 new_initscript = rec['initscript']
138 if new_initscript != self.initscript:
139 self.initscript = new_initscript
140 logger.log('%s: installing initscript' % self.name)
141 def install_initscript():
142 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
143 fd = os.open('/etc/rc.vinit', flags, 0755)
144 os.write(fd, new_initscript)
147 self.chroot_call(install_initscript)
148 self.initscriptchanged = True
149 except: logger.log_exc(self.name)
151 accounts.Account.configure(self, rec) # install ssh keys
153 def start(self, delay=0):
154 if self.rspec['enabled'] > 0:
155 logger.log('%s: starting in %d seconds' % (self.name, delay))
157 # VServer.start calls fork() internally
158 vserver.VServer.start(self)
159 # Watch for 5 mins to see if slice is running before setting the name
160 # It would make sense to do this as part of start in VServer, but the name
161 # comes from NM. Also, the name would only change in NM. Name can only be
162 # set from root context, so overloading chcontext wont work; chcontext, setname
163 # will fail, and in the converse the context isn't setup in the kernel.
164 for i in range(0,60):
166 if vserver.VServer.is_running(self):
167 # Set the vciVHI_CONTEXT to slice_id for
168 # fprobe-ulog to mark packets with.
169 logger.log("%s: Setting name to %s" % (self.name, self.slice_id),2)
170 self.setname(self.slice_id)
173 else: logger.log('%s: not starting, is not enabled' % self.name)
174 self.initscriptchanged = False
177 logger.log('%s: stopping' % self.name)
178 vserver.VServer.stop(self)
180 def set_resources(self):
181 disk_max = self.rspec['disk_max']
182 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
183 try: # if the sliver is over quota, .set_disk_limit will throw an exception
184 if not self.disk_usage_initialized:
185 self.vm_running = False
186 logger.log('%s: computing disk usage: beginning' % self.name)
187 Sliver_VS._init_disk_info_sem.acquire()
188 try: self.init_disk_info()
189 finally: Sliver_VS._init_disk_info_sem.release()
190 logger.log('%s: computing disk usage: ended' % self.name)
191 self.disk_usage_initialized = True
192 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
194 logger.log('%s: failed to set max disk usage' % self.name)
195 logger.log_exc(self.name)
197 # get/set the min/soft/hard values for all of the vserver
198 # related RLIMITS. Note that vserver currently only
199 # implements support for hard limits.
200 for limit in vserver.RLIMITS.keys():
202 minimum = self.rspec['%s_min'%type]
203 soft = self.rspec['%s_soft'%type]
204 hard = self.rspec['%s_hard'%type]
205 update = self.set_rlimit(limit, hard, soft, minimum)
207 logger.log('%s: setting rlimit %s to (%d, %d, %d)'
208 % (self.name, type, hard, soft, minimum))
210 self.set_capabilities_config(self.rspec['capabilities'])
211 if self.rspec['capabilities']:
212 logger.log('%s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
214 cpu_pct = self.rspec['cpu_pct']
215 cpu_share = self.rspec['cpu_share']
217 if self.rspec['enabled'] > 0:
219 logger.log('%s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
224 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
228 self.set_sched_config(cpu_pct, cpu_share)
229 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
230 if self.rspec['ip_addresses'] != '0.0.0.0':
231 logger.log('%s: setting IP address(es) to %s' % \
232 (self.name, self.rspec['ip_addresses']))
233 self.set_ipaddresses_config(self.rspec['ip_addresses'])
235 if False: # Does not work properly yet.
236 if self.have_limits_changed():
237 logger.log('%s: limits have changed --- restarting' % self.name)
239 while self.is_running() and stopcount > 0:
243 stopcount = stopcount - 1
246 else: # tell vsh to disable remote login by setting CPULIMIT to 0
247 logger.log('%s: disabling remote login' % self.name)
248 self.set_sched_config(0, 0)