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)
71 logger.log_call('/usr/sbin/vuseradd', '-t', vref, name)
73 logger.log_call('/usr/sbin/vuseradd', name)
74 open('/vservers/%s/etc/slicename' % name, 'w').write(name)
77 def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
79 def configure(self, rec):
80 new_rspec = rec['_rspec']
81 if new_rspec != self.rspec:
82 self.rspec = new_rspec
85 new_initscript = rec['initscript']
86 if new_initscript != self.initscript:
87 self.initscript = new_initscript
88 logger.log('%s: installing initscript' % self.name)
89 def install_initscript():
90 flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
91 fd = os.open('/etc/rc.vinit', flags, 0755)
92 os.write(fd, new_initscript)
95 self.chroot_call(install_initscript)
96 self.initscriptchanged = True
97 except: logger.log_exc(self.name)
99 accounts.Account.configure(self, rec) # install ssh keys
101 def start(self, delay=0):
102 if self.rspec['enabled'] > 0:
103 logger.log('%s: starting in %d seconds' % (self.name, delay))
105 child_pid = os.fork()
107 # VServer.start calls fork() internally, so just close the nonstandard fds and fork once to avoid creating zombies
108 tools.close_nonstandard_fds()
109 vserver.VServer.start(self, True)
111 else: os.waitpid(child_pid, 0)
112 else: logger.log('%s: not starting, is not enabled' % self.name)
113 self.initscriptchanged = False
116 logger.log('%s: stopping' % self.name)
117 vserver.VServer.stop(self)
119 def set_resources(self):
120 disk_max = self.rspec['disk_max']
121 logger.log('%s: setting max disk usage to %d KiB' % (self.name, disk_max))
122 try: # if the sliver is over quota, .set_disk_limit will throw an exception
123 if not self.disk_usage_initialized:
124 self.vm_running = False
125 logger.log('%s: computing disk usage: beginning' % self.name)
126 Sliver_VS._init_disk_info_sem.acquire()
127 try: self.init_disk_info()
128 finally: Sliver_VS._init_disk_info_sem.release()
129 logger.log('%s: computing disk usage: ended' % self.name)
130 self.disk_usage_initialized = True
131 vserver.VServer.set_disklimit(self, max(disk_max, self.disk_blocks))
133 logger.log('%s: failed to set max disk usage' % self.name)
134 logger.log_exc(self.name)
136 # get/set the min/soft/hard values for all of the vserver
137 # related RLIMITS. Note that vserver currently only
138 # implements support for hard limits.
139 for limit in vserver.RLIMITS.keys():
141 minimum = self.rspec['%s_min'%type]
142 soft = self.rspec['%s_soft'%type]
143 hard = self.rspec['%s_hard'%type]
144 self.set_rlimit_config(limit, hard, soft, minimum)
146 self.set_capabilities_config(self.rspec['capabilities'])
147 if self.rspec['capabilities']:
148 logger.log('%s: setting capabilities to %s' % (self.name, self.rspec['capabilities']))
150 if False: # this code was commented out before
151 # N.B. net_*_rate are in kbps because of XML-RPC maxint
152 # limitations, convert to bps which is what bwlimit.py expects.
153 net_limits = (self.rspec['net_min_rate'] * 1000,
154 self.rspec['net_max_rate'] * 1000,
155 self.rspec['net_i2_min_rate'] * 1000,
156 self.rspec['net_i2_max_rate'] * 1000,
157 self.rspec['net_share'])
158 logger.log('%s: setting net limits to %s bps' % (self.name, net_limits[:-1]))
159 logger.log('%s: setting net share to %d' % (self.name, net_limits[-1]))
160 self.set_bwlimit(*net_limits)
162 cpu_min = self.rspec['cpu_min']
163 cpu_share = self.rspec['cpu_share']
165 if self.rspec['enabled'] > 0:
166 if cpu_min >= 50: # at least 5%: keep people from shooting themselves in the foot
167 logger.log('%s: setting cpu share to %d%% guaranteed' % (self.name, cpu_min/10.0))
168 self.set_sched_config(cpu_min, vserver.SCHED_CPU_GUARANTEED)
170 logger.log('%s: setting cpu share to %d' % (self.name, cpu_share))
171 self.set_sched_config(cpu_share, 0)
172 # if IP address isn't set (even to 0.0.0.0), sliver won't be able to use network
173 if self.rspec['ip_addresses'] != '0.0.0.0':
174 logger.log('%s: setting IP address(es) to %s' % (self.name, self.rspec['ip_addresses']))
175 self.set_ipaddresses_config(self.rspec['ip_addresses'])
177 if False: # Does not work properly yet.
178 if self.have_limits_changed():
179 logger.log('%s: limits have changed --- restarting' % self.name)
181 while self.is_running() and stopcount > 0:
185 stopcount = stopcount - 1
188 else: # tell vsh to disable remote login by setting CPULIMIT to 0
189 logger.log('%s: disabling remote login' % self.name)
190 self.set_sched_config(0, 0)