daemon: initial changes to support one emane process per nem
This commit is contained in:
parent
d16f6b234b
commit
e2a9f6b1f4
4 changed files with 109 additions and 99 deletions
|
@ -227,12 +227,6 @@ class Ospfv3mdr(Ospfv3):
|
||||||
|
|
||||||
name: str = "OSPFv3MDR"
|
name: str = "OSPFv3MDR"
|
||||||
|
|
||||||
def data(self) -> Dict[str, Any]:
|
|
||||||
for iface in self.node.get_ifaces():
|
|
||||||
is_wireless = isinstance(iface.net, (WlanNode, EmaneNet))
|
|
||||||
logger.info("MDR wireless: %s", is_wireless)
|
|
||||||
return dict()
|
|
||||||
|
|
||||||
def quagga_iface_config(self, iface: CoreInterface) -> str:
|
def quagga_iface_config(self, iface: CoreInterface) -> str:
|
||||||
config = super().quagga_iface_config(iface)
|
config = super().quagga_iface_config(iface)
|
||||||
if isinstance(iface.net, (WlanNode, EmaneNet)):
|
if isinstance(iface.net, (WlanNode, EmaneNet)):
|
||||||
|
|
|
@ -114,10 +114,13 @@ class EmaneManager:
|
||||||
self.eventchannel: Optional[Tuple[str, int, str]] = None
|
self.eventchannel: Optional[Tuple[str, int, str]] = None
|
||||||
self.event_device: Optional[str] = None
|
self.event_device: Optional[str] = None
|
||||||
|
|
||||||
def next_nem_id(self) -> int:
|
def next_nem_id(self, iface: CoreInterface) -> int:
|
||||||
nem_id = int(self.config["nem_id_start"])
|
nem_id = int(self.config["nem_id_start"])
|
||||||
while nem_id in self.nems_to_ifaces:
|
while nem_id in self.nems_to_ifaces:
|
||||||
nem_id += 1
|
nem_id += 1
|
||||||
|
self.nems_to_ifaces[nem_id] = iface
|
||||||
|
self.ifaces_to_nems[iface] = nem_id
|
||||||
|
self.write_nem(iface, nem_id)
|
||||||
return nem_id
|
return nem_id
|
||||||
|
|
||||||
def get_config(
|
def get_config(
|
||||||
|
@ -385,26 +388,54 @@ class EmaneManager:
|
||||||
return start_nodes
|
return start_nodes
|
||||||
|
|
||||||
def start_node(self, data: StartData) -> None:
|
def start_node(self, data: StartData) -> None:
|
||||||
|
node = data.node
|
||||||
control_net = self.session.add_remove_control_net(
|
control_net = self.session.add_remove_control_net(
|
||||||
0, remove=False, conf_required=False
|
0, remove=False, conf_required=False
|
||||||
)
|
)
|
||||||
emanexml.build_platform_xml(self, control_net, data)
|
if isinstance(node, CoreNode):
|
||||||
self.start_daemon(data.node)
|
# setup ota device
|
||||||
|
otagroup, _otaport = self.config["otamanagergroup"].split(":")
|
||||||
|
otadev = self.config["otamanagerdevice"]
|
||||||
|
otanetidx = self.session.get_control_net_index(otadev)
|
||||||
|
eventgroup, _eventport = self.config["eventservicegroup"].split(":")
|
||||||
|
eventdev = self.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:
|
||||||
|
logger.info("adding ota device ctrl%d", otanetidx)
|
||||||
|
self.session.add_remove_control_iface(
|
||||||
|
node, otanetidx, remove=False, conf_required=False
|
||||||
|
)
|
||||||
|
if eventservicenetidx >= 0:
|
||||||
|
logger.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
|
||||||
|
logger.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)
|
||||||
|
# builds xmls and start emane daemons
|
||||||
for iface in data.ifaces:
|
for iface in data.ifaces:
|
||||||
|
emanexml.build_platform_xml(self, control_net, node, iface)
|
||||||
|
self.start_daemon(node, iface)
|
||||||
self.install_iface(iface)
|
self.install_iface(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]:
|
def get_iface(self, nem_id: int) -> Optional[CoreInterface]:
|
||||||
return self.nems_to_ifaces.get(nem_id)
|
return self.nems_to_ifaces.get(nem_id)
|
||||||
|
|
||||||
def get_nem_id(self, iface: CoreInterface) -> Optional[int]:
|
def get_nem_id(self, iface: CoreInterface) -> Optional[int]:
|
||||||
return self.ifaces_to_nems.get(iface)
|
return self.ifaces_to_nems.get(iface)
|
||||||
|
|
||||||
|
def get_nem_port(self, iface: CoreInterface) -> int:
|
||||||
|
nem_id = self.get_nem_id(iface)
|
||||||
|
return int(f"47{nem_id:03}")
|
||||||
|
|
||||||
def write_nem(self, iface: CoreInterface, nem_id: int) -> None:
|
def write_nem(self, iface: CoreInterface, nem_id: int) -> None:
|
||||||
path = self.session.directory / "emane_nems"
|
path = self.session.directory / "emane_nems"
|
||||||
try:
|
try:
|
||||||
|
@ -549,7 +580,7 @@ class EmaneManager:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def start_daemon(self, node: CoreNodeBase) -> None:
|
def start_daemon(self, node: CoreNodeBase, 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.
|
||||||
|
@ -565,42 +596,15 @@ class EmaneManager:
|
||||||
if realtime:
|
if realtime:
|
||||||
emanecmd += " -r"
|
emanecmd += " -r"
|
||||||
if isinstance(node, CoreNode):
|
if isinstance(node, CoreNode):
|
||||||
otagroup, _otaport = self.config["otamanagergroup"].split(":")
|
|
||||||
otadev = self.config["otamanagerdevice"]
|
|
||||||
otanetidx = self.session.get_control_net_index(otadev)
|
|
||||||
eventgroup, _eventport = self.config["eventservicegroup"].split(":")
|
|
||||||
eventdev = self.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:
|
|
||||||
logger.info("adding ota device ctrl%d", otanetidx)
|
|
||||||
self.session.add_remove_control_iface(
|
|
||||||
node, otanetidx, remove=False, conf_required=False
|
|
||||||
)
|
|
||||||
if eventservicenetidx >= 0:
|
|
||||||
logger.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
|
|
||||||
logger.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
|
# start emane
|
||||||
log_file = node.directory / f"{node.name}-emane.log"
|
log_file = node.directory / f"{iface.name}-emane.log"
|
||||||
platform_xml = node.directory / f"{node.name}-platform.xml"
|
platform_xml = node.directory / emanexml.platform_file_name(iface)
|
||||||
args = f"{emanecmd} -f {log_file} {platform_xml}"
|
args = f"{emanecmd} -f {log_file} {platform_xml}"
|
||||||
node.cmd(args)
|
node.cmd(args)
|
||||||
logger.info("node(%s) emane daemon running: %s", node.name, args)
|
logger.info("node(%s) emane daemon running: %s", node.name, args)
|
||||||
else:
|
else:
|
||||||
log_file = self.session.directory / f"{node.name}-emane.log"
|
log_file = self.session.directory / f"{iface.name}-emane.log"
|
||||||
platform_xml = self.session.directory / f"{node.name}-platform.xml"
|
platform_xml = self.session.directory / emanexml.platform_file_name(iface)
|
||||||
args = f"{emanecmd} -f {log_file} {platform_xml}"
|
args = f"{emanecmd} -f {log_file} {platform_xml}"
|
||||||
node.host_cmd(args, cwd=self.session.directory)
|
node.host_cmd(args, cwd=self.session.directory)
|
||||||
logger.info("node(%s) host emane daemon running: %s", node.name, args)
|
logger.info("node(%s) host emane daemon running: %s", node.name, args)
|
||||||
|
|
|
@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
|
from core.emane.nodes import EmaneNet
|
||||||
from core.emulator.data import LinkData
|
from core.emulator.data import LinkData
|
||||||
from core.emulator.enumerations import LinkTypes, MessageFlags
|
from core.emulator.enumerations import LinkTypes, MessageFlags
|
||||||
from core.nodes.network import CtrlNet
|
from core.nodes.network import CtrlNet
|
||||||
|
@ -24,7 +25,6 @@ except ImportError:
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.emane.emanemanager import EmaneManager
|
from core.emane.emanemanager import EmaneManager
|
||||||
|
|
||||||
DEFAULT_PORT: int = 47_000
|
|
||||||
MAC_COMPONENT_INDEX: int = 1
|
MAC_COMPONENT_INDEX: int = 1
|
||||||
EMANE_RFPIPE: str = "rfpipemaclayer"
|
EMANE_RFPIPE: str = "rfpipemaclayer"
|
||||||
EMANE_80211: str = "ieee80211abgmaclayer"
|
EMANE_80211: str = "ieee80211abgmaclayer"
|
||||||
|
@ -79,10 +79,10 @@ class EmaneLink:
|
||||||
|
|
||||||
|
|
||||||
class EmaneClient:
|
class EmaneClient:
|
||||||
def __init__(self, address: str) -> None:
|
def __init__(self, address: str, port: int) -> None:
|
||||||
self.address: str = address
|
self.address: str = address
|
||||||
self.client: shell.ControlPortClient = shell.ControlPortClient(
|
self.client: shell.ControlPortClient = shell.ControlPortClient(
|
||||||
self.address, DEFAULT_PORT
|
self.address, port
|
||||||
)
|
)
|
||||||
self.nems: Dict[int, LossTable] = {}
|
self.nems: Dict[int, LossTable] = {}
|
||||||
self.setup()
|
self.setup()
|
||||||
|
@ -204,22 +204,28 @@ class EmaneLinkMonitor:
|
||||||
|
|
||||||
def initialize(self) -> None:
|
def initialize(self) -> None:
|
||||||
addresses = self.get_addresses()
|
addresses = self.get_addresses()
|
||||||
for address in addresses:
|
for address, port in addresses:
|
||||||
client = EmaneClient(address)
|
client = EmaneClient(address, port)
|
||||||
if client.nems:
|
if client.nems:
|
||||||
self.clients.append(client)
|
self.clients.append(client)
|
||||||
|
|
||||||
def get_addresses(self) -> List[str]:
|
def get_addresses(self) -> List[Tuple[str, int]]:
|
||||||
addresses = []
|
addresses = []
|
||||||
nodes = self.emane_manager.getnodes()
|
nodes = self.emane_manager.getnodes()
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
|
control = None
|
||||||
|
ports = []
|
||||||
for iface in node.get_ifaces():
|
for iface in node.get_ifaces():
|
||||||
if isinstance(iface.net, CtrlNet):
|
if isinstance(iface.net, CtrlNet):
|
||||||
ip4 = iface.get_ip4()
|
ip4 = iface.get_ip4()
|
||||||
if ip4:
|
if ip4:
|
||||||
address = str(ip4.ip)
|
control = str(ip4.ip)
|
||||||
addresses.append(address)
|
if isinstance(iface.net, EmaneNet):
|
||||||
break
|
port = self.emane_manager.get_nem_port(iface)
|
||||||
|
ports.append(port)
|
||||||
|
if control:
|
||||||
|
for port in ports:
|
||||||
|
addresses.append((control, port))
|
||||||
return addresses
|
return addresses
|
||||||
|
|
||||||
def check_links(self) -> None:
|
def check_links(self) -> None:
|
||||||
|
|
|
@ -18,7 +18,7 @@ from core.xml import corexml
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.emane.emanemanager import EmaneManager, StartData
|
from core.emane.emanemanager import EmaneManager
|
||||||
from core.emane.emanemodel import EmaneModel
|
from core.emane.emanemodel import EmaneModel
|
||||||
|
|
||||||
_MAC_PREFIX = "02:02"
|
_MAC_PREFIX = "02:02"
|
||||||
|
@ -146,7 +146,10 @@ def add_configurations(
|
||||||
|
|
||||||
|
|
||||||
def build_platform_xml(
|
def build_platform_xml(
|
||||||
emane_manager: "EmaneManager", control_net: CtrlNet, data: "StartData"
|
emane_manager: "EmaneManager",
|
||||||
|
control_net: CtrlNet,
|
||||||
|
node: CoreNodeBase,
|
||||||
|
iface: CoreInterface,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create platform xml for a specific node.
|
Create platform xml for a specific node.
|
||||||
|
@ -155,33 +158,32 @@ def build_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 data: start data for a node connected to emane and associated interfaces
|
:param node: node to create a platform xml for
|
||||||
|
:param iface: node interface to create platform xml for
|
||||||
: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
|
||||||
"""
|
"""
|
||||||
|
# create nem xml entries for all interfaces
|
||||||
|
emane_net = iface.net
|
||||||
|
if not isinstance(emane_net, EmaneNet):
|
||||||
|
raise CoreError(f"emane interface not connected to emane net: {emane_net.name}")
|
||||||
|
nem_id = emane_manager.next_nem_id(iface)
|
||||||
|
config = emane_manager.get_iface_config(emane_net, iface)
|
||||||
|
emane_net.model.build_xml_files(config, iface)
|
||||||
|
|
||||||
# create top level platform element
|
# create top level platform element
|
||||||
transport_configs = {"otamanagerdevice", "eventservicedevice"}
|
transport_configs = {"otamanagerdevice", "eventservicedevice"}
|
||||||
platform_element = etree.Element("platform")
|
platform_element = etree.Element("platform")
|
||||||
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 not isinstance(data.node, CoreNode) and name in transport_configs:
|
if not isinstance(node, CoreNode) and name in transport_configs:
|
||||||
value = control_net.brname
|
value = control_net.brname
|
||||||
else:
|
else:
|
||||||
value = emane_manager.config[name]
|
value = emane_manager.config[name]
|
||||||
|
if name == "controlportendpoint":
|
||||||
|
port = emane_manager.get_nem_port(iface)
|
||||||
|
value = f"0.0.0.0:{port}"
|
||||||
add_param(platform_element, name, value)
|
add_param(platform_element, name, value)
|
||||||
|
|
||||||
# create nem xml entries for all interfaces
|
|
||||||
for iface in data.ifaces:
|
|
||||||
emane_net = iface.net
|
|
||||||
if not isinstance(emane_net, EmaneNet):
|
|
||||||
raise CoreError(
|
|
||||||
f"emane interface not connected to emane net: {emane_net.name}"
|
|
||||||
)
|
|
||||||
nem_id = emane_manager.next_nem_id()
|
|
||||||
emane_manager.set_nem(nem_id, iface)
|
|
||||||
emane_manager.write_nem(iface, nem_id)
|
|
||||||
config = emane_manager.get_iface_config(emane_net, iface)
|
|
||||||
emane_net.model.build_xml_files(config, iface)
|
|
||||||
|
|
||||||
# build nem xml
|
# build nem xml
|
||||||
nem_definition = nem_file_name(iface)
|
nem_definition = nem_file_name(iface)
|
||||||
nem_element = etree.Element(
|
nem_element = etree.Element(
|
||||||
|
@ -212,8 +214,8 @@ def build_platform_xml(
|
||||||
iface.set_mac(mac)
|
iface.set_mac(mac)
|
||||||
|
|
||||||
doc_name = "platform"
|
doc_name = "platform"
|
||||||
file_name = f"{data.node.name}-platform.xml"
|
file_name = platform_file_name(iface)
|
||||||
create_node_file(data.node, platform_element, doc_name, file_name)
|
create_node_file(node, platform_element, doc_name, file_name)
|
||||||
|
|
||||||
|
|
||||||
def create_transport_xml(iface: CoreInterface, config: Dict[str, str]) -> None:
|
def create_transport_xml(iface: CoreInterface, config: Dict[str, str]) -> None:
|
||||||
|
@ -396,3 +398,7 @@ def phy_file_name(iface: CoreInterface) -> str:
|
||||||
:return: phy xml file name
|
:return: phy xml file name
|
||||||
"""
|
"""
|
||||||
return f"{iface.name}-phy.xml"
|
return f"{iface.name}-phy.xml"
|
||||||
|
|
||||||
|
|
||||||
|
def platform_file_name(iface: CoreInterface) -> str:
|
||||||
|
return f"{iface.name}-platform.xml"
|
||||||
|
|
Loading…
Add table
Reference in a new issue