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):
49 vserver.VServer.__init__(self, rec['name'])
50 except Exception, err:
51 if not isinstance(err, vserver.NoSuchVServer):
52 # Probably a bad vserver or vserver configuration file
53 logger.log_exc(self.name)
54 logger.log('%s: recreating bad vserver' % rec['name'])
55 self.destroy(rec['name'])
56 self.create(rec['name'], rec['vref'])
57 vserver.VServer.__init__(self, rec['name'])
62 self.disk_usage_initialized = False
63 self.initscriptchanged = False
67 def create(name, vref = None):
69 logger.log_call('/usr/sbin/vuseradd', '-t', vref, name)
71 logger.log_call('/usr/sbin/vuseradd', name)
72 open('/vservers/%s/etc/slicename' % name, 'w').write(name)
75 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
77 def configure(self, rec):
78 new_rspec = rec['_rspec']
79 if new_rspec != self.rspec:
80 self.rspec = new_rspec
83 new_initscript = rec['initscript']
84 if new_initscript != self.initscript:
85 self.initscript = new_initscript
86 logger.log('%s: installing initscript' % self.name)
87 def install_initscript():
88 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
89 fd = os.open('/etc/rc.vinit', flags, 0755)
90 os.write(fd, new_initscript)
93 self.chroot_call(install_initscript)
94 self.initscriptchanged = True
95 except: logger.log_exc(self.name)
97 accounts.Account.configure(self, rec) # install ssh keys
99 def start(self, delay=0):
100 if self.rspec['enabled'] > 0:
101 logger.log('%s: starting in %d seconds' % (self.name, delay))
103 child_pid = os.fork()
105 # VServer.start calls fork() internally, so just close the nonstandard fds and fork once to avoid creating zombies
106 tools.close_nonstandard_fds()
107 vserver.VServer.start(self, True)
109 else: os.waitpid(child_pid, 0)
110 else: logger.log('%s: not starting, is not enabled' % self.name)
111 self.initscriptchanged = False
114 logger.log('%s: stopping' % self.name)
115 vserver.VServer.stop(self)
117 def set_resources(self):
118 disk_max = self.rspec['disk_max']
119 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
120 try: # if the sliver is over quota, .set_disk_limit will throw an exception
121 if not self.disk_usage_initialized:
122 self.vm_running = False
123 logger.log('%s: computing disk usage: beginning' % self.name)
124 Sliver_VS._init_disk_info_sem.acquire()
125 try: self.init_disk_info()
126 finally: Sliver_VS._init_disk_info_sem.release()
127 logger.log('%s: computing disk usage: ended' % self.name)
128 self.disk_usage_initialized = True
129 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
131 logger.log('%s: failed to set max disk usage' % self.name)
132 logger.log_exc(self.name)
134 # get/set the min/soft/hard values for all of the vserver
135 # related RLIMITS. Note that vserver currently only
136 # implements support for hard limits.
137 for limit in vserver.RLIMITS.keys():
139 minimum = self.rspec['%s_min'%type]
140 soft = self.rspec['%s_soft'%type]
141 hard = self.rspec['%s_hard'%type]
142 self.set_rlimit_config(limit, hard, soft, minimum)
144 self.set_capabilities_config(self.rspec['capabilities'])
145 if self.rspec['capabilities']:
146 logger.log('%s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
148 if False: # this code was commented out before
149 # N.B. net_*_rate are in kbps because of XML-RPC maxint
150 # limitations, convert to bps which is what bwlimit.py expects.
151 net_limits = (self.rspec['net_min_rate'] * 1000,
152 self.rspec['net_max_rate'] * 1000,
153 self.rspec['net_i2_min_rate'] * 1000,
154 self.rspec['net_i2_max_rate'] * 1000,
155 self.rspec['net_share'])
156 logger.log('%s: setting net limits to %s bps' % (self.name, net_limits[:-1]))
157 logger.log('%s: setting net share to %d' % (self.name, net_limits[-1]))
158 self.set_bwlimit(*net_limits)
160 cpu_min = self.rspec['cpu_min']
161 cpu_share = self.rspec['cpu_share']
163 if self.rspec['enabled'] > 0:
164 if cpu_min >= 50: # at least 5%: keep people from shooting themselves in the foot
165 logger.log('%s: setting cpu share to %d%% guaranteed' % (self.name, cpu_min/10.0))
166 self.set_sched_config(cpu_min, vserver.SCHED_CPU_GUARANTEED)
168 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
169 self.set_sched_config(cpu_share, 0)
171 if self.rspec['ip_addresses'] != '0.0.0.0':
172 logger.log('%s: setting IP address(es) to %s' % (self.name, self.rspec['ip_addresses']))
173 self.set_ipaddresses_config(self.rspec['ip_addresses'])
175 if False: # Does not work properly yet.
176 if self.have_limits_changed():
177 logger.log('%s: limits have changed --- restarting' % self.name)
179 while self.is_running() and stopcount > 0:
183 stopcount = stopcount - 1
186 else: # tell vsh to disable remote login by setting CPULIMIT to 0
187 logger.log('%s: disabling remote login' % self.name)
188 self.set_sched_config(0, 0)