diff --git a/hypervisor/scripts/makefile/config.mk b/hypervisor/scripts/makefile/config.mk index 2c224d719..139565bac 100644 --- a/hypervisor/scripts/makefile/config.mk +++ b/hypervisor/scripts/makefile/config.mk @@ -113,6 +113,7 @@ HV_UNIFIED_XML_IN := $(BASEDIR)/scripts/makefile/unified.xml.in HV_PREDEFINED_DATA_DIR := $(realpath $(BASEDIR)/../misc/config_tools/data) HV_CONFIG_TOOL_DIR := $(realpath $(BASEDIR)/../misc/config_tools) HV_CONFIG_XFORM_DIR := $(HV_CONFIG_TOOL_DIR)/xforms +HV_SCENARIO_XSD := $(HV_CONFIG_TOOL_DIR)/schema/config.xsd # Paths to the outputs: HV_CONFIG_DIR := $(HV_OBJDIR)/configs @@ -208,7 +209,7 @@ $(HV_SCENARIO_XML): if [ -f $(SCENARIO_FILE) ]; then \ echo "Scneario XML is configuration fetched from $(realpath $(SCENARIO_FILE))"; \ mkdir -p $(dir $(HV_SCENARIO_XML)); \ - cp $(SCENARIO_FILE) $(HV_SCENARIO_XML); \ + python3 $(HV_CONFIG_TOOL_DIR)/scenario_config/default_populator.py $(HV_SCENARIO_XSD) $(SCENARIO_FILE) $(HV_SCENARIO_XML); \ else \ echo "No pre-defined scenario available at $(SCENARIO_FILE)"; \ echo "Try setting another predefined BOARD or SCENARIO or specifying a scenario XML file"; \ diff --git a/misc/config_tools/scenario_config/default_populator.py b/misc/config_tools/scenario_config/default_populator.py new file mode 100755 index 000000000..597b82be6 --- /dev/null +++ b/misc/config_tools/scenario_config/default_populator.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 Intel Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +import argparse +import lxml.etree as etree + +xpath_ns = { + "xs": "http://www.w3.org/2001/XMLSchema", +} + +def get_node(element, xpath): + return next(iter(element.xpath(xpath, namespaces=xpath_ns)), None) + +def populate_sequence(xsd_etree, xsd_sequence_node, xml_node): + children = list(xml_node) + for element_node in xsd_sequence_node.findall("xs:element", namespaces=xpath_ns): + element_name = element_node.get("name") + element_min_occurs = element_node.get("minOccurs") + + if len(children) == 0: + if element_min_occurs != "0": + xml_child_node = etree.Element(element_name) + xml_node.append(xml_child_node) + populate(xsd_etree, element_node, xml_child_node, True) + elif children[0].tag != element_name: + if element_min_occurs != "0": + xml_child_node = etree.Element(element_name) + children[0].addprevious(xml_child_node) + populate(xsd_etree, element_node, xml_child_node, True) + else: + while len(children) > 0 and children[0].tag == element_name: + child_node = children.pop(0) + populate(xsd_etree, element_node, child_node, False) + +def populate_all(xsd_etree, xsd_all_node, xml_node): + for element_node in xsd_all_node.findall("xs:element", namespaces=xpath_ns): + element_name = element_node.get("name") + if not element_name: + continue + xml_child_node = xml_node.find(element_name) + if xml_child_node is None: + element_min_occurs = element_node.get("minOccurs") + if element_min_occurs == "0": + continue + + xml_child_node = etree.Element(element_name) + xml_node.append(xml_child_node) + populate(xsd_etree, element_node, xml_child_node, True) + else: + populate(xsd_etree, element_node, xml_child_node, False) + +def populate(xsd_etree, xsd_element_node, xml_node, is_new_node): + complex_type_node = xsd_element_node.find("xs:complexType", namespaces=xpath_ns) + if not complex_type_node: + type_name = xsd_element_node.get("type") + if type_name: + complex_type_node = get_node(xsd_etree, f"//xs:complexType[@name='{type_name}']") + + if complex_type_node is not None: + sequence_node = complex_type_node.find("xs:sequence", namespaces=xpath_ns) + if sequence_node is not None: + populate_sequence(xsd_etree, sequence_node, xml_node) + + all_node = complex_type_node.find("xs:all", namespaces=xpath_ns) + if all_node is not None: + populate_all(xsd_etree, all_node, xml_node) + elif is_new_node: + default_value = xsd_element_node.get("default") + xml_node.text = default_value + +def main(xsd_file, xml_file, out_file): + xml_etree = etree.parse(xml_file, etree.XMLParser(remove_blank_text=True)) + xsd_etree = etree.parse(xsd_file) + xsd_etree.xinclude() + + xml_root = xml_etree.getroot() + xsd_root = get_node(xsd_etree, f"/xs:schema/xs:element[@name='{xml_root.tag}']") + + if xsd_root is not None: + populate(xsd_etree, xsd_root, xml_root, False) + + xml_etree.write(out_file, pretty_print=True) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Populate a given scenario XML with default values of nonexistent nodes") + parser.add_argument("xsd", help="Path to the schema of scenario XMLs") + parser.add_argument("xml", help="Path to the scenario XML file from users") + parser.add_argument("out", default="out.xml", help="Path where the output is placed") + args = parser.parse_args() + + main(args.xsd, args.xml, args.out)