"""Functionality common to all account classes.
-Each subclass of Account must provide 6 methods: create() and
-destroy(), which are static; configure(), is_running(), start(), and stop(),
-which are not. configure() takes a record as its only argument, and does
+Each subclass of Account must provide five methods: create() and
+destroy(), which are static; configure(), start(), and stop(), which
+are not. configure(), which takes a record as its only argument, does
things like set up ssh keys. In addition, an Account subclass must
provide static member variables SHELL, which contains the unique shell
that it uses; and TYPE, a string that is used by the account creation
periods; at the moment the only convention is that all sliver accounts
have type that begins with sliver.
+There are any number of race conditions that may result from the fact
+that account names are not unique over time. Moreover, it's a bad
+idea to perform lengthy operations while holding the database lock.
+In order to deal with both of these problems, we use a worker thread
+for each account name that ever exists. On 32-bit systems with large
+numbers of accounts, this may cause the NM process to run out of
+*virtual* memory! This problem may be remedied by decreasing the
+maximum stack size.
"""
import Queue
import pwd
import sha
import string
-import threading
import curlwrapper
import logger
import tools
import xmlrpclib
+from config import Config
class conf_files:
- def __init__(self, config, noscripts=False):
- self.config = config
+ def __init__(self, noscripts=False):
+ self.config = Config()
self.noscripts = noscripts
- self.cond = threading.Condition()
self.data = None
def checksum(self, path):
if self.system(cf_rec['postinstall_cmd']): self.system(err_cmd)
def run_once(self, data):
- for f in data['conf_files']:
- try: self.update_conf_file(f)
- except: logger.log_exc()
+ if data.has_key("conf_files"):
+ for f in data['conf_files']:
+ try: self.update_conf_file(f)
+ except: logger.log_exc()
+ else: logger.log("conf_files: No conf_files found or API failure. Skipping")
- def run(self):
- while True:
- self.cond.acquire()
- while self.data == None: self.cond.wait()
- data = self.data
- self.data = None
- self.cond.release()
- self.run_once(data)
- def callback(self, data):
- if data != None:
- self.cond.acquire()
- self.data = data
- self.cond.notify()
- self.cond.release()
-
-main = None
-
-def start(options, config):
- global main
- main = conf_files(config)
- tools.as_daemon_thread(main.run)
+def start(options, config): pass
def GetSlivers(data):
- global main
- assert main is not None
- return main.callback(data)
+ logger.log("conf_files: Running.")
+ cf = conf_files()
+ cf.run_once(data)
+ logger.log("conf_files: Done.")
if __name__ == '__main__':
import optparse
(options, args) = parser.parse_args()
# Load /etc/planetlab/plc_config
- from config import Config
config = Config(options.config)
# Load /etc/planetlab/session
from plcapi import PLCAPI
plc = PLCAPI(config.plc_api_uri, config.cacert, auth = session)
- main = conf_files(config, options.noscripts)
+ main = conf_files(options.noscripts)
data = plc.GetSlivers()
main.run_once(data)
import logger
import tools
from pwd import getpwnam
+from grp import getgrnam
class Controller(accounts.Account):
SHELL = '/usr/bin/forward_api_calls' # tunneling shell
@staticmethod
def create(name, vref = None):
add_shell(Controller.SHELL)
- logger.log_call('/usr/sbin/useradd', '-p', '*', '-s', Controller.SHELL, name)
+ group = getgrnam("slices")[2]
+ logger.log_call('/usr/sbin/useradd', '-p', '*', '-g', str(group), '-s', Controller.SHELL, name)
@staticmethod
def destroy(name): logger.log_call('/usr/sbin/userdel', '-r', name)
def log_call(*args):
log('running command %s' % ' '.join(args))
- try: subprocess.call(args, close_fds=True)
+ try: subprocess.check_call(args, close_fds=True)
except: log_exc()
def log_exc(name = None):
#
# $Id$
#
+
"""network configuration"""
# system provided modules
def GetSlivers(plc, data, config):
logger.verbose("net:GetSlivers called.")
InitInterfaces(plc, data) # writes sysconfig files.
- if 'OVERRIDES' in dir(config):
+ if 'OVERRIDES' in dir(config):
if config.OVERRIDES.get('net_max_rate') == '-1':
logger.log("net: Slice and node BW Limits disabled.")
- if len(bwlimit.tc("class show dev eth0")):
+ if len(bwlimit.tc("class show dev eth0")):
logger.verbose("*** DISABLING NODE BW LIMITS ***")
bwlimit.stop()
else:
def InitNodeLimit(data):
+ if not 'networks' in data: return
+
# query running network interfaces
devs = sioc.gifconf()
ips = dict(zip(devs.values(), devs.keys()))
for network in data['networks']:
# Get interface name preferably from MAC address, falling
# back on IP address.
- if macs.has_key(network['mac'].lower()):
- dev = macs[network['mac'].lower()]
- elif ips.has_key(network['ip']):
+ hwaddr=network['mac']
+ if hwaddr <> None: hwaddr=hwaddr.lower()
+ if hwaddr in macs:
+ dev = macs[network['mac']]
+ elif network['ip'] in ips:
dev = ips[network['ip']]
else:
logger.log('%s: no such interface with address %s/%s' % (network['hostname'], network['ip'], network['mac']))
# again, or vice-versa.
def InitI2(plc, data):
+ if not 'groups' in data: return
+
if "Internet2" in data['groups']:
logger.log("This is an Internet2 node. Setting rules.")
i2nodes = []
os.popen("/sbin/iptables -t mangle " + cmd)
def InitNAT(plc, data):
+ if not 'networks' in data: return
+
# query running network interfaces
devs = sioc.gifconf()
ips = dict(zip(devs.values(), devs.keys()))
for network in data['networks']:
# Get interface name preferably from MAC address, falling
# back on IP address.
- if macs.has_key(network['mac']):
- dev = macs[network['mac'].lower()]
- elif ips.has_key(network['ip']):
+ hwaddr=network['mac']
+ if hwaddr <> None: hwaddr=hwaddr.lower()
+ if hwaddr in macs:
+ dev = macs[network['mac']]
+ elif network['ip'] in ips:
dev = ips[network['ip']]
else:
logger.log('%s: no such interface with address %s/%s' % (network['hostname'], network['ip'], network['mac']))
settings = plc.GetInterfaceTags({'interface_tag_id': network['interface_tag_ids']})
except:
continue
- # XXX arbitrary names
+
for setting in settings:
if setting['category'].upper() != 'FIREWALL':
continue
ipt.commit()
def InitInterfaces(plc, data):
- if not 'networks' in data:
- return
+ if not 'networks' in data: return
plnet.InitInterfaces(logger, plc, data)
def start(options, config):
"""
import errno
+import traceback
import os, os.path
import time
-import commands
import vserver
self.initscriptchanged = False
self.configure(rec)
- _root_context_arch=None
- @staticmethod
- def root_context_arch():
- if not Sliver_VS._root_context_arch:
- Sliver_VS._root_context_arch=commands.getoutput("uname -i")
- return Sliver_VS._root_context_arch
-
- @staticmethod
- def personality (arch):
- personality="linux32"
- if arch.find("64")>=0:
- personality="linux64"
- return personality
-
@staticmethod
def create(name, vref = None):
logger.verbose('Sliver_VS:create - name=%s'%name)
refname="-".join( (pldistro,fcdistro,arch) )
# check the template exists -- there's probably a better way..
- if os.path.isdir ("/vservers/.vref/%s"% vref): refname = vref
-
- if not os.path.isdir ("/vservers/.vref/%s"% refname):
+ if not os.path.isdir ("/vservers/.vref/%s"%refname):
logger.log("%s (%s) : vref %s not found, using default %s"%(
name,vref,refname,default))
refname=default
refname="default"
arch="i386"
except:
- import traceback
logger.log("%s (%s) : unexpected error follows - using 'default'"%(name,vref))
logger.log(traceback.format_exc())
refname="default"
arch="i386"
+ def personality (arch):
+ personality="linux32"
+ if arch.find("64")>=0:
+ personality="linux64"
+ return personality
+
logger.log_call('/usr/sbin/vuseradd', '-t', refname, name)
# export slicename to the slice in /etc/slicename
file('/vservers/%s/etc/slicename' % name, 'w').write(name)
# set personality: only if needed (if arch's differ)
- if Sliver_VS.root_context_arch() != arch:
- file('/etc/vservers/%s/personality' % name, 'w').write(Sliver_VS.personality(arch))
- logger.log('%s: set personality to %s'%(name,Sliver_VS.personality(arch)))
+ if tools.root_context_arch() != arch:
+ file('/etc/vservers/%s/personality' % name, 'w').write(personality(arch))
+ logger.log('%s: set personality to %s'%(name,personality(arch)))
@staticmethod
def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
self.initscript = new_initscript
self.initscriptchanged = True
- # install ssh keys
- accounts.Account.configure(self, rec)
+ accounts.Account.configure(self, rec) # install ssh keys
def start(self, delay=0):
if self.rspec['enabled'] > 0:
import api
import api_calls
import database
-import controller
+import controller
import logger
import sliver_vs
import string,re
## 'Type' isn't returned by GetSlivers() for whatever reason. We're overloading
## instantiation here, but i suppose its the ssame thing when you think about it. -FA
# Handle nm controller here
- if rec['instantiation'].lower() == 'nm-controller':
+ if rec['instantiation'].lower() == 'nm-controller':
rec.setdefault('type', attr_dict.get('type', 'controller.Controller'))
else:
rec.setdefault('type', attr_dict.get('type', 'sliver.VServer'))