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 # special constant that tells vserver to keep its existing settings
29 KEEP_LIMIT = vserver.VC_LIM_KEEP
31 # populate the sliver/vserver specific default allocations table,
32 # which is used to look for slice attributes
33 DEFAULT_ALLOCATION = {}
34 for rlimit in vserver.RLIMITS.keys():
36 DEFAULT_ALLOCATION["%s_min"%rlim]=KEEP_LIMIT
37 DEFAULT_ALLOCATION["%s_soft"%rlim]=KEEP_LIMIT
38 DEFAULT_ALLOCATION["%s_hard"%rlim]=KEEP_LIMIT
40 class Sliver_VS(accounts.Account, vserver.VServer):
41 """This class wraps vserver.VServer to make its interface closer to what we need."""
44 TYPE = 'sliver.VServer'
45 _init_disk_info_sem = tools.NMLock("/var/run/nm-disk-info.lock")
47 def __init__(self, rec):
48 logger.verbose ('initing Sliver_VS with name=%s'%rec['name'])
50 vserver.VServer.__init__(self, rec['name'],logfile='/var/log/nm')
51 except Exception, err:
52 if not isinstance(err, vserver.NoSuchVServer):
53 # Probably a bad vserver or vserver configuration file
54 logger.log_exc(self.name)
55 logger.log('%s: recreating bad vserver' % rec['name'])
56 self.destroy(rec['name'])
57 self.create(rec['name'], rec['vref'])
58 vserver.VServer.__init__(self, rec['name'],logfile='/var/log/nm')
63 self.disk_usage_initialized = False
64 self.initscriptchanged = False
68 def create(name, vref = None):
69 logger.verbose('Sliver_VS:create - name=%s'%name)
73 ### locating the right slicefamily
74 # this is a first draft, and more a proof of concept thing
75 # the idea is to parse vref for dash-separated wishes,
76 # and to project these against the defaults
77 # so e.g. if the default slice family (as found in /etc/planetlab/slicefamily)
78 # is planetlab-f8-i386, then here is what we get
79 # vref=x86_64 -> vuseradd -t planetlab-f8-x86_64
80 # vref=centos5 -> vuseradd -t planetlab-centos5-i386
81 # vref=centos5-onelab -> vuseradd -t onelab-centos5-i386
82 # vref=planetflow -> vuseradd -t planetflow-f8-i386
83 # vref=x86_64-planetflow -> vuseradd -t planetflow-f8-x86_64
86 default=file("/etc/planetlab/slicefamily").read().strip()
87 (pldistro,fcdistro,arch) = default.split("-")
88 # from the slice attribute: cut dashes and try to figure the meaning
89 slice_wishes = vref.split("-")
90 for wish in slice_wishes:
91 if wish == "i386" or wish == "x86_64":
93 elif wish == "f8" or wish == "centos5" :
99 refname="-".join( (pldistro,fcdistro,arch) )
101 # check the templates exists -- there's probably a better way..
102 if not os.path.isdir ("/vservers/.vref/%s"%refname):
103 logger.verbose("%s (%s) : vref %s not found, using default %s"%(
104 name,vref,refname,default))
106 # could check again, but as we have /etc/slicefamily
107 # there's probably no /vservers/.vref/default
110 # have not found slicefamily
111 logger.verbose("%s (%s): legacy node - using fallback vrefname 'default'"%(name,vref))
116 logger.log("%s (%s) : unexpected error follows - using 'default'"%(
118 logger.log(traceback.format_exc())
121 logger.log_call('/usr/sbin/vuseradd', '-t', refname, name)
122 open('/vservers/%s/etc/slicename' % name, 'w').write(name)
125 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
127 def configure(self, rec):
128 new_rspec = rec['_rspec']
129 if new_rspec != self.rspec:
130 self.rspec = new_rspec
133 new_initscript = rec['initscript']
134 if new_initscript != self.initscript:
135 self.initscript = new_initscript
136 logger.log('%s: installing initscript' % self.name)
137 def install_initscript():
138 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
139 fd = os.open('/etc/rc.vinit', flags, 0755)
140 os.write(fd, new_initscript)
143 self.chroot_call(install_initscript)
144 self.initscriptchanged = True
145 except: logger.log_exc(self.name)
147 accounts.Account.configure(self, rec) # install ssh keys
149 def start(self, delay=0):
150 if self.rspec['enabled'] > 0:
151 logger.log('%s: starting in %d seconds' % (self.name, delay))
153 child_pid = os.fork()
155 # VServer.start calls fork() internally, so just close the nonstandard fds and fork once to avoid creating zombies
156 tools.close_nonstandard_fds()
157 vserver.VServer.start(self, True)
159 else: os.waitpid(child_pid, 0)
160 else: logger.log('%s: not starting, is not enabled' % self.name)
161 self.initscriptchanged = False
164 logger.log('%s: stopping' % self.name)
165 vserver.VServer.stop(self)
167 def set_resources(self):
168 disk_max = self.rspec['disk_max']
169 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
170 try: # if the sliver is over quota, .set_disk_limit will throw an exception
171 if not self.disk_usage_initialized:
172 self.vm_running = False
173 logger.log('%s: computing disk usage: beginning' % self.name)
174 Sliver_VS._init_disk_info_sem.acquire()
175 try: self.init_disk_info()
176 finally: Sliver_VS._init_disk_info_sem.release()
177 logger.log('%s: computing disk usage: ended' % self.name)
178 self.disk_usage_initialized = True
179 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
181 logger.log('%s: failed to set max disk usage' % self.name)
182 logger.log_exc(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('%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('%s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
201 if False: # this code was commented out before
202 # N.B. net_*_rate are in kbps because of XML-RPC maxint
203 # limitations, convert to bps which is what bwlimit.py expects.
204 net_limits = (self.rspec['net_min_rate'] * 1000,
205 self.rspec['net_max_rate'] * 1000,
206 self.rspec['net_i2_min_rate'] * 1000,
207 self.rspec['net_i2_max_rate'] * 1000,
208 self.rspec['net_share'])
209 logger.log('%s: setting net limits to %s bps' % (self.name, net_limits[:-1]))
210 logger.log('%s: setting net share to %d' % (self.name, net_limits[-1]))
211 self.set_bwlimit(*net_limits)
213 cpu_pct = self.rspec['cpu_pct']
214 cpu_share = self.rspec['cpu_share']
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' % (self.name, self.rspec['ip_addresses']))
231 self.set_ipaddresses_config(self.rspec['ip_addresses'])
233 if False: # Does not work properly yet.
234 if self.have_limits_changed():
235 logger.log('%s: limits have changed --- restarting' % self.name)
237 while self.is_running() and stopcount > 0:
241 stopcount = stopcount - 1
244 else: # tell vsh to disable remote login by setting CPULIMIT to 0
245 logger.log('%s: disabling remote login' % self.name)
246 self.set_sched_config(0, 0)