daemon: initial changes to rework logic to start emane for a given interface
This commit is contained in:
parent
da9c0d0660
commit
bd48e14348
6 changed files with 262 additions and 496 deletions
|
@ -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(
|
||||||
|
|
|
@ -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:
|
|
||||||
return r
|
|
||||||
|
|
||||||
nems = []
|
|
||||||
with self._emane_node_lock:
|
|
||||||
self.buildxml()
|
|
||||||
self.starteventmonitor()
|
self.starteventmonitor()
|
||||||
|
self.buildeventservicexml()
|
||||||
if self.numnems() > 0:
|
with self._emane_node_lock:
|
||||||
self.startdaemons()
|
# on master, control network bridge added earlier in startup()
|
||||||
self.install_ifaces()
|
control_net = self.session.add_remove_control_net(
|
||||||
|
0, remove=False, conf_required=False
|
||||||
for node_id in self._emane_nets:
|
|
||||||
emane_node = self._emane_nets[node_id]
|
|
||||||
for iface in emane_node.get_ifaces():
|
|
||||||
nems.append(
|
|
||||||
(iface.node.name, iface.name, emane_node.getnemid(iface))
|
|
||||||
)
|
)
|
||||||
|
logging.info("emane building xmls...")
|
||||||
if nems:
|
for node_id in sorted(self._emane_nets):
|
||||||
emane_nems_filename = os.path.join(self.session.session_dir, "emane_nems")
|
emane_net = self._emane_nets[node_id]
|
||||||
try:
|
if not emane_net.model:
|
||||||
with open(emane_nems_filename, "w") as f:
|
logging.error("emane net(%s) has no model", emane_net.name)
|
||||||
for nodename, ifname, nemid in nems:
|
continue
|
||||||
f.write(f"{nodename} {ifname} {nemid}\n")
|
for iface in emane_net.get_ifaces():
|
||||||
except IOError:
|
if not iface.node:
|
||||||
logging.exception("Error writing EMANE NEMs file: %s")
|
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)
|
||||||
|
self.start_daemon(iface)
|
||||||
|
self.install_iface(emane_net, iface)
|
||||||
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,65 +539,47 @@ 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:
|
|
||||||
return
|
|
||||||
|
|
||||||
path = self.session.session_dir
|
path = self.session.session_dir
|
||||||
log_file = os.path.join(path, "emane.log")
|
log_file = os.path.join(path, f"{iface.name}-emane.log")
|
||||||
platform_xml = os.path.join(path, "platform.xml")
|
platform_xml = os.path.join(path, f"{iface.name}-platform.xml")
|
||||||
emanecmd += f" -f {log_file} {platform_xml}"
|
emanecmd += f" -f {log_file} {platform_xml}"
|
||||||
utils.cmd(emanecmd, cwd=path)
|
utils.cmd(emanecmd, cwd=path)
|
||||||
self.session.distributed.execute(lambda x: x.remote_cmd(emanecmd, cwd=path))
|
self.session.distributed.execute(lambda x: x.remote_cmd(emanecmd, cwd=path))
|
||||||
|
@ -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 "
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,37 +138,20 @@ 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(
|
|
||||||
"building emane platform xml for node(%s) nem_id(%s): %s",
|
|
||||||
node,
|
|
||||||
nem_id,
|
|
||||||
node.name,
|
|
||||||
)
|
|
||||||
nem_entries = {}
|
|
||||||
|
|
||||||
if node.model is None:
|
|
||||||
logging.warning("warning: EMANE network %s has no associated model", node.name)
|
|
||||||
return nem_id
|
|
||||||
|
|
||||||
for iface in node.get_ifaces():
|
|
||||||
logging.debug(
|
|
||||||
"building platform xml for interface(%s) nem_id(%s)", iface.name, nem_id
|
|
||||||
)
|
|
||||||
# build nem xml
|
# build nem xml
|
||||||
nem_definition = nem_file_name(node.model, iface)
|
nem_definition = nem_file_name(iface)
|
||||||
nem_element = etree.Element(
|
nem_element = etree.Element(
|
||||||
"nem", id=str(nem_id), name=iface.localname, definition=nem_definition
|
"nem", id=str(nem_id), name=iface.localname, definition=nem_definition
|
||||||
)
|
)
|
||||||
|
|
||||||
# check if this is an external transport, get default config if an interface
|
# check if this is an external transport, get default config if an interface
|
||||||
# specific one does not exist
|
# specific one does not exist
|
||||||
config = emane_manager.get_iface_config(node.model.id, iface, node.model.name)
|
config = emane_manager.get_iface_config(emane_net, iface)
|
||||||
|
|
||||||
if is_external(config):
|
if is_external(config):
|
||||||
nem_element.set("transport", "external")
|
nem_element.set("transport", "external")
|
||||||
platform_endpoint = "platformendpoint"
|
platform_endpoint = "platformendpoint"
|
||||||
|
@ -184,18 +164,13 @@ def build_node_platform_xml(
|
||||||
if not transport_type:
|
if not transport_type:
|
||||||
logging.info("warning: %s interface type unsupported!", iface.name)
|
logging.info("warning: %s interface type unsupported!", iface.name)
|
||||||
transport_type = TransportType.RAW
|
transport_type = TransportType.RAW
|
||||||
transport_file = transport_file_name(node.id, transport_type)
|
transport_file = transport_file_name(iface, transport_type)
|
||||||
transport_element = etree.SubElement(
|
transport_element = etree.SubElement(
|
||||||
nem_element, "transport", definition=transport_file
|
nem_element, "transport", definition=transport_file
|
||||||
)
|
)
|
||||||
|
|
||||||
# add transport parameter
|
|
||||||
add_param(transport_element, "device", iface.name)
|
add_param(transport_element, "device", iface.name)
|
||||||
|
|
||||||
# add nem entry
|
# determine platform element to add xml to
|
||||||
nem_entries[iface] = nem_element
|
|
||||||
|
|
||||||
# merging code
|
|
||||||
key = iface.node.id
|
key = iface.node.id
|
||||||
if iface.transport_type == TransportType.RAW:
|
if iface.transport_type == TransportType.RAW:
|
||||||
key = "host"
|
key = "host"
|
||||||
|
@ -204,112 +179,80 @@ def build_node_platform_xml(
|
||||||
else:
|
else:
|
||||||
otadev = None
|
otadev = None
|
||||||
eventdev = None
|
eventdev = None
|
||||||
|
|
||||||
platform_element = platform_xmls.get(key)
|
|
||||||
if platform_element is None:
|
|
||||||
platform_element = etree.Element("platform")
|
platform_element = etree.Element("platform")
|
||||||
|
|
||||||
if otadev:
|
if otadev:
|
||||||
emane_manager.set_config("otamanagerdevice", otadev)
|
emane_manager.set_config("otamanagerdevice", otadev)
|
||||||
|
|
||||||
if eventdev:
|
if eventdev:
|
||||||
emane_manager.set_config("eventservicedevice", 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:
|
for configuration in emane_manager.emane_config.emulator_config:
|
||||||
name = configuration.id
|
name = configuration.id
|
||||||
if name == "platform_id_start":
|
|
||||||
continue
|
|
||||||
|
|
||||||
value = emane_manager.get_config(name)
|
value = emane_manager.get_config(name)
|
||||||
add_param(platform_element, name, value)
|
add_param(platform_element, name, value)
|
||||||
|
|
||||||
# add platform xml
|
|
||||||
platform_xmls[key] = platform_element
|
|
||||||
|
|
||||||
platform_element.append(nem_element)
|
platform_element.append(nem_element)
|
||||||
|
emane_net.setnemid(iface, nem_id)
|
||||||
node.setnemid(iface, nem_id)
|
|
||||||
mac = _MAC_PREFIX + ":00:00:"
|
mac = _MAC_PREFIX + ":00:00:"
|
||||||
mac += f"{(nem_id >> 8) & 0xFF:02X}:{nem_id & 0xFF:02X}"
|
mac += f"{(nem_id >> 8) & 0xFF:02X}:{nem_id & 0xFF:02X}"
|
||||||
iface.set_mac(mac)
|
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)
|
||||||
create_file(platform_element, doc_name, file_path)
|
|
||||||
else:
|
else:
|
||||||
file_name = f"platform{key}.xml"
|
node = iface.node
|
||||||
file_path = os.path.join(emane_manager.session.session_dir, file_name)
|
file_name = f"{iface.name}-platform.xml"
|
||||||
linked_node = emane_manager.session.nodes[key]
|
file_path = os.path.join(node.nodedir, file_name)
|
||||||
create_file(platform_element, doc_name, file_path, linked_node.server)
|
server = node.server
|
||||||
|
create_file(platform_element, doc_name, file_path, 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
|
||||||
|
|
||||||
for iface in node.get_ifaces():
|
|
||||||
# check for interface specific emane configuration and write xml files
|
|
||||||
config = emane_manager.get_iface_config(node.model.id, iface, node.model.name)
|
|
||||||
if config:
|
|
||||||
node.model.build_xml_files(config, iface)
|
|
||||||
|
|
||||||
# check transport type needed for interface
|
|
||||||
if iface.transport_type == TransportType.VIRTUAL:
|
if iface.transport_type == TransportType.VIRTUAL:
|
||||||
need_virtual = True
|
need_virtual = True
|
||||||
vtype = iface.transport_type
|
vtype = iface.transport_type
|
||||||
else:
|
else:
|
||||||
need_raw = True
|
need_raw = True
|
||||||
rtype = iface.transport_type
|
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)
|
|
||||||
if server is not None:
|
|
||||||
create_file(phy_element, "phy", file_path, server)
|
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)
|
|
||||||
if server is not None:
|
|
||||||
create_file(mac_element, "mac", file_path, server)
|
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 file name
|
||||||
"""
|
"""
|
||||||
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 file name
|
||||||
"""
|
"""
|
||||||
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 file name
|
||||||
"""
|
"""
|
||||||
name = _basename(emane_model, iface)
|
return f"{iface.name}-phy.xml"
|
||||||
return f"{name}phy.xml"
|
|
||||||
|
|
Loading…
Reference in a new issue