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
75 def create(name, vref = None):
76 logger.verbose('Sliver_VS:create - name=%s'%name)
78 logger.log("creating %s : no vref attached, this is unexpected"%name)
80 # used to look in /etc/planetlab/family, now relies on the 'family' extra attribute in GetSlivers()
81 # which for legacy is still exposed here as the 'vref' key
83 # check the template exists -- there's probably a better way..
84 if not os.path.isdir ("/vservers/.vref/%s"%vref):
85 # find a resonable default
86 if os.path.isfile ("/etc/planetlab/slicefamily"):
87 default=file("/etc/planetlab/slicefamily").read().strip()
90 logger.log("creating %s : /etc/planetlab/slicefamily not found, this is unexpected"%name)
91 logger.log("creating %s - vref %s not found, using default %s"%(name,vref,default))
96 (x,y,arch)=vref.split('-')
100 def personality (arch):
101 personality="linux32"
102 if arch.find("64")>=0:
103 personality="linux64"
106 logger.log_call('/usr/sbin/vuseradd', '-t', vref, name)
107 # export slicename to the slice in /etc/slicename
108 file('/vservers/%s/etc/slicename' % name, 'w').write(name)
109 file('/vservers/%s/etc/slicefamily' % name, 'w').write(vref)
110 # set personality: only if needed (if arch's differ)
111 if tools.root_context_arch() != arch:
112 file('/etc/vservers/%s/personality' % name, 'w').write(personality(arch))
113 logger.log('%s: set personality to %s'%(name,personality(arch)))
116 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
118 def configure(self, rec):
119 new_rspec = rec['_rspec']
120 if new_rspec != self.rspec:
121 self.rspec = new_rspec
124 new_initscript = rec['initscript']
125 if new_initscript != self.initscript:
126 self.initscript = new_initscript
127 self.initscriptchanged = True
129 accounts.Account.configure(self, rec) # install ssh keys
131 def start(self, delay=0):
132 if self.rspec['enabled'] > 0:
133 logger.log('%s: starting in %d seconds' % (self.name, delay))
135 # VServer.start calls fork() internally,
136 # so just close the nonstandard fds and fork once to avoid creating zombies
137 child_pid = os.fork()
139 if self.initscriptchanged:
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, self.initscript)
147 self.chroot_call(install_initscript)
148 except: logger.log_exc(self.name)
149 tools.close_nonstandard_fds()
150 vserver.VServer.start(self)
153 os.waitpid(child_pid, 0)
154 self.initscriptchanged = False
155 else: logger.log('%s: not starting, is not enabled' % self.name)
158 logger.log('%s: stopping' % self.name)
159 vserver.VServer.stop(self)
161 def is_running(self):
162 return vserver.VServer.is_running(self)
164 def set_resources(self,setup=False):
165 disk_max = self.rspec['disk_max']
166 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
167 try: # if the sliver is over quota, .set_disk_limit will throw an exception
168 if not self.disk_usage_initialized:
169 self.vm_running = False
170 Sliver_VS._init_disk_info_sem.acquire()
171 logger.log('%s: computing disk usage: beginning' % self.name)
172 try: self.init_disk_info()
173 finally: Sliver_VS._init_disk_info_sem.release()
174 logger.log('%s: computing disk usage: ended' % self.name)
175 self.disk_usage_initialized = True
176 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
178 logger.log('%s: failed to set max disk usage' % self.name)
179 logger.log_exc(self.name)
181 # get/set the min/soft/hard values for all of the vserver
182 # related RLIMITS. Note that vserver currently only
183 # implements support for hard limits.
184 for limit in vserver.RLIMITS.keys():
186 minimum = self.rspec['%s_min'%type]
187 soft = self.rspec['%s_soft'%type]
188 hard = self.rspec['%s_hard'%type]
189 update = self.set_rlimit(limit, hard, soft, minimum)
191 logger.log('%s: setting rlimit %s to (%d, %d, %d)'
192 % (self.name, type, hard, soft, minimum))
194 self.set_capabilities_config(self.rspec['capabilities'])
195 if self.rspec['capabilities']:
196 logger.log('%s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
198 cpu_pct = self.rspec['cpu_pct']
199 cpu_share = self.rspec['cpu_share']
202 for key in self.rspec.keys():
203 if key.find('sysctl.') == 0:
204 sysctl=key.split('.')
206 path="/proc/sys/%s" % ("/".join(sysctl[1:]))
207 logger.log("%s: opening %s"%(self.name,path))
209 fd = os.open(path, flags)
210 logger.log("%s: writing %s=%s"%(self.name,key,self.rspec[key]))
211 os.write(fd,self.rspec[key])
214 logger.log("%s: could not set %s=%s"%(self.name,key,self.rspec[key]))
215 logger.log("%s: error = %s"%(self.name,e))
218 if self.rspec['enabled'] > 0:
220 logger.log('%s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
225 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
229 self.set_sched_config(cpu_pct, cpu_share)
230 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
231 if self.rspec['ip_addresses'] != '0.0.0.0':
232 logger.log('%s: setting IP address(es) to %s' % \
233 (self.name, self.rspec['ip_addresses']))
234 self.set_ipaddresses_config(self.rspec['ip_addresses'])
236 if self.is_running():
237 logger.log("%s: Storing slice id for PlanetFlow" % (self.name, self.slice_id),2)
238 #self.setname(self.slice_id)
239 file('/etc/vservers/%s/slice_id' % name, 'w').write(self.slice_id)
241 if self.enabled == False:
245 if False: # Does not work properly yet.
246 if self.have_limits_changed():
247 logger.log('%s: limits have changed --- restarting' % self.name)
249 while self.is_running() and stopcount > 0:
253 stopcount = stopcount - 1
256 else: # tell vsh to disable remote login by setting CPULIMIT to 0
257 logger.log('%s: disabling remote login' % self.name)
258 self.set_sched_config(0, 0)