daemon: initial changes to rework logic to start emane for a given interface

This commit is contained in:
Blake Harnden 2020-07-02 15:37:51 -07:00
parent da9c0d0660
commit bd48e14348
6 changed files with 262 additions and 496 deletions

View file

@ -62,9 +62,7 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
def config_groups(cls) -> List[ConfigGroup]: def config_groups(cls) -> List[ConfigGroup]:
return [ConfigGroup("CommEffect SHIM Parameters", 1, len(cls.configurations()))] return [ConfigGroup("CommEffect SHIM Parameters", 1, len(cls.configurations()))]
def build_xml_files( def build_xml_files(self, config: Dict[str, str], iface: CoreInterface) -> None:
self, config: Dict[str, str], iface: CoreInterface = None
) -> None:
""" """
Build the necessary nem and commeffect XMLs in the given path. Build the necessary nem and commeffect XMLs in the given path.
If an individual NEM has a nonstandard config, we need to build If an individual NEM has a nonstandard config, we need to build
@ -75,22 +73,25 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
:param iface: interface for the emane node :param iface: interface for the emane node
:return: nothing :return: nothing
""" """
# interface node
node = iface.node
# retrieve xml names # retrieve xml names
nem_name = emanexml.nem_file_name(self, iface) nem_name = emanexml.nem_file_name(iface)
shim_name = emanexml.shim_file_name(self, iface) shim_name = emanexml.shim_file_name(iface)
# create and write nem document # create and write nem document
nem_element = etree.Element("nem", name=f"{self.name} NEM", type="unstructured") nem_element = etree.Element("nem", name=f"{self.name} NEM", type="unstructured")
transport_type = TransportType.VIRTUAL transport_type = TransportType.VIRTUAL
if iface and iface.transport_type == TransportType.RAW: if iface.transport_type == TransportType.RAW:
transport_type = TransportType.RAW transport_type = TransportType.RAW
transport_file = emanexml.transport_file_name(self.id, transport_type) transport_file = emanexml.transport_file_name(iface, transport_type)
etree.SubElement(nem_element, "transport", definition=transport_file) etree.SubElement(nem_element, "transport", definition=transport_file)
# set shim configuration # set shim configuration
etree.SubElement(nem_element, "shim", definition=shim_name) etree.SubElement(nem_element, "shim", definition=shim_name)
nem_file = os.path.join(self.session.session_dir, nem_name) nem_file = os.path.join(node.nodedir, nem_name)
emanexml.create_file(nem_element, "nem", nem_file) emanexml.create_file(nem_element, "nem", nem_file)
# create and write shim document # create and write shim document
@ -111,7 +112,7 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
if ff.strip() != "": if ff.strip() != "":
emanexml.add_param(shim_element, "filterfile", ff) emanexml.add_param(shim_element, "filterfile", ff)
shim_file = os.path.join(self.session.session_dir, shim_name) shim_file = os.path.join(node.nodedir, shim_name)
emanexml.create_file(shim_element, "shim", shim_file) emanexml.create_file(shim_element, "shim", shim_file)
def linkconfig( def linkconfig(

View file

@ -6,6 +6,7 @@ import logging
import os import os
import threading import threading
from collections import OrderedDict from collections import OrderedDict
from enum import Enum
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type
from core import utils from core import utils
@ -25,11 +26,11 @@ from core.emulator.enumerations import (
LinkTypes, LinkTypes,
MessageFlags, MessageFlags,
RegisterTlvs, RegisterTlvs,
TransportType,
) )
from core.errors import CoreCommandError, CoreError from core.errors import CoreCommandError, CoreError
from core.nodes.base import CoreNode, NodeBase from core.nodes.base import CoreNode, NodeBase
from core.nodes.interface import CoreInterface from core.nodes.interface import CoreInterface, TunTap
from core.nodes.network import CtrlNet
from core.nodes.physical import Rj45Node from core.nodes.physical import Rj45Node
from core.xml import emanexml from core.xml import emanexml
@ -63,6 +64,12 @@ DEFAULT_EMANE_PREFIX = "/usr"
DEFAULT_DEV = "ctrl0" DEFAULT_DEV = "ctrl0"
class EmaneState(Enum):
SUCCESS = 0
NOT_NEEDED = 1
NOT_READY = 2
class EmaneManager(ModelManager): class EmaneManager(ModelManager):
""" """
EMANE controller object. Lives in a Session instance and is used for EMANE controller object. Lives in a Session instance and is used for
@ -72,8 +79,6 @@ class EmaneManager(ModelManager):
name: str = "emane" name: str = "emane"
config_type: RegisterTlvs = RegisterTlvs.EMULATION_SERVER config_type: RegisterTlvs = RegisterTlvs.EMULATION_SERVER
SUCCESS: int = 0
NOT_NEEDED: int = 1
NOT_READY: int = 2 NOT_READY: int = 2
EVENTCFGVAR: str = "LIBEMANEEVENTSERVICECONFIG" EVENTCFGVAR: str = "LIBEMANEEVENTSERVICECONFIG"
DEFAULT_LOG_LEVEL: int = 3 DEFAULT_LOG_LEVEL: int = 3
@ -87,6 +92,7 @@ class EmaneManager(ModelManager):
""" """
super().__init__() super().__init__()
self.session: "Session" = session self.session: "Session" = session
self.nems: Dict[int, CoreInterface] = {}
self._emane_nets: Dict[int, EmaneNet] = {} self._emane_nets: Dict[int, EmaneNet] = {}
self._emane_node_lock: threading.Lock = threading.Lock() self._emane_node_lock: threading.Lock = threading.Lock()
# port numbers are allocated from these counters # port numbers are allocated from these counters
@ -111,46 +117,47 @@ class EmaneManager(ModelManager):
self.event_device: Optional[str] = None self.event_device: Optional[str] = None
self.emane_check() self.emane_check()
def next_nem_id(self) -> int:
nem_id = int(self.get_config("nem_id_start"))
while nem_id in self.nems:
nem_id += 1
return nem_id
def get_iface_config( def get_iface_config(
self, node_id: int, iface: CoreInterface, model_name: str self, emane_net: EmaneNet, iface: CoreInterface
) -> Dict[str, str]: ) -> Dict[str, str]:
""" """
Retrieve interface configuration or node configuration if not provided. Retrieve configuration for a given interface.
:param node_id: node id :param emane_net: emane network the interface is connected to
:param iface: node interface :param iface: interface running emane
:param model_name: model to get configuration for :return: net, node, or interface model configuration
:return: node/interface model configuration
""" """
model_name = emane_net.model.name
# use the network-wide config values or interface(NEM)-specific values? # use the network-wide config values or interface(NEM)-specific values?
if iface is None: if iface is None:
return self.get_configs(node_id=node_id, config_type=model_name) return self.get_configs(node_id=emane_net.id, config_type=model_name)
else: else:
# don"t use default values when interface config is the same as net # don"t use default values when interface config is the same as net
# note here that using iface.node.id as key allows for only one type # note here that using iface.node.id as key allows for only one type
# of each model per node; # of each model per node;
# TODO: use both node and interface as key # TODO: use both node and interface as key
# Adamson change: first check for iface config keyed by "node:iface.name" # Adamson change: first check for iface config keyed by "node:iface.name"
# (so that nodes w/ multiple interfaces of same conftype can have # (so that nodes w/ multiple interfaces of same conftype can have
# different configs for each separate interface) # different configs for each separate interface)
key = 1000 * iface.node.id key = 1000 * iface.node.id
if iface.node_id is not None: if iface.node_id is not None:
key += iface.node_id key += iface.node_id
# try retrieve interface specific configuration, avoid getting defaults # try retrieve interface specific configuration, avoid getting defaults
config = self.get_configs(node_id=key, config_type=model_name) config = self.get_configs(node_id=key, config_type=model_name)
# otherwise retrieve the interfaces node configuration, avoid using defaults # otherwise retrieve the interfaces node configuration, avoid using defaults
if not config: if not config:
config = self.get_configs(node_id=iface.node.id, config_type=model_name) config = self.get_configs(node_id=iface.node.id, config_type=model_name)
# get non interface config, when none found # get non interface config, when none found
if not config: if not config:
# with EMANE 0.9.2+, we need an extra NEM XML from # with EMANE 0.9.2+, we need an extra NEM XML from
# model.buildnemxmlfiles(), so defaults are returned here # model.buildnemxmlfiles(), so defaults are returned here
config = self.get_configs(node_id=node_id, config_type=model_name) config = self.get_configs(node_id=emane_net.id, config_type=model_name)
return config return config
def config_reset(self, node_id: int = None) -> None: def config_reset(self, node_id: int = None) -> None:
@ -260,14 +267,13 @@ class EmaneManager(ModelManager):
Return a set of CoreNodes that are linked to an EMANE network, Return a set of CoreNodes that are linked to an EMANE network,
e.g. containers having one or more radio interfaces. e.g. containers having one or more radio interfaces.
""" """
# assumes self._objslock already held
nodes = set() nodes = set()
for emane_net in self._emane_nets.values(): for emane_net in self._emane_nets.values():
for iface in emane_net.get_ifaces(): for iface in emane_net.get_ifaces():
nodes.add(iface.node) nodes.add(iface.node)
return nodes return nodes
def setup(self) -> int: def setup(self) -> EmaneState:
""" """
Setup duties for EMANE manager. Setup duties for EMANE manager.
@ -288,7 +294,7 @@ class EmaneManager(ModelManager):
if not self._emane_nets: if not self._emane_nets:
logging.debug("no emane nodes in session") logging.debug("no emane nodes in session")
return EmaneManager.NOT_NEEDED return EmaneState.NOT_NEEDED
# check if bindings were installed # check if bindings were installed
if EventService is None: if EventService is None:
@ -304,7 +310,7 @@ class EmaneManager(ModelManager):
"EMANE cannot start, check core config. invalid OTA device provided: %s", "EMANE cannot start, check core config. invalid OTA device provided: %s",
otadev, otadev,
) )
return EmaneManager.NOT_READY return EmaneState.NOT_READY
self.session.add_remove_control_net( self.session.add_remove_control_net(
net_index=netidx, remove=False, conf_required=False net_index=netidx, remove=False, conf_required=False
@ -319,16 +325,16 @@ class EmaneManager(ModelManager):
"EMANE cannot start, check core config. invalid event service device: %s", "EMANE cannot start, check core config. invalid event service device: %s",
eventdev, eventdev,
) )
return EmaneManager.NOT_READY return EmaneState.NOT_READY
self.session.add_remove_control_net( self.session.add_remove_control_net(
net_index=netidx, remove=False, conf_required=False net_index=netidx, remove=False, conf_required=False
) )
self.check_node_models() self.check_node_models()
return EmaneManager.SUCCESS return EmaneState.SUCCESS
def startup(self) -> int: def startup(self) -> EmaneState:
""" """
After all the EMANE networks have been added, build XML files After all the EMANE networks have been added, build XML files
and start the daemons. and start the daemons.
@ -337,39 +343,49 @@ class EmaneManager(ModelManager):
instantiation instantiation
""" """
self.reset() self.reset()
r = self.setup() status = self.setup()
if status != EmaneState.SUCCESS:
# NOT_NEEDED or NOT_READY return status
if r != EmaneManager.SUCCESS: self.starteventmonitor()
return r self.buildeventservicexml()
nems = []
with self._emane_node_lock: with self._emane_node_lock:
self.buildxml() # on master, control network bridge added earlier in startup()
self.starteventmonitor() control_net = self.session.add_remove_control_net(
0, remove=False, conf_required=False
if self.numnems() > 0: )
self.startdaemons() logging.info("emane building xmls...")
self.install_ifaces() for node_id in sorted(self._emane_nets):
emane_net = self._emane_nets[node_id]
for node_id in self._emane_nets: if not emane_net.model:
emane_node = self._emane_nets[node_id] logging.error("emane net(%s) has no model", emane_net.name)
for iface in emane_node.get_ifaces(): continue
nems.append( for iface in emane_net.get_ifaces():
(iface.node.name, iface.name, emane_node.getnemid(iface)) if not iface.node:
logging.error(
"emane net(%s) connected interface missing node",
emane_net.name,
)
continue
nem_id = self.next_nem_id()
self.nems[nem_id] = iface
self.write_nem(iface, nem_id)
emanexml.build_platform_xml(
self, control_net, emane_net, iface, nem_id
) )
emanexml.build_model_xmls(self, emane_net, iface)
if nems: self.start_daemon(iface)
emane_nems_filename = os.path.join(self.session.session_dir, "emane_nems") self.install_iface(emane_net, iface)
try:
with open(emane_nems_filename, "w") as f:
for nodename, ifname, nemid in nems:
f.write(f"{nodename} {ifname} {nemid}\n")
except IOError:
logging.exception("Error writing EMANE NEMs file: %s")
if self.links_enabled(): if self.links_enabled():
self.link_monitor.start() self.link_monitor.start()
return EmaneManager.SUCCESS return EmaneState.SUCCESS
def write_nem(self, iface: CoreInterface, nem_id: int) -> None:
path = os.path.join(self.session.session_dir, "emane_nems")
try:
with open(path, "a") as f:
f.write(f"{iface.node.name} {iface.name} {nem_id}\n")
except IOError:
logging.exception("error writing to emane nem file")
def links_enabled(self) -> bool: def links_enabled(self) -> bool:
return self.get_config("link_enabled") == "1" return self.get_config("link_enabled") == "1"
@ -380,17 +396,14 @@ class EmaneManager(ModelManager):
""" """
if not self.genlocationevents(): if not self.genlocationevents():
return return
with self._emane_node_lock: with self._emane_node_lock:
for key in sorted(self._emane_nets.keys()): for node_id in sorted(self._emane_nets):
emane_node = self._emane_nets[key] emane_net = self._emane_nets[node_id]
logging.debug( logging.debug(
"post startup for emane node: %s - %s", "post startup for emane node: %s - %s", emane_net.id, emane_net.name
emane_node.id,
emane_node.name,
) )
emane_node.model.post_startup() emane_net.model.post_startup()
for iface in emane_node.get_ifaces(): for iface in emane_net.get_ifaces():
iface.setposition() iface.setposition()
def reset(self) -> None: def reset(self) -> None:
@ -400,13 +413,7 @@ class EmaneManager(ModelManager):
""" """
with self._emane_node_lock: with self._emane_node_lock:
self._emane_nets.clear() self._emane_nets.clear()
self.nems.clear()
self.platformport = self.session.options.get_config_int(
"emane_platform_port", 8100
)
self.transformport = self.session.options.get_config_int(
"emane_transform_port", 8200
)
def shutdown(self) -> None: def shutdown(self) -> None:
""" """
@ -422,40 +429,23 @@ class EmaneManager(ModelManager):
self.stopdaemons() self.stopdaemons()
self.stopeventmonitor() self.stopeventmonitor()
def buildxml(self) -> None:
"""
Build XML files required to run EMANE on each node.
NEMs run inside containers using the control network for passing
events and data.
"""
# assume self._objslock is already held here
logging.info("emane building xml...")
# on master, control network bridge added earlier in startup()
ctrlnet = self.session.add_remove_control_net(
net_index=0, remove=False, conf_required=False
)
self.buildplatformxml(ctrlnet)
self.buildnemxml()
self.buildeventservicexml()
def check_node_models(self) -> None: def check_node_models(self) -> None:
""" """
Associate EMANE model classes with EMANE network nodes. Associate EMANE model classes with EMANE network nodes.
""" """
for node_id in self._emane_nets: for node_id in self._emane_nets:
emane_node = self._emane_nets[node_id] emane_net = self._emane_nets[node_id]
logging.debug("checking emane model for node: %s", node_id) logging.debug("checking emane model for node: %s", node_id)
# skip nodes that already have a model set # skip nodes that already have a model set
if emane_node.model: if emane_net.model:
logging.debug( logging.debug(
"node(%s) already has model(%s)", "node(%s) already has model(%s)", emane_net.id, emane_net.model.name
emane_node.id,
emane_node.model.name,
) )
continue continue
# set model configured for node, due to legacy messaging configuration before nodes exist # set model configured for node, due to legacy messaging configuration
# before nodes exist
model_name = self.node_models.get(node_id) model_name = self.node_models.get(node_id)
if not model_name: if not model_name:
logging.error("emane node(%s) has no node model", node_id) logging.error("emane node(%s) has no node model", node_id)
@ -464,7 +454,7 @@ class EmaneManager(ModelManager):
config = self.get_model_config(node_id=node_id, model_name=model_name) config = self.get_model_config(node_id=node_id, model_name=model_name)
logging.debug("setting emane model(%s) config(%s)", model_name, config) logging.debug("setting emane model(%s) config(%s)", model_name, config)
model_class = self.models[model_name] model_class = self.models[model_name]
emane_node.setmodel(model_class, config) emane_net.setmodel(model_class, config)
def nemlookup(self, nemid) -> Tuple[Optional[EmaneNet], Optional[CoreInterface]]: def nemlookup(self, nemid) -> Tuple[Optional[EmaneNet], Optional[CoreInterface]]:
""" """
@ -473,7 +463,6 @@ class EmaneManager(ModelManager):
""" """
emane_node = None emane_node = None
iface = None iface = None
for node_id in self._emane_nets: for node_id in self._emane_nets:
emane_node = self._emane_nets[node_id] emane_node = self._emane_nets[node_id]
iface = emane_node.get_nem_iface(nemid) iface = emane_node.get_nem_iface(nemid)
@ -481,7 +470,6 @@ class EmaneManager(ModelManager):
break break
else: else:
emane_node = None emane_node = None
return emane_node, iface return emane_node, iface
def get_nem_link( def get_nem_link(
@ -507,38 +495,6 @@ class EmaneManager(ModelManager):
color=color, color=color,
) )
def numnems(self) -> int:
"""
Return the number of NEMs emulated locally.
"""
count = 0
for node_id in self._emane_nets:
emane_node = self._emane_nets[node_id]
count += len(emane_node.ifaces)
return count
def buildplatformxml(self, ctrlnet: CtrlNet) -> None:
"""
Build a platform.xml file now that all nodes are configured.
"""
nemid = int(self.get_config("nem_id_start"))
platform_xmls = {}
# assume self._objslock is already held here
for key in sorted(self._emane_nets.keys()):
emane_node = self._emane_nets[key]
nemid = emanexml.build_node_platform_xml(
self, ctrlnet, emane_node, nemid, platform_xmls
)
def buildnemxml(self) -> None:
"""
Builds the nem, mac, and phy xml files for each EMANE network.
"""
for key in sorted(self._emane_nets):
emane_net = self._emane_nets[key]
emanexml.build_xml_files(self, emane_net)
def buildeventservicexml(self) -> None: def buildeventservicexml(self) -> None:
""" """
Build the libemaneeventservice.xml file if event service options Build the libemaneeventservice.xml file if event service options
@ -571,7 +527,7 @@ class EmaneManager(ModelManager):
) )
) )
def startdaemons(self) -> None: def start_daemon(self, iface: CoreInterface) -> None:
""" """
Start one EMANE daemon per node having a radio. Start one EMANE daemon per node having a radio.
Add a control network even if the user has not configured one. Add a control network even if the user has not configured one.
@ -583,69 +539,51 @@ class EmaneManager(ModelManager):
if cfgloglevel: if cfgloglevel:
logging.info("setting user-defined EMANE log level: %d", cfgloglevel) logging.info("setting user-defined EMANE log level: %d", cfgloglevel)
loglevel = str(cfgloglevel) loglevel = str(cfgloglevel)
emanecmd = f"emane -d -l {loglevel}" emanecmd = f"emane -d -l {loglevel}"
if realtime: if realtime:
emanecmd += " -r" emanecmd += " -r"
otagroup, _otaport = self.get_config("otamanagergroup").split(":") otagroup, _otaport = self.get_config("otamanagergroup").split(":")
otadev = self.get_config("otamanagerdevice") otadev = self.get_config("otamanagerdevice")
otanetidx = self.session.get_control_net_index(otadev) otanetidx = self.session.get_control_net_index(otadev)
eventgroup, _eventport = self.get_config("eventservicegroup").split(":") eventgroup, _eventport = self.get_config("eventservicegroup").split(":")
eventdev = self.get_config("eventservicedevice") eventdev = self.get_config("eventservicedevice")
eventservicenetidx = self.session.get_control_net_index(eventdev) eventservicenetidx = self.session.get_control_net_index(eventdev)
node = iface.node
run_emane_on_host = False if not isinstance(node, Rj45Node):
for node in self.getnodes():
if isinstance(node, Rj45Node):
run_emane_on_host = True
continue
path = self.session.session_dir
n = node.id
# control network not yet started here # control network not yet started here
self.session.add_remove_control_iface( self.session.add_remove_control_iface(
node, 0, remove=False, conf_required=False node, 0, remove=False, conf_required=False
) )
if otanetidx > 0: if otanetidx > 0:
logging.info("adding ota device ctrl%d", otanetidx) logging.info("adding ota device ctrl%d", otanetidx)
self.session.add_remove_control_iface( self.session.add_remove_control_iface(
node, otanetidx, remove=False, conf_required=False node, otanetidx, remove=False, conf_required=False
) )
if eventservicenetidx >= 0: if eventservicenetidx >= 0:
logging.info("adding event service device ctrl%d", eventservicenetidx) logging.info("adding event service device ctrl%d", eventservicenetidx)
self.session.add_remove_control_iface( self.session.add_remove_control_iface(
node, eventservicenetidx, remove=False, conf_required=False node, eventservicenetidx, remove=False, conf_required=False
) )
# multicast route is needed for OTA data # multicast route is needed for OTA data
node.node_net_client.create_route(otagroup, otadev) node.node_net_client.create_route(otagroup, otadev)
# multicast route is also needed for event data if on control network # multicast route is also needed for event data if on control network
if eventservicenetidx >= 0 and eventgroup != otagroup: if eventservicenetidx >= 0 and eventgroup != otagroup:
node.node_net_client.create_route(eventgroup, eventdev) node.node_net_client.create_route(eventgroup, eventdev)
# start emane # start emane
log_file = os.path.join(path, f"emane{n}.log") log_file = os.path.join(node.nodedir, f"{iface.name}-emane.log")
platform_xml = os.path.join(path, f"platform{n}.xml") platform_xml = os.path.join(node.nodedir, f"{iface.name}-platform.xml")
args = f"{emanecmd} -f {log_file} {platform_xml}" args = f"{emanecmd} -f {log_file} {platform_xml}"
output = node.cmd(args) output = node.cmd(args)
logging.info("node(%s) emane daemon running: %s", node.name, args) logging.info("node(%s) emane daemon running: %s", node.name, args)
logging.debug("node(%s) emane daemon output: %s", node.name, output) logging.debug("node(%s) emane daemon output: %s", node.name, output)
else:
if not run_emane_on_host: path = self.session.session_dir
return log_file = os.path.join(path, f"{iface.name}-emane.log")
platform_xml = os.path.join(path, f"{iface.name}-platform.xml")
path = self.session.session_dir emanecmd += f" -f {log_file} {platform_xml}"
log_file = os.path.join(path, "emane.log") utils.cmd(emanecmd, cwd=path)
platform_xml = os.path.join(path, "platform.xml") self.session.distributed.execute(lambda x: x.remote_cmd(emanecmd, cwd=path))
emanecmd += f" -f {log_file} {platform_xml}" logging.info("host emane daemon running: %s", emanecmd)
utils.cmd(emanecmd, cwd=path)
self.session.distributed.execute(lambda x: x.remote_cmd(emanecmd, cwd=path))
logging.info("host emane daemon running: %s", emanecmd)
def stopdaemons(self) -> None: def stopdaemons(self) -> None:
""" """
@ -674,23 +612,27 @@ class EmaneManager(ModelManager):
except CoreCommandError: except CoreCommandError:
logging.exception("error shutting down emane daemons") logging.exception("error shutting down emane daemons")
def install_ifaces(self) -> None: def install_iface(self, emane_net: EmaneNet, iface: CoreInterface) -> None:
""" config = self.get_iface_config(emane_net, iface)
Install TUN/TAP virtual interfaces into their proper namespaces external = config.get("external", "0")
now that the EMANE daemons are running. if isinstance(iface, TunTap) and external == "0":
""" iface.set_ips()
for key in sorted(self._emane_nets.keys()): # at this point we register location handlers for generating
node = self._emane_nets[key] # EMANE location events
logging.info("emane install interface for node(%s): %d", node.name, key) if self.genlocationevents():
node.install_ifaces() iface.poshook = emane_net.setnemposition
iface.setposition()
def deinstall_ifaces(self) -> None: def deinstall_ifaces(self) -> None:
""" """
Uninstall TUN/TAP virtual interfaces. Uninstall TUN/TAP virtual interfaces.
""" """
for key in sorted(self._emane_nets.keys()): for key in sorted(self._emane_nets):
emane_node = self._emane_nets[key] emane_net = self._emane_nets[key]
emane_node.deinstall_ifaces() for iface in emane_net.get_ifaces():
if iface.transport_type == TransportType.VIRTUAL:
iface.shutdown()
iface.poshook = None
def doeventmonitor(self) -> bool: def doeventmonitor(self) -> bool:
""" """
@ -718,7 +660,6 @@ class EmaneManager(ModelManager):
logging.info("emane start event monitor") logging.info("emane start event monitor")
if not self.doeventmonitor(): if not self.doeventmonitor():
return return
if self.service is None: if self.service is None:
logging.error( logging.error(
"Warning: EMANE events will not be generated " "Warning: EMANE events will not be generated "

View file

@ -96,9 +96,7 @@ class EmaneModel(WirelessModel):
ConfigGroup("External Parameters", phy_len + 1, config_len), ConfigGroup("External Parameters", phy_len + 1, config_len),
] ]
def build_xml_files( def build_xml_files(self, config: Dict[str, str], iface: CoreInterface) -> None:
self, config: Dict[str, str], iface: CoreInterface = None
) -> None:
""" """
Builds xml files for this emane model. Creates a nem.xml file that points to Builds xml files for this emane model. Creates a nem.xml file that points to
both mac.xml and phy.xml definitions. both mac.xml and phy.xml definitions.
@ -107,33 +105,30 @@ class EmaneModel(WirelessModel):
:param iface: interface for the emane node :param iface: interface for the emane node
:return: nothing :return: nothing
""" """
nem_name = emanexml.nem_file_name(self, iface) nem_name = emanexml.nem_file_name(iface)
mac_name = emanexml.mac_file_name(self, iface) mac_name = emanexml.mac_file_name(iface)
phy_name = emanexml.phy_file_name(self, iface) phy_name = emanexml.phy_file_name(iface)
# remote server for file
server = None
if iface is not None:
server = iface.node.server
# check if this is external # check if this is external
transport_type = TransportType.VIRTUAL transport_type = TransportType.VIRTUAL
if iface and iface.transport_type == TransportType.RAW: if iface.transport_type == TransportType.RAW:
transport_type = TransportType.RAW transport_type = TransportType.RAW
transport_name = emanexml.transport_file_name(self.id, transport_type) transport_name = emanexml.transport_file_name(iface, transport_type)
node = iface.node
server = node.server
# create nem xml file # create nem xml file
nem_file = os.path.join(self.session.session_dir, nem_name) nem_file = os.path.join(node.nodedir, nem_name)
emanexml.create_nem_xml( emanexml.create_nem_xml(
self, config, nem_file, transport_name, mac_name, phy_name, server self, config, nem_file, transport_name, mac_name, phy_name, server
) )
# create mac xml file # create mac xml file
mac_file = os.path.join(self.session.session_dir, mac_name) mac_file = os.path.join(node.nodedir, mac_name)
emanexml.create_mac_xml(self, config, mac_file, server) emanexml.create_mac_xml(self, config, mac_file, server)
# create phy xml file # create phy xml file
phy_file = os.path.join(self.session.session_dir, phy_name) phy_file = os.path.join(node.nodedir, phy_name)
emanexml.create_phy_xml(self, config, phy_file, server) emanexml.create_phy_xml(self, config, phy_file, server)
def post_startup(self) -> None: def post_startup(self) -> None:

View file

@ -8,17 +8,10 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Type
from core.emulator.data import InterfaceData, LinkData, LinkOptions from core.emulator.data import InterfaceData, LinkData, LinkOptions
from core.emulator.distributed import DistributedServer from core.emulator.distributed import DistributedServer
from core.emulator.enumerations import ( from core.emulator.enumerations import LinkTypes, MessageFlags, NodeTypes, RegisterTlvs
EventTypes,
LinkTypes,
MessageFlags,
NodeTypes,
RegisterTlvs,
TransportType,
)
from core.errors import CoreError from core.errors import CoreError
from core.nodes.base import CoreNetworkBase, CoreNode from core.nodes.base import CoreNetworkBase, CoreNode
from core.nodes.interface import CoreInterface, TunTap from core.nodes.interface import CoreInterface
if TYPE_CHECKING: if TYPE_CHECKING:
from core.emane.emanemodel import EmaneModel from core.emane.emanemodel import EmaneModel
@ -139,45 +132,6 @@ class EmaneNet(CoreNetworkBase):
return iface return iface
return None return None
def install_ifaces(self) -> None:
"""
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 iface in self.get_ifaces():
config = self.session.emane.get_iface_config(
self.id, iface, self.model.name
)
external = config.get("external", "0")
if isinstance(iface, TunTap) and external == "0":
iface.set_ips()
if not self.session.emane.genlocationevents():
iface.poshook = None
continue
# at this point we register location handlers for generating
# EMANE location events
iface.poshook = self.setnemposition
iface.setposition()
def deinstall_ifaces(self) -> None:
"""
Uninstall TAP devices. This invokes their shutdown method for
any required cleanup; the device may be actually removed when
emanetransportd terminates.
"""
for iface in self.get_ifaces():
if iface.transport_type == TransportType.VIRTUAL:
iface.shutdown()
iface.poshook = None
def _nem_position( def _nem_position(
self, iface: CoreInterface self, iface: CoreInterface
) -> Optional[Tuple[int, float, float, float]]: ) -> Optional[Tuple[int, float, float, float]]:
@ -275,20 +229,4 @@ class EmaneNet(CoreNetworkBase):
iface.set_mac(iface_data.mac) iface.set_mac(iface_data.mac)
for ip in iface_data.get_ips(): for ip in iface_data.get_ips():
iface.add_ip(ip) iface.add_ip(ip)
# TODO: if added during runtime start EMANE
if self.session.state == EventTypes.RUNTIME_STATE:
logging.info("startup emane for node: %s", node.name)
# create specific xml if needed
config = self.session.emane.get_iface_config(
self.model.id, iface, self.model.name
)
if config:
self.model.build_xml_files(config, iface)
# start emane daemon
# install netif
# add nem to nemfile
return iface return iface

View file

@ -15,7 +15,7 @@ from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, TypeVa
from core import constants, utils from core import constants, utils
from core.configservice.manager import ConfigServiceManager from core.configservice.manager import ConfigServiceManager
from core.emane.emanemanager import EmaneManager from core.emane.emanemanager import EmaneManager, EmaneState
from core.emane.nodes import EmaneNet from core.emane.nodes import EmaneNet
from core.emulator.data import ( from core.emulator.data import (
ConfigData, ConfigData,
@ -1181,7 +1181,7 @@ class Session:
self.distributed.start() self.distributed.start()
# instantiate will be invoked again upon emane configure # instantiate will be invoked again upon emane configure
if self.emane.startup() == self.emane.NOT_READY: if self.emane.startup() == EmaneState.NOT_READY:
return [] return []
# boot node services and then start mobility # boot node services and then start mobility

View file

@ -10,6 +10,7 @@ from core.config import Configuration
from core.emane.nodes import EmaneNet from core.emane.nodes import EmaneNet
from core.emulator.distributed import DistributedServer from core.emulator.distributed import DistributedServer
from core.emulator.enumerations import TransportType from core.emulator.enumerations import TransportType
from core.errors import CoreError
from core.nodes.interface import CoreInterface from core.nodes.interface import CoreInterface
from core.nodes.network import CtrlNet from core.nodes.network import CtrlNet
from core.xml import corexml from core.xml import corexml
@ -40,15 +41,11 @@ def _value_to_params(value: str) -> Optional[Tuple[str]]:
""" """
try: try:
values = utils.make_tuple_fromstr(value, str) values = utils.make_tuple_fromstr(value, str)
if not hasattr(values, "__iter__"): if not hasattr(values, "__iter__"):
return None return None
if len(values) < 2: if len(values) < 2:
return None return None
return values return values
except SyntaxError: except SyntaxError:
logging.exception("error in value string to param list") logging.exception("error in value string to param list")
return None return None
@ -127,13 +124,13 @@ def add_configurations(
add_param(xml_element, name, value) add_param(xml_element, name, value)
def build_node_platform_xml( def build_platform_xml(
emane_manager: "EmaneManager", emane_manager: "EmaneManager",
control_net: CtrlNet, control_net: CtrlNet,
node: EmaneNet, emane_net: EmaneNet,
iface: CoreInterface,
nem_id: int, nem_id: int,
platform_xmls: Dict[str, etree.Element], ) -> None:
) -> int:
""" """
Create platform xml for a specific node. Create platform xml for a specific node.
@ -141,175 +138,121 @@ def build_node_platform_xml(
configurations configurations
:param control_net: control net node for this emane :param control_net: control net node for this emane
network network
:param node: node to write platform xml for :param emane_net: emane network associated with interface
:param nem_id: nem id to use for interfaces for this node :param iface: interface running emane
:param platform_xmls: stores platform xml elements to append nem entries to :param nem_id: nem id to use for this interface
:return: the next nem id that can be used for creating platform xml files :return: the next nem id that can be used for creating platform xml files
""" """
logging.debug( # build nem xml
"building emane platform xml for node(%s) nem_id(%s): %s", nem_definition = nem_file_name(iface)
node, nem_element = etree.Element(
nem_id, "nem", id=str(nem_id), name=iface.localname, definition=nem_definition
node.name,
) )
nem_entries = {}
if node.model is None: # check if this is an external transport, get default config if an interface
logging.warning("warning: EMANE network %s has no associated model", node.name) # specific one does not exist
return nem_id config = emane_manager.get_iface_config(emane_net, iface)
if is_external(config):
for iface in node.get_ifaces(): nem_element.set("transport", "external")
logging.debug( platform_endpoint = "platformendpoint"
"building platform xml for interface(%s) nem_id(%s)", iface.name, nem_id add_param(nem_element, platform_endpoint, config[platform_endpoint])
) transport_endpoint = "transportendpoint"
# build nem xml add_param(nem_element, transport_endpoint, config[transport_endpoint])
nem_definition = nem_file_name(node.model, iface) else:
nem_element = etree.Element( # build transport xml
"nem", id=str(nem_id), name=iface.localname, definition=nem_definition transport_type = iface.transport_type
if not transport_type:
logging.info("warning: %s interface type unsupported!", iface.name)
transport_type = TransportType.RAW
transport_file = transport_file_name(iface, transport_type)
transport_element = etree.SubElement(
nem_element, "transport", definition=transport_file
) )
add_param(transport_element, "device", iface.name)
# check if this is an external transport, get default config if an interface # determine platform element to add xml to
# specific one does not exist key = iface.node.id
config = emane_manager.get_iface_config(node.model.id, iface, node.model.name) if iface.transport_type == TransportType.RAW:
key = "host"
if is_external(config): otadev = control_net.brname
nem_element.set("transport", "external") eventdev = control_net.brname
platform_endpoint = "platformendpoint" else:
add_param(nem_element, platform_endpoint, config[platform_endpoint]) otadev = None
transport_endpoint = "transportendpoint" eventdev = None
add_param(nem_element, transport_endpoint, config[transport_endpoint]) platform_element = etree.Element("platform")
else: if otadev:
# build transport xml emane_manager.set_config("otamanagerdevice", otadev)
transport_type = iface.transport_type if eventdev:
if not transport_type: emane_manager.set_config("eventservicedevice", eventdev)
logging.info("warning: %s interface type unsupported!", iface.name) for configuration in emane_manager.emane_config.emulator_config:
transport_type = TransportType.RAW name = configuration.id
transport_file = transport_file_name(node.id, transport_type) value = emane_manager.get_config(name)
transport_element = etree.SubElement( add_param(platform_element, name, value)
nem_element, "transport", definition=transport_file platform_element.append(nem_element)
) emane_net.setnemid(iface, nem_id)
mac = _MAC_PREFIX + ":00:00:"
# add transport parameter mac += f"{(nem_id >> 8) & 0xFF:02X}:{nem_id & 0xFF:02X}"
add_param(transport_element, "device", iface.name) iface.set_mac(mac)
# add nem entry
nem_entries[iface] = nem_element
# merging code
key = iface.node.id
if iface.transport_type == TransportType.RAW:
key = "host"
otadev = control_net.brname
eventdev = control_net.brname
else:
otadev = None
eventdev = None
platform_element = platform_xmls.get(key)
if platform_element is None:
platform_element = etree.Element("platform")
if otadev:
emane_manager.set_config("otamanagerdevice", otadev)
if eventdev:
emane_manager.set_config("eventservicedevice", eventdev)
# append all platform options (except starting id) to doc
for configuration in emane_manager.emane_config.emulator_config:
name = configuration.id
if name == "platform_id_start":
continue
value = emane_manager.get_config(name)
add_param(platform_element, name, value)
# add platform xml
platform_xmls[key] = platform_element
platform_element.append(nem_element)
node.setnemid(iface, nem_id)
mac = _MAC_PREFIX + ":00:00:"
mac += f"{(nem_id >> 8) & 0xFF:02X}:{nem_id & 0xFF:02X}"
iface.set_mac(mac)
# increment nem id
nem_id += 1
doc_name = "platform" doc_name = "platform"
for key in sorted(platform_xmls.keys()): server = None
platform_element = platform_xmls[key] if key == "host":
if key == "host": file_name = "platform.xml"
file_name = "platform.xml" file_path = os.path.join(emane_manager.session.session_dir, file_name)
file_path = os.path.join(emane_manager.session.session_dir, file_name) else:
create_file(platform_element, doc_name, file_path) node = iface.node
else: file_name = f"{iface.name}-platform.xml"
file_name = f"platform{key}.xml" file_path = os.path.join(node.nodedir, file_name)
file_path = os.path.join(emane_manager.session.session_dir, file_name) server = node.server
linked_node = emane_manager.session.nodes[key] create_file(platform_element, doc_name, file_path, server)
create_file(platform_element, doc_name, file_path, linked_node.server)
return nem_id
def build_xml_files(emane_manager: "EmaneManager", node: EmaneNet) -> None: def build_model_xmls(
manager: "EmaneManager", emane_net: EmaneNet, iface: CoreInterface
) -> None:
""" """
Generate emane xml files required for node. Generate emane xml files required for node.
:param emane_manager: emane manager with emane :param manager: emane manager with emane
configurations configurations
:param node: node to write platform xml for :param emane_net: emane network associated with interface
:param iface: interface to create emane xml for
:return: nothing :return: nothing
""" """
logging.debug("building all emane xml for node(%s): %s", node, node.name)
if node.model is None:
return
# get model configurations
config = emane_manager.get_configs(node.model.id, node.model.name)
if not config:
return
# build XML for overall network EMANE configs
node.model.build_xml_files(config)
# build XML for specific interface (NEM) configs # build XML for specific interface (NEM) configs
# check for interface specific emane configuration and write xml files
config = manager.get_iface_config(emane_net, iface)
emane_net.model.build_xml_files(config, iface)
# check transport type needed for interface
need_virtual = False need_virtual = False
need_raw = False need_raw = False
vtype = TransportType.VIRTUAL vtype = TransportType.VIRTUAL
rtype = TransportType.RAW rtype = TransportType.RAW
if iface.transport_type == TransportType.VIRTUAL:
for iface in node.get_ifaces(): need_virtual = True
# check for interface specific emane configuration and write xml files vtype = iface.transport_type
config = emane_manager.get_iface_config(node.model.id, iface, node.model.name) else:
if config: need_raw = True
node.model.build_xml_files(config, iface) rtype = iface.transport_type
# check transport type needed for interface
if iface.transport_type == TransportType.VIRTUAL:
need_virtual = True
vtype = iface.transport_type
else:
need_raw = True
rtype = iface.transport_type
if need_virtual: if need_virtual:
build_transport_xml(emane_manager, node, vtype) build_transport_xml(manager, emane_net, iface, vtype)
if need_raw: if need_raw:
build_transport_xml(emane_manager, node, rtype) build_transport_xml(manager, emane_net, iface, rtype)
def build_transport_xml( def build_transport_xml(
emane_manager: "EmaneManager", node: EmaneNet, transport_type: TransportType manager: "EmaneManager",
emane_net: EmaneNet,
iface: CoreInterface,
transport_type: TransportType,
) -> None: ) -> None:
""" """
Build transport xml file for node and transport type. Build transport xml file for node and transport type.
:param emane_manager: emane manager with emane :param manager: emane manager with emane configurations
configurations :param emane_net: emane network associated with interface
:param node: node to write platform xml for :param iface: interface to build transport xml for
:param transport_type: transport type to build xml for :param transport_type: transport type to build xml for
:return: nothing :return: nothing
""" """
@ -318,28 +261,24 @@ def build_transport_xml(
name=f"{transport_type.value.capitalize()} Transport", name=f"{transport_type.value.capitalize()} Transport",
library=f"trans{transport_type.value.lower()}", library=f"trans{transport_type.value.lower()}",
) )
# add bitrate
add_param(transport_element, "bitrate", "0") add_param(transport_element, "bitrate", "0")
# get emane model cnfiguration # get emane model cnfiguration
config = emane_manager.get_configs(node.id, node.model.name) config = manager.get_iface_config(emane_net, iface)
flowcontrol = config.get("flowcontrolenable", "0") == "1" flowcontrol = config.get("flowcontrolenable", "0") == "1"
if transport_type == TransportType.VIRTUAL: if transport_type == TransportType.VIRTUAL:
device_path = "/dev/net/tun_flowctl" device_path = "/dev/net/tun_flowctl"
if not os.path.exists(device_path): if not os.path.exists(device_path):
device_path = "/dev/net/tun" device_path = "/dev/net/tun"
add_param(transport_element, "devicepath", device_path) add_param(transport_element, "devicepath", device_path)
if flowcontrol: if flowcontrol:
add_param(transport_element, "flowcontrolenable", "on") add_param(transport_element, "flowcontrolenable", "on")
doc_name = "transport" doc_name = "transport"
file_name = transport_file_name(node.id, transport_type) node = iface.node
file_path = os.path.join(emane_manager.session.session_dir, file_name) file_name = transport_file_name(iface, transport_type)
file_path = os.path.join(node.nodedir, file_name)
create_file(transport_element, doc_name, file_path) create_file(transport_element, doc_name, file_path)
emane_manager.session.distributed.execute( manager.session.distributed.execute(
lambda x: create_file(transport_element, doc_name, file_path, x) lambda x: create_file(transport_element, doc_name, file_path, x)
) )
@ -348,7 +287,7 @@ def create_phy_xml(
emane_model: "EmaneModel", emane_model: "EmaneModel",
config: Dict[str, str], config: Dict[str, str],
file_path: str, file_path: str,
server: DistributedServer, server: Optional[DistributedServer],
) -> None: ) -> None:
""" """
Create the phy xml document. Create the phy xml document.
@ -363,25 +302,17 @@ def create_phy_xml(
phy_element = etree.Element("phy", name=f"{emane_model.name} PHY") phy_element = etree.Element("phy", name=f"{emane_model.name} PHY")
if emane_model.phy_library: if emane_model.phy_library:
phy_element.set("library", emane_model.phy_library) phy_element.set("library", emane_model.phy_library)
add_configurations( add_configurations(
phy_element, emane_model.phy_config, config, emane_model.config_ignore phy_element, emane_model.phy_config, config, emane_model.config_ignore
) )
create_file(phy_element, "phy", file_path) create_file(phy_element, "phy", file_path, server)
if server is not None:
create_file(phy_element, "phy", file_path, server)
else:
create_file(phy_element, "phy", file_path)
emane_model.session.distributed.execute(
lambda x: create_file(phy_element, "phy", file_path, x)
)
def create_mac_xml( def create_mac_xml(
emane_model: "EmaneModel", emane_model: "EmaneModel",
config: Dict[str, str], config: Dict[str, str],
file_path: str, file_path: str,
server: DistributedServer, server: Optional[DistributedServer],
) -> None: ) -> None:
""" """
Create the mac xml document. Create the mac xml document.
@ -394,22 +325,14 @@ def create_mac_xml(
:return: nothing :return: nothing
""" """
if not emane_model.mac_library: if not emane_model.mac_library:
raise ValueError("must define emane model library") raise CoreError("must define emane model library")
mac_element = etree.Element( mac_element = etree.Element(
"mac", name=f"{emane_model.name} MAC", library=emane_model.mac_library "mac", name=f"{emane_model.name} MAC", library=emane_model.mac_library
) )
add_configurations( add_configurations(
mac_element, emane_model.mac_config, config, emane_model.config_ignore mac_element, emane_model.mac_config, config, emane_model.config_ignore
) )
create_file(mac_element, "mac", file_path) create_file(mac_element, "mac", file_path, server)
if server is not None:
create_file(mac_element, "mac", file_path, server)
else:
create_file(mac_element, "mac", file_path)
emane_model.session.distributed.execute(
lambda x: create_file(mac_element, "mac", file_path, x)
)
def create_nem_xml( def create_nem_xml(
@ -419,7 +342,7 @@ def create_nem_xml(
transport_definition: str, transport_definition: str,
mac_definition: str, mac_definition: str,
phy_definition: str, phy_definition: str,
server: DistributedServer, server: Optional[DistributedServer],
) -> None: ) -> None:
""" """
Create the nem xml document. Create the nem xml document.
@ -441,13 +364,7 @@ def create_nem_xml(
etree.SubElement(nem_element, "transport", definition=transport_definition) etree.SubElement(nem_element, "transport", definition=transport_definition)
etree.SubElement(nem_element, "mac", definition=mac_definition) etree.SubElement(nem_element, "mac", definition=mac_definition)
etree.SubElement(nem_element, "phy", definition=phy_definition) etree.SubElement(nem_element, "phy", definition=phy_definition)
if server is not None: create_file(nem_element, "nem", nem_file, server)
create_file(nem_element, "nem", nem_file, server)
else:
create_file(nem_element, "nem", nem_file)
emane_model.session.distributed.execute(
lambda x: create_file(nem_element, "nem", nem_file, x)
)
def create_event_service_xml( def create_event_service_xml(
@ -483,81 +400,55 @@ def create_event_service_xml(
create_file(event_element, "emaneeventmsgsvc", file_path, server) create_file(event_element, "emaneeventmsgsvc", file_path, server)
def transport_file_name(node_id: int, transport_type: TransportType) -> str: def transport_file_name(iface: CoreInterface, transport_type: TransportType) -> str:
""" """
Create name for a transport xml file. Create name for a transport xml file.
:param node_id: node id to generate transport file name for :param iface: interface running emane
:param transport_type: transport type to generate transport file :param transport_type: transport type to generate transport file
:return: :return: transport xml file name
""" """
return f"n{node_id}trans{transport_type.value}.xml" return f"{iface.name}-trans-{transport_type.value}.xml"
def _basename(emane_model: "EmaneModel", iface: CoreInterface = None) -> str: def nem_file_name(iface: CoreInterface) -> str:
""" """
Create name that is leveraged for configuration file creation. Return the string name for the NEM XML file, e.g. "eth0-nem.xml"
:param emane_model: emane model to create name for :param iface: interface running emane
:param iface: interface for this model :return: nem xm file name
:return: basename used for file creation
""" """
name = f"n{emane_model.id}"
if iface:
node_id = iface.node.id
if emane_model.session.emane.get_iface_config(node_id, iface, emane_model.name):
name = iface.localname.replace(".", "_")
return f"{name}{emane_model.name}"
def nem_file_name(emane_model: "EmaneModel", iface: CoreInterface = None) -> str:
"""
Return the string name for the NEM XML file, e.g. "n3rfpipenem.xml"
:param emane_model: emane model to create file
:param iface: interface for this model
:return: nem xml filename
"""
basename = _basename(emane_model, iface)
append = "" append = ""
if iface and iface.transport_type == TransportType.RAW: if iface and iface.transport_type == TransportType.RAW:
append = "_raw" append = "-raw"
return f"{basename}nem{append}.xml" return f"{iface.name}-nem{append}.xml"
def shim_file_name(emane_model: "EmaneModel", iface: CoreInterface = None) -> str: def shim_file_name(iface: CoreInterface = None) -> str:
""" """
Return the string name for the SHIM XML file, e.g. "commeffectshim.xml" Return the string name for the SHIM XML file, e.g. "eth0-shim.xml"
:param emane_model: emane model to create file :param iface: interface running emane
:param iface: interface for this model :return: shim xml file name
:return: shim xml filename
""" """
name = _basename(emane_model, iface) return f"{iface.name}-shim.xml"
return f"{name}shim.xml"
def mac_file_name(emane_model: "EmaneModel", iface: CoreInterface = None) -> str: def mac_file_name(iface: CoreInterface) -> str:
""" """
Return the string name for the MAC XML file, e.g. "n3rfpipemac.xml" Return the string name for the MAC XML file, e.g. "eth0-mac.xml"
:param emane_model: emane model to create file :param iface: interface running emane
:param iface: interface for this model :return: mac xml file name
:return: mac xml filename
""" """
name = _basename(emane_model, iface) return f"{iface.name}-mac.xml"
return f"{name}mac.xml"
def phy_file_name(emane_model: "EmaneModel", iface: CoreInterface = None) -> str: def phy_file_name(iface: CoreInterface) -> str:
""" """
Return the string name for the PHY XML file, e.g. "n3rfpipephy.xml" Return the string name for the PHY XML file, e.g. "eth0-phy.xml"
:param emane_model: emane model to create file :param iface: interface running emane
:param iface: interface for this model :return: phy xml file name
:return: phy xml filename
""" """
name = _basename(emane_model, iface) return f"{iface.name}-phy.xml"
return f"{name}phy.xml"