6 There are a couple of tricky things going on here. First, the kernel
7 needs disk usage information in order to enforce the quota. However,
8 determining disk usage redundantly strains the disks. Thus, the
9 Sliver_VS.disk_usage_initialized flag is used to determine whether
10 this initialization has been made.
12 Second, it's not currently possible to set the scheduler parameters
13 for a sliver unless that sliver has a running process. /bin/vsh helps
14 us out by reading the configuration file so that it can set the
15 appropriate limits after entering the sliver context. Making the
16 syscall that actually sets the parameters gives a harmless error if no
17 process is running. Thus we keep vm_running on when setting scheduler
18 parameters so that set_sched_params() always makes the syscall, and we
19 don't have to guess if there is a running process or not.
32 from threading import BoundedSemaphore
34 globalsem = BoundedSemaphore()
36 # special constant that tells vserver to keep its existing settings
37 KEEP_LIMIT = vserver.VC_LIM_KEEP
39 # populate the sliver/vserver specific default allocations table,
40 # which is used to look for slice attributes
41 DEFAULT_ALLOCATION = {}
42 for rlimit in vserver.RLIMITS.keys():
44 DEFAULT_ALLOCATION["%s_min"%rlim]=KEEP_LIMIT
45 DEFAULT_ALLOCATION["%s_soft"%rlim]=KEEP_LIMIT
46 DEFAULT_ALLOCATION["%s_hard"%rlim]=KEEP_LIMIT
48 class Sliver_VS(accounts.Account, vserver.VServer):
49 """This class wraps vserver.VServer to make its interface closer to what we need."""
52 TYPE = 'sliver.VServer'
53 _init_disk_info_sem = globalsem
55 def __init__(self, rec):
57 logger.verbose ('sliver_vs: %s init'%name)
59 logger.log("sliver_vs: %s: first chance..."%name)
60 vserver.VServer.__init__(self, name,logfile='/var/log/nm')
61 except Exception, err:
62 if not isinstance(err, vserver.NoSuchVServer):
63 # Probably a bad vserver or vserver configuration file
64 logger.log_exc("sliver_vs:__init__ (first chance) %s",name=name)
65 logger.log('sliver_vs: %s: recreating bad vserver' % name)
67 self.create(name, rec['vref'])
68 logger.log("sliver_vs: %s: second chance..."%name)
69 vserver.VServer.__init__(self, name,logfile='/var/log/nm')
74 self.slice_id = rec['slice_id']
75 self.disk_usage_initialized = False
76 self.initscriptchanged = False
81 def create(name, vref = None):
82 logger.verbose('sliver_vs: %s: create'%name)
84 logger.log("sliver_vs: %s: ERROR - no vref attached, this is unexpected"%(name))
86 # used to look in /etc/planetlab/family,
87 # now relies on the 'GetSliceFamily' extra attribute in GetSlivers()
88 # which for legacy is still exposed here as the 'vref' key
90 # check the template exists -- there's probably a better way..
91 if not os.path.isdir ("/vservers/.vref/%s"%vref):
92 logger.log ("sliver_vs: %s: ERROR Could not create sliver - vreference image %s not found"%(name,vref))
97 (x,y,arch)=vref.split('-')
98 # mh, this of course applies when 'vref' is e.g. 'netflow'
99 # and that's not quite right
103 def personality (arch):
104 personality="linux32"
105 if arch.find("64")>=0:
106 personality="linux64"
109 # logger.log_call(['/usr/sbin/vuseradd', '-t', vref, name, ], timeout=15*60)
110 logger.log_call(['/bin/bash','-x','/usr/sbin/vuseradd', '-t', vref, name, ], timeout=15*60)
111 # export slicename to the slice in /etc/slicename
112 file('/vservers/%s/etc/slicename' % name, 'w').write(name)
113 file('/vservers/%s/etc/slicefamily' % name, 'w').write(vref)
114 # set personality: only if needed (if arch's differ)
115 if tools.root_context_arch() != arch:
116 file('/etc/vservers/%s/personality' % name, 'w').write(personality(arch))
117 logger.log('sliver_vs: %s: set personality to %s'%(name,personality(arch)))
121 # logger.log_call(['/usr/sbin/vuserdel', name, ])
122 logger.log_call(['/bin/bash','-x','/usr/sbin/vuserdel', name, ])
124 def configure(self, rec):
125 new_rspec = rec['_rspec']
126 if new_rspec != self.rspec:
127 self.rspec = new_rspec
130 new_initscript = rec['initscript']
131 if new_initscript != self.initscript:
132 self.initscript = new_initscript
133 self.initscriptchanged = True
135 accounts.Account.configure(self, rec) # install ssh keys
137 def start(self, delay=0):
138 if self.rspec['enabled'] > 0:
139 logger.log('sliver_vs: %s: starting in %d seconds' % (self.name, delay))
141 # VServer.start calls fork() internally,
142 # so just close the nonstandard fds and fork once to avoid creating zombies
143 child_pid = os.fork()
145 if self.initscriptchanged:
146 logger.log('sliver_vs: %s: installing initscript' % self.name)
147 def install_initscript():
148 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
149 fd = os.open('/etc/rc.vinit', flags, 0755)
150 os.write(fd, self.initscript)
153 self.chroot_call(install_initscript)
154 except: logger.log_exc("sliver_vs: start",name=self.name)
155 tools.close_nonstandard_fds()
156 vserver.VServer.start(self)
159 os.waitpid(child_pid, 0)
160 self.initscriptchanged = False
161 else: logger.log('sliver_vs: not starting %s, is not enabled'%self.name)
164 logger.log('sliver_vs: %s: stopping' % self.name)
165 vserver.VServer.stop(self)
167 def is_running(self):
168 return vserver.VServer.is_running(self)
170 def set_resources(self,setup=False):
171 disk_max = self.rspec['disk_max']
172 logger.log('sliver_vs: %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 Sliver_VS._init_disk_info_sem.acquire()
177 logger.log('sliver_vs: %s: computing disk usage: beginning' % self.name)
178 try: self.init_disk_info()
179 finally: Sliver_VS._init_disk_info_sem.release()
180 logger.log('sliver_vs: %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_exc('sliver_vs: failed to set max disk usage',name=self.name)
186 # get/set the min/soft/hard values for all of the vserver
187 # related RLIMITS. Note that vserver currently only
188 # implements support for hard limits.
189 for limit in vserver.RLIMITS.keys():
191 minimum = self.rspec['%s_min'%type]
192 soft = self.rspec['%s_soft'%type]
193 hard = self.rspec['%s_hard'%type]
194 update = self.set_rlimit(limit, hard, soft, minimum)
196 logger.log('sliver_vs: %s: setting rlimit %s to (%d, %d, %d)'
197 % (self.name, type, hard, soft, minimum))
199 self.set_capabilities_config(self.rspec['capabilities'])
200 if self.rspec['capabilities']:
201 logger.log('sliver_vs: %s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
203 cpu_pct = self.rspec['cpu_pct']
204 cpu_share = self.rspec['cpu_share']
207 for key in self.rspec.keys():
208 if key.find('sysctl.') == 0:
209 sysctl=key.split('.')
211 path="/proc/sys/%s" % ("/".join(sysctl[1:]))
212 logger.log("sliver_vs: %s: opening %s"%(self.name,path))
214 fd = os.open(path, flags)
215 logger.log("sliver_vs: %s: writing %s=%s"%(self.name,key,self.rspec[key]))
216 os.write(fd,self.rspec[key])
219 logger.log("sliver_vs: %s: could not set %s=%s"%(self.name,key,self.rspec[key]))
220 logger.log("sliver_vs: %s: error = %s"%(self.name,e))
223 if self.rspec['enabled'] > 0:
225 logger.log('sliver_vs: %s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
230 logger.log('sliver_vs: %s: setting cpu share to %d' % (self.name, cpu_share))
234 self.set_sched_config(cpu_pct, cpu_share)
235 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
236 if self.rspec['ip_addresses'] != '0.0.0.0':
237 logger.log('sliver_vs: %s: setting IP address(es) to %s' % \
238 (self.name, self.rspec['ip_addresses']))
239 self.set_ipaddresses_config(self.rspec['ip_addresses'])
241 #logger.log("sliver_vs: %s: Setting name to %s" % (self.name, self.slice_id))
242 #self.setname(self.slice_id)
243 #logger.log("sliver_vs: %s: Storing slice id of %s for PlanetFlow" % (self.name, self.slice_id))
245 vserver_config_path = '/etc/vservers/%s'%self.name
246 if not os.path.exists (vserver_config_path):
247 os.makedirs (vserver_config_path)
248 file('%s/slice_id'%vserver_config_path, 'w').write("%d"%self.slice_id)
249 logger.log("sliver_vs: Recorded slice id %d for slice %s"%(self.slice_id,self.name))
251 logger.log("sliver_vs: Could not record slice_id for slice %s. Error: %s"%(self.name,str(e)))
253 logger.log_exc("sliver_vs: Error recording slice id: %s"%str(e),name=self.name)
256 if self.enabled == False:
260 if False: # Does not work properly yet.
261 if self.have_limits_changed():
262 logger.log('sliver_vs: %s: limits have changed --- restarting' % self.name)
264 while self.is_running() and stopcount > 0:
268 stopcount = stopcount - 1
271 else: # tell vsh to disable remote login by setting CPULIMIT to 0
272 logger.log('sliver_vs: %s: disabling remote login' % self.name)
273 self.set_sched_config(0, 0)