[Goldeneye] ONOS-3939 Implementing Yang Sb XML utils

Change-Id: Ibf435f4c8e967ab793ec4a6d882df82b790f554f
diff --git a/drivers/utilities/src/main/java/org/onosproject/drivers/utilities/YangElement.java b/drivers/utilities/src/main/java/org/onosproject/drivers/utilities/YangElement.java
new file mode 100644
index 0000000..7ad0b21
--- /dev/null
+++ b/drivers/utilities/src/main/java/org/onosproject/drivers/utilities/YangElement.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.utilities;
+
+import com.google.common.base.MoreObjects;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Class that contains the element base key and a map with all the values to
+ * set or retrieved with their relative key.
+ */
+public class YangElement {
+
+    private final String baseKey;
+    private final Map<String, String> keysAndValues;
+
+    public YangElement(String baseKey, Map<String, String> keysAndValues) {
+        this.baseKey = baseKey;
+        this.keysAndValues = keysAndValues;
+    }
+
+    public Map<String, String> getKeysAndValues() {
+        return keysAndValues;
+    }
+
+    public String getBaseKey() {
+        return baseKey;
+    }
+
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("baseKey", baseKey)
+                .add("keysAndValues", keysAndValues)
+                .toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        YangElement element = (YangElement) o;
+
+        if (baseKey != null ? !baseKey.equals(element.baseKey) : element.baseKey != null) {
+            return false;
+        }
+        return (keysAndValues == null ? element.keysAndValues == null :
+                keysAndValues.equals(element.keysAndValues));
+
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(baseKey, keysAndValues);
+    }
+}
diff --git a/drivers/utilities/src/main/java/org/onosproject/drivers/utilities/YangXmlUtils.java b/drivers/utilities/src/main/java/org/onosproject/drivers/utilities/YangXmlUtils.java
new file mode 100644
index 0000000..86da6f8
--- /dev/null
+++ b/drivers/utilities/src/main/java/org/onosproject/drivers/utilities/YangXmlUtils.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.utilities;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.commons.configuration.tree.ConfigurationNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import static org.onlab.util.Tools.nullIsNotFound;
+
+/**
+ * Util CLass for Yang models.
+ * Clean abstraction to read, obtain and populate
+ * XML from Yang models translated into XML skeletons.
+ */
+public class YangXmlUtils {
+
+    public final Logger log = LoggerFactory
+            .getLogger(getClass());
+
+    private static YangXmlUtils instance = null;
+
+    //no instantiation, single instance.
+    protected YangXmlUtils() {
+
+    }
+
+    /**
+     * Retrieves a valid XML configuration for a specific XML path for a single
+     * instance of the Map specified key-value pairs.
+     *
+     * @param file   path of the file to be used.
+     * @param values map of key and values to set under the generic path.
+     * @return Hierarchical configuration containing XML with values.
+     */
+    public XMLConfiguration getXmlConfiguration(String file, Map<String, String> values) {
+        InputStream stream = getCfgInputStream(file);
+        XMLConfiguration cfg = loadXml(stream);
+        XMLConfiguration complete = new XMLConfiguration();
+        List<String> paths = new ArrayList<>();
+        Map<String, String> valuesWithKey = new HashMap<>();
+        values.keySet().stream().forEach(path -> {
+            List<String> allPaths = findPaths(cfg, path);
+            String key = nullIsNotFound(allPaths.isEmpty() ? null : allPaths.get(0),
+                                        "Yang model does not contain desired path");
+            paths.add(key);
+            valuesWithKey.put(key, values.get(path));
+        });
+        Collections.sort(paths, new StringLengthComparator());
+        paths.forEach(key -> complete.setProperty(key, valuesWithKey.get(key)));
+        addProperties(cfg, complete);
+        return complete;
+    }
+
+
+    /**
+     * Retrieves a valid XML configuration for a specific XML path for multiple
+     * instance of YangElements objects.
+     *
+     * @param file     path of the file to be used.
+     * @param elements List of YangElements that are to be set.
+     * @return Hierachical configuration containing XML with values.
+     */
+    public XMLConfiguration getXmlConfiguration(String file, List<YangElement> elements) {
+        InputStream stream = getCfgInputStream(file);
+        HierarchicalConfiguration cfg = loadXml(stream);
+        XMLConfiguration complete = new XMLConfiguration();
+        Multimap<String, YangElement> commonElements = ArrayListMultimap.create();
+
+        //saves the elements in a Multimap based on the computed key.
+        elements.forEach(element -> {
+            String completeKey = nullIsNotFound(findPath(cfg, element.getBaseKey()),
+                                                "Yang model does not contain desired path");
+            commonElements.put(completeKey, element);
+        });
+
+        //iterates over the elements and constructs the configuration
+        commonElements.keySet().forEach(key -> {
+            // if there is more than one element for a given path
+            if (commonElements.get(key).size() > 1) {
+                //creates a list of nodes that have to be added for that specific path
+                ArrayList<ConfigurationNode> nodes = new ArrayList<>();
+                //creates the nodes
+                commonElements.get(key).forEach(element -> nodes.add(getInnerNode(element).getRootNode()));
+                //computes the parent path
+                String parentPath = key.substring(0, key.lastIndexOf("."));
+                //adds the nodes to the complete configuration
+                complete.addNodes(parentPath, nodes);
+            } else {
+                //since there is only a single element we can assume it's the first one.
+                Map<String, String> keysAndValues = commonElements.get(key).stream().
+                        findFirst().get().getKeysAndValues();
+                keysAndValues.forEach((k, v) -> complete.setProperty(key + "." + k, v));
+            }
+        });
+        addProperties(cfg, complete);
+        return complete;
+    }
+
+    //Adds all the properties of the original configuration to the new one.
+    private void addProperties(HierarchicalConfiguration cfg, HierarchicalConfiguration complete) {
+        cfg.getKeys().forEachRemaining(key -> {
+            String property = (String) cfg.getProperty(key);
+            if (!property.equals("")) {
+                complete.setProperty(key, property);
+            }
+        });
+    }
+
+    protected InputStream getCfgInputStream(String file) {
+        return getClass().getResourceAsStream(file);
+    }
+
+    /**
+     * Reads a valid XML configuration and returns a Map containing XML field name.
+     * and value contained for every subpath.
+     *
+     * @param cfg the Configuration to read.
+     * @param path path of the information to be read.
+     * @return list of elements containing baskey and map of key value pairs.
+     */
+    public List<YangElement> readXmlConfiguration(HierarchicalConfiguration cfg, String path) {
+        List<YangElement> elements = new ArrayList<>();
+
+        String key = nullIsNotFound(findPath(cfg, path), "Configuration does not contain desired path");
+
+        getElements(cfg.configurationsAt(key), elements, key, cfg, path, key);
+        return ImmutableList.copyOf(elements);
+    }
+
+    private void getElements(List<HierarchicalConfiguration> configurations,
+                             List<YangElement> elements, String basekey,
+                             HierarchicalConfiguration originalCfg, String path,
+                             String originalKey) {
+        //consider each sub configuration
+        configurations.stream().forEach(config -> {
+
+            YangElement element = new YangElement(path, new HashMap<>());
+            //for each of the keys of the sub configuration
+            config.getKeys().forEachRemaining(key -> {
+                //considers only one step ahead
+                //if one step ahead has other steps calls self to analize them
+                //else adds to yang element.
+                if (key.split("\\.").length > 1) {
+                    getElements(originalCfg.configurationsAt(basekey + "." + key.split("\\.")[0]),
+                                elements, basekey + "." + key.split("\\.")[0], originalCfg, path,
+                                originalKey);
+                } else {
+                    String replaced = basekey.replace(originalKey, "");
+                    String partialKey = replaced.isEmpty() ? key : replaced.substring(1) + "." + key;
+                    partialKey = partialKey.isEmpty() ? originalKey : partialKey;
+                    //Adds values to the element with a subkey starting from the requeste path onwards
+                    element.getKeysAndValues().put(partialKey, config.getProperty(key).toString());
+                }
+            });
+            //if the element doesnt already exist
+            if (!elements.contains(element) && !element.getKeysAndValues().isEmpty()) {
+                elements.add(element);
+            }
+        });
+    }
+
+    /**
+     * Single Instance of Yang utilities retriever.
+     *
+     * @return instance of YangXmlUtils
+     */
+    public static YangXmlUtils getInstance() {
+        if (instance == null) {
+            instance = new YangXmlUtils();
+        }
+        return instance;
+    }
+
+    /**
+     * Return the string representation of the XMLConfig without header
+     * and configuration element.
+     *
+     * @param cfg the XML to convert
+     * @return the cfg string.
+     */
+    public String getString(XMLConfiguration cfg) {
+        StringWriter stringWriter = new StringWriter();
+        try {
+            cfg.save(stringWriter);
+        } catch (ConfigurationException e) {
+            log.error("Cannot convert configuration", e.getMessage());
+        }
+        String xml = stringWriter.toString();
+        xml = xml.substring(xml.indexOf("\n"));
+        xml = xml.substring(xml.indexOf(">") + 1);
+        return xml;
+    }
+
+    /**
+     * Method to read an input stream into a XMLConfiguration.
+     * @param xmlStream inputstream containing XML description
+     * @return the XMLConfiguration object
+     */
+    public XMLConfiguration loadXml(InputStream xmlStream) {
+        XMLConfiguration cfg = new XMLConfiguration();
+        try {
+            cfg.load(xmlStream);
+            return cfg;
+        } catch (ConfigurationException e) {
+            throw new IllegalArgumentException("Cannot load xml from Stream", e);
+        }
+    }
+
+    //Finds all paths for a corresponding element
+    private List<String> findPaths(HierarchicalConfiguration cfg, String path) {
+        List<String> paths = new ArrayList<>();
+        cfg.getKeys().forEachRemaining(key -> {
+            if (key.equals(path)) {
+                paths.add(key);
+            }
+            if (key.contains("." + path)) {
+                paths.add(key);
+            }
+        });
+        return paths;
+    }
+
+    //Finds the first parent path corresponding to an element.
+    private String findPath(HierarchicalConfiguration cfg, String element) {
+        Iterator<String> it = cfg.getKeys();
+        while (it.hasNext()) {
+            String key = it.next();
+            String[] arr = key.split("\\.");
+            for (int i = 0; i < arr.length; i++) {
+                if (element.equals(arr[i])) {
+                    String completeKey = "";
+                    for (int j = 0; j <= i; j++) {
+                        completeKey = completeKey + "." + arr[j];
+                    }
+                    return completeKey.substring(1);
+                }
+            }
+        }
+        return null;
+    }
+
+    //creates a node based on a single Yang element.
+    private HierarchicalConfiguration getInnerNode(YangElement element) {
+        HierarchicalConfiguration node = new HierarchicalConfiguration();
+        node.setRoot(new HierarchicalConfiguration.Node(element.getBaseKey()));
+        element.getKeysAndValues().forEach(node::setProperty);
+        return node;
+    }
+
+    //String lenght comparator
+    private class StringLengthComparator implements Comparator<String> {
+
+        public int compare(String o1, String o2) {
+            if (o2 == null && o1 == null) {
+                return 0;
+            }
+
+            if (o1 == null) {
+                return o2.length();
+            }
+
+            if (o2 == null) {
+                return o1.length();
+            }
+
+            if (o1.length() != o2.length()) {
+                return o1.length() - o2.length(); //overflow impossible since lengths are non-negative
+            }
+            return o1.compareTo(o2);
+        }
+    }
+
+}
diff --git a/drivers/utilities/src/test/java/org/onosproject/drivers/utilities/YangXmlUtilsTest.java b/drivers/utilities/src/test/java/org/onosproject/drivers/utilities/YangXmlUtilsTest.java
new file mode 100644
index 0000000..0d6f30e
--- /dev/null
+++ b/drivers/utilities/src/test/java/org/onosproject/drivers/utilities/YangXmlUtilsTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.utilities;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.collections.IteratorUtils;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.behaviour.ControllerInfo;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests for the XMLYangUtils.
+ */
+public class YangXmlUtilsTest {
+    public static final String OF_CONFIG_XML_PATH = "/of-config/of-config.xml";
+    private YangXmlUtilsAdap utils;
+    private XMLConfiguration testCreateConfig;
+
+    @Before
+    public void setUp() throws Exception {
+        assertTrue("No resource for test", YangXmlUtilsTest.class.
+                getResourceAsStream("/of-config/of-config.xml") != null);
+        utils = new YangXmlUtilsAdap();
+
+        testCreateConfig = new XMLConfiguration();
+    }
+
+    /**
+     * Tests getting a single object configuration via passing the path and the map of the desired values.
+     *
+     * @throws ConfigurationException if the testing xml file is not there.
+     */
+    @Test
+    public void testGetXmlUtilsInstance() throws ConfigurationException {
+
+        YangXmlUtils instance1 = YangXmlUtils.getInstance();
+        YangXmlUtils instance2 = YangXmlUtils.getInstance();
+
+        assertEquals("Duplicate instance", instance1, instance2);
+
+    }
+
+    /**
+     * Tests getting a single object configuration via passing the path and the map of the desired values.
+     *
+     * @throws ConfigurationException if the testing xml file is not there.
+     */
+    @Test
+    public void testGetXmlConfigurationFromMap() throws ConfigurationException {
+        Map<String, String> pathAndValues = new HashMap<>();
+        pathAndValues.put("capable-switch.id", "openvswitch");
+        pathAndValues.put("switch.id", "ofc-bridge");
+        pathAndValues.put("controller.id", "tcp:1.1.1.1:1");
+        pathAndValues.put("controller.ip-address", "1.1.1.1");
+        XMLConfiguration cfg = utils.getXmlConfiguration(OF_CONFIG_XML_PATH, pathAndValues);
+        testCreateConfig.load(getClass().getResourceAsStream("/testCreateSingleYangConfig.xml"));
+        assertNotEquals("Null testConfiguration", new XMLConfiguration(), testCreateConfig);
+
+        assertEquals("Wrong configuaration", IteratorUtils.toList(testCreateConfig.getKeys()),
+                     IteratorUtils.toList(cfg.getKeys()));
+
+        assertEquals("Wrong string configuaration", utils.getString(testCreateConfig),
+                     utils.getString(cfg));
+    }
+
+    /**
+     * Tests getting a multiple object nested configuration via passing the path
+     * and a list of YangElements containing with the element and desired value.
+     *
+     * @throws ConfigurationException
+     */
+    @Test
+    public void getXmlConfigurationFromYangElements() throws ConfigurationException {
+
+        assertNotEquals("Null testConfiguration", new XMLConfiguration(), testCreateConfig);
+        testCreateConfig.load(getClass().getResourceAsStream("/testYangConfig.xml"));
+        List<YangElement> elements = new ArrayList<>();
+        elements.add(new YangElement("capable-switch", ImmutableMap.of("id", "openvswitch")));
+        elements.add(new YangElement("switch", ImmutableMap.of("id", "ofc-bridge")));
+        List<ControllerInfo> controllers =
+                ImmutableList.of(new ControllerInfo(IpAddress.valueOf("1.1.1.1"), 1, "tcp"),
+                                 new ControllerInfo(IpAddress.valueOf("2.2.2.2"), 2, "tcp"));
+        controllers.stream().forEach(cInfo -> {
+            elements.add(new YangElement("controller", ImmutableMap.of("id", cInfo.target(),
+                                                                       "ip-address", cInfo.ip().toString())));
+        });
+        XMLConfiguration cfg =
+                new XMLConfiguration(YangXmlUtils.getInstance()
+                                             .getXmlConfiguration(OF_CONFIG_XML_PATH, elements));
+        assertEquals("Wrong configuaration", IteratorUtils.toList(testCreateConfig.getKeys()),
+                     IteratorUtils.toList(cfg.getKeys()));
+        assertEquals("Wrong string configuaration", utils.getString(testCreateConfig),
+                     utils.getString(cfg));
+    }
+
+    /**
+     * Test reading an XML configuration and retrieving the requested elements.
+     *
+     * @throws ConfigurationException
+     */
+    @Test
+    public void testReadLastXmlConfiguration() throws ConfigurationException {
+        testCreateConfig.load(getClass().getResourceAsStream("/testYangConfig.xml"));
+        List<YangElement> elements = utils.readXmlConfiguration(testCreateConfig,
+                                                                "controller");
+        List<YangElement> expected = ImmutableList.of(
+                new YangElement("controller", ImmutableMap.of("id", "tcp:1.1.1.1:1",
+                                                              "ip-address", "1.1.1.1")),
+                new YangElement("controller", ImmutableMap.of("id", "tcp:2.2.2.2:2",
+                                                              "ip-address", "2.2.2.2")));
+        assertEquals("Wrong elements collected", expected, elements);
+    }
+
+    /**
+     * Test reading an XML configuration and retrieving the requested elements.
+     *
+     * @throws ConfigurationException
+     */
+    @Test
+    public void testReadNestedXmlConfiguration() throws ConfigurationException {
+        testCreateConfig.load(getClass().getResourceAsStream("/testYangConfig.xml"));
+        List<YangElement> elements = utils.readXmlConfiguration(testCreateConfig, "controllers");
+        List<YangElement> expected = ImmutableList.of(
+                new YangElement("controllers", ImmutableMap.of("controller.id", "tcp:1.1.1.1:1",
+                                                               "controller.ip-address", "1.1.1.1")),
+                new YangElement("controllers", ImmutableMap.of("controller.id", "tcp:2.2.2.2:2",
+                                                               "controller.ip-address", "2.2.2.2")));
+        assertEquals("Wrong elements collected", expected, elements);
+    }
+
+    //enables to change the path to the resources directory.
+    private class YangXmlUtilsAdap extends YangXmlUtils {
+
+        @Override
+        protected InputStream getCfgInputStream(String file) {
+            return YangXmlUtilsAdap.class.getResourceAsStream(file);
+        }
+    }
+}
\ No newline at end of file
diff --git a/drivers/utilities/src/test/resources/of-config/ietf-inet-types.xml b/drivers/utilities/src/test/resources/of-config/ietf-inet-types.xml
new file mode 100644
index 0000000..d977358
--- /dev/null
+++ b/drivers/utilities/src/test/resources/of-config/ietf-inet-types.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" />
\ No newline at end of file
diff --git a/drivers/utilities/src/test/resources/of-config/of-config.xml b/drivers/utilities/src/test/resources/of-config/of-config.xml
new file mode 100644
index 0000000..c11ad84
--- /dev/null
+++ b/drivers/utilities/src/test/resources/of-config/of-config.xml
@@ -0,0 +1,148 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  ~ Copyright 2016 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <capable-switch xmlns="urn:onf:config:yang">
+        <id/>
+        <config-version/>
+        <resources>
+            <port>
+                <name/>
+                <number/>
+                <requested-number/>
+                <current-rate/>
+                <max-rate/>
+                <configuration/>
+                <state>
+                    <oper-state/>
+                    <blocked/>
+                    <live/>
+                </state>
+                <features>
+                    <current>
+                        <rate/>
+                        <auto-negotiate/>
+                        <medium/>
+                        <pause/>
+                    </current>
+                    <advertised>
+                        <rate/>
+                        <medium/>
+                        <pause/>
+                    </advertised>
+                    <supported>
+                        <rate/>
+                        <medium/>
+                        <pause/>
+                    </supported>
+                    <advertised-peer>
+                        <rate/>
+                        <medium/>
+                        <pause/>
+                    </advertised-peer>
+                </features>
+                <tunnel>
+                    <local-endpoint-ipv4-adress/>
+                    <remote-endpoint-ipv4-adress/>
+                </tunnel>
+                <ipgre-tunnel>
+                    <local-endpoint-ipv4-adress/>
+                    <remote-endpoint-ipv4-adress/>
+                    <key/>
+                </ipgre-tunnel>
+                <vxlan-tunnel>
+                    <local-endpoint-ipv4-adress/>
+                    <remote-endpoint-ipv4-adress/>
+                    <vni/>
+                </vxlan-tunnel>
+            </port>
+            <queue>
+                <resource-id/>
+                <id/>
+                <port/>
+                <properties>
+                    <min-rate/>
+                    <max-rate/>
+                    <experimenter-id/>
+                    <experimenter-data/>
+                </properties>
+            </queue>
+            <owned-certificate>
+                <resource-id/>
+                <certificate/>
+                <private-key>
+                    <key-type/>
+                    <key-data/>
+                </private-key>
+            </owned-certificate>
+            <external-certificate>
+                <resource-id/>
+                <certificate/>
+            </external-certificate>
+            <flow-table>
+                <resource-id/>
+                <table-id/>
+                <name/>
+                <max-entries/>
+            </flow-table>
+        </resources>
+        <logical-switches>
+            <switch>
+                <id/>
+                <capabilities>
+                    <max-buffered-packets/>
+                    <max-tables/>
+                    <max-ports/>
+                    <reserved-port-types>
+                        <type/>
+                    </reserved-port-types>
+                    <group-types>
+                        <type/>
+                    </group-types>
+                    <group-capabilities>
+                        <capability/>
+                    </group-capabilities>
+                    <action-types>
+                        <type/>
+                    </action-types>
+                    <instruction-types>
+                        <type/>
+                    </instruction-types>
+                </capabilities>
+                <datapath-id/>
+                <controllers>
+                    <controller>
+                        <id/>
+                        <ip-address/>
+                        <local-ip-address/>
+                        <state>
+                            <connection-state/>
+                            <local-ip-address-in-use/>
+                            <local-port-in-use/>
+                        </state>
+                    </controller>
+                </controllers>
+                <resources>
+                    <port/>
+                    <queue/>
+                    <certificate/>
+                    <flow-table/>
+                </resources>
+            </switch>
+        </logical-switches>
+    </capable-switch>
+</data>
\ No newline at end of file
diff --git a/drivers/utilities/src/test/resources/testCreateSingleYangConfig.xml b/drivers/utilities/src/test/resources/testCreateSingleYangConfig.xml
new file mode 100644
index 0000000..bab0054
--- /dev/null
+++ b/drivers/utilities/src/test/resources/testCreateSingleYangConfig.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<configuration xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+<capable-switch xmlns="urn:onf:config:yang">
+<id>openvswitch</id>
+<logical-switches>
+<switch>
+<id>ofc-bridge</id>
+<controllers>
+<controller>
+<id>tcp:1.1.1.1:1</id>
+<ip-address>1.1.1.1</ip-address>
+</controller>
+</controllers>
+</switch>
+</logical-switches>
+</capable-switch>
+</configuration>
\ No newline at end of file
diff --git a/drivers/utilities/src/test/resources/testYangConfig.xml b/drivers/utilities/src/test/resources/testYangConfig.xml
new file mode 100644
index 0000000..58e4b31
--- /dev/null
+++ b/drivers/utilities/src/test/resources/testYangConfig.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<configuration xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+<capable-switch xmlns="urn:onf:config:yang">
+<id>openvswitch</id>
+<logical-switches>
+<switch>
+<id>ofc-bridge</id>
+<controllers>
+<controller>
+<id>tcp:1.1.1.1:1</id>
+<ip-address>1.1.1.1</ip-address>
+</controller>
+<controller>
+<id>tcp:2.2.2.2:2</id>
+<ip-address>2.2.2.2</ip-address>
+</controller>
+</controllers>
+</switch>
+</logical-switches>
+</capable-switch>
+</configuration>
\ No newline at end of file
diff --git a/tools/dev/bin/onos-convert-yang b/tools/dev/bin/onos-convert-yang
new file mode 100755
index 0000000..21762d7
--- /dev/null
+++ b/tools/dev/bin/onos-convert-yang
@@ -0,0 +1,33 @@
+#!/bin/bash
+# -----------------------------------------------------------------------------
+# ONOS YANG to XML skeleton convert via pyang
+# -----------------------------------------------------------------------------
+
+[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
+. $ONOS_ROOT/tools/build/envDefaults
+
+aux=/tmp/pyangversion
+pyang -v $cmd > $aux
+errorstring="Pyang no installed, please download and intall from https://github.com/mbj4668/pyang"
+cat $aux
+grep -q "pyang: command not found" $aux && echo $errorString && exit 1
+
+grep -q "pyang 1" $aux
+
+if ! [ -e "$1" ]; then
+  echo "$1 input directory not found" >&2
+  exit 1
+fi
+if ! [ -d "$1" ]; then
+  echo "$1 not a directory for output" >&2
+  exit 1
+fi
+cd $1
+find . -name '*.yang' | while read file; do f=$(basename $file ".yang"); \
+directory=$ONOS_ROOT/drivers/utilities/src/main/resources/${PWD##*/}; \
+if [ ! -d "$directory" ]; then
+  mkdir $directory; \
+fi
+echo $directory/$f.xml
+pyang -f sample-xml-skeleton $f.yang > $directory/$f.xml; done
+exit 0
\ No newline at end of file