daemon: further heavy cleanup to how emane generates and runs xml files

This commit is contained in:
Blake Harnden 2020-07-02 17:49:56 -07:00
parent bd48e14348
commit ce4b61d3b2
5 changed files with 103 additions and 179 deletions

View file

@ -12,7 +12,6 @@ from core.config import ConfigGroup, Configuration
from core.emane import emanemanifest, emanemodel from core.emane import emanemanifest, emanemodel
from core.emane.nodes import EmaneNet from core.emane.nodes import EmaneNet
from core.emulator.data import LinkOptions from core.emulator.data import LinkOptions
from core.emulator.enumerations import TransportType
from core.nodes.interface import CoreInterface from core.nodes.interface import CoreInterface
from core.xml import emanexml from core.xml import emanexml
@ -73,26 +72,16 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
:param iface: interface for the emane node :param iface: interface for the emane node
:return: nothing :return: nothing
""" """
# interface node
node = iface.node
# retrieve xml names
nem_name = emanexml.nem_file_name(iface)
shim_name = emanexml.shim_file_name(iface)
# create and write nem document # create and write nem document
nem_element = etree.Element("nem", name=f"{self.name} NEM", type="unstructured") nem_element = etree.Element("nem", name=f"{self.name} NEM", type="unstructured")
transport_type = TransportType.VIRTUAL transport_name = emanexml.transport_file_name(iface)
if iface.transport_type == TransportType.RAW: etree.SubElement(nem_element, "transport", definition=transport_name)
transport_type = TransportType.RAW
transport_file = emanexml.transport_file_name(iface, transport_type)
etree.SubElement(nem_element, "transport", definition=transport_file)
# set shim configuration # set shim configuration
nem_name = emanexml.nem_file_name(iface)
shim_name = emanexml.shim_file_name(iface)
etree.SubElement(nem_element, "shim", definition=shim_name) etree.SubElement(nem_element, "shim", definition=shim_name)
emanexml.create_iface_file(iface, nem_element, "nem", nem_name)
nem_file = os.path.join(node.nodedir, nem_name)
emanexml.create_file(nem_element, "nem", nem_file)
# create and write shim document # create and write shim document
shim_element = etree.Element( shim_element = etree.Element(
@ -111,9 +100,10 @@ class EmaneCommEffectModel(emanemodel.EmaneModel):
ff = config["filterfile"] ff = config["filterfile"]
if ff.strip() != "": if ff.strip() != "":
emanexml.add_param(shim_element, "filterfile", ff) emanexml.add_param(shim_element, "filterfile", ff)
emanexml.create_iface_file(iface, shim_element, "shim", shim_name)
shim_file = os.path.join(node.nodedir, shim_name) # create transport xml
emanexml.create_file(shim_element, "shim", shim_file) emanexml.create_transport_xml(iface, config)
def linkconfig( def linkconfig(
self, iface: CoreInterface, options: LinkOptions, iface2: CoreInterface = None self, iface: CoreInterface, options: LinkOptions, iface2: CoreInterface = None

View file

@ -281,8 +281,6 @@ class EmaneManager(ModelManager):
instantiation instantiation
""" """
logging.debug("emane setup") logging.debug("emane setup")
# TODO: drive this from the session object
with self.session.nodes_lock: with self.session.nodes_lock:
for node_id in self.session.nodes: for node_id in self.session.nodes:
node = self.session.nodes[node_id] node = self.session.nodes[node_id]
@ -291,7 +289,6 @@ class EmaneManager(ModelManager):
"adding emane node: id(%s) name(%s)", node.id, node.name "adding emane node: id(%s) name(%s)", node.id, node.name
) )
self.add_node(node) self.add_node(node)
if not self._emane_nets: if not self._emane_nets:
logging.debug("no emane nodes in session") logging.debug("no emane nodes in session")
return EmaneState.NOT_NEEDED return EmaneState.NOT_NEEDED
@ -322,7 +319,7 @@ class EmaneManager(ModelManager):
logging.debug("emane event service device index: %s", netidx) logging.debug("emane event service device index: %s", netidx)
if netidx < 0: if netidx < 0:
logging.error( logging.error(
"EMANE cannot start, check core config. invalid event service device: %s", "emane cannot start due to invalid event service device: %s",
eventdev, eventdev,
) )
return EmaneState.NOT_READY return EmaneState.NOT_READY
@ -330,7 +327,6 @@ class EmaneManager(ModelManager):
self.session.add_remove_control_net( self.session.add_remove_control_net(
net_index=netidx, remove=False, conf_required=False net_index=netidx, remove=False, conf_required=False
) )
self.check_node_models() self.check_node_models()
return EmaneState.SUCCESS return EmaneState.SUCCESS
@ -349,10 +345,6 @@ class EmaneManager(ModelManager):
self.starteventmonitor() self.starteventmonitor()
self.buildeventservicexml() self.buildeventservicexml()
with self._emane_node_lock: with self._emane_node_lock:
# on master, control network bridge added earlier in startup()
control_net = self.session.add_remove_control_net(
0, remove=False, conf_required=False
)
logging.info("emane building xmls...") logging.info("emane building xmls...")
for node_id in sorted(self._emane_nets): for node_id in sorted(self._emane_nets):
emane_net = self._emane_nets[node_id] emane_net = self._emane_nets[node_id]
@ -360,25 +352,31 @@ class EmaneManager(ModelManager):
logging.error("emane net(%s) has no model", emane_net.name) logging.error("emane net(%s) has no model", emane_net.name)
continue continue
for iface in emane_net.get_ifaces(): for iface in emane_net.get_ifaces():
if not iface.node: self.start_iface(emane_net, iface)
logging.error(
"emane net(%s) connected interface missing node",
emane_net.name,
)
continue
nem_id = self.next_nem_id()
self.nems[nem_id] = iface
self.write_nem(iface, nem_id)
emanexml.build_platform_xml(
self, control_net, emane_net, iface, nem_id
)
emanexml.build_model_xmls(self, emane_net, iface)
self.start_daemon(iface)
self.install_iface(emane_net, iface)
if self.links_enabled(): if self.links_enabled():
self.link_monitor.start() self.link_monitor.start()
return EmaneState.SUCCESS return EmaneState.SUCCESS
def start_iface(self, emane_net: EmaneNet, iface: CoreInterface) -> None:
if not iface.node:
logging.error(
"emane net(%s) connected interface(%s) missing node",
emane_net.name,
iface.name,
)
return
control_net = self.session.add_remove_control_net(
0, remove=False, conf_required=False
)
nem_id = self.next_nem_id()
self.nems[nem_id] = iface
self.write_nem(iface, nem_id)
emanexml.build_platform_xml(self, control_net, emane_net, iface, nem_id)
config = self.get_iface_config(emane_net, iface)
emane_net.model.build_xml_files(config, iface)
self.start_daemon(iface)
self.install_iface(emane_net, iface)
def write_nem(self, iface: CoreInterface, nem_id: int) -> None: def write_nem(self, iface: CoreInterface, nem_id: int) -> None:
path = os.path.join(self.session.session_dir, "emane_nems") path = os.path.join(self.session.session_dir, "emane_nems")
try: try:

View file

@ -9,7 +9,7 @@ from core.config import ConfigGroup, Configuration
from core.emane import emanemanifest from core.emane import emanemanifest
from core.emane.nodes import EmaneNet from core.emane.nodes import EmaneNet
from core.emulator.data import LinkOptions from core.emulator.data import LinkOptions
from core.emulator.enumerations import ConfigDataTypes, TransportType from core.emulator.enumerations import ConfigDataTypes
from core.errors import CoreError from core.errors import CoreError
from core.location.mobility import WirelessModel from core.location.mobility import WirelessModel
from core.nodes.base import CoreNode from core.nodes.base import CoreNode
@ -102,34 +102,14 @@ class EmaneModel(WirelessModel):
both mac.xml and phy.xml definitions. both mac.xml and phy.xml definitions.
:param config: emane model configuration for the node and interface :param config: emane model configuration for the node and interface
:param iface: interface for the emane node :param iface: interface to run emane for
:return: nothing :return: nothing
""" """
nem_name = emanexml.nem_file_name(iface) # create nem, mac, and phy xml files
mac_name = emanexml.mac_file_name(iface) emanexml.create_nem_xml(self, iface, config)
phy_name = emanexml.phy_file_name(iface) emanexml.create_mac_xml(self, iface, config)
emanexml.create_phy_xml(self, iface, config)
# check if this is external emanexml.create_transport_xml(iface, config)
transport_type = TransportType.VIRTUAL
if iface.transport_type == TransportType.RAW:
transport_type = TransportType.RAW
transport_name = emanexml.transport_file_name(iface, transport_type)
node = iface.node
server = node.server
# create nem xml file
nem_file = os.path.join(node.nodedir, nem_name)
emanexml.create_nem_xml(
self, config, nem_file, transport_name, mac_name, phy_name, server
)
# create mac xml file
mac_file = os.path.join(node.nodedir, mac_name)
emanexml.create_mac_xml(self, config, mac_file, server)
# create phy xml file
phy_file = os.path.join(node.nodedir, phy_name)
emanexml.create_phy_xml(self, config, phy_file, server)
def post_startup(self) -> None: def post_startup(self) -> None:
""" """

View file

@ -96,7 +96,6 @@ class EmaneNet(CoreNetworkBase):
""" """
set the EmaneModel associated with this node set the EmaneModel associated with this node
""" """
logging.info("adding model: %s", model.name)
if model.config_type == RegisterTlvs.WIRELESS: if model.config_type == RegisterTlvs.WIRELESS:
# EmaneModel really uses values from ConfigurableManager # EmaneModel really uses values from ConfigurableManager
# when buildnemxml() is called, not during init() # when buildnemxml() is called, not during init()

View file

@ -13,6 +13,7 @@ from core.emulator.enumerations import TransportType
from core.errors import CoreError from core.errors import CoreError
from core.nodes.interface import CoreInterface from core.nodes.interface import CoreInterface
from core.nodes.network import CtrlNet from core.nodes.network import CtrlNet
from core.nodes.physical import Rj45Node
from core.xml import corexml from core.xml import corexml
if TYPE_CHECKING: if TYPE_CHECKING:
@ -63,16 +64,15 @@ def create_file(
:param xml_element: root element to write to file :param xml_element: root element to write to file
:param doc_name: name to use in the emane doctype :param doc_name: name to use in the emane doctype
:param file_path: file path to write xml file to :param file_path: file path to write xml file to
:param server: remote server node :param server: remote server to create file on
will run on, default is None for localhost
:return: nothing :return: nothing
""" """
doctype = ( doctype = (
f'<!DOCTYPE {doc_name} SYSTEM "file:///usr/share/emane/dtd/{doc_name}.dtd">' f'<!DOCTYPE {doc_name} SYSTEM "file:///usr/share/emane/dtd/{doc_name}.dtd">'
) )
if server is not None: if server:
temp = NamedTemporaryFile(delete=False) temp = NamedTemporaryFile(delete=False)
create_file(xml_element, doc_name, temp.name) corexml.write_xml_file(xml_element, temp.name, doctype=doctype)
temp.close() temp.close()
server.remote_put(temp.name, file_path) server.remote_put(temp.name, file_path)
os.unlink(temp.name) os.unlink(temp.name)
@ -80,6 +80,26 @@ def create_file(
corexml.write_xml_file(xml_element, file_path, doctype=doctype) corexml.write_xml_file(xml_element, file_path, doctype=doctype)
def create_iface_file(
iface: CoreInterface, xml_element: etree.Element, doc_name: str, file_name: str
) -> None:
"""
Create emane xml for an interface.
:param iface: interface running emane
:param xml_element: root element to write to file
:param doc_name: name to use in the emane doctype
:param file_name: name of xml file
:return:
"""
node = iface.node
if isinstance(node, Rj45Node):
file_path = os.path.join(node.session.session_dir, file_name)
else:
file_path = os.path.join(node.nodedir, file_name)
create_file(xml_element, doc_name, file_path, node.server)
def add_param(xml_element: etree.Element, name: str, value: str) -> None: def add_param(xml_element: etree.Element, name: str, value: str) -> None:
""" """
Add emane configuration parameter to xml element. Add emane configuration parameter to xml element.
@ -159,21 +179,14 @@ def build_platform_xml(
transport_endpoint = "transportendpoint" transport_endpoint = "transportendpoint"
add_param(nem_element, transport_endpoint, config[transport_endpoint]) add_param(nem_element, transport_endpoint, config[transport_endpoint])
else: else:
# build transport xml transport_name = transport_file_name(iface)
transport_type = iface.transport_type
if not transport_type:
logging.info("warning: %s interface type unsupported!", iface.name)
transport_type = TransportType.RAW
transport_file = transport_file_name(iface, transport_type)
transport_element = etree.SubElement( transport_element = etree.SubElement(
nem_element, "transport", definition=transport_file nem_element, "transport", definition=transport_name
) )
add_param(transport_element, "device", iface.name) add_param(transport_element, "device", iface.name)
# determine platform element to add xml to # determine platform element to add xml to
key = iface.node.id
if iface.transport_type == TransportType.RAW: if iface.transport_type == TransportType.RAW:
key = "host"
otadev = control_net.brname otadev = control_net.brname
eventdev = control_net.brname eventdev = control_net.brname
else: else:
@ -195,67 +208,19 @@ def build_platform_xml(
iface.set_mac(mac) iface.set_mac(mac)
doc_name = "platform" doc_name = "platform"
server = None file_name = f"{iface.name}-platform.xml"
if key == "host": create_iface_file(iface, platform_element, doc_name, file_name)
file_name = "platform.xml"
file_path = os.path.join(emane_manager.session.session_dir, file_name)
else:
node = iface.node
file_name = f"{iface.name}-platform.xml"
file_path = os.path.join(node.nodedir, file_name)
server = node.server
create_file(platform_element, doc_name, file_path, server)
def build_model_xmls( def create_transport_xml(iface: CoreInterface, config: Dict[str, str]) -> None:
manager: "EmaneManager", emane_net: EmaneNet, iface: CoreInterface
) -> None:
"""
Generate emane xml files required for node.
:param manager: emane manager with emane
configurations
:param emane_net: emane network associated with interface
:param iface: interface to create emane xml for
:return: nothing
"""
# build XML for specific interface (NEM) configs
# check for interface specific emane configuration and write xml files
config = manager.get_iface_config(emane_net, iface)
emane_net.model.build_xml_files(config, iface)
# check transport type needed for interface
need_virtual = False
need_raw = False
vtype = TransportType.VIRTUAL
rtype = TransportType.RAW
if iface.transport_type == TransportType.VIRTUAL:
need_virtual = True
vtype = iface.transport_type
else:
need_raw = True
rtype = iface.transport_type
if need_virtual:
build_transport_xml(manager, emane_net, iface, vtype)
if need_raw:
build_transport_xml(manager, emane_net, iface, rtype)
def build_transport_xml(
manager: "EmaneManager",
emane_net: EmaneNet,
iface: CoreInterface,
transport_type: TransportType,
) -> None:
""" """
Build transport xml file for node and transport type. Build transport xml file for node and transport type.
:param manager: emane manager with emane configurations
:param emane_net: emane network associated with interface
:param iface: interface to build transport xml for :param iface: interface to build transport xml for
:param transport_type: transport type to build xml for :param config: all current configuration values
:return: nothing :return: nothing
""" """
transport_type = get_transport_type(iface)
transport_element = etree.Element( transport_element = etree.Element(
"transport", "transport",
name=f"{transport_type.value.capitalize()} Transport", name=f"{transport_type.value.capitalize()} Transport",
@ -264,7 +229,6 @@ def build_transport_xml(
add_param(transport_element, "bitrate", "0") add_param(transport_element, "bitrate", "0")
# get emane model cnfiguration # get emane model cnfiguration
config = manager.get_iface_config(emane_net, iface)
flowcontrol = config.get("flowcontrolenable", "0") == "1" flowcontrol = config.get("flowcontrolenable", "0") == "1"
if transport_type == TransportType.VIRTUAL: if transport_type == TransportType.VIRTUAL:
device_path = "/dev/net/tun_flowctl" device_path = "/dev/net/tun_flowctl"
@ -274,29 +238,19 @@ def build_transport_xml(
if flowcontrol: if flowcontrol:
add_param(transport_element, "flowcontrolenable", "on") add_param(transport_element, "flowcontrolenable", "on")
doc_name = "transport" doc_name = "transport"
node = iface.node transport_name = transport_file_name(iface)
file_name = transport_file_name(iface, transport_type) create_iface_file(iface, transport_element, doc_name, transport_name)
file_path = os.path.join(node.nodedir, file_name)
create_file(transport_element, doc_name, file_path)
manager.session.distributed.execute(
lambda x: create_file(transport_element, doc_name, file_path, x)
)
def create_phy_xml( def create_phy_xml(
emane_model: "EmaneModel", emane_model: "EmaneModel", iface: CoreInterface, config: Dict[str, str]
config: Dict[str, str],
file_path: str,
server: Optional[DistributedServer],
) -> None: ) -> None:
""" """
Create the phy xml document. Create the phy xml document.
:param emane_model: emane model to create xml :param emane_model: emane model to create xml
:param iface: interface to create xml for
:param config: all current configuration values :param config: all current configuration values
:param file_path: path to write file to
:param server: remote server node
will run on, default is None for localhost
:return: nothing :return: nothing
""" """
phy_element = etree.Element("phy", name=f"{emane_model.name} PHY") phy_element = etree.Element("phy", name=f"{emane_model.name} PHY")
@ -305,23 +259,19 @@ def create_phy_xml(
add_configurations( add_configurations(
phy_element, emane_model.phy_config, config, emane_model.config_ignore phy_element, emane_model.phy_config, config, emane_model.config_ignore
) )
create_file(phy_element, "phy", file_path, server) file_name = phy_file_name(iface)
create_iface_file(iface, phy_element, "phy", file_name)
def create_mac_xml( def create_mac_xml(
emane_model: "EmaneModel", emane_model: "EmaneModel", iface: CoreInterface, config: Dict[str, str]
config: Dict[str, str],
file_path: str,
server: Optional[DistributedServer],
) -> None: ) -> None:
""" """
Create the mac xml document. Create the mac xml document.
:param emane_model: emane model to create xml :param emane_model: emane model to create xml
:param iface: interface to create xml for
:param config: all current configuration values :param config: all current configuration values
:param file_path: path to write file to
:param server: remote server node
will run on, default is None for localhost
:return: nothing :return: nothing
""" """
if not emane_model.mac_library: if not emane_model.mac_library:
@ -332,39 +282,33 @@ def create_mac_xml(
add_configurations( add_configurations(
mac_element, emane_model.mac_config, config, emane_model.config_ignore mac_element, emane_model.mac_config, config, emane_model.config_ignore
) )
create_file(mac_element, "mac", file_path, server) file_name = mac_file_name(iface)
create_iface_file(iface, mac_element, "mac", file_name)
def create_nem_xml( def create_nem_xml(
emane_model: "EmaneModel", emane_model: "EmaneModel", iface: CoreInterface, config: Dict[str, str]
config: Dict[str, str],
nem_file: str,
transport_definition: str,
mac_definition: str,
phy_definition: str,
server: Optional[DistributedServer],
) -> None: ) -> None:
""" """
Create the nem xml document. Create the nem xml document.
:param emane_model: emane model to create xml :param emane_model: emane model to create xml
:param iface: interface to create xml for
:param config: all current configuration values :param config: all current configuration values
:param nem_file: nem file path to write
:param transport_definition: transport file definition path
:param mac_definition: mac file definition path
:param phy_definition: phy file definition path
:param server: remote server node
will run on, default is None for localhost
:return: nothing :return: nothing
""" """
nem_element = etree.Element("nem", name=f"{emane_model.name} NEM") nem_element = etree.Element("nem", name=f"{emane_model.name} NEM")
if is_external(config): if is_external(config):
nem_element.set("type", "unstructured") nem_element.set("type", "unstructured")
else: else:
etree.SubElement(nem_element, "transport", definition=transport_definition) transport_name = transport_file_name(iface)
etree.SubElement(nem_element, "mac", definition=mac_definition) etree.SubElement(nem_element, "transport", definition=transport_name)
etree.SubElement(nem_element, "phy", definition=phy_definition) mac_name = mac_file_name(iface)
create_file(nem_element, "nem", nem_file, server) etree.SubElement(nem_element, "mac", definition=mac_name)
phy_name = phy_file_name(iface)
etree.SubElement(nem_element, "phy", definition=phy_name)
nem_name = nem_file_name(iface)
create_iface_file(iface, nem_element, "nem", nem_name)
def create_event_service_xml( def create_event_service_xml(
@ -400,14 +344,27 @@ def create_event_service_xml(
create_file(event_element, "emaneeventmsgsvc", file_path, server) create_file(event_element, "emaneeventmsgsvc", file_path, server)
def transport_file_name(iface: CoreInterface, transport_type: TransportType) -> str: def get_transport_type(iface: CoreInterface) -> TransportType:
"""
Get transport type for a given interface.
:param iface: interface to get transport type for
:return: transport type
"""
transport_type = TransportType.VIRTUAL
if iface.transport_type == TransportType.RAW:
transport_type = TransportType.RAW
return transport_type
def transport_file_name(iface: CoreInterface) -> str:
""" """
Create name for a transport xml file. Create name for a transport xml file.
:param iface: interface running emane :param iface: interface running emane
:param transport_type: transport type to generate transport file
:return: transport xml file name :return: transport xml file name
""" """
transport_type = get_transport_type(iface)
return f"{iface.name}-trans-{transport_type.value}.xml" return f"{iface.name}-trans-{transport_type.value}.xml"