daemon: initial changes to support one emane process per nem

This commit is contained in:
Blake Harnden 2021-05-18 09:48:38 -07:00
parent d16f6b234b
commit e2a9f6b1f4
4 changed files with 109 additions and 99 deletions

View file

@ -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)):

View file

@ -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)

View file

@ -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:

View file

@ -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"