daemon: refactoring for starting up and shutting down emane daemon per interface
This commit is contained in:
parent
820539191d
commit
795a5f5865
2 changed files with 60 additions and 76 deletions
|
@ -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()
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue