import os from lxml import etree from core import logger from core.misc import utils from core.misc.ipaddress import MacAddress from core.xml import corexml _hwaddr_prefix = "02:02" def is_external(config): """ Checks if the configuration is for an external transport. :param dict config: configuration to check :return: True if external, False otherwise :rtype: bool """ return config.get("external") == "1" def _value_to_params(value): """ Helper to convert a parameter to a parameter tuple. :param str value: value string to convert to tuple :return: parameter tuple, None otherwise """ try: values = utils.make_tuple_fromstr(value, str) if not hasattr(values, "__iter__"): return None if len(values) < 2: return None return values except SyntaxError: logger.exception("error in value string to param list") return None def create_file(xml_element, doc_name, file_path): """ Create xml file. :param lxml.etree.Element xml_element: root element to write to file :param str doc_name: name to use in the emane doctype :param str file_path: file path to write xml file to :return: nothing """ doctype = '' % {"doc_name": doc_name} corexml.write_xml_file(xml_element, file_path, doctype=doctype) def add_param(xml_element, name, value): """ Add emane configuration parameter to xml element. :param lxml.etree.Element xml_element: element to append parameter to :param str name: name of parameter :param str value: value for parameter :return: nothing """ etree.SubElement(xml_element, "param", name=name, value=value) def add_configurations(xml_element, configurations, config, config_ignore): """ Add emane model configurations to xml element. :param lxml.etree.Element xml_element: xml element to add emane configurations to :param list[core.config.Configuration] configurations: configurations to add to xml :param dict config: configuration values :param set config_ignore: configuration options to ignore :return: """ for configuration in configurations: # ignore custom configurations name = configuration.id if name in config_ignore: continue # check if value is a multi param value = str(config[name]) params = _value_to_params(value) if params: params_element = etree.SubElement(xml_element, "paramlist", name=name) for param in params: etree.SubElement(params_element, "item", value=param) else: add_param(xml_element, name, value) def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_xmls): """ Create platform xml for a specific node. :param core.emane.emanemanager.EmaneManager emane_manager: emane manager with emane configurations :param core.netns.nodes.CtrlNet control_net: control net node for this emane network :param core.emane.nodes.EmaneNode node: node to write platform xml for :param int nem_id: nem id to use for interfaces for this node :param dict platform_xmls: stores platform xml elements to append nem entries to :return: the next nem id that can be used for creating platform xml files :rtype: int """ logger.debug("building emane platform xml for node(%s): %s", node, node.name) nem_entries = {} if node.model is None: logger.warn("warning: EmaneNode %s has no associated model", node.name) return nem_entries for netif in node.netifs(): # build nem xml nem_definition = nem_file_name(node.model, netif) nem_element = etree.Element("nem", id=str(nem_id), name=netif.localname, definition=nem_definition) # check if this is an external transport, get default config if an interface specific one does not exist config = emane_manager.getifcconfig(node.model.object_id, netif, node.model.name) if is_external(config): nem_element.set("transport", "external") platform_endpoint = "platformendpoint" add_param(nem_element, platform_endpoint, config[platform_endpoint]) transport_endpoint = "transportendpoint" add_param(nem_element, transport_endpoint, config[transport_endpoint]) else: # build transport xml transport_type = netif.transport_type if not transport_type: logger.info("warning: %s interface type unsupported!", netif.name) transport_type = "raw" transport_file = transport_file_name(node.objid, transport_type) transport_element = etree.SubElement(nem_element, "transport", definition=transport_file) # add transport parameter add_param(transport_element, "device", netif.name) # add nem entry nem_entries[netif] = nem_element # merging code key = netif.node.objid if netif.transport_type == "raw": key = "host" otadev = control_net.brname eventdev = control_net.brname else: otadev = None eventdev = None platform_element = platform_xmls.get(key) if platform_element is None: platform_element = etree.Element("platform") if otadev: emane_manager.set_config("otamanagerdevice", otadev) if eventdev: emane_manager.set_config("eventservicedevice", eventdev) # append all platform options (except starting id) to doc for configuration in emane_manager.emane_config.emulator_config: name = configuration.id if name == "platform_id_start": continue value = emane_manager.get_config(name) add_param(platform_element, name, value) # add platform xml platform_xmls[key] = platform_element platform_element.append(nem_element) node.setnemid(netif, nem_id) macstr = _hwaddr_prefix + ":00:00:" macstr += "%02X:%02X" % ((nem_id >> 8) & 0xFF, nem_id & 0xFF) netif.sethwaddr(MacAddress.from_string(macstr)) # increment nem id nem_id += 1 for key in sorted(platform_xmls.keys()): if key == "host": file_name = "platform.xml" else: file_name = "platform%d.xml" % key platform_element = platform_xmls[key] doc_name = "platform" file_path = os.path.join(emane_manager.session.session_dir, file_name) create_file(platform_element, doc_name, file_path) return nem_id def build_xml_files(emane_manager, node): """ Generate emane xml files required for node. :param core.emane.emanemanager.EmaneManager emane_manager: emane manager with emane configurations :param core.emane.nodes.EmaneNode node: node to write platform xml for :return: nothing """ logger.debug("building all emane xml for node(%s): %s", node, node.name) if node.model is None: return # get model configurations config = emane_manager.get_configs(node.model.object_id, node.model.name) if not config: return # build XML for overall network (EmaneNode) configs node.model.build_xml_files(config) # build XML for specific interface (NEM) configs need_virtual = False need_raw = False vtype = "virtual" rtype = "raw" for netif in node.netifs(): # check for interface specific emane configuration and write xml files, if needed config = emane_manager.getifcconfig(node.model.object_id, netif, node.model.name) if config: node.model.build_xml_files(config, netif) # check transport type needed for interface if "virtual" in netif.transport_type: need_virtual = True vtype = netif.transport_type else: need_raw = True rtype = netif.transport_type if need_virtual: build_transport_xml(emane_manager, node, vtype) if need_raw: build_transport_xml(emane_manager, node, rtype) def build_transport_xml(emane_manager, node, transport_type): """ Build transport xml file for node and transport type. :param core.emane.emanemanager.EmaneManager emane_manager: emane manager with emane configurations :param core.emane.nodes.EmaneNode node: node to write platform xml for :param str transport_type: transport type to build xml for :return: nothing """ transport_element = etree.Element( "transport", name="%s Transport" % transport_type.capitalize(), library="trans%s" % transport_type.lower() ) # add bitrate add_param(transport_element, "bitrate", "0") # get emane model cnfiguration config = emane_manager.get_configs(node.objid, node.model.name) flowcontrol = config.get("flowcontrolenable", "0") == "1" if "virtual" in transport_type.lower(): device_path = "/dev/net/tun_flowctl" if not os.path.exists(device_path): device_path = "/dev/net/tun" add_param(transport_element, "devicepath", device_path) if flowcontrol: add_param(transport_element, "flowcontrolenable", "on") doc_name = "transport" file_name = transport_file_name(node.objid, transport_type) file_path = os.path.join(emane_manager.session.session_dir, file_name) create_file(transport_element, doc_name, file_path) def create_phy_xml(emane_model, config, file_path): """ Create the phy xml document. :param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for :param dict config: all current configuration values :param str file_path: path to write file to :return: nothing """ phy_element = etree.Element("phy", name="%s PHY" % emane_model.name) if emane_model.phy_library: phy_element.set("library", emane_model.phy_library) add_configurations(phy_element, emane_model.phy_config, config, emane_model.config_ignore) create_file(phy_element, "phy", file_path) def create_mac_xml(emane_model, config, file_path): """ Create the mac xml document. :param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for :param dict config: all current configuration values :param str file_path: path to write file to :return: nothing """ if not emane_model.mac_library: raise ValueError("must define emane model library") mac_element = etree.Element("mac", name="%s MAC" % emane_model.name, library=emane_model.mac_library) add_configurations(mac_element, emane_model.mac_config, config, emane_model.config_ignore) create_file(mac_element, "mac", file_path) def create_nem_xml(emane_model, config, nem_file, transport_definition, mac_definition, phy_definition): """ Create the nem xml document. :param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for :param dict config: all current configuration values :param str nem_file: nem file path to write :param str transport_definition: transport file definition path :param str mac_definition: mac file definition path :param str phy_definition: phy file definition path :return: nothing """ nem_element = etree.Element("nem", name="%s NEM" % emane_model.name) if is_external(config): nem_element.set("type", "unstructured") else: etree.SubElement(nem_element, "transport", definition=transport_definition) etree.SubElement(nem_element, "mac", definition=mac_definition) etree.SubElement(nem_element, "phy", definition=phy_definition) create_file(nem_element, "nem", nem_file) def create_event_service_xml(group, port, device, file_directory): """ Create a emane event service xml file. :param str group: event group :param str port: event port :param str device: event device :param str file_directory: directory to create file in :return: nothing """ event_element = etree.Element("emaneeventmsgsvc") for name, value in (("group", group), ("port", port), ("device", device), ("mcloop", "1"), ("ttl", "32")): sub_element = etree.SubElement(event_element, name) sub_element.text = value file_name = "libemaneeventservice.xml" file_path = os.path.join(file_directory, file_name) create_file(event_element, "emaneeventmsgsvc", file_path) def transport_file_name(node_id, transport_type): """ Create name for a transport xml file. :param int node_id: node id to generate transport file name for :param str transport_type: transport type to generate transport file :return: """ return "n%strans%s.xml" % (node_id, transport_type) def _basename(emane_model, interface=None): """ Create name that is leveraged for configuration file creation. :param interface: interface for this model :return: basename used for file creation :rtype: str """ name = "n%s" % emane_model.object_id if interface: node_id = interface.node.objid if emane_model.session.emane.getifcconfig(node_id, interface, emane_model.name): name = interface.localname.replace(".", "_") return "%s%s" % (name, emane_model.name) def nem_file_name(emane_model, interface=None): """ Return the string name for the NEM XML file, e.g. "n3rfpipenem.xml" :param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for :param interface: interface for this model :return: nem xml filename :rtype: str """ basename = _basename(emane_model, interface) append = "" if interface and interface.transport_type == "raw": append = "_raw" return "%snem%s.xml" % (basename, append) def shim_file_name(emane_model, interface=None): """ Return the string name for the SHIM XML file, e.g. "commeffectshim.xml" :param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for :param interface: interface for this model :return: shim xml filename :rtype: str """ return "%sshim.xml" % _basename(emane_model, interface) def mac_file_name(emane_model, interface=None): """ Return the string name for the MAC XML file, e.g. "n3rfpipemac.xml" :param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for :param interface: interface for this model :return: mac xml filename :rtype: str """ return "%smac.xml" % _basename(emane_model, interface) def phy_file_name(emane_model, interface=None): """ Return the string name for the PHY XML file, e.g. "n3rfpipephy.xml" :param core.emane.emanemodel.EmaneModel emane_model: emane model to create phy xml for :param interface: interface for this model :return: phy xml filename :rtype: str """ return "%sphy.xml" % _basename(emane_model, interface)