[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