added type hinting to core.xml

This commit is contained in:
Blake Harnden 2020-01-14 14:56:00 -08:00
parent 02156867e2
commit 8cd8b2ae2c
3 changed files with 178 additions and 93 deletions

View file

@ -1,17 +1,30 @@
import logging import logging
from typing import TYPE_CHECKING, Any, Dict, Generic, List, Optional, Type, TypeVar
from lxml import etree from lxml import etree
import core.nodes.base import core.nodes.base
import core.nodes.physical import core.nodes.physical
from core.emane.nodes import EmaneNet from core.emane.nodes import EmaneNet
from core.emulator.data import LinkData
from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions
from core.emulator.enumerations import NodeTypes from core.emulator.enumerations import NodeTypes
from core.nodes.base import CoreNetworkBase from core.nodes.base import CoreNetworkBase, NodeBase
from core.nodes.network import CtrlNet from core.nodes.network import CtrlNet
from core.services.coreservices import CoreService
if TYPE_CHECKING:
from core.emane.emanemanager import EmaneGlobalModel
from core.emane.emanemodel import EmaneModel
from core.emulator.session import Session
EmaneModelType = Type[EmaneModel]
T = TypeVar("T")
def write_xml_file(xml_element, file_path, doctype=None): def write_xml_file(
xml_element: etree.Element, file_path: str, doctype: str = None
) -> None:
xml_data = etree.tostring( xml_data = etree.tostring(
xml_element, xml_element,
xml_declaration=True, xml_declaration=True,
@ -23,27 +36,27 @@ def write_xml_file(xml_element, file_path, doctype=None):
xml_file.write(xml_data) xml_file.write(xml_data)
def get_type(element, name, _type): def get_type(element: etree.Element, name: str, _type: Generic[T]) -> Optional[T]:
value = element.get(name) value = element.get(name)
if value is not None: if value is not None:
value = _type(value) value = _type(value)
return value return value
def get_float(element, name): def get_float(element: etree.Element, name: str) -> float:
return get_type(element, name, float) return get_type(element, name, float)
def get_int(element, name): def get_int(element: etree.Element, name: str) -> int:
return get_type(element, name, int) return get_type(element, name, int)
def add_attribute(element, name, value): def add_attribute(element: etree.Element, name: str, value: Any) -> None:
if value is not None: if value is not None:
element.set(name, str(value)) element.set(name, str(value))
def create_interface_data(interface_element): def create_interface_data(interface_element: etree.Element) -> InterfaceData:
interface_id = int(interface_element.get("id")) interface_id = int(interface_element.get("id"))
name = interface_element.get("name") name = interface_element.get("name")
mac = interface_element.get("mac") mac = interface_element.get("mac")
@ -54,7 +67,9 @@ def create_interface_data(interface_element):
return InterfaceData(interface_id, name, mac, ip4, ip4_mask, ip6, ip6_mask) return InterfaceData(interface_id, name, mac, ip4, ip4_mask, ip6, ip6_mask)
def create_emane_config(node_id, emane_config, config): def create_emane_config(
node_id: int, emane_config: "EmaneGlobalModel", config: Dict[str, str]
) -> etree.Element:
emane_configuration = etree.Element("emane_configuration") emane_configuration = etree.Element("emane_configuration")
add_attribute(emane_configuration, "node", node_id) add_attribute(emane_configuration, "node", node_id)
add_attribute(emane_configuration, "model", "emane") add_attribute(emane_configuration, "model", "emane")
@ -72,7 +87,9 @@ def create_emane_config(node_id, emane_config, config):
return emane_configuration return emane_configuration
def create_emane_model_config(node_id, model, config): def create_emane_model_config(
node_id: int, model: "EmaneModelType", config: Dict[str, str]
) -> etree.Element:
emane_element = etree.Element("emane_configuration") emane_element = etree.Element("emane_configuration")
add_attribute(emane_element, "node", node_id) add_attribute(emane_element, "node", node_id)
add_attribute(emane_element, "model", model.name) add_attribute(emane_element, "model", model.name)
@ -95,14 +112,14 @@ def create_emane_model_config(node_id, model, config):
return emane_element return emane_element
def add_configuration(parent, name, value): def add_configuration(parent: etree.Element, name: str, value: str) -> None:
config_element = etree.SubElement(parent, "configuration") config_element = etree.SubElement(parent, "configuration")
add_attribute(config_element, "name", name) add_attribute(config_element, "name", name)
add_attribute(config_element, "value", value) add_attribute(config_element, "value", value)
class NodeElement: class NodeElement:
def __init__(self, session, node, element_name): def __init__(self, session: "Session", node: NodeBase, element_name: str) -> None:
self.session = session self.session = session
self.node = node self.node = node
self.element = etree.Element(element_name) self.element = etree.Element(element_name)
@ -112,7 +129,7 @@ class NodeElement:
add_attribute(self.element, "canvas", node.canvas) add_attribute(self.element, "canvas", node.canvas)
self.add_position() self.add_position()
def add_position(self): def add_position(self) -> None:
x = self.node.position.x x = self.node.position.x
y = self.node.position.y y = self.node.position.y
z = self.node.position.z z = self.node.position.z
@ -129,7 +146,7 @@ class NodeElement:
class ServiceElement: class ServiceElement:
def __init__(self, service): def __init__(self, service: Type[CoreService]) -> None:
self.service = service self.service = service
self.element = etree.Element("service") self.element = etree.Element("service")
add_attribute(self.element, "name", service.name) add_attribute(self.element, "name", service.name)
@ -139,7 +156,7 @@ class ServiceElement:
self.add_shutdown() self.add_shutdown()
self.add_files() self.add_files()
def add_directories(self): def add_directories(self) -> None:
# get custom directories # get custom directories
directories = etree.Element("directories") directories = etree.Element("directories")
for directory in self.service.dirs: for directory in self.service.dirs:
@ -149,7 +166,7 @@ class ServiceElement:
if directories.getchildren(): if directories.getchildren():
self.element.append(directories) self.element.append(directories)
def add_files(self): def add_files(self) -> None:
# get custom files # get custom files
file_elements = etree.Element("files") file_elements = etree.Element("files")
for file_name in self.service.config_data: for file_name in self.service.config_data:
@ -161,7 +178,7 @@ class ServiceElement:
if file_elements.getchildren(): if file_elements.getchildren():
self.element.append(file_elements) self.element.append(file_elements)
def add_startup(self): def add_startup(self) -> None:
# get custom startup # get custom startup
startup_elements = etree.Element("startups") startup_elements = etree.Element("startups")
for startup in self.service.startup: for startup in self.service.startup:
@ -171,7 +188,7 @@ class ServiceElement:
if startup_elements.getchildren(): if startup_elements.getchildren():
self.element.append(startup_elements) self.element.append(startup_elements)
def add_validate(self): def add_validate(self) -> None:
# get custom validate # get custom validate
validate_elements = etree.Element("validates") validate_elements = etree.Element("validates")
for validate in self.service.validate: for validate in self.service.validate:
@ -181,7 +198,7 @@ class ServiceElement:
if validate_elements.getchildren(): if validate_elements.getchildren():
self.element.append(validate_elements) self.element.append(validate_elements)
def add_shutdown(self): def add_shutdown(self) -> None:
# get custom shutdown # get custom shutdown
shutdown_elements = etree.Element("shutdowns") shutdown_elements = etree.Element("shutdowns")
for shutdown in self.service.shutdown: for shutdown in self.service.shutdown:
@ -193,12 +210,12 @@ class ServiceElement:
class DeviceElement(NodeElement): class DeviceElement(NodeElement):
def __init__(self, session, node): def __init__(self, session: "Session", node: NodeBase) -> None:
super().__init__(session, node, "device") super().__init__(session, node, "device")
add_attribute(self.element, "type", node.type) add_attribute(self.element, "type", node.type)
self.add_services() self.add_services()
def add_services(self): def add_services(self) -> None:
service_elements = etree.Element("services") service_elements = etree.Element("services")
for service in self.node.services: for service in self.node.services:
etree.SubElement(service_elements, "service", name=service.name) etree.SubElement(service_elements, "service", name=service.name)
@ -208,7 +225,7 @@ class DeviceElement(NodeElement):
class NetworkElement(NodeElement): class NetworkElement(NodeElement):
def __init__(self, session, node): def __init__(self, session: "Session", node: NodeBase) -> None:
super().__init__(session, node, "network") super().__init__(session, node, "network")
model = getattr(self.node, "model", None) model = getattr(self.node, "model", None)
if model: if model:
@ -221,7 +238,7 @@ class NetworkElement(NodeElement):
add_attribute(self.element, "grekey", grekey) add_attribute(self.element, "grekey", grekey)
self.add_type() self.add_type()
def add_type(self): def add_type(self) -> None:
if self.node.apitype: if self.node.apitype:
node_type = NodeTypes(self.node.apitype).name node_type = NodeTypes(self.node.apitype).name
else: else:
@ -230,14 +247,14 @@ class NetworkElement(NodeElement):
class CoreXmlWriter: class CoreXmlWriter:
def __init__(self, session): def __init__(self, session: "Session") -> None:
self.session = session self.session = session
self.scenario = etree.Element("scenario") self.scenario = etree.Element("scenario")
self.networks = None self.networks = None
self.devices = None self.devices = None
self.write_session() self.write_session()
def write_session(self): def write_session(self) -> None:
# generate xml content # generate xml content
links = self.write_nodes() links = self.write_nodes()
self.write_links(links) self.write_links(links)
@ -250,7 +267,7 @@ class CoreXmlWriter:
self.write_session_metadata() self.write_session_metadata()
self.write_default_services() self.write_default_services()
def write(self, file_name): def write(self, file_name: str) -> None:
self.scenario.set("name", file_name) self.scenario.set("name", file_name)
# write out generated xml # write out generated xml
@ -259,7 +276,7 @@ class CoreXmlWriter:
file_name, xml_declaration=True, pretty_print=True, encoding="UTF-8" file_name, xml_declaration=True, pretty_print=True, encoding="UTF-8"
) )
def write_session_origin(self): def write_session_origin(self) -> None:
# origin: geolocation of cartesian coordinate 0,0,0 # origin: geolocation of cartesian coordinate 0,0,0
lat, lon, alt = self.session.location.refgeo lat, lon, alt = self.session.location.refgeo
origin = etree.Element("session_origin") origin = etree.Element("session_origin")
@ -279,7 +296,7 @@ class CoreXmlWriter:
add_attribute(origin, "y", y) add_attribute(origin, "y", y)
add_attribute(origin, "z", z) add_attribute(origin, "z", z)
def write_session_hooks(self): def write_session_hooks(self) -> None:
# hook scripts # hook scripts
hooks = etree.Element("session_hooks") hooks = etree.Element("session_hooks")
for state in sorted(self.session._hooks.keys()): for state in sorted(self.session._hooks.keys()):
@ -292,7 +309,7 @@ class CoreXmlWriter:
if hooks.getchildren(): if hooks.getchildren():
self.scenario.append(hooks) self.scenario.append(hooks)
def write_session_options(self): def write_session_options(self) -> None:
option_elements = etree.Element("session_options") option_elements = etree.Element("session_options")
options_config = self.session.options.get_configs() options_config = self.session.options.get_configs()
if not options_config: if not options_config:
@ -307,7 +324,7 @@ class CoreXmlWriter:
if option_elements.getchildren(): if option_elements.getchildren():
self.scenario.append(option_elements) self.scenario.append(option_elements)
def write_session_metadata(self): def write_session_metadata(self) -> None:
# metadata # metadata
metadata_elements = etree.Element("session_metadata") metadata_elements = etree.Element("session_metadata")
config = self.session.metadata config = self.session.metadata
@ -321,7 +338,7 @@ class CoreXmlWriter:
if metadata_elements.getchildren(): if metadata_elements.getchildren():
self.scenario.append(metadata_elements) self.scenario.append(metadata_elements)
def write_emane_configs(self): def write_emane_configs(self) -> None:
emane_configurations = etree.Element("emane_configurations") emane_configurations = etree.Element("emane_configurations")
for node_id in self.session.emane.nodes(): for node_id in self.session.emane.nodes():
all_configs = self.session.emane.get_all_configs(node_id) all_configs = self.session.emane.get_all_configs(node_id)
@ -347,7 +364,7 @@ class CoreXmlWriter:
if emane_configurations.getchildren(): if emane_configurations.getchildren():
self.scenario.append(emane_configurations) self.scenario.append(emane_configurations)
def write_mobility_configs(self): def write_mobility_configs(self) -> None:
mobility_configurations = etree.Element("mobility_configurations") mobility_configurations = etree.Element("mobility_configurations")
for node_id in self.session.mobility.nodes(): for node_id in self.session.mobility.nodes():
all_configs = self.session.mobility.get_all_configs(node_id) all_configs = self.session.mobility.get_all_configs(node_id)
@ -371,7 +388,7 @@ class CoreXmlWriter:
if mobility_configurations.getchildren(): if mobility_configurations.getchildren():
self.scenario.append(mobility_configurations) self.scenario.append(mobility_configurations)
def write_service_configs(self): def write_service_configs(self) -> None:
service_configurations = etree.Element("service_configurations") service_configurations = etree.Element("service_configurations")
service_configs = self.session.services.all_configs() service_configs = self.session.services.all_configs()
for node_id, service in service_configs: for node_id, service in service_configs:
@ -382,7 +399,7 @@ class CoreXmlWriter:
if service_configurations.getchildren(): if service_configurations.getchildren():
self.scenario.append(service_configurations) self.scenario.append(service_configurations)
def write_default_services(self): def write_default_services(self) -> None:
node_types = etree.Element("default_services") node_types = etree.Element("default_services")
for node_type in self.session.services.default_services: for node_type in self.session.services.default_services:
services = self.session.services.default_services[node_type] services = self.session.services.default_services[node_type]
@ -393,7 +410,7 @@ class CoreXmlWriter:
if node_types.getchildren(): if node_types.getchildren():
self.scenario.append(node_types) self.scenario.append(node_types)
def write_nodes(self): def write_nodes(self) -> List[LinkData]:
self.networks = etree.SubElement(self.scenario, "networks") self.networks = etree.SubElement(self.scenario, "networks")
self.devices = etree.SubElement(self.scenario, "devices") self.devices = etree.SubElement(self.scenario, "devices")
@ -416,7 +433,7 @@ class CoreXmlWriter:
return links return links
def write_network(self, node): def write_network(self, node: NodeBase) -> None:
# ignore p2p and other nodes that are not part of the api # ignore p2p and other nodes that are not part of the api
if not node.apitype: if not node.apitype:
return return
@ -424,7 +441,7 @@ class CoreXmlWriter:
network = NetworkElement(self.session, node) network = NetworkElement(self.session, node)
self.networks.append(network.element) self.networks.append(network.element)
def write_links(self, links): def write_links(self, links: List[LinkData]) -> None:
link_elements = etree.Element("links") link_elements = etree.Element("links")
# add link data # add link data
for link_data in links: for link_data in links:
@ -438,13 +455,21 @@ class CoreXmlWriter:
if link_elements.getchildren(): if link_elements.getchildren():
self.scenario.append(link_elements) self.scenario.append(link_elements)
def write_device(self, node): def write_device(self, node: NodeBase) -> None:
device = DeviceElement(self.session, node) device = DeviceElement(self.session, node)
self.devices.append(device.element) self.devices.append(device.element)
def create_interface_element( def create_interface_element(
self, element_name, node_id, interface_id, mac, ip4, ip4_mask, ip6, ip6_mask self,
): element_name: str,
node_id: int,
interface_id: int,
mac: str,
ip4: str,
ip4_mask: int,
ip6: str,
ip6_mask: int,
) -> etree.Element:
interface = etree.Element(element_name) interface = etree.Element(element_name)
node = self.session.get_node(node_id) node = self.session.get_node(node_id)
interface_name = None interface_name = None
@ -467,7 +492,7 @@ class CoreXmlWriter:
return interface return interface
def create_link_element(self, link_data): def create_link_element(self, link_data: LinkData) -> etree.Element:
link_element = etree.Element("link") link_element = etree.Element("link")
add_attribute(link_element, "node_one", link_data.node1_id) add_attribute(link_element, "node_one", link_data.node1_id)
add_attribute(link_element, "node_two", link_data.node2_id) add_attribute(link_element, "node_two", link_data.node2_id)
@ -525,11 +550,11 @@ class CoreXmlWriter:
class CoreXmlReader: class CoreXmlReader:
def __init__(self, session): def __init__(self, session: "Session") -> None:
self.session = session self.session = session
self.scenario = None self.scenario = None
def read(self, file_name): def read(self, file_name: str) -> None:
xml_tree = etree.parse(file_name) xml_tree = etree.parse(file_name)
self.scenario = xml_tree.getroot() self.scenario = xml_tree.getroot()
@ -545,7 +570,7 @@ class CoreXmlReader:
self.read_nodes() self.read_nodes()
self.read_links() self.read_links()
def read_default_services(self): def read_default_services(self) -> None:
default_services = self.scenario.find("default_services") default_services = self.scenario.find("default_services")
if default_services is None: if default_services is None:
return return
@ -560,7 +585,7 @@ class CoreXmlReader:
) )
self.session.services.default_services[node_type] = services self.session.services.default_services[node_type] = services
def read_session_metadata(self): def read_session_metadata(self) -> None:
session_metadata = self.scenario.find("session_metadata") session_metadata = self.scenario.find("session_metadata")
if session_metadata is None: if session_metadata is None:
return return
@ -573,7 +598,7 @@ class CoreXmlReader:
logging.info("reading session metadata: %s", configs) logging.info("reading session metadata: %s", configs)
self.session.metadata = configs self.session.metadata = configs
def read_session_options(self): def read_session_options(self) -> None:
session_options = self.scenario.find("session_options") session_options = self.scenario.find("session_options")
if session_options is None: if session_options is None:
return return
@ -586,7 +611,7 @@ class CoreXmlReader:
logging.info("reading session options: %s", configs) logging.info("reading session options: %s", configs)
self.session.options.set_configs(configs) self.session.options.set_configs(configs)
def read_session_hooks(self): def read_session_hooks(self) -> None:
session_hooks = self.scenario.find("session_hooks") session_hooks = self.scenario.find("session_hooks")
if session_hooks is None: if session_hooks is None:
return return
@ -601,7 +626,7 @@ class CoreXmlReader:
hook_type, file_name=name, source_name=None, data=data hook_type, file_name=name, source_name=None, data=data
) )
def read_session_origin(self): def read_session_origin(self) -> None:
session_origin = self.scenario.find("session_origin") session_origin = self.scenario.find("session_origin")
if session_origin is None: if session_origin is None:
return return
@ -625,7 +650,7 @@ class CoreXmlReader:
logging.info("reading session reference xyz: %s, %s, %s", x, y, z) logging.info("reading session reference xyz: %s, %s, %s", x, y, z)
self.session.location.refxyz = (x, y, z) self.session.location.refxyz = (x, y, z)
def read_service_configs(self): def read_service_configs(self) -> None:
service_configurations = self.scenario.find("service_configurations") service_configurations = self.scenario.find("service_configurations")
if service_configurations is None: if service_configurations is None:
return return
@ -669,7 +694,7 @@ class CoreXmlReader:
files.add(name) files.add(name)
service.configs = tuple(files) service.configs = tuple(files)
def read_emane_configs(self): def read_emane_configs(self) -> None:
emane_configurations = self.scenario.find("emane_configurations") emane_configurations = self.scenario.find("emane_configurations")
if emane_configurations is None: if emane_configurations is None:
return return
@ -702,7 +727,7 @@ class CoreXmlReader:
) )
self.session.emane.set_model_config(node_id, model_name, configs) self.session.emane.set_model_config(node_id, model_name, configs)
def read_mobility_configs(self): def read_mobility_configs(self) -> None:
mobility_configurations = self.scenario.find("mobility_configurations") mobility_configurations = self.scenario.find("mobility_configurations")
if mobility_configurations is None: if mobility_configurations is None:
return return
@ -722,7 +747,7 @@ class CoreXmlReader:
) )
self.session.mobility.set_model_config(node_id, model_name, configs) self.session.mobility.set_model_config(node_id, model_name, configs)
def read_nodes(self): def read_nodes(self) -> None:
device_elements = self.scenario.find("devices") device_elements = self.scenario.find("devices")
if device_elements is not None: if device_elements is not None:
for device_element in device_elements.iterchildren(): for device_element in device_elements.iterchildren():
@ -733,7 +758,7 @@ class CoreXmlReader:
for network_element in network_elements.iterchildren(): for network_element in network_elements.iterchildren():
self.read_network(network_element) self.read_network(network_element)
def read_device(self, device_element): def read_device(self, device_element: etree.Element) -> None:
node_id = get_int(device_element, "id") node_id = get_int(device_element, "id")
name = device_element.get("name") name = device_element.get("name")
model = device_element.get("type") model = device_element.get("type")
@ -759,7 +784,7 @@ class CoreXmlReader:
logging.info("reading node id(%s) model(%s) name(%s)", node_id, model, name) logging.info("reading node id(%s) model(%s) name(%s)", node_id, model, name)
self.session.add_node(_id=node_id, options=options) self.session.add_node(_id=node_id, options=options)
def read_network(self, network_element): def read_network(self, network_element: etree.Element) -> None:
node_id = get_int(network_element, "id") node_id = get_int(network_element, "id")
name = network_element.get("name") name = network_element.get("name")
node_type = NodeTypes[network_element.get("type")] node_type = NodeTypes[network_element.get("type")]
@ -783,7 +808,7 @@ class CoreXmlReader:
) )
self.session.add_node(_type=node_type, _id=node_id, options=options) self.session.add_node(_type=node_type, _id=node_id, options=options)
def read_links(self): def read_links(self) -> None:
link_elements = self.scenario.find("links") link_elements = self.scenario.find("links")
if link_elements is None: if link_elements is None:
return return

View file

@ -1,5 +1,6 @@
import os import os
import socket import socket
from typing import TYPE_CHECKING, List, Tuple
import netaddr import netaddr
from lxml import etree from lxml import etree
@ -7,26 +8,40 @@ from lxml import etree
from core import utils from core import utils
from core.constants import IP_BIN from core.constants import IP_BIN
from core.emane.nodes import EmaneNet from core.emane.nodes import EmaneNet
from core.nodes.base import CoreNodeBase from core.nodes.base import CoreNodeBase, NodeBase
from core.nodes.interface import CoreInterface
if TYPE_CHECKING:
from core.emulator.session import Session
def add_type(parent_element, name): def add_type(parent_element: etree.Element, name: str) -> None:
type_element = etree.SubElement(parent_element, "type") type_element = etree.SubElement(parent_element, "type")
type_element.text = name type_element.text = name
def add_address(parent_element, address_type, address, interface_name=None): def add_address(
parent_element: etree.Element,
address_type: str,
address: str,
interface_name: str = None,
) -> None:
address_element = etree.SubElement(parent_element, "address", type=address_type) address_element = etree.SubElement(parent_element, "address", type=address_type)
address_element.text = address address_element.text = address
if interface_name is not None: if interface_name is not None:
address_element.set("iface", interface_name) address_element.set("iface", interface_name)
def add_mapping(parent_element, maptype, mapref): def add_mapping(parent_element: etree.Element, maptype: str, mapref: str) -> None:
etree.SubElement(parent_element, "mapping", type=maptype, ref=mapref) etree.SubElement(parent_element, "mapping", type=maptype, ref=mapref)
def add_emane_interface(host_element, netif, platform_name="p1", transport_name="t1"): def add_emane_interface(
host_element: etree.Element,
netif: CoreInterface,
platform_name: str = "p1",
transport_name: str = "t1",
) -> etree.Element:
nem_id = netif.net.nemidmap[netif] nem_id = netif.net.nemidmap[netif]
host_id = host_element.get("id") host_id = host_element.get("id")
@ -54,7 +69,7 @@ def add_emane_interface(host_element, netif, platform_name="p1", transport_name=
return platform_element return platform_element
def get_address_type(address): def get_address_type(address: str) -> str:
addr, _slash, _prefixlen = address.partition("/") addr, _slash, _prefixlen = address.partition("/")
if netaddr.valid_ipv4(addr): if netaddr.valid_ipv4(addr):
address_type = "IPv4" address_type = "IPv4"
@ -65,7 +80,7 @@ def get_address_type(address):
return address_type return address_type
def get_ipv4_addresses(hostname): def get_ipv4_addresses(hostname: str) -> List[Tuple[str, str]]:
if hostname == "localhost": if hostname == "localhost":
addresses = [] addresses = []
args = f"{IP_BIN} -o -f inet address show" args = f"{IP_BIN} -o -f inet address show"
@ -85,7 +100,7 @@ def get_ipv4_addresses(hostname):
class CoreXmlDeployment: class CoreXmlDeployment:
def __init__(self, session, scenario): def __init__(self, session: "Session", scenario: etree.Element) -> None:
self.session = session self.session = session
self.scenario = scenario self.scenario = scenario
self.root = etree.SubElement( self.root = etree.SubElement(
@ -93,17 +108,17 @@ class CoreXmlDeployment:
) )
self.add_deployment() self.add_deployment()
def find_device(self, name): def find_device(self, name: str) -> etree.Element:
device = self.scenario.find(f"devices/device[@name='{name}']") device = self.scenario.find(f"devices/device[@name='{name}']")
return device return device
def find_interface(self, device, name): def find_interface(self, device: NodeBase, name: str) -> etree.Element:
interface = self.scenario.find( interface = self.scenario.find(
f"devices/device[@name='{device.name}']/interfaces/interface[@name='{name}']" f"devices/device[@name='{device.name}']/interfaces/interface[@name='{name}']"
) )
return interface return interface
def add_deployment(self): def add_deployment(self) -> None:
physical_host = self.add_physical_host(socket.gethostname()) physical_host = self.add_physical_host(socket.gethostname())
for node_id in self.session.nodes: for node_id in self.session.nodes:
@ -111,7 +126,7 @@ class CoreXmlDeployment:
if isinstance(node, CoreNodeBase): if isinstance(node, CoreNodeBase):
self.add_virtual_host(physical_host, node) self.add_virtual_host(physical_host, node)
def add_physical_host(self, name): def add_physical_host(self, name: str) -> etree.Element:
# add host # add host
root_id = self.root.get("id") root_id = self.root.get("id")
host_id = f"{root_id}/{name}" host_id = f"{root_id}/{name}"
@ -126,7 +141,7 @@ class CoreXmlDeployment:
return host_element return host_element
def add_virtual_host(self, physical_host, node): def add_virtual_host(self, physical_host: etree.Element, node: NodeBase) -> None:
if not isinstance(node, CoreNodeBase): if not isinstance(node, CoreNodeBase):
raise TypeError(f"invalid node type: {node}") raise TypeError(f"invalid node type: {node}")

View file

@ -1,16 +1,26 @@
import logging import logging
import os import os
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
from lxml import etree from lxml import etree
from core import utils from core import utils
from core.config import Configuration
from core.emane.nodes import EmaneNet
from core.emulator.distributed import DistributedServer
from core.nodes.interface import CoreInterface
from core.nodes.network import CtrlNet
from core.xml import corexml from core.xml import corexml
if TYPE_CHECKING:
from core.emane.emanemanager import EmaneManager
from core.emane.emanemodel import EmaneModel
_hwaddr_prefix = "02:02" _hwaddr_prefix = "02:02"
def is_external(config): def is_external(config: Dict[str, str]) -> bool:
""" """
Checks if the configuration is for an external transport. Checks if the configuration is for an external transport.
@ -21,7 +31,7 @@ def is_external(config):
return config.get("external") == "1" return config.get("external") == "1"
def _value_to_params(value): def _value_to_params(value: str) -> Optional[Tuple[str]]:
""" """
Helper to convert a parameter to a parameter tuple. Helper to convert a parameter to a parameter tuple.
@ -44,7 +54,12 @@ def _value_to_params(value):
return None return None
def create_file(xml_element, doc_name, file_path, server=None): def create_file(
xml_element: etree.Element,
doc_name: str,
file_path: str,
server: DistributedServer = None,
) -> None:
""" """
Create xml file. Create xml file.
@ -68,7 +83,7 @@ def create_file(xml_element, doc_name, file_path, server=None):
corexml.write_xml_file(xml_element, file_path, doctype=doctype) corexml.write_xml_file(xml_element, file_path, doctype=doctype)
def add_param(xml_element, name, value): 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.
@ -80,7 +95,12 @@ def add_param(xml_element, name, value):
etree.SubElement(xml_element, "param", name=name, value=value) etree.SubElement(xml_element, "param", name=name, value=value)
def add_configurations(xml_element, configurations, config, config_ignore): def add_configurations(
xml_element: etree.Element,
configurations: List[Configuration],
config: Dict[str, str],
config_ignore: Set,
) -> None:
""" """
Add emane model configurations to xml element. Add emane model configurations to xml element.
@ -107,7 +127,13 @@ def add_configurations(xml_element, configurations, config, config_ignore):
add_param(xml_element, name, value) add_param(xml_element, name, value)
def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_xmls): def build_node_platform_xml(
emane_manager: "EmaneManager",
control_net: CtrlNet,
node: EmaneNet,
nem_id: int,
platform_xmls: Dict[str, etree.Element],
) -> int:
""" """
Create platform xml for a specific node. Create platform xml for a specific node.
@ -131,7 +157,7 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
if node.model is None: if node.model is None:
logging.warning("warning: EMANE network %s has no associated model", node.name) logging.warning("warning: EMANE network %s has no associated model", node.name)
return nem_entries return nem_id
for netif in node.netifs(): for netif in node.netifs():
logging.debug( logging.debug(
@ -228,7 +254,7 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x
return nem_id return nem_id
def build_xml_files(emane_manager, node): def build_xml_files(emane_manager: "EmaneManager", node: EmaneNet) -> None:
""" """
Generate emane xml files required for node. Generate emane xml files required for node.
@ -276,7 +302,9 @@ def build_xml_files(emane_manager, node):
build_transport_xml(emane_manager, node, rtype) build_transport_xml(emane_manager, node, rtype)
def build_transport_xml(emane_manager, node, transport_type): def build_transport_xml(
emane_manager: "EmaneManager", node: EmaneNet, transport_type: str
) -> None:
""" """
Build transport xml file for node and transport type. Build transport xml file for node and transport type.
@ -317,7 +345,12 @@ def build_transport_xml(emane_manager, node, transport_type):
) )
def create_phy_xml(emane_model, config, file_path, server): def create_phy_xml(
emane_model: "EmaneModel",
config: Dict[str, str],
file_path: str,
server: DistributedServer,
) -> None:
""" """
Create the phy xml document. Create the phy xml document.
@ -345,7 +378,12 @@ def create_phy_xml(emane_model, config, file_path, server):
) )
def create_mac_xml(emane_model, config, file_path, server): def create_mac_xml(
emane_model: "EmaneModel",
config: Dict[str, str],
file_path: str,
server: DistributedServer,
) -> None:
""" """
Create the mac xml document. Create the mac xml document.
@ -376,14 +414,14 @@ def create_mac_xml(emane_model, config, file_path, server):
def create_nem_xml( def create_nem_xml(
emane_model, emane_model: "EmaneModel",
config, config: Dict[str, str],
nem_file, nem_file: str,
transport_definition, transport_definition: str,
mac_definition, mac_definition: str,
phy_definition, phy_definition: str,
server, server: DistributedServer,
): ) -> None:
""" """
Create the nem xml document. Create the nem xml document.
@ -413,7 +451,13 @@ def create_nem_xml(
) )
def create_event_service_xml(group, port, device, file_directory, server=None): def create_event_service_xml(
group: str,
port: str,
device: str,
file_directory: str,
server: DistributedServer = None,
) -> None:
""" """
Create a emane event service xml file. Create a emane event service xml file.
@ -440,7 +484,7 @@ def create_event_service_xml(group, port, device, file_directory, server=None):
create_file(event_element, "emaneeventmsgsvc", file_path, server) create_file(event_element, "emaneeventmsgsvc", file_path, server)
def transport_file_name(node_id, transport_type): def transport_file_name(node_id: int, transport_type: str) -> str:
""" """
Create name for a transport xml file. Create name for a transport xml file.
@ -451,10 +495,11 @@ def transport_file_name(node_id, transport_type):
return f"n{node_id}trans{transport_type}.xml" return f"n{node_id}trans{transport_type}.xml"
def _basename(emane_model, interface=None): def _basename(emane_model: "EmaneModel", interface: CoreInterface = None) -> str:
""" """
Create name that is leveraged for configuration file creation. Create name that is leveraged for configuration file creation.
:param emane_model: emane model to create name for
:param interface: interface for this model :param interface: interface for this model
:return: basename used for file creation :return: basename used for file creation
:rtype: str :rtype: str
@ -469,7 +514,7 @@ def _basename(emane_model, interface=None):
return f"{name}{emane_model.name}" return f"{name}{emane_model.name}"
def nem_file_name(emane_model, interface=None): def nem_file_name(emane_model: "EmaneModel", interface: CoreInterface = None) -> str:
""" """
Return the string name for the NEM XML file, e.g. "n3rfpipenem.xml" Return the string name for the NEM XML file, e.g. "n3rfpipenem.xml"
@ -485,7 +530,7 @@ def nem_file_name(emane_model, interface=None):
return f"{basename}nem{append}.xml" return f"{basename}nem{append}.xml"
def shim_file_name(emane_model, interface=None): def shim_file_name(emane_model: "EmaneModel", interface: CoreInterface = None) -> str:
""" """
Return the string name for the SHIM XML file, e.g. "commeffectshim.xml" Return the string name for the SHIM XML file, e.g. "commeffectshim.xml"
@ -498,7 +543,7 @@ def shim_file_name(emane_model, interface=None):
return f"{name}shim.xml" return f"{name}shim.xml"
def mac_file_name(emane_model, interface=None): def mac_file_name(emane_model: "EmaneModel", interface: CoreInterface = None) -> str:
""" """
Return the string name for the MAC XML file, e.g. "n3rfpipemac.xml" Return the string name for the MAC XML file, e.g. "n3rfpipemac.xml"
@ -511,7 +556,7 @@ def mac_file_name(emane_model, interface=None):
return f"{name}mac.xml" return f"{name}mac.xml"
def phy_file_name(emane_model, interface=None): def phy_file_name(emane_model: "EmaneModel", interface: CoreInterface = None) -> str:
""" """
Return the string name for the PHY XML file, e.g. "n3rfpipephy.xml" Return the string name for the PHY XML file, e.g. "n3rfpipephy.xml"