<groupId>org.eclipse.xtend</groupId>
<artifactId>org.eclipse.xtend.lib</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
<version>3.0.4.Final</version>
<scope>provided</scope>
</dependency>
+
+ <!-- Testing Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>1.0.9</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ <version>${yang.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ <version>${yang.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
@Property
private ConsumerSession context;
-
+
@Property
private DataBrokerService dataService;
-
+
override readConfigurationData(InstanceIdentifier path) {
return dataService.readConfigurationData(path);
}
override readOperationalData(InstanceIdentifier path) {
return dataService.readOperationalData(path);
}
-
+
def RpcResult<CompositeNode> invokeRpc(QName type, CompositeNode payload) {
val future = context.rpc(type, payload);
return future.get;
}
-
+
def commitConfigurationDataUpdate(InstanceIdentifier path, CompositeNode payload) {
val transaction = dataService.beginTransaction;
transaction.putConfigurationData(path, payload);
return transaction.commit()
}
-
+
def commitConfigurationDataCreate(InstanceIdentifier path, CompositeNode payload) {
val transaction = dataService.beginTransaction;
transaction.putConfigurationData(path, payload);
return transaction.commit()
}
-
+
}
import org.opendaylight.yangtools.yang.model.api.SchemaContext
import static com.google.common.base.Preconditions.*
+import java.util.Date
class ControllerContext {
public def InstanceIdWithSchemaNode toInstanceIdentifier(String restconfInstance) {
val ret = InstanceIdentifier.builder();
val pathArgs = restconfInstance.split("/");
+ if (pathArgs.empty) {
+ return null;
+ }
val schemaNode = ret.collectPathArguments(pathArgs, restconfInstance.findModule);
new InstanceIdWithSchemaNode(ret.toInstance, schemaNode)
}
-
+
def findModule(String restconfInstance) {
+ checkNotNull(restconfInstance);
val pathArgs = restconfInstance.split("/");
- val first = pathArgs.get(0);
- val startModule = first.toModuleName();
- val module = schemas.findModuleByNamespace(moduleNameToUri.get(startModule));
- checkArgument(module.size == 1); // Only one version supported now
- module.iterator.next
+ if (pathArgs.empty) {
+ return null;
+ }
+ val modulWithFirstYangStatement = pathArgs.filter[s|s.contains(":")].head
+ val startModule = modulWithFirstYangStatement.toModuleName();
+ schemas.getLatestModule(startModule)
+ }
+
+ private def getLatestModule(SchemaContext schema, String moduleName) {
+ checkNotNull(schema)
+ checkArgument(moduleName != null && !moduleName.empty)
+ val modules = schema.modules.filter[m|m.name == moduleName]
+ var latestModule = modules.head
+ for (module : modules) {
+ if (module.revision.after(latestModule.revision)) {
+ latestModule = module
+ }
+ }
+ return latestModule
}
def String toFullRestconfIdentifier(InstanceIdentifier path) {
return URLEncoder.encode(object.toString)
}
- def DataSchemaNode collectPathArguments(InstanceIdentifierBuilder builder, List<String> strings, DataNodeContainer parentNode) {
+ def DataSchemaNode collectPathArguments(InstanceIdentifierBuilder builder, List<String> strings,
+ DataNodeContainer parentNode) {
checkNotNull(strings)
- if (strings.length == 0) {
- return null;
+ if (strings.empty) {
+ return parentNode as DataSchemaNode;
}
- val nodeRef = strings.get(0);
+ val nodeRef = strings.head;
//val moduleName = nodeRef.toModuleName();
val nodeName = nodeRef.toNodeName();
- val naiveTargetNode = parentNode.getDataChildByName(nodeName);
-
- //var URI namespace;
- var DataSchemaNode targetNode = naiveTargetNode;
+ val targetNode = parentNode.getDataChildByName(nodeName);
+ if (targetNode == null) {
+ return null
+ }
- /*if(moduleName !== null) {
- namespace = moduleNameToUri.get(moduleName);
-
- }*/
// Number of consumed elements
var consumed = 1;
if (targetNode instanceof ListSchemaNode) {
val listNode = targetNode as ListSchemaNode;
val keysSize = listNode.keyDefinition.size
- val uriKeyValues = strings.subList(1, keysSize);
+ val uriKeyValues = strings.subList(consumed, consumed + keysSize);
val keyValues = new HashMap<QName, Object>();
var i = 0;
for (key : listNode.keyDefinition) {
builder.node(targetNode.QName);
}
if (targetNode instanceof DataNodeContainer) {
- val remaining = strings.subList(consumed, strings.length - 1);
+ val remaining = strings.subList(consumed, strings.length);
val result = builder.collectPathArguments(remaining, targetNode as DataNodeContainer);
return result
}
-
+
return targetNode
}
}
def String toModuleName(String str) {
+ checkNotNull(str)
if (str.contains(":")) {
val args = str.split(":");
checkArgument(args.size === 2);
return str;
}
}
-
+
public def QName toRpcQName(String name) {
-
-
}
}
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
public class InstanceIdWithSchemaNode {
-
+
private final InstanceIdentifier instanceIdentifier;
private final DataSchemaNode schemaNode;
-
+
public InstanceIdWithSchemaNode(InstanceIdentifier instanceIdentifier, DataSchemaNode schemaNode) {
this.instanceIdentifier = instanceIdentifier;
this.schemaNode = schemaNode;
public DataSchemaNode getSchemaNode() {
return schemaNode;
}
-
+
}
import static com.google.common.base.Preconditions.*
class RestconfImpl implements RestconfService {
-
+
@Property
BrokerFacade broker
-
+
@Property
extension ControllerContext controllerContext
-
+
val JsonMapper jsonMapper = new JsonMapper;
-
+
def init(SchemaService schemaService) {
checkState(broker !== null)
checkState(controllerContext !== null)
checkState(schemaService !== null)
controllerContext.schemas = schemaService.globalContext
}
-
+
override readAllData() {
return broker.readOperationalData("".removePrefixes.toInstanceIdentifier.getInstanceIdentifier);
}
-
-
+
override getModules() {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
}
-
+
override getRoot() {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
-
+
}
-
+
override readData(String identifier) {
val instanceIdentifierWithSchemaNode = identifier.removePrefixes.toInstanceIdentifier
- val data = broker.readOperationalData(instanceIdentifierWithSchemaNode.getInstanceIdentifier);
+ val data = broker.readOperationalData(instanceIdentifierWithSchemaNode.getInstanceIdentifier);
jsonMapper.convert(instanceIdentifierWithSchemaNode.getSchemaNode, data)
}
-
+
override createConfigurationData(String identifier, CompositeNode payload) {
return broker.commitConfigurationDataCreate(identifier.removePrefixes.toInstanceIdentifier.getInstanceIdentifier, payload);
}
-
-
+
override updateConfigurationData(String identifier, CompositeNode payload) {
return broker.commitConfigurationDataCreate(identifier.removePrefixes.toInstanceIdentifier.getInstanceIdentifier, payload);
}
-
+
override invokeRpc(String identifier, CompositeNode payload) {
val rpcResult = broker.invokeRpc(identifier.removePrefixes.toRpcQName, payload);
jsonMapper.convert(identifier.removePrefixes.toInstanceIdentifier.getSchemaNode, rpcResult.result);
}
-
+
private def String removePrefixes(String path) {
return path;
}
-
-}
\ No newline at end of file
+
+}
*/
package org.opendaylight.controller.sal.restconf.impl;
+import static org.opendaylight.controller.sal.restconf.impl.MediaTypes.API;
+
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import static org.opendaylight.controller.sal.restconf.impl.MediaTypes.*;
-
/**
* The URI hierarchy for the RESTCONF resources consists of an entry
* point container, 3 top-level resources, and 1 field. Refer to
public static final String XML = "+xml";
public static final String JSON = "+json";
-
+
@GET
public Object getRoot();
-
+
@GET
@Path("/datastore")
@Produces({API+JSON,API+XML})
public Object readAllData();
-
+
@GET
@Path("/datastore/{identifier}")
@Produces({API+XML})
public Object readData(@PathParam("identifier") String identifier);
-
+
@PUT
@Path("/datastore/{identifier}")
@Produces({API+XML})
public Object createConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
-
+
@POST
@Path("/datastore/{identifier}")
@Produces({API+XML})
public Object updateConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
-
-
-
+
@GET
@Path("/modules")
@Produces(API+XML)
public Object getModules();
-
+
@POST
@Path("/operations/{identifier}")
@Produces(API+XML)
--- /dev/null
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.FileNotFoundException;
+import java.util.Set;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class ControllerContextTest {
+
+ private static final ControllerContext controllerContext = new ControllerContext();
+
+ @BeforeClass
+ public static void init() throws FileNotFoundException {
+ Set<Module> allModules = TestUtils.loadModules(ControllerContextTest.class.getResource("/full-versions/yangs").getPath());
+ assertEquals(4, allModules.size());
+ SchemaContext schemaContext = TestUtils.loadSchemaContext(allModules);
+ controllerContext.setSchemas(schemaContext);
+ }
+
+ @Test
+ public void testToInstanceIdentifierList() throws FileNotFoundException {
+ InstanceIdWithSchemaNode instanceIdentifier = controllerContext.toInstanceIdentifier("ietf-interfaces:interfaces/interface/foo");
+ DataSchemaNode schemaNode = instanceIdentifier.getSchemaNode();
+ assertEquals(schemaNode.getQName().getLocalName(), "interface");
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import static org.junit.Assert.*;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.Set;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.JsonMapper;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class JsonMapperTest {
+
+ private static final ControllerContext controllerContext = new ControllerContext();
+
+ @BeforeClass
+ public static void init() throws FileNotFoundException {
+ Set<Module> allModules = TestUtils.loadModules(JsonMapperTest.class.getResource("/full-versions/yangs").getPath());
+ assertEquals(4, allModules.size());
+ SchemaContext schemaContext = TestUtils.loadSchemaContext(allModules);
+ controllerContext.setSchemas(schemaContext);
+ }
+
+ @Test
+ public void test() throws FileNotFoundException {
+ InputStream xmlStream = JsonMapperTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
+ CompositeNode loadedCompositeNode = TestUtils.loadCompositeNode(xmlStream);
+ DataSchemaNode loadedSchemaNode = controllerContext.toInstanceIdentifier("ietf-interfaces:interfaces/interface/eth0").getSchemaNode();
+ JsonMapper jsonMapper = new JsonMapper();
+ String json = jsonMapper.convert(loadedSchemaNode, loadedCompositeNode);
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import static org.mockito.Mockito.*;
+import static org.junit.Assert.*;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.Set;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class RestconfImplTest {
+
+ private static final RestconfImpl restconfImpl = new RestconfImpl();
+
+ @BeforeClass
+ public static void init() throws FileNotFoundException {
+ Set<Module> allModules = TestUtils.loadModules(RestconfImplTest.class.getResource("/full-versions/yangs").getPath());
+ assertEquals(4, allModules.size());
+ SchemaContext schemaContext = TestUtils.loadSchemaContext(allModules);
+ ControllerContext controllerContext = new ControllerContext();
+ controllerContext.setSchemas(schemaContext);
+ restconfImpl.setControllerContext(controllerContext);
+ }
+
+ @Test
+ public void testExample() throws FileNotFoundException {
+ InputStream xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
+ CompositeNode loadedCompositeNode = TestUtils.loadCompositeNode(xmlStream);
+ BrokerFacade brokerFacade = mock(BrokerFacade.class);
+ when(brokerFacade.readOperationalData(any(InstanceIdentifier.class))).thenReturn(loadedCompositeNode);
+ assertEquals(loadedCompositeNode, brokerFacade.readOperationalData(null));
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+
+final class TestUtils {
+
+ private static final Logger logger = LoggerFactory.getLogger(TestUtils.class);
+
+ private final static YangModelParser parser = new YangParserImpl();
+
+ public static Set<Module> loadModules(String resourceDirectory) throws FileNotFoundException {
+ final File testDir = new File(resourceDirectory);
+ final String[] fileList = testDir.list();
+ final List<File> testFiles = new ArrayList<File>();
+ if (fileList == null) {
+ throw new FileNotFoundException(resourceDirectory);
+ }
+ for (int i = 0; i < fileList.length; i++) {
+ String fileName = fileList[i];
+ testFiles.add(new File(testDir, fileName));
+ }
+ return parser.parseYangModels(testFiles);
+ }
+
+ public static SchemaContext loadSchemaContext(Set<Module> modules) {
+ return parser.resolveSchemaContext(modules);
+ }
+
+ public static SchemaContext loadSchemaContext(String resourceDirectory) throws FileNotFoundException {
+ return parser.resolveSchemaContext(loadModules(resourceDirectory));
+ }
+
+ public static Module findModule(Set<Module> modules, String moduleName) {
+ Module result = null;
+ for (Module module : modules) {
+ if (module.getName().equals(moduleName)) {
+ result = module;
+ break;
+ }
+ }
+ return result;
+ }
+
+ public static CompositeNode loadCompositeNode(InputStream xmlInputStream) throws FileNotFoundException {
+ if (xmlInputStream == null) {
+ throw new IllegalArgumentException();
+ }
+ Node<?> dataTree;
+ try {
+ dataTree = XmlTreeBuilder.buildDataTree(xmlInputStream);
+ } catch (XMLStreamException e) {
+ logger.error("Error during building data tree from XML", e);
+ return null;
+ }
+ if (dataTree == null) {
+ logger.error("data tree is null");
+ return null;
+ }
+ if (dataTree instanceof SimpleNode) {
+ logger.error("RPC XML was resolved as SimpleNode");
+ return null;
+ }
+ return (CompositeNode) dataTree;
+ }
+
+ public static String getDocumentInPrintableForm(Document doc) {
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ TransformerFactory tf = TransformerFactory.newInstance();
+ Transformer transformer = tf.newTransformer();
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
+ transformer.setOutputProperty(OutputKeys.METHOD, "xml");
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+ transformer.transform(new DOMSource(doc), new StreamResult(new OutputStreamWriter(out, "UTF-8")));
+ byte[] charData = out.toByteArray();
+ return new String(charData, "UTF-8");
+ } catch (IOException | TransformerException e) {
+ String msg = "Error during transformation of Document into String";
+ logger.error(msg, e);
+ return msg;
+ }
+
+ }
+
+}
--- /dev/null
+<interface>
+ <name>eth0</name>
+ <type>ethernetCsmacd</type>
+ <enabled>false</enabled>
+ <description>some interface</description>
+</interface>