From ae94c78fbbe73e21a312bbe7052203f63f6ab246 Mon Sep 17 00:00:00 2001 From: "Blake J. Harnden" Date: Tue, 3 Jul 2018 12:48:54 -0700 Subject: [PATCH] updated emane model xml to leverage lxml --- daemon/core/emane/bypass.py | 5 +- daemon/core/emane/emanemanager.py | 35 +++++ daemon/core/emane/emanemodel.py | 208 +++++++++++++++++------------- daemon/core/xml/corexml.py | 6 + 4 files changed, 166 insertions(+), 88 deletions(-) diff --git a/daemon/core/emane/bypass.py b/daemon/core/emane/bypass.py index 8f60f238..42340f2b 100644 --- a/daemon/core/emane/bypass.py +++ b/daemon/core/emane/bypass.py @@ -1,6 +1,7 @@ """ EMANE Bypass model for CORE """ +from core.conf import ConfigGroup from core.conf import Configuration from core.emane import emanemodel from core.enumerations import ConfigDataTypes @@ -31,4 +32,6 @@ class EmaneBypassModel(emanemodel.EmaneModel): # override config groups @classmethod def config_groups(cls): - return "Bypass Parameters:1-1" + return [ + ConfigGroup("Bypass Parameters", 1, 1), + ] diff --git a/daemon/core/emane/emanemanager.py b/daemon/core/emane/emanemanager.py index 2f85760b..62acb800 100644 --- a/daemon/core/emane/emanemanager.py +++ b/daemon/core/emane/emanemanager.py @@ -4,6 +4,7 @@ emane.py: definition of an Emane class for implementing configuration control of import os import threading +from lxml import etree from xml.dom.minidom import parseString from core import CoreCommandError @@ -55,6 +56,39 @@ EMANE_MODELS = [ ] +def build_node_platform_xml(node): + platform_element = etree.Element("platform") + + nem_entries = {} + + if node.model is None: + logger.info("warning: EmaneNode %s has no associated model", node.name) + return nem_entries + + for netif in node.netifs(): + # build nem xml + # nementry = node.model.build_nem_xml(doc, node, netif) + nem_name = node.model.nem_name(netif) + nem_element = etree.Element("nem", name=netif.localname, definition=nem_name) + + # build transport xml + # trans = node.model.build_transport_xml(doc, node, netif) + transport_type = netif.transport_type + if not transport_type: + logger.info("warning: %s interface type unsupported!", netif.name) + transport_type = "raw" + transport_name = node.transportxmlname(transport_type) + transport_element = etree.SubElement(nem_element, "transport", definition=transport_name) + + # add transport parameter + etree.SubElement(transport_element, "param", name="device", value=netif.name) + + # add nem entry + nem_entries[netif] = nem_element + + return nem_entries + + class EmaneManager(ModelManager): """ EMANE controller object. Lives in a Session instance and is used for @@ -637,6 +671,7 @@ class EmaneManager(ModelManager): for key in sorted(self._emane_nodes.keys()): emane_node = self._emane_nodes[key] nems = emane_node.buildplatformxmlentry(self.xmldoc("platform")) + for netif in sorted(nems, key=lambda x: x.node.objid): nementry = nems[netif] nementry.setAttribute("id", "%d" % nemid) diff --git a/daemon/core/emane/emanemodel.py b/daemon/core/emane/emanemodel.py index 77aafcec..e69f377b 100644 --- a/daemon/core/emane/emanemodel.py +++ b/daemon/core/emane/emanemodel.py @@ -1,38 +1,102 @@ """ Defines Emane Models used within CORE. """ +import os + +from lxml import etree from core import logger from core.conf import ConfigGroup from core.emane import emanemanifest from core.misc import utils from core.mobility import WirelessModel -from core.xml import xmlutils +from core.xml import corexml -def value_to_params(doc, name, value): +def emane_xml_doctype(name): """ - Helper to convert a parameter to a paramlist. Returns an XML paramlist, or None if the value does not expand to - multiple values. + Create emane xml doctype for xml files. + + :param str name: name for xml doctype + :return: emane xml doctype + :rtype: str + """ + return '' % (name, name) + + +def add_emane_configurations(xml_element, configurations, config, config_ignore): + """ + + + :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 = lxml_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: + etree.SubElement(xml_element, "param", name=name, value=value) + + +def lxml_value_to_params(value): + """ + Helper to convert a parameter to a parameter tuple. - :param xml.dom.minidom.Document doc: xml document - :param name: name of element for params :param str value: value string to convert to tuple - :return: xml document with added params or None, when an invalid value has been provided + :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 + return None - if not hasattr(values, "__iter__"): - return None - if len(values) < 2: - return None - - return xmlutils.add_param_list_to_parent(doc, parent=None, name=name, values=values) +# def value_to_params(doc, name, value): +# """ +# Helper to convert a parameter to a paramlist. Returns an XML paramlist, or None if the value does not expand to +# multiple values. +# +# :param xml.dom.minidom.Document doc: xml document +# :param name: name of element for params +# :param str value: value string to convert to tuple +# :return: xml document with added params or None, when an invalid value has been provided +# """ +# try: +# values = utils.make_tuple_fromstr(value, str) +# except SyntaxError: +# logger.exception("error in value string to param list") +# return None +# +# if not hasattr(values, "__iter__"): +# return None +# +# if len(values) < 2: +# return None +# +# return xmlutils.add_param_list_to_parent(doc, parent=None, name=name, values=values) class EmaneModel(WirelessModel): @@ -88,112 +152,82 @@ class EmaneModel(WirelessModel): return # create document and write to disk - nem_name = self.nem_name(interface) - nem_document = self.create_nem_doc(emane_manager, interface) - emane_manager.xmlwrite(nem_document, nem_name) + self.create_nem_doc(interface) # create mac document and write to disk - mac_name = self.mac_name(interface) - mac_document = self.create_mac_doc(emane_manager, config) - emane_manager.xmlwrite(mac_document, mac_name) + self.create_mac_doc(interface, config) # create phy document and write to disk - phy_name = self.phy_name(interface) - phy_document = self.create_phy_doc(emane_manager, config) - emane_manager.xmlwrite(phy_document, phy_name) + self.create_phy_doc(interface, config) - def create_nem_doc(self, emane_manager, interface): + def create_nem_doc(self, interface): """ Create the nem xml document. - :param core.emane.emanemanager.EmaneManager emane_manager: core emane manager :param interface: interface for the emane node - :return: nem document - :rtype: xml.dom.minidom.Document + :return: nothing """ + nem_element = etree.Element("nem", name="%s NEM" % self.name) + + # add transport + transport_type = "virtual" + if interface and interface.transport_type == "raw": + transport_type = "raw" + transport_type = "n%strans%s.xml" % (self.object_id, transport_type) + etree.SubElement(nem_element, "transport", definition=transport_type) + + # create mac mac_name = self.mac_name(interface) + etree.SubElement(nem_element, "mac", definition=mac_name) + + # create phy phy_name = self.phy_name(interface) + etree.SubElement(nem_element, "phy", definition=phy_name) - nem_document = emane_manager.xmldoc("nem") - nem_element = nem_document.getElementsByTagName("nem").pop() - nem_element.setAttribute("name", "%s NEM" % self.name) - emane_manager.appendtransporttonem(nem_document, nem_element, self.object_id, interface) + # write out xml + nem_name = self.nem_name(interface) + self.create_file(nem_element, nem_name, "nem") - mac_element = nem_document.createElement("mac") - mac_element.setAttribute("definition", mac_name) - nem_element.appendChild(mac_element) - - phy_element = nem_document.createElement("phy") - phy_element.setAttribute("definition", phy_name) - nem_element.appendChild(phy_element) - - return nem_document - - def create_mac_doc(self, emane_manager, config): + def create_mac_doc(self, interface, config): """ Create the mac xml document. - :param core.emane.emanemanager.EmaneManager emane_manager: core emane manager + :param interface: interface for the emane node :param dict config: all current configuration values, mac + phy - :return: nem document - :rtype: xml.dom.minidom.Document + :return: nothing """ - mac_document = emane_manager.xmldoc("mac") - mac_element = mac_document.getElementsByTagName("mac").pop() - mac_element.setAttribute("name", "%s MAC" % self.name) - if not self.mac_library: raise ValueError("must define emane model library") - mac_element.setAttribute("library", self.mac_library) - for mac_configuration in self.mac_config: - # ignore custom configurations - name = mac_configuration.id - if name in self.config_ignore: - continue + mac_element = etree.Element("mac", name="%s MAC" % self.name, library=self.mac_library) + add_emane_configurations(mac_element, self.mac_config, config, self.config_ignore) - # check if value is a multi param - value = str(config[name]) - param = value_to_params(mac_document, name, value) - if not param: - param = emane_manager.xmlparam(mac_document, name, value) + # write out xml + mac_name = self.mac_name(interface) + self.create_file(mac_element, mac_name, "mac") - mac_element.appendChild(param) - - return mac_document - - def create_phy_doc(self, emane_manager, config): + def create_phy_doc(self, interface, config): """ Create the phy xml document. - :param core.emane.emanemanager.EmaneManager emane_manager: core emane manager + :param interface: interface for the emane node :param dict config: all current configuration values, mac + phy - :return: nem document - :rtype: xml.dom.minidom.Document + :return: nothing """ - phy_document = emane_manager.xmldoc("phy") - phy_element = phy_document.getElementsByTagName("phy").pop() - phy_element.setAttribute("name", "%s PHY" % self.name) - + phy_element = etree.Element("phy", name="%s PHY" % self.name) if self.phy_library: - phy_element.setAttribute("library", self.phy_library) + phy_element.set("library", self.phy_library) - # append all phy options - for phy_configuration in self.phy_config: - # ignore custom configurations - name = phy_configuration.id - if name in self.config_ignore: - continue + add_emane_configurations(phy_element, self.phy_config, config, self.config_ignore) - # check if value is a multi param - value = str(config[name]) - param = value_to_params(phy_document, name, value) - if not param: - param = emane_manager.xmlparam(phy_document, name, value) + # write out xml + phy_name = self.phy_name(interface) + self.create_file(phy_element, phy_name, "phy") - phy_element.appendChild(param) - - return phy_document + def create_file(self, xml_element, file_name, doc_name): + file_path = os.path.join(self.session.session_dir, file_name) + doctype = emane_xml_doctype(doc_name) + corexml.write_xml_file(xml_element, file_path, doctype=doctype) def post_startup(self): """ diff --git a/daemon/core/xml/corexml.py b/daemon/core/xml/corexml.py index 07516762..551a75e0 100644 --- a/daemon/core/xml/corexml.py +++ b/daemon/core/xml/corexml.py @@ -12,6 +12,12 @@ from core.misc.ipaddress import MacAddress from core.netns import nodes +def write_xml_file(xml_element, file_path, doctype=None): + xml_data = etree.tostring(xml_element, xml_declaration=True, pretty_print=True, encoding="UTF-8", doctype=doctype) + with open(file_path, "w") as xml_file: + xml_file.write(xml_data) + + def get_type(element, name, _type): value = element.get(name) if value is not None: