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.
26 from threading import BoundedSemaphore
34 # special constant that tells vserver to keep its existing settings
35 KEEP_LIMIT = vserver.VC_LIM_KEEP
37 # populate the sliver/vserver specific default allocations table,
38 # which is used to look for slice attributes
39 DEFAULT_ALLOCATION = {}
40 for rlimit in vserver.RLIMITS.keys():
42 DEFAULT_ALLOCATION["%s_min"%rlim]=KEEP_LIMIT
43 DEFAULT_ALLOCATION["%s_soft"%rlim]=KEEP_LIMIT
44 DEFAULT_ALLOCATION["%s_hard"%rlim]=KEEP_LIMIT
46 class Sliver_VS(accounts.Account, vserver.VServer):
47 """This class wraps vserver.VServer to make its interface closer to what we need."""
50 TYPE = 'sliver.VServer'
51 _init_disk_info_sem = BoundedSemaphore()
53 def __init__(self, rec):
55 logger.verbose ('sliver_vs: %s init'%name)
57 logger.log("sliver_vs: %s: first chance..."%name)
58 vserver.VServer.__init__(self, name,logfile='/var/log/nodemanager')
59 except Exception, err:
60 if not isinstance(err, vserver.NoSuchVServer):
61 # Probably a bad vserver or vserver configuration file
62 logger.log_exc("sliver_vs:__init__ (first chance) %s",name=name)
63 logger.log('sliver_vs: %s: recreating bad vserver' % name)
65 self.create(name, rec['vref'])
66 logger.log("sliver_vs: %s: second chance..."%name)
67 vserver.VServer.__init__(self, name,logfile='/var/log/nodemanager')
72 self.slice_id = rec['slice_id']
73 self.disk_usage_initialized = False
74 self.initscriptchanged = False
79 def create(name, vref = None):
80 logger.verbose('sliver_vs: %s: create'%name)
82 logger.log("sliver_vs: %s: ERROR - no vref attached, this is unexpected"%(name))
84 # used to look in /etc/planetlab/family,
85 # now relies on the 'GetSliceFamily' extra attribute in GetSlivers()
86 # which for legacy is still exposed here as the 'vref' key
88 # check the template exists -- there's probably a better way..
89 if not os.path.isdir ("/vservers/.vref/%s"%vref):
90 logger.log ("sliver_vs: %s: ERROR Could not create sliver - vreference image %s not found"%(name,vref))
95 (x,y,arch)=vref.split('-')
96 # mh, this of course applies when 'vref' is e.g. 'netflow'
97 # and that's not quite right
101 def personality (arch):
102 personality="linux32"
103 if arch.find("64")>=0:
104 personality="linux64"
107 # logger.log_call(['/usr/sbin/vuseradd', '-t', vref, name, ], timeout=15*60)
108 logger.log_call(['/bin/bash','-x','/usr/sbin/vuseradd', '-t', vref, name, ], timeout=15*60)
109 # export slicename to the slice in /etc/slicename
110 file('/vservers/%s/etc/slicename' % name, 'w').write(name)
111 file('/vservers/%s/etc/slicefamily' % name, 'w').write(vref)
112 # set personality: only if needed (if arch's differ)
113 if tools.root_context_arch() != arch:
114 file('/etc/vservers/%s/personality' % name, 'w').write(personality(arch)+"\n")
115 logger.log('sliver_vs: %s: set personality to %s'%(name,personality(arch)))
119 # logger.log_call(['/usr/sbin/vuserdel', name, ])
120 logger.log_call(['/bin/bash','-x','/usr/sbin/vuserdel', name, ])
122 def configure(self, rec):
123 new_rspec = rec['_rspec']
124 if new_rspec != self.rspec:
125 self.rspec = new_rspec
128 new_initscript = rec['initscript']
129 if new_initscript != self.initscript:
130 self.initscript = new_initscript
131 self.initscriptchanged = True
133 accounts.Account.configure(self, rec) # install ssh keys
135 def start(self, delay=0):
136 if self.rspec['enabled'] > 0:
137 logger.log('sliver_vs: %s: starting in %d seconds' % (self.name, delay))
139 # VServer.start calls fork() internally,
140 # so just close the nonstandard fds and fork once to avoid creating zombies
141 child_pid = os.fork()
143 if self.initscriptchanged:
144 logger.log('sliver_vs: %s: installing initscript' % self.name)
145 def install_initscript():
146 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
147 fd = os.open('/etc/rc.vinit', flags, 0755)
148 os.write(fd, self.initscript)
151 self.chroot_call(install_initscript)
152 except: logger.log_exc("sliver_vs: start",name=self.name)
153 tools.close_nonstandard_fds()
154 vserver.VServer.start(self)
157 os.waitpid(child_pid, 0)
158 self.initscriptchanged = False
159 else: logger.log('sliver_vs: not starting %s, is not enabled'%self.name)
162 logger.log('sliver_vs: %s: stopping' % self.name)
163 vserver.VServer.stop(self)
165 def is_running(self):
166 return vserver.VServer.is_running(self)
168 def set_resources(self,setup=False):
169 disk_max = self.rspec['disk_max']
170 logger.log('sliver_vs: %s: setting max disk usage to %d KiB' % (self.name, disk_max))
171 try: # if the sliver is over quota, .set_disk_limit will throw an exception
172 if not self.disk_usage_initialized:
173 self.vm_running = False
174 Sliver_VS._init_disk_info_sem.acquire()
175 logger.log('sliver_vs: %s: computing disk usage: beginning' % self.name)
176 try: self.init_disk_info()
177 finally: Sliver_VS._init_disk_info_sem.release()
178 logger.log('sliver_vs: %s: computing disk usage: ended' % self.name)
179 self.disk_usage_initialized = True
180 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
182 logger.log_exc('sliver_vs: failed to set max disk usage',name=self.name)
184 # get/set the min/soft/hard values for all of the vserver
185 # related RLIMITS. Note that vserver currently only
186 # implements support for hard limits.
187 for limit in vserver.RLIMITS.keys():
189 minimum = self.rspec['%s_min'%type]
190 soft = self.rspec['%s_soft'%type]
191 hard = self.rspec['%s_hard'%type]
192 update = self.set_rlimit(limit, hard, soft, minimum)
194 logger.log('sliver_vs: %s: setting rlimit %s to (%d, %d, %d)'
195 % (self.name, type, hard, soft, minimum))
197 self.set_capabilities_config(self.rspec['capabilities'])
198 if self.rspec['capabilities']:
199 logger.log('sliver_vs: %s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
201 cpu_pct = self.rspec['cpu_pct']
202 cpu_share = self.rspec['cpu_share']
205 for key in self.rspec.keys():
206 if key.find('sysctl.') == 0:
207 sysctl=key.split('.')
209 path="/proc/sys/%s" % ("/".join(sysctl[1:]))
210 logger.log("sliver_vs: %s: opening %s"%(self.name,path))
212 fd = os.open(path, flags)
213 logger.log("sliver_vs: %s: writing %s=%s"%(self.name,key,self.rspec[key]))
214 os.write(fd,self.rspec[key])
217 logger.log("sliver_vs: %s: could not set %s=%s"%(self.name,key,self.rspec[key]))
218 logger.log("sliver_vs: %s: error = %s"%(self.name,e))
221 if self.rspec['enabled'] > 0:
223 logger.log('sliver_vs: %s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
228 logger.log('sliver_vs: %s: setting cpu share to %d' % (self.name, cpu_share))
232 self.set_sched_config(cpu_pct, cpu_share)
233 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
234 if self.rspec['ip_addresses'] != '0.0.0.0':
235 logger.log('sliver_vs: %s: setting IP address(es) to %s' % \
236 (self.name, self.rspec['ip_addresses']))
237 self.set_ipaddresses_config(self.rspec['ip_addresses'])
239 #logger.log("sliver_vs: %s: Setting name to %s" % (self.name, self.slice_id))
240 #self.setname(self.slice_id)
241 #logger.log("sliver_vs: %s: Storing slice id of %s for PlanetFlow" % (self.name, self.slice_id))
243 vserver_config_path = '/etc/vservers/%s'%self.name
244 if not os.path.exists (vserver_config_path):
245 os.makedirs (vserver_config_path)
246 file('%s/slice_id'%vserver_config_path, 'w').write("%d\n"%self.slice_id)
247 logger.log("sliver_vs: Recorded slice id %d for slice %s"%(self.slice_id,self.name))
249 logger.log("sliver_vs: Could not record slice_id for slice %s. Error: %s"%(self.name,str(e)))
251 logger.log_exc("sliver_vs: Error recording slice id: %s"%str(e),name=self.name)
254 if self.enabled == False:
258 if False: # Does not work properly yet.
259 if self.have_limits_changed():
260 logger.log('sliver_vs: %s: limits have changed --- restarting' % self.name)
262 while self.is_running() and stopcount > 0:
266 stopcount = stopcount - 1
269 else: # tell vsh to disable remote login by setting CPULIMIT to 0
270 logger.log('sliver_vs: %s: disabling remote login' % self.name)
271 self.set_sched_config(0, 0)