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.disk_usage_initialized = False
67 self.initscriptchanged = False
71 def create(name, vref = None):
72 logger.verbose('Sliver_VS:create - name=%s'%name)
76 ### locating the right slicefamily
77 # this is a first draft, and more a proof of concept thing
78 # the idea is to parse vref for dash-separated wishes,
79 # and to project these against the defaults
80 # so e.g. if the default slice family (as found in /etc/planetlab/slicefamily)
81 # is planetlab-f8-i386, then here is what we get
82 # vref=x86_64 -> vuseradd -t planetlab-f8-x86_64
83 # vref=centos5 -> vuseradd -t planetlab-centos5-i386
84 # vref=centos5-onelab -> vuseradd -t onelab-centos5-i386
85 # vref=planetflow -> vuseradd -t planetflow-f8-i386
86 # vref=x86_64-planetflow -> vuseradd -t planetflow-f8-x86_64
89 default=file("/etc/planetlab/slicefamily").read().strip()
90 (pldistro,fcdistro,arch) = default.split("-")
91 # from the slice attribute: cut dashes and try to figure the meaning
92 slice_wishes = vref.split("-")
93 for wish in slice_wishes:
94 if wish == "i386" or wish == "x86_64":
96 elif wish == "f8" or wish == "centos5" :
102 refname="-".join( (pldistro,fcdistro,arch) )
104 # check the templates exists -- there's probably a better way..
105 if not os.path.isdir ("/vservers/.vref/%s"%refname):
106 logger.verbose("%s (%s) : vref %s not found, using default %s"%(
107 name,vref,refname,default))
109 # could check again, but as we have /etc/slicefamily
110 # there's probably no /vservers/.vref/default
113 # have not found slicefamily
114 logger.verbose("%s (%s): legacy node - using fallback vrefname 'default'"%(name,vref))
119 logger.log("%s (%s) : unexpected error follows - using 'default'"%(
121 logger.log(traceback.format_exc())
124 logger.log_call('/usr/sbin/vuseradd', '-t', refname, name)
125 open('/vservers/%s/etc/slicename' % name, 'w').write(name)
128 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
130 def configure(self, rec):
131 new_rspec = rec['_rspec']
132 if new_rspec != self.rspec:
133 self.rspec = new_rspec
136 new_initscript = rec['initscript']
137 if new_initscript != self.initscript:
138 self.initscript = new_initscript
139 logger.log('%s: installing initscript' % self.name)
140 def install_initscript():
141 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
142 fd = os.open('/etc/rc.vinit', flags, 0755)
143 os.write(fd, new_initscript)
146 self.chroot_call(install_initscript)
147 self.initscriptchanged = True
148 except: logger.log_exc(self.name)
150 accounts.Account.configure(self, rec) # install ssh keys
152 def start(self, delay=0):
153 if self.rspec['enabled'] > 0:
154 logger.log('%s: starting in %d seconds' % (self.name, delay))
156 child_pid = os.fork()
158 # VServer.start calls fork() internally, so just close the nonstandard fds and fork once to avoid creating zombies
159 tools.close_nonstandard_fds()
160 vserver.VServer.start(self, True)
162 else: os.waitpid(child_pid, 0)
163 else: logger.log('%s: not starting, is not enabled' % self.name)
164 self.initscriptchanged = False
167 logger.log('%s: stopping' % self.name)
168 vserver.VServer.stop(self)
170 def set_resources(self):
171 disk_max = self.rspec['disk_max']
172 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
173 try: # if the sliver is over quota, .set_disk_limit will throw an exception
174 if not self.disk_usage_initialized:
175 self.vm_running = False
176 logger.log('%s: computing disk usage: beginning' % self.name)
177 Sliver_VS._init_disk_info_sem.acquire()
178 try: self.init_disk_info()
179 finally: Sliver_VS._init_disk_info_sem.release()
180 logger.log('%s: computing disk usage: ended' % self.name)
181 self.disk_usage_initialized = True
182 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
184 logger.log('%s: failed to set max disk usage' % self.name)
185 logger.log_exc(self.name)
187 # get/set the min/soft/hard values for all of the vserver
188 # related RLIMITS. Note that vserver currently only
189 # implements support for hard limits.
190 for limit in vserver.RLIMITS.keys():
192 minimum = self.rspec['%s_min'%type]
193 soft = self.rspec['%s_soft'%type]
194 hard = self.rspec['%s_hard'%type]
195 update = self.set_rlimit(limit, hard, soft, minimum)
197 logger.log('%s: setting rlimit %s to (%d, %d, %d)'
198 % (self.name, type, hard, soft, minimum))
200 self.set_capabilities_config(self.rspec['capabilities'])
201 if self.rspec['capabilities']:
202 logger.log('%s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
204 cpu_pct = self.rspec['cpu_pct']
205 cpu_share = self.rspec['cpu_share']
207 self.setname(rec['slice_id'])
209 if self.rspec['enabled'] > 0:
211 logger.log('%s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
216 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
220 self.set_sched_config(cpu_pct, cpu_share)
221 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
222 if self.rspec['ip_addresses'] != '0.0.0.0':
223 logger.log('%s: setting IP address(es) to %s' % (self.name, self.rspec['ip_addresses']))
224 self.set_ipaddresses_config(self.rspec['ip_addresses'])
226 if False: # Does not work properly yet.
227 if self.have_limits_changed():
228 logger.log('%s: limits have changed --- restarting' % self.name)
230 while self.is_running() and stopcount > 0:
234 stopcount = stopcount - 1
237 else: # tell vsh to disable remote login by setting CPULIMIT to 0
238 logger.log('%s: disabling remote login' % self.name)
239 self.set_sched_config(0, 0)