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):
56 logger.verbose ('initing Sliver_VS with name=%s'%rec['name'])
58 vserver.VServer.__init__(self, rec['name'],logfile='/var/log/nm')
59 except Exception, err:
60 if not isinstance(err, vserver.NoSuchVServer):
61 # Probably a bad vserver or vserver configuration file
62 logger.log_exc(self.name)
63 logger.log('%s: recreating bad vserver' % rec['name'])
64 self.destroy(rec['name'])
65 self.create(rec['name'], rec['vref'])
66 vserver.VServer.__init__(self, rec['name'],logfile='/var/log/nm')
71 self.slice_id = rec['slice_id']
72 self.disk_usage_initialized = False
73 self.initscriptchanged = False
78 def create(name, vref = None):
79 logger.verbose('Sliver_VS:create - name=%s'%name)
81 logger.log("creating %s : no vref attached, this is unexpected"%name)
83 # used to look in /etc/planetlab/family,
84 # now relies on the 'GetSliceFamily' extra attribute in GetSlivers()
85 # which for legacy is still exposed here as the 'vref' key
87 # check the template exists -- there's probably a better way..
88 if not os.path.isdir ("/vservers/.vref/%s"%vref):
89 logger.log ("%s: ERROR Could not create sliver - vreference image %s not found"%(name,vref))
94 (x,y,arch)=vref.split('-')
98 def personality (arch):
100 if arch.find("64")>=0:
101 personality="linux64"
104 logger.log_call('/usr/sbin/vuseradd', '-t', vref, name)
105 # export slicename to the slice in /etc/slicename
106 file('/vservers/%s/etc/slicename' % name, 'w').write(name)
107 file('/vservers/%s/etc/slicefamily' % name, 'w').write(vref)
108 # set personality: only if needed (if arch's differ)
109 if tools.root_context_arch() != arch:
110 file('/etc/vservers/%s/personality' % name, 'w').write(personality(arch))
111 logger.log('%s: set personality to %s'%(name,personality(arch)))
114 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
116 def configure(self, rec):
117 new_rspec = rec['_rspec']
118 if new_rspec != self.rspec:
119 self.rspec = new_rspec
122 new_initscript = rec['initscript']
123 if new_initscript != self.initscript:
124 self.initscript = new_initscript
125 self.initscriptchanged = True
127 accounts.Account.configure(self, rec) # install ssh keys
129 def start(self, delay=0):
130 if self.rspec['enabled'] > 0:
131 logger.log('%s: starting in %d seconds' % (self.name, delay))
133 # VServer.start calls fork() internally,
134 # so just close the nonstandard fds and fork once to avoid creating zombies
135 child_pid = os.fork()
137 if self.initscriptchanged:
138 logger.log('%s: installing initscript' % self.name)
139 def install_initscript():
140 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
141 fd = os.open('/etc/rc.vinit', flags, 0755)
142 os.write(fd, self.initscript)
145 self.chroot_call(install_initscript)
146 except: logger.log_exc(self.name)
147 tools.close_nonstandard_fds()
148 vserver.VServer.start(self)
151 os.waitpid(child_pid, 0)
152 self.initscriptchanged = False
153 else: logger.log('%s: not starting, is not enabled' % self.name)
156 logger.log('%s: stopping' % self.name)
157 vserver.VServer.stop(self)
159 def is_running(self):
160 return vserver.VServer.is_running(self)
162 def set_resources(self,setup=False):
163 disk_max = self.rspec['disk_max']
164 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
165 try: # if the sliver is over quota, .set_disk_limit will throw an exception
166 if not self.disk_usage_initialized:
167 self.vm_running = False
168 Sliver_VS._init_disk_info_sem.acquire()
169 logger.log('%s: computing disk usage: beginning' % self.name)
170 try: self.init_disk_info()
171 finally: Sliver_VS._init_disk_info_sem.release()
172 logger.log('%s: computing disk usage: ended' % self.name)
173 self.disk_usage_initialized = True
174 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
176 logger.log('%s: failed to set max disk usage' % self.name)
177 logger.log_exc(self.name)
179 # get/set the min/soft/hard values for all of the vserver
180 # related RLIMITS. Note that vserver currently only
181 # implements support for hard limits.
182 for limit in vserver.RLIMITS.keys():
184 minimum = self.rspec['%s_min'%type]
185 soft = self.rspec['%s_soft'%type]
186 hard = self.rspec['%s_hard'%type]
187 update = self.set_rlimit(limit, hard, soft, minimum)
189 logger.log('%s: setting rlimit %s to (%d, %d, %d)'
190 % (self.name, type, hard, soft, minimum))
192 self.set_capabilities_config(self.rspec['capabilities'])
193 if self.rspec['capabilities']:
194 logger.log('%s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
196 cpu_pct = self.rspec['cpu_pct']
197 cpu_share = self.rspec['cpu_share']
200 for key in self.rspec.keys():
201 if key.find('sysctl.') == 0:
202 sysctl=key.split('.')
204 path="/proc/sys/%s" % ("/".join(sysctl[1:]))
205 logger.log("%s: opening %s"%(self.name,path))
207 fd = os.open(path, flags)
208 logger.log("%s: writing %s=%s"%(self.name,key,self.rspec[key]))
209 os.write(fd,self.rspec[key])
212 logger.log("%s: could not set %s=%s"%(self.name,key,self.rspec[key]))
213 logger.log("%s: error = %s"%(self.name,e))
216 if self.rspec['enabled'] > 0:
218 logger.log('%s: setting cpu reservation to %d%%' % (self.name, cpu_pct))
223 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
227 self.set_sched_config(cpu_pct, cpu_share)
228 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
229 if self.rspec['ip_addresses'] != '0.0.0.0':
230 logger.log('%s: setting IP address(es) to %s' % \
231 (self.name, self.rspec['ip_addresses']))
232 self.set_ipaddresses_config(self.rspec['ip_addresses'])
234 if self.is_running():
235 logger.log("%s: Setting name to %s" % (self.name, self.slice_id))
236 self.setname(self.slice_id)
237 ### Sapan's change needs more work
238 # raise IOException, file does not get created
239 # might be that /etc/vservers is not available here, are we in the chroot ?
240 #logger.log("%s: Storing slice id of %s for PlanetFlow" % (self.name, self.slice_id))
241 #file('/etc/vservers/%s/slice_id' % self.name, 'w').write(self.slice_id)
243 if self.enabled == False:
247 if False: # Does not work properly yet.
248 if self.have_limits_changed():
249 logger.log('%s: limits have changed --- restarting' % self.name)
251 while self.is_running() and stopcount > 0:
255 stopcount = stopcount - 1
258 else: # tell vsh to disable remote login by setting CPULIMIT to 0
259 logger.log('%s: disabling remote login' % self.name)
260 self.set_sched_config(0, 0)