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:
parent
24263d77bd
commit
e825b94e13
8 changed files with 296 additions and 122 deletions
|
@ -27,7 +27,7 @@ class EmaneBypassModel(EmaneModel):
|
||||||
]
|
]
|
||||||
|
|
||||||
# value groupings
|
# value groupings
|
||||||
_confgroups = "Bypass Parameters:1-1"
|
_confgroups = "Bypass Parameters:1-1"
|
||||||
|
|
||||||
def buildnemxmlfiles(self, e, ifc):
|
def buildnemxmlfiles(self, e, ifc):
|
||||||
''' Build the necessary nem, mac, and phy XMLs in the given path.
|
''' Build the necessary nem, mac, and phy XMLs in the given path.
|
||||||
|
@ -42,6 +42,7 @@ class EmaneBypassModel(EmaneModel):
|
||||||
nemdoc = e.xmldoc("nem")
|
nemdoc = e.xmldoc("nem")
|
||||||
nem = nemdoc.getElementsByTagName("nem").pop()
|
nem = nemdoc.getElementsByTagName("nem").pop()
|
||||||
nem.setAttribute("name", "BYPASS NEM")
|
nem.setAttribute("name", "BYPASS NEM")
|
||||||
|
e.appendtransporttonem(nemdoc, nem, self.objid)
|
||||||
mactag = nemdoc.createElement("mac")
|
mactag = nemdoc.createElement("mac")
|
||||||
mactag.setAttribute("definition", self.macxmlname(ifc))
|
mactag.setAttribute("definition", self.macxmlname(ifc))
|
||||||
nem.appendChild(mactag)
|
nem.appendChild(mactag)
|
||||||
|
|
|
@ -95,6 +95,7 @@ class EmaneCommEffectModel(EmaneModel):
|
||||||
nem = nemdoc.getElementsByTagName("nem").pop()
|
nem = nemdoc.getElementsByTagName("nem").pop()
|
||||||
nem.setAttribute("name", "commeffect NEM")
|
nem.setAttribute("name", "commeffect NEM")
|
||||||
nem.setAttribute("type", "unstructured")
|
nem.setAttribute("type", "unstructured")
|
||||||
|
e.appendtransporttonem(nemdoc, nem, self.objid)
|
||||||
nem.appendChild(e.xmlshimdefinition(nemdoc, self.shimxmlname(ifc)))
|
nem.appendChild(e.xmlshimdefinition(nemdoc, self.shimxmlname(ifc)))
|
||||||
e.xmlwrite(nemdoc, self.nemxmlname(ifc))
|
e.xmlwrite(nemdoc, self.nemxmlname(ifc))
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,14 @@ try:
|
||||||
from emanesh.events import LocationEvent
|
from emanesh.events import LocationEvent
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
pass
|
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):
|
class Emane(ConfigurableManager):
|
||||||
|
@ -67,7 +75,7 @@ class Emane(ConfigurableManager):
|
||||||
self.emane_config = EmaneGlobalModel(session, None, self.verbose)
|
self.emane_config = EmaneGlobalModel(session, None, self.verbose)
|
||||||
session.broker.handlers += (self.handledistributed, )
|
session.broker.handlers += (self.handledistributed, )
|
||||||
self.loadmodels()
|
self.loadmodels()
|
||||||
self.initeventservice()
|
self.service = None
|
||||||
|
|
||||||
def detectversion(self):
|
def detectversion(self):
|
||||||
''' Detects the installed EMANE version and sets self.version.
|
''' 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
|
# for further study: different EMANE versions on distributed machines
|
||||||
try:
|
try:
|
||||||
|
# TODO: fix BUG here -- killall may kill this process too
|
||||||
status, result = cmdresult(['emane', '--version'])
|
status, result = cmdresult(['emane', '--version'])
|
||||||
except OSError:
|
except OSError:
|
||||||
status = -1
|
status = -1
|
||||||
|
@ -111,17 +120,16 @@ class Emane(ConfigurableManager):
|
||||||
self.emane_config.getdefaultvalues())[1]
|
self.emane_config.getdefaultvalues())[1]
|
||||||
group, port = self.emane_config.valueof('eventservicegroup',
|
group, port = self.emane_config.valueof('eventservicegroup',
|
||||||
values).split(':')
|
values).split(':')
|
||||||
dev = self.emane_config.valueof('eventservicedevice', values)
|
if self.version > self.EMANE091 and \
|
||||||
otachannel = None
|
'ctrlnet' in self.session._objs:
|
||||||
ota_enable = self.emane_config.valueof('otamanagerchannelenable',
|
# direct EMANE events towards control net bridge
|
||||||
values)
|
dev = self.session.obj('ctrlnet').brname
|
||||||
if self.emane_config.offontobool(ota_enable):
|
else:
|
||||||
ogroup, oport = self.emane_config.valueof('otamanagergroup',
|
dev = self.emane_config.valueof('eventservicedevice', values)
|
||||||
values).split(':')
|
# disabled otachannel for event service
|
||||||
odev = self.emane_config.valueof('otamanagerdevice', values)
|
# only needed for e.g. antennaprofile events xmit by models
|
||||||
otachannel = (ogroup, int(oport), odev)
|
|
||||||
self.service = EventService(eventchannel=(group, int(port), dev),
|
self.service = EventService(eventchannel=(group, int(port), dev),
|
||||||
otachannel=otachannel)
|
otachannel=None)
|
||||||
return True
|
return True
|
||||||
if filename is not None:
|
if filename is not None:
|
||||||
tmp = os.getenv(self.EVENTCFGVAR)
|
tmp = os.getenv(self.EVENTCFGVAR)
|
||||||
|
@ -181,6 +189,17 @@ class Emane(ConfigurableManager):
|
||||||
self._objs[obj.objid] = obj
|
self._objs[obj.objid] = obj
|
||||||
self._objslock.release()
|
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):
|
def getmodels(self, n):
|
||||||
''' Used with XML export; see ConfigurableManager.getmodels()
|
''' Used with XML export; see ConfigurableManager.getmodels()
|
||||||
'''
|
'''
|
||||||
|
@ -238,14 +257,25 @@ class Emane(ConfigurableManager):
|
||||||
r = self.setup()
|
r = self.setup()
|
||||||
if r != Emane.SUCCESS:
|
if r != Emane.SUCCESS:
|
||||||
return r # NOT_NEEDED or NOT_READY
|
return r # NOT_NEEDED or NOT_READY
|
||||||
|
if self.versionstr == "":
|
||||||
|
raise ValueError, "EMANE version not properly detected"
|
||||||
with self._objslock:
|
with self._objslock:
|
||||||
self.buildxml()
|
if self.version < self.EMANE092:
|
||||||
self.starteventmonitor()
|
self.buildxml()
|
||||||
if self.numnems() > 0:
|
self.initeventservice()
|
||||||
# TODO: check and return failure for these methods
|
self.starteventmonitor()
|
||||||
self.startdaemons()
|
if self.numnems() > 0:
|
||||||
self.installnetifs()
|
# TODO: check and return failure for these methods
|
||||||
return Emane.SUCCESS
|
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):
|
def poststartup(self):
|
||||||
''' Retransmit location events now that all NEMs are active.
|
''' Retransmit location events now that all NEMs are active.
|
||||||
|
@ -369,7 +399,9 @@ class Emane(ConfigurableManager):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def buildxml(self):
|
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
|
# assume self._objslock is already held here
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
|
@ -379,6 +411,20 @@ class Emane(ConfigurableManager):
|
||||||
self.buildtransportxml()
|
self.buildtransportxml()
|
||||||
self.buildeventservicexml()
|
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):
|
def xmldoc(self, doctype):
|
||||||
''' Returns an XML xml.minidom.Document with a DOCTYPE tag set to the
|
''' Returns an XML xml.minidom.Document with a DOCTYPE tag set to the
|
||||||
provided doctype string, and an initial element having the same
|
provided doctype string, and an initial element having the same
|
||||||
|
@ -473,8 +519,8 @@ class Emane(ConfigurableManager):
|
||||||
self.emane_config.getdefaultvalues())[1]
|
self.emane_config.getdefaultvalues())[1]
|
||||||
doc = self.xmldoc("platform")
|
doc = self.xmldoc("platform")
|
||||||
plat = doc.getElementsByTagName("platform").pop()
|
plat = doc.getElementsByTagName("platform").pop()
|
||||||
platformid = self.emane_config.valueof("platform_id_start", values)
|
|
||||||
if self.version < self.EMANE091:
|
if self.version < self.EMANE091:
|
||||||
|
platformid = self.emane_config.valueof("platform_id_start", values)
|
||||||
plat.setAttribute("name", "Platform %s" % platformid)
|
plat.setAttribute("name", "Platform %s" % platformid)
|
||||||
plat.setAttribute("id", platformid)
|
plat.setAttribute("id", platformid)
|
||||||
|
|
||||||
|
@ -521,6 +567,47 @@ class Emane(ConfigurableManager):
|
||||||
self.transformport += 1
|
self.transformport += 1
|
||||||
self.xmlwrite(doc, "platform.xml")
|
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):
|
def buildnemxml(self):
|
||||||
''' Builds the xxxnem.xml, xxxmac.xml, and xxxphy.xml files which
|
''' Builds the xxxnem.xml, xxxmac.xml, and xxxphy.xml files which
|
||||||
are defined on a per-EmaneNode basis.
|
are defined on a per-EmaneNode basis.
|
||||||
|
@ -529,6 +616,19 @@ class Emane(ConfigurableManager):
|
||||||
emanenode = self._objs[n]
|
emanenode = self._objs[n]
|
||||||
nems = emanenode.buildnemxmlfiles(self)
|
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):
|
def buildtransportxml(self):
|
||||||
''' Calls emanegentransportxml using a platform.xml file to build
|
''' Calls emanegentransportxml using a platform.xml file to build
|
||||||
the transportdaemon*.xml.
|
the transportdaemon*.xml.
|
||||||
|
@ -624,15 +724,70 @@ class Emane(ConfigurableManager):
|
||||||
None, errmsg)
|
None, errmsg)
|
||||||
self.info(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):
|
def stopdaemons(self):
|
||||||
''' Kill the appropriate EMANE daemons.
|
''' Kill the appropriate EMANE daemons.
|
||||||
'''
|
'''
|
||||||
# TODO: we may want to improve this if we had the PIDs from the
|
# TODO: we may want to improve this if we had the PIDs from the
|
||||||
# specific EMANE daemons that we've started
|
# specific EMANE daemons that we've started
|
||||||
subprocess.call(["killall", "-q", "emane"])
|
cmd = ["killall", "-q", "emane"]
|
||||||
subprocess.call(["killall", "-q", "emanetransportd"])
|
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
|
''' Install TUN/TAP virtual interfaces into their proper namespaces
|
||||||
now that the EMANE daemons are running.
|
now that the EMANE daemons are running.
|
||||||
'''
|
'''
|
||||||
|
@ -640,7 +795,7 @@ class Emane(ConfigurableManager):
|
||||||
emanenode = self._objs[n]
|
emanenode = self._objs[n]
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
self.info("Emane.installnetifs() for node %d" % n)
|
self.info("Emane.installnetifs() for node %d" % n)
|
||||||
emanenode.installnetifs()
|
emanenode.installnetifs(do_netns)
|
||||||
|
|
||||||
def deinstallnetifs(self):
|
def deinstallnetifs(self):
|
||||||
''' Uninstall TUN/TAP virtual interfaces.
|
''' Uninstall TUN/TAP virtual interfaces.
|
||||||
|
@ -893,7 +1048,8 @@ class EmaneModel(WirelessModel):
|
||||||
type = "raw"
|
type = "raw"
|
||||||
trans = doc.createElement("transport")
|
trans = doc.createElement("transport")
|
||||||
trans.setAttribute("definition", n.transportxmlname(type))
|
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 = doc.createElement("param")
|
||||||
param.setAttribute("name", "device")
|
param.setAttribute("name", "device")
|
||||||
if type == "raw":
|
if type == "raw":
|
||||||
|
@ -902,6 +1058,9 @@ class EmaneModel(WirelessModel):
|
||||||
else:
|
else:
|
||||||
# virtual TAP name e.g. 'n3.0.17'
|
# virtual TAP name e.g. 'n3.0.17'
|
||||||
param.setAttribute("value", ifc.localname)
|
param.setAttribute("value", ifc.localname)
|
||||||
|
if self.session.emane.version > self.session.emane.EMANE091:
|
||||||
|
param.setAttribute("value", ifc.name)
|
||||||
|
|
||||||
trans.appendChild(param)
|
trans.appendChild(param)
|
||||||
return trans
|
return trans
|
||||||
|
|
||||||
|
@ -967,6 +1126,8 @@ class EmaneModel(WirelessModel):
|
||||||
values = maketuplefromstr(value, str)
|
values = maketuplefromstr(value, str)
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
return None
|
return None
|
||||||
|
if not hasattr(values, '__iter__'):
|
||||||
|
return None
|
||||||
if len(values) < 2:
|
if len(values) < 2:
|
||||||
return None
|
return None
|
||||||
return addparamlisttoparent(dom, parent=None, name=name, values=values)
|
return addparamlisttoparent(dom, parent=None, name=name, values=values)
|
||||||
|
@ -978,17 +1139,24 @@ class EmaneGlobalModel(EmaneModel):
|
||||||
def __init__(self, session, objid = None, verbose = False):
|
def __init__(self, session, objid = None, verbose = False):
|
||||||
EmaneModel.__init__(self, session, objid, verbose)
|
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"
|
_name = "emane"
|
||||||
_confmatrix_platform_base = [
|
_confmatrix_platform_base = [
|
||||||
("otamanagerchannelenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("otamanagerchannelenable", coreapi.CONF_DATA_TYPE_BOOL, _DEFAULT_OTA,
|
||||||
'on,off', 'enable OTA Manager channel'),
|
'on,off', 'enable OTA Manager channel'),
|
||||||
("otamanagergroup", coreapi.CONF_DATA_TYPE_STRING, '224.1.2.8:45702',
|
("otamanagergroup", coreapi.CONF_DATA_TYPE_STRING, '224.1.2.8:45702',
|
||||||
'', 'OTA Manager group'),
|
'', 'OTA Manager group'),
|
||||||
("otamanagerdevice", coreapi.CONF_DATA_TYPE_STRING, 'lo',
|
("otamanagerdevice", coreapi.CONF_DATA_TYPE_STRING, _DEFAULT_DEV,
|
||||||
'', 'OTA Manager device'),
|
'', 'OTA Manager device'),
|
||||||
("eventservicegroup", coreapi.CONF_DATA_TYPE_STRING, '224.1.2.8:45703',
|
("eventservicegroup", coreapi.CONF_DATA_TYPE_STRING, '224.1.2.8:45703',
|
||||||
'', 'Event Service group'),
|
'', 'Event Service group'),
|
||||||
("eventservicedevice", coreapi.CONF_DATA_TYPE_STRING, 'lo',
|
("eventservicedevice", coreapi.CONF_DATA_TYPE_STRING, _DEFAULT_DEV,
|
||||||
'', 'Event Service device'),
|
'', 'Event Service device'),
|
||||||
("platform_id_start", coreapi.CONF_DATA_TYPE_INT32, '1',
|
("platform_id_start", coreapi.CONF_DATA_TYPE_INT32, '1',
|
||||||
'', 'starting Platform ID'),
|
'', 'starting Platform ID'),
|
||||||
|
@ -1013,14 +1181,20 @@ class EmaneGlobalModel(EmaneModel):
|
||||||
("nem_id_start", coreapi.CONF_DATA_TYPE_INT32, '1',
|
("nem_id_start", coreapi.CONF_DATA_TYPE_INT32, '1',
|
||||||
'', 'starting NEM ID'),
|
'', 'starting NEM ID'),
|
||||||
]
|
]
|
||||||
|
_confmatrix_nem_092 = [
|
||||||
|
("nem_id_start", coreapi.CONF_DATA_TYPE_INT32, '1',
|
||||||
|
'', 'starting NEM ID'),
|
||||||
|
]
|
||||||
|
|
||||||
if 'EventService' in globals():
|
if 'EventService' in globals():
|
||||||
_confmatrix_platform = _confmatrix_platform_base + \
|
_confmatrix_platform = _confmatrix_platform_base + \
|
||||||
_confmatrix_platform_091
|
_confmatrix_platform_091
|
||||||
|
if HAVE092:
|
||||||
|
_confmatrix_nem = _confmatrix_nem_092
|
||||||
else:
|
else:
|
||||||
_confmatrix_platform = _confmatrix_platform_base + \
|
_confmatrix_platform = _confmatrix_platform_base + \
|
||||||
_confmatrix_platform_081
|
_confmatrix_platform_081
|
||||||
_confmatrix = _confmatrix_platform + _confmatrix_nem
|
_confmatrix = _confmatrix_platform + _confmatrix_nem
|
||||||
_confgroups = "Platform Attributes:1-%d|NEM Parameters:%d-%d" % \
|
_confgroups = "Platform Attributes:1-%d|NEM Parameters:%d-%d" % \
|
||||||
(len(_confmatrix_platform), len(_confmatrix_platform) + 1,
|
(len(_confmatrix_platform), len(_confmatrix_platform) + 1,
|
||||||
len(_confmatrix))
|
len(_confmatrix))
|
||||||
|
|
|
@ -52,9 +52,9 @@ class EmaneIeee80211abgModel(EmaneModel):
|
||||||
("pcrcurveuri", coreapi.CONF_DATA_TYPE_STRING,
|
("pcrcurveuri", coreapi.CONF_DATA_TYPE_STRING,
|
||||||
'%s/ieee80211pcr.xml' % xml_path,
|
'%s/ieee80211pcr.xml' % xml_path,
|
||||||
'', 'SINR/PCR curve file'),
|
'', 'SINR/PCR curve file'),
|
||||||
("flowcontrolenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
("flowcontrolenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||||
'On,Off', 'enable traffic flow control'),
|
'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'),
|
'', 'number of flow control tokens'),
|
||||||
]
|
]
|
||||||
_confmatrix_mac_081 = [
|
_confmatrix_mac_081 = [
|
||||||
|
@ -79,7 +79,7 @@ class EmaneIeee80211abgModel(EmaneModel):
|
||||||
else:
|
else:
|
||||||
_confmatrix_mac = _confmatrix_mac_base + _confmatrix_mac_081
|
_confmatrix_mac = _confmatrix_mac_base + _confmatrix_mac_081
|
||||||
# PHY parameters from Universal PHY
|
# PHY parameters from Universal PHY
|
||||||
_confmatrix_phy = EmaneUniversalModel._confmatrix
|
_confmatrix_phy = EmaneUniversalModel._confmatrix
|
||||||
|
|
||||||
_confmatrix = _confmatrix_mac + _confmatrix_phy
|
_confmatrix = _confmatrix_mac + _confmatrix_phy
|
||||||
# value groupings
|
# value groupings
|
||||||
|
@ -93,20 +93,14 @@ class EmaneIeee80211abgModel(EmaneModel):
|
||||||
nXXemane_ieee80211abgnem.xml, nXXemane_ieee80211abgemac.xml,
|
nXXemane_ieee80211abgnem.xml, nXXemane_ieee80211abgemac.xml,
|
||||||
nXXemane_ieee80211abgphy.xml are used.
|
nXXemane_ieee80211abgphy.xml are used.
|
||||||
'''
|
'''
|
||||||
# use the network-wide config values or interface(NEM)-specific values?
|
values = e.getifcconfig(self.objid, self._name,
|
||||||
if ifc is None:
|
self.getdefaultvalues(), ifc)
|
||||||
values = e.getconfig(self.objid, self._name,
|
if values is None:
|
||||||
self.getdefaultvalues())[1]
|
return
|
||||||
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
|
|
||||||
nemdoc = e.xmldoc("nem")
|
nemdoc = e.xmldoc("nem")
|
||||||
nem = nemdoc.getElementsByTagName("nem").pop()
|
nem = nemdoc.getElementsByTagName("nem").pop()
|
||||||
nem.setAttribute("name", "ieee80211abg NEM")
|
nem.setAttribute("name", "ieee80211abg NEM")
|
||||||
|
e.appendtransporttonem(nemdoc, nem, self.objid)
|
||||||
mactag = nemdoc.createElement("mac")
|
mactag = nemdoc.createElement("mac")
|
||||||
mactag.setAttribute("definition", self.macxmlname(ifc))
|
mactag.setAttribute("definition", self.macxmlname(ifc))
|
||||||
nem.appendChild(mactag)
|
nem.appendChild(mactag)
|
||||||
|
|
|
@ -197,7 +197,7 @@ class EmaneNode(EmaneNet):
|
||||||
return "n%strans%s.xml" % (self.objid, type)
|
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
|
''' Install TAP devices into their namespaces. This is done after
|
||||||
EMANE daemons have been started, because that is their only chance
|
EMANE daemons have been started, because that is their only chance
|
||||||
to bind to the TAPs.
|
to bind to the TAPs.
|
||||||
|
@ -210,7 +210,7 @@ class EmaneNode(EmaneNet):
|
||||||
self.objid, warntxt)
|
self.objid, warntxt)
|
||||||
|
|
||||||
for netif in self.netifs():
|
for netif in self.netifs():
|
||||||
if "virtual" in netif.transport_type.lower():
|
if do_netns and "virtual" in netif.transport_type.lower():
|
||||||
netif.install()
|
netif.install()
|
||||||
# if we are listening for EMANE events, don't generate them
|
# if we are listening for EMANE events, don't generate them
|
||||||
if self.session.emane.doeventmonitor():
|
if self.session.emane.doeventmonitor():
|
||||||
|
|
|
@ -86,6 +86,7 @@ class EmaneRfPipeModel(EmaneModel):
|
||||||
nemdoc = e.xmldoc("nem")
|
nemdoc = e.xmldoc("nem")
|
||||||
nem = nemdoc.getElementsByTagName("nem").pop()
|
nem = nemdoc.getElementsByTagName("nem").pop()
|
||||||
nem.setAttribute("name", "RF-PIPE NEM")
|
nem.setAttribute("name", "RF-PIPE NEM")
|
||||||
|
e.appendtransporttonem(nemdoc, nem, self.objid)
|
||||||
mactag = nemdoc.createElement("mac")
|
mactag = nemdoc.createElement("mac")
|
||||||
mactag.setAttribute("definition", self.macxmlname(ifc))
|
mactag.setAttribute("definition", self.macxmlname(ifc))
|
||||||
nem.appendChild(mactag)
|
nem.appendChild(mactag)
|
||||||
|
|
|
@ -20,6 +20,7 @@ from core.coreobj import PyCoreNode
|
||||||
class CtrlNet(LxBrNet):
|
class CtrlNet(LxBrNet):
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
CTRLIF_IDX_BASE = 99 # base control interface index
|
CTRLIF_IDX_BASE = 99 # base control interface index
|
||||||
|
DEFAULT_PREFIX = "172.16.0.0/24"
|
||||||
|
|
||||||
def __init__(self, session, objid = "ctrlnet", name = None,
|
def __init__(self, session, objid = "ctrlnet", name = None,
|
||||||
verbose = False, netid = 1, prefix = None,
|
verbose = False, netid = 1, prefix = None,
|
||||||
|
@ -168,7 +169,7 @@ class HubNode(LxBrNet):
|
||||||
apitype = coreapi.CORE_NODE_HUB
|
apitype = coreapi.CORE_NODE_HUB
|
||||||
policy = "ACCEPT"
|
policy = "ACCEPT"
|
||||||
type = "hub"
|
type = "hub"
|
||||||
|
|
||||||
def __init__(self, session, objid = None, name = None, verbose = False,
|
def __init__(self, session, objid = None, name = None, verbose = False,
|
||||||
start = True):
|
start = True):
|
||||||
''' the Hub node forwards packets to all bridge ports by turning off
|
''' the Hub node forwards packets to all bridge ports by turning off
|
||||||
|
@ -184,7 +185,7 @@ class WlanNode(LxBrNet):
|
||||||
linktype = coreapi.CORE_LINK_WIRELESS
|
linktype = coreapi.CORE_LINK_WIRELESS
|
||||||
policy = "DROP"
|
policy = "DROP"
|
||||||
type = "wlan"
|
type = "wlan"
|
||||||
|
|
||||||
def __init__(self, session, objid = None, name = None, verbose = False,
|
def __init__(self, session, objid = None, name = None, verbose = False,
|
||||||
start = True, policy = None):
|
start = True, policy = None):
|
||||||
LxBrNet.__init__(self, session, objid, name, verbose, start, policy)
|
LxBrNet.__init__(self, session, objid, name, verbose, start, policy)
|
||||||
|
@ -192,7 +193,7 @@ class WlanNode(LxBrNet):
|
||||||
self.model = None
|
self.model = None
|
||||||
# mobility model such as scripted
|
# mobility model such as scripted
|
||||||
self.mobility = None
|
self.mobility = None
|
||||||
|
|
||||||
def attach(self, netif):
|
def attach(self, netif):
|
||||||
LxBrNet.attach(self, netif)
|
LxBrNet.attach(self, netif)
|
||||||
if self.model:
|
if self.model:
|
||||||
|
@ -203,7 +204,7 @@ class WlanNode(LxBrNet):
|
||||||
# invokes any netif.poshook
|
# invokes any netif.poshook
|
||||||
netif.setposition(x, y, z)
|
netif.setposition(x, y, z)
|
||||||
#self.model.setlinkparams()
|
#self.model.setlinkparams()
|
||||||
|
|
||||||
def setmodel(self, model, config):
|
def setmodel(self, model, config):
|
||||||
''' Mobility and wireless model.
|
''' Mobility and wireless model.
|
||||||
'''
|
'''
|
||||||
|
@ -222,7 +223,7 @@ class WlanNode(LxBrNet):
|
||||||
elif model._type == coreapi.CORE_TLV_REG_MOBILITY:
|
elif model._type == coreapi.CORE_TLV_REG_MOBILITY:
|
||||||
self.mobility = model(session=self.session, objid=self.objid,
|
self.mobility = model(session=self.session, objid=self.objid,
|
||||||
verbose=self.verbose, values=config)
|
verbose=self.verbose, values=config)
|
||||||
|
|
||||||
def updatemodel(self, model_name, values):
|
def updatemodel(self, model_name, values):
|
||||||
''' Allow for model updates during runtime (similar to setmodel().)
|
''' Allow for model updates during runtime (similar to setmodel().)
|
||||||
'''
|
'''
|
||||||
|
@ -241,7 +242,7 @@ class WlanNode(LxBrNet):
|
||||||
(x,y,z) = netif.node.position.get()
|
(x,y,z) = netif.node.position.get()
|
||||||
netif.poshook(netif, x, y, z)
|
netif.poshook(netif, x, y, z)
|
||||||
self.model.setlinkparams()
|
self.model.setlinkparams()
|
||||||
|
|
||||||
def tolinkmsgs(self, flags):
|
def tolinkmsgs(self, flags):
|
||||||
msgs = LxBrNet.tolinkmsgs(self, flags)
|
msgs = LxBrNet.tolinkmsgs(self, flags)
|
||||||
if self.model:
|
if self.model:
|
||||||
|
@ -255,7 +256,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
|
||||||
'''
|
'''
|
||||||
apitype = coreapi.CORE_NODE_RJ45
|
apitype = coreapi.CORE_NODE_RJ45
|
||||||
type = "rj45"
|
type = "rj45"
|
||||||
|
|
||||||
def __init__(self, session, objid = None, name = None, mtu = 1500,
|
def __init__(self, session, objid = None, name = None, mtu = 1500,
|
||||||
verbose = False, start = True):
|
verbose = False, start = True):
|
||||||
PyCoreNode.__init__(self, session, objid, name, verbose=verbose,
|
PyCoreNode.__init__(self, session, objid, name, verbose=verbose,
|
||||||
|
@ -283,7 +284,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
|
||||||
(IP_BIN, self.localname))
|
(IP_BIN, self.localname))
|
||||||
return
|
return
|
||||||
self.up = True
|
self.up = True
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
''' Bring the interface down. Remove any addresses and queuing
|
''' Bring the interface down. Remove any addresses and queuing
|
||||||
disciplines.
|
disciplines.
|
||||||
|
@ -295,16 +296,16 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
|
||||||
mutecall([TC_BIN, "qdisc", "del", "dev", self.localname, "root"])
|
mutecall([TC_BIN, "qdisc", "del", "dev", self.localname, "root"])
|
||||||
self.up = False
|
self.up = False
|
||||||
self.restorestate()
|
self.restorestate()
|
||||||
|
|
||||||
def attachnet(self, net):
|
def attachnet(self, net):
|
||||||
PyCoreNetIf.attachnet(self, net)
|
PyCoreNetIf.attachnet(self, net)
|
||||||
|
|
||||||
def detachnet(self):
|
def detachnet(self):
|
||||||
PyCoreNetIf.detachnet(self)
|
PyCoreNetIf.detachnet(self)
|
||||||
|
|
||||||
def newnetif(self, net = None, addrlist = [], hwaddr = None,
|
def newnetif(self, net = None, addrlist = [], hwaddr = None,
|
||||||
ifindex = None, ifname = None):
|
ifindex = None, ifname = None):
|
||||||
''' This is called when linking with another node. Since this node
|
''' This is called when linking with another node. Since this node
|
||||||
represents an interface, we do not create another object here,
|
represents an interface, we do not create another object here,
|
||||||
but attach ourselves to the given network.
|
but attach ourselves to the given network.
|
||||||
'''
|
'''
|
||||||
|
@ -325,7 +326,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
|
||||||
return ifindex
|
return ifindex
|
||||||
finally:
|
finally:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
def delnetif(self, ifindex):
|
def delnetif(self, ifindex):
|
||||||
if ifindex is None:
|
if ifindex is None:
|
||||||
ifindex = 0
|
ifindex = 0
|
||||||
|
@ -349,12 +350,12 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
|
||||||
if ifindex == self.ifindex:
|
if ifindex == self.ifindex:
|
||||||
return self
|
return self
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def getifindex(self, netif):
|
def getifindex(self, netif):
|
||||||
if netif != self:
|
if netif != self:
|
||||||
return None
|
return None
|
||||||
return self.ifindex
|
return self.ifindex
|
||||||
|
|
||||||
def addaddr(self, addr):
|
def addaddr(self, addr):
|
||||||
if self.up:
|
if self.up:
|
||||||
check_call([IP_BIN, "addr", "add", str(addr), "dev", self.name])
|
check_call([IP_BIN, "addr", "add", str(addr), "dev", self.name])
|
||||||
|
@ -364,7 +365,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
|
||||||
if self.up:
|
if self.up:
|
||||||
check_call([IP_BIN, "addr", "del", str(addr), "dev", self.name])
|
check_call([IP_BIN, "addr", "del", str(addr), "dev", self.name])
|
||||||
PyCoreNetIf.deladdr(self, addr)
|
PyCoreNetIf.deladdr(self, addr)
|
||||||
|
|
||||||
def savestate(self):
|
def savestate(self):
|
||||||
''' Save the addresses and other interface state before using the
|
''' Save the addresses and other interface state before using the
|
||||||
interface for emulation purposes. TODO: save/restore the PROMISC flag
|
interface for emulation purposes. TODO: save/restore the PROMISC flag
|
||||||
|
@ -395,7 +396,7 @@ class RJ45Node(PyCoreNode, PyCoreNetIf):
|
||||||
if items[1][:4] == "fe80":
|
if items[1][:4] == "fe80":
|
||||||
continue
|
continue
|
||||||
self.old_addrs.append((items[1], None))
|
self.old_addrs.append((items[1], None))
|
||||||
|
|
||||||
def restorestate(self):
|
def restorestate(self):
|
||||||
''' Restore the addresses and other interface state after using it.
|
''' 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",
|
check_call([IP_BIN, "addr", "add", addr[0], "dev",
|
||||||
self.localname])
|
self.localname])
|
||||||
else:
|
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])
|
"dev", self.localname])
|
||||||
if self.old_up:
|
if self.old_up:
|
||||||
check_call([IP_BIN, "link", "set", self.localname, "up"])
|
check_call([IP_BIN, "link", "set", self.localname, "up"])
|
||||||
|
|
||||||
def setposition(self, x=None, y=None, z=None):
|
def setposition(self, x=None, y=None, z=None):
|
||||||
''' Use setposition() from both parent classes.
|
''' Use setposition() from both parent classes.
|
||||||
'''
|
'''
|
||||||
PyCoreObj.setposition(self, x, y, z)
|
PyCoreObj.setposition(self, x, y, z)
|
||||||
# invoke any poshook
|
# invoke any poshook
|
||||||
PyCoreNetIf.setposition(self, x, y, z)
|
PyCoreNetIf.setposition(self, x, y, z)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -201,7 +201,7 @@ class Session(object):
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.warn("sendall() error: %s" % e)
|
self.warn("sendall() error: %s" % e)
|
||||||
self._handlerslock.release()
|
self._handlerslock.release()
|
||||||
|
|
||||||
def gethandler(self):
|
def gethandler(self):
|
||||||
''' Get one of the connected handlers, preferrably the master.
|
''' Get one of the connected handlers, preferrably the master.
|
||||||
'''
|
'''
|
||||||
|
@ -214,7 +214,7 @@ class Session(object):
|
||||||
for handler in self._handlers:
|
for handler in self._handlers:
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
def setstate(self, state, info = False, sendevent = False,
|
def setstate(self, state, info = False, sendevent = False,
|
||||||
returnevent = False):
|
returnevent = False):
|
||||||
''' Set the session state. When info is true, log the state change
|
''' Set the session state. When info is true, log the state change
|
||||||
event using the session handler's info method. When sendevent is
|
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.
|
''' Retrieve the current state of the session.
|
||||||
'''
|
'''
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
def writestate(self, state):
|
def writestate(self, state):
|
||||||
''' Write the current state to a state file in the session dir.
|
''' 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,
|
check_call(["/bin/sh", filename], cwd=self.sessiondir,
|
||||||
env=self.getenviron())
|
env=self.getenviron())
|
||||||
except Exception, e:
|
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))
|
(filename, state, e))
|
||||||
|
|
||||||
def sethook(self, type, filename, srcname, data):
|
def sethook(self, type, filename, srcname, data):
|
||||||
''' Store a hook from a received File Message.
|
''' Store a hook from a received File Message.
|
||||||
'''
|
'''
|
||||||
|
@ -308,12 +308,12 @@ class Session(object):
|
||||||
# (this allows hooks in the definition and configuration states)
|
# (this allows hooks in the definition and configuration states)
|
||||||
if self.getstate() == state:
|
if self.getstate() == state:
|
||||||
self.runhook(state, hooks = [hook,])
|
self.runhook(state, hooks = [hook,])
|
||||||
|
|
||||||
def delhooks(self):
|
def delhooks(self):
|
||||||
''' Clear the hook scripts dict.
|
''' Clear the hook scripts dict.
|
||||||
'''
|
'''
|
||||||
self._hooks = {}
|
self._hooks = {}
|
||||||
|
|
||||||
def getenviron(self, state=True):
|
def getenviron(self, state=True):
|
||||||
''' Get an environment suitable for a subprocess.Popen call.
|
''' Get an environment suitable for a subprocess.Popen call.
|
||||||
This is the current process environment with some session-specific
|
This is the current process environment with some session-specific
|
||||||
|
@ -362,14 +362,14 @@ class Session(object):
|
||||||
gid = os.stat(self.sessiondir).st_gid
|
gid = os.stat(self.sessiondir).st_gid
|
||||||
os.chown(self.sessiondir, uid, gid)
|
os.chown(self.sessiondir, uid, gid)
|
||||||
except Exception, e:
|
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
|
self.user = user
|
||||||
|
|
||||||
def objs(self):
|
def objs(self):
|
||||||
''' Return iterator over the emulation object dictionary.
|
''' Return iterator over the emulation object dictionary.
|
||||||
'''
|
'''
|
||||||
return self._objs.itervalues()
|
return self._objs.itervalues()
|
||||||
|
|
||||||
def getobjid(self):
|
def getobjid(self):
|
||||||
''' Return a unique, random object id.
|
''' Return a unique, random object id.
|
||||||
'''
|
'''
|
||||||
|
@ -400,7 +400,7 @@ class Session(object):
|
||||||
if objid not in self._objs:
|
if objid not in self._objs:
|
||||||
raise KeyError, "unknown object id %s" % (objid)
|
raise KeyError, "unknown object id %s" % (objid)
|
||||||
return self._objs[objid]
|
return self._objs[objid]
|
||||||
|
|
||||||
def objbyname(self, name):
|
def objbyname(self, name):
|
||||||
''' Get an emulation object using its name attribute.
|
''' Get an emulation object using its name attribute.
|
||||||
'''
|
'''
|
||||||
|
@ -438,7 +438,7 @@ class Session(object):
|
||||||
k, o = self._objs.popitem()
|
k, o = self._objs.popitem()
|
||||||
o.shutdown()
|
o.shutdown()
|
||||||
self._objslock.release()
|
self._objslock.release()
|
||||||
|
|
||||||
def writeobjs(self):
|
def writeobjs(self):
|
||||||
''' Write objects to a 'nodes' file in the session dir.
|
''' Write objects to a 'nodes' file in the session dir.
|
||||||
The 'nodes' file lists:
|
The 'nodes' file lists:
|
||||||
|
@ -524,15 +524,15 @@ class Session(object):
|
||||||
num = len(self._objs)
|
num = len(self._objs)
|
||||||
self.info(" file=%s thumb=%s nc=%s/%s" % \
|
self.info(" file=%s thumb=%s nc=%s/%s" % \
|
||||||
(self.filename, self.thumbnail, self.node_count, num))
|
(self.filename, self.thumbnail, self.node_count, num))
|
||||||
|
|
||||||
def exception(self, level, source, objid, text):
|
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)
|
vals = (objid, str(self.sessionid), level, source, time.ctime(), text)
|
||||||
types = ("NODE", "SESSION", "LEVEL", "SOURCE", "DATE", "TEXT")
|
types = ("NODE", "SESSION", "LEVEL", "SOURCE", "DATE", "TEXT")
|
||||||
tlvdata = ""
|
tlvdata = ""
|
||||||
for (t,v) in zip(types, vals):
|
for (t,v) in zip(types, vals):
|
||||||
if v is not None:
|
if v is not None:
|
||||||
tlvdata += coreapi.CoreExceptionTlv.pack(
|
tlvdata += coreapi.CoreExceptionTlv.pack(
|
||||||
eval("coreapi.CORE_TLV_EXCP_%s" % t), v)
|
eval("coreapi.CORE_TLV_EXCP_%s" % t), v)
|
||||||
msg = coreapi.CoreExceptionMessage.pack(0, tlvdata)
|
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
|
# nodes on slave servers that will be booted and those servers will
|
||||||
# send a node status response message
|
# send a node status response message
|
||||||
self.checkruntime()
|
self.checkruntime()
|
||||||
|
|
||||||
def getnodecount(self):
|
def getnodecount(self):
|
||||||
''' Returns the number of CoreNodes and CoreNets, except for those
|
''' Returns the number of CoreNodes and CoreNets, except for those
|
||||||
that are not considered in the GUI's node count.
|
that are not considered in the GUI's node count.
|
||||||
|
@ -599,7 +599,7 @@ class Session(object):
|
||||||
count = len(filter(lambda(x): \
|
count = len(filter(lambda(x): \
|
||||||
not isinstance(x, (nodes.PtpNet, nodes.CtrlNet)),
|
not isinstance(x, (nodes.PtpNet, nodes.CtrlNet)),
|
||||||
self.objs()))
|
self.objs()))
|
||||||
# on Linux, GreTapBridges are auto-created, not part of
|
# on Linux, GreTapBridges are auto-created, not part of
|
||||||
# GUI's node count
|
# GUI's node count
|
||||||
if 'GreTapBridge' in globals():
|
if 'GreTapBridge' in globals():
|
||||||
count -= len(filter(lambda(x): \
|
count -= len(filter(lambda(x): \
|
||||||
|
@ -624,7 +624,7 @@ class Session(object):
|
||||||
nc = self.getnodecount()
|
nc = self.getnodecount()
|
||||||
# count booted nodes not emulated on this server
|
# count booted nodes not emulated on this server
|
||||||
# TODO: let slave server determine RUNTIME and wait for Event Message
|
# 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.
|
# messages, plus any remote WLANs; remote EMANE, hub, switch, etc.
|
||||||
# are already counted in self._objs
|
# are already counted in self._objs
|
||||||
nc += self.broker.getbootcount()
|
nc += self.broker.getbootcount()
|
||||||
|
@ -653,7 +653,7 @@ class Session(object):
|
||||||
self.updatectrlifhosts(remove=True)
|
self.updatectrlifhosts(remove=True)
|
||||||
self.addremovectrlif(node=None, remove=True)
|
self.addremovectrlif(node=None, remove=True)
|
||||||
# self.checkshutdown() is currently invoked from node delete handler
|
# self.checkshutdown() is currently invoked from node delete handler
|
||||||
|
|
||||||
def checkshutdown(self):
|
def checkshutdown(self):
|
||||||
''' Check if we have entered the shutdown state, when no running nodes
|
''' Check if we have entered the shutdown state, when no running nodes
|
||||||
and links remain.
|
and links remain.
|
||||||
|
@ -665,7 +665,7 @@ class Session(object):
|
||||||
# can enter SHUTDOWN
|
# can enter SHUTDOWN
|
||||||
replies = ()
|
replies = ()
|
||||||
if nc == 0:
|
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)
|
info=True, sendevent=True, returnevent=True)
|
||||||
self.sdt.shutdown()
|
self.sdt.shutdown()
|
||||||
return replies
|
return replies
|
||||||
|
@ -682,13 +682,13 @@ class Session(object):
|
||||||
self.master = h.master
|
self.master = h.master
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def shortsessionid(self):
|
def shortsessionid(self):
|
||||||
''' Return a shorter version of the session ID, appropriate for
|
''' Return a shorter version of the session ID, appropriate for
|
||||||
interface names, where length may be limited.
|
interface names, where length may be limited.
|
||||||
'''
|
'''
|
||||||
return (self.sessionid >> 8) ^ (self.sessionid & ((1 << 8) - 1))
|
return (self.sessionid >> 8) ^ (self.sessionid & ((1 << 8) - 1))
|
||||||
|
|
||||||
def sendnodeemuid(self, handler, nodenum):
|
def sendnodeemuid(self, handler, nodenum):
|
||||||
''' Send back node messages to the GUI for node messages that had
|
''' Send back node messages to the GUI for node messages that had
|
||||||
the status request flag.
|
the status request flag.
|
||||||
|
@ -711,7 +711,7 @@ class Session(object):
|
||||||
del handler.nodestatusreq[nodenum]
|
del handler.nodestatusreq[nodenum]
|
||||||
|
|
||||||
def bootnodes(self, handler):
|
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
|
messages to the GUI for node messages that had the status
|
||||||
request flag.
|
request flag.
|
||||||
'''
|
'''
|
||||||
|
@ -724,7 +724,7 @@ class Session(object):
|
||||||
n.boot()
|
n.boot()
|
||||||
self.sendnodeemuid(handler, n.objid)
|
self.sendnodeemuid(handler, n.objid)
|
||||||
self.updatectrlifhosts()
|
self.updatectrlifhosts()
|
||||||
|
|
||||||
def validatenodes(self):
|
def validatenodes(self):
|
||||||
with self._objslock:
|
with self._objslock:
|
||||||
for n in self.objs():
|
for n in self.objs():
|
||||||
|
@ -735,23 +735,21 @@ class Session(object):
|
||||||
if isinstance(n, nodes.RJ45Node):
|
if isinstance(n, nodes.RJ45Node):
|
||||||
continue
|
continue
|
||||||
n.validate()
|
n.validate()
|
||||||
|
|
||||||
def addremovectrlnet(self, remove=False):
|
def addremovectrlnet(self, remove=False, conf_reqd=True):
|
||||||
''' Create a control network bridge as necessary.
|
''' Create a control network bridge as necessary.
|
||||||
When the remove flag is True, remove the bridge that connects control
|
When the remove flag is True, remove the bridge that connects control
|
||||||
interfaces.
|
interfaces.
|
||||||
'''
|
'''
|
||||||
prefix = None
|
prefix = self.cfg.get('controlnet')
|
||||||
try:
|
|
||||||
if self.cfg['controlnet']:
|
|
||||||
prefix = self.cfg['controlnet']
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
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 None # no controlnet needed
|
if conf_reqd:
|
||||||
|
return None # no controlnet needed
|
||||||
|
else:
|
||||||
|
prefix = nodes.CtrlNet.DEFAULT_PREFIX
|
||||||
|
|
||||||
# return any existing controlnet bridge
|
# return any existing controlnet bridge
|
||||||
id = "ctrlnet"
|
id = "ctrlnet"
|
||||||
try:
|
try:
|
||||||
|
@ -776,7 +774,7 @@ class Session(object):
|
||||||
new_uds = self.options.controlnet_updown_script
|
new_uds = self.options.controlnet_updown_script
|
||||||
if new_uds:
|
if new_uds:
|
||||||
updown_script = new_uds
|
updown_script = new_uds
|
||||||
|
|
||||||
prefixes = prefix.split()
|
prefixes = prefix.split()
|
||||||
if len(prefixes) > 1:
|
if len(prefixes) > 1:
|
||||||
assign_address = True
|
assign_address = True
|
||||||
|
@ -814,16 +812,20 @@ class Session(object):
|
||||||
self.broker.addnodemap(server, id)
|
self.broker.addnodemap(server, id)
|
||||||
return ctrlnet
|
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
|
''' Add a control interface to a node when a 'controlnet' prefix is
|
||||||
listed in the config file or session options. Uses
|
listed in the config file or session options. Uses
|
||||||
addremovectrlnet() to build or remove the control bridge.
|
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:
|
if ctrlnet is None:
|
||||||
return
|
return
|
||||||
if node is None:
|
if node is None:
|
||||||
return
|
return
|
||||||
|
if node.netif(ctrlnet.CTRLIF_IDX_BASE):
|
||||||
|
return # ctrl0 already exists
|
||||||
ctrlip = node.objid
|
ctrlip = node.objid
|
||||||
try:
|
try:
|
||||||
addrlist = ["%s/%s" % (ctrlnet.prefix.addr(ctrlip),
|
addrlist = ["%s/%s" % (ctrlnet.prefix.addr(ctrlip),
|
||||||
|
@ -873,7 +875,7 @@ class Session(object):
|
||||||
return time.time() - self._time
|
return time.time() - self._time
|
||||||
else:
|
else:
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
def addevent(self, etime, node=None, name=None, data=None):
|
def addevent(self, etime, node=None, name=None, data=None):
|
||||||
''' Add an event to the event queue, with a start time relative to the
|
''' Add an event to the event queue, with a start time relative to the
|
||||||
start of the runtime state.
|
start of the runtime state.
|
||||||
|
@ -905,7 +907,7 @@ class Session(object):
|
||||||
else:
|
else:
|
||||||
n = self.obj(node)
|
n = self.obj(node)
|
||||||
n.cmd(shlex.split(data), wait=False)
|
n.cmd(shlex.split(data), wait=False)
|
||||||
|
|
||||||
def sendobjs(self):
|
def sendobjs(self):
|
||||||
''' Return API messages that describe the current session.
|
''' Return API messages that describe the current session.
|
||||||
'''
|
'''
|
||||||
|
@ -983,7 +985,7 @@ class Session(object):
|
||||||
typeflags = coreapi.CONF_TYPE_FLAGS_UPDATE)
|
typeflags = coreapi.CONF_TYPE_FLAGS_UPDATE)
|
||||||
if meta:
|
if meta:
|
||||||
replies.append(meta)
|
replies.append(meta)
|
||||||
|
|
||||||
self.info("informing GUI about %d nodes and %d links" % (nn, nl))
|
self.info("informing GUI about %d nodes and %d links" % (nn, nl))
|
||||||
return replies
|
return replies
|
||||||
|
|
||||||
|
@ -994,25 +996,25 @@ class SessionConfig(ConfigurableManager, Configurable):
|
||||||
_type = coreapi.CORE_TLV_REG_UTILITY
|
_type = coreapi.CORE_TLV_REG_UTILITY
|
||||||
_confmatrix = [
|
_confmatrix = [
|
||||||
("controlnet", coreapi.CONF_DATA_TYPE_STRING, '', '',
|
("controlnet", coreapi.CONF_DATA_TYPE_STRING, '', '',
|
||||||
'Control network'),
|
'Control network'),
|
||||||
("controlnet_updown_script", coreapi.CONF_DATA_TYPE_STRING, '', '',
|
("controlnet_updown_script", coreapi.CONF_DATA_TYPE_STRING, '', '',
|
||||||
'Control network script'),
|
'Control network script'),
|
||||||
("enablerj45", coreapi.CONF_DATA_TYPE_BOOL, '1', 'On,Off',
|
("enablerj45", coreapi.CONF_DATA_TYPE_BOOL, '1', 'On,Off',
|
||||||
'Enable RJ45s'),
|
'Enable RJ45s'),
|
||||||
("preservedir", coreapi.CONF_DATA_TYPE_BOOL, '0', 'On,Off',
|
("preservedir", coreapi.CONF_DATA_TYPE_BOOL, '0', 'On,Off',
|
||||||
'Preserve session dir'),
|
'Preserve session dir'),
|
||||||
("enablesdt", coreapi.CONF_DATA_TYPE_BOOL, '0', 'On,Off',
|
("enablesdt", coreapi.CONF_DATA_TYPE_BOOL, '0', 'On,Off',
|
||||||
'Enable SDT3D output'),
|
'Enable SDT3D output'),
|
||||||
("sdturl", coreapi.CONF_DATA_TYPE_STRING, Sdt.DEFAULT_SDT_URL, '',
|
("sdturl", coreapi.CONF_DATA_TYPE_STRING, Sdt.DEFAULT_SDT_URL, '',
|
||||||
'SDT3D URL'),
|
'SDT3D URL'),
|
||||||
]
|
]
|
||||||
_confgroups = "Options:1-%d" % len(_confmatrix)
|
_confgroups = "Options:1-%d" % len(_confmatrix)
|
||||||
|
|
||||||
def __init__(self, session):
|
def __init__(self, session):
|
||||||
ConfigurableManager.__init__(self, session)
|
ConfigurableManager.__init__(self, session)
|
||||||
session.broker.handlers += (self.handledistributed, )
|
session.broker.handlers += (self.handledistributed, )
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
defaults = self.getdefaultvalues()
|
defaults = self.getdefaultvalues()
|
||||||
for k in self.getnames():
|
for k in self.getnames():
|
||||||
|
@ -1022,9 +1024,9 @@ class SessionConfig(ConfigurableManager, Configurable):
|
||||||
v = self.valueof(k, defaults)
|
v = self.valueof(k, defaults)
|
||||||
v = self.offontobool(v)
|
v = self.offontobool(v)
|
||||||
setattr(self, k, v)
|
setattr(self, k, v)
|
||||||
|
|
||||||
def configure_values(self, msg, values):
|
def configure_values(self, msg, values):
|
||||||
return self.configure_values_keyvalues(msg, values, self,
|
return self.configure_values_keyvalues(msg, values, self,
|
||||||
self.getnames())
|
self.getnames())
|
||||||
|
|
||||||
def configure_request(self, msg, typeflags = coreapi.CONF_TYPE_FLAGS_NONE):
|
def configure_request(self, msg, typeflags = coreapi.CONF_TYPE_FLAGS_NONE):
|
||||||
|
@ -1036,7 +1038,7 @@ 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):
|
def handledistributed(self, msg):
|
||||||
''' Handle the session options config message as it has reached the
|
''' Handle the session options config message as it has reached the
|
||||||
broker. Options requiring modification for distributed operation should
|
broker. Options requiring modification for distributed operation should
|
||||||
|
@ -1057,7 +1059,7 @@ class SessionConfig(ConfigurableManager, Configurable):
|
||||||
key, value = v.split('=', 1)
|
key, value = v.split('=', 1)
|
||||||
if key == "controlnet":
|
if key == "controlnet":
|
||||||
self.handledistributedcontrolnet(msg, values, values.index(v))
|
self.handledistributedcontrolnet(msg, values, values.index(v))
|
||||||
|
|
||||||
def handledistributedcontrolnet(self, msg, values, idx):
|
def handledistributedcontrolnet(self, msg, values, idx):
|
||||||
''' Modify Config Message if multiple control network prefixes are
|
''' Modify Config Message if multiple control network prefixes are
|
||||||
defined. Map server names to prefixes and repack the message before
|
defined. Map server names to prefixes and repack the message before
|
||||||
|
@ -1068,7 +1070,7 @@ class SessionConfig(ConfigurableManager, Configurable):
|
||||||
controlnets = value.split()
|
controlnets = value.split()
|
||||||
if len(controlnets) < 2:
|
if len(controlnets) < 2:
|
||||||
return # multiple controlnet prefixes do not exist
|
return # multiple controlnet prefixes do not exist
|
||||||
|
|
||||||
servers = self.session.broker.getserverlist()
|
servers = self.session.broker.getserverlist()
|
||||||
if len(servers) < 2:
|
if len(servers) < 2:
|
||||||
return # not distributed
|
return # not distributed
|
||||||
|
@ -1102,7 +1104,7 @@ class SessionMetaData(ConfigurableManager):
|
||||||
raise ValueError, "invalid key in metdata: %s" % kv
|
raise ValueError, "invalid key in metdata: %s" % kv
|
||||||
self.additem(key, value)
|
self.additem(key, value)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def configure_request(self, msg, typeflags = coreapi.CONF_TYPE_FLAGS_NONE):
|
def configure_request(self, msg, typeflags = coreapi.CONF_TYPE_FLAGS_NONE):
|
||||||
nodenum = msg.gettlv(coreapi.CORE_TLV_CONF_NODE)
|
nodenum = msg.gettlv(coreapi.CORE_TLV_CONF_NODE)
|
||||||
values_str = "|".join(map(lambda(k,v): "%s=%s" % (k,v), self.items()))
|
values_str = "|".join(map(lambda(k,v): "%s=%s" % (k,v), self.items()))
|
||||||
|
@ -1125,10 +1127,10 @@ class SessionMetaData(ConfigurableManager):
|
||||||
values_str)
|
values_str)
|
||||||
msg = coreapi.CoreConfMessage.pack(flags, tlvdata)
|
msg = coreapi.CoreConfMessage.pack(flags, tlvdata)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def additem(self, key, value):
|
def additem(self, key, value):
|
||||||
self.configs[key] = value
|
self.configs[key] = value
|
||||||
|
|
||||||
def items(self):
|
def items(self):
|
||||||
return self.configs.iteritems()
|
return self.configs.iteritems()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue