321 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			321 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#
 | 
						|
# CORE
 | 
						|
# Copyright (c)2010-2014 the Boeing Company.
 | 
						|
# See the LICENSE file included in this distribution.
 | 
						|
#
 | 
						|
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
 | 
						|
#
 | 
						|
'''
 | 
						|
nodes.py: definition of an EmaneNode class for implementing configuration
 | 
						|
control of an EMANE emulation. An EmaneNode has several attached NEMs that
 | 
						|
share the same MAC+PHY model.
 | 
						|
'''
 | 
						|
 | 
						|
import sys
 | 
						|
import os.path
 | 
						|
 | 
						|
from core.api import coreapi
 | 
						|
from core.coreobj import PyCoreNet
 | 
						|
try:
 | 
						|
    from emanesh.events import EventService
 | 
						|
    from emanesh.events import LocationEvent
 | 
						|
except Exception, e:
 | 
						|
    pass
 | 
						|
 | 
						|
try:
 | 
						|
    import emaneeventservice
 | 
						|
    import emaneeventlocation
 | 
						|
except Exception, e:
 | 
						|
    ''' Don't require all CORE users to have EMANE libeventservice and its
 | 
						|
        Python bindings installed.
 | 
						|
    '''
 | 
						|
    pass
 | 
						|
 | 
						|
class EmaneNet(PyCoreNet):
 | 
						|
    ''' EMANE network base class.
 | 
						|
    '''
 | 
						|
    apitype = coreapi.CORE_NODE_EMANE
 | 
						|
    linktype = coreapi.CORE_LINK_WIRELESS
 | 
						|
    type = "wlan" # icon used
 | 
						|
 | 
						|
class EmaneNode(EmaneNet):
 | 
						|
    ''' EMANE node contains NEM configuration and causes connected nodes
 | 
						|
        to have TAP interfaces (instead of VEth). These are managed by the
 | 
						|
        Emane controller object that exists in a session.
 | 
						|
    '''
 | 
						|
    def __init__(self, session, objid = None, name = None, verbose = False,
 | 
						|
                start = True):
 | 
						|
        PyCoreNet.__init__(self, session, objid, name, verbose, start)
 | 
						|
        self.verbose = verbose
 | 
						|
        self.conf = ""
 | 
						|
        self.up = False
 | 
						|
        self.nemidmap = {}
 | 
						|
        self.model = None
 | 
						|
        self.mobility = None
 | 
						|
 | 
						|
    def linkconfig(self, netif, bw = None, delay = None,
 | 
						|
                   loss = None, duplicate = None, jitter = None, netif2 = None):
 | 
						|
        ''' The CommEffect model supports link configuration.
 | 
						|
        '''
 | 
						|
        if not self.model:
 | 
						|
            return
 | 
						|
        return self.model.linkconfig(netif=netif, bw=bw, delay=delay, loss=loss,
 | 
						|
                            duplicate=duplicate, jitter=jitter, netif2=netif2)
 | 
						|
 | 
						|
    def config(self, conf):
 | 
						|
        #print "emane", self.name, "got config:", conf
 | 
						|
        self.conf = conf
 | 
						|
 | 
						|
    def shutdown(self):
 | 
						|
        pass
 | 
						|
 | 
						|
    def link(self, netif1, netif2):
 | 
						|
        pass
 | 
						|
 | 
						|
    def unlink(self, netif1, netif2):
 | 
						|
        pass
 | 
						|
 | 
						|
    def setmodel(self, model, config):
 | 
						|
        ''' set the EmaneModel associated with this node
 | 
						|
        '''
 | 
						|
        if (self.verbose):
 | 
						|
            self.info("adding model %s" % model._name)
 | 
						|
        if model._type == coreapi.CORE_TLV_REG_WIRELESS:
 | 
						|
            # EmaneModel really uses values from ConfigurableManager
 | 
						|
            #  when buildnemxml() is called, not during init()
 | 
						|
            self.model = model(session=self.session, objid=self.objid,
 | 
						|
                               verbose=self.verbose)
 | 
						|
        elif model._type == coreapi.CORE_TLV_REG_MOBILITY:
 | 
						|
            self.mobility = model(session=self.session, objid=self.objid,
 | 
						|
                               verbose=self.verbose, values=config)
 | 
						|
 | 
						|
    def setnemid(self, netif, nemid):
 | 
						|
        ''' Record an interface to numerical ID mapping. The Emane controller
 | 
						|
            object manages and assigns these IDs for all NEMs.
 | 
						|
        '''
 | 
						|
        self.nemidmap[netif] = nemid
 | 
						|
 | 
						|
    def getnemid(self, netif):
 | 
						|
        ''' Given an interface, return its numerical ID.
 | 
						|
        '''
 | 
						|
        if netif not in self.nemidmap:
 | 
						|
            return None
 | 
						|
        else:
 | 
						|
            return self.nemidmap[netif]
 | 
						|
 | 
						|
    def getnemnetif(self, nemid):
 | 
						|
        ''' Given a numerical NEM ID, return its interface. This returns the
 | 
						|
            first interface that matches the given NEM ID.
 | 
						|
        '''
 | 
						|
        for netif in self.nemidmap:
 | 
						|
            if self.nemidmap[netif] == nemid:
 | 
						|
                return netif
 | 
						|
        return None
 | 
						|
 | 
						|
    def netifs(self, sort=True):
 | 
						|
        ''' Retrieve list of linked interfaces sorted by node number.
 | 
						|
        '''
 | 
						|
        return sorted(self._netif.values(), key=lambda ifc: ifc.node.objid)
 | 
						|
 | 
						|
    def buildplatformxmlentry(self, doc):
 | 
						|
        ''' Return a dictionary of XML elements describing the NEMs
 | 
						|
            connected to this EmaneNode for inclusion in the platform.xml file.
 | 
						|
        '''
 | 
						|
        ret = {}
 | 
						|
        if self.model is None:
 | 
						|
            self.info("warning: EmaneNode %s has no associated model" % \
 | 
						|
                      self.name)
 | 
						|
            return ret
 | 
						|
        for netif in self.netifs():
 | 
						|
            # <nem name="NODE-001" definition="rfpipenem.xml">
 | 
						|
            nementry = self.model.buildplatformxmlnementry(doc, self, netif)
 | 
						|
            # <transport definition="transvirtual.xml" group="1">
 | 
						|
            #    <param name="device" value="n1.0.158" />
 | 
						|
            # </transport>
 | 
						|
            trans = self.model.buildplatformxmltransportentry(doc, self, netif)
 | 
						|
            nementry.appendChild(trans)
 | 
						|
            ret[netif] = nementry
 | 
						|
 | 
						|
        return ret
 | 
						|
 | 
						|
    def buildnemxmlfiles(self, emane):
 | 
						|
        ''' Let the configured model build the necessary nem, mac, and phy
 | 
						|
            XMLs.
 | 
						|
        '''
 | 
						|
        if self.model is None:
 | 
						|
            return
 | 
						|
        # build XML for overall network (EmaneNode) configs
 | 
						|
        self.model.buildnemxmlfiles(emane, ifc=None)
 | 
						|
        # build XML for specific interface (NEM) configs
 | 
						|
        need_virtual = False
 | 
						|
        need_raw = False
 | 
						|
        vtype = "virtual"
 | 
						|
        rtype = "raw"
 | 
						|
        for netif in self.netifs():
 | 
						|
            self.model.buildnemxmlfiles(emane, netif)
 | 
						|
            if "virtual" in netif.transport_type:
 | 
						|
                need_virtual = True
 | 
						|
                vtype = netif.transport_type
 | 
						|
            else:
 | 
						|
                need_raw = True
 | 
						|
                rtype = netif.transport_type
 | 
						|
        # build transport XML files depending on type of interfaces involved
 | 
						|
        if need_virtual:
 | 
						|
            self.buildtransportxml(emane, vtype)
 | 
						|
        if need_raw:
 | 
						|
            self.buildtransportxml(emane, rtype)
 | 
						|
 | 
						|
    def buildtransportxml(self, emane, type):
 | 
						|
        ''' Write a transport XML file for the Virtual or Raw Transport.
 | 
						|
        '''
 | 
						|
        transdoc = emane.xmldoc("transport")
 | 
						|
        trans = transdoc.getElementsByTagName("transport").pop()
 | 
						|
        trans.setAttribute("name", "%s Transport" % type.capitalize())
 | 
						|
        trans.setAttribute("library", "trans%s" % type.lower())
 | 
						|
        trans.appendChild(emane.xmlparam(transdoc, "bitrate", "0"))
 | 
						|
 | 
						|
        flowcontrol = False
 | 
						|
        names = self.model.getnames()
 | 
						|
        values = emane.getconfig(self.objid, self.model._name,
 | 
						|
                                 self.model.getdefaultvalues())[1]
 | 
						|
        if "flowcontrolenable" in names and values:
 | 
						|
            i = names.index("flowcontrolenable")
 | 
						|
            if self.model.booltooffon(values[i]) == "on":
 | 
						|
                flowcontrol = True
 | 
						|
 | 
						|
        if "virtual" in type.lower():
 | 
						|
            if os.path.exists("/dev/net/tun_flowctl"):
 | 
						|
                trans.appendChild(emane.xmlparam(transdoc, "devicepath",
 | 
						|
                                  "/dev/net/tun_flowctl"))
 | 
						|
            else:
 | 
						|
                trans.appendChild(emane.xmlparam(transdoc, "devicepath",
 | 
						|
                                  "/dev/net/tun"))
 | 
						|
            if flowcontrol:
 | 
						|
                trans.appendChild(emane.xmlparam(transdoc, "flowcontrolenable",
 | 
						|
                                                 "on"))
 | 
						|
        emane.xmlwrite(transdoc, self.transportxmlname(type.lower()))
 | 
						|
 | 
						|
    def transportxmlname(self, type):
 | 
						|
        ''' Return the string name for the Transport XML file,
 | 
						|
            e.g. 'n3transvirtual.xml'
 | 
						|
        '''
 | 
						|
        return "n%strans%s.xml" % (self.objid, type)
 | 
						|
 | 
						|
 | 
						|
    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.
 | 
						|
        '''
 | 
						|
        if self.session.emane.genlocationevents() and \
 | 
						|
            self.session.emane.service is None:
 | 
						|
            warntxt = "unable to publish EMANE events because the eventservice "
 | 
						|
            warntxt += "Python bindings failed to load"
 | 
						|
            self.session.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.name,
 | 
						|
                                    self.objid, warntxt)
 | 
						|
 | 
						|
        for netif in self.netifs():
 | 
						|
            if do_netns and "virtual" in netif.transport_type.lower():
 | 
						|
                netif.install()
 | 
						|
            netif.setaddrs()
 | 
						|
            if not self.session.emane.genlocationevents():
 | 
						|
                netif.poshook = None
 | 
						|
                continue
 | 
						|
            # at this point we register location handlers for generating
 | 
						|
            # EMANE location events
 | 
						|
            netif.poshook = self.setnemposition
 | 
						|
            (x,y,z) = netif.node.position.get()
 | 
						|
            self.setnemposition(netif, x, y, z)
 | 
						|
 | 
						|
    def deinstallnetifs(self):
 | 
						|
        ''' Uninstall TAP devices. This invokes their shutdown method for
 | 
						|
            any required cleanup; the device may be actually removed when
 | 
						|
            emanetransportd terminates.
 | 
						|
        '''
 | 
						|
        for netif in self.netifs():
 | 
						|
            if "virtual" in netif.transport_type.lower():
 | 
						|
                netif.shutdown()
 | 
						|
            netif.poshook = None
 | 
						|
 | 
						|
    def setnemposition(self, netif, x, y, z):
 | 
						|
        ''' Publish a NEM location change event using the EMANE event service.
 | 
						|
        '''
 | 
						|
        if self.session.emane.service is None:
 | 
						|
            if self.verbose:
 | 
						|
                self.info("position service not available")
 | 
						|
            return
 | 
						|
        nemid =  self.getnemid(netif)
 | 
						|
        ifname = netif.localname
 | 
						|
        if nemid is None:
 | 
						|
            self.info("nemid for %s is unknown" % ifname)
 | 
						|
            return
 | 
						|
        (lat, long, alt) = self.session.location.getgeo(x, y, z)
 | 
						|
        if self.verbose:
 | 
						|
            self.info("setnemposition %s (%s) x,y,z=(%d,%d,%s)"
 | 
						|
                      "(%.6f,%.6f,%.6f)" % \
 | 
						|
                      (ifname, nemid, x, y, z, lat, long, alt))
 | 
						|
        if self.session.emane.version >= self.session.emane.EMANE091:
 | 
						|
            event = LocationEvent()
 | 
						|
        else:
 | 
						|
            event = emaneeventlocation.EventLocation(1)
 | 
						|
        # altitude must be an integer or warning is printed
 | 
						|
        # unused: yaw, pitch, roll, azimuth, elevation, velocity
 | 
						|
        alt = int(round(alt))
 | 
						|
        if self.session.emane.version >= self.session.emane.EMANE091:
 | 
						|
            event.append(nemid, latitude=lat, longitude=long, altitude=alt)
 | 
						|
            self.session.emane.service.publish(0, event)
 | 
						|
        else:
 | 
						|
            event.set(0, nemid, lat, long, alt)
 | 
						|
            self.session.emane.service.publish(emaneeventlocation.EVENT_ID,
 | 
						|
                                           emaneeventservice.PLATFORMID_ANY,
 | 
						|
                                           emaneeventservice.NEMID_ANY,
 | 
						|
                                           emaneeventservice.COMPONENTID_ANY,
 | 
						|
                                           event.export())
 | 
						|
 | 
						|
    def setnempositions(self, moved_netifs):
 | 
						|
        ''' Several NEMs have moved, from e.g. a WaypointMobilityModel
 | 
						|
            calculation. Generate an EMANE Location Event having several
 | 
						|
            entries for each netif that has moved.
 | 
						|
        '''
 | 
						|
        if len(moved_netifs) == 0:
 | 
						|
            return
 | 
						|
        if self.session.emane.service is None:
 | 
						|
            if self.verbose:
 | 
						|
                self.info("position service not available")
 | 
						|
            return
 | 
						|
 | 
						|
        if self.session.emane.version >= self.session.emane.EMANE091:
 | 
						|
            event = LocationEvent()
 | 
						|
        else:
 | 
						|
            event = emaneeventlocation.EventLocation(len(moved_netifs))
 | 
						|
        i = 0
 | 
						|
        for netif in moved_netifs:
 | 
						|
            nemid =  self.getnemid(netif)
 | 
						|
            ifname = netif.localname
 | 
						|
            if nemid is None:
 | 
						|
                self.info("nemid for %s is unknown" % ifname)
 | 
						|
                continue
 | 
						|
            (x, y, z) = netif.node.getposition()
 | 
						|
            (lat, long, alt) = self.session.location.getgeo(x, y, z)
 | 
						|
            if self.verbose:
 | 
						|
                self.info("setnempositions %d %s (%s) x,y,z=(%d,%d,%s)"
 | 
						|
                          "(%.6f,%.6f,%.6f)" % \
 | 
						|
                          (i, ifname, nemid, x, y, z, lat, long, alt))
 | 
						|
            # altitude must be an integer or warning is printed
 | 
						|
            alt = int(round(alt))
 | 
						|
            if self.session.emane.version >= self.session.emane.EMANE091:
 | 
						|
                event.append(nemid, latitude=lat, longitude=long, altitude=alt)
 | 
						|
            else:
 | 
						|
                event.set(i, nemid, lat, long, alt)
 | 
						|
            i += 1
 | 
						|
 | 
						|
        if self.session.emane.version >= self.session.emane.EMANE091:
 | 
						|
            self.session.emane.service.publish(0, event)
 | 
						|
        else:
 | 
						|
            self.session.emane.service.publish(emaneeventlocation.EVENT_ID,
 | 
						|
                                           emaneeventservice.PLATFORMID_ANY,
 | 
						|
                                           emaneeventservice.NEMID_ANY,
 | 
						|
                                           emaneeventservice.COMPONENTID_ANY,
 | 
						|
                                           event.export())
 | 
						|
 | 
						|
 |