@Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML,
MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
public StructuredData readConfigurationData(@PathParam("identifier") String identifier);
-
- @POST
- @Path("/config/{identifier:.+}")
+
+ @GET
+ @Path("/operational/{identifier:.+}")
@Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML,
MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
- public Response createConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
+ public StructuredData readOperationalData(@PathParam("identifier") String identifier);
@PUT
@Path("/config/{identifier:.+}")
MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
public Response updateConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
- @GET
- @Path("/operational/{identifier:.+}")
+ @POST
+ @Path("/config/{identifier:.+}")
@Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML,
MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
- public StructuredData readOperationalData(@PathParam("identifier") String identifier);
+ public Response createConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
+
+ @POST
+ @Path("/config")
+ @Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML,
+ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
+ public Response createConfigurationData(CompositeNode payload);
}
if (node.getValue() instanceof QName) {
IdentityValuesDTO valueDTO = (IdentityValuesDTO) RestCodec.from(baseType).serialize(node.getValue());
IdentityValue valueFromDTO = valueDTO.getValuesWithNamespaces().get(0);
- String moduleName = ControllerContext.getInstance().findModuleByNamespace(
+ String moduleName = ControllerContext.getInstance().findModuleNameByNamespace(
URI.create(valueFromDTO.getNamespace()));
writer.value(moduleName + ":" + valueFromDTO.getValue());
} else {
override readConfigurationData(InstanceIdentifier path) {
checkPreconditions
- LOG.info("Read Configuration via Restconf: {}",path)
+ LOG.info("Read Configuration via Restconf: {}", path)
return dataService.readConfigurationData(path);
}
override readOperationalData(InstanceIdentifier path) {
checkPreconditions
- LOG.info("Read Operational via Restconf: {}",path)
+ LOG.info("Read Operational via Restconf: {}", path)
return dataService.readOperationalData(path);
}
def commitConfigurationDataPut(InstanceIdentifier path, CompositeNode payload) {
checkPreconditions
val transaction = dataService.beginTransaction;
+ LOG.info("Put Configuration via Restconf: {}", path)
transaction.putConfigurationData(path, payload);
return transaction.commit()
}
- def commitOperationalDataPut(InstanceIdentifier path, CompositeNode payload) {
+ def commitConfigurationDataPost(InstanceIdentifier path, CompositeNode payload) {
checkPreconditions
val transaction = dataService.beginTransaction;
- transaction.putOperationalData(path, payload);
- return transaction.commit()
+ transaction.putConfigurationData(path, payload);
+ if (payload == transaction.createdConfigurationData.get(path)) {
+ LOG.info("Post Configuration via Restconf: {}", path)
+ return transaction.commit()
+ }
+ LOG.info("Post Configuration via Restconf was not executed because data already exists: {}", path)
+ return null;
}
-
+
}
Preconditions.checkState(compositeNode == null, "Data can be inconsistent.");
return Collections.unmodifiableList(values);
}
+
+ @Override
+ public boolean isChangeAllowed() {
+ return compositeNode == null ? true : false;
+ }
@Override
public CompositeNode unwrap() {
public Set<java.util.Map.Entry<QName, List<Node<?>>>> entrySet() {
return unwrap().entrySet();
}
+
}
import java.util.concurrent.ConcurrentHashMap
import javax.ws.rs.core.Response
import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener
+import org.opendaylight.controller.sal.core.api.mount.MountService
import org.opendaylight.controller.sal.rest.impl.RestUtil
import org.opendaylight.controller.sal.rest.impl.RestconfProvider
import org.opendaylight.yangtools.yang.common.QName
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
+import org.opendaylight.yangtools.yang.model.api.Module
import org.opendaylight.yangtools.yang.model.api.RpcDefinition
import org.opendaylight.yangtools.yang.model.api.SchemaContext
import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
import org.slf4j.LoggerFactory
import static com.google.common.base.Preconditions.*
-import org.opendaylight.controller.sal.core.api.mount.MountService
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
+import java.util.ArrayList
class ControllerContext implements SchemaServiceListener {
val static LOG = LoggerFactory.getLogger(ControllerContext)
}
public def InstanceIdWithSchemaNode toInstanceIdentifier(String restconfInstance) {
+ checkPreconditions
val ret = InstanceIdentifier.builder();
val pathArgs = restconfInstance.split("/");
if (pathArgs.empty) {
if (pathArgs.head.empty) {
pathArgs.remove(0)
}
- val schemaNode = ret.collectPathArguments(pathArgs, globalSchema.findModule(pathArgs.head));
+ val mountPoints = new ArrayList
+ val schemaNode = ret.collectPathArguments(pathArgs, globalSchema.findModule(pathArgs.head), mountPoints);
if (schemaNode === null) {
return null
}
- return new InstanceIdWithSchemaNode(ret.toInstance, schemaNode)
+ return new InstanceIdWithSchemaNode(ret.toInstance, schemaNode, mountPoints.last)
}
- private static def findModule(SchemaContext context,String argument) {
- //checkPreconditions
+ private def findModule(SchemaContext context,String argument) {
checkNotNull(argument);
val startModule = argument.toModuleName();
return context.getLatestModule(startModule)
}
-
- static def getLatestModule(SchemaContext schema,String moduleName) {
- checkArgument(schema != null);
+
+ private def getLatestModule(SchemaContext schema,String moduleName) {
+ checkArgument(schema !== null);
checkArgument(moduleName !== null && !moduleName.empty)
val modules = schema.modules.filter[m|m.name == moduleName]
+ return modules.filterLatestModule
+ }
+
+ private def filterLatestModule(Iterable<Module> modules) {
var latestModule = modules.head
for (module : modules) {
if (module.revision.after(latestModule.revision)) {
}
return latestModule
}
+
+ def findModuleByName(String moduleName) {
+ checkPreconditions
+ checkArgument(moduleName !== null && !moduleName.empty)
+ return globalSchema.getLatestModule(moduleName)
+ }
+
+ def findModuleByName(String moduleName, InstanceIdentifier partialPath) {
+ checkArgument(moduleName !== null && !moduleName.empty && partialPath !== null && !partialPath.path.empty)
+ val mountPointSchema = mountService?.getMountPoint(partialPath)?.schemaContext;
+ return mountPointSchema?.getLatestModule(moduleName);
+ }
+
+ def findModuleByNamespace(URI namespace) {
+ checkPreconditions
+ val moduleSchemas = globalSchema.findModuleByNamespace(namespace)
+ return moduleSchemas?.filterLatestModule
+ }
+
+ def findModuleByNamespace(URI namespace, InstanceIdentifier partialPath) {
+ checkArgument(namespace !== null && !namespace.toString.empty && partialPath !== null && !partialPath.path.empty)
+ val mountPointSchema = mountService?.getMountPoint(partialPath)?.schemaContext;
+ val moduleSchemas = mountPointSchema?.findModuleByNamespace(namespace)
+ return moduleSchemas?.filterLatestModule
+ }
def String toFullRestconfIdentifier(InstanceIdentifier path) {
checkPreconditions
throw new IllegalArgumentException("Conversion of generic path argument is not supported");
}
- def findModuleByNamespace(URI namespace) {
+ def findModuleNameByNamespace(URI namespace) {
checkPreconditions
var module = uriToModuleName.get(namespace)
if (module === null) {
val moduleSchemas = globalSchema.findModuleByNamespace(namespace);
if(moduleSchemas === null) return null
- var latestModule = moduleSchemas.head
- for (m : moduleSchemas) {
- if (m.revision.after(latestModule.revision)) {
- latestModule = m
- }
- }
+ var latestModule = moduleSchemas.filterLatestModule
if(latestModule === null) return null
uriToModuleName.put(namespace, latestModule.name)
module = latestModule.name;
return module
}
- def findNamespaceByModule(String module) {
+ def findNamespaceByModuleName(String module) {
var namespace = moduleNameToUri.get(module)
if (namespace === null) {
- val moduleSchemas = globalSchema.modules.filter[it|it.name.equals(module)]
- var latestModule = moduleSchemas.head
- for (m : moduleSchemas) {
- if (m.revision.after(latestModule.revision)) {
- latestModule = m
- }
- }
+ var latestModule = globalSchema.getLatestModule(module)
if(latestModule === null) return null
namespace = latestModule.namespace
uriToModuleName.put(namespace, latestModule.name)
}
private def DataSchemaNode collectPathArguments(InstanceIdentifierBuilder builder, List<String> strings,
- DataNodeContainer parentNode) {
+ DataNodeContainer parentNode, List<InstanceIdentifier> mountPoints) {
checkNotNull(strings)
if (parentNode === null) {
return null;
// Node is possibly in other mount point
val partialPath = builder.toInstance;
val mountPointSchema = mountService?.getMountPoint(partialPath)?.schemaContext;
- if(mountPointSchema != null) {
- return builder.collectPathArguments(strings, mountPointSchema.findModule(strings.head));
+ if(mountPointSchema !== null) {
+ val module = mountPointSchema.findModule(strings.head)
+ if (module !== null) {
+ mountPoints.add(partialPath)
+ }
+ return builder.collectPathArguments(strings, module, mountPoints);
}
return null
}
}
if (targetNode instanceof DataNodeContainer) {
val remaining = strings.subList(consumed, strings.length);
- val result = builder.collectPathArguments(remaining, targetNode as DataNodeContainer);
+ val result = builder.collectPathArguments(remaining, targetNode as DataNodeContainer, mountPoints);
return result
}
val allCases = container.childNodes.filter(ChoiceNode).map[cases].flatten
for (caze : allCases) {
potentialNode = caze.findInstanceDataChild(name);
- if(potentialNode != null) {
+ if(potentialNode !== null) {
return potentialNode;
}
}
this.namespace = namespace;
}
+ @Override
+ public boolean isChangeAllowed() {
+ return unwrapped == null ? true : false;
+ }
+
@Override
public Node<?> unwrap() {
if (unwrapped == null) {
private final InstanceIdentifier instanceIdentifier;
private final DataSchemaNode schemaNode;
+ private final InstanceIdentifier mountPoint;
- public InstanceIdWithSchemaNode(InstanceIdentifier instanceIdentifier, DataSchemaNode schemaNode) {
+ public InstanceIdWithSchemaNode(InstanceIdentifier instanceIdentifier, DataSchemaNode schemaNode, InstanceIdentifier mountPoint) {
this.instanceIdentifier = instanceIdentifier;
this.schemaNode = schemaNode;
+ this.mountPoint = mountPoint;
}
public InstanceIdentifier getInstanceIdentifier() {
return schemaNode;
}
+ public InstanceIdentifier getMountPoint() {
+ return mountPoint;
+ }
+
}
T unwrap();
+ boolean isChangeAllowed();
+
URI getNamespace();
void setNamespace(URI namespace);
public QName deserialize(IdentityValuesDTO data) {
IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0);
String namespace = valueWithNamespace.getNamespace();
- URI validNamespace = ControllerContext.getInstance().findNamespaceByModule(namespace);
+ URI validNamespace = ControllerContext.getInstance().findNamespaceByModuleName(namespace);
if (validNamespace == null) {
validNamespace = URI.create(namespace);
}
package org.opendaylight.controller.sal.restconf.impl
import java.util.ArrayList
+import java.util.HashMap
import java.util.List
import java.util.Set
import javax.ws.rs.core.Response
import org.opendaylight.controller.sal.rest.api.RestconfService
import org.opendaylight.yangtools.yang.common.QName
import org.opendaylight.yangtools.yang.data.api.CompositeNode
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder
import org.opendaylight.yangtools.yang.data.api.Node
import org.opendaylight.yangtools.yang.data.impl.NodeFactory
import org.opendaylight.yangtools.yang.model.api.ChoiceNode
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
+import org.opendaylight.yangtools.yang.model.api.Module
import org.opendaylight.yangtools.yang.model.api.TypeDefinition
import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
return null;
}
- override readData(String identifier) {
- val instanceIdentifierWithSchemaNode = identifier.resolveInstanceIdentifier
- val data = broker.readOperationalData(instanceIdentifierWithSchemaNode.getInstanceIdentifier);
- return new StructuredData(data, instanceIdentifierWithSchemaNode.schemaNode)
- }
-
- override createConfigurationData(String identifier, CompositeNode payload) {
- val identifierWithSchemaNode = identifier.resolveInstanceIdentifier
- val value = normalizeNode(payload, identifierWithSchemaNode.schemaNode)
- val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier, value).get();
- switch status.result {
- case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
- default: Response.status(INTERNAL_SERVER_ERROR).build
- }
- }
-
- override updateConfigurationData(String identifier, CompositeNode payload) {
- val identifierWithSchemaNode = identifier.resolveInstanceIdentifier
- val value = normalizeNode(payload, identifierWithSchemaNode.schemaNode)
- val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier, value).get();
- switch status.result {
- case TransactionStatus.COMMITED: Response.status(OK).build
- default: Response.status(INTERNAL_SERVER_ERROR).build
- }
- }
-
override invokeRpc(String identifier, CompositeNode payload) {
val rpc = identifier.rpcDefinition
if (rpc === null) {
throw new ResponseException(NOT_FOUND, "RPC does not exist.");
}
- val value = normalizeNode(payload, rpc.input)
+ val value = normalizeNode(payload, rpc.input, null)
val List<Node<?>> input = new ArrayList
input.add(value)
val rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null)
return new StructuredData(rpcResult.result, rpc.output)
}
+ override readData(String identifier) {
+ val instanceIdentifierWithSchemaNode = identifier.resolveInstanceIdentifier
+ val data = broker.readOperationalData(instanceIdentifierWithSchemaNode.getInstanceIdentifier);
+ return new StructuredData(data, instanceIdentifierWithSchemaNode.schemaNode)
+ }
+
override readConfigurationData(String identifier) {
val instanceIdentifierWithSchemaNode = identifier.resolveInstanceIdentifier
val data = broker.readConfigurationData(instanceIdentifierWithSchemaNode.getInstanceIdentifier);
updateConfigurationData(identifier, payload);
}
+ override updateConfigurationData(String identifier, CompositeNode payload) {
+ val identifierWithSchemaNode = identifier.resolveInstanceIdentifier
+ val value = normalizeNode(payload, identifierWithSchemaNode.schemaNode, identifierWithSchemaNode.mountPoint)
+ val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier, value).get();
+ switch status.result {
+ case TransactionStatus.COMMITED: Response.status(OK).build
+ default: Response.status(INTERNAL_SERVER_ERROR).build
+ }
+ }
+
override createConfigurationDataLegacy(String identifier, CompositeNode payload) {
createConfigurationData(identifier, payload);
}
+ override createConfigurationData(String identifier, CompositeNode payload) {
+ val uncompleteIdentifierWithSchemaNode = identifier.resolveInstanceIdentifier
+ var schemaNode = (uncompleteIdentifierWithSchemaNode.schemaNode as DataNodeContainer).getSchemaChildNode(payload)
+ if (schemaNode === null) {
+ schemaNode = payload.findModule(uncompleteIdentifierWithSchemaNode.instanceIdentifier)?.getSchemaChildNode(payload)
+ }
+ val value = normalizeNode(payload, schemaNode, uncompleteIdentifierWithSchemaNode.instanceIdentifier)
+ val completeIdentifierWithSchemaNode = uncompleteIdentifierWithSchemaNode.addLastIdentifierFromData(value, schemaNode)
+ val status = broker.commitConfigurationDataPost(completeIdentifierWithSchemaNode.instanceIdentifier, value)?.get();
+ if (status === null) {
+ return Response.status(ACCEPTED).build
+ }
+ switch status.result {
+ case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
+ default: Response.status(INTERNAL_SERVER_ERROR).build
+ }
+ }
+
+ override createConfigurationData(CompositeNode payload) {
+ val schemaNode = payload.findModule(null)?.getSchemaChildNode(payload)
+ val value = normalizeNode(payload, schemaNode, null)
+ val identifierWithSchemaNode = addLastIdentifierFromData(null, value, schemaNode)
+ val status = broker.commitConfigurationDataPost(identifierWithSchemaNode.instanceIdentifier, value)?.get();
+ if (status === null) {
+ return Response.status(ACCEPTED).build
+ }
+ switch status.result {
+ case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
+ default: Response.status(INTERNAL_SERVER_ERROR).build
+ }
+ }
+
private def InstanceIdWithSchemaNode resolveInstanceIdentifier(String identifier) {
val identifierWithSchemaNode = identifier.toInstanceIdentifier
if (identifierWithSchemaNode === null) {
return identifierWithSchemaNode
}
- private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema) {
+ private def dispatch Module findModule(CompositeNode data, InstanceIdentifier partialPath) {
+ if (partialPath !== null && !partialPath.path.empty) {
+ return data.nodeType.namespace.findModuleByNamespace(partialPath)
+ } else {
+ return data.nodeType.namespace.findModuleByNamespace
+ }
+ }
+
+ private def dispatch Module findModule(CompositeNodeWrapper data, InstanceIdentifier partialPath) {
+ var Module module = null;
+ if (partialPath !== null && !partialPath.path.empty) {
+ module = data.namespace.findModuleByNamespace(partialPath) // namespace from XML
+ if (module === null) {
+ module = data.namespace.toString.findModuleByName(partialPath) // namespace (module name) from JSON
+ }
+ } else {
+ module = data.namespace.findModuleByNamespace // namespace from XML
+ if (module === null) {
+ module = data.namespace.toString.findModuleByName // namespace (module name) from JSON
+ }
+ }
+ return module
+ }
+
+ private def dispatch DataSchemaNode getSchemaChildNode(DataNodeContainer parentSchemaNode, CompositeNode data) {
+ return parentSchemaNode?.getDataChildByName(data.nodeType.localName)
+ }
+
+ private def dispatch DataSchemaNode getSchemaChildNode(DataNodeContainer parentSchemaNode, CompositeNodeWrapper data) {
+ return parentSchemaNode?.getDataChildByName(data.localName)
+ }
+
+ private def InstanceIdWithSchemaNode addLastIdentifierFromData(InstanceIdWithSchemaNode identifierWithSchemaNode, CompositeNode data, DataSchemaNode schemaOfData) {
+ val iiOriginal = identifierWithSchemaNode?.instanceIdentifier
+ var InstanceIdentifierBuilder iiBuilder = null
+ if (iiOriginal === null) {
+ iiBuilder = InstanceIdentifier.builder
+ } else {
+ iiBuilder = InstanceIdentifier.builder(iiOriginal)
+ }
+
+ if (schemaOfData instanceof ListSchemaNode) {
+ iiBuilder.nodeWithKey(schemaOfData.QName, (schemaOfData as ListSchemaNode).resolveKeysFromData(data))
+ } else {
+ iiBuilder.node(schemaOfData.QName)
+ }
+ return new InstanceIdWithSchemaNode(iiBuilder.toInstance, schemaOfData, identifierWithSchemaNode?.mountPoint)
+ }
+
+ private def resolveKeysFromData(ListSchemaNode listNode, CompositeNode dataNode) {
+ val keyValues = new HashMap<QName, Object>();
+ for (key : listNode.keyDefinition) {
+ val dataNodeKeyValueObject = dataNode.getSimpleNodesByName(key.localName)?.head?.value
+ if (dataNodeKeyValueObject === null) {
+ throw new ResponseException(BAD_REQUEST, "List " + dataNode.nodeType.localName + " does not contain key: " + key.localName)
+ }
+ keyValues.put(key, dataNodeKeyValueObject);
+ }
+ return keyValues
+ }
+
+ private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema, InstanceIdentifier mountPoint) {
+ if (schema !== null && !schema.containerOrList) {
+ throw new ResponseException(BAD_REQUEST, "Root element has to be container or list yang datatype.");
+ }
if (node instanceof CompositeNodeWrapper) {
- normalizeNode(node as CompositeNodeWrapper, schema, null)
+ if ((node as CompositeNodeWrapper).changeAllowed) {
+ normalizeNode(node as CompositeNodeWrapper, schema, null, mountPoint)
+ }
return (node as CompositeNodeWrapper).unwrap()
}
return node
}
- private def void normalizeNode(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment) {
+ private def isContainerOrList(DataSchemaNode schemaNode) {
+ return (schemaNode instanceof ContainerSchemaNode) || (schemaNode instanceof ListSchemaNode)
+ }
+
+ private def void normalizeNode(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
+ InstanceIdentifier mountPoint) {
if (schema === null) {
throw new ResponseException(BAD_REQUEST,
"Data has bad format\n" + nodeBuilder.localName + " does not exist in yang schema.");
} else if (previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) {
validQName = QName.create(currentAugment, schema.QName.localName);
}
- val moduleName = controllerContext.findModuleByNamespace(validQName.namespace);
+ var moduleName = controllerContext.findModuleNameByNamespace(validQName.namespace);
+ if (moduleName === null && mountPoint !== null && !mountPoint.path.empty) {
+ moduleName = controllerContext.findModuleByNamespace(validQName.namespace, mountPoint)?.name
+ }
if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace ||
nodeBuilder.namespace.toString == moduleName) {
nodeBuilder.qname = validQName
} else {
throw new ResponseException(BAD_REQUEST,
- "Data has bad format\nIf data is in XML format then namespace for " + nodeBuilder.localName +
- " should be " + schema.QName.namespace + ".\n If data is in Json format then module name for " +
+ "Data has bad format.\nIf data is in XML format then namespace for " + nodeBuilder.localName +
+ " should be " + schema.QName.namespace + ".\nIf data is in Json format then module name for " +
nodeBuilder.localName + " should be " + moduleName + ".");
}
for (child : children) {
normalizeNode(child,
findFirstSchemaByLocalName(child.localName, (schema as DataNodeContainer).childNodes),
- currentAugment)
+ currentAugment, mountPoint)
}
if(schema instanceof ListSchemaNode) {
val listKeys = (schema as ListSchemaNode).keyDefinition
this.namespace = namespace;
}
+ @Override
+ public boolean isChangeAllowed() {
+ return simpleNode == null ? true : false;
+ }
+
@Override
public SimpleNode<Object> unwrap() {
if (simpleNode == null) {
import org.junit.Test;
import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider;
import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.ResponseException;
import org.opendaylight.controller.sal.restconf.impl.test.TestUtils;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
assertEquals("lst", compNode.getNodeType().getLocalName());
verifyCompositeNode(compNode, "simple:list:yang1");
- String exceptionMessage = "";
- try {
- TestUtils.normalizeCompositeNode(compositeNode, modules2, "simple-list-yang2:lst");
- } catch (ResponseException e) {
- exceptionMessage = String.valueOf(e.getResponse().getEntity());
- }
- assertTrue(exceptionMessage
- .contains("Data has bad format\nIf data is in XML format then namespace for lst should be simple:list:yang2.\n If data is in Json format then module name for lst should be simple-list-yang2."));
-
+ TestUtils.normalizeCompositeNode(compositeNode, modules2, "simple-list-yang2:lst");
+ verifyCompositeNode(compNode, "simple:list:yang1");
}
@Test
ControllerContext contContext = ControllerContext.getInstance();
contContext.onGlobalContextUpdated(TestUtils.loadSchemaContext(modules));
try {
- contContext.findModuleByNamespace(new URI("invoke:rpc:module"));
+ contContext.findModuleNameByNamespace(new URI("invoke:rpc:module"));
} catch (URISyntaxException e) {
assertTrue("Uri wasn't created sucessfuly", false);
}
--- /dev/null
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+public class NormalizeNodeTest extends YangAndXmlAndDataSchemaLoader {
+
+ @BeforeClass
+ public static void initialization() {
+ dataLoad("/normalize-node/yang/");
+ }
+
+ @Test
+ public void namespaceNotNullAndInvalidNamespaceAndNoModuleNameTest() {
+ String exceptionMessage = null;
+ try {
+ TestUtils.normalizeCompositeNode(prepareCnSn("wrongnamespace"), modules, schemaNodePath);
+ } catch (ResponseException e) {
+ exceptionMessage = String.valueOf(e.getResponse().getEntity());
+ }
+ assertEquals(
+ exceptionMessage,
+ "Data has bad format.\nIf data is in XML format then namespace for cont should be normalize:node:module.\nIf data is in Json format then module name for cont should be normalize-node-module.");
+ }
+
+ @Test
+ public void namespaceNullTest() {
+ String exceptionMessage = null;
+ try {
+ TestUtils.normalizeCompositeNode(prepareCnSn(null), modules, schemaNodePath);
+ } catch (ResponseException e) {
+ exceptionMessage = String.valueOf(e.getResponse().getEntity());
+ }
+ assertNull(exceptionMessage);
+ }
+
+ @Test
+ public void namespaceValidNamespaceTest() {
+ String exceptionMessage = null;
+ try {
+ TestUtils.normalizeCompositeNode(prepareCnSn("normalize:node:module"), modules, schemaNodePath);
+ } catch (ResponseException e) {
+ exceptionMessage = String.valueOf(e.getResponse().getEntity());
+ }
+ assertNull(exceptionMessage);
+ }
+
+ @Test
+ public void namespaceValidModuleNameTest() {
+ String exceptionMessage = null;
+ try {
+ TestUtils.normalizeCompositeNode(prepareCnSn("normalize-node-module"), modules, schemaNodePath);
+ } catch (ResponseException e) {
+ exceptionMessage = String.valueOf(e.getResponse().getEntity());
+ }
+ assertNull(exceptionMessage);
+ }
+
+ private CompositeNode prepareCnSn(String namespace) {
+ URI uri = null;
+ if (namespace != null) {
+ try {
+ uri = new URI(namespace);
+ } catch (URISyntaxException e) {
+ }
+ assertNotNull(uri);
+ }
+
+ SimpleNodeWrapper lf1 = new SimpleNodeWrapper(uri, "lf1", 43);
+ CompositeNodeWrapper cont = new CompositeNodeWrapper(uri, "cont");
+ cont.addValue(lf1);
+
+ return cont;
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
+import static org.junit.Assert.assertEquals;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.text.ParseException;
+import java.util.Set;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
+import org.opendaylight.controller.sal.rest.impl.XmlMapper;
+import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
+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.common.RpcResult;
+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;
+import org.opendaylight.controller.sal.core.api.mount.MountInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountService;
+
+import com.google.common.base.Charsets;
+
+public class RestConfigDataTest extends JerseyTest {
+
+ private static ControllerContext controllerContext;
+ private static BrokerFacade brokerFacade;
+ private static RestconfImpl restconfImpl;
+ private static MountService mountService;
+ private static SchemaContext schemaContext;
+
+ private static final MediaType MEDIA_TYPE_XML_DRAFT02 = new MediaType("application", "yang.data+xml");
+
+ @BeforeClass
+ public static void init() throws FileNotFoundException {
+ Set<Module> modules = TestUtils.loadModulesFrom("/test-config-data/yang1");
+ schemaContext = TestUtils.loadSchemaContext(modules);
+ initMocking();
+ }
+
+ private static void initMocking() {
+ controllerContext = ControllerContext.getInstance();
+ controllerContext.setSchemas(schemaContext);
+ mountService = mock(MountService.class);
+ controllerContext.setMountService(mountService);
+ brokerFacade = mock(BrokerFacade.class);
+ restconfImpl = RestconfImpl.getInstance();
+ restconfImpl.setBroker(brokerFacade);
+ restconfImpl.setControllerContext(controllerContext);
+ }
+
+ @Test
+ public void createConfigurationDataTest() throws UnsupportedEncodingException, ParseException {
+ initMocking();
+ String URI_1 = createUri("/config", "");
+ String URI_2 = createUri("/config/", "");
+ String URI_3 = createUri("/config/", "test-interface:interfaces/");
+ String URI_4 = createUri("/config/", "test-interface:interfaces/");
+ String URI_5 = createUri("/config/", "test-interface:interfaces/test-interface2:class");
+
+ RpcResult<TransactionStatus> rpcResult = new DummyRpcResult.Builder<TransactionStatus>().result(
+ TransactionStatus.COMMITED).build();
+ Future<RpcResult<TransactionStatus>> dummyFuture = DummyFuture.builder().rpcResult(rpcResult).build();
+
+ when(brokerFacade.commitConfigurationDataPost(any(InstanceIdentifier.class), any(CompositeNode.class)))
+ .thenReturn(dummyFuture);
+
+ ArgumentCaptor<InstanceIdentifier> instanceIdCaptor = ArgumentCaptor.forClass(InstanceIdentifier.class);
+ ArgumentCaptor<CompositeNode> compNodeCaptor = ArgumentCaptor.forClass(CompositeNode.class);
+
+ // Test URI_1
+ Entity<String> entity = createEntity("/test-config-data/xml/test-interface.xml");
+ Response response = target(URI_1).request(MEDIA_TYPE_XML_DRAFT02).post(entity);
+ assertEquals(204, response.getStatus());
+ verify(brokerFacade).commitConfigurationDataPost(instanceIdCaptor.capture(), compNodeCaptor.capture());
+ String identifier = "[(urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)interfaces]";
+ assertEquals("Bad format URI", identifier, instanceIdCaptor.getValue().getPath().toString());
+
+ // Test URI_2
+ response = target(URI_2).request(MEDIA_TYPE_XML_DRAFT02).post(entity);
+ assertEquals(204, response.getStatus());
+ verify(brokerFacade, times(2))
+ .commitConfigurationDataPost(instanceIdCaptor.capture(), compNodeCaptor.capture());
+ assertEquals("Bad format URI", identifier, instanceIdCaptor.getValue().getPath().toString());
+
+ // Test URI_3
+ entity = createEntity("/test-config-data/xml/test-interface2.xml");
+ response = target(URI_3).request(MEDIA_TYPE_XML_DRAFT02).post(entity);
+ assertEquals(204, response.getStatus());
+ verify(brokerFacade, times(3))
+ .commitConfigurationDataPost(instanceIdCaptor.capture(), compNodeCaptor.capture());
+
+ identifier = "[(urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)interfaces, (urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)interface[{(urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)name=eth0}]]";
+ assertEquals("Bad format URI", identifier, instanceIdCaptor.getValue().getPath().toString());
+
+ // Test URI_4
+ Set<Module> modules2 = TestUtils.loadModulesFrom("/test-config-data/yang2");
+ SchemaContext schemaContext2 = TestUtils.loadSchemaContext(modules2);
+ MountInstance mountInstance = mock(MountInstance.class);
+ when(mountInstance.getSchemaContext()).thenReturn(schemaContext2);
+ when(mountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
+
+ entity = createEntity("/test-config-data/xml/test-interface3.xml");
+ response = target(URI_4).request(MEDIA_TYPE_XML_DRAFT02).post(entity);
+ assertEquals(204, response.getStatus());
+ verify(brokerFacade, times(4))
+ .commitConfigurationDataPost(instanceIdCaptor.capture(), compNodeCaptor.capture());
+ identifier = "[(urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)interfaces, (urn:ietf:params:xml:ns:yang:test-interface2?revision=2014-08-01)class]";
+ assertEquals("Bad format URI", identifier, instanceIdCaptor.getValue().getPath().toString());
+
+ // Test URI_5
+ response = target(URI_5).request(MEDIA_TYPE_XML_DRAFT02).post(entity);
+ assertEquals(204, response.getStatus());
+ verify(brokerFacade, times(5))
+ .commitConfigurationDataPost(instanceIdCaptor.capture(), compNodeCaptor.capture());
+ identifier = "[(urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)interfaces, (urn:ietf:params:xml:ns:yang:test-interface2?revision=2014-08-01)class, (urn:ietf:params:xml:ns:yang:test-interface2?revision=2014-08-01)class]";
+ assertEquals("Bad format URI", identifier, instanceIdCaptor.getValue().getPath().toString());
+ }
+
+ @Test
+ public void testExistingData() throws UnsupportedEncodingException {
+ initMocking();
+ String URI_1 = createUri("/config", "");
+ String URI_2 = createUri("/config/", "");
+ String URI_3 = createUri("/config/", "test-interface:interfaces/");
+ String URI_4 = createUri("/config/", "test-interface:interfaces/");
+ String URI_5 = createUri("/config/", "test-interface:interfaces/test-interface2:class");
+
+ when(brokerFacade.commitConfigurationDataPost(any(InstanceIdentifier.class), any(CompositeNode.class)))
+ .thenReturn(null);
+
+ // Test URI_1
+ Entity<String> entity = createEntity("/test-config-data/xml/test-interface.xml");
+ Response response = target(URI_1).request(MEDIA_TYPE_XML_DRAFT02).post(entity);
+ assertEquals(202, response.getStatus());
+
+ // Test URI_2
+ response = target(URI_2).request(MEDIA_TYPE_XML_DRAFT02).post(entity);
+ assertEquals(202, response.getStatus());
+
+ // Test URI_3
+ entity = createEntity("/test-config-data/xml/test-interface2.xml");
+ response = target(URI_3).request(MEDIA_TYPE_XML_DRAFT02).post(entity);
+ assertEquals(202, response.getStatus());
+
+ // Test URI_4
+ Set<Module> modules2 = TestUtils.loadModulesFrom("/test-config-data/yang2");
+ SchemaContext schemaContext2 = TestUtils.loadSchemaContext(modules2);
+ MountInstance mountInstance = mock(MountInstance.class);
+ when(mountInstance.getSchemaContext()).thenReturn(schemaContext2);
+ when(mountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
+
+ entity = createEntity("/test-config-data/xml/test-interface3.xml");
+ response = target(URI_4).request(MEDIA_TYPE_XML_DRAFT02).post(entity);
+ assertEquals(202, response.getStatus());
+
+ // Test URI_5
+ response = target(URI_5).request(MEDIA_TYPE_XML_DRAFT02).post(entity);
+ assertEquals(202, response.getStatus());
+ }
+
+ private String createUri(String prefix, String encodedPart) throws UnsupportedEncodingException {
+ return URI.create(prefix + URLEncoder.encode(encodedPart, Charsets.US_ASCII.name()).toString()).toASCIIString();
+ }
+
+ private Entity<String> createEntity(final String relativePathToXml) {
+ InputStream inputStream = XmlMapper.class.getResourceAsStream(relativePathToXml);
+ String xml = TestUtils.getDocumentInPrintableForm(TestUtils.loadDocumentFrom(inputStream));
+ Entity<String> entity = Entity.entity(xml, MEDIA_TYPE_XML_DRAFT02);
+
+ return entity;
+ }
+
+ @Override
+ protected Application configure() {
+ enable(TestProperties.LOG_TRAFFIC);
+ enable(TestProperties.DUMP_ENTITY);
+ enable(TestProperties.RECORD_LOG_LEVEL);
+ set(TestProperties.RECORD_LOG_LEVEL, Level.ALL.intValue());
+
+ ResourceConfig resourceConfig = new ResourceConfig();
+ resourceConfig = resourceConfig.registerInstances(restconfImpl, StructuredDataToXmlProvider.INSTANCE,
+ XmlToCompositeNodeProvider.INSTANCE);
+ return resourceConfig;
+ }
+}
ControllerContext.getInstance().setSchemas(TestUtils.loadSchemaContext(modules));
prepareMocksForRestconf(modules, restconf);
- restconf.createConfigurationData(schemaNodePath, compositeNode);
+ restconf.updateConfigurationData(schemaNodePath, compositeNode);
}
/**
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.opendaylight.controller.sal.rest.api.RestconfService;
import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
-import org.opendaylight.controller.sal.restconf.impl.*;
+import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
+import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
+import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.*;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
RpcResult<TransactionStatus> rpcResult = new DummyRpcResult.Builder<TransactionStatus>().result(
TransactionStatus.COMMITED).build();
Future<RpcResult<TransactionStatus>> dummyFuture = DummyFuture.builder().rpcResult(rpcResult).build();
- when(brokerFacade.commitOperationalDataPut(any(InstanceIdentifier.class), any(CompositeNode.class)))
- .thenReturn(dummyFuture);
when(brokerFacade.commitConfigurationDataPut(any(InstanceIdentifier.class), any(CompositeNode.class)))
.thenReturn(dummyFuture);
+ when(brokerFacade.commitConfigurationDataPost(any(InstanceIdentifier.class), any(CompositeNode.class)))
+ .thenReturn(dummyFuture);
String uri = createUri("/config/", "ietf-interfaces:interfaces/interface/eth0");
Response response = target(uri).request(MEDIA_TYPE_DRAFT02).put(entity);
assertEquals(200, response.getStatus());
+
+ uri = createUri("/config/", "ietf-interfaces:interfaces");
response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity);
assertEquals(204, response.getStatus());
uri = createUri("/config/", "ietf-interfaces:interfaces/interface/eth0");
response = target(uri).request(MEDIA_TYPE_DRAFT02).put(entity);
assertEquals(200, response.getStatus());
+
+ uri = createUri("/config/", "ietf-interfaces:interfaces");
response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity);
assertEquals(204, response.getStatus());
uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
response = target(uri).request(MEDIA_TYPE).put(entity);
assertEquals(200, response.getStatus());
+
+ uri = createUri("/datastore/", "ietf-interfaces:interfaces");
response = target(uri).request(MEDIA_TYPE).post(entity);
assertEquals(204, response.getStatus());
}
RpcResult<TransactionStatus> rpcResult = new DummyRpcResult.Builder<TransactionStatus>().result(
TransactionStatus.FAILED).build();
Future<RpcResult<TransactionStatus>> dummyFuture = DummyFuture.builder().rpcResult(rpcResult).build();
- when(brokerFacade.commitOperationalDataPut(any(InstanceIdentifier.class), any(CompositeNode.class)))
- .thenReturn(dummyFuture);
when(brokerFacade.commitConfigurationDataPut(any(InstanceIdentifier.class), any(CompositeNode.class)))
.thenReturn(dummyFuture);
--- /dev/null
+module normalize-node-module {
+ namespace "normalize:node:module";
+
+ prefix "nonomo";
+ revision 2014-01-09 {
+ }
+
+ container cont {
+ leaf lf1 {
+ type int32;
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<interfaces xmlns="urn:ietf:params:xml:ns:yang:test-interface">
+ <interface>
+ <name>eth0</name>
+ <type>ethernetCsmacd</type>
+ <enabled>false</enabled>
+ </interface>
+</interfaces>
+
--- /dev/null
+<interface>
+ <name>eth0</name>
+ <type>ethernetCsmacd</type>
+ <enabled>false</enabled>
+</interface>
--- /dev/null
+<class xmlns="urn:ietf:params:xml:ns:yang:test-interface2">
+ <student>
+ <name>Thomas</name>
+ <age>23</age>
+ </student>
+</class>
--- /dev/null
+module test-interface {
+ yang-version 1;
+ namespace "urn:ietf:params:xml:ns:yang:test-interface";
+ prefix "sn";
+
+ description
+ "test file";
+
+ revision "2014-07-01" {
+ description
+ "Initial revision";
+ reference "will be defined";
+ }
+
+ container interfaces {
+ list interface {
+ key "name";
+
+ leaf name {
+ type string;
+ }
+ leaf type {
+ type string;
+ }
+ leaf enabled {
+ type string;
+ }
+ }
+ }
+}
--- /dev/null
+module test-interface2 {
+ yang-version 1;
+ namespace "urn:ietf:params:xml:ns:yang:test-interface2";
+ prefix "snn";
+
+ description
+ "test file";
+
+ revision "2014-08-01" {
+ description
+ "Initial revision";
+ reference "will be defined";
+ }
+
+ container class {
+ list student {
+ key "name";
+
+ leaf name {
+ type string;
+ }
+ leaf age {
+ type string;
+ }
+ }
+ }
+}