import os import socket from typing import TYPE_CHECKING, List, Tuple import netaddr from lxml import etree from core import utils from core.constants import IP_BIN from core.emane.nodes import EmaneNet 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: etree.Element, name: str) -> None: type_element = etree.SubElement(parent_element, "type") type_element.text = name 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.text = address if interface_name is not None: address_element.set("iface", interface_name) def add_mapping(parent_element: etree.Element, maptype: str, mapref: str) -> None: etree.SubElement(parent_element, "mapping", type=maptype, ref=mapref) 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] host_id = host_element.get("id") # platform data platform_id = f"{host_id}/{platform_name}" platform_element = etree.SubElement( host_element, "emanePlatform", id=platform_id, name=platform_name ) # transport data transport_id = f"{host_id}/{transport_name}" etree.SubElement( platform_element, "transport", id=transport_id, name=transport_name ) # nem data nem_name = f"nem{nem_id}" nem_element_id = f"{host_id}/{nem_name}" nem_element = etree.SubElement( platform_element, "nem", id=nem_element_id, name=nem_name ) nem_id_element = etree.SubElement(nem_element, "parameter", name="nemid") nem_id_element.text = str(nem_id) return platform_element def get_address_type(address: str) -> str: addr, _slash, _prefixlen = address.partition("/") if netaddr.valid_ipv4(addr): address_type = "IPv4" elif netaddr.valid_ipv6(addr): address_type = "IPv6" else: raise NotImplementedError return address_type def get_ipv4_addresses(hostname: str) -> List[Tuple[str, str]]: if hostname == "localhost": addresses = [] args = f"{IP_BIN} -o -f inet address show" output = utils.cmd(args) for line in output.split(os.linesep): split = line.split() if not split: continue interface_name = split[1] address = split[3] if not address.startswith("127."): addresses.append((interface_name, address)) return addresses else: # TODO: handle other hosts raise NotImplementedError class CoreXmlDeployment: def __init__(self, session: "Session", scenario: etree.Element) -> None: self.session = session self.scenario = scenario self.root = etree.SubElement( scenario, "container", id="TestBed", name="TestBed" ) self.add_deployment() def find_device(self, name: str) -> etree.Element: device = self.scenario.find(f"devices/device[@name='{name}']") return device def find_interface(self, device: NodeBase, name: str) -> etree.Element: interface = self.scenario.find( f"devices/device[@name='{device.name}']/interfaces/interface[@name='{name}']" ) return interface def add_deployment(self) -> None: physical_host = self.add_physical_host(socket.gethostname()) for node_id in self.session.nodes: node = self.session.nodes[node_id] if isinstance(node, CoreNodeBase): self.add_virtual_host(physical_host, node) def add_physical_host(self, name: str) -> etree.Element: # add host root_id = self.root.get("id") host_id = f"{root_id}/{name}" host_element = etree.SubElement(self.root, "testHost", id=host_id, name=name) # add type element add_type(host_element, "physical") # add ipv4 addresses for interface_name, address in get_ipv4_addresses("localhost"): add_address(host_element, "IPv4", address, interface_name) return host_element def add_virtual_host(self, physical_host: etree.Element, node: NodeBase) -> None: if not isinstance(node, CoreNodeBase): raise TypeError(f"invalid node type: {node}") # create virtual host element phys_id = physical_host.get("id") host_id = f"{phys_id}/{node.name}" host_element = etree.SubElement( physical_host, "testHost", id=host_id, name=node.name ) # add host type add_type(host_element, "virtual") for netif in node.netifs(): emane_element = None if isinstance(netif.net, EmaneNet): emane_element = add_emane_interface(host_element, netif) parent_element = host_element if emane_element is not None: parent_element = emane_element for address in netif.addrlist: address_type = get_address_type(address) add_address(parent_element, address_type, address, netif.name)