daemon: refactoring for starting up and shutting down emane daemon per interface

This commit is contained in:
Blake Harnden 2021-05-26 09:54:32 -07:00
parent 820539191d
commit 795a5f5865
2 changed files with 60 additions and 76 deletions

View file

@ -5,9 +5,8 @@ Implements configuration and control of an EMANE emulation.
import logging import logging
import os import os
import threading import threading
from dataclasses import dataclass, field
from enum import Enum 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 import utils
from core.emane.emanemodel import EmaneModel 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.data import LinkData
from core.emulator.enumerations import LinkTypes, MessageFlags, RegisterTlvs from core.emulator.enumerations import LinkTypes, MessageFlags, RegisterTlvs
from core.errors import CoreCommandError, CoreError 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.nodes.interface import CoreInterface, TunTap
from core.xml import emanexml from core.xml import emanexml
@ -57,12 +56,6 @@ class EmaneState(Enum):
NOT_READY = 2 NOT_READY = 2
@dataclass
class StartData:
node: CoreNodeBase
ifaces: List[CoreInterface] = field(default_factory=list)
class EmaneEventService: class EmaneEventService:
def __init__( def __init__(
self, manager: "EmaneManager", device: str, group: str, port: int self, manager: "EmaneManager", device: str, group: str, port: int
@ -327,26 +320,24 @@ class EmaneManager:
def startup_nodes(self) -> None: def startup_nodes(self) -> None:
with self._emane_node_lock: with self._emane_node_lock:
logger.info("emane building xmls...") logger.info("emane building xmls...")
start_data = self.get_start_data() for emane_net, iface in self.get_ifaces():
for data in start_data: nem_id = self.next_nem_id(iface)
node = data.node nem_port = self.get_nem_port(iface)
for iface in data.ifaces: logger.info(
nem_id = self.next_nem_id(iface) "starting emane for node(%s) iface(%s) nem(%s)",
logger.info( iface.node.name,
"starting emane for node(%s) iface(%s) nem(%s)", iface.name,
node.name, nem_id,
iface.name, )
nem_id, config = self.get_iface_config(emane_net, iface)
) self.setup_control_channels(nem_id, iface, config)
self.setup_control_channels(nem_id, node, iface) emanexml.build_platform_xml(nem_id, nem_port, emane_net, iface, config)
emanexml.build_platform_xml(self, nem_id, node, iface) self.start_daemon(iface)
self.start_daemon(node, iface) self.install_iface(emane_net, iface, config)
self.install_iface(iface)
def get_start_data(self) -> List[StartData]: def get_ifaces(self) -> List[Tuple[EmaneNet, CoreInterface]]:
node_map = {} ifaces = []
for node_id in sorted(self._emane_nets): for emane_net in self._emane_nets.values():
emane_net = self._emane_nets[node_id]
if not emane_net.model: if not emane_net.model:
logger.error("emane net(%s) has no model", emane_net.name) logger.error("emane net(%s) has no model", emane_net.name)
continue continue
@ -358,21 +349,13 @@ class EmaneManager:
iface.name, iface.name,
) )
continue continue
start_node = node_map.setdefault(iface.node, StartData(iface.node)) ifaces.append((emane_net, iface))
start_node.ifaces.append(iface) return sorted(ifaces, key=lambda x: (x[1].node.id, x[1].node_id))
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
def setup_control_channels( def setup_control_channels(
self, nem_id: int, node: CoreNodeBase, iface: CoreInterface self, nem_id: int, iface: CoreInterface, config: Dict[str, str]
) -> None: ) -> None:
if not isinstance(iface.net, EmaneNet): node = iface.node
raise CoreError(
f"emane interface not connected to emane net: {iface.net.name}"
)
config = self.get_iface_config(iface.net, iface)
# setup ota device # setup ota device
otagroup, _otaport = config["otamanagergroup"].split(":") otagroup, _otaport = config["otamanagergroup"].split(":")
otadev = config["otamanagerdevice"] otadev = config["otamanagerdevice"]
@ -467,6 +450,8 @@ class EmaneManager:
self._emane_nets.clear() self._emane_nets.clear()
self.nems_to_ifaces.clear() self.nems_to_ifaces.clear()
self.ifaces_to_nems.clear() self.ifaces_to_nems.clear()
self.nems_to_ifaces.clear()
self.services.clear()
def shutdown(self) -> None: def shutdown(self) -> None:
""" """
@ -478,17 +463,19 @@ class EmaneManager:
logger.info("stopping EMANE daemons") logger.info("stopping EMANE daemons")
if self.links_enabled(): if self.links_enabled():
self.link_monitor.stop() self.link_monitor.stop()
# shutdown interfaces and stop daemons # shutdown interfaces
kill_emaned = "killall -q emane" nodes = set()
start_data = self.get_start_data() for _, iface in self.get_ifaces():
for data in start_data: node = iface.node
node = data.node
if not node.up: if not node.up:
continue continue
for iface in data.ifaces: nodes.add(node)
if isinstance(node, CoreNode): if isinstance(node, CoreNode):
iface.shutdown() iface.shutdown()
iface.poshook = None iface.poshook = None
kill_emaned = "killall -q emane"
# stop all emane daemons on associated nodes
for node in nodes:
if isinstance(node, CoreNode): if isinstance(node, CoreNode):
node.cmd(kill_emaned, wait=False) node.cmd(kill_emaned, wait=False)
else: else:
@ -550,11 +537,14 @@ class EmaneManager:
color=color, 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. Start emane daemon for a given nem/interface.
Add a control network even if the user has not configured one.
:param iface: interface to start emane daemon for
:return: nothing
""" """
node = iface.node
loglevel = str(DEFAULT_LOG_LEVEL) loglevel = str(DEFAULT_LOG_LEVEL)
cfgloglevel = self.session.options.get_config_int("emane_log_level") cfgloglevel = self.session.options.get_config_int("emane_log_level")
realtime = self.session.options.get_config_bool("emane_realtime", default=True) 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}" args = f"{emanecmd} -f {log_file} {platform_xml}"
node.host_cmd(args, cwd=self.session.directory) node.host_cmd(args, cwd=self.session.directory)
def install_iface(self, iface: CoreInterface) -> None: def install_iface(
emane_net = iface.net self, emane_net: EmaneNet, iface: CoreInterface, config: Dict[str, str]
if not isinstance(emane_net, EmaneNet): ) -> None:
raise CoreError(
f"emane interface not connected to emane net: {emane_net.name}"
)
config = self.get_iface_config(emane_net, iface)
external = config.get("external", "0") external = config.get("external", "0")
if isinstance(iface, TunTap) and external == "0": if isinstance(iface, TunTap) and external == "0":
iface.set_ips() iface.set_ips()

View file

@ -17,7 +17,6 @@ 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
from core.emane.emanemodel import EmaneModel from core.emane.emanemodel import EmaneModel
_MAC_PREFIX = "02:02" _MAC_PREFIX = "02:02"
@ -145,33 +144,29 @@ def add_configurations(
def build_platform_xml( 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: ) -> 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 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 :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 # create top level platform element
platform_element = etree.Element("platform") platform_element = etree.Element("platform")
for configuration in emane_net.model.platform_config: for configuration in emane_net.model.platform_config:
name = configuration.id name = configuration.id
value = config[configuration.id] value = config[configuration.id]
if name == "controlportendpoint": if name == "controlportendpoint":
port = emane_manager.get_nem_port(iface) value = f"0.0.0.0:{nem_port}"
value = f"0.0.0.0:{port}"
add_param(platform_element, name, value) add_param(platform_element, name, value)
# build nem xml # build nem xml
@ -180,6 +175,9 @@ def build_platform_xml(
"nem", id=str(nem_id), name=iface.localname, definition=nem_definition "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 # check if this is an external transport
if is_external(config): if is_external(config):
nem_element.set("transport", "external") nem_element.set("transport", "external")
@ -205,7 +203,7 @@ def build_platform_xml(
doc_name = "platform" doc_name = "platform"
file_name = platform_file_name(iface) 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: def create_transport_xml(iface: CoreInterface, config: Dict[str, str]) -> None: