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.
28 from threading import BoundedSemaphore
30 globalsem = BoundedSemaphore()
32 # special constant that tells vserver to keep its existing settings
33 KEEP_LIMIT = vserver.VC_LIM_KEEP
35 # populate the sliver/vserver specific default allocations table,
36 # which is used to look for slice attributes
37 DEFAULT_ALLOCATION = {}
38 for rlimit in vserver.RLIMITS.keys():
40 DEFAULT_ALLOCATION["%s_min"%rlim]=KEEP_LIMIT
41 DEFAULT_ALLOCATION["%s_soft"%rlim]=KEEP_LIMIT
42 DEFAULT_ALLOCATION["%s_hard"%rlim]=KEEP_LIMIT
44 class Sliver_VS(accounts.Account, vserver.VServer):
45 """This class wraps vserver.VServer to make its interface closer to what we need."""
48 TYPE = 'sliver.VServer'
49 _init_disk_info_sem = globalsem
51 def __init__(self, rec):
52 logger.verbose ('initing Sliver_VS with name=%s'%rec['name'])
54 vserver.VServer.__init__(self, rec['name'],logfile='/var/log/nm')
55 except Exception, err:
56 if not isinstance(err, vserver.NoSuchVServer):
57 # Probably a bad vserver or vserver configuration file
58 logger.log_exc(self.name)
59 logger.log('%s: recreating bad vserver' % rec['name'])
60 self.destroy(rec['name'])
61 self.create(rec['name'], rec['vref'])
62 vserver.VServer.__init__(self, rec['name'],logfile='/var/log/nm')
67 self.slice_id = rec['slice_id']
68 self.disk_usage_initialized = False
69 self.initscriptchanged = False
73 def create(name, vref = None):
74 logger.verbose('Sliver_VS:create - name=%s'%name)
78 ### locating the right slicefamily
79 # this is a first draft, and more a proof of concept thing
80 # the idea is to parse vref for dash-separated wishes,
81 # and to project these against the defaults
82 # so e.g. if the default slice family (as found in /etc/planetlab/slicefamily)
83 # is planetlab-f8-i386, then here is what we get
84 # vref=x86_64 -> vuseradd -t planetlab-f8-x86_64
85 # vref=centos5 -> vuseradd -t planetlab-centos5-i386
86 # vref=centos5-onelab -> vuseradd -t onelab-centos5-i386
87 # vref=planetflow -> vuseradd -t planetflow-f8-i386
88 # vref=x86_64-planetflow -> vuseradd -t planetflow-f8-x86_64
91 default=file("/etc/planetlab/slicefamily").read().strip()
92 (pldistro,fcdistro,arch) = default.split("-")
94 known_archs = [ 'i386', 'x86_64' ]
95 known_fcdistros = [ 'f8', 'f9', 'centos5' ]
96 # from the slice attribute: cut dashes and try to figure the meaning
97 slice_wishes = vref.split("-")
98 for wish in slice_wishes:
99 if wish in known_archs:
101 elif wish in known_fcdistros:
107 refname="-".join( (pldistro,fcdistro,arch) )
109 # check the template exists -- there's probably a better way..
110 if not os.path.isdir ("/vservers/.vref/%s"%refname):
111 logger.verbose("%s (%s) : vref %s not found, using default %s"%(
112 name,vref,refname,default))
114 # reset so arch is right
115 (pldistro,fcdistro,arch) = default.split("-")
116 # could check again, but as we have /etc/slicefamily
117 # there's probably no /vservers/.vref/default
120 # have not found slicefamily
121 logger.verbose("%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 child_pid = os.fork()
168 if self.initscriptchanged:
169 logger.log('%s: installing initscript' % self.name)
170 def install_initscript():
171 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
172 fd = os.open('/etc/rc.vinit', flags, 0755)
173 os.write(fd, self.initscript)
176 self.chroot_call(install_initscript)
177 self.initscriptchanged = False
178 except: logger.log_exc(self.name)
179 # VServer.start calls fork() internally,
180 # so just close the nonstandard fds and fork once to avoid creating zombies
181 tools.close_nonstandard_fds()
182 vserver.VServer.start(self)
184 else: os.waitpid(child_pid, 0)
185 else: logger.log('%s: not starting, is not enabled' % self.name)
188 logger.log('%s: stopping' % self.name)
189 vserver.VServer.stop(self)
191 def is_running(self): vserver.VServer.is_running(self)
193 def set_resources(self):
194 disk_max = self.rspec['disk_max']
195 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
196 try: # if the sliver is over quota, .set_disk_limit will throw an exception
197 if not self.disk_usage_initialized:
198 self.vm_running = False
199 Sliver_VS._init_disk_info_sem.acquire()
200 logger.log('%s: computing disk usage: beginning' % self.name)
201 try: self.init_disk_info()
202 finally: Sliver_VS._init_disk_info_sem.release()
203 logger.log('%s: computing disk usage: ended' % self.name)
204 self.disk_usage_initialized = True
205 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
207 logger.log('%s: failed to set max disk usage' % self.name)
208 logger.log_exc(self.name)
210 # get/set the min/soft/hard values for all of the vserver
211 # related RLIMITS. Note that vserver currently only
212 # implements support for hard limits.
213 for limit in vserver.RLIMITS.keys():
215 minimum = self.rspec['%s_min'%type]
216 soft = self.rspec['%s_soft'%type]
217 hard = self.rspec['%s_hard'%type]
218 update = self.set_rlimit(limit, hard, soft, minimum)
220 logger.log('%s: setting rlimit %s to (%d, %d, %d)'
221 % (self.name, type, hard, soft, minimum))
223 self.set_capabilities_config(self.rspec['capabilities'])
224 if self.rspec['capabilities']:
225 logger.log('%s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
227 cpu_pct = self.rspec['cpu_pct']
228 cpu_share = self.rspec['cpu_share']
230 if self.rspec['enabled'] > 0:
232 logger.log('%s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
237 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
241 self.set_sched_config(cpu_pct, cpu_share)
242 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
243 if self.rspec['ip_addresses'] != '0.0.0.0':
244 logger.log('%s: setting IP address(es) to %s' % \
245 (self.name, self.rspec['ip_addresses']))
246 self.set_ipaddresses_config(self.rspec['ip_addresses'])
248 if self.is_running():
249 logger.log("%s: Setting name to %s" % (self.name, self.slice_id),2)
250 self.setname(self.slice_id)
252 if False: # Does not work properly yet.
253 if self.have_limits_changed():
254 logger.log('%s: limits have changed --- restarting' % self.name)
256 while self.is_running() and stopcount > 0:
260 stopcount = stopcount - 1
263 else: # tell vsh to disable remote login by setting CPULIMIT to 0
264 logger.log('%s: disabling remote login' % self.name)
265 self.set_sched_config(0, 0)