support for EMANE 0.9.2 by running emane process in each container

use control network for data and events
use internal transport instead of emanetransportd for 0.9.2
(Boeing r1881)
This commit is contained in:
ahrenholz 2014-09-23 16:26:22 +00:00
parent 24263d77bd
commit e825b94e13
8 changed files with 296 additions and 122 deletions

View file

@ -27,7 +27,7 @@ class EmaneBypassModel(EmaneModel):
]
# value groupings
_confgroups = "Bypass Parameters:1-1"
_confgroups = "Bypass Parameters:1-1"
def buildnemxmlfiles(self, e, ifc):
''' Build the necessary nem, mac, and phy XMLs in the given path.
@ -42,6 +42,7 @@ class EmaneBypassModel(EmaneModel):
nemdoc = e.xmldoc("nem")
nem = nemdoc.getElementsByTagName("nem").pop()
nem.setAttribute("name", "BYPASS NEM")
e.appendtransporttonem(nemdoc, nem, self.objid)
mactag = nemdoc.createElement("mac")
mactag.setAttribute("definition", self.macxmlname(ifc))
nem.appendChild(mactag)

View file

@ -95,6 +95,7 @@ class EmaneCommEffectModel(EmaneModel):
nem = nemdoc.getElementsByTagName("nem").pop()
nem.setAttribute("name", "commeffect NEM")
nem.setAttribute("type", "unstructured")
e.appendtransporttonem(nemdoc, nem, self.objid)
nem.appendChild(e.xmlshimdefinition(nemdoc, self.shimxmlname(ifc)))
e.xmlwrite(nemdoc, self.nemxmlname(ifc))

View file

@ -32,6 +32,14 @@ try:
from emanesh.events import LocationEvent
except Exception, e:
pass
# EMANE 0.9.2
try:
from emanesh import remotecontrolportapi_pb2
HAVE092 = ('TYPE_COMPONENT_TRANSPORT' in
dir(remotecontrolportapi_pb2.Response.Query.Manifest.NEM.Component))
del remotecontrolportapi_pb2
except Exception, e:
HAVE092 = False
class Emane(ConfigurableManager):
@ -67,7 +75,7 @@ class Emane(ConfigurableManager):
self.emane_config = EmaneGlobalModel(session, None, self.verbose)
session.broker.handlers += (self.handledistributed, )
self.loadmodels()
self.initeventservice()
self.service = None
def detectversion(self):
''' Detects the installed EMANE version and sets self.version.
@ -81,6 +89,7 @@ class Emane(ConfigurableManager):
'''
# for further study: different EMANE versions on distributed machines
try:
# TODO: fix BUG here -- killall may kill this process too
status, result = cmdresult(['emane', '--version'])
except OSError:
status = -1
@ -111,17 +120,16 @@ class Emane(ConfigurableManager):
self.emane_config.getdefaultvalues())[1]
group, port = self.emane_config.valueof('eventservicegroup',
values).split(':')
dev = self.emane_config.valueof('eventservicedevice', values)
otachannel = None
ota_enable = self.emane_config.valueof('otamanagerchannelenable',
values)
if self.emane_config.offontobool(ota_enable):
ogroup, oport = self.emane_config.valueof('otamanagergroup',
values).split(':')
odev = self.emane_config.valueof('otamanagerdevice', values)
otachannel = (ogroup, int(oport), odev)
if self.version > self.EMANE091 and \
'ctrlnet' in self.session._objs:
# direct EMANE events towards control net bridge
dev = self.session.obj('ctrlnet').brname
else:
dev = self.emane_config.valueof('eventservicedevice', values)
# disabled otachannel for event service
# only needed for e.g. antennaprofile events xmit by models
self.service = EventService(eventchannel=(group, int(port), dev),
otachannel=otachannel)
otachannel=None)
return True
if filename is not None:
tmp = os.getenv(self.EVENTCFGVAR)
@ -181,6 +189,17 @@ class Emane(ConfigurableManager):
self._objs[obj.objid] = obj
self._objslock.release()
def getnodes(self):
''' Return a set of CoreNodes that are linked to an EmaneNode,
e.g. containers having one or more radio interfaces.
'''
# assumes self._objslock already held
r = set()
for e in self._objs.values():
for netif in e.netifs():
r.add(netif.node)
return r
def getmodels(self, n):
''' Used with XML export; see ConfigurableManager.getmodels()
'''
@ -238,14 +257,25 @@ class Emane(ConfigurableManager):
r = self.setup()
if r != Emane.SUCCESS:
return r # NOT_NEEDED or NOT_READY
if self.versionstr == "":
raise ValueError, "EMANE version not properly detected"
with self._objslock:
self.buildxml()
self.starteventmonitor()
if self.numnems() > 0:
# TODO: check and return failure for these methods
self.startdaemons()
self.installnetifs()
return Emane.SUCCESS
if self.version < self.EMANE092:
self.buildxml()
self.initeventservice()
self.starteventmonitor()
if self.numnems() > 0:
# TODO: check and return failure for these methods
self.startdaemons()
self.installnetifs()
else:
self.buildxml2()
self.initeventservice()
self.starteventmonitor()
if self.numnems() > 0:
self.startdaemons2()
self.installnetifs(do_netns=False)
return Emane.SUCCESS
def poststartup(self):
''' Retransmit location events now that all NEMs are active.
@ -369,7 +399,9 @@ class Emane(ConfigurableManager):
return False
def buildxml(self):
''' Build all of the XML files required to run EMANE.
''' Build all of the XML files required to run EMANE on the host.
NEMs run in a single host emane process, with TAP devices pushed
into namespaces.
'''
# assume self._objslock is already held here
if self.verbose:
@ -379,6 +411,20 @@ class Emane(ConfigurableManager):
self.buildtransportxml()
self.buildeventservicexml()
def buildxml2(self):
''' Build XML files required to run EMANE on each node.
NEMs run inside containers using the control network for passing
events and data.
'''
# control network bridge needs to exist when eventservice binds to it
self.session.addremovectrlif(node=None, remove=False, conf_reqd=False)
# assume self._objslock is already held here
if self.verbose:
self.info("Emane.buildxml2()")
self.buildplatformxml2()
self.buildnemxml()
self.buildeventservicexml()
def xmldoc(self, doctype):
''' Returns an XML xml.minidom.Document with a DOCTYPE tag set to the
provided doctype string, and an initial element having the same
@ -473,8 +519,8 @@ class Emane(ConfigurableManager):
self.emane_config.getdefaultvalues())[1]
doc = self.xmldoc("platform")
plat = doc.getElementsByTagName("platform").pop()
platformid = self.emane_config.valueof("platform_id_start", values)
if self.version < self.EMANE091:
platformid = self.emane_config.valueof("platform_id_start", values)
plat.setAttribute("name", "Platform %s" % platformid)
plat.setAttribute("id", platformid)
@ -521,6 +567,47 @@ class Emane(ConfigurableManager):
self.transformport += 1
self.xmlwrite(doc, "platform.xml")
def newplatformxmldoc(self, values):
doc = self.xmldoc("platform")
plat = doc.getElementsByTagName("platform").pop()
names = list(self.emane_config.getnames())
platform_names = names[:len(self.emane_config._confmatrix_platform)]
platform_names.remove('platform_id_start')
# append all platform options (except starting id) to doc
map( lambda n: plat.appendChild(self.xmlparam(doc, n, \
self.emane_config.valueof(n, values))), platform_names)
return doc
def buildplatformxml2(self):
''' Build a platform.xml file now that all nodes are configured.
'''
values = self.getconfig(None, "emane",
self.emane_config.getdefaultvalues())[1]
nemid = int(self.emane_config.valueof("nem_id_start", values))
platformxmls = {}
# assume self._objslock is already held here
for n in sorted(self._objs.keys()):
emanenode = self._objs[n]
nems = emanenode.buildplatformxmlentry(self.xmldoc("platform"))
for netif in sorted(nems, key=lambda n: n.node.objid):
# set ID, endpoints here
nementry = nems[netif]
nementry.setAttribute("id", "%d" % nemid)
nodenum = netif.node.objid
if nodenum not in platformxmls:
platformxmls[nodenum] = self.newplatformxmldoc(values)
doc = platformxmls[nodenum]
plat = doc.getElementsByTagName("platform").pop()
plat.appendChild(nementry)
emanenode.setnemid(netif, nemid)
macstr = self._hwaddr_prefix + ":00:00:"
macstr += "%02X:%02X" % ((nemid >> 8) & 0xFF, nemid & 0xFF)
netif.sethwaddr(MacAddr.fromstring(macstr))
nemid += 1
for nodenum in sorted(platformxmls.keys()):
self.xmlwrite(platformxmls[nodenum], "platform%d.xml" % nodenum)
def buildnemxml(self):
''' Builds the xxxnem.xml, xxxmac.xml, and xxxphy.xml files which
are defined on a per-EmaneNode basis.
@ -529,6 +616,19 @@ class Emane(ConfigurableManager):
emanenode = self._objs[n]
nems = emanenode.buildnemxmlfiles(self)
def appendtransporttonem(self, doc, nem, nodenum):
''' Given a nem XML node and EMANE WLAN node number, append
a <transport/> tag to the NEM definition, required for using
EMANE's internal transport.
'''
if self.version < self.EMANE092:
return
emanenode = self._objs[nodenum]
transtag = doc.createElement("transport")
transtag.setAttribute("definition",
emanenode.transportxmlname("virtual"))
nem.appendChild(transtag)
def buildtransportxml(self):
''' Calls emanegentransportxml using a platform.xml file to build
the transportdaemon*.xml.
@ -624,15 +724,70 @@ class Emane(ConfigurableManager):
None, errmsg)
self.info(errmsg)
def startdaemons2(self):
''' Start one EMANE daemon per node having a radio.
Add a control network even if the user has not configured one.
'''
if self.verbose:
self.info("Emane.startdaemons()")
loglevel = "2"
cfgloglevel = self.session.getcfgitemint("emane_log_level")
realtime = self.session.getcfgitembool("emane_realtime", True)
if cfgloglevel:
self.info("setting user-defined EMANE log level: %d" % cfgloglevel)
loglevel = str(cfgloglevel)
emanecmd = ["emane", "-d", "--logl", loglevel]
if realtime:
emanecmd += "-r",
values = self.getconfig(None, "emane",
self.emane_config.getdefaultvalues())[1]
otagroup, otaport = self.emane_config.valueof('otamanagergroup',
values).split(':')
otadev = self.emane_config.valueof('otamanagerdevice', values)
for node in self.getnodes():
path = self.session.sessiondir
n = node.objid
# control network not yet started here
self.session.addremovectrlif(node, remove=False, conf_reqd=False)
# multicast route is needed for OTA data on ctrl0
cmd = [IP_BIN, "route", "add", otagroup, "dev", otadev]
#rc = node.cmd(cmd, wait=True)
node.cmd(cmd, wait=True)
try:
cmd = emanecmd + ["-f", os.path.join(path, "emane%d.log" % n),
os.path.join(path, "platform%d.xml" % n)]
if self.verbose:
self.info("Emane.startdaemons() running %s" % str(cmd))
#node.cmd(cmd, cwd=path, wait=True)
#status, result = node.cmdresult(cmd, cwd=path, wait=True)
status = node.cmd(cmd, wait=True)
if self.verbose:
self.info("Emane.startdaemons2() return code %d" % status)
except Exception, e:
errmsg = "error starting emane: %s" % e
self.session.exception(coreapi.CORE_EXCP_LEVEL_FATAL, "emane",
n, errmsg)
self.info(errmsg)
def stopdaemons(self):
''' Kill the appropriate EMANE daemons.
'''
# TODO: we may want to improve this if we had the PIDs from the
# specific EMANE daemons that we've started
subprocess.call(["killall", "-q", "emane"])
subprocess.call(["killall", "-q", "emanetransportd"])
cmd = ["killall", "-q", "emane"]
if self.version > self.EMANE091:
for node in self.getnodes():
if node.up:
node.cmd(cmd, wait=False)
# TODO: RJ45 node
else:
subprocess.call(cmd)
subprocess.call(["killall", "-q", "emanetransportd"])
def installnetifs(self):
def installnetifs(self, do_netns=True):
''' Install TUN/TAP virtual interfaces into their proper namespaces
now that the EMANE daemons are running.
'''
@ -640,7 +795,7 @@ class Emane(ConfigurableManager):
emanenode = self._objs[n]
if self.verbose:
self.info("Emane.installnetifs() for node %d" % n)
emanenode.installnetifs()
emanenode.installnetifs(do_netns)
def deinstallnetifs(self):
''' Uninstall TUN/TAP virtual interfaces.
@ -893,7 +1048,8 @@ class EmaneModel(WirelessModel):
type = "raw"
trans = doc.createElement("transport")
trans.setAttribute("definition", n.transportxmlname(type))
trans.setAttribute("group", "1")
if self.session.emane.version < self.session.emane.EMANE092:
trans.setAttribute("group", "1")
param = doc.createElement("param")
param.setAttribute("name", "device")
if type == "raw":
@ -902,6 +1058,9 @@ class EmaneModel(WirelessModel):
else:
# virtual TAP name e.g. 'n3.0.17'
param.setAttribute("value", ifc.localname)
if self.session.emane.version > self.session.emane.EMANE091:
param.setAttribute("value", ifc.name)
trans.appendChild(param)
return trans
@ -967,6 +1126,8 @@ class EmaneModel(WirelessModel):
values = maketuplefromstr(value, str)
except SyntaxError:
return None
if not hasattr(values, '__iter__'):
return None
if len(values) < 2:
return None
return addparamlisttoparent(dom, parent=None, name=name, values=values)
@ -978,17 +1139,24 @@ class EmaneGlobalModel(EmaneModel):
def __init__(self, session, objid = None, verbose = False):
EmaneModel.__init__(self, session, objid, verbose)
# Over-The-Air channel required for EMANE 0.9.2
_DEFAULT_OTA = '0'
_DEFAULT_DEV = 'lo'
if HAVE092:
_DEFAULT_OTA = '1'
_DEFAULT_DEV = 'ctrl0'
_name = "emane"
_confmatrix_platform_base = [
("otamanagerchannelenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
("otamanagerchannelenable", coreapi.CONF_DATA_TYPE_BOOL, _DEFAULT_OTA,
'on,off', 'enable OTA Manager channel'),
("otamanagergroup", coreapi.CONF_DATA_TYPE_STRING, '224.1.2.8:45702',
'', 'OTA Manager group'),
("otamanagerdevice", coreapi.CONF_DATA_TYPE_STRING, 'lo',
("otamanagerdevice", coreapi.CONF_DATA_TYPE_STRING, _DEFAULT_DEV,
'', 'OTA Manager device'),
("eventservicegroup", coreapi.CONF_DATA_TYPE_STRING, '224.1.2.8:45703',
'', 'Event Service group'),
("eventservicedevice", coreapi.CONF_DATA_TYPE_STRING, 'lo',
("eventservicedevice", coreapi.CONF_DATA_TYPE_STRING, _DEFAULT_DEV,
'', 'Event Service device'),
("platform_id_start", coreapi.CONF_DATA_TYPE_INT32, '1',
'', 'starting Platform ID'),
@ -1013,14 +1181,20 @@ class EmaneGlobalModel(EmaneModel):
("nem_id_start", coreapi.CONF_DATA_TYPE_INT32, '1',
'', 'starting NEM ID'),
]
_confmatrix_nem_092 = [
("nem_id_start", coreapi.CONF_DATA_TYPE_INT32, '1',
'', 'starting NEM ID'),
]
if 'EventService' in globals():
_confmatrix_platform = _confmatrix_platform_base + \
_confmatrix_platform_091
if HAVE092:
_confmatrix_nem = _confmatrix_nem_092
else:
_confmatrix_platform = _confmatrix_platform_base + \
_confmatrix_platform_081
_confmatrix = _confmatrix_platform + _confmatrix_nem
_confgroups = "Platform Attributes:1-%d|NEM Parameters:%d-%d" % \
(len(_confmatrix_platform), len(_confmatrix_platform) + 1,
len(_confmatrix))
len(_confmatrix))

View file

@ -52,9 +52,9 @@ class EmaneIeee80211abgModel(EmaneModel):
("pcrcurveuri", coreapi.CONF_DATA_TYPE_STRING,
'%s/ieee80211pcr.xml' % xml_path,
'', 'SINR/PCR curve file'),
("flowcontrolenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
("flowcontrolenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
'On,Off', 'enable traffic flow control'),
("flowcontroltokens", coreapi.CONF_DATA_TYPE_UINT16, '10',
("flowcontroltokens", coreapi.CONF_DATA_TYPE_UINT16, '10',
'', 'number of flow control tokens'),
]
_confmatrix_mac_081 = [
@ -79,7 +79,7 @@ class EmaneIeee80211abgModel(EmaneModel):
else:
_confmatrix_mac = _confmatrix_mac_base + _confmatrix_mac_081
# PHY parameters from Universal PHY
_confmatrix_phy = EmaneUniversalModel._confmatrix
_confmatrix_phy = EmaneUniversalModel._confmatrix
_confmatrix = _confmatrix_mac + _confmatrix_phy
# value groupings
@ -93,20 +93,14 @@ class EmaneIeee80211abgModel(EmaneModel):
nXXemane_ieee80211abgnem.xml, nXXemane_ieee80211abgemac.xml,
nXXemane_ieee80211abgphy.xml are used.
'''
# use the network-wide config values or interface(NEM)-specific values?
if ifc is None:
values = e.getconfig(self.objid, self._name,
self.getdefaultvalues())[1]
else:
nodenum = ifc.node.objid
values = e.getconfig(nodenum, self._name, None)[1]
if values is None:
# do not build specific files for this NEM when config is same
# as the network
return
values = e.getifcconfig(self.objid, self._name,
self.getdefaultvalues(), ifc)
if values is None:
return
nemdoc = e.xmldoc("nem")
nem = nemdoc.getElementsByTagName("nem").pop()
nem.setAttribute("name", "ieee80211abg NEM")
e.appendtransporttonem(nemdoc, nem, self.objid)
mactag = nemdoc.createElement("mac")
mactag.setAttribute("definition", self.macxmlname(ifc))
nem.appendChild(mactag)

View file

@ -197,7 +197,7 @@ class EmaneNode(EmaneNet):
return "n%strans%s.xml" % (self.objid, type)
def installnetifs(self):
def installnetifs(self, do_netns=True):
''' Install TAP devices into their namespaces. This is done after
EMANE daemons have been started, because that is their only chance
to bind to the TAPs.
@ -210,7 +210,7 @@ class EmaneNode(EmaneNet):
self.objid, warntxt)
for netif in self.netifs():
if "virtual" in netif.transport_type.lower():
if do_netns and "virtual" in netif.transport_type.lower():
netif.install()
# if we are listening for EMANE events, don't generate them
if self.session.emane.doeventmonitor():

View file

@ -86,6 +86,7 @@ class EmaneRfPipeModel(EmaneModel):
nemdoc = e.xmldoc("nem")
nem = nemdoc.getElementsByTagName("nem").pop()
nem.setAttribute("name", "RF-PIPE NEM")
e.appendtransporttonem(nemdoc, nem, self.objid)
mactag = nemdoc.createElement("mac")
mactag.setAttribute("definition", self.macxmlname(ifc))
nem.appendChild(mactag)

View file

@ -20,6 +20,7 @@ from core.coreobj import PyCoreNode
class CtrlNet(LxBrNet):
policy = "ACCEPT"
CTRLIF_IDX_BASE = 99 # base control interface index
DEFAULT_PREFIX = "172.16.0.0/24"
def __init__(self, session, objid = "ctrlnet", name = None,
verbose = False, netid = 1, prefix = None,
@ -168,7 +169,7 @@ class HubNode(LxBrNet):
apitype = coreapi.CORE_NODE_HUB
policy = "ACCEPT"
type = "hub"
def __init__(self, session, objid = None, name = None, verbose = False,
start = True):
''' the Hub node forwards packets to all bridge ports by turning off
@ -184,7 +185,7 @@ class WlanNode(LxBrNet):
linktype = coreapi.CORE_LINK_WIRELESS
policy = "DROP"
type = "wlan"
def __init__(self, session, objid = None, name = None, verbose = False,
start = True, policy = None):
LxBrNet.__init__(self, session, objid, name, verbose, start, policy)
@ -192,7 +193,7 @@ class WlanNode(LxBrNet):
self.model = None
# mobility model such as scripted
self.mobility = None
def attach(self, netif):
LxBrNet.attach(self, netif)
if self.model:
@ -203,7 +204,7 @@ class WlanNode(LxBrNet):
# invokes any netif.poshook
netif.setposition(x, y, z)
#self.model.setlinkparams()
def setmodel(self, model, config):
''' Mobility and wireless model.
'''
@ -222,7 +223,7 @@ class WlanNode(LxBrNet):
elif model._type == coreapi.CORE_TLV_REG_MOBILITY:
self.mobility = model(session=self.session, objid=self.objid,
verbose=self.verbose, values=config)
def updatemodel(self, model_name, values):
''' Allow for model updates during runtime (similar to setmodel().)
'''
@ -241,7 +242,7 @@ class WlanNode(LxBrNet):
(x,y,z) = netif.node.position.get()
netif.poshook(netif, x, y, z)
self.model.setlinkparams()
def tolinkmsgs(self, flags):
msgs = LxBrNet.tolinkmsgs(self, flags)
if self.model:
@ -255,7 +256,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
'''
apitype = coreapi.CORE_NODE_RJ45
type = "rj45"
def __init__(self, session, objid = None, name = None, mtu = 1500,
verbose = False, start = True):
PyCoreNode.__init__(self, session, objid, name, verbose=verbose,
@ -283,7 +284,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
(IP_BIN, self.localname))
return
self.up = True
def shutdown(self):
''' Bring the interface down. Remove any addresses and queuing
disciplines.
@ -295,16 +296,16 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
mutecall([TC_BIN, "qdisc", "del", "dev", self.localname, "root"])
self.up = False
self.restorestate()
def attachnet(self, net):
PyCoreNetIf.attachnet(self, net)
def detachnet(self):
PyCoreNetIf.detachnet(self)
def newnetif(self, net = None, addrlist = [], hwaddr = None,
ifindex = None, ifname = None):
''' This is called when linking with another node. Since this node
ifindex = None, ifname = None):
''' This is called when linking with another node. Since this node
represents an interface, we do not create another object here,
but attach ourselves to the given network.
'''
@ -325,7 +326,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
return ifindex
finally:
self.lock.release()
def delnetif(self, ifindex):
if ifindex is None:
ifindex = 0
@ -349,12 +350,12 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
if ifindex == self.ifindex:
return self
return None
def getifindex(self, netif):
if netif != self:
return None
return self.ifindex
def addaddr(self, addr):
if self.up:
check_call([IP_BIN, "addr", "add", str(addr), "dev", self.name])
@ -364,7 +365,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
if self.up:
check_call([IP_BIN, "addr", "del", str(addr), "dev", self.name])
PyCoreNetIf.deladdr(self, addr)
def savestate(self):
''' Save the addresses and other interface state before using the
interface for emulation purposes. TODO: save/restore the PROMISC flag
@ -395,7 +396,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
if items[1][:4] == "fe80":
continue
self.old_addrs.append((items[1], None))
def restorestate(self):
''' Restore the addresses and other interface state after using it.
'''
@ -404,19 +405,19 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
check_call([IP_BIN, "addr", "add", addr[0], "dev",
self.localname])
else:
check_call([IP_BIN, "addr", "add", addr[0], "brd", addr[1],
check_call([IP_BIN, "addr", "add", addr[0], "brd", addr[1],
"dev", self.localname])
if self.old_up:
check_call([IP_BIN, "link", "set", self.localname, "up"])
def setposition(self, x=None, y=None, z=None):
''' Use setposition() from both parent classes.
'''
PyCoreObj.setposition(self, x, y, z)
# invoke any poshook
PyCoreNetIf.setposition(self, x, y, z)

View file

@ -201,7 +201,7 @@ class Session(object):
except Exception, e:
self.warn("sendall() error: %s" % e)
self._handlerslock.release()
def gethandler(self):
''' Get one of the connected handlers, preferrably the master.
'''
@ -214,7 +214,7 @@ class Session(object):
for handler in self._handlers:
return handler
def setstate(self, state, info = False, sendevent = False,
def setstate(self, state, info = False, sendevent = False,
returnevent = False):
''' Set the session state. When info is true, log the state change
event using the session handler's info method. When sendevent is
@ -255,7 +255,7 @@ class Session(object):
''' Retrieve the current state of the session.
'''
return self._state
def writestate(self, state):
''' Write the current state to a state file in the session dir.
'''
@ -286,9 +286,9 @@ class Session(object):
check_call(["/bin/sh", filename], cwd=self.sessiondir,
env=self.getenviron())
except Exception, e:
self.warn("Error running hook '%s' for state %s: %s" %
self.warn("Error running hook '%s' for state %s: %s" %
(filename, state, e))
def sethook(self, type, filename, srcname, data):
''' Store a hook from a received File Message.
'''
@ -308,12 +308,12 @@ class Session(object):
# (this allows hooks in the definition and configuration states)
if self.getstate() == state:
self.runhook(state, hooks = [hook,])
def delhooks(self):
''' Clear the hook scripts dict.
'''
self._hooks = {}
def getenviron(self, state=True):
''' Get an environment suitable for a subprocess.Popen call.
This is the current process environment with some session-specific
@ -362,14 +362,14 @@ class Session(object):
gid = os.stat(self.sessiondir).st_gid
os.chown(self.sessiondir, uid, gid)
except Exception, e:
self.warn("Failed to set permission on %s: %s" % (self.sessiondir, e))
self.warn("Failed to set permission on %s: %s" % (self.sessiondir, e))
self.user = user
def objs(self):
''' Return iterator over the emulation object dictionary.
'''
return self._objs.itervalues()
def getobjid(self):
''' Return a unique, random object id.
'''
@ -400,7 +400,7 @@ class Session(object):
if objid not in self._objs:
raise KeyError, "unknown object id %s" % (objid)
return self._objs[objid]
def objbyname(self, name):
''' Get an emulation object using its name attribute.
'''
@ -438,7 +438,7 @@ class Session(object):
k, o = self._objs.popitem()
o.shutdown()
self._objslock.release()
def writeobjs(self):
''' Write objects to a 'nodes' file in the session dir.
The 'nodes' file lists:
@ -524,15 +524,15 @@ class Session(object):
num = len(self._objs)
self.info(" file=%s thumb=%s nc=%s/%s" % \
(self.filename, self.thumbnail, self.node_count, num))
def exception(self, level, source, objid, text):
''' Generate an Exception Message
''' Generate an Exception Message
'''
vals = (objid, str(self.sessionid), level, source, time.ctime(), text)
types = ("NODE", "SESSION", "LEVEL", "SOURCE", "DATE", "TEXT")
tlvdata = ""
for (t,v) in zip(types, vals):
if v is not None:
if v is not None:
tlvdata += coreapi.CoreExceptionTlv.pack(
eval("coreapi.CORE_TLV_EXCP_%s" % t), v)
msg = coreapi.CoreExceptionMessage.pack(0, tlvdata)
@ -589,7 +589,7 @@ class Session(object):
# nodes on slave servers that will be booted and those servers will
# send a node status response message
self.checkruntime()
def getnodecount(self):
''' Returns the number of CoreNodes and CoreNets, except for those
that are not considered in the GUI's node count.
@ -599,7 +599,7 @@ class Session(object):
count = len(filter(lambda(x): \
not isinstance(x, (nodes.PtpNet, nodes.CtrlNet)),
self.objs()))
# on Linux, GreTapBridges are auto-created, not part of
# on Linux, GreTapBridges are auto-created, not part of
# GUI's node count
if 'GreTapBridge' in globals():
count -= len(filter(lambda(x): \
@ -624,7 +624,7 @@ class Session(object):
nc = self.getnodecount()
# count booted nodes not emulated on this server
# TODO: let slave server determine RUNTIME and wait for Event Message
# broker.getbootocunt() counts all CoreNodes from status reponse
# broker.getbootocunt() counts all CoreNodes from status reponse
# messages, plus any remote WLANs; remote EMANE, hub, switch, etc.
# are already counted in self._objs
nc += self.broker.getbootcount()
@ -653,7 +653,7 @@ class Session(object):
self.updatectrlifhosts(remove=True)
self.addremovectrlif(node=None, remove=True)
# self.checkshutdown() is currently invoked from node delete handler
def checkshutdown(self):
''' Check if we have entered the shutdown state, when no running nodes
and links remain.
@ -665,7 +665,7 @@ class Session(object):
# can enter SHUTDOWN
replies = ()
if nc == 0:
replies = self.setstate(state=coreapi.CORE_EVENT_SHUTDOWN_STATE,
replies = self.setstate(state=coreapi.CORE_EVENT_SHUTDOWN_STATE,
info=True, sendevent=True, returnevent=True)
self.sdt.shutdown()
return replies
@ -682,13 +682,13 @@ class Session(object):
self.master = h.master
return True
return False
def shortsessionid(self):
''' Return a shorter version of the session ID, appropriate for
interface names, where length may be limited.
'''
return (self.sessionid >> 8) ^ (self.sessionid & ((1 << 8) - 1))
def sendnodeemuid(self, handler, nodenum):
''' Send back node messages to the GUI for node messages that had
the status request flag.
@ -711,7 +711,7 @@ class Session(object):
del handler.nodestatusreq[nodenum]
def bootnodes(self, handler):
''' Invoke the boot() procedure for all nodes and send back node
''' Invoke the boot() procedure for all nodes and send back node
messages to the GUI for node messages that had the status
request flag.
'''
@ -724,7 +724,7 @@ class Session(object):
n.boot()
self.sendnodeemuid(handler, n.objid)
self.updatectrlifhosts()
def validatenodes(self):
with self._objslock:
for n in self.objs():
@ -735,23 +735,21 @@ class Session(object):
if isinstance(n, nodes.RJ45Node):
continue
n.validate()
def addremovectrlnet(self, remove=False):
''' Create a control network bridge as necessary.
def addremovectrlnet(self, remove=False, conf_reqd=True):
''' Create a control network bridge as necessary.
When the remove flag is True, remove the bridge that connects control
interfaces.
'''
prefix = None
try:
if self.cfg['controlnet']:
prefix = self.cfg['controlnet']
except KeyError:
pass
prefix = self.cfg.get('controlnet')
if hasattr(self.options, 'controlnet'):
prefix = self.options.controlnet
if not prefix:
return None # no controlnet needed
if conf_reqd:
return None # no controlnet needed
else:
prefix = nodes.CtrlNet.DEFAULT_PREFIX
# return any existing controlnet bridge
id = "ctrlnet"
try:
@ -776,7 +774,7 @@ class Session(object):
new_uds = self.options.controlnet_updown_script
if new_uds:
updown_script = new_uds
prefixes = prefix.split()
if len(prefixes) > 1:
assign_address = True
@ -814,16 +812,20 @@ class Session(object):
self.broker.addnodemap(server, id)
return ctrlnet
def addremovectrlif(self, node, remove=False):
def addremovectrlif(self, node, remove=False, conf_reqd=True):
''' Add a control interface to a node when a 'controlnet' prefix is
listed in the config file or session options. Uses
addremovectrlnet() to build or remove the control bridge.
If conf_reqd is False, the control network may be built even
when the user has not configured one (e.g. for EMANE.)
'''
ctrlnet = self.addremovectrlnet(remove)
ctrlnet = self.addremovectrlnet(remove, conf_reqd)
if ctrlnet is None:
return
if node is None:
return
if node.netif(ctrlnet.CTRLIF_IDX_BASE):
return # ctrl0 already exists
ctrlip = node.objid
try:
addrlist = ["%s/%s" % (ctrlnet.prefix.addr(ctrlip),
@ -873,7 +875,7 @@ class Session(object):
return time.time() - self._time
else:
return 0.0
def addevent(self, etime, node=None, name=None, data=None):
''' Add an event to the event queue, with a start time relative to the
start of the runtime state.
@ -905,7 +907,7 @@ class Session(object):
else:
n = self.obj(node)
n.cmd(shlex.split(data), wait=False)
def sendobjs(self):
''' Return API messages that describe the current session.
'''
@ -983,7 +985,7 @@ class Session(object):
typeflags = coreapi.CONF_TYPE_FLAGS_UPDATE)
if meta:
replies.append(meta)
self.info("informing GUI about %d nodes and %d links" % (nn, nl))
return replies
@ -994,25 +996,25 @@ class SessionConfig(ConfigurableManager, Configurable):
_type = coreapi.CORE_TLV_REG_UTILITY
_confmatrix = [
("controlnet", coreapi.CONF_DATA_TYPE_STRING, '', '',
'Control network'),
'Control network'),
("controlnet_updown_script", coreapi.CONF_DATA_TYPE_STRING, '', '',
'Control network script'),
("enablerj45", coreapi.CONF_DATA_TYPE_BOOL, '1', 'On,Off',
'Control network script'),
("enablerj45", coreapi.CONF_DATA_TYPE_BOOL, '1', 'On,Off',
'Enable RJ45s'),
("preservedir", coreapi.CONF_DATA_TYPE_BOOL, '0', 'On,Off',
'Preserve session dir'),
("enablesdt", coreapi.CONF_DATA_TYPE_BOOL, '0', 'On,Off',
("enablesdt", coreapi.CONF_DATA_TYPE_BOOL, '0', 'On,Off',
'Enable SDT3D output'),
("sdturl", coreapi.CONF_DATA_TYPE_STRING, Sdt.DEFAULT_SDT_URL, '',
'SDT3D URL'),
]
_confgroups = "Options:1-%d" % len(_confmatrix)
def __init__(self, session):
ConfigurableManager.__init__(self, session)
session.broker.handlers += (self.handledistributed, )
self.reset()
def reset(self):
defaults = self.getdefaultvalues()
for k in self.getnames():
@ -1022,9 +1024,9 @@ class SessionConfig(ConfigurableManager, Configurable):
v = self.valueof(k, defaults)
v = self.offontobool(v)
setattr(self, k, v)
def configure_values(self, msg, values):
return self.configure_values_keyvalues(msg, values, self,
return self.configure_values_keyvalues(msg, values, self,
self.getnames())
def configure_request(self, msg, typeflags = coreapi.CONF_TYPE_FLAGS_NONE):
@ -1036,7 +1038,7 @@ class SessionConfig(ConfigurableManager, Configurable):
v = ""
values.append("%s" % v)
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
@ -1057,7 +1059,7 @@ class SessionConfig(ConfigurableManager, Configurable):
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
@ -1068,7 +1070,7 @@ class SessionConfig(ConfigurableManager, Configurable):
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
@ -1102,7 +1104,7 @@ class SessionMetaData(ConfigurableManager):
raise ValueError, "invalid key in metdata: %s" % kv
self.additem(key, value)
return None
def configure_request(self, msg, typeflags = coreapi.CONF_TYPE_FLAGS_NONE):
nodenum = msg.gettlv(coreapi.CORE_TLV_CONF_NODE)
values_str = "|".join(map(lambda(k,v): "%s=%s" % (k,v), self.items()))
@ -1125,10 +1127,10 @@ class SessionMetaData(ConfigurableManager):
values_str)
msg = coreapi.CoreConfMessage.pack(flags, tlvdata)
return msg
def additem(self, key, value):
self.configs[key] = value
def items(self):
return self.configs.iteritems()