Merge pull request #484 from coreemu/enhancement/runtime-emane-link
Enhancement/runtime emane link
This commit is contained in:
commit
3590f2c370
14 changed files with 407 additions and 756 deletions
|
@ -491,10 +491,13 @@ def iface_to_proto(node_id: int, iface: CoreInterface) -> core_pb2.Interface:
|
|||
)
|
||||
|
||||
|
||||
def get_nem_id(node: CoreNode, iface_id: int, context: ServicerContext) -> int:
|
||||
def get_nem_id(
|
||||
session: Session, node: CoreNode, iface_id: int, context: ServicerContext
|
||||
) -> int:
|
||||
"""
|
||||
Get nem id for a given node and interface id.
|
||||
|
||||
:param session: session node belongs to
|
||||
:param node: node to get nem id for
|
||||
:param iface_id: id of interface on node to get nem id for
|
||||
:param context: request context
|
||||
|
@ -508,7 +511,7 @@ def get_nem_id(node: CoreNode, iface_id: int, context: ServicerContext) -> int:
|
|||
if not isinstance(net, EmaneNet):
|
||||
message = f"{node.name} interface {iface_id} is not an EMANE network"
|
||||
context.abort(grpc.StatusCode.INVALID_ARGUMENT, message)
|
||||
nem_id = net.getnemid(iface)
|
||||
nem_id = session.emane.get_nem_id(iface)
|
||||
if nem_id is None:
|
||||
message = f"{node.name} interface {iface_id} nem id does not exist"
|
||||
context.abort(grpc.StatusCode.INVALID_ARGUMENT, message)
|
||||
|
|
|
@ -1551,29 +1551,29 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
logging.debug("emane link: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
nem1 = request.nem1
|
||||
emane1, iface = session.emane.nemlookup(nem1)
|
||||
if not emane1 or not iface:
|
||||
iface1 = session.emane.get_iface(nem1)
|
||||
if not iface1:
|
||||
context.abort(grpc.StatusCode.NOT_FOUND, f"nem one {nem1} not found")
|
||||
node1 = iface.node
|
||||
node1 = iface1.node
|
||||
|
||||
nem2 = request.nem2
|
||||
emane2, iface = session.emane.nemlookup(nem2)
|
||||
if not emane2 or not iface:
|
||||
iface2 = session.emane.get_iface(nem2)
|
||||
if not iface2:
|
||||
context.abort(grpc.StatusCode.NOT_FOUND, f"nem two {nem2} not found")
|
||||
node2 = iface.node
|
||||
node2 = iface2.node
|
||||
|
||||
if emane1.id == emane2.id:
|
||||
if iface1.net == iface2.net:
|
||||
if request.linked:
|
||||
flag = MessageFlags.ADD
|
||||
else:
|
||||
flag = MessageFlags.DELETE
|
||||
color = session.get_link_color(emane1.id)
|
||||
color = session.get_link_color(iface1.net.id)
|
||||
link = LinkData(
|
||||
message_type=flag,
|
||||
type=LinkTypes.WIRELESS,
|
||||
node1_id=node1.id,
|
||||
node2_id=node2.id,
|
||||
network_id=emane1.id,
|
||||
network_id=iface1.net.id,
|
||||
color=color,
|
||||
)
|
||||
session.broadcast_link(link)
|
||||
|
@ -1796,8 +1796,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
for request in request_iterator:
|
||||
session = self.get_session(request.session_id, context)
|
||||
node1 = self.get_node(session, request.node1_id, context, CoreNode)
|
||||
nem1 = grpcutils.get_nem_id(node1, request.iface1_id, context)
|
||||
nem1 = grpcutils.get_nem_id(session, node1, request.iface1_id, context)
|
||||
node2 = self.get_node(session, request.node2_id, context, CoreNode)
|
||||
nem2 = grpcutils.get_nem_id(node2, request.iface2_id, context)
|
||||
nem2 = grpcutils.get_nem_id(session, node2, request.iface2_id, context)
|
||||
session.emane.publish_pathloss(nem1, nem2, request.rx1, request.rx2)
|
||||
return EmanePathlossesResponse()
|
||||
|
|
|
@ -10,9 +10,7 @@ from lxml import etree
|
|||
|
||||
from core.config import ConfigGroup, Configuration
|
||||
from core.emane import emanemanifest, emanemodel
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.emulator.data import LinkOptions
|
||||
from core.emulator.enumerations import TransportType
|
||||
from core.nodes.interface import CoreInterface
|
||||
from core.xml import emanexml
|
||||
|
||||
|
@ -62,9 +60,7 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
|||
def config_groups(cls) -> List[ConfigGroup]:
|
||||
return [ConfigGroup("CommEffect SHIM Parameters", 1, len(cls.configurations()))]
|
||||
|
||||
def build_xml_files(
|
||||
self, config: Dict[str, str], iface: CoreInterface = None
|
||||
) -> None:
|
||||
def build_xml_files(self, config: Dict[str, str], iface: CoreInterface) -> None:
|
||||
"""
|
||||
Build the necessary nem and commeffect XMLs in the given path.
|
||||
If an individual NEM has a nonstandard config, we need to build
|
||||
|
@ -75,23 +71,16 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
|||
:param iface: interface for the emane node
|
||||
:return: nothing
|
||||
"""
|
||||
# retrieve xml names
|
||||
nem_name = emanexml.nem_file_name(self, iface)
|
||||
shim_name = emanexml.shim_file_name(self, iface)
|
||||
|
||||
# create and write nem document
|
||||
nem_element = etree.Element("nem", name=f"{self.name} NEM", type="unstructured")
|
||||
transport_type = TransportType.VIRTUAL
|
||||
if iface and iface.transport_type == TransportType.RAW:
|
||||
transport_type = TransportType.RAW
|
||||
transport_file = emanexml.transport_file_name(self.id, transport_type)
|
||||
etree.SubElement(nem_element, "transport", definition=transport_file)
|
||||
transport_name = emanexml.transport_file_name(iface)
|
||||
etree.SubElement(nem_element, "transport", definition=transport_name)
|
||||
|
||||
# set shim configuration
|
||||
nem_name = emanexml.nem_file_name(iface)
|
||||
shim_name = emanexml.shim_file_name(iface)
|
||||
etree.SubElement(nem_element, "shim", definition=shim_name)
|
||||
|
||||
nem_file = os.path.join(self.session.session_dir, nem_name)
|
||||
emanexml.create_file(nem_element, "nem", nem_file)
|
||||
emanexml.create_iface_file(iface, nem_element, "nem", nem_name)
|
||||
|
||||
# create and write shim document
|
||||
shim_element = etree.Element(
|
||||
|
@ -110,9 +99,10 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
|||
ff = config["filterfile"]
|
||||
if ff.strip() != "":
|
||||
emanexml.add_param(shim_element, "filterfile", ff)
|
||||
emanexml.create_iface_file(iface, shim_element, "shim", shim_name)
|
||||
|
||||
shim_file = os.path.join(self.session.session_dir, shim_name)
|
||||
emanexml.create_file(shim_element, "shim", shim_file)
|
||||
# create transport xml
|
||||
emanexml.create_transport_xml(iface, config)
|
||||
|
||||
def linkconfig(
|
||||
self, iface: CoreInterface, options: LinkOptions, iface2: CoreInterface = None
|
||||
|
@ -133,12 +123,11 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
|||
# TODO: batch these into multiple events per transmission
|
||||
# TODO: may want to split out seconds portion of delay and jitter
|
||||
event = CommEffectEvent()
|
||||
emane_node = self.session.get_node(self.id, EmaneNet)
|
||||
nemid = emane_node.getnemid(iface)
|
||||
nemid2 = emane_node.getnemid(iface2)
|
||||
nem1 = self.session.emane.get_nem_id(iface)
|
||||
nem2 = self.session.emane.get_nem_id(iface2)
|
||||
logging.info("sending comm effect event")
|
||||
event.append(
|
||||
nemid,
|
||||
nem1,
|
||||
latency=convert_none(options.delay),
|
||||
jitter=convert_none(options.jitter),
|
||||
loss=convert_none(options.loss),
|
||||
|
@ -146,4 +135,4 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
|
|||
unicast=int(convert_none(options.bandwidth)),
|
||||
broadcast=int(convert_none(options.bandwidth)),
|
||||
)
|
||||
service.publish(nemid2, event)
|
||||
service.publish(nem2, event)
|
||||
|
|
|
@ -6,6 +6,7 @@ import logging
|
|||
import os
|
||||
import threading
|
||||
from collections import OrderedDict
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type
|
||||
|
||||
from core import utils
|
||||
|
@ -28,9 +29,7 @@ from core.emulator.enumerations import (
|
|||
)
|
||||
from core.errors import CoreCommandError, CoreError
|
||||
from core.nodes.base import CoreNode, NodeBase
|
||||
from core.nodes.interface import CoreInterface
|
||||
from core.nodes.network import CtrlNet
|
||||
from core.nodes.physical import Rj45Node
|
||||
from core.nodes.interface import CoreInterface, TunTap
|
||||
from core.xml import emanexml
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -63,6 +62,12 @@ DEFAULT_EMANE_PREFIX = "/usr"
|
|||
DEFAULT_DEV = "ctrl0"
|
||||
|
||||
|
||||
class EmaneState(Enum):
|
||||
SUCCESS = 0
|
||||
NOT_NEEDED = 1
|
||||
NOT_READY = 2
|
||||
|
||||
|
||||
class EmaneManager(ModelManager):
|
||||
"""
|
||||
EMANE controller object. Lives in a Session instance and is used for
|
||||
|
@ -72,8 +77,6 @@ class EmaneManager(ModelManager):
|
|||
|
||||
name: str = "emane"
|
||||
config_type: RegisterTlvs = RegisterTlvs.EMULATION_SERVER
|
||||
SUCCESS: int = 0
|
||||
NOT_NEEDED: int = 1
|
||||
NOT_READY: int = 2
|
||||
EVENTCFGVAR: str = "LIBEMANEEVENTSERVICECONFIG"
|
||||
DEFAULT_LOG_LEVEL: int = 3
|
||||
|
@ -87,6 +90,8 @@ class EmaneManager(ModelManager):
|
|||
"""
|
||||
super().__init__()
|
||||
self.session: "Session" = session
|
||||
self.nems_to_ifaces: Dict[int, CoreInterface] = {}
|
||||
self.ifaces_to_nems: Dict[CoreInterface, int] = {}
|
||||
self._emane_nets: Dict[int, EmaneNet] = {}
|
||||
self._emane_node_lock: threading.Lock = threading.Lock()
|
||||
# port numbers are allocated from these counters
|
||||
|
@ -111,47 +116,44 @@ class EmaneManager(ModelManager):
|
|||
self.event_device: Optional[str] = None
|
||||
self.emane_check()
|
||||
|
||||
def next_nem_id(self) -> int:
|
||||
nem_id = int(self.get_config("nem_id_start"))
|
||||
while nem_id in self.nems_to_ifaces:
|
||||
nem_id += 1
|
||||
return nem_id
|
||||
|
||||
def get_iface_config(
|
||||
self, node_id: int, iface: CoreInterface, model_name: str
|
||||
self, emane_net: EmaneNet, iface: CoreInterface
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Retrieve interface configuration or node configuration if not provided.
|
||||
Retrieve configuration for a given interface.
|
||||
|
||||
:param node_id: node id
|
||||
:param iface: node interface
|
||||
:param model_name: model to get configuration for
|
||||
:return: node/interface model configuration
|
||||
:param emane_net: emane network the interface is connected to
|
||||
:param iface: interface running emane
|
||||
:return: net, node, or interface model configuration
|
||||
"""
|
||||
# use the network-wide config values or interface(NEM)-specific values?
|
||||
if iface is None:
|
||||
return self.get_configs(node_id=node_id, config_type=model_name)
|
||||
else:
|
||||
# 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
|
||||
# of each model per node;
|
||||
# TODO: use both node and interface as key
|
||||
|
||||
# Adamson change: first check for iface config keyed by "node:iface.name"
|
||||
# (so that nodes w/ multiple interfaces of same conftype can have
|
||||
# different configs for each separate interface)
|
||||
key = 1000 * iface.node.id
|
||||
if iface.node_id is not None:
|
||||
key += iface.node_id
|
||||
|
||||
# try retrieve interface specific configuration, avoid getting defaults
|
||||
config = self.get_configs(node_id=key, config_type=model_name)
|
||||
|
||||
# otherwise retrieve the interfaces node configuration, avoid using defaults
|
||||
if not config:
|
||||
config = self.get_configs(node_id=iface.node.id, config_type=model_name)
|
||||
|
||||
# get non interface config, when none found
|
||||
if not config:
|
||||
# with EMANE 0.9.2+, we need an extra NEM XML from
|
||||
# model.buildnemxmlfiles(), so defaults are returned here
|
||||
config = self.get_configs(node_id=node_id, config_type=model_name)
|
||||
|
||||
return config
|
||||
model_name = emane_net.model.name
|
||||
# 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
|
||||
# of each model per node;
|
||||
# TODO: use both node and interface as key
|
||||
# Adamson change: first check for iface config keyed by "node:iface.name"
|
||||
# (so that nodes w/ multiple interfaces of same conftype can have
|
||||
# different configs for each separate interface)
|
||||
key = 1000 * iface.node.id
|
||||
if iface.node_id is not None:
|
||||
key += iface.node_id
|
||||
# try retrieve interface specific configuration, avoid getting defaults
|
||||
config = self.get_configs(node_id=key, config_type=model_name)
|
||||
# otherwise retrieve the interfaces node configuration, avoid using defaults
|
||||
if not config:
|
||||
config = self.get_configs(node_id=iface.node.id, config_type=model_name)
|
||||
# get non interface config, when none found
|
||||
if not config:
|
||||
# with EMANE 0.9.2+, we need an extra NEM XML from
|
||||
# model.buildnemxmlfiles(), so defaults are returned here
|
||||
config = self.get_configs(node_id=emane_net.id, config_type=model_name)
|
||||
return config
|
||||
|
||||
def config_reset(self, node_id: int = None) -> None:
|
||||
super().config_reset(node_id)
|
||||
|
@ -250,8 +252,8 @@ class EmaneManager(ModelManager):
|
|||
"""
|
||||
with self._emane_node_lock:
|
||||
if emane_net.id in self._emane_nets:
|
||||
raise KeyError(
|
||||
f"non-unique EMANE object id {emane_net.id} for {emane_net}"
|
||||
raise CoreError(
|
||||
f"duplicate emane network({emane_net.id}): {emane_net.name}"
|
||||
)
|
||||
self._emane_nets[emane_net.id] = emane_net
|
||||
|
||||
|
@ -260,14 +262,13 @@ class EmaneManager(ModelManager):
|
|||
Return a set of CoreNodes that are linked to an EMANE network,
|
||||
e.g. containers having one or more radio interfaces.
|
||||
"""
|
||||
# assumes self._objslock already held
|
||||
nodes = set()
|
||||
for emane_net in self._emane_nets.values():
|
||||
for iface in emane_net.get_ifaces():
|
||||
nodes.add(iface.node)
|
||||
return nodes
|
||||
|
||||
def setup(self) -> int:
|
||||
def setup(self) -> EmaneState:
|
||||
"""
|
||||
Setup duties for EMANE manager.
|
||||
|
||||
|
@ -275,8 +276,6 @@ class EmaneManager(ModelManager):
|
|||
instantiation
|
||||
"""
|
||||
logging.debug("emane setup")
|
||||
|
||||
# TODO: drive this from the session object
|
||||
with self.session.nodes_lock:
|
||||
for node_id in self.session.nodes:
|
||||
node = self.session.nodes[node_id]
|
||||
|
@ -285,10 +284,9 @@ class EmaneManager(ModelManager):
|
|||
"adding emane node: id(%s) name(%s)", node.id, node.name
|
||||
)
|
||||
self.add_node(node)
|
||||
|
||||
if not self._emane_nets:
|
||||
logging.debug("no emane nodes in session")
|
||||
return EmaneManager.NOT_NEEDED
|
||||
return EmaneState.NOT_NEEDED
|
||||
|
||||
# check if bindings were installed
|
||||
if EventService is None:
|
||||
|
@ -304,7 +302,7 @@ class EmaneManager(ModelManager):
|
|||
"EMANE cannot start, check core config. invalid OTA device provided: %s",
|
||||
otadev,
|
||||
)
|
||||
return EmaneManager.NOT_READY
|
||||
return EmaneState.NOT_READY
|
||||
|
||||
self.session.add_remove_control_net(
|
||||
net_index=netidx, remove=False, conf_required=False
|
||||
|
@ -316,19 +314,18 @@ class EmaneManager(ModelManager):
|
|||
logging.debug("emane event service device index: %s", netidx)
|
||||
if netidx < 0:
|
||||
logging.error(
|
||||
"EMANE cannot start, check core config. invalid event service device: %s",
|
||||
"emane cannot start due to invalid event service device: %s",
|
||||
eventdev,
|
||||
)
|
||||
return EmaneManager.NOT_READY
|
||||
return EmaneState.NOT_READY
|
||||
|
||||
self.session.add_remove_control_net(
|
||||
net_index=netidx, remove=False, conf_required=False
|
||||
)
|
||||
|
||||
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
|
||||
and start the daemons.
|
||||
|
@ -337,39 +334,63 @@ class EmaneManager(ModelManager):
|
|||
instantiation
|
||||
"""
|
||||
self.reset()
|
||||
r = self.setup()
|
||||
|
||||
# NOT_NEEDED or NOT_READY
|
||||
if r != EmaneManager.SUCCESS:
|
||||
return r
|
||||
|
||||
nems = []
|
||||
status = self.setup()
|
||||
if status != EmaneState.SUCCESS:
|
||||
return status
|
||||
self.starteventmonitor()
|
||||
self.buildeventservicexml()
|
||||
with self._emane_node_lock:
|
||||
self.buildxml()
|
||||
self.starteventmonitor()
|
||||
|
||||
if self.numnems() > 0:
|
||||
self.startdaemons()
|
||||
self.install_ifaces()
|
||||
|
||||
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))
|
||||
)
|
||||
|
||||
if nems:
|
||||
emane_nems_filename = os.path.join(self.session.session_dir, "emane_nems")
|
||||
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")
|
||||
logging.info("emane building xmls...")
|
||||
for node_id in sorted(self._emane_nets):
|
||||
emane_net = self._emane_nets[node_id]
|
||||
if not emane_net.model:
|
||||
logging.error("emane net(%s) has no model", emane_net.name)
|
||||
continue
|
||||
for iface in emane_net.get_ifaces():
|
||||
self.start_iface(emane_net, iface)
|
||||
if self.links_enabled():
|
||||
self.link_monitor.start()
|
||||
return EmaneManager.SUCCESS
|
||||
return EmaneState.SUCCESS
|
||||
|
||||
def start_iface(self, emane_net: EmaneNet, iface: CoreInterface) -> None:
|
||||
if not iface.node:
|
||||
logging.error(
|
||||
"emane net(%s) connected interface(%s) missing node",
|
||||
emane_net.name,
|
||||
iface.name,
|
||||
)
|
||||
return
|
||||
control_net = self.session.add_remove_control_net(
|
||||
0, remove=False, conf_required=False
|
||||
)
|
||||
nem_id = self.next_nem_id()
|
||||
self.set_nem(nem_id, iface)
|
||||
self.write_nem(iface, nem_id)
|
||||
emanexml.build_platform_xml(self, control_net, emane_net, iface, nem_id)
|
||||
config = self.get_iface_config(emane_net, iface)
|
||||
emane_net.model.build_xml_files(config, iface)
|
||||
self.start_daemon(iface)
|
||||
self.install_iface(emane_net, iface)
|
||||
|
||||
def set_nem(self, nem_id: int, iface: CoreInterface) -> None:
|
||||
if nem_id in self.nems_to_ifaces:
|
||||
raise CoreError(f"adding duplicate nem: {nem_id}")
|
||||
self.nems_to_ifaces[nem_id] = iface
|
||||
self.ifaces_to_nems[iface] = nem_id
|
||||
|
||||
def get_iface(self, nem_id: int) -> Optional[CoreInterface]:
|
||||
return self.nems_to_ifaces.get(nem_id)
|
||||
|
||||
def get_nem_id(self, iface: CoreInterface) -> Optional[int]:
|
||||
return self.ifaces_to_nems.get(iface)
|
||||
|
||||
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:
|
||||
return self.get_config("link_enabled") == "1"
|
||||
|
@ -380,17 +401,14 @@ class EmaneManager(ModelManager):
|
|||
"""
|
||||
if not self.genlocationevents():
|
||||
return
|
||||
|
||||
with self._emane_node_lock:
|
||||
for key in sorted(self._emane_nets.keys()):
|
||||
emane_node = self._emane_nets[key]
|
||||
for node_id in sorted(self._emane_nets):
|
||||
emane_net = self._emane_nets[node_id]
|
||||
logging.debug(
|
||||
"post startup for emane node: %s - %s",
|
||||
emane_node.id,
|
||||
emane_node.name,
|
||||
"post startup for emane node: %s - %s", emane_net.id, emane_net.name
|
||||
)
|
||||
emane_node.model.post_startup()
|
||||
for iface in emane_node.get_ifaces():
|
||||
emane_net.model.post_startup()
|
||||
for iface in emane_net.get_ifaces():
|
||||
iface.setposition()
|
||||
|
||||
def reset(self) -> None:
|
||||
|
@ -400,13 +418,8 @@ class EmaneManager(ModelManager):
|
|||
"""
|
||||
with self._emane_node_lock:
|
||||
self._emane_nets.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
|
||||
)
|
||||
self.nems_to_ifaces.clear()
|
||||
self.ifaces_to_nems.clear()
|
||||
|
||||
def shutdown(self) -> None:
|
||||
"""
|
||||
|
@ -422,40 +435,23 @@ class EmaneManager(ModelManager):
|
|||
self.stopdaemons()
|
||||
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:
|
||||
"""
|
||||
Associate EMANE model classes with EMANE network nodes.
|
||||
"""
|
||||
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)
|
||||
|
||||
# skip nodes that already have a model set
|
||||
if emane_node.model:
|
||||
if emane_net.model:
|
||||
logging.debug(
|
||||
"node(%s) already has model(%s)",
|
||||
emane_node.id,
|
||||
emane_node.model.name,
|
||||
"node(%s) already has model(%s)", emane_net.id, emane_net.model.name
|
||||
)
|
||||
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)
|
||||
if not model_name:
|
||||
logging.error("emane node(%s) has no node model", node_id)
|
||||
|
@ -464,81 +460,34 @@ class EmaneManager(ModelManager):
|
|||
config = self.get_model_config(node_id=node_id, model_name=model_name)
|
||||
logging.debug("setting emane model(%s) config(%s)", model_name, config)
|
||||
model_class = self.models[model_name]
|
||||
emane_node.setmodel(model_class, config)
|
||||
|
||||
def nemlookup(self, nemid) -> Tuple[Optional[EmaneNet], Optional[CoreInterface]]:
|
||||
"""
|
||||
Look for the given numerical NEM ID and return the first matching
|
||||
EMANE network and NEM interface.
|
||||
"""
|
||||
emane_node = None
|
||||
iface = None
|
||||
|
||||
for node_id in self._emane_nets:
|
||||
emane_node = self._emane_nets[node_id]
|
||||
iface = emane_node.get_nem_iface(nemid)
|
||||
if iface is not None:
|
||||
break
|
||||
else:
|
||||
emane_node = None
|
||||
|
||||
return emane_node, iface
|
||||
emane_net.setmodel(model_class, config)
|
||||
|
||||
def get_nem_link(
|
||||
self, nem1: int, nem2: int, flags: MessageFlags = MessageFlags.NONE
|
||||
) -> Optional[LinkData]:
|
||||
emane1, iface = self.nemlookup(nem1)
|
||||
if not emane1 or not iface:
|
||||
iface1 = self.get_iface(nem1)
|
||||
if not iface1:
|
||||
logging.error("invalid nem: %s", nem1)
|
||||
return None
|
||||
node1 = iface.node
|
||||
emane2, iface = self.nemlookup(nem2)
|
||||
if not emane2 or not iface:
|
||||
node1 = iface1.node
|
||||
iface2 = self.get_iface(nem2)
|
||||
if not iface2:
|
||||
logging.error("invalid nem: %s", nem2)
|
||||
return None
|
||||
node2 = iface.node
|
||||
color = self.session.get_link_color(emane1.id)
|
||||
node2 = iface2.node
|
||||
if iface1.net != iface2.net:
|
||||
return None
|
||||
emane_net = iface1.net
|
||||
color = self.session.get_link_color(emane_net.id)
|
||||
return LinkData(
|
||||
message_type=flags,
|
||||
type=LinkTypes.WIRELESS,
|
||||
node1_id=node1.id,
|
||||
node2_id=node2.id,
|
||||
network_id=emane1.id,
|
||||
network_id=emane_net.id,
|
||||
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:
|
||||
"""
|
||||
Build the libemaneeventservice.xml file if event service options
|
||||
|
@ -571,7 +520,7 @@ class EmaneManager(ModelManager):
|
|||
)
|
||||
)
|
||||
|
||||
def startdaemons(self) -> None:
|
||||
def start_daemon(self, iface: CoreInterface) -> None:
|
||||
"""
|
||||
Start one EMANE daemon per node having a radio.
|
||||
Add a control network even if the user has not configured one.
|
||||
|
@ -581,116 +530,91 @@ class EmaneManager(ModelManager):
|
|||
cfgloglevel = self.session.options.get_config_int("emane_log_level")
|
||||
realtime = self.session.options.get_config_bool("emane_realtime", default=True)
|
||||
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)
|
||||
|
||||
emanecmd = f"emane -d -l {loglevel}"
|
||||
if realtime:
|
||||
emanecmd += " -r"
|
||||
|
||||
otagroup, _otaport = self.get_config("otamanagergroup").split(":")
|
||||
otadev = self.get_config("otamanagerdevice")
|
||||
otanetidx = self.session.get_control_net_index(otadev)
|
||||
|
||||
eventgroup, _eventport = self.get_config("eventservicegroup").split(":")
|
||||
eventdev = self.get_config("eventservicedevice")
|
||||
eventservicenetidx = self.session.get_control_net_index(eventdev)
|
||||
|
||||
run_emane_on_host = False
|
||||
for node in self.getnodes():
|
||||
if isinstance(node, Rj45Node):
|
||||
run_emane_on_host = True
|
||||
continue
|
||||
path = self.session.session_dir
|
||||
n = node.id
|
||||
node = iface.node
|
||||
if iface.is_virtual():
|
||||
otagroup, _otaport = self.get_config("otamanagergroup").split(":")
|
||||
otadev = self.get_config("otamanagerdevice")
|
||||
otanetidx = self.session.get_control_net_index(otadev)
|
||||
eventgroup, _eventport = self.get_config("eventservicegroup").split(":")
|
||||
eventdev = self.get_config("eventservicedevice")
|
||||
eventservicenetidx = self.session.get_control_net_index(eventdev)
|
||||
|
||||
# control network not yet started here
|
||||
self.session.add_remove_control_iface(
|
||||
node, 0, remove=False, conf_required=False
|
||||
)
|
||||
|
||||
if otanetidx > 0:
|
||||
logging.info("adding ota device ctrl%d", otanetidx)
|
||||
self.session.add_remove_control_iface(
|
||||
node, otanetidx, remove=False, conf_required=False
|
||||
)
|
||||
|
||||
if eventservicenetidx >= 0:
|
||||
logging.info("adding event service device ctrl%d", eventservicenetidx)
|
||||
self.session.add_remove_control_iface(
|
||||
node, eventservicenetidx, remove=False, conf_required=False
|
||||
)
|
||||
|
||||
# multicast route is needed for OTA data
|
||||
logging.info("OTA GROUP(%s) OTA DEV(%s)", otagroup, otadev)
|
||||
node.node_net_client.create_route(otagroup, otadev)
|
||||
|
||||
# multicast route is also needed for event data if on control network
|
||||
if eventservicenetidx >= 0 and eventgroup != otagroup:
|
||||
node.node_net_client.create_route(eventgroup, eventdev)
|
||||
|
||||
# start emane
|
||||
log_file = os.path.join(path, f"emane{n}.log")
|
||||
platform_xml = os.path.join(path, f"platform{n}.xml")
|
||||
log_file = os.path.join(node.nodedir, f"{iface.name}-emane.log")
|
||||
platform_xml = os.path.join(node.nodedir, f"{iface.name}-platform.xml")
|
||||
args = f"{emanecmd} -f {log_file} {platform_xml}"
|
||||
output = node.cmd(args)
|
||||
node.cmd(args)
|
||||
logging.info("node(%s) emane daemon running: %s", node.name, args)
|
||||
logging.debug("node(%s) emane daemon output: %s", node.name, output)
|
||||
|
||||
if not run_emane_on_host:
|
||||
return
|
||||
|
||||
path = self.session.session_dir
|
||||
log_file = os.path.join(path, "emane.log")
|
||||
platform_xml = os.path.join(path, "platform.xml")
|
||||
emanecmd += f" -f {log_file} {platform_xml}"
|
||||
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)
|
||||
else:
|
||||
path = self.session.session_dir
|
||||
log_file = os.path.join(path, f"{iface.name}-emane.log")
|
||||
platform_xml = os.path.join(path, f"{iface.name}-platform.xml")
|
||||
emanecmd += f" -f {log_file} {platform_xml}"
|
||||
node.host_cmd(emanecmd, cwd=path)
|
||||
logging.info("node(%s) host emane daemon running: %s", node.name, emanecmd)
|
||||
|
||||
def stopdaemons(self) -> None:
|
||||
"""
|
||||
Kill the appropriate EMANE daemons.
|
||||
"""
|
||||
# TODO: we may want to improve this if we had the PIDs from the specific EMANE
|
||||
# daemons that we"ve started
|
||||
kill_emaned = "killall -q emane"
|
||||
kill_transortd = "killall -q emanetransportd"
|
||||
stop_emane_on_host = False
|
||||
for node in self.getnodes():
|
||||
if isinstance(node, Rj45Node):
|
||||
stop_emane_on_host = True
|
||||
continue
|
||||
for node_id in sorted(self._emane_nets):
|
||||
emane_net = self._emane_nets[node_id]
|
||||
for iface in emane_net.get_ifaces():
|
||||
node = iface.node
|
||||
if not node.up:
|
||||
continue
|
||||
if iface.is_raw():
|
||||
node.host_cmd(kill_emaned, wait=False)
|
||||
else:
|
||||
node.cmd(kill_emaned, wait=False)
|
||||
|
||||
if node.up:
|
||||
node.cmd(kill_emaned, wait=False)
|
||||
# TODO: RJ45 node
|
||||
|
||||
if stop_emane_on_host:
|
||||
try:
|
||||
utils.cmd(kill_emaned)
|
||||
utils.cmd(kill_transortd)
|
||||
self.session.distributed.execute(lambda x: x.remote_cmd(kill_emaned))
|
||||
self.session.distributed.execute(lambda x: x.remote_cmd(kill_transortd))
|
||||
except CoreCommandError:
|
||||
logging.exception("error shutting down emane daemons")
|
||||
|
||||
def install_ifaces(self) -> None:
|
||||
"""
|
||||
Install TUN/TAP virtual interfaces into their proper namespaces
|
||||
now that the EMANE daemons are running.
|
||||
"""
|
||||
for key in sorted(self._emane_nets.keys()):
|
||||
node = self._emane_nets[key]
|
||||
logging.info("emane install interface for node(%s): %d", node.name, key)
|
||||
node.install_ifaces()
|
||||
def install_iface(self, emane_net: EmaneNet, iface: CoreInterface) -> None:
|
||||
config = self.get_iface_config(emane_net, iface)
|
||||
external = config.get("external", "0")
|
||||
if isinstance(iface, TunTap) and external == "0":
|
||||
iface.set_ips()
|
||||
# at this point we register location handlers for generating
|
||||
# EMANE location events
|
||||
if self.genlocationevents():
|
||||
iface.poshook = emane_net.setnemposition
|
||||
iface.setposition()
|
||||
|
||||
def deinstall_ifaces(self) -> None:
|
||||
"""
|
||||
Uninstall TUN/TAP virtual interfaces.
|
||||
"""
|
||||
for key in sorted(self._emane_nets.keys()):
|
||||
emane_node = self._emane_nets[key]
|
||||
emane_node.deinstall_ifaces()
|
||||
for key in sorted(self._emane_nets):
|
||||
emane_net = self._emane_nets[key]
|
||||
for iface in emane_net.get_ifaces():
|
||||
if iface.is_virtual():
|
||||
iface.shutdown()
|
||||
iface.poshook = None
|
||||
|
||||
def doeventmonitor(self) -> bool:
|
||||
"""
|
||||
|
@ -718,7 +642,6 @@ class EmaneManager(ModelManager):
|
|||
logging.info("emane start event monitor")
|
||||
if not self.doeventmonitor():
|
||||
return
|
||||
|
||||
if self.service is None:
|
||||
logging.error(
|
||||
"Warning: EMANE events will not be generated "
|
||||
|
@ -806,7 +729,7 @@ class EmaneManager(ModelManager):
|
|||
Returns True if successfully parsed and a Node Message was sent.
|
||||
"""
|
||||
# convert nemid to node number
|
||||
_emanenode, iface = self.nemlookup(nemid)
|
||||
iface = self.get_iface(nemid)
|
||||
if iface is None:
|
||||
logging.info("location event for unknown NEM %s", nemid)
|
||||
return False
|
||||
|
|
|
@ -9,7 +9,7 @@ from core.config import ConfigGroup, Configuration
|
|||
from core.emane import emanemanifest
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.emulator.data import LinkOptions
|
||||
from core.emulator.enumerations import ConfigDataTypes, TransportType
|
||||
from core.emulator.enumerations import ConfigDataTypes
|
||||
from core.errors import CoreError
|
||||
from core.location.mobility import WirelessModel
|
||||
from core.nodes.base import CoreNode
|
||||
|
@ -96,45 +96,20 @@ class EmaneModel(WirelessModel):
|
|||
ConfigGroup("External Parameters", phy_len + 1, config_len),
|
||||
]
|
||||
|
||||
def build_xml_files(
|
||||
self, config: Dict[str, str], iface: CoreInterface = None
|
||||
) -> None:
|
||||
def build_xml_files(self, config: Dict[str, str], iface: CoreInterface) -> None:
|
||||
"""
|
||||
Builds xml files for this emane model. Creates a nem.xml file that points to
|
||||
both mac.xml and phy.xml definitions.
|
||||
|
||||
:param config: emane model configuration for the node and interface
|
||||
:param iface: interface for the emane node
|
||||
:param iface: interface to run emane for
|
||||
:return: nothing
|
||||
"""
|
||||
nem_name = emanexml.nem_file_name(self, iface)
|
||||
mac_name = emanexml.mac_file_name(self, iface)
|
||||
phy_name = emanexml.phy_file_name(self, iface)
|
||||
|
||||
# remote server for file
|
||||
server = None
|
||||
if iface is not None:
|
||||
server = iface.node.server
|
||||
|
||||
# check if this is external
|
||||
transport_type = TransportType.VIRTUAL
|
||||
if iface and iface.transport_type == TransportType.RAW:
|
||||
transport_type = TransportType.RAW
|
||||
transport_name = emanexml.transport_file_name(self.id, transport_type)
|
||||
|
||||
# create nem xml file
|
||||
nem_file = os.path.join(self.session.session_dir, nem_name)
|
||||
emanexml.create_nem_xml(
|
||||
self, config, nem_file, transport_name, mac_name, phy_name, server
|
||||
)
|
||||
|
||||
# create mac xml file
|
||||
mac_file = os.path.join(self.session.session_dir, mac_name)
|
||||
emanexml.create_mac_xml(self, config, mac_file, server)
|
||||
|
||||
# create phy xml file
|
||||
phy_file = os.path.join(self.session.session_dir, phy_name)
|
||||
emanexml.create_phy_xml(self, config, phy_file, server)
|
||||
# create nem, mac, and phy xml files
|
||||
emanexml.create_nem_xml(self, iface, config)
|
||||
emanexml.create_mac_xml(self, iface, config)
|
||||
emanexml.create_phy_xml(self, iface, config)
|
||||
emanexml.create_transport_xml(iface, config)
|
||||
|
||||
def post_startup(self) -> None:
|
||||
"""
|
||||
|
|
|
@ -6,18 +6,18 @@ share the same MAC+PHY model.
|
|||
import logging
|
||||
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Type
|
||||
|
||||
from core.emulator.data import LinkData, LinkOptions
|
||||
from core.emulator.data import InterfaceData, LinkData, LinkOptions
|
||||
from core.emulator.distributed import DistributedServer
|
||||
from core.emulator.enumerations import (
|
||||
EventTypes,
|
||||
LinkTypes,
|
||||
MessageFlags,
|
||||
NodeTypes,
|
||||
RegisterTlvs,
|
||||
TransportType,
|
||||
)
|
||||
from core.errors import CoreError
|
||||
from core.nodes.base import CoreNetworkBase
|
||||
from core.nodes.interface import CoreInterface, TunTap
|
||||
from core.nodes.base import CoreNetworkBase, CoreNode
|
||||
from core.nodes.interface import CoreInterface
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.emane.emanemodel import EmaneModel
|
||||
|
@ -47,7 +47,7 @@ class EmaneNet(CoreNetworkBase):
|
|||
apitype: NodeTypes = NodeTypes.EMANE
|
||||
linktype: LinkTypes = LinkTypes.WIRED
|
||||
type: str = "wlan"
|
||||
is_emane: bool = True
|
||||
has_custom_iface: bool = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -58,7 +58,6 @@ class EmaneNet(CoreNetworkBase):
|
|||
) -> None:
|
||||
super().__init__(session, _id, name, server)
|
||||
self.conf: str = ""
|
||||
self.nemidmap: Dict[CoreInterface, int] = {}
|
||||
self.model: "OptionalEmaneModel" = None
|
||||
self.mobility: Optional[WayPointMobility] = None
|
||||
|
||||
|
@ -102,7 +101,6 @@ class EmaneNet(CoreNetworkBase):
|
|||
"""
|
||||
set the EmaneModel associated with this node
|
||||
"""
|
||||
logging.info("adding model: %s", model.name)
|
||||
if model.config_type == RegisterTlvs.WIRELESS:
|
||||
# EmaneModel really uses values from ConfigurableManager
|
||||
# when buildnemxml() is called, not during init()
|
||||
|
@ -112,71 +110,6 @@ class EmaneNet(CoreNetworkBase):
|
|||
self.mobility = model(session=self.session, _id=self.id)
|
||||
self.mobility.update_config(config)
|
||||
|
||||
def setnemid(self, iface: CoreInterface, nemid: int) -> None:
|
||||
"""
|
||||
Record an interface to numerical ID mapping. The Emane controller
|
||||
object manages and assigns these IDs for all NEMs.
|
||||
"""
|
||||
self.nemidmap[iface] = nemid
|
||||
|
||||
def getnemid(self, iface: CoreInterface) -> Optional[int]:
|
||||
"""
|
||||
Given an interface, return its numerical ID.
|
||||
"""
|
||||
if iface not in self.nemidmap:
|
||||
return None
|
||||
else:
|
||||
return self.nemidmap[iface]
|
||||
|
||||
def get_nem_iface(self, nemid: int) -> Optional[CoreInterface]:
|
||||
"""
|
||||
Given a numerical NEM ID, return its interface. This returns the
|
||||
first interface that matches the given NEM ID.
|
||||
"""
|
||||
for iface in self.nemidmap:
|
||||
if self.nemidmap[iface] == nemid:
|
||||
return iface
|
||||
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(
|
||||
self, iface: CoreInterface
|
||||
) -> Optional[Tuple[int, float, float, float]]:
|
||||
|
@ -186,9 +119,9 @@ class EmaneNet(CoreNetworkBase):
|
|||
:param iface: interface to get nem emane position for
|
||||
:return: nem position tuple, None otherwise
|
||||
"""
|
||||
nemid = self.getnemid(iface)
|
||||
nem_id = self.session.emane.get_nem_id(iface)
|
||||
ifname = iface.localname
|
||||
if nemid is None:
|
||||
if nem_id is None:
|
||||
logging.info("nemid for %s is unknown", ifname)
|
||||
return
|
||||
node = iface.node
|
||||
|
@ -199,7 +132,7 @@ class EmaneNet(CoreNetworkBase):
|
|||
node.position.set_geo(lon, lat, alt)
|
||||
# altitude must be an integer or warning is printed
|
||||
alt = int(round(alt))
|
||||
return nemid, lon, lat, alt
|
||||
return nem_id, lon, lat, alt
|
||||
|
||||
def setnemposition(self, iface: CoreInterface) -> None:
|
||||
"""
|
||||
|
@ -210,7 +143,6 @@ class EmaneNet(CoreNetworkBase):
|
|||
if self.session.emane.service is None:
|
||||
logging.info("position service not available")
|
||||
return
|
||||
|
||||
position = self._nem_position(iface)
|
||||
if position:
|
||||
nemid, lon, lat, alt = position
|
||||
|
@ -241,9 +173,12 @@ class EmaneNet(CoreNetworkBase):
|
|||
|
||||
def links(self, flags: MessageFlags = MessageFlags.NONE) -> List[LinkData]:
|
||||
links = super().links(flags)
|
||||
# gather current emane links
|
||||
nem_ids = set(self.nemidmap.values())
|
||||
emane_manager = self.session.emane
|
||||
# gather current emane links
|
||||
nem_ids = set()
|
||||
for iface in self.get_ifaces():
|
||||
nem_id = emane_manager.get_nem_id(iface)
|
||||
nem_ids.add(nem_id)
|
||||
emane_links = emane_manager.link_monitor.links
|
||||
considered = set()
|
||||
for link_key in emane_links:
|
||||
|
@ -262,3 +197,18 @@ class EmaneNet(CoreNetworkBase):
|
|||
if link:
|
||||
links.append(link)
|
||||
return links
|
||||
|
||||
def custom_iface(self, node: CoreNode, iface_data: InterfaceData) -> CoreInterface:
|
||||
# TUN/TAP is not ready for addressing yet; the device may
|
||||
# take some time to appear, and installing it into a
|
||||
# namespace after it has been bound removes addressing;
|
||||
# save addresses with the interface now
|
||||
iface_id = node.newtuntap(iface_data.id, iface_data.name)
|
||||
node.attachnet(iface_id, self)
|
||||
iface = node.get_iface(iface_id)
|
||||
iface.set_mac(iface_data.mac)
|
||||
for ip in iface_data.get_ips():
|
||||
iface.add_ip(ip)
|
||||
if self.session.state == EventTypes.RUNTIME_STATE:
|
||||
self.session.emane.start_iface(self, 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.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.emulator.data import (
|
||||
ConfigData,
|
||||
|
@ -531,7 +531,7 @@ class Session:
|
|||
self.set_node_position(node, options)
|
||||
|
||||
# add services to needed nodes
|
||||
if isinstance(node, (CoreNode, PhysicalNode, DockerNode, LxcNode)):
|
||||
if isinstance(node, (CoreNode, PhysicalNode)):
|
||||
node.type = options.model
|
||||
logging.debug("set node type: %s", node.type)
|
||||
self.services.add_services(node, node.type, options.services)
|
||||
|
@ -545,6 +545,8 @@ class Session:
|
|||
# ensure default emane configuration
|
||||
if isinstance(node, EmaneNet) and options.emane:
|
||||
self.emane.set_model_config(_id, options.emane)
|
||||
if self.state == EventTypes.RUNTIME_STATE:
|
||||
self.emane.add_node(node)
|
||||
# set default wlan config if needed
|
||||
if isinstance(node, WlanNode):
|
||||
self.mobility.set_model_config(_id, BasicRangeModel.name)
|
||||
|
@ -1181,7 +1183,7 @@ class Session:
|
|||
self.distributed.start()
|
||||
|
||||
# instantiate will be invoked again upon emane configure
|
||||
if self.emane.startup() == self.emane.NOT_READY:
|
||||
if self.emane.startup() == EmaneState.NOT_READY:
|
||||
return []
|
||||
|
||||
# boot node services and then start mobility
|
||||
|
|
|
@ -812,30 +812,18 @@ class CoreNode(CoreNodeBase):
|
|||
:param iface_data: interface data for new interface
|
||||
:return: interface index
|
||||
"""
|
||||
ips = iface_data.get_ips()
|
||||
with self.lock:
|
||||
# TODO: emane specific code
|
||||
if net.is_emane is True:
|
||||
iface_id = self.newtuntap(iface_data.id, iface_data.name)
|
||||
# TUN/TAP is not ready for addressing yet; the device may
|
||||
# take some time to appear, and installing it into a
|
||||
# namespace after it has been bound removes addressing;
|
||||
# save addresses with the interface now
|
||||
self.attachnet(iface_id, net)
|
||||
iface = self.get_iface(iface_id)
|
||||
iface.set_mac(iface_data.mac)
|
||||
for ip in ips:
|
||||
iface.add_ip(ip)
|
||||
if net.has_custom_iface:
|
||||
return net.custom_iface(self, iface_data)
|
||||
else:
|
||||
iface_id = self.newveth(iface_data.id, iface_data.name)
|
||||
self.attachnet(iface_id, net)
|
||||
if iface_data.mac:
|
||||
self.set_mac(iface_id, iface_data.mac)
|
||||
for ip in ips:
|
||||
for ip in iface_data.get_ips():
|
||||
self.add_ip(iface_id, ip)
|
||||
self.ifup(iface_id)
|
||||
iface = self.get_iface(iface_id)
|
||||
return iface
|
||||
return self.get_iface(iface_id)
|
||||
|
||||
def addfile(self, srcname: str, filename: str) -> None:
|
||||
"""
|
||||
|
@ -925,7 +913,7 @@ class CoreNetworkBase(NodeBase):
|
|||
"""
|
||||
|
||||
linktype: LinkTypes = LinkTypes.WIRED
|
||||
is_emane: bool = False
|
||||
has_custom_iface: bool = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -990,6 +978,9 @@ class CoreNetworkBase(NodeBase):
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def custom_iface(self, node: CoreNode, iface_data: InterfaceData) -> CoreInterface:
|
||||
raise NotImplementedError
|
||||
|
||||
def get_linked_iface(self, net: "CoreNetworkBase") -> Optional[CoreInterface]:
|
||||
"""
|
||||
Return the interface that links this net with another net.
|
||||
|
|
|
@ -60,7 +60,7 @@ class CoreInterface:
|
|||
# placeholder position hook
|
||||
self.poshook: Callable[[CoreInterface], None] = lambda x: None
|
||||
# used with EMANE
|
||||
self.transport_type: Optional[TransportType] = None
|
||||
self.transport_type: TransportType = TransportType.VIRTUAL
|
||||
# id of interface for node
|
||||
self.node_id: Optional[int] = None
|
||||
# id of interface for network
|
||||
|
@ -310,6 +310,22 @@ class CoreInterface:
|
|||
"""
|
||||
return id(self) < id(other)
|
||||
|
||||
def is_raw(self) -> bool:
|
||||
"""
|
||||
Used to determine if this interface is considered a raw interface.
|
||||
|
||||
:return: True if raw interface, False otherwise
|
||||
"""
|
||||
return self.transport_type == TransportType.RAW
|
||||
|
||||
def is_virtual(self) -> bool:
|
||||
"""
|
||||
Used to determine if this interface is considered a virtual interface.
|
||||
|
||||
:return: True if virtual interface, False otherwise
|
||||
"""
|
||||
return self.transport_type == TransportType.VIRTUAL
|
||||
|
||||
|
||||
class Veth(CoreInterface):
|
||||
"""
|
||||
|
@ -404,7 +420,6 @@ class TunTap(CoreInterface):
|
|||
:param start: start flag
|
||||
"""
|
||||
super().__init__(session, node, name, localname, mtu, server)
|
||||
self.transport_type = TransportType.VIRTUAL
|
||||
if start:
|
||||
self.startup()
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from typing import Tuple
|
||||
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.errors import CoreError
|
||||
from core.nodes.base import CoreNode
|
||||
from core.services.coreservices import CoreService
|
||||
from core.xml import emanexml
|
||||
|
@ -14,37 +13,22 @@ class EmaneTransportService(CoreService):
|
|||
dependencies: Tuple[str, ...] = ()
|
||||
dirs: Tuple[str, ...] = ()
|
||||
configs: Tuple[str, ...] = ("emanetransport.sh",)
|
||||
startup: Tuple[str, ...] = ("sh %s" % configs[0],)
|
||||
validate: Tuple[str, ...] = ("pidof %s" % executables[0],)
|
||||
startup: Tuple[str, ...] = (f"sh {configs[0]}",)
|
||||
validate: Tuple[str, ...] = (f"pidof {executables[0]}",)
|
||||
validation_timer: float = 0.5
|
||||
shutdown: Tuple[str, ...] = ("killall %s" % executables[0],)
|
||||
shutdown: Tuple[str, ...] = (f"killall {executables[0]}",)
|
||||
|
||||
@classmethod
|
||||
def generate_config(cls, node: CoreNode, filename: str) -> str:
|
||||
if filename == cls.configs[0]:
|
||||
transport_commands = []
|
||||
for iface in node.get_ifaces():
|
||||
try:
|
||||
network_node = node.session.get_node(iface.net.id, EmaneNet)
|
||||
config = node.session.emane.get_configs(
|
||||
network_node.id, network_node.model.name
|
||||
)
|
||||
if config and emanexml.is_external(config):
|
||||
nem_id = network_node.getnemid(iface)
|
||||
command = (
|
||||
"emanetransportd -r -l 0 -d ../transportdaemon%s.xml"
|
||||
% nem_id
|
||||
)
|
||||
transport_commands.append(command)
|
||||
except CoreError:
|
||||
pass
|
||||
transport_commands = "\n".join(transport_commands)
|
||||
return """
|
||||
emanegentransportxml -o ../ ../platform%s.xml
|
||||
%s
|
||||
""" % (
|
||||
node.id,
|
||||
transport_commands,
|
||||
)
|
||||
else:
|
||||
raise ValueError
|
||||
emane_manager = node.session.emane
|
||||
cfg = ""
|
||||
for iface in node.get_ifaces():
|
||||
if not isinstance(iface.net, EmaneNet):
|
||||
continue
|
||||
emane_net = iface.net
|
||||
config = emane_manager.get_iface_config(emane_net, iface)
|
||||
if emanexml.is_external(config):
|
||||
nem_id = emane_manager.get_nem_id(iface)
|
||||
cfg += f"emanegentransportxml {iface.name}-platform.xml\n"
|
||||
cfg += f"emanetransportd -r -l 0 -d transportdaemon{nem_id}.xml\n"
|
||||
return cfg
|
||||
|
|
|
@ -501,8 +501,8 @@ class CoreXmlWriter:
|
|||
iface = node.get_iface(iface_data.id)
|
||||
# check if emane interface
|
||||
if isinstance(iface.net, EmaneNet):
|
||||
nem = iface.net.getnemid(iface)
|
||||
add_attribute(iface_element, "nem", nem)
|
||||
nem_id = self.session.emane.get_nem_id(iface)
|
||||
add_attribute(iface_element, "nem", nem_id)
|
||||
add_attribute(iface_element, "id", iface_data.id)
|
||||
add_attribute(iface_element, "name", iface_data.name)
|
||||
add_attribute(iface_element, "mac", iface_data.mac)
|
||||
|
|
|
@ -9,7 +9,6 @@ from core import utils
|
|||
from core.emane.nodes import EmaneNet
|
||||
from core.executables import IP
|
||||
from core.nodes.base import CoreNodeBase, NodeBase
|
||||
from core.nodes.interface import CoreInterface
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.emulator.session import Session
|
||||
|
@ -38,11 +37,10 @@ def add_mapping(parent_element: etree.Element, maptype: str, mapref: str) -> Non
|
|||
|
||||
def add_emane_iface(
|
||||
host_element: etree.Element,
|
||||
iface: CoreInterface,
|
||||
nem_id: int,
|
||||
platform_name: str = "p1",
|
||||
transport_name: str = "t1",
|
||||
) -> etree.Element:
|
||||
nem_id = iface.net.nemidmap[iface]
|
||||
host_id = host_element.get("id")
|
||||
|
||||
# platform data
|
||||
|
@ -158,7 +156,8 @@ class CoreXmlDeployment:
|
|||
for iface in node.get_ifaces():
|
||||
emane_element = None
|
||||
if isinstance(iface.net, EmaneNet):
|
||||
emane_element = add_emane_iface(host_element, iface)
|
||||
nem_id = self.session.emane.get_nem_id(iface)
|
||||
emane_element = add_emane_iface(host_element, nem_id)
|
||||
|
||||
parent_element = host_element
|
||||
if emane_element is not None:
|
||||
|
|
|
@ -9,7 +9,7 @@ from core import utils
|
|||
from core.config import Configuration
|
||||
from core.emane.nodes import EmaneNet
|
||||
from core.emulator.distributed import DistributedServer
|
||||
from core.emulator.enumerations import TransportType
|
||||
from core.errors import CoreError
|
||||
from core.nodes.interface import CoreInterface
|
||||
from core.nodes.network import CtrlNet
|
||||
from core.xml import corexml
|
||||
|
@ -40,15 +40,11 @@ def _value_to_params(value: str) -> Optional[Tuple[str]]:
|
|||
"""
|
||||
try:
|
||||
values = utils.make_tuple_fromstr(value, str)
|
||||
|
||||
if not hasattr(values, "__iter__"):
|
||||
return None
|
||||
|
||||
if len(values) < 2:
|
||||
return None
|
||||
|
||||
return values
|
||||
|
||||
except SyntaxError:
|
||||
logging.exception("error in value string to param list")
|
||||
return None
|
||||
|
@ -66,16 +62,15 @@ def create_file(
|
|||
:param xml_element: root element to write to file
|
||||
:param doc_name: name to use in the emane doctype
|
||||
:param file_path: file path to write xml file to
|
||||
:param server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:param server: remote server to create file on
|
||||
:return: nothing
|
||||
"""
|
||||
doctype = (
|
||||
f'<!DOCTYPE {doc_name} SYSTEM "file:///usr/share/emane/dtd/{doc_name}.dtd">'
|
||||
)
|
||||
if server is not None:
|
||||
if server:
|
||||
temp = NamedTemporaryFile(delete=False)
|
||||
create_file(xml_element, doc_name, temp.name)
|
||||
corexml.write_xml_file(xml_element, temp.name, doctype=doctype)
|
||||
temp.close()
|
||||
server.remote_put(temp.name, file_path)
|
||||
os.unlink(temp.name)
|
||||
|
@ -83,6 +78,26 @@ def create_file(
|
|||
corexml.write_xml_file(xml_element, file_path, doctype=doctype)
|
||||
|
||||
|
||||
def create_iface_file(
|
||||
iface: CoreInterface, xml_element: etree.Element, doc_name: str, file_name: str
|
||||
) -> None:
|
||||
"""
|
||||
Create emane xml for an interface.
|
||||
|
||||
:param iface: interface running emane
|
||||
:param xml_element: root element to write to file
|
||||
:param doc_name: name to use in the emane doctype
|
||||
:param file_name: name of xml file
|
||||
:return:
|
||||
"""
|
||||
node = iface.node
|
||||
if iface.is_raw():
|
||||
file_path = os.path.join(node.session.session_dir, file_name)
|
||||
else:
|
||||
file_path = os.path.join(node.nodedir, file_name)
|
||||
create_file(xml_element, doc_name, file_path, node.server)
|
||||
|
||||
|
||||
def add_param(xml_element: etree.Element, name: str, value: str) -> None:
|
||||
"""
|
||||
Add emane configuration parameter to xml element.
|
||||
|
@ -127,13 +142,13 @@ def add_configurations(
|
|||
add_param(xml_element, name, value)
|
||||
|
||||
|
||||
def build_node_platform_xml(
|
||||
def build_platform_xml(
|
||||
emane_manager: "EmaneManager",
|
||||
control_net: CtrlNet,
|
||||
node: EmaneNet,
|
||||
emane_net: EmaneNet,
|
||||
iface: CoreInterface,
|
||||
nem_id: int,
|
||||
platform_xmls: Dict[str, etree.Element],
|
||||
) -> int:
|
||||
) -> None:
|
||||
"""
|
||||
Create platform xml for a specific node.
|
||||
|
||||
|
@ -141,313 +156,149 @@ def build_node_platform_xml(
|
|||
configurations
|
||||
:param control_net: control net node for this emane
|
||||
network
|
||||
:param node: node to write platform xml for
|
||||
:param nem_id: nem id to use for interfaces for this node
|
||||
:param platform_xmls: stores platform xml elements to append nem entries to
|
||||
:param emane_net: emane network associated with interface
|
||||
:param iface: interface running emane
|
||||
:param nem_id: nem id to use for this interface
|
||||
: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,
|
||||
# build nem xml
|
||||
nem_definition = nem_file_name(iface)
|
||||
nem_element = etree.Element(
|
||||
"nem", id=str(nem_id), name=iface.localname, definition=nem_definition
|
||||
)
|
||||
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
|
||||
nem_definition = nem_file_name(node.model, iface)
|
||||
nem_element = etree.Element(
|
||||
"nem", id=str(nem_id), name=iface.localname, definition=nem_definition
|
||||
# check if this is an external transport, get default config if an interface
|
||||
# specific one does not exist
|
||||
config = emane_manager.get_iface_config(emane_net, iface)
|
||||
if is_external(config):
|
||||
nem_element.set("transport", "external")
|
||||
platform_endpoint = "platformendpoint"
|
||||
add_param(nem_element, platform_endpoint, config[platform_endpoint])
|
||||
transport_endpoint = "transportendpoint"
|
||||
add_param(nem_element, transport_endpoint, config[transport_endpoint])
|
||||
else:
|
||||
transport_name = transport_file_name(iface)
|
||||
transport_element = etree.SubElement(
|
||||
nem_element, "transport", definition=transport_name
|
||||
)
|
||||
add_param(transport_element, "device", iface.name)
|
||||
|
||||
# check if this is an external transport, get default config if an interface
|
||||
# specific one does not exist
|
||||
config = emane_manager.get_iface_config(node.model.id, iface, node.model.name)
|
||||
|
||||
if is_external(config):
|
||||
nem_element.set("transport", "external")
|
||||
platform_endpoint = "platformendpoint"
|
||||
add_param(nem_element, platform_endpoint, config[platform_endpoint])
|
||||
transport_endpoint = "transportendpoint"
|
||||
add_param(nem_element, transport_endpoint, config[transport_endpoint])
|
||||
transport_configs = {"otamanagerdevice", "eventservicedevice"}
|
||||
platform_element = etree.Element("platform")
|
||||
for configuration in emane_manager.emane_config.emulator_config:
|
||||
name = configuration.id
|
||||
if iface.is_raw() and name in transport_configs:
|
||||
value = control_net.brname
|
||||
else:
|
||||
# build transport xml
|
||||
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(node.id, transport_type)
|
||||
transport_element = etree.SubElement(
|
||||
nem_element, "transport", definition=transport_file
|
||||
)
|
||||
|
||||
# add transport parameter
|
||||
add_param(transport_element, "device", iface.name)
|
||||
|
||||
# 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
|
||||
value = emane_manager.get_config(name)
|
||||
add_param(platform_element, name, value)
|
||||
platform_element.append(nem_element)
|
||||
mac = _MAC_PREFIX + ":00:00:"
|
||||
mac += f"{(nem_id >> 8) & 0xFF:02X}:{nem_id & 0xFF:02X}"
|
||||
iface.set_mac(mac)
|
||||
|
||||
doc_name = "platform"
|
||||
for key in sorted(platform_xmls.keys()):
|
||||
platform_element = platform_xmls[key]
|
||||
if key == "host":
|
||||
file_name = "platform.xml"
|
||||
file_path = os.path.join(emane_manager.session.session_dir, file_name)
|
||||
create_file(platform_element, doc_name, file_path)
|
||||
else:
|
||||
file_name = f"platform{key}.xml"
|
||||
file_path = os.path.join(emane_manager.session.session_dir, file_name)
|
||||
linked_node = emane_manager.session.nodes[key]
|
||||
create_file(platform_element, doc_name, file_path, linked_node.server)
|
||||
|
||||
return nem_id
|
||||
file_name = f"{iface.name}-platform.xml"
|
||||
create_iface_file(iface, platform_element, doc_name, file_name)
|
||||
|
||||
|
||||
def build_xml_files(emane_manager: "EmaneManager", node: EmaneNet) -> None:
|
||||
"""
|
||||
Generate emane xml files required for node.
|
||||
|
||||
:param emane_manager: emane manager with emane
|
||||
configurations
|
||||
:param node: node to write platform xml for
|
||||
: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
|
||||
need_virtual = False
|
||||
need_raw = False
|
||||
vtype = TransportType.VIRTUAL
|
||||
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:
|
||||
need_virtual = True
|
||||
vtype = iface.transport_type
|
||||
else:
|
||||
need_raw = True
|
||||
rtype = iface.transport_type
|
||||
|
||||
if need_virtual:
|
||||
build_transport_xml(emane_manager, node, vtype)
|
||||
|
||||
if need_raw:
|
||||
build_transport_xml(emane_manager, node, rtype)
|
||||
|
||||
|
||||
def build_transport_xml(
|
||||
emane_manager: "EmaneManager", node: EmaneNet, transport_type: TransportType
|
||||
) -> None:
|
||||
def create_transport_xml(iface: CoreInterface, config: Dict[str, str]) -> None:
|
||||
"""
|
||||
Build transport xml file for node and transport type.
|
||||
|
||||
:param emane_manager: emane manager with emane
|
||||
configurations
|
||||
:param node: node to write platform xml for
|
||||
:param transport_type: transport type to build xml for
|
||||
:param iface: interface to build transport xml for
|
||||
:param config: all current configuration values
|
||||
:return: nothing
|
||||
"""
|
||||
transport_type = iface.transport_type
|
||||
transport_element = etree.Element(
|
||||
"transport",
|
||||
name=f"{transport_type.value.capitalize()} Transport",
|
||||
library=f"trans{transport_type.value.lower()}",
|
||||
)
|
||||
|
||||
# add bitrate
|
||||
add_param(transport_element, "bitrate", "0")
|
||||
|
||||
# get emane model cnfiguration
|
||||
config = emane_manager.get_configs(node.id, node.model.name)
|
||||
flowcontrol = config.get("flowcontrolenable", "0") == "1"
|
||||
|
||||
if transport_type == TransportType.VIRTUAL:
|
||||
if iface.is_virtual():
|
||||
device_path = "/dev/net/tun_flowctl"
|
||||
if not os.path.exists(device_path):
|
||||
device_path = "/dev/net/tun"
|
||||
add_param(transport_element, "devicepath", device_path)
|
||||
|
||||
if flowcontrol:
|
||||
add_param(transport_element, "flowcontrolenable", "on")
|
||||
|
||||
doc_name = "transport"
|
||||
file_name = transport_file_name(node.id, transport_type)
|
||||
file_path = os.path.join(emane_manager.session.session_dir, file_name)
|
||||
create_file(transport_element, doc_name, file_path)
|
||||
emane_manager.session.distributed.execute(
|
||||
lambda x: create_file(transport_element, doc_name, file_path, x)
|
||||
)
|
||||
transport_name = transport_file_name(iface)
|
||||
create_iface_file(iface, transport_element, doc_name, transport_name)
|
||||
|
||||
|
||||
def create_phy_xml(
|
||||
emane_model: "EmaneModel",
|
||||
config: Dict[str, str],
|
||||
file_path: str,
|
||||
server: DistributedServer,
|
||||
emane_model: "EmaneModel", iface: CoreInterface, config: Dict[str, str]
|
||||
) -> None:
|
||||
"""
|
||||
Create the phy xml document.
|
||||
|
||||
:param emane_model: emane model to create xml
|
||||
:param iface: interface to create xml for
|
||||
:param config: all current configuration values
|
||||
:param file_path: path to write file to
|
||||
:param server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:return: nothing
|
||||
"""
|
||||
phy_element = etree.Element("phy", name=f"{emane_model.name} PHY")
|
||||
if emane_model.phy_library:
|
||||
phy_element.set("library", emane_model.phy_library)
|
||||
|
||||
add_configurations(
|
||||
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)
|
||||
else:
|
||||
create_file(phy_element, "phy", file_path)
|
||||
emane_model.session.distributed.execute(
|
||||
lambda x: create_file(phy_element, "phy", file_path, x)
|
||||
)
|
||||
file_name = phy_file_name(iface)
|
||||
create_iface_file(iface, phy_element, "phy", file_name)
|
||||
|
||||
|
||||
def create_mac_xml(
|
||||
emane_model: "EmaneModel",
|
||||
config: Dict[str, str],
|
||||
file_path: str,
|
||||
server: DistributedServer,
|
||||
emane_model: "EmaneModel", iface: CoreInterface, config: Dict[str, str]
|
||||
) -> None:
|
||||
"""
|
||||
Create the mac xml document.
|
||||
|
||||
:param emane_model: emane model to create xml
|
||||
:param iface: interface to create xml for
|
||||
:param config: all current configuration values
|
||||
:param file_path: path to write file to
|
||||
:param server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:return: nothing
|
||||
"""
|
||||
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", name=f"{emane_model.name} MAC", library=emane_model.mac_library
|
||||
)
|
||||
add_configurations(
|
||||
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)
|
||||
else:
|
||||
create_file(mac_element, "mac", file_path)
|
||||
emane_model.session.distributed.execute(
|
||||
lambda x: create_file(mac_element, "mac", file_path, x)
|
||||
)
|
||||
file_name = mac_file_name(iface)
|
||||
create_iface_file(iface, mac_element, "mac", file_name)
|
||||
|
||||
|
||||
def create_nem_xml(
|
||||
emane_model: "EmaneModel",
|
||||
config: Dict[str, str],
|
||||
nem_file: str,
|
||||
transport_definition: str,
|
||||
mac_definition: str,
|
||||
phy_definition: str,
|
||||
server: DistributedServer,
|
||||
emane_model: "EmaneModel", iface: CoreInterface, config: Dict[str, str]
|
||||
) -> None:
|
||||
"""
|
||||
Create the nem xml document.
|
||||
|
||||
:param emane_model: emane model to create xml
|
||||
:param iface: interface to create xml for
|
||||
:param config: all current configuration values
|
||||
:param nem_file: nem file path to write
|
||||
:param transport_definition: transport file definition path
|
||||
:param mac_definition: mac file definition path
|
||||
:param phy_definition: phy file definition path
|
||||
:param server: remote server node
|
||||
will run on, default is None for localhost
|
||||
:return: nothing
|
||||
"""
|
||||
nem_element = etree.Element("nem", name=f"{emane_model.name} NEM")
|
||||
if is_external(config):
|
||||
nem_element.set("type", "unstructured")
|
||||
else:
|
||||
etree.SubElement(nem_element, "transport", definition=transport_definition)
|
||||
etree.SubElement(nem_element, "mac", definition=mac_definition)
|
||||
etree.SubElement(nem_element, "phy", definition=phy_definition)
|
||||
if server is not None:
|
||||
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)
|
||||
)
|
||||
transport_name = transport_file_name(iface)
|
||||
etree.SubElement(nem_element, "transport", definition=transport_name)
|
||||
mac_name = mac_file_name(iface)
|
||||
etree.SubElement(nem_element, "mac", definition=mac_name)
|
||||
phy_name = phy_file_name(iface)
|
||||
etree.SubElement(nem_element, "phy", definition=phy_name)
|
||||
nem_name = nem_file_name(iface)
|
||||
create_iface_file(iface, nem_element, "nem", nem_name)
|
||||
|
||||
|
||||
def create_event_service_xml(
|
||||
|
@ -483,81 +334,52 @@ def create_event_service_xml(
|
|||
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) -> str:
|
||||
"""
|
||||
Create name for a transport xml file.
|
||||
|
||||
:param node_id: node id to generate transport file name for
|
||||
:param transport_type: transport type to generate transport file
|
||||
:return:
|
||||
:param iface: interface running emane
|
||||
:return: transport xml file name
|
||||
"""
|
||||
return f"n{node_id}trans{transport_type.value}.xml"
|
||||
return f"{iface.name}-trans-{iface.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 for this model
|
||||
:return: basename used for file creation
|
||||
:param iface: interface running emane
|
||||
:return: nem xm file name
|
||||
"""
|
||||
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}"
|
||||
append = "-raw" if iface.is_raw() else ""
|
||||
return f"{iface.name}-nem{append}.xml"
|
||||
|
||||
|
||||
def nem_file_name(emane_model: "EmaneModel", iface: CoreInterface = None) -> str:
|
||||
def shim_file_name(iface: CoreInterface = None) -> str:
|
||||
"""
|
||||
Return the string name for the NEM XML file, e.g. "n3rfpipenem.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 for this model
|
||||
:return: nem xml filename
|
||||
:param iface: interface running emane
|
||||
:return: shim xml file name
|
||||
"""
|
||||
basename = _basename(emane_model, iface)
|
||||
append = ""
|
||||
if iface and iface.transport_type == TransportType.RAW:
|
||||
append = "_raw"
|
||||
return f"{basename}nem{append}.xml"
|
||||
return f"{iface.name}-shim.xml"
|
||||
|
||||
|
||||
def shim_file_name(emane_model: "EmaneModel", iface: CoreInterface = None) -> str:
|
||||
def mac_file_name(iface: CoreInterface) -> str:
|
||||
"""
|
||||
Return the string name for the SHIM XML file, e.g. "commeffectshim.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 for this model
|
||||
:return: shim xml filename
|
||||
:param iface: interface running emane
|
||||
:return: mac xml file name
|
||||
"""
|
||||
name = _basename(emane_model, iface)
|
||||
return f"{name}shim.xml"
|
||||
return f"{iface.name}-mac.xml"
|
||||
|
||||
|
||||
def mac_file_name(emane_model: "EmaneModel", iface: CoreInterface = None) -> str:
|
||||
def phy_file_name(iface: CoreInterface) -> str:
|
||||
"""
|
||||
Return the string name for the MAC XML file, e.g. "n3rfpipemac.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 for this model
|
||||
:return: mac xml filename
|
||||
:param iface: interface running emane
|
||||
:return: phy xml file name
|
||||
"""
|
||||
name = _basename(emane_model, iface)
|
||||
return f"{name}mac.xml"
|
||||
|
||||
|
||||
def phy_file_name(emane_model: "EmaneModel", iface: CoreInterface = None) -> str:
|
||||
"""
|
||||
Return the string name for the PHY XML file, e.g. "n3rfpipephy.xml"
|
||||
|
||||
:param emane_model: emane model to create file
|
||||
:param iface: interface for this model
|
||||
:return: phy xml filename
|
||||
"""
|
||||
name = _basename(emane_model, iface)
|
||||
return f"{name}phy.xml"
|
||||
return f"{iface.name}-phy.xml"
|
||||
|
|
|
@ -12,7 +12,6 @@ from mock.mock import MagicMock
|
|||
from core.api.grpc.client import InterfaceHelper
|
||||
from core.api.grpc.server import CoreGrpcServer
|
||||
from core.api.tlv.corehandlers import CoreHandler
|
||||
from core.emane.emanemanager import EmaneManager
|
||||
from core.emulator.coreemu import CoreEmu
|
||||
from core.emulator.data import IpPrefixes
|
||||
from core.emulator.distributed import DistributedServer
|
||||
|
@ -63,7 +62,6 @@ def patcher(request):
|
|||
patch_manager.patch_obj(CoreNode, "nodefile")
|
||||
patch_manager.patch_obj(Session, "write_state")
|
||||
patch_manager.patch_obj(Session, "write_nodes")
|
||||
patch_manager.patch_obj(EmaneManager, "buildxml")
|
||||
yield patch_manager
|
||||
patch_manager.shutdown()
|
||||
|
||||
|
|
Loading…
Reference in a new issue