""" Provides an EMANE network node class, which has several attached NEMs that share the same MAC+PHY model. """ import logging from core.emulator.enumerations import LinkTypes, NodeTypes, RegisterTlvs from core.nodes.base import CoreNetworkBase try: from emane.events import LocationEvent except ImportError: try: from emanesh.events import LocationEvent except ImportError: logging.debug("compatible emane python bindings not installed") class EmaneNet(CoreNetworkBase): """ 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. """ apitype = NodeTypes.EMANE.value linktype = LinkTypes.WIRELESS.value type = "wlan" is_emane = True def __init__(self, session, _id=None, name=None, start=True): super(EmaneNet, self).__init__(session, _id, name, start) 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): self.conf = conf def shutdown(self): pass def link(self, netif1, netif2): pass def unlink(self, netif1, netif2): pass def updatemodel(self, config): if not self.model: raise ValueError("no model set to update for node(%s)", self.id) logging.info( "node(%s) updating model(%s): %s", self.id, self.model.name, config ) self.model.set_configs(config, node_id=self.id) def setmodel(self, model, config): """ set the EmaneModel associated with this node """ logging.info("adding model: %s", model.name) if model.config_type == RegisterTlvs.WIRELESS.value: # EmaneModel really uses values from ConfigurableManager # when buildnemxml() is called, not during init() self.model = model(session=self.session, _id=self.id) self.model.update_config(config) elif model.config_type == RegisterTlvs.MOBILITY.value: self.mobility = model(session=self.session, _id=self.id) self.mobility.update_config(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.id) def installnetifs(self): """ 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" logging.error(warntxt) for netif in self.netifs(): external = self.session.emane.get_config( "external", self.id, self.model.name ) if external == "0": 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: logging.info("position service not available") return nemid = self.getnemid(netif) ifname = netif.localname if nemid is None: logging.info("nemid for %s is unknown", ifname) return lat, lon, alt = self.session.location.getgeo(x, y, z) event = LocationEvent() # altitude must be an integer or warning is printed # unused: yaw, pitch, roll, azimuth, elevation, velocity alt = int(round(alt)) event.append(nemid, latitude=lat, longitude=lon, altitude=alt) self.session.emane.service.publish(0, event) 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: logging.info("position service not available") return event = LocationEvent() i = 0 for netif in moved_netifs: nemid = self.getnemid(netif) ifname = netif.localname if nemid is None: logging.info("nemid for %s is unknown" % ifname) continue x, y, z = netif.node.getposition() lat, lon, alt = self.session.location.getgeo(x, y, z) # altitude must be an integer or warning is printed alt = int(round(alt)) event.append(nemid, latitude=lat, longitude=lon, altitude=alt) i += 1 self.session.emane.service.publish(0, event)