From 795a5f5865542c47bd6e19ab8eaf66538ba3a6d6 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Wed, 26 May 2021 09:54:32 -0700 Subject: [PATCH] daemon: refactoring for starting up and shutting down emane daemon per interface --- daemon/core/emane/emanemanager.py | 104 +++++++++++++----------------- daemon/core/xml/emanexml.py | 32 +++++---- 2 files changed, 60 insertions(+), 76 deletions(-) diff --git a/daemon/core/emane/emanemanager.py b/daemon/core/emane/emanemanager.py index 60f25023..01b9758c 100644 --- a/daemon/core/emane/emanemanager.py +++ b/daemon/core/emane/emanemanager.py @@ -5,9 +5,8 @@ Implements configuration and control of an EMANE emulation. import logging import os import threading -from dataclasses import dataclass, field from enum import Enum -from typing import TYPE_CHECKING, Dict, List, Optional, Set, Type, Union +from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type, Union from core import utils from core.emane.emanemodel import EmaneModel @@ -17,7 +16,7 @@ from core.emane.nodes import EmaneNet from core.emulator.data import LinkData from core.emulator.enumerations import LinkTypes, MessageFlags, RegisterTlvs from core.errors import CoreCommandError, CoreError -from core.nodes.base import CoreNetworkBase, CoreNode, CoreNodeBase, NodeBase +from core.nodes.base import CoreNetworkBase, CoreNode, NodeBase from core.nodes.interface import CoreInterface, TunTap from core.xml import emanexml @@ -57,12 +56,6 @@ class EmaneState(Enum): NOT_READY = 2 -@dataclass -class StartData: - node: CoreNodeBase - ifaces: List[CoreInterface] = field(default_factory=list) - - class EmaneEventService: def __init__( self, manager: "EmaneManager", device: str, group: str, port: int @@ -327,26 +320,24 @@ class EmaneManager: def startup_nodes(self) -> None: with self._emane_node_lock: logger.info("emane building xmls...") - start_data = self.get_start_data() - for data in start_data: - node = data.node - for iface in data.ifaces: - nem_id = self.next_nem_id(iface) - logger.info( - "starting emane for node(%s) iface(%s) nem(%s)", - node.name, - iface.name, - nem_id, - ) - self.setup_control_channels(nem_id, node, iface) - emanexml.build_platform_xml(self, nem_id, node, iface) - self.start_daemon(node, iface) - self.install_iface(iface) + for emane_net, iface in self.get_ifaces(): + nem_id = self.next_nem_id(iface) + nem_port = self.get_nem_port(iface) + logger.info( + "starting emane for node(%s) iface(%s) nem(%s)", + iface.node.name, + iface.name, + nem_id, + ) + config = self.get_iface_config(emane_net, iface) + self.setup_control_channels(nem_id, iface, config) + emanexml.build_platform_xml(nem_id, nem_port, emane_net, iface, config) + self.start_daemon(iface) + self.install_iface(emane_net, iface, config) - def get_start_data(self) -> List[StartData]: - node_map = {} - for node_id in sorted(self._emane_nets): - emane_net = self._emane_nets[node_id] + def get_ifaces(self) -> List[Tuple[EmaneNet, CoreInterface]]: + ifaces = [] + for emane_net in self._emane_nets.values(): if not emane_net.model: logger.error("emane net(%s) has no model", emane_net.name) continue @@ -358,21 +349,13 @@ class EmaneManager: iface.name, ) continue - start_node = node_map.setdefault(iface.node, StartData(iface.node)) - start_node.ifaces.append(iface) - start_nodes = sorted(node_map.values(), key=lambda x: x.node.id) - for start_node in start_nodes: - start_node.ifaces = sorted(start_node.ifaces, key=lambda x: x.node_id) - return start_nodes + ifaces.append((emane_net, iface)) + return sorted(ifaces, key=lambda x: (x[1].node.id, x[1].node_id)) def setup_control_channels( - self, nem_id: int, node: CoreNodeBase, iface: CoreInterface + self, nem_id: int, iface: CoreInterface, config: Dict[str, str] ) -> None: - if not isinstance(iface.net, EmaneNet): - raise CoreError( - f"emane interface not connected to emane net: {iface.net.name}" - ) - config = self.get_iface_config(iface.net, iface) + node = iface.node # setup ota device otagroup, _otaport = config["otamanagergroup"].split(":") otadev = config["otamanagerdevice"] @@ -467,6 +450,8 @@ class EmaneManager: self._emane_nets.clear() self.nems_to_ifaces.clear() self.ifaces_to_nems.clear() + self.nems_to_ifaces.clear() + self.services.clear() def shutdown(self) -> None: """ @@ -478,17 +463,19 @@ class EmaneManager: logger.info("stopping EMANE daemons") if self.links_enabled(): self.link_monitor.stop() - # shutdown interfaces and stop daemons - kill_emaned = "killall -q emane" - start_data = self.get_start_data() - for data in start_data: - node = data.node + # shutdown interfaces + nodes = set() + for _, iface in self.get_ifaces(): + node = iface.node if not node.up: continue - for iface in data.ifaces: - if isinstance(node, CoreNode): - iface.shutdown() - iface.poshook = None + nodes.add(node) + if isinstance(node, CoreNode): + iface.shutdown() + iface.poshook = None + kill_emaned = "killall -q emane" + # stop all emane daemons on associated nodes + for node in nodes: if isinstance(node, CoreNode): node.cmd(kill_emaned, wait=False) else: @@ -550,11 +537,14 @@ class EmaneManager: color=color, ) - def start_daemon(self, node: CoreNodeBase, iface: CoreInterface) -> 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. + Start emane daemon for a given nem/interface. + + :param iface: interface to start emane daemon for + :return: nothing """ + node = iface.node loglevel = str(DEFAULT_LOG_LEVEL) cfgloglevel = self.session.options.get_config_int("emane_log_level") realtime = self.session.options.get_config_bool("emane_realtime", default=True) @@ -576,13 +566,9 @@ class EmaneManager: args = f"{emanecmd} -f {log_file} {platform_xml}" node.host_cmd(args, cwd=self.session.directory) - def install_iface(self, iface: CoreInterface) -> None: - emane_net = iface.net - if not isinstance(emane_net, EmaneNet): - raise CoreError( - f"emane interface not connected to emane net: {emane_net.name}" - ) - config = self.get_iface_config(emane_net, iface) + def install_iface( + self, emane_net: EmaneNet, iface: CoreInterface, config: Dict[str, str] + ) -> None: external = config.get("external", "0") if isinstance(iface, TunTap) and external == "0": iface.set_ips() diff --git a/daemon/core/xml/emanexml.py b/daemon/core/xml/emanexml.py index 9d0753cb..f8489b5b 100644 --- a/daemon/core/xml/emanexml.py +++ b/daemon/core/xml/emanexml.py @@ -17,7 +17,6 @@ from core.xml import corexml logger = logging.getLogger(__name__) if TYPE_CHECKING: - from core.emane.emanemanager import EmaneManager from core.emane.emanemodel import EmaneModel _MAC_PREFIX = "02:02" @@ -145,33 +144,29 @@ def add_configurations( def build_platform_xml( - emane_manager: "EmaneManager", nem_id: int, node: CoreNodeBase, iface: CoreInterface + nem_id: int, + nem_port: int, + emane_net: EmaneNet, + iface: CoreInterface, + config: Dict[str, str], ) -> None: """ - Create platform xml for a specific node. + Create platform xml for a nem/interface. - :param emane_manager: emane manager with emane - configurations :param nem_id: nem id for current node/interface - :param node: node to create a platform xml for + :param nem_port: control port to configure for emane + :param emane_net: emane network associate with node and interface :param iface: node interface to create platform xml for - :return: the next nem id that can be used for creating platform xml files + :param config: emane configuration for interface + :return: nothing """ - # create model based xml files - emane_net = iface.net - if not isinstance(emane_net, EmaneNet): - raise CoreError(f"emane interface not connected to emane net: {emane_net.name}") - config = emane_manager.get_iface_config(emane_net, iface) - emane_net.model.build_xml_files(config, iface) - # create top level platform element platform_element = etree.Element("platform") for configuration in emane_net.model.platform_config: name = configuration.id value = config[configuration.id] if name == "controlportendpoint": - port = emane_manager.get_nem_port(iface) - value = f"0.0.0.0:{port}" + value = f"0.0.0.0:{nem_port}" add_param(platform_element, name, value) # build nem xml @@ -180,6 +175,9 @@ def build_platform_xml( "nem", id=str(nem_id), name=iface.localname, definition=nem_definition ) + # create model based xml files + emane_net.model.build_xml_files(config, iface) + # check if this is an external transport if is_external(config): nem_element.set("transport", "external") @@ -205,7 +203,7 @@ def build_platform_xml( doc_name = "platform" file_name = platform_file_name(iface) - create_node_file(node, platform_element, doc_name, file_name) + create_node_file(iface.node, platform_element, doc_name, file_name) def create_transport_xml(iface: CoreInterface, config: Dict[str, str]) -> None: