(Boeing r1755)

allow specifying multiple controlnet prefixes for distributed sessions

a callback causes the broker to munge the CONF message so that controlnet
  prefixes are assigned to slave servers by the master server
This commit is contained in:
ahrenholz 2013-08-29 17:51:19 +00:00
parent 7e63173d07
commit ce65d617c5

View file

@ -727,11 +727,10 @@ class Session(object):
continue continue
n.validate() n.validate()
def addremovectrlif(self, node, remove=False): def addremovectrlnet(self, remove=False):
''' Add a control interface to a node when a 'controlnet' prefix is ''' Create a control network bridge as necessary.
listed in the config file. Create a control interface bridge as When the remove flag is True, remove the bridge that connects control
necessary. When the remove flag is True, remove the bridge that interfaces.
connects control interfaces.
''' '''
prefix = None prefix = None
try: try:
@ -742,31 +741,73 @@ class Session(object):
if hasattr(self.options, 'controlnet'): if hasattr(self.options, 'controlnet'):
prefix = self.options.controlnet prefix = self.options.controlnet
if not prefix: if not prefix:
return return None # no controlnet needed
# return any existing controlnet bridge
id = "ctrlnet"
try:
ctrlnet = self.obj(id)
if remove:
self.delobj(ctrlnet.objid)
return None
return ctrlnet
except KeyError:
if remove:
return None
# build a new controlnet bridge
updown_script = None updown_script = None
try: try:
if self.cfg['controlnet_updown_script']: if self.cfg['controlnet_updown_script']:
updown_script = self.cfg['controlnet_updown_script'] updown_script = self.cfg['controlnet_updown_script']
except KeyError: except KeyError:
pass pass
prefixes = prefix.split()
if len(prefixes) > 1:
assign_address = True
if self.master:
try:
prefix = prefixes[0].split(':', 1)[1]
except IndexError:
prefix = prefixes[0] # possibly only one server
else:
# slave servers have their name and localhost in the serverlist
servers = self.broker.getserverlist()
servers.remove('localhost')
prefix = None
for server_prefix in prefixes:
server, p = server_prefix.split(':')
if server == servers[0]:
prefix = p
break
if not prefix:
msg = "Control network prefix not found for server '%s'" % \
servers[0]
self.exception(coreapi.CORE_EXCP_LEVEL_ERROR,
"Session.addremovectrlnet()", None, msg)
prefix = prefixes[0].split(':', 1)[1]
assign_address = False
else:
# with one prefix, only master gets a ctrlnet address
assign_address = self.master
ctrlnet = self.addobj(cls=nodes.CtrlNet, objid=id, prefix=prefix,
assign_address=assign_address,
updown_script=updown_script)
# tunnels between controlnets will be built with Broker.addnettunnels()
self.broker.addnet(id)
for server in self.broker.getserverlist():
self.broker.addnodemap(server, id)
return ctrlnet
id = "ctrlnet" def addremovectrlif(self, node, remove=False):
try: ''' Add a control interface to a node when a 'controlnet' prefix is
ctrlnet = self.obj(id) listed in the config file or session options. Uses
if remove: addremovectrlnet() to build or remove the control bridge.
self.delobj(ctrlnet.objid) '''
return ctrlnet = self.addremovectrlnet(remove)
except KeyError: if ctrlnet is None:
if remove: return
return
ctrlnet = self.addobj(cls = nodes.CtrlNet, objid=id,
prefix=prefix,
assign_address = self.master,
updown_script=updown_script)
self.broker.addnet(id) # to build tunnels during addnettunnels()
for server in self.broker.getserverlist():
self.broker.addnodemap(server, id)
if node is None: if node is None:
return return
ctrlip = node.objid ctrlip = node.objid
@ -951,6 +992,7 @@ class SessionConfig(ConfigurableManager, Configurable):
def __init__(self, session): def __init__(self, session):
ConfigurableManager.__init__(self, session) ConfigurableManager.__init__(self, session)
session.broker.handlers += (self.handledistributed, )
self.reset() self.reset()
def reset(self): def reset(self):
@ -976,6 +1018,52 @@ class SessionConfig(ConfigurableManager, Configurable):
v = "" v = ""
values.append("%s" % v) values.append("%s" % v)
return self.toconfmsg(0, nodenum, typeflags, values) return self.toconfmsg(0, nodenum, typeflags, values)
def handledistributed(self, msg):
''' Handle the session options config message as it has reached the
broker. Options requiring modification for distributed operation should
be handled here.
'''
if not self.session.master:
return
if msg.msgtype != coreapi.CORE_API_CONF_MSG or \
msg.gettlv(coreapi.CORE_TLV_CONF_OBJ) != "session":
return
values_str = msg.gettlv(coreapi.CORE_TLV_CONF_VALUES)
if values_str is None:
return
values = values_str.split('|')
if not self.haskeyvalues(values):
return
for v in values:
key, value = v.split('=', 1)
if key == "controlnet":
self.handledistributedcontrolnet(msg, values, values.index(v))
def handledistributedcontrolnet(self, msg, values, idx):
''' Modify Config Message if multiple control network prefixes are
defined. Map server names to prefixes and repack the message before
it is forwarded to slave servers.
'''
kv = values[idx]
key, value = kv.split('=', 1)
controlnets = value.split()
if len(controlnets) < 2:
return # multiple controlnet prefixes do not exist
servers = self.session.broker.getserverlist()
if len(servers) < 2:
return # not distributed
servers.remove("localhost")
servers.insert(0, "localhost") # master always gets first prefix
# create list of "server1:ctrlnet1 server2:ctrlnet2 ..."
controlnets = map(lambda(x): "%s:%s" % (x[0],x[1]),
zip(servers, controlnets))
values[idx] = "controlnet=%s" % (' '.join(controlnets))
values_str = '|'.join(values)
msg.tlvdata[coreapi.CORE_TLV_CONF_VALUES] = values_str
msg.repack()
class SessionMetaData(ConfigurableManager): class SessionMetaData(ConfigurableManager):
''' Metadata is simply stored in a configs[] dict. Key=value pairs are ''' Metadata is simply stored in a configs[] dict. Key=value pairs are