Merge "builder class for path creating"
authorTomas Cere <tcere@cisco.com>
Fri, 22 Apr 2016 10:19:33 +0000 (10:19 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 22 Apr 2016 10:19:33 +0000 (10:19 +0000)
100 files changed:
features/netconf/src/main/features/features.xml
netconf/mdsal-netconf-connector/pom.xml
netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/ops/RuntimeRpc.java
netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/ops/get/AbstractGet.java
netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/ops/get/FilterContentValidator.java [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/java/org/opendaylight/netconf/mdsal/connector/ops/NetconfMDSalMappingTest.java
netconf/mdsal-netconf-connector/src/test/java/org/opendaylight/netconf/mdsal/connector/ops/get/FilterContentValidatorTest.java [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/resources/filter/expected.txt [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/resources/filter/f1.xml [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/resources/filter/f2.xml [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/resources/filter/f3.xml [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/resources/filter/f4.xml [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/resources/filter/f5.xml [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/resources/filter/f6.xml [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/resources/filter/f7.xml [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/resources/filter/f8.xml [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/resources/yang/filter-validator-test-augment.yang [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/resources/yang/filter-validator-test-mod-0.yang [new file with mode: 0644]
netconf/mdsal-netconf-notification/src/main/java/org/opendaylight/netconf/mdsal/notification/NetconfNotificationOperationService.java
netconf/messagebus-netconf/src/main/java/org/opendaylight/netconf/messagebus/eventsources/netconf/NetconfEventSource.java
netconf/netconf-api/src/main/java/org/opendaylight/netconf/api/messages/NetconfHelloMessageAdditionalHeader.java
netconf/netconf-impl/src/main/java/org/opendaylight/controller/config/yang/config/netconf/northbound/impl/NetconfServerDispatcherModule.java
netconf/netconf-impl/src/main/java/org/opendaylight/netconf/impl/NetconfServerSession.java
netconf/netconf-impl/src/main/java/org/opendaylight/netconf/impl/NetconfServerSessionListener.java
netconf/netconf-impl/src/main/java/org/opendaylight/netconf/impl/NetconfServerSessionNegotiatorFactory.java
netconf/netconf-impl/src/main/java/org/opendaylight/netconf/impl/NetconfServerSessionNegotiatorFactoryBuilder.java [new file with mode: 0644]
netconf/netconf-impl/src/main/java/org/opendaylight/netconf/impl/osgi/NetconfImplActivator.java
netconf/netconf-impl/src/main/java/org/opendaylight/netconf/impl/osgi/NetconfMonitoringServiceImpl.java
netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/ConcurrentClientsTest.java
netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/MessageHeaderTest.java [deleted file]
netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/MessageParserTest.java
netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/NetconfDispatcherImplTest.java
netconf/netconf-monitoring/src/main/java/org/opendaylight/netconf/monitoring/xml/model/MonitoringSession.java
netconf/netconf-monitoring/src/test/java/org/opendaylight/netconf/monitoring/xml/JaxBSerializerTest.java
netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/AbstractNetconfSessionNegotiator.java
netconf/netconf-topology/pom.xml
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/ClusteredNetconfTopology.java
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImpl.java
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/pipeline/NetconfDeviceMasterDataBroker.java
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/pipeline/TopologyMountPointFacade.java
netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/AbstractNetconfTopologyTest.java
netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/pipeline/TopologyMountPointFacadeTest.java
netconf/netconf-util/src/main/java/org/opendaylight/netconf/util/messages/NetconfMessageHeader.java [deleted file]
netconf/netconf-util/src/main/java/org/opendaylight/netconf/util/messages/SubtreeFilter.java
netconf/netconf-util/src/test/java/org/opendaylight/netconf/util/messages/NetconfMessageHeaderTest.java [deleted file]
netconf/sal-netconf-connector/pom.xml
netconf/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemas.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/listener/NetconfSessionPreferences.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/KeepaliveSalFacade.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceDataBroker.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java [deleted file]
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceRpc.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalFacade.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalProvider.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/AbstractWriteTx.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/ReadOnlyTx.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/WriteCandidateRunningTx.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/WriteCandidateTx.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/WriteRunningTx.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/KeepaliveSalFacadeTest.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/MountInstanceTest.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceDataBrokerTest.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/ReadOnlyTxTest.java
netconf/tools/netconf-testtool/pom.xml
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/ScaleUtil.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/TesttoolNegotiationFactory.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/http/perf/RestPerfClient.java
netconf/tools/netconf-testtool/src/main/resources/logback.xml
restconf/sal-rest-connector/pom.xml
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfService.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonToPATCHBodyReader.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/PATCH.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/PATCHJsonBodyWriter.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/PATCHXmlBodyWriter.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfApplication.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfCompositeWrapper.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlToPATCHBodyReader.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/BrokerFacade.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/ControllerContext.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHContext.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHEditOperation.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHEntity.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHStatusContext.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHStatusEntity.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/StatisticsRestconfServiceWrapper.java
restconf/sal-rest-connector/src/main/yang/instance-identifier-patch-module.yang [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/md/sal/rest/common/TestRestconfUtils.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/AbstractBodyReaderTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonPATCHBodyReader.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlPATCHBodyReader.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/resources/instanceidentifier/json/jsonPATCHdata.json [new file with mode: 0644]
restconf/sal-rest-connector/src/test/resources/instanceidentifier/xml/xmlPATCHdata.xml [new file with mode: 0644]
restconf/sal-rest-connector/src/test/resources/instanceidentifier/yang/instance-identifier-patch-module.yang [new file with mode: 0644]
restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/mountpoints/MountPointSwagger.java
restconf/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/MountPointSwaggerTest.java

index 82920bcba169b4fba6542dcc8b78c13dca269413..ac0ad75e53a982c79f2e4be8140d91c04a38bfd2 100644 (file)
     <bundle>mvn:org.opendaylight.netconf/ietf-netconf/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.netconf/ietf-netconf-notifications/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.netconf/ietf-netconf-monitoring-extension/{{VERSION}}</bundle>
-    <bundle>mvn:org.opendaylight.mdsal.model/ietf-inet-types/{{VERSION}}</bundle>
-    <bundle>mvn:org.opendaylight.mdsal.model/ietf-yang-types/{{VERSION}}</bundle>
-    <bundle>mvn:org.opendaylight.mdsal.model/ietf-yang-types-20130715/2013.07.15.9-SNAPSHOT</bundle>
-    <bundle>mvn:org.opendaylight.mdsal.model/ietf-type-util/{{VERSION}}</bundle>
+    <feature version='${mdsal.model.version}'>odl-mdsal-models</feature>
   </feature>
 
   <feature name='odl-netconf-mapping-api' version='${project.version}' description="OpenDaylight :: Netconf :: Mapping API">
index ff73077b62402250eeead0d7b549fc89aaddf30e..cf5878c82bb45bd6e91b9b8bff34360ee6ff9dbc 100644 (file)
           <groupId>org.opendaylight.netconf</groupId>
           <artifactId>ietf-netconf</artifactId>
       </dependency>
-      <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>sal-broker-impl</artifactId>
-          <scope>test</scope>
-      </dependency>
       <dependency>
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>sal-distributed-datastore</artifactId>
index bec5a4a509d857a30a639d70c604d6bd5fc01c75..94dfa5e38a5d94f109a7597dbbb61a8a3806eb32 100644 (file)
@@ -202,7 +202,6 @@ public class RuntimeRpc extends AbstractSingletonNetconfOperation {
         return document;
     }
 
-    //TODO move all occurences of this method in mdsal netconf(and xml factories) to a utility class
     private Node transformNormalizedNode(final Document document, final NormalizedNode<?, ?> data, final SchemaPath rpcOutputPath) {
         final DOMResult result = new DOMResult(document.createElement(XmlMappingConstants.RPC_REPLY_KEY));
 
index b4de3164e1b7abf315da4eb686ab09871a61fcca..7bf710f37076e609b47cdd92b50288d6c8c08b40 100644 (file)
@@ -13,11 +13,7 @@ import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Throwables;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
 import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Collections;
 import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
@@ -36,18 +32,11 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
@@ -63,10 +52,12 @@ public abstract class AbstractGet extends AbstractSingletonNetconfOperation {
     protected static final String FILTER = "filter";
     static final YangInstanceIdentifier ROOT = YangInstanceIdentifier.builder().build();
     protected final CurrentSchemaContext schemaContext;
+    private final FilterContentValidator validator;
 
     public AbstractGet(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext) {
         super(netconfSessionIdForReporting);
         this.schemaContext = schemaContext;
+        this.validator = new FilterContentValidator(schemaContext);
     }
 
     private static final XMLOutputFactory XML_OUTPUT_FACTORY;
@@ -111,7 +102,6 @@ public abstract class AbstractGet extends AbstractSingletonNetconfOperation {
         return SchemaPath.create(Iterables.transform(dataRoot.getPathArguments(), PATH_ARG_TO_QNAME), dataRoot.equals(ROOT));
     }
 
-    // TODO this code is located in Restconf already
     private void writeRootElement(final XMLStreamWriter xmlWriter, final NormalizedNodeWriter nnWriter, final ContainerNode data) {
         try {
             if (data.getNodeType().equals(SchemaContext.NAME)) {
@@ -128,26 +118,6 @@ public abstract class AbstractGet extends AbstractSingletonNetconfOperation {
         }
     }
 
-    private DataSchemaNode getSchemaNodeFromNamespace(final XmlElement element) throws DocumentedException {
-
-        try {
-            final Module module = schemaContext.getCurrentContext().findModuleByNamespaceAndRevision(new URI(element.getNamespace()), null);
-            DataSchemaNode dataSchemaNode = module.getDataChildByName(element.getName());
-            if (dataSchemaNode != null) {
-                return dataSchemaNode;
-            }
-        } catch (URISyntaxException e) {
-            LOG.debug("Error during parsing of element namespace, this should not happen since namespace of an xml " +
-                    "element is valid and if the xml was parsed then the URI should be as well");
-            throw new IllegalArgumentException("Unable to parse element namespace, this should not happen since " +
-                    "namespace of an xml element is valid and if the xml was parsed then the URI should be as well");
-        }
-        throw new DocumentedException("Unable to find node with namespace: " + element.getNamespace() + "in schema context: " + schemaContext.getCurrentContext().toString(),
-                ErrorType.application,
-                ErrorTag.unknown_namespace,
-                ErrorSeverity.error);
-    }
-
     protected Element serializeNodeWithParentStructure(Document document, YangInstanceIdentifier dataRoot, NormalizedNode node) {
         if (!dataRoot.equals(ROOT)) {
             return (Element) transformNormalizedNode(document,
@@ -186,37 +156,7 @@ public abstract class AbstractGet extends AbstractSingletonNetconfOperation {
         }
 
         XmlElement element = filterElement.getOnlyChildElement();
-        DataSchemaNode schemaNode = getSchemaNodeFromNamespace(element);
-
-        return getReadPointFromNode(YangInstanceIdentifier.builder().build(), filterToNormalizedNode(element, schemaNode));
-    }
-
-    private YangInstanceIdentifier getReadPointFromNode(final YangInstanceIdentifier pathArg, final NormalizedNode nNode) {
-        final YangInstanceIdentifier path = pathArg.node(nNode.getIdentifier());
-        if (nNode instanceof DataContainerNode) {
-            DataContainerNode node = (DataContainerNode) nNode;
-            if (node.getValue().size() == 1) {
-                return getReadPointFromNode(path, (NormalizedNode) Lists.newArrayList(node.getValue()).get(0));
-            }
-        }
-        return path;
-    }
-
-    private NormalizedNode filterToNormalizedNode(XmlElement element, DataSchemaNode schemaNode) throws DocumentedException {
-        DomToNormalizedNodeParserFactory parserFactory = DomToNormalizedNodeParserFactory
-                .getInstance(DomUtils.defaultValueCodecProvider(), schemaContext.getCurrentContext());
-
-        final NormalizedNode parsedNode;
-
-        if (schemaNode instanceof ContainerSchemaNode) {
-            parsedNode = parserFactory.getContainerNodeParser().parse(Collections.singletonList(element.getDomElement()), (ContainerSchemaNode) schemaNode);
-        } else if (schemaNode instanceof ListSchemaNode) {
-            parsedNode = parserFactory.getMapNodeParser().parse(Collections.singletonList(element.getDomElement()), (ListSchemaNode) schemaNode);
-        } else {
-            throw new DocumentedException("Schema node of the top level element is not an instance of container or list",
-                    ErrorType.application, ErrorTag.unknown_element, ErrorSeverity.error);
-        }
-        return parsedNode;
+        return validator.validate(element);
     }
 
     protected static final class GetConfigExecution {
diff --git a/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/ops/get/FilterContentValidator.java b/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/ops/get/FilterContentValidator.java
new file mode 100644 (file)
index 0000000..7e7a157
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netconf.mdsal.connector.ops.get;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.controller.config.util.xml.DocumentedException;
+import org.opendaylight.controller.config.util.xml.MissingNameSpaceException;
+import org.opendaylight.controller.config.util.xml.XmlElement;
+import org.opendaylight.netconf.mdsal.connector.CurrentSchemaContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+/**
+ * Class validates filter content against schema context.
+ */
+public class FilterContentValidator {
+
+    private final CurrentSchemaContext schemaContext;
+
+    /**
+     * @param schemaContext current schema context
+     */
+    public FilterContentValidator(CurrentSchemaContext schemaContext) {
+        this.schemaContext = schemaContext;
+    }
+
+    /**
+     * Validates filter content against this validator schema context. If the filter is valid, method returns {@link YangInstanceIdentifier}
+     * of node which can be used as root for data selection.
+     * @param filterContent filter content
+     * @return YangInstanceIdentifier
+     * @throws DocumentedException if filter content is not valid
+     */
+    public YangInstanceIdentifier validate(XmlElement filterContent) throws DocumentedException {
+        try {
+            final URI namespace = new URI(filterContent.getNamespace());
+            final Module module = schemaContext.getCurrentContext().findModuleByNamespaceAndRevision(namespace, null);
+            final DataSchemaNode schema = getRootDataSchemaNode(module, namespace, filterContent.getName());
+            final FilterTree filterTree = validateNode(filterContent, schema, new FilterTree(schema.getQName(), Type.OTHER));
+            return getFilterDataRoot(filterTree, YangInstanceIdentifier.builder());
+        } catch (DocumentedException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new DocumentedException("Validation failed. Cause: " + e.getMessage(),
+                    DocumentedException.ErrorType.application,
+                    DocumentedException.ErrorTag.unknown_namespace,
+                    DocumentedException.ErrorSeverity.error);
+        }
+    }
+
+    /**
+     * Returns module's child data node of given name space and name
+     * @param module module
+     * @param nameSpace name space
+     * @param name name
+     * @return child data node schema
+     * @throws DocumentedException if child with given name is not present
+     */
+    private DataSchemaNode getRootDataSchemaNode(Module module, URI nameSpace, String name) throws DocumentedException {
+        final Collection<DataSchemaNode> childNodes = module.getChildNodes();
+        for (DataSchemaNode childNode : childNodes) {
+            final QName qName = childNode.getQName();
+            if (qName.getNamespace().equals(nameSpace) && qName.getLocalName().equals(name)) {
+                return childNode;
+            }
+        }
+        throw new DocumentedException("Unable to find node with namespace: " + nameSpace + "in schema context: " + schemaContext.getCurrentContext().toString(),
+                DocumentedException.ErrorType.application,
+                DocumentedException.ErrorTag.unknown_namespace,
+                DocumentedException.ErrorSeverity.error);
+    }
+
+    /**
+     * Recursively checks filter elements against the schema. Returns tree of nodes QNames as they appear in filter.
+     * @param element element to check
+     * @param parentNodeSchema parent node schema
+     * @param tree parent node tree
+     * @return tree
+     * @throws ValidationException if filter content is not valid
+     */
+    private FilterTree validateNode(XmlElement element, DataSchemaNode parentNodeSchema, FilterTree tree) throws ValidationException {
+        final List<XmlElement> childElements = element.getChildElements();
+        for (XmlElement childElement : childElements) {
+            try {
+                final Deque<DataSchemaNode> path = findSchemaNodeByNameAndNamespace(parentNodeSchema, childElement.getName(), new URI(childElement.getNamespace()));
+                if (path.isEmpty()) {
+                    throw new ValidationException(element, childElement);
+                }
+                FilterTree subtree = tree;
+                for (DataSchemaNode dataSchemaNode : path) {
+                        subtree = subtree.addChild(dataSchemaNode);
+                }
+                final DataSchemaNode childSchema = path.getLast();
+                validateNode(childElement, childSchema, subtree);
+            } catch (URISyntaxException | MissingNameSpaceException e) {
+                throw new RuntimeException("Wrong namespace in element + " + childElement.toString());
+            }
+        }
+        return tree;
+    }
+
+    /**
+     * Searches for YangInstanceIdentifier of node, which can be used as root for data selection.
+     * It goes as deep in tree as possible. Method stops traversing, when there are multiple child elements
+     * or when it encounters list node.
+     * @param tree QName tree
+     * @param builder builder
+     * @return YangInstanceIdentifier
+     */
+    private YangInstanceIdentifier getFilterDataRoot(FilterTree tree, YangInstanceIdentifier.InstanceIdentifierBuilder builder) {
+        builder.node(tree.getName());
+        while (tree.getChildren().size() == 1) {
+            final FilterTree child = tree.getChildren().iterator().next();
+            if (child.getType() == Type.CHOICE_CASE) {
+                tree = child;
+                continue;
+            }
+            builder.node(child.getName());
+            if (child.getType() == Type.LIST) {
+                return builder.build();
+            }
+            tree = child;
+        }
+        return builder.build();
+    }
+
+    //FIXME this method will also be in yangtools ParserUtils, use that when https://git.opendaylight.org/gerrit/#/c/37031/ will be merged
+    /**
+     * Returns stack of schema nodes via which it was necessary to pass to get schema node with specified
+     * {@code childName} and {@code namespace}
+     *
+     * @param dataSchemaNode
+     * @param childName
+     * @param namespace
+     * @return stack of schema nodes via which it was passed through. If found schema node is direct child then stack
+     *         contains only one node. If it is found under choice and case then stack should contains 2*n+1 element
+     *         (where n is number of choices through it was passed)
+     */
+    private Deque<DataSchemaNode> findSchemaNodeByNameAndNamespace(final DataSchemaNode dataSchemaNode,
+                                                                   final String childName, final URI namespace) {
+        final Deque<DataSchemaNode> result = new ArrayDeque<>();
+        final List<ChoiceSchemaNode> childChoices = new ArrayList<>();
+        DataSchemaNode potentialChildNode = null;
+        if (dataSchemaNode instanceof DataNodeContainer) {
+            for (final DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
+                if (childNode instanceof ChoiceSchemaNode) {
+                    childChoices.add((ChoiceSchemaNode) childNode);
+                } else {
+                    final QName childQName = childNode.getQName();
+
+                    if (childQName.getLocalName().equals(childName) && childQName.getNamespace().equals(namespace)) {
+                        if (potentialChildNode == null ||
+                                childQName.getRevision().after(potentialChildNode.getQName().getRevision())) {
+                            potentialChildNode = childNode;
+                        }
+                    }
+                }
+            }
+        }
+        if (potentialChildNode != null) {
+            result.push(potentialChildNode);
+            return result;
+        }
+
+        // try to find data schema node in choice (looking for first match)
+        for (final ChoiceSchemaNode choiceNode : childChoices) {
+            for (final ChoiceCaseNode concreteCase : choiceNode.getCases()) {
+                final Deque<DataSchemaNode> resultFromRecursion = findSchemaNodeByNameAndNamespace(concreteCase, childName,
+                        namespace);
+                if (!resultFromRecursion.isEmpty()) {
+                    resultFromRecursion.push(concreteCase);
+                    resultFromRecursion.push(choiceNode);
+                    return resultFromRecursion;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Class represents tree of QNames as they are present in the filter.
+     */
+    private static class FilterTree {
+
+        private final QName name;
+        private final Type type;
+        private final Map<QName, FilterTree> children;
+
+        FilterTree(QName name, Type type) {
+            this.name = name;
+            this.type = type;
+            this.children = new HashMap<>();
+        }
+
+        FilterTree addChild(DataSchemaNode data) {
+            Type type;
+            if (data instanceof ChoiceCaseNode) {
+                type = Type.CHOICE_CASE;
+            } else if (data instanceof ListSchemaNode) {
+                type = Type.LIST;
+            } else {
+                type = Type.OTHER;
+            }
+            final QName name = data.getQName();
+            FilterTree childTree = children.get(name);
+            if (childTree == null) {
+                childTree = new FilterTree(name, type);
+            }
+            children.put(name, childTree);
+            return childTree;
+        }
+
+        Collection<FilterTree> getChildren() {
+            return children.values();
+        }
+
+        QName getName() {
+            return name;
+        }
+
+        Type getType() {
+            return type;
+        }
+    }
+
+    private enum Type {
+        LIST, CHOICE_CASE, OTHER
+    }
+
+    private static class ValidationException extends Exception {
+        public ValidationException(XmlElement parent, XmlElement child) {
+            super("Element " + child + " can't be child of " + parent);
+        }
+    }
+
+}
index 4bf2a2b2738decd223b18b53e43c5b043b0d6132..80a7181ba6566d246757f70aa73308106d0cea18 100644 (file)
@@ -15,6 +15,7 @@ import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doAnswer;
 
+import com.google.common.io.ByteSource;
 import com.google.common.util.concurrent.Futures;
 import java.io.IOException;
 import java.io.InputStream;
@@ -22,11 +23,9 @@ import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.EnumMap;
 import java.util.List;
 import java.util.concurrent.ExecutorService;
-
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.OutputKeys;
 import javax.xml.transform.Transformer;
@@ -34,7 +33,6 @@ import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
-
 import org.custommonkey.xmlunit.DetailedDiff;
 import org.custommonkey.xmlunit.Diff;
 import org.custommonkey.xmlunit.XMLUnit;
@@ -44,7 +42,7 @@ import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
-import org.opendaylight.controller.cluster.datastore.ConcurrentDOMDataBroker;
+import org.opendaylight.controller.cluster.databroker.ConcurrentDOMDataBroker;
 import org.opendaylight.controller.config.util.xml.DocumentedException;
 import org.opendaylight.controller.config.util.xml.DocumentedException.ErrorSeverity;
 import org.opendaylight.controller.config.util.xml.DocumentedException.ErrorTag;
@@ -67,7 +65,6 @@ import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
@@ -84,8 +81,6 @@ import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
 
-import com.google.common.io.ByteSource;
-
 public class NetconfMDSalMappingTest {
 
     private static final Logger LOG = LoggerFactory.getLogger(NetconfMDSalMappingTest.class);
@@ -107,7 +102,7 @@ public class NetconfMDSalMappingTest {
     private static final QName INNER_CHOICE_TEXT = QName.create("urn:opendaylight:mdsal:mapping:test", "2015-02-26", "text");
 
     private static final YangInstanceIdentifier AUGMENTED_CONTAINER_IN_MODULES =
-            YangInstanceIdentifier.builder().node(TOP).node(MODULES).build().node(new AugmentationIdentifier(Collections.singleton(AUGMENTED_CONTAINER)));
+            YangInstanceIdentifier.builder().node(TOP).node(MODULES).build();
 
     private static Document RPC_REPLY_OK = null;
 
@@ -493,19 +488,18 @@ public class NetconfMDSalMappingTest {
         verifyResponse(edit("messages/mapping/editConfigs/editConfig-filtering-setup.xml"), RPC_REPLY_OK);
         verifyResponse(commit(), RPC_REPLY_OK);
 
-        //TODO uncomment these tests once we can parse KeyedListNode as a selection node, currently you cannot use a KeyedList as a selection node in filter
-//        verifyFilterIdentifier("messages/mapping/filters/get-filter-alluser.xml",
-//                YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
+        verifyFilterIdentifier("messages/mapping/filters/get-filter-alluser.xml",
+                YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
         verifyFilterIdentifier("messages/mapping/filters/get-filter-company-info.xml",
                 YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
         verifyFilterIdentifier("messages/mapping/filters/get-filter-modules-and-admin.xml",
                 YangInstanceIdentifier.builder().node(TOP).build());
         verifyFilterIdentifier("messages/mapping/filters/get-filter-only-names-types.xml",
                 YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
-//        verifyFilterIdentifier("messages/mapping/filters/get-filter-specific-module-type-and-user.xml",
-//                YangInstanceIdentifier.builder().node(TOP).build());
-//        verifyFilterIdentifier("messages/mapping/filters/get-filter-superuser.xml",
-//                YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
+        verifyFilterIdentifier("messages/mapping/filters/get-filter-specific-module-type-and-user.xml",
+                YangInstanceIdentifier.builder().node(TOP).build());
+        verifyFilterIdentifier("messages/mapping/filters/get-filter-superuser.xml",
+                YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
         verifyFilterIdentifier("messages/mapping/filters/get-filter-users.xml",
                 YangInstanceIdentifier.builder().node(TOP).node(USERS).build());
 
@@ -535,10 +529,10 @@ public class NetconfMDSalMappingTest {
         //verifyResponse(edit("messages/mapping/editConfigs/editConfig-filtering-setup2.xml"), RPC_REPLY_OK);
         //verifyResponse(commit(), RPC_REPLY_OK);
 
-//        verifyFilterIdentifier("messages/mapping/filters/get-filter-augmented-case-inner-choice.xml",
-//                YangInstanceIdentifier.builder().node(TOP).node(CHOICE_NODE).node(CHOICE_WRAPPER).build());
-//        verifyFilterIdentifier("messages/mapping/filters/get-filter-augmented-case-inner-case.xml",
-//                YangInstanceIdentifier.builder().node(TOP).node(CHOICE_NODE).node(CHOICE_WRAPPER).node(INNER_CHOICE).node(INNER_CHOICE_TEXT).build());
+        verifyFilterIdentifier("messages/mapping/filters/get-filter-augmented-case-inner-choice.xml",
+                YangInstanceIdentifier.builder().node(TOP).node(CHOICE_NODE).node(CHOICE_WRAPPER).build());
+        verifyFilterIdentifier("messages/mapping/filters/get-filter-augmented-case-inner-case.xml",
+                YangInstanceIdentifier.builder().node(TOP).node(CHOICE_NODE).node(CHOICE_WRAPPER).node(INNER_CHOICE).node(INNER_CHOICE_TEXT).build());
 
 //        verifyResponse(getConfigWithFilter("messages/mapping/filters/get-filter-augmented-string.xml"),
 //                XmlFileLoader.xmlFileToDocument("messages/mapping/filters/response-augmented-string.xml"));
@@ -556,7 +550,7 @@ public class NetconfMDSalMappingTest {
         TestingGetConfig getConfig = new TestingGetConfig(sessionIdForReporting, currentSchemaContext, transactionProvider);
         Document request = XmlFileLoader.xmlFileToDocument(resource);
         YangInstanceIdentifier iid = getConfig.getInstanceIdentifierFromDocument(request);
-        assertTrue(iid.equals(identifier));
+        assertEquals(identifier, iid);
     }
 
     private class TestingGetConfig extends GetConfig{
diff --git a/netconf/mdsal-netconf-connector/src/test/java/org/opendaylight/netconf/mdsal/connector/ops/get/FilterContentValidatorTest.java b/netconf/mdsal-netconf-connector/src/test/java/org/opendaylight/netconf/mdsal/connector/ops/get/FilterContentValidatorTest.java
new file mode 100644 (file)
index 0000000..bd0b59f
--- /dev/null
@@ -0,0 +1,99 @@
+package org.opendaylight.netconf.mdsal.connector.ops.get;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.model.InitializationError;
+import org.opendaylight.controller.config.util.xml.XmlElement;
+import org.opendaylight.controller.config.util.xml.XmlUtil;
+import org.opendaylight.netconf.mdsal.connector.CurrentSchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+@RunWith(value = Parameterized.class)
+public class FilterContentValidatorTest {
+
+    private static final int TEST_CASE_COUNT = 8;
+    private final XmlElement filterContent;
+    private final String expected;
+    private FilterContentValidator validator;
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() throws IOException, SAXException, URISyntaxException, InitializationError {
+        List<Object[]> result = new ArrayList<>();
+        final Path path = Paths.get(FilterContentValidatorTest.class.getResource("/filter/expected.txt").toURI());
+        final List<String> expected = Files.readAllLines(path);
+        if (expected.size() != TEST_CASE_COUNT) {
+            throw new InitializationError("Number of lines in results file must be same as test case count");
+        }
+        for (int i = 1; i <= TEST_CASE_COUNT; i++) {
+            final Document document = XmlUtil.readXmlToDocument(FilterContentValidatorTest.class.getResourceAsStream("/filter/f" + i + ".xml"));
+            result.add(new Object[]{document, expected.get(i-1)});
+        }
+        return result;
+    }
+
+    public FilterContentValidatorTest(Document filterContent, String expected) {
+        this.filterContent = XmlElement.fromDomDocument(filterContent);
+        this.expected = expected;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        List<InputStream> sources = new ArrayList<>();
+        sources.add(getClass().getResourceAsStream("/yang/filter-validator-test-mod-0.yang"));
+        sources.add(getClass().getResourceAsStream("/yang/filter-validator-test-augment.yang"));
+        SchemaContext context = parseYangSources(sources);
+        CurrentSchemaContext currentContext = mock(CurrentSchemaContext.class);
+        doReturn(context).when(currentContext).getCurrentContext();
+        validator = new FilterContentValidator(currentContext);
+    }
+
+    @Test
+    public void testValidate() throws Exception {
+        if (expected.startsWith("success")) {
+            final String expId = expected.replace("success=", "");
+            Assert.assertEquals(expId, validator.validate(filterContent).toString());
+        } else if (expected.startsWith("error")) {
+            try {
+                validator.validate(filterContent);
+                Assert.fail(XmlUtil.toString(filterContent) + " is not valid and should throw exception.");
+            } catch (Exception e) {
+                final String expectedExceptionClass = expected.replace("error=", "");
+                Assert.assertEquals(expectedExceptionClass, e.getClass().getName());
+            }
+        }
+
+    }
+
+    public static SchemaContext parseYangSources(Collection<InputStream> testFiles)
+            throws SourceException, ReactorException, FileNotFoundException {
+        CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR
+                .newBuild();
+        for (InputStream testFile : testFiles) {
+            reactor.addSource(new YangStatementSourceImpl(testFile));
+        }
+        return reactor.buildEffective();
+    }
+}
\ No newline at end of file
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/filter/expected.txt b/netconf/mdsal-netconf-connector/src/test/resources/filter/expected.txt
new file mode 100644 (file)
index 0000000..8b15a01
--- /dev/null
@@ -0,0 +1,8 @@
+error=org.opendaylight.controller.config.util.xml.DocumentedException
+success=/(urn:dummy:mod-0?revision=2016-03-01)mainroot
+success=/(urn:dummy:mod-0?revision=2016-03-01)mainroot/choiceList
+success=/(urn:dummy:mod-0?revision=2016-03-01)mainroot/maincontent
+success=/(urn:dummy:mod-0?revision=2016-03-01)mainroot/choiceList
+success=/(urn:dummy:mod-0?revision=2016-03-01)mainroot
+success=/(urn:dummy:mod-0?revision=2016-03-01)mainroot/(urn:dummy:aug?revision=1999-08-17)augmented-leaf
+error=org.opendaylight.controller.config.util.xml.DocumentedException
\ No newline at end of file
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/filter/f1.xml b/netconf/mdsal-netconf-connector/src/test/resources/filter/f1.xml
new file mode 100644 (file)
index 0000000..c2c5ef1
--- /dev/null
@@ -0,0 +1,4 @@
+<mainroot xmlns="urn:dummy:mod-0">
+    <maincontent></maincontent>
+    <not-in-schema/>
+</mainroot>
\ No newline at end of file
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/filter/f2.xml b/netconf/mdsal-netconf-connector/src/test/resources/filter/f2.xml
new file mode 100644 (file)
index 0000000..9dd67df
--- /dev/null
@@ -0,0 +1,8 @@
+<mainroot xmlns="urn:dummy:mod-0">
+    <maincontent/>
+    <choiceList>
+        <choice-leaf>
+
+        </choice-leaf>
+    </choiceList>
+</mainroot>
\ No newline at end of file
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/filter/f3.xml b/netconf/mdsal-netconf-connector/src/test/resources/filter/f3.xml
new file mode 100644 (file)
index 0000000..3c5b656
--- /dev/null
@@ -0,0 +1,12 @@
+<mainroot xmlns="urn:dummy:mod-0">
+        <choiceList>
+            <choice-leaf>
+                aaa
+            </choice-leaf>
+        </choiceList>
+        <choiceList>
+            <choice-leaf>
+                bbb
+            </choice-leaf>
+        </choiceList>
+</mainroot>
\ No newline at end of file
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/filter/f4.xml b/netconf/mdsal-netconf-connector/src/test/resources/filter/f4.xml
new file mode 100644 (file)
index 0000000..502d0b5
--- /dev/null
@@ -0,0 +1,3 @@
+<mainroot xmlns="urn:dummy:mod-0">
+        <maincontent/>
+</mainroot>
\ No newline at end of file
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/filter/f5.xml b/netconf/mdsal-netconf-connector/src/test/resources/filter/f5.xml
new file mode 100644 (file)
index 0000000..6e13113
--- /dev/null
@@ -0,0 +1,3 @@
+<mainroot xmlns="urn:dummy:mod-0">
+    <choiceList></choiceList>
+</mainroot>
\ No newline at end of file
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/filter/f6.xml b/netconf/mdsal-netconf-connector/src/test/resources/filter/f6.xml
new file mode 100644 (file)
index 0000000..2fe4c78
--- /dev/null
@@ -0,0 +1,4 @@
+<mainroot xmlns="urn:dummy:mod-0">
+    <maincontent/>
+    <choiceList></choiceList>
+</mainroot>
\ No newline at end of file
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/filter/f7.xml b/netconf/mdsal-netconf-connector/src/test/resources/filter/f7.xml
new file mode 100644 (file)
index 0000000..a59c407
--- /dev/null
@@ -0,0 +1,3 @@
+<mainroot xmlns="urn:dummy:mod-0">
+    <augmented-leaf xmlns="urn:dummy:aug"/>
+</mainroot>
\ No newline at end of file
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/filter/f8.xml b/netconf/mdsal-netconf-connector/src/test/resources/filter/f8.xml
new file mode 100644 (file)
index 0000000..e3eec67
--- /dev/null
@@ -0,0 +1,6 @@
+<mainroot xmlns="urn:dummy:mod-0">
+    <maincontent>
+        <choiceList>
+        </choiceList>
+    </maincontent>
+</mainroot>
\ No newline at end of file
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/yang/filter-validator-test-augment.yang b/netconf/mdsal-netconf-connector/src/test/resources/yang/filter-validator-test-augment.yang
new file mode 100644 (file)
index 0000000..0723989
--- /dev/null
@@ -0,0 +1,14 @@
+module filter-validator-test-augment {
+    namespace "urn:dummy:aug";
+    prefix "aug-0";
+    revision "1999-08-17";
+    import filter-validator-test-mod-0 {
+        prefix mod-0;
+        revision-date "2016-03-01";
+    }
+    augment "/mod-0:mainroot" {
+        leaf augmented-leaf {
+                type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/yang/filter-validator-test-mod-0.yang b/netconf/mdsal-netconf-connector/src/test/resources/yang/filter-validator-test-mod-0.yang
new file mode 100644 (file)
index 0000000..bee040a
--- /dev/null
@@ -0,0 +1,31 @@
+module filter-validator-test-mod-0 {
+    namespace "urn:dummy:mod-0";
+    prefix "mod-0";
+    revision "2016-03-01";
+    container mainroot {
+        leaf maincontent {
+            mandatory true;
+            type string;
+        }
+        list choiceList {
+            key name;
+            leaf name {
+                type string;
+            }
+            choice v {
+                case a {
+                    leaf choice-leaf {
+                        type string;
+                    }
+                }
+                case b {
+                    container choice-cont {
+                        leaf content {
+                            type string;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
index 94678c9d5589158ea859fa459d4cc8aa48cdadd8..9824e1e57bd14744820119355f594ba8d51e8e6e 100644 (file)
@@ -30,6 +30,14 @@ public class NetconfNotificationOperationService implements NetconfOperationServ
 
     @Override
     public void close() {
-
+        for (NetconfOperation netconfOperation : netconfOperations) {
+            if (netconfOperation instanceof AutoCloseable) {
+                try {
+                    ((AutoCloseable) netconfOperation).close();
+                } catch (Exception e) {
+                    throw new IllegalStateException("Exception while closing " + netconfOperation, e);
+                }
+            }
+        }
     }
 }
index 01d229bf24fdc50f359df00cd69500911f7808e9..31246bbea019fc6e4cc0f52261384acf55232d9a 100644 (file)
@@ -237,7 +237,7 @@ public class NetconfEventSource implements EventSource, DOMNotificationListener
     }
 
     private AnyXmlNode encapsulate(final DOMNotification body) {
-        // FIXME: Introduce something like AnyXmlWithNormalizedNodeData in Yangtools
+        // FIXME: Introduce something like YangModeledAnyXmlNode in Yangtools
         final Document doc = XmlUtil.newDocument();
         final Optional<String> namespace = Optional.of(PAYLOAD_ARG.getNodeType().getNamespace().toString());
         final Element element = XmlUtil.createElement(doc, "payload", namespace);
index 03b211d472ab8fd3200110866e414db7049d5368..32557653c0e5913621cac2f6ba7b7c60a05fe328 100644 (file)
@@ -9,6 +9,7 @@
 package org.opendaylight.netconf.api.messages;
 
 import com.google.common.base.Preconditions;
+import com.google.common.net.InetAddresses;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -90,11 +91,10 @@ public class NetconfHelloMessageAdditionalHeader {
         return sb.toString();
     }
 
-    // TODO IPv6
     private static final Pattern PATTERN = Pattern
-            .compile("\\[(?<username>[^;]+);(?<address>[0-9\\.]+)[:/](?<port>[0-9]+);(?<transport>[a-z]+)[^\\]]+\\]");
+            .compile("\\[(?<username>[^;]+);(?<address>.+)[:/](?<port>[0-9]+);(?<transport>[a-z]+)[^\\]]+\\]");
     private static final Pattern CUSTOM_HEADER_PATTERN = Pattern
-            .compile("\\[(?<username>[^;]+);(?<address>[0-9\\.]+)[:/](?<port>[0-9]+);(?<transport>[a-z]+);(?<sessionIdentifier>[a-z]+)[^\\]]+\\]");
+            .compile("\\[(?<username>[^;]+);(?<address>.+)[:/](?<port>[0-9]+);(?<transport>[a-z]+);(?<sessionIdentifier>[a-z]+)[^\\]]+\\]");
 
     /**
      * Parse additional header from a formatted string
@@ -108,6 +108,7 @@ public class NetconfHelloMessageAdditionalHeader {
 
         String username = matcher.group("username");
         String address = matcher.group("address");
+        Preconditions.checkArgument(InetAddresses.isInetAddress(address));
         String port = matcher.group("port");
         String transport = matcher.group("transport");
         String sessionIdentifier = "client";
index ce91d01334ab58e4dcfb02ba4d0f77d2b9dca554..6383b1845beedf7c04b0a0f2693836c5c9893183 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -9,6 +9,7 @@
 package org.opendaylight.controller.config.yang.config.netconf.northbound.impl;
 
 import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.netconf.impl.NetconfServerSessionNegotiatorFactoryBuilder;
 import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
 import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService;
 import org.opendaylight.netconf.impl.NetconfServerDispatcherImpl;
@@ -16,12 +17,12 @@ import org.opendaylight.netconf.impl.NetconfServerSessionNegotiatorFactory;
 import org.opendaylight.netconf.impl.SessionIdProvider;
 import org.opendaylight.netconf.impl.osgi.AggregatedNetconfOperationServiceFactory;
 
-public class NetconfServerDispatcherModule extends org.opendaylight.controller.config.yang.config.netconf.northbound.impl.AbstractNetconfServerDispatcherModule {
+public class NetconfServerDispatcherModule extends AbstractNetconfServerDispatcherModule {
     public NetconfServerDispatcherModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
         super(identifier, dependencyResolver);
     }
 
-    public NetconfServerDispatcherModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.config.netconf.northbound.impl.NetconfServerDispatcherModule oldModule, java.lang.AutoCloseable oldInstance) {
+    public NetconfServerDispatcherModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, NetconfServerDispatcherModule oldModule, AutoCloseable oldInstance) {
         super(identifier, dependencyResolver, oldModule, oldInstance);
     }
 
@@ -31,12 +32,18 @@ public class NetconfServerDispatcherModule extends org.opendaylight.controller.c
     }
 
     @Override
-    public java.lang.AutoCloseable createInstance() {
+    public AutoCloseable createInstance() {
 
         final AggregatedNetconfOperationServiceFactory aggregatedOpProvider = getAggregatedOpProvider();
         final NetconfMonitoringService monitoringService = getServerMonitorDependency();
-        final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
-                getTimerDependency(), aggregatedOpProvider, new SessionIdProvider(), getConnectionTimeoutMillis(), monitoringService);
+
+        final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactoryBuilder()
+                .setAggregatedOpService(aggregatedOpProvider)
+                .setTimer(getTimerDependency())
+                .setIdProvider(new SessionIdProvider())
+                .setMonitoringService(monitoringService)
+                .setConnectionTimeoutMillis(getConnectionTimeoutMillis())
+                .build();
         final NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer(
                 serverNegotiatorFactory);
 
index 16e9285d704e04b3d6a481fc70615a2d2029ba27..b6c88a60b255418dddde5fdcfbc6454ff104408c 100644 (file)
@@ -9,11 +9,14 @@
 package org.opendaylight.netconf.impl;
 
 import com.google.common.base.Preconditions;
+import com.google.common.net.InetAddresses;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
 import io.netty.handler.codec.ByteToMessageDecoder;
 import io.netty.handler.codec.MessageToByteEncoder;
+import java.net.Inet4Address;
+import java.net.InetAddress;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
@@ -26,8 +29,10 @@ import org.opendaylight.netconf.api.monitoring.NetconfManagementSession;
 import org.opendaylight.netconf.nettyutil.AbstractNetconfSession;
 import org.opendaylight.netconf.nettyutil.handler.NetconfMessageToXMLEncoder;
 import org.opendaylight.netconf.nettyutil.handler.NetconfXMLToMessageDecoder;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.DomainName;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.NetconfTcp;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.Session1;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.Session1Builder;
@@ -109,7 +114,14 @@ public final class NetconfServerSession extends AbstractNetconfSession<NetconfSe
         SessionBuilder builder = new SessionBuilder();
 
         builder.setSessionId(getSessionId());
-        builder.setSourceHost(new Host(new DomainName(header.getAddress())));
+        IpAddress address;
+        InetAddress address1 = InetAddresses.forString(header.getAddress());
+        if (address1 instanceof Inet4Address) {
+            address = new IpAddress(new Ipv4Address(header.getAddress()));
+        } else {
+            address = new IpAddress(new Ipv6Address(header.getAddress()));
+        }
+        builder.setSourceHost(new Host(address));
 
         Preconditions.checkState(DateAndTime.PATTERN_CONSTANTS.size() == 1);
         String formattedDateTime = dateFormatter.format(loginTime);
index ca61311cdcbd48f046d2be71644be9432e2296ad..f3a26ce40b97494d71a00cf10b72c6f3f0069d62 100644 (file)
@@ -82,8 +82,7 @@ public class NetconfServerSessionListener implements NetconfSessionListener<Netc
         try {
 
             Preconditions.checkState(operationRouter != null, "Cannot handle message, session up was not yet received");
-            // FIXME: there is no validation since the document may contain yang
-            // schemas
+            // there is no validation since the document may contain yang schemas
             final NetconfMessage message = processDocument(netconfMessage,
                     session);
             LOG.debug("Responding with message {}", message);
index 5b278efceceddaec738681a3f005b4ea5c35b8fc..3153277ca6d43907e79377a058d0db78b019dbb4 100644 (file)
@@ -18,15 +18,15 @@ import io.netty.util.Timer;
 import io.netty.util.concurrent.Promise;
 import java.net.SocketAddress;
 import java.util.Set;
-import org.opendaylight.netconf.mapping.api.NetconfOperationService;
-import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
-import org.opendaylight.netconf.api.messages.NetconfHelloMessage;
 import org.opendaylight.netconf.api.NetconfDocumentedException;
 import org.opendaylight.netconf.api.NetconfServerSessionPreferences;
+import org.opendaylight.netconf.api.messages.NetconfHelloMessage;
 import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService;
 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
 import org.opendaylight.netconf.impl.osgi.NetconfOperationRouter;
 import org.opendaylight.netconf.impl.osgi.NetconfOperationRouterImpl;
+import org.opendaylight.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
 import org.opendaylight.protocol.framework.SessionListenerFactory;
 import org.opendaylight.protocol.framework.SessionNegotiator;
 import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
@@ -52,15 +52,7 @@ public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorF
     private static final Logger LOG = LoggerFactory.getLogger(NetconfServerSessionNegotiatorFactory.class);
     private final Set<String> baseCapabilities;
 
-    // TODO too many params, refactor
-    public NetconfServerSessionNegotiatorFactory(final Timer timer, final NetconfOperationServiceFactory netconfOperationProvider,
-                                                 final SessionIdProvider idProvider, final long connectionTimeoutMillis,
-                                                 final NetconfMonitoringService monitoringService) {
-        this(timer, netconfOperationProvider, idProvider, connectionTimeoutMillis, monitoringService, DEFAULT_BASE_CAPABILITIES);
-    }
-
-    // TODO too many params, refactor
-    public NetconfServerSessionNegotiatorFactory(final Timer timer, final NetconfOperationServiceFactory netconfOperationProvider,
+    protected NetconfServerSessionNegotiatorFactory(final Timer timer, final NetconfOperationServiceFactory netconfOperationProvider,
                                                  final SessionIdProvider idProvider, final long connectionTimeoutMillis,
                                                  final NetconfMonitoringService monitoringService, final Set<String> baseCapabilities) {
         this.timer = timer;
@@ -68,9 +60,10 @@ public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorF
         this.idProvider = idProvider;
         this.connectionTimeoutMillis = connectionTimeoutMillis;
         this.monitoringService = monitoringService;
-        this.baseCapabilities = validateBaseCapabilities(baseCapabilities);
+        this.baseCapabilities = validateBaseCapabilities(baseCapabilities == null ? DEFAULT_BASE_CAPABILITIES : baseCapabilities);
     }
 
+
     private static ImmutableSet<String> validateBaseCapabilities(final Set<String> baseCapabilities) {
         // Check base capabilities to be supported by the server
         final Sets.SetView<String> unknownBaseCaps = Sets.difference(baseCapabilities, DEFAULT_BASE_CAPABILITIES);
@@ -86,7 +79,6 @@ public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorF
     }
 
     /**
-     *
      * @param defunctSessionListenerFactory will not be taken into account as session listener factory can
      *                                      only be created after snapshot is opened, thus this method constructs
      *                                      proper session listener factory.
diff --git a/netconf/netconf-impl/src/main/java/org/opendaylight/netconf/impl/NetconfServerSessionNegotiatorFactoryBuilder.java b/netconf/netconf-impl/src/main/java/org/opendaylight/netconf/impl/NetconfServerSessionNegotiatorFactoryBuilder.java
new file mode 100644 (file)
index 0000000..1f83e15
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.impl;
+
+import com.google.common.base.Preconditions;
+import io.netty.util.Timer;
+import java.util.Set;
+import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
+
+public class NetconfServerSessionNegotiatorFactoryBuilder {
+    private Timer timer;
+    private SessionIdProvider idProvider;
+    private NetconfOperationServiceFactory aggregatedOpService;
+    private long connectionTimeoutMillis;
+    private NetconfMonitoringService monitoringService;
+    private Set<String> baseCapabilities;
+
+    public NetconfServerSessionNegotiatorFactoryBuilder() {
+    }
+
+    public NetconfServerSessionNegotiatorFactoryBuilder setTimer(final Timer timer) {
+        this.timer = timer;
+        return this;
+    }
+
+    public NetconfServerSessionNegotiatorFactoryBuilder setIdProvider(final SessionIdProvider idProvider) {
+        this.idProvider = idProvider;
+        return this;
+    }
+
+    public NetconfServerSessionNegotiatorFactoryBuilder setAggregatedOpService(final NetconfOperationServiceFactory aggregatedOpService) {
+        this.aggregatedOpService = aggregatedOpService;
+        return this;
+    }
+
+    public NetconfServerSessionNegotiatorFactoryBuilder setConnectionTimeoutMillis(final long connectionTimeoutMillis) {
+        this.connectionTimeoutMillis = connectionTimeoutMillis;
+        return this;
+    }
+
+    public NetconfServerSessionNegotiatorFactoryBuilder setMonitoringService(final NetconfMonitoringService monitoringService) {
+        this.monitoringService = monitoringService;
+        return this;
+    }
+
+    public NetconfServerSessionNegotiatorFactoryBuilder setBaseCapabilities(final Set<String> baseCapabilities) {
+        this.baseCapabilities = baseCapabilities;
+        return this;
+    }
+
+
+    public NetconfServerSessionNegotiatorFactory build() {
+        validate();
+        return new NetconfServerSessionNegotiatorFactory(timer, aggregatedOpService, idProvider, connectionTimeoutMillis, monitoringService, baseCapabilities);
+    }
+
+
+    private void validate() {
+        Preconditions.checkNotNull(timer, "timer not initialized");
+        Preconditions.checkNotNull(aggregatedOpService, "NetconfOperationServiceFactory not initialized");
+        Preconditions.checkNotNull(idProvider, "SessionIdProvider not initialized");
+        Preconditions.checkArgument(connectionTimeoutMillis > 0, "connection time out <=0");
+        Preconditions.checkNotNull(monitoringService, "NetconfMonitoringService not initialized");
+
+        baseCapabilities = (baseCapabilities == null) ? NetconfServerSessionNegotiatorFactory.DEFAULT_BASE_CAPABILITIES : baseCapabilities;
+    }
+}
index 7512f2616b4eac6c44a905d2ea9b10c3a9703071..9d520bea56b82da45857f9e4029c24882d69762c 100644 (file)
@@ -14,15 +14,16 @@ import io.netty.util.HashedWheelTimer;
 import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.concurrent.TimeUnit;
-import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactoryListener;
-import org.opendaylight.netconf.notifications.BaseNotificationPublisherRegistration;
-import org.opendaylight.netconf.notifications.NetconfNotificationCollector;
-import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
 import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService;
 import org.opendaylight.netconf.impl.NetconfServerDispatcherImpl;
 import org.opendaylight.netconf.impl.NetconfServerDispatcherImpl.ServerChannelInitializer;
 import org.opendaylight.netconf.impl.NetconfServerSessionNegotiatorFactory;
+import org.opendaylight.netconf.impl.NetconfServerSessionNegotiatorFactoryBuilder;
 import org.opendaylight.netconf.impl.SessionIdProvider;
+import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactoryListener;
+import org.opendaylight.netconf.notifications.BaseNotificationPublisherRegistration;
+import org.opendaylight.netconf.notifications.NetconfNotificationCollector;
+import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
@@ -44,7 +45,7 @@ public class NetconfImplActivator implements BundleActivator {
     private BaseNotificationPublisherRegistration listenerReg;
 
     @Override
-    public void start(final BundleContext context)  {
+    public void start(final BundleContext context) {
         try {
             AggregatedNetconfOperationServiceFactory factoriesListener = new AggregatedNetconfOperationServiceFactory();
             startOperationServiceFactoryTracker(context, factoriesListener);
@@ -55,8 +56,13 @@ public class NetconfImplActivator implements BundleActivator {
 
             final NetconfMonitoringServiceImpl monitoringService = startMonitoringService(context, factoriesListener);
 
-            NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
-                    timer, factoriesListener, idProvider, connectionTimeoutMillis, monitoringService);
+            NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactoryBuilder()
+                    .setAggregatedOpService(factoriesListener)
+                    .setTimer(timer)
+                    .setIdProvider(idProvider)
+                    .setMonitoringService(monitoringService)
+                    .setConnectionTimeoutMillis(connectionTimeoutMillis)
+                    .build();
 
             eventLoopGroup = new NioEventLoopGroup();
 
index 27dbb67715c7e499b19f9796d4f6fb0a4572bedb..9d7d760b2173a36955b826a05718c6707966554c 100644 (file)
@@ -284,7 +284,6 @@ public class NetconfMonitoringServiceImpl implements NetconfMonitoringService, A
 
 
     private synchronized void onCapabilitiesAdded(final Set<Capability> addedCaps) {
-        // FIXME howto check for duplicates
         this.capabilities.putAll(Maps.uniqueIndex(setupCapabilities(addedCaps), CAPABILITY_TO_URI));
     }
 
index 83a88daf18213873ad2f3b3fe721022b63a6997e..35abee564046d5249f47cc3efd18090bc39d963b 100644 (file)
@@ -52,16 +52,8 @@ import org.junit.runners.Parameterized;
 import org.opendaylight.controller.config.util.capability.Capability;
 import org.opendaylight.controller.config.util.xml.DocumentedException;
 import org.opendaylight.controller.config.util.xml.XmlUtil;
-import org.opendaylight.netconf.mapping.api.HandlingPriority;
-import org.opendaylight.netconf.mapping.api.NetconfOperation;
-import org.opendaylight.netconf.mapping.api.NetconfOperationChainedExecution;
-import org.opendaylight.netconf.mapping.api.NetconfOperationService;
-import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
-import org.opendaylight.netconf.nettyutil.handler.exi.NetconfStartExiMessage;
-import org.opendaylight.netconf.api.messages.NetconfHelloMessageAdditionalHeader;
-import org.opendaylight.netconf.util.messages.NetconfMessageUtil;
-import org.opendaylight.netconf.util.test.XmlFileLoader;
 import org.opendaylight.netconf.api.NetconfMessage;
+import org.opendaylight.netconf.api.messages.NetconfHelloMessageAdditionalHeader;
 import org.opendaylight.netconf.api.monitoring.CapabilityListener;
 import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService;
 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
@@ -72,6 +64,14 @@ import org.opendaylight.netconf.client.TestingNetconfClient;
 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
 import org.opendaylight.netconf.impl.osgi.AggregatedNetconfOperationServiceFactory;
+import org.opendaylight.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.netconf.mapping.api.NetconfOperationChainedExecution;
+import org.opendaylight.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.netconf.nettyutil.handler.exi.NetconfStartExiMessage;
+import org.opendaylight.netconf.util.messages.NetconfMessageUtil;
+import org.opendaylight.netconf.util.test.XmlFileLoader;
 import org.opendaylight.protocol.framework.NeverReconnectStrategy;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.CapabilitiesBuilder;
@@ -160,8 +160,14 @@ public class ConcurrentClientsTest {
 
         SessionIdProvider idProvider = new SessionIdProvider();
 
-        NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
-                hashedWheelTimer, factoriesListener, idProvider, 5000, createMockedMonitoringService(), serverCaps);
+        NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactoryBuilder()
+                .setTimer(hashedWheelTimer)
+                .setAggregatedOpService(factoriesListener)
+                .setIdProvider(idProvider)
+                .setConnectionTimeoutMillis(5000)
+                .setMonitoringService(createMockedMonitoringService())
+                .setBaseCapabilities(serverCaps)
+                .build();
 
         NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer(serverNegotiatorFactory);
         final NetconfServerDispatcherImpl dispatch = new NetconfServerDispatcherImpl(serverChannelInitializer, nettyGroup, nettyGroup);
diff --git a/netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/MessageHeaderTest.java b/netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/MessageHeaderTest.java
deleted file mode 100644 (file)
index 40ab00c..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.netconf.impl;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-import org.opendaylight.netconf.util.messages.NetconfMessageHeader;
-
-@Deprecated
-public class MessageHeaderTest {
-    @Test
-    public void testFromBytes() {
-        final byte[] raw = new byte[] { (byte) 0x0a, (byte) 0x23, (byte) 0x35, (byte) 0x38, (byte) 0x0a };
-        NetconfMessageHeader header = NetconfMessageHeader.fromBytes(raw);
-        assertEquals(58, header.getLength());
-    }
-
-    @Test
-    public void testToBytes() {
-        NetconfMessageHeader header = new NetconfMessageHeader(123);
-        assertArrayEquals(new byte[] { (byte) 0x0a, (byte) 0x23, (byte) 0x31, (byte) 0x32, (byte) 0x33, (byte) 0x0a },
-                header.toBytes());
-    }
-}
index df34c45c5fc004f2f96b517fb1f7caaedbeff9fd..562d4a4ba993b4d307ca366e9e261a8f86f256a7 100644 (file)
@@ -15,12 +15,15 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import com.google.common.base.Charsets;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.embedded.EmbeddedChannel;
+import java.nio.ByteBuffer;
 import java.util.Queue;
 import org.junit.Before;
 import org.junit.Test;
+import org.opendaylight.netconf.api.NetconfMessage;
 import org.opendaylight.netconf.nettyutil.handler.ChunkedFramingMechanismEncoder;
 import org.opendaylight.netconf.nettyutil.handler.FramingMechanismHandlerFactory;
 import org.opendaylight.netconf.nettyutil.handler.NetconfChunkAggregator;
@@ -29,9 +32,7 @@ import org.opendaylight.netconf.nettyutil.handler.NetconfMessageToXMLEncoder;
 import org.opendaylight.netconf.nettyutil.handler.NetconfXMLToMessageDecoder;
 import org.opendaylight.netconf.util.messages.FramingMechanism;
 import org.opendaylight.netconf.util.messages.NetconfMessageConstants;
-import org.opendaylight.netconf.util.messages.NetconfMessageHeader;
 import org.opendaylight.netconf.util.test.XmlFileLoader;
-import org.opendaylight.netconf.api.NetconfMessage;
 
 public class MessageParserTest {
 
@@ -78,8 +79,7 @@ public class MessageParserTest {
             byte[] header = new byte[String.valueOf(exptHeaderLength).length()
                     + NetconfMessageConstants.MIN_HEADER_LENGTH - 1];
             recievedOutbound.getBytes(0, header);
-            NetconfMessageHeader messageHeader = NetconfMessageHeader.fromBytes(header);
-            assertEquals(exptHeaderLength, messageHeader.getLength());
+            assertEquals(exptHeaderLength, getHeaderLength(header));
 
             testChunkChannel.writeInbound(recievedOutbound);
         }
@@ -108,4 +108,10 @@ public class MessageParserTest {
         assertNotNull(receivedMessage);
         assertXMLEqual(this.msg.getDocument(), receivedMessage.getDocument());
     }
+
+    private static long getHeaderLength(byte[] bytes) {
+        byte[] HEADER_START = new byte[] { (byte) 0x0a, (byte) 0x23 };
+        return Long.parseLong(Charsets.US_ASCII.decode(
+                ByteBuffer.wrap(bytes, HEADER_START.length, bytes.length - HEADER_START.length - 1)).toString());
+    }
 }
index e464c704df29628abdac36a5c281f8c116ce47a5..ba38938917fcdf2f8ff00c39810ff5305c917202 100644 (file)
@@ -33,8 +33,14 @@ public class NetconfDispatcherImplTest {
 
         SessionIdProvider idProvider = new SessionIdProvider();
         hashedWheelTimer = new HashedWheelTimer();
-        NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
-                hashedWheelTimer, factoriesListener, idProvider, 5000, ConcurrentClientsTest.createMockedMonitoringService());
+
+        NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactoryBuilder()
+                .setAggregatedOpService(factoriesListener)
+                .setTimer(hashedWheelTimer)
+                .setIdProvider(idProvider)
+                .setMonitoringService(ConcurrentClientsTest.createMockedMonitoringService())
+                .setConnectionTimeoutMillis(5000)
+                .build();
 
         NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer(serverNegotiatorFactory);
 
index 6d966880198710ab0a9e28ef2e930d5e4d55d4b1..3b075433d3d3f86f592eec3a984291f1d9bac2d0 100644 (file)
@@ -11,6 +11,7 @@ import com.google.common.base.Joiner;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlTransient;
 import org.opendaylight.netconf.monitoring.MonitoringConstants;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.Session1;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -38,7 +39,12 @@ final class MonitoringSession {
 
     @XmlElement(name = "source-host")
     public String getSourceHost() {
-        return managementSession.getSourceHost().getDomainName().getValue();
+        final IpAddress ipAddress = managementSession.getSourceHost().getIpAddress();
+        if (ipAddress.getIpv4Address() != null) {
+            return ipAddress.getIpv4Address().getValue();
+        } else {
+            return ipAddress.getIpv6Address().getValue();
+        }
     }
 
     @XmlElement(name = "login-time")
index 98291dc06ca340267f98ce3ff60da3e91164b827..af332963e61a61a64c1effca215aee4fdcf6c285 100644 (file)
@@ -12,28 +12,26 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
-import com.google.common.base.Optional;
 import com.google.common.collect.Lists;
-import java.util.Set;
 import org.hamcrest.CoreMatchers;
+import org.junit.Before;
 import org.junit.Test;
-import org.opendaylight.controller.config.util.capability.Capability;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.config.util.xml.XmlUtil;
-import org.opendaylight.netconf.api.monitoring.NetconfManagementSession;
 import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService;
 import org.opendaylight.netconf.monitoring.xml.model.NetconfState;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.DomainName;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.NetconfTcp;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.extension.rev131210.Session1;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfSsh;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Transport;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Yang;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Capabilities;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SchemasBuilder;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SessionsBuilder;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.SchemaKey;
@@ -43,59 +41,44 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.
 
 public class JaxBSerializerTest {
 
+    private static final String IPV4 = "192.168.1.1";
+    private static final String IPV6 = "FE80:0000:0000:0000:0202:B3FF:FE1E:8329";
+    private static final String SESSION_XML = "<session>" +
+            "<session-id>1</session-id>" +
+            "<in-bad-rpcs>0</in-bad-rpcs>" +
+            "<in-rpcs>0</in-rpcs>" +
+            "<login-time>2010-10-10T12:32:32Z</login-time>" +
+            "<out-notifications>0</out-notifications>" +
+            "<out-rpc-errors>0</out-rpc-errors>" +
+            "<ncme:session-identifier>client</ncme:session-identifier>" +
+            "<source-host>%s</source-host>" +
+            "<transport>ncme:netconf-tcp</transport>" +
+            "<username>username</username>" +
+            "</session>";
+
+    @Mock
+    private NetconfMonitoringService monitoringService;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        doReturn(new SessionsBuilder().setSession(Lists.newArrayList(
+                getMockIPv4Session(NetconfTcp.class),
+                getMockIPv4Session(NetconfSsh.class),
+                getMockIPv6Session(NetconfTcp.class),
+                getMockIPv6Session(NetconfSsh.class)
+        )).build())
+                .when(monitoringService).getSessions();
+        doReturn(new SchemasBuilder().setSchema(Lists.newArrayList(getMockSchema("id", "v1", Yang.class), getMockSchema("id2", "", Yang.class))).build())
+                .when(monitoringService).getSchemas();
+    }
+
     @Test
     public void testSerialization() throws Exception {
 
-        final NetconfMonitoringService service = new NetconfMonitoringService() {
-
-            @Override
-            public void onCapabilitiesChanged(Set<Capability> added, Set<Capability> removed) {
-
-            }
-
-            @Override
-            public void onSessionUp(final NetconfManagementSession session) {
-
-            }
-
-            @Override
-            public void onSessionDown(final NetconfManagementSession session) {
-
-            }
-
-            @Override
-            public Sessions getSessions() {
-                return new SessionsBuilder().setSession(Lists.newArrayList(getMockSession(NetconfTcp.class), getMockSession(NetconfSsh.class))).build();
-            }
-
-            @Override
-            public Schemas getSchemas() {
-                return new SchemasBuilder().setSchema(Lists.newArrayList(getMockSchema("id", "v1", Yang.class), getMockSchema("id2", "", Yang.class))).build();
-            }
-
-            @Override
-            public String getSchemaForCapability(final String moduleName, final Optional<String> revision) {
-                return null;
-            }
-
-            @Override
-            public Capabilities getCapabilities() {
-                return null;
-            }
-
-            @Override
-            public AutoCloseable registerListener(final MonitoringListener listener) {
-                return new AutoCloseable() {
-                    @Override
-                    public void close() throws Exception {
-                        // NOOP
-                    }
-                };
-            }
-        };
-        final NetconfState model = new NetconfState(service);
+        final NetconfState model = new NetconfState(monitoringService);
         final String xml = XmlUtil.toString(new JaxBSerializer().toXml(model)).replaceAll("\\s", "");
-
+        System.out.println(xml);
         assertThat(xml, CoreMatchers.containsString(
                 "<schema>" +
                 "<format>yang</format>" +
@@ -106,18 +89,9 @@ public class JaxBSerializerTest {
                 "</schema>"));
 
         assertThat(xml, CoreMatchers.containsString(
-                "<session>" +
-                "<session-id>1</session-id>" +
-                "<in-bad-rpcs>0</in-bad-rpcs>" +
-                "<in-rpcs>0</in-rpcs>" +
-                "<login-time>2010-10-10T12:32:32Z</login-time>" +
-                "<out-notifications>0</out-notifications>" +
-                "<out-rpc-errors>0</out-rpc-errors>" +
-                "<ncme:session-identifier>client</ncme:session-identifier>" +
-                "<source-host>192.168.1.1</source-host>" +
-                "<transport>ncme:netconf-tcp</transport>" +
-                "<username>username</username>" +
-                "</session>"));
+                String.format(SESSION_XML, IPV4)));
+        assertThat(xml, CoreMatchers.containsString(
+                String.format(SESSION_XML, IPV6)));
     }
 
     private Schema getMockSchema(final String id, final String version, final Class<Yang> format) {
@@ -132,13 +106,24 @@ public class JaxBSerializerTest {
         return mock;
     }
 
+    private Session getMockIPv4Session(final Class<? extends Transport> transportType) {
+        final Session mocked = getMockSession(transportType);
+        doReturn(new Host(new IpAddress(new Ipv4Address(IPV4)))).when(mocked).getSourceHost();
+        return mocked;
+    }
+
+    private Session getMockIPv6Session(final Class<? extends Transport> transportType) {
+        final Session mocked = getMockSession(transportType);
+        doReturn(new Host(new IpAddress(new Ipv6Address(IPV6)))).when(mocked).getSourceHost();
+        return mocked;
+    }
+
     private Session getMockSession(final Class<? extends Transport> transportType) {
         final Session mocked = mock(Session.class);
         final Session1 mockedSession1 = mock(Session1.class);
         doReturn("client").when(mockedSession1).getSessionIdentifier();
         doReturn(1L).when(mocked).getSessionId();
         doReturn(new DateAndTime("2010-10-10T12:32:32Z")).when(mocked).getLoginTime();
-        doReturn(new Host(new DomainName("192.168.1.1"))).when(mocked).getSourceHost();
         doReturn(new ZeroBasedCounter32(0L)).when(mocked).getInBadRpcs();
         doReturn(new ZeroBasedCounter32(0L)).when(mocked).getInRpcs();
         doReturn(new ZeroBasedCounter32(0L)).when(mocked).getOutNotifications();
index 3482c1fbef34e4882b753e0cd3949f189101709f..b3d35dc3c0d05bd8811505b4c450948052c9f0ff 100644 (file)
@@ -65,7 +65,6 @@ public abstract class AbstractNetconfSessionNegotiator<P extends NetconfSessionP
     private final Timer timer;
     private final long connectionTimeoutMillis;
 
-    // TODO shrink constructor
     protected AbstractNetconfSessionNegotiator(final P sessionPreferences, final Promise<S> promise, final Channel channel, final Timer timer,
             final L sessionListener, final long connectionTimeoutMillis) {
         super(promise, channel);
index 41184f7d090f385355c73e099a6506c0115111ab..08b0dbac789e73e428b08abf8e7614ad7dd73742 100644 (file)
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-parser-impl</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>yang-jmx-generator-plugin</artifactId>
-        </dependency>
         <dependency>
             <groupId>org.opendaylight.netconf</groupId>
             <artifactId>sal-netconf-connector</artifactId>
index bfc919e5955d81740f0c8090387fa50164e76906..7a0d0f7dbb9527e060e304e0739b13e61d1440d4 100644 (file)
@@ -266,11 +266,11 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
         RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
 
         RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
-                createSalFacade(remoteDeviceId, domBroker, bindingAwareBroker, defaultRequestTimeoutMillis);
+                createSalFacade(remoteDeviceId, domBroker, bindingAwareBroker);
 
         if (keepaliveDelay > 0) {
             LOG.warn("Adding keepalive facade, for device {}", nodeId);
-            salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(), keepaliveDelay);
+            salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(), keepaliveDelay, defaultRequestTimeoutMillis);
         }
 
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
@@ -393,7 +393,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
                 .build();
     }
 
-    protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker, long defaultRequestTimeoutMillis);
+    protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker);
 
     @Override
     public abstract ConnectionStatusListenerRegistration registerConnectionStatusListener(NodeId node, RemoteDeviceHandler<NetconfSessionPreferences> listener);
index 04c95b49aaf49e0074858f10b5d3cd8adcb5ef4b..1612c7d9fccb23af2e7238145a91d232bc722772 100644 (file)
@@ -149,11 +149,11 @@ public class ClusteredNetconfTopology extends AbstractNetconfTopology implements
         RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
 
         RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
-                createSalFacade(remoteDeviceId, domBroker, bindingAwareBroker, defaultRequestTimeoutMillis);
+                createSalFacade(remoteDeviceId, domBroker, bindingAwareBroker);
 
         if (keepaliveDelay > 0) {
             LOG.warn("Adding keepalive facade, for device {}", nodeId);
-            salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(), keepaliveDelay);
+            salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(), keepaliveDelay, defaultRequestTimeoutMillis);
         }
 
         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
@@ -165,8 +165,8 @@ public class ClusteredNetconfTopology extends AbstractNetconfTopology implements
     }
 
     @Override
-    protected RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker, long defaultRequestTimeoutMillis) {
-        return new TopologyMountPointFacade(topologyId, id, domBroker, bindingBroker, defaultRequestTimeoutMillis);
+    protected RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker) {
+        return new TopologyMountPointFacade(topologyId, id, domBroker, bindingBroker);
     }
 
     @Override
index 33b468ce2bc700edb0e70f0df076328677c66d8e..c1e41189e3353a05dac9aa010cd1ec855ef2f6c8 100644 (file)
@@ -80,8 +80,8 @@ public class NetconfTopologyImpl extends AbstractNetconfTopology implements Data
     }
 
     @Override
-    protected RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id, Broker domBroker, BindingAwareBroker bindingBroker, long defaultRequestTimeoutMillis) {
-        return new NetconfDeviceSalFacade(id, domBroker, bindingAwareBroker, defaultRequestTimeoutMillis);
+    protected RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id, Broker domBroker, BindingAwareBroker bindingBroker) {
+        return new NetconfDeviceSalFacade(id, domBroker, bindingAwareBroker);
     }
 
     @Override
index f210820ccdf71a886f0fb0203dae12e22572e254..dc292d33ebb47fe2c1a4c4a3d0b61e9ff81b7a44 100644 (file)
@@ -57,9 +57,9 @@ public class NetconfDeviceMasterDataBroker implements ProxyNetconfDeviceDataBrok
 
     public NetconfDeviceMasterDataBroker(final ActorSystem actorSystem, final RemoteDeviceId id,
                                          final SchemaContext schemaContext, final DOMRpcService rpc,
-                                         final NetconfSessionPreferences netconfSessionPreferences, final long requestTimeoutMillis) {
+                                         final NetconfSessionPreferences netconfSessionPreferences) {
         this.id = id;
-        delegateBroker = new NetconfDeviceDataBroker(id, schemaContext, rpc, netconfSessionPreferences, requestTimeoutMillis);
+        delegateBroker = new NetconfDeviceDataBroker(id, schemaContext, rpc, netconfSessionPreferences);
         this.actorSystem = actorSystem;
 
         // only ever need 1 readTx since it doesnt need to be closed
index 271910d43089c9c9bcfa5c40d5bdac0371e97eb4..a44bf47a19a24cfff37d86e7937acd3ad7fa0bce 100644 (file)
@@ -44,7 +44,6 @@ public class TopologyMountPointFacade implements AutoCloseable, RemoteDeviceHand
     private final RemoteDeviceId id;
     private final Broker domBroker;
     private final BindingAwareBroker bindingBroker;
-    private final long defaultRequestTimeoutMillis;
 
     private SchemaContext remoteSchemaContext = null;
     private NetconfSessionPreferences netconfSessionPreferences = null;
@@ -59,13 +58,11 @@ public class TopologyMountPointFacade implements AutoCloseable, RemoteDeviceHand
     public TopologyMountPointFacade(final String topologyId,
                                     final RemoteDeviceId id,
                                     final Broker domBroker,
-                                    final BindingAwareBroker bindingBroker,
-                                    long defaultRequestTimeoutMillis) {
+                                    final BindingAwareBroker bindingBroker) {
         this.topologyId = topologyId;
         this.id = id;
         this.domBroker = domBroker;
         this.bindingBroker = bindingBroker;
-        this.defaultRequestTimeoutMillis = defaultRequestTimeoutMillis;
         this.salProvider = new ClusteredNetconfDeviceMountInstanceProxy(id);
         registerToSal(domBroker);
     }
@@ -125,7 +122,7 @@ public class TopologyMountPointFacade implements AutoCloseable, RemoteDeviceHand
         deviceDataBroker = TypedActor.get(context).typedActorOf(new TypedProps<>(ProxyNetconfDeviceDataBroker.class, new Creator<NetconfDeviceMasterDataBroker>() {
             @Override
             public NetconfDeviceMasterDataBroker create() throws Exception {
-                return new NetconfDeviceMasterDataBroker(actorSystem, id, remoteSchemaContext, deviceRpc, netconfSessionPreferences, defaultRequestTimeoutMillis);
+                return new NetconfDeviceMasterDataBroker(actorSystem, id, remoteSchemaContext, deviceRpc, netconfSessionPreferences);
             }
         }), MOUNT_POINT);
         LOG.debug("Master data broker registered on path {}", TypedActor.get(actorSystem).getActorRefFor(deviceDataBroker).path());
index a29d95eb1a4c7c14cc69f20a691484b29396d255..2e16cca1cb51e9b53e22b84191988e95d4782fed 100644 (file)
@@ -287,7 +287,7 @@ public class AbstractNetconfTopologyTest {
         }
 
         @Override
-        protected RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id, Broker domBroker, BindingAwareBroker bindingBroker, long defaultRequestTimeoutMillis) {
+        protected RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id, Broker domBroker, BindingAwareBroker bindingBroker) {
             return salFacade;
         }
 
index 2b4b37418a3da7b0bc698b6ad07c19378f3049d8..68342a56e5da0f028f33d73a86daef4f9a13a081 100644 (file)
@@ -48,7 +48,7 @@ public class TopologyMountPointFacadeTest {
 
         MockitoAnnotations.initMocks(this);
 
-        mountPointFacade = new TopologyMountPointFacade(TOPOLOGY_ID, REMOTE_DEVICE_ID, domBroker, bindingBroker, 1L);
+        mountPointFacade = new TopologyMountPointFacade(TOPOLOGY_ID, REMOTE_DEVICE_ID, domBroker, bindingBroker);
 
         mountPointFacade.registerConnectionStatusListener(connectionStatusListener1);
         mountPointFacade.registerConnectionStatusListener(connectionStatusListener2);
diff --git a/netconf/netconf-util/src/main/java/org/opendaylight/netconf/util/messages/NetconfMessageHeader.java b/netconf/netconf-util/src/main/java/org/opendaylight/netconf/util/messages/NetconfMessageHeader.java
deleted file mode 100644 (file)
index 8a425c2..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.netconf.util.messages;
-
-import com.google.common.base.Charsets;
-import com.google.common.base.Preconditions;
-import java.nio.ByteBuffer;
-
-/**
- * Netconf message header is used only when chunked framing mechanism is
- * supported. The header consists of only the length field.
- */
-@Deprecated
-public final class NetconfMessageHeader {
-    // \n#<length>\n
-    private static final byte[] HEADER_START = new byte[] { (byte) 0x0a, (byte) 0x23 };
-    private static final byte HEADER_END = (byte) 0x0a;
-    private final long length;
-
-    public NetconfMessageHeader(final long length) {
-        Preconditions.checkArgument(length < Integer.MAX_VALUE && length > 0);
-        this.length = length;
-    }
-
-    public byte[] toBytes() {
-        return toBytes(this.length);
-    }
-
-    // FIXME: improve precision to long
-    public int getLength() {
-        return (int) this.length;
-    }
-
-    public static NetconfMessageHeader fromBytes(final byte[] bytes) {
-        // the length is variable therefore bytes between headerBegin and
-        // headerEnd mark the length
-        // the length should be only numbers and therefore easily parsed with
-        // ASCII
-        long length = Long.parseLong(Charsets.US_ASCII.decode(
-                ByteBuffer.wrap(bytes, HEADER_START.length, bytes.length - HEADER_START.length - 1)).toString());
-
-        return new NetconfMessageHeader(length);
-    }
-
-    public static byte[] toBytes(final long length) {
-        final byte[] l = String.valueOf(length).getBytes(Charsets.US_ASCII);
-        final byte[] h = new byte[HEADER_START.length + l.length + 1];
-        System.arraycopy(HEADER_START, 0, h, 0, HEADER_START.length);
-        System.arraycopy(l, 0, h, HEADER_START.length, l.length);
-        System.arraycopy(new byte[] { HEADER_END }, 0, h, HEADER_START.length + l.length, 1);
-        return h;
-    }
-}
index 949e63d661a1deea9d1692c50ec997ccd683f1bb..73ce520d2b8774013082c389abb9ff49085473b2 100644 (file)
@@ -42,8 +42,6 @@ public class SubtreeFilter {
             if (!maybeFilter.isPresent()) {
                 return rpcReply;
             }
-
-            rpcReply = reReadDocument(rpcReply);
             XmlElement filter = maybeFilter.get();
             if (isSupported(filter)) {
 
@@ -64,7 +62,6 @@ public class SubtreeFilter {
      * @throws DocumentedException
      */
     public static Optional<Document> applySubtreeNotificationFilter(XmlElement filter, Document notification) throws DocumentedException {
-        notification = reReadDocument(notification);
         removeEventTimeNode(notification);
         if (isSupported(filter)) {
             return Optional.fromNullable(filteredNotification(filter, notification));
@@ -72,17 +69,6 @@ public class SubtreeFilter {
         return Optional.of(extractNotificationContent(notification));
     }
 
-    private static Document reReadDocument(Document notification) throws DocumentedException {
-        // FIXME: rpcReply document must be reread otherwise some nodes do not inherit namespaces. (services/service)
-        try {
-            notification = XmlUtil.readXmlToDocument(XmlUtil.toString(notification, true));
-        } catch (SAXException | IOException e) {
-            LOG.error("Cannot transform document", e);
-            throw new DocumentedException("Cannot transform document" + e);
-        }
-        return notification;
-    }
-
     private static void removeEventTimeNode(Document document) {
         final Node eventTimeNode = document.getDocumentElement().getElementsByTagNameNS(
                 XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_CAPABILITY_NOTIFICATION_1_0, XmlNetconfConstants.EVENT_TIME).item(0);
diff --git a/netconf/netconf-util/src/test/java/org/opendaylight/netconf/util/messages/NetconfMessageHeaderTest.java b/netconf/netconf-util/src/test/java/org/opendaylight/netconf/util/messages/NetconfMessageHeaderTest.java
deleted file mode 100644 (file)
index 1c09b95..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.netconf.util.messages;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-
-import com.google.common.base.Charsets;
-import org.junit.Test;
-
-@Deprecated
-public class NetconfMessageHeaderTest {
-    @Test
-    public void testGet() throws Exception {
-        NetconfMessageHeader header = new NetconfMessageHeader(10);
-        assertEquals(header.getLength(), 10);
-
-        byte[] expectedValue = "\n#10\n".getBytes(Charsets.US_ASCII);
-        assertArrayEquals(expectedValue, header.toBytes());
-    }
-}
index ee1640e33d0a3f904c3715b6ea06b39e6df6eeef..40fe3b2f70a17303d8138ac24e84a130ecde168f 100644 (file)
       <groupId>org.hamcrest</groupId>
       <artifactId>hamcrest-core</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.powermock</groupId>
-      <artifactId>powermock-api-mockito</artifactId>
-      <version>1.6.4</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.powermock</groupId>
-      <artifactId>powermock-module-junit4</artifactId>
-      <version>1.6.4</version>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
 
   <scm>
index 785be9954d3e038b3f2cb7655180584a2b88c564..4538857184f42f7befc4c9034dd42fd6e9f85e25 100644 (file)
@@ -47,10 +47,13 @@ import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
 import org.opendaylight.protocol.framework.TimedReconnectStrategy;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
@@ -105,6 +108,8 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
     private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
             DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
 
+    private static final int LOCAL_IO_FALLBACK_COST = PotentialSchemaSource.Costs.LOCAL_IO.getValue() + 1;
+
     /**
      * Keeps track of initialized Schema resources.  A Map is maintained in which the key represents the name
      * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>.  The
@@ -192,14 +197,14 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
         final ExecutorService globalProcessingExecutor = processingExecutor.getExecutor();
 
         RemoteDeviceHandler<NetconfSessionPreferences> salFacade
-                = new NetconfDeviceSalFacade(id, domRegistry, bindingRegistry, getDefaultRequestTimeoutMillis());
+                = new NetconfDeviceSalFacade(id, domRegistry, bindingRegistry);
 
         final Long keepaliveDelay = getKeepaliveDelay();
         if (shouldSendKeepalive()) {
             // Keepalive executor is optional for now and a default instance is supported
             final ScheduledExecutorService executor = keepaliveExecutor == null ? DEFAULT_KEEPALIVE_EXECUTOR : keepaliveExecutor.getExecutor();
 
-            salFacade = new KeepaliveSalFacade(id, salFacade, executor, keepaliveDelay);
+            salFacade = new KeepaliveSalFacade(id, salFacade, executor, keepaliveDelay, getDefaultRequestTimeoutMillis());
         }
 
         // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
@@ -226,6 +231,12 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
                         setSchemaRegistry(dto.getSchemaRegistry());
                         schemaResourcesDTO = dto;
                     }
+                    if (userCapabilities.isPresent()) {
+                        for (QName qname : userCapabilities.get().getModuleBasedCaps()) {
+                            final SourceIdentifier sourceIdentifier = new SourceIdentifier(qname.getLocalName(), qname.getFormattedRevision());
+                            dto.getSchemaRegistry().registerSchemaSource(DEFAULT_CACHE, PotentialSchemaSource.create(sourceIdentifier, YangTextSchemaSource.class, LOCAL_IO_FALLBACK_COST));
+                        }
+                    }
                 }
                 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
                         instanceName, moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
index ee119344ebe2f9220f91a0092ab0481fc465950b..32d22be32f96afc7d3bbd5c7de7aea67651933d8 100644 (file)
@@ -110,6 +110,8 @@ public final class NetconfStateSchemas {
      */
     private static NetconfStateSchemas create(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) {
         if(remoteSessionCapabilities.isMonitoringSupported() == false) {
+            // TODO - need to search for get-schema support, not just ietf-netconf-monitoring support
+            // issue might be a deviation to ietf-netconf-monitoring where get-schema is unsupported...
             LOG.warn("{}: Netconf monitoring not supported on device, cannot detect provided schemas", id);
             return EMPTY;
         }
index 3e1198b0413134884e43ad26c3125a634e753e22..084481eec736096ff700801aeb2efc1d75a9b75b 100644 (file)
@@ -20,6 +20,7 @@ import com.google.common.collect.Sets;
 import java.net.URI;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Set;
 import org.opendaylight.netconf.client.NetconfClientSession;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
@@ -82,6 +83,18 @@ public final class NetconfSessionPreferences {
         return nonModuleCaps;
     }
 
+    // allows partial matches - assuming parameters are in the same order
+    public boolean containsPartialNonModuleCapability(final String capability) {
+        final Iterator<String> iterator = nonModuleCaps.iterator();
+        while(iterator.hasNext()) {
+            if (iterator.next().startsWith(capability)) {
+                LOG.trace("capability {} partially matches {}", capability, nonModuleCaps);
+                return true;
+            }
+        }
+        return false;
+    }
+
     public boolean containsNonModuleCapability(final String capability) {
         return nonModuleCaps.contains(capability);
     }
@@ -115,13 +128,13 @@ public final class NetconfSessionPreferences {
     }
 
     public boolean isNotificationsSupported() {
-        return containsNonModuleCapability(NetconfMessageTransformUtil.NETCONF_NOTIFICATONS_URI.toString())
+        return containsPartialNonModuleCapability(NetconfMessageTransformUtil.NETCONF_NOTIFICATONS_URI.toString())
                 || containsModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_NOTIFICATIONS);
     }
 
     public boolean isMonitoringSupported() {
         return containsModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)
-                || containsNonModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
+                || containsPartialNonModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
     }
 
     /**
index dc2b5dff427f3b40c12ebd5624b1a33151cabff3..c450ac68fecd83c40d8eff6c306e30d02e6b35c1 100644 (file)
@@ -51,28 +51,33 @@ public final class KeepaliveSalFacade implements RemoteDeviceHandler<NetconfSess
     // 2 minutes keepalive delay by default
     private static final long DEFAULT_DELAY = TimeUnit.MINUTES.toSeconds(2);
 
+    // 1 minute transaction timeout by default
+    private static final long DEFAULT_TRANSACTION_TIMEOUT_MILLI = TimeUnit.MILLISECONDS.toMillis(60000);
+
     private final RemoteDeviceId id;
     private final RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
     private final ScheduledExecutorService executor;
     private final long keepaliveDelaySeconds;
     private final ResetKeepalive resetKeepaliveTask;
+    private final long defaultRequestTimeoutMillis;
 
     private volatile NetconfDeviceCommunicator listener;
     private volatile ScheduledFuture<?> currentKeepalive;
     private volatile DOMRpcService currentDeviceRpc;
 
     public KeepaliveSalFacade(final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
-                              final ScheduledExecutorService executor, final long keepaliveDelaySeconds) {
+                              final ScheduledExecutorService executor, final long keepaliveDelaySeconds, final long defaultRequestTimeoutMillis) {
         this.id = id;
         this.salFacade = salFacade;
         this.executor = executor;
         this.keepaliveDelaySeconds = keepaliveDelaySeconds;
+        this.defaultRequestTimeoutMillis = defaultRequestTimeoutMillis;
         this.resetKeepaliveTask = new ResetKeepalive();
     }
 
     public KeepaliveSalFacade(final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
                               final ScheduledExecutorService executor) {
-        this(id, salFacade, executor, DEFAULT_DELAY);
+        this(id, salFacade, executor, DEFAULT_DELAY, DEFAULT_TRANSACTION_TIMEOUT_MILLI);
     }
 
     /**
@@ -118,7 +123,7 @@ public final class KeepaliveSalFacade implements RemoteDeviceHandler<NetconfSess
     @Override
     public void onDeviceConnected(final SchemaContext remoteSchemaContext, final NetconfSessionPreferences netconfSessionPreferences, final DOMRpcService deviceRpc) {
         this.currentDeviceRpc = deviceRpc;
-        final DOMRpcService deviceRpc1 = new KeepaliveDOMRpcService(deviceRpc, resetKeepaliveTask);
+        final DOMRpcService deviceRpc1 = new KeepaliveDOMRpcService(deviceRpc, resetKeepaliveTask, defaultRequestTimeoutMillis, executor);
         salFacade.onDeviceConnected(remoteSchemaContext, netconfSessionPreferences, deviceRpc1);
 
         LOG.debug("{}: Netconf session initiated, starting keepalives", id);
@@ -214,7 +219,7 @@ public final class KeepaliveSalFacade implements RemoteDeviceHandler<NetconfSess
     /**
      * Reset keepalive after each RPC response received
      */
-    private class ResetKeepalive implements com.google.common.util.concurrent.FutureCallback<DOMRpcResult> {
+    private class ResetKeepalive implements FutureCallback<DOMRpcResult> {
         @Override
         public void onSuccess(@Nullable final DOMRpcResult result) {
             // No matter what response we got, rpc-reply or rpc-error, we got it from device so the netconf session is OK
@@ -223,24 +228,51 @@ public final class KeepaliveSalFacade implements RemoteDeviceHandler<NetconfSess
 
         @Override
         public void onFailure(@Nonnull final Throwable t) {
-            // User/Application RPC failed (The RPC did not reach the remote device.
+            // User/Application RPC failed (The RPC did not reach the remote device or .. TODO what other reasons could cause this ?)
             // There is no point in keeping this session. Reconnect.
             LOG.warn("{}: Rpc failure detected. Reconnecting netconf session", id, t);
             reconnect();
         }
     }
 
+    /*
+     * Request timeout task is called once the defaultRequestTimeoutMillis is
+     * reached. At this moment, if the request is not yet finished, we cancel
+     * it.
+     */
+    private static final class RequestTimeoutTask implements Runnable {
+
+        private final CheckedFuture<DOMRpcResult, DOMRpcException> rpcResultFuture;
+
+        public RequestTimeoutTask(final CheckedFuture<DOMRpcResult, DOMRpcException> rpcResultFuture) {
+            this.rpcResultFuture = rpcResultFuture;
+        }
+
+        @Override
+        public void run() {
+            if (!rpcResultFuture.isDone()) {
+                rpcResultFuture.cancel(true);
+            }
+        }
+    }
+
     /**
-     * DOMRpcService proxy that attaches reset-keepalive-task to each RPC invocation.
+     * DOMRpcService proxy that attaches reset-keepalive-task and schedule
+     * request-timeout-task to each RPC invocation.
      */
     private static final class KeepaliveDOMRpcService implements DOMRpcService {
 
         private final DOMRpcService deviceRpc;
         private ResetKeepalive resetKeepaliveTask;
+        private final long defaultRequestTimeoutMillis;
+        private final ScheduledExecutorService executor;
 
-        public KeepaliveDOMRpcService(final DOMRpcService deviceRpc, final ResetKeepalive resetKeepaliveTask) {
+        public KeepaliveDOMRpcService(final DOMRpcService deviceRpc, final ResetKeepalive resetKeepaliveTask,
+                final long defaultRequestTimeoutMillis, final ScheduledExecutorService executor) {
             this.deviceRpc = deviceRpc;
             this.resetKeepaliveTask = resetKeepaliveTask;
+            this.defaultRequestTimeoutMillis = defaultRequestTimeoutMillis;
+            this.executor = executor;
         }
 
         @Nonnull
@@ -248,6 +280,10 @@ public final class KeepaliveSalFacade implements RemoteDeviceHandler<NetconfSess
         public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(@Nonnull final SchemaPath type, final NormalizedNode<?, ?> input) {
             final CheckedFuture<DOMRpcResult, DOMRpcException> domRpcResultDOMRpcExceptionCheckedFuture = deviceRpc.invokeRpc(type, input);
             Futures.addCallback(domRpcResultDOMRpcExceptionCheckedFuture, resetKeepaliveTask);
+
+            final RequestTimeoutTask timeoutTask = new RequestTimeoutTask(domRpcResultDOMRpcExceptionCheckedFuture);
+            executor.schedule(timeoutTask, defaultRequestTimeoutMillis, TimeUnit.MILLISECONDS);
+
             return domRpcResultDOMRpcExceptionCheckedFuture;
         }
 
index 7283cae9b85b9446a8e6b284e465f18ba7fb54c2..421e52da71978af327083e7f39b83aa3262015a7 100644 (file)
@@ -36,16 +36,14 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 public final class NetconfDeviceDataBroker implements DOMDataBroker {
     private final RemoteDeviceId id;
     private final NetconfBaseOps netconfOps;
-    private final long requestTimeoutMillis;
 
     private final boolean rollbackSupport;
     private boolean candidateSupported;
     private boolean runningWritable;
 
-    public NetconfDeviceDataBroker(final RemoteDeviceId id, final SchemaContext schemaContext, final DOMRpcService rpc, final NetconfSessionPreferences netconfSessionPreferences, long requestTimeoutMillis) {
+    public NetconfDeviceDataBroker(final RemoteDeviceId id, final SchemaContext schemaContext, final DOMRpcService rpc, final NetconfSessionPreferences netconfSessionPreferences) {
         this.id = id;
         this.netconfOps = new NetconfBaseOps(rpc, schemaContext);
-        this.requestTimeoutMillis = requestTimeoutMillis;
         // get specific attributes from netconf preferences and get rid of it
         // no need to keep the entire preferences object, its quite big with all the capability QNames
         candidateSupported = netconfSessionPreferences.isCandidateSupported();
@@ -57,7 +55,7 @@ public final class NetconfDeviceDataBroker implements DOMDataBroker {
 
     @Override
     public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
-        return new ReadOnlyTx(netconfOps, id, requestTimeoutMillis);
+        return new ReadOnlyTx(netconfOps, id);
     }
 
     @Override
@@ -69,12 +67,12 @@ public final class NetconfDeviceDataBroker implements DOMDataBroker {
     public DOMDataWriteTransaction newWriteOnlyTransaction() {
         if(candidateSupported) {
             if(runningWritable) {
-                return new WriteCandidateRunningTx(id, netconfOps, rollbackSupport, requestTimeoutMillis);
+                return new WriteCandidateRunningTx(id, netconfOps, rollbackSupport);
             } else {
-                return new WriteCandidateTx(id, netconfOps, rollbackSupport, requestTimeoutMillis);
+                return new WriteCandidateTx(id, netconfOps, rollbackSupport);
             }
         } else {
-            return new WriteRunningTx(id, netconfOps, rollbackSupport, requestTimeoutMillis);
+            return new WriteRunningTx(id, netconfOps, rollbackSupport);
         }
     }
 
diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceDatastoreAdapter.java
deleted file mode 100644 (file)
index 54dda9d..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.netconf.sal.connect.netconf.sal;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.FluentIterable;
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-
-import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
-import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
-import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.inventory.rev140108.NetconfNode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.inventory.rev140108.NetconfNodeBuilder;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Asynchronous (Binding-aware) adapter over datastore subtree for netconf device.
- *
- * All data changes are submitted to an ExecutorService to avoid Thread blocking while sal is waiting for schema.
- *
- * @deprecated Data is pushed into Topology instead if Inventory model
- */
-@Deprecated
-final class NetconfDeviceDatastoreAdapter implements AutoCloseable {
-
-    private static final Logger logger  = LoggerFactory.getLogger(NetconfDeviceDatastoreAdapter.class);
-
-    private final RemoteDeviceId id;
-    private BindingTransactionChain txChain;
-
-    NetconfDeviceDatastoreAdapter(final RemoteDeviceId deviceId, final BindingTransactionChain txChain) {
-        this.id = Preconditions.checkNotNull(deviceId);
-        this.txChain = Preconditions.checkNotNull(txChain);
-
-        initDeviceData();
-    }
-
-    public void updateDeviceState(final boolean up, final Set<QName> capabilities) {
-        final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node data = buildDataForDeviceState(
-                up, capabilities, id);
-
-        final ReadWriteTransaction transaction = txChain.newReadWriteTransaction();
-        logger.trace("{}: Update device state transaction {} merging operational data started.", id, transaction.getIdentifier());
-        transaction.put(LogicalDatastoreType.OPERATIONAL, id.getBindingPath(), data);
-        logger.trace("{}: Update device state transaction {} merging operational data ended.", id, transaction.getIdentifier());
-
-        commitTransaction(transaction, "update");
-    }
-
-    private void removeDeviceConfigAndState() {
-        final WriteTransaction transaction = txChain.newWriteOnlyTransaction();
-        logger.trace("{}: Close device state transaction {} removing all data started.", id, transaction.getIdentifier());
-        transaction.delete(LogicalDatastoreType.CONFIGURATION, id.getBindingPath());
-        transaction.delete(LogicalDatastoreType.OPERATIONAL, id.getBindingPath());
-        logger.trace("{}: Close device state transaction {} removing all data ended.", id, transaction.getIdentifier());
-
-        try {
-            transaction.submit().get();
-        } catch (InterruptedException | ExecutionException e) {
-            logger.error("{}: Transaction(close) {} FAILED!", id, transaction.getIdentifier(), e);
-            throw new IllegalStateException(id + "  Transaction(close) not committed correctly", e);
-        }
-    }
-
-    private void initDeviceData() {
-        final WriteTransaction transaction = txChain.newWriteOnlyTransaction();
-
-        createNodesListIfNotPresent(transaction);
-
-        final InstanceIdentifier<Node> path = id.getBindingPath();
-        final Node nodeWithId = getNodeWithId(id);
-
-        logger.trace("{}: Init device state transaction {} putting if absent operational data started.", id, transaction.getIdentifier());
-        transaction.put(LogicalDatastoreType.OPERATIONAL, path, nodeWithId);
-        logger.trace("{}: Init device state transaction {} putting operational data ended.", id, transaction.getIdentifier());
-
-        logger.trace("{}: Init device state transaction {} putting if absent config data started.", id, transaction.getIdentifier());
-        transaction.put(LogicalDatastoreType.CONFIGURATION, path, nodeWithId);
-        logger.trace("{}: Init device state transaction {} putting config data ended.", id, transaction.getIdentifier());
-
-        commitTransaction(transaction, "init");
-    }
-
-    private void createNodesListIfNotPresent(final WriteTransaction writeTx) {
-        final Nodes nodes = new NodesBuilder().build();
-        final InstanceIdentifier<Nodes> path = InstanceIdentifier.builder(Nodes.class).build();
-        logger.trace("{}: Merging {} container to ensure its presence", id, Nodes.QNAME, writeTx.getIdentifier());
-        writeTx.merge(LogicalDatastoreType.CONFIGURATION, path, nodes);
-        writeTx.merge(LogicalDatastoreType.OPERATIONAL, path, nodes);
-    }
-
-    private void commitTransaction(final WriteTransaction transaction, final String txType) {
-        logger.trace("{}: Committing Transaction {}:{}", id, txType, transaction.getIdentifier());
-        final CheckedFuture<Void, TransactionCommitFailedException> result = transaction.submit();
-
-        Futures.addCallback(result, new FutureCallback<Void>() {
-            @Override
-            public void onSuccess(final Void result) {
-                logger.trace("{}: Transaction({}) {} SUCCESSFUL", id, txType, transaction.getIdentifier());
-            }
-
-            @Override
-            public void onFailure(final Throwable t) {
-                logger.error("{}: Transaction({}) {} FAILED!", id, txType, transaction.getIdentifier(), t);
-                throw new IllegalStateException(id + "  Transaction(" + txType + ") not committed correctly", t);
-            }
-        });
-
-    }
-
-    @Override
-    public void close() throws Exception {
-        removeDeviceConfigAndState();
-    }
-
-    public static org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node buildDataForDeviceState(
-            final boolean up, final Set<QName> capabilities, final RemoteDeviceId id) {
-
-        final NodeBuilder nodeBuilder = getNodeWithIdBuilder(id);
-        final NetconfNodeBuilder netconfNodeBuilder = new NetconfNodeBuilder();
-        netconfNodeBuilder.setConnected(up);
-        netconfNodeBuilder.setInitialCapability(FluentIterable.from(capabilities)
-                .transform(new Function<QName, String>() {
-                    @Override
-                    public String apply(final QName input) {
-                        return input.toString();
-                    }
-                }).toList());
-        nodeBuilder.addAugmentation(NetconfNode.class, netconfNodeBuilder.build());
-
-        return nodeBuilder.build();
-    }
-
-    private static ListenableFuture<Optional<Node>> readNodeData(
-            final LogicalDatastoreType store,
-            final ReadWriteTransaction transaction,
-            final InstanceIdentifier<Node> path) {
-        return transaction.read(store, path);
-    }
-
-    private static Node getNodeWithId(final RemoteDeviceId id) {
-        final NodeBuilder nodeBuilder = getNodeWithIdBuilder(id);
-        return nodeBuilder.build();
-    }
-
-    private static NodeBuilder getNodeWithIdBuilder(final RemoteDeviceId id) {
-        final NodeBuilder nodeBuilder = new NodeBuilder();
-        nodeBuilder.setKey(id.getBindingKey());
-        nodeBuilder.setId(id.getBindingKey().getId());
-        return nodeBuilder;
-    }
-
-    public void setTxChain(BindingTransactionChain txChain) {
-        this.txChain = Preconditions.checkNotNull(txChain);
-    }
-}
index d1282d09b45c58c82785e391a11059705c2c25da..ff27e3c9129404d36f9329bf7afd975ebe388e99 100644 (file)
@@ -67,7 +67,6 @@ public final class NetconfDeviceRpc implements DOMRpcService {
                 if (input.isSuccessful()) {
                     return transformer.toRpcResult(input.getResult(), type);
                 } else {
-                    // TODO check whether the listener sets errors properly
                     return new DefaultDOMRpcResult(input.getErrors());
                 }
             }
@@ -77,7 +76,6 @@ public final class NetconfDeviceRpc implements DOMRpcService {
             @Nullable
             @Override
             public DOMRpcException apply(@Nullable final Exception e) {
-                // FIXME what other possible exceptions are there ?
                 return new DOMRpcImplementationNotAvailableException(e, "Unable to invoke rpc %s", type);
             }
         });
index 7bb3df08a5ef6644028be5fcd162145b361dd1db..829487a409a232c7ab040fbe6eee013047b7c613 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.netconf.sal.connect.netconf.sal;
 
 import com.google.common.collect.Lists;
-import java.util.Collections;
 import java.util.List;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
@@ -19,7 +18,6 @@ import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -30,14 +28,12 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice
 
     private final RemoteDeviceId id;
     private final NetconfDeviceSalProvider salProvider;
-    private final long defaultRequestTimeoutMillis;
 
     private final List<AutoCloseable> salRegistrations = Lists.newArrayList();
 
-    public NetconfDeviceSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker, long defaultRequestTimeoutMillis) {
+    public NetconfDeviceSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker) {
         this.id = id;
         this.salProvider = new NetconfDeviceSalProvider(id);
-        this.defaultRequestTimeoutMillis = defaultRequestTimeoutMillis;
         registerToSal(domBroker, bindingBroker);
     }
 
@@ -55,28 +51,23 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice
     public synchronized void onDeviceConnected(final SchemaContext schemaContext,
                                                final NetconfSessionPreferences netconfSessionPreferences, final DOMRpcService deviceRpc) {
 
-        final DOMDataBroker domBroker = new NetconfDeviceDataBroker(id, schemaContext, deviceRpc, netconfSessionPreferences, defaultRequestTimeoutMillis);
+        final DOMDataBroker domBroker = new NetconfDeviceDataBroker(id, schemaContext, deviceRpc, netconfSessionPreferences);
 
         final NetconfDeviceNotificationService notificationService = new NetconfDeviceNotificationService();
 
-        salProvider.getMountInstance().onDeviceConnected(schemaContext, domBroker, deviceRpc, notificationService);
-        salProvider.getDatastoreAdapter().updateDeviceState(true, netconfSessionPreferences.getModuleBasedCaps());
         salProvider.getMountInstance().onTopologyDeviceConnected(schemaContext, domBroker, deviceRpc, notificationService);
         salProvider.getTopologyDatastoreAdapter().updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities());
     }
 
     @Override
     public synchronized void onDeviceDisconnected() {
-        salProvider.getDatastoreAdapter().updateDeviceState(false, Collections.<QName>emptySet());
         salProvider.getTopologyDatastoreAdapter().updateDeviceData(false, new NetconfDeviceCapabilities());
-        salProvider.getMountInstance().onDeviceDisconnected();
         salProvider.getMountInstance().onTopologyDeviceDisconnected();
     }
 
     @Override
     public synchronized void onDeviceFailed(final Throwable throwable) {
         salProvider.getTopologyDatastoreAdapter().setDeviceAsFailed(throwable);
-        salProvider.getMountInstance().onDeviceDisconnected();
         salProvider.getMountInstance().onTopologyDeviceDisconnected();
     }
 
index a01b74e94149bdd78c673bcae856900374154b28..278771a5ba880a97ec4e3c0eaaa2da7bdb841be4 100644 (file)
@@ -38,7 +38,6 @@ public class NetconfDeviceSalProvider implements AutoCloseable, Provider, Bindin
     private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceSalProvider.class);
 
     private final RemoteDeviceId id;
-    private volatile NetconfDeviceDatastoreAdapter datastoreAdapter;
     private MountInstance mountInstance;
 
     private volatile NetconfDeviceTopologyAdapter topologyDatastoreAdapter;
@@ -71,12 +70,6 @@ public class NetconfDeviceSalProvider implements AutoCloseable, Provider, Bindin
         return mountInstance;
     }
 
-    public NetconfDeviceDatastoreAdapter getDatastoreAdapter() {
-        Preconditions.checkState(datastoreAdapter != null,
-                "%s: Sal provider %s was not initialized by sal. Cannot get datastore adapter", id);
-        return datastoreAdapter;
-    }
-
     public NetconfDeviceTopologyAdapter getTopologyDatastoreAdapter() {
         Preconditions.checkState(topologyDatastoreAdapter != null,
                 "%s: Sal provider %s was not initialized by sal. Cannot get topology datastore adapter", id);
@@ -105,14 +98,12 @@ public class NetconfDeviceSalProvider implements AutoCloseable, Provider, Bindin
         this.dataBroker = session.getSALService(DataBroker.class);
         txChain = Preconditions.checkNotNull(dataBroker).createTransactionChain(transactionChainListener);
 
-        datastoreAdapter = new NetconfDeviceDatastoreAdapter(id, txChain);
         topologyDatastoreAdapter = new NetconfDeviceTopologyAdapter(id, txChain);
     }
 
     private void resetTransactionChainForAdapaters() {
         txChain = Preconditions.checkNotNull(dataBroker).createTransactionChain(transactionChainListener);
 
-        datastoreAdapter.setTxChain(txChain);
         topologyDatastoreAdapter.setTxChain(txChain);
 
         logger.trace("{}: Resetting TransactionChain {}", id, txChain);
@@ -121,8 +112,6 @@ public class NetconfDeviceSalProvider implements AutoCloseable, Provider, Bindin
 
     public void close() throws Exception {
         mountInstance.close();
-        datastoreAdapter.close();
-        datastoreAdapter = null;
         topologyDatastoreAdapter.close();
         topologyDatastoreAdapter = null;
         txChain.close();
@@ -132,7 +121,6 @@ public class NetconfDeviceSalProvider implements AutoCloseable, Provider, Bindin
 
         private DOMMountPointService mountService;
         private final RemoteDeviceId id;
-        private ObjectRegistration<DOMMountPoint> registration;
         private NetconfDeviceNotificationService notificationService;
 
         private ObjectRegistration<DOMMountPoint> topologyRegistration;
@@ -142,44 +130,6 @@ public class NetconfDeviceSalProvider implements AutoCloseable, Provider, Bindin
             this.id = Preconditions.checkNotNull(id);
         }
 
-        @Deprecated
-        synchronized void onDeviceConnected(final SchemaContext initialCtx,
-                                            final DOMDataBroker broker, final DOMRpcService rpc,
-                                            final NetconfDeviceNotificationService notificationService) {
-
-            Preconditions.checkNotNull(mountService, "Closed");
-            Preconditions.checkState(registration == null, "Already initialized");
-
-            final DOMMountPointService.DOMMountPointBuilder mountBuilder = mountService.createMountPoint(id.getPath());
-            mountBuilder.addInitialSchemaContext(initialCtx);
-
-            mountBuilder.addService(DOMDataBroker.class, broker);
-            mountBuilder.addService(DOMRpcService.class, rpc);
-            mountBuilder.addService(DOMNotificationService.class, notificationService);
-            this.notificationService = notificationService;
-
-            registration = mountBuilder.register();
-            logger.debug("{}: Mountpoint exposed into MD-SAL {}", id, registration);
-        }
-
-        @Deprecated
-        synchronized void onDeviceDisconnected() {
-            if(registration == null) {
-                logger.trace("{}: Not removing mountpoint from MD-SAL, mountpoint was not registered yet", id);
-                return;
-            }
-
-            try {
-                registration.close();
-            } catch (final Exception e) {
-                // Only log and ignore
-                logger.warn("Unable to unregister mount instance for {}. Ignoring exception", id.getPath(), e);
-            } finally {
-                logger.debug("{}: Mountpoint removed from MD-SAL {}", id, registration);
-                registration = null;
-            }
-        }
-
         public synchronized void onTopologyDeviceConnected(final SchemaContext initialCtx,
                                                     final DOMDataBroker broker, final DOMRpcService rpc,
                                                     final NetconfDeviceNotificationService notificationService) {
@@ -193,6 +143,7 @@ public class NetconfDeviceSalProvider implements AutoCloseable, Provider, Bindin
             mountBuilder.addService(DOMDataBroker.class, broker);
             mountBuilder.addService(DOMRpcService.class, rpc);
             mountBuilder.addService(DOMNotificationService.class, notificationService);
+            this.notificationService = notificationService;
 
             topologyRegistration = mountBuilder.register();
             logger.debug("{}: TOPOLOGY Mountpoint exposed into MD-SAL {}", id, topologyRegistration);
@@ -218,7 +169,6 @@ public class NetconfDeviceSalProvider implements AutoCloseable, Provider, Bindin
 
         @Override
         public synchronized void close() throws Exception {
-            onDeviceDisconnected();
             onTopologyDeviceDisconnected();
             mountService = null;
         }
index a12ba3a02abbc5b0a920fe03af58428c8d47b3a2..3aeff93da40082c2a4405b5213a759a434890360 100644 (file)
@@ -11,11 +11,8 @@ package org.opendaylight.netconf.sal.connect.netconf.sal.tx;
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
@@ -29,7 +26,6 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.MixinNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -37,15 +33,13 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction {
 
     private static final Logger LOG  = LoggerFactory.getLogger(AbstractWriteTx.class);
 
-    protected final long defaultRequestTimeoutMillis;
     protected final RemoteDeviceId id;
     protected final NetconfBaseOps netOps;
     protected final boolean rollbackSupport;
     // Allow commit to be called only once
     protected boolean finished = false;
 
-    public AbstractWriteTx(final long requestTimeoutMillis, final NetconfBaseOps netOps, final RemoteDeviceId id, final boolean rollbackSupport) {
-        this.defaultRequestTimeoutMillis = requestTimeoutMillis;
+    public AbstractWriteTx(final NetconfBaseOps netOps, final RemoteDeviceId id, final boolean rollbackSupport) {
         this.netOps = netOps;
         this.id = id;
         this.rollbackSupport = rollbackSupport;
@@ -176,18 +170,4 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction {
     }
 
     protected abstract void editConfig(DataContainerChild<?, ?> editStructure, Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException;
-
-
-    protected ListenableFuture<DOMRpcResult> perfomRequestWithTimeout(String operation, ListenableFuture<DOMRpcResult> future) {
-        try {
-            future.get(defaultRequestTimeoutMillis, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.error("{}: {} failed with error", operation, id, e);
-            return Futures.immediateFailedCheckedFuture(new RuntimeException(id + ": " + operation + " failed"));
-        } catch (TimeoutException e) {
-            LOG.warn("{}: Unable to {} after {} milliseconds", id, operation, defaultRequestTimeoutMillis, e);
-            return Futures.immediateFailedCheckedFuture(new SchemaSourceException(e.getMessage()));
-        }
-        return future;
-    }
 }
index 28d75cdaa4dd3210d9686f391e211abb4a22ad5c..dda54851094e612bf97a86c3020349427a384e8f 100644 (file)
@@ -16,9 +16,6 @@ import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
@@ -44,12 +41,9 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction {
     private final RemoteDeviceId id;
     private final FutureCallback<DOMRpcResult> loggingCallback;
 
-    private final long requestTimeoutMillis;
-
-    public ReadOnlyTx(final NetconfBaseOps netconfOps, final RemoteDeviceId id, final long requestTimeoutMillis) {
+    public ReadOnlyTx(final NetconfBaseOps netconfOps, final RemoteDeviceId id) {
         this.netconfOps = netconfOps;
         this.id = id;
-        this.requestTimeoutMillis = requestTimeoutMillis;
 
         // Simple logging callback to log result of read operation
         loggingCallback = new FutureCallback<DOMRpcResult>() {
@@ -84,10 +78,6 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction {
             }
         });
 
-        if(!readWithTimeout("readConfigurationData", configRunning)) {
-            return null;
-        }
-
         return MappingCheckedFuture.create(transformedFuture, ReadFailedException.MAPPER);
     }
 
@@ -119,10 +109,6 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction {
             }
         });
 
-        if(!readWithTimeout("readOperationalData", configCandidate)) {
-            return null;
-        }
-
         return MappingCheckedFuture.create(transformedFuture, ReadFailedException.MAPPER);
     }
 
@@ -161,18 +147,4 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction {
     public Object getIdentifier() {
         return this;
     }
-
-    private boolean readWithTimeout(String operation, ListenableFuture<DOMRpcResult> future) {
-        try {
-            future.get(requestTimeoutMillis, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.error("{}: {} failed with error", id, operation, e);
-            throw new RuntimeException(id + ": readOperationalData failed");
-        } catch (TimeoutException e) {
-            LOG.warn("{}: Unable to {} after {} milliseconds", id, operation, requestTimeoutMillis, e);
-            future.cancel(true);
-            return false;
-        }
-        return true;
-    }
 }
index 0b9bb98e9c75b7ad539313c677c418fe666f790b..c076d0b7a8fc6833d820f3abdb9ee69028f76c48 100644 (file)
@@ -29,8 +29,8 @@ public class WriteCandidateRunningTx extends WriteCandidateTx {
 
     private static final Logger LOG  = LoggerFactory.getLogger(WriteCandidateRunningTx.class);
 
-    public WriteCandidateRunningTx(final RemoteDeviceId id, final NetconfBaseOps netOps, final boolean rollbackSupport, final long requestTimeoutMillis) {
-        super(id, netOps, rollbackSupport, requestTimeoutMillis);
+    public WriteCandidateRunningTx(final RemoteDeviceId id, final NetconfBaseOps netOps, final boolean rollbackSupport) {
+        super(id, netOps, rollbackSupport);
     }
 
     @Override
@@ -46,13 +46,11 @@ public class WriteCandidateRunningTx extends WriteCandidateTx {
     }
 
     private void lockRunning() {
-        final String operation = "Lock Running";
         try {
-            invokeBlocking(operation, new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
+            invokeBlocking("Lock running", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
                 @Override
                 public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
-                    return perfomRequestWithTimeout(operation, input.lockRunning(new NetconfRpcFutureCallback(operation, id)));
-
+                    return input.lockRunning(new NetconfRpcFutureCallback("Lock running", id));
                 }
             });
         } catch (final NetconfDocumentedException e) {
index f607089dc01b9a1881ed9d0d7fda7e9f621f5195..b143a3bf8e969fb227e70f660862131c6eb509bf 100644 (file)
@@ -71,8 +71,8 @@ public class WriteCandidateTx extends AbstractWriteTx {
         }
     };
 
-    public WriteCandidateTx(final RemoteDeviceId id, final NetconfBaseOps rpc, final boolean rollbackSupport, long requestTimeoutMillis) {
-        super(requestTimeoutMillis, rpc, id, rollbackSupport);
+    public WriteCandidateTx(final RemoteDeviceId id, final NetconfBaseOps rpc, final boolean rollbackSupport) {
+        super(rpc, id, rollbackSupport);
     }
 
     @Override
@@ -95,12 +95,11 @@ public class WriteCandidateTx extends AbstractWriteTx {
     }
 
     private void lock() throws NetconfDocumentedException {
-        final String operation = "Lock candidate";
         try {
-            invokeBlocking(operation, new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
+            invokeBlocking("Lock candidate", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
                 @Override
                 public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
-                    return perfomRequestWithTimeout(operation, input.lockCandidate(new NetconfRpcFutureCallback(operation, id)));
+                    return input.lockCandidate(new NetconfRpcFutureCallback("Lock candidate", id));
                 }
             });
         } catch (final NetconfDocumentedException e) {
@@ -186,16 +185,14 @@ public class WriteCandidateTx extends AbstractWriteTx {
 
     @Override
     protected void editConfig(final DataContainerChild<?, ?> editStructure, final Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException {
-        final String operation = "Edit candidate";
-        invokeBlocking(operation, new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
+        invokeBlocking("Edit candidate", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
             @Override
             public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
-
-                        return perfomRequestWithTimeout(operation, defaultOperation.isPresent()
-                                ? input.editConfigCandidate(new NetconfRpcFutureCallback(operation, id), editStructure, defaultOperation.get(),
-                                rollbackSupport)
-                                : input.editConfigCandidate(new NetconfRpcFutureCallback(operation, id), editStructure,
-                                rollbackSupport));
+                    return defaultOperation.isPresent()
+                            ? input.editConfigCandidate(new NetconfRpcFutureCallback("Edit candidate", id), editStructure, defaultOperation.get(),
+                            rollbackSupport)
+                            : input.editConfigCandidate(new NetconfRpcFutureCallback("Edit candidate", id), editStructure,
+                            rollbackSupport);
             }
         });
     }
index 1d0ff653ef0d85abf515da0c409ab0a861c35636..cf7594a7bcf0b9dbdadb9dc7a6aa6ef500d88a19 100644 (file)
@@ -51,8 +51,8 @@ public class WriteRunningTx extends AbstractWriteTx {
     private static final Logger LOG  = LoggerFactory.getLogger(WriteRunningTx.class);
 
     public WriteRunningTx(final RemoteDeviceId id, final NetconfBaseOps netOps,
-                          final boolean rollbackSupport, long requestTimeoutMillis) {
-        super(requestTimeoutMillis, netOps, id, rollbackSupport);
+                          final boolean rollbackSupport) {
+        super(netOps, id, rollbackSupport);
     }
 
     @Override
@@ -61,12 +61,11 @@ public class WriteRunningTx extends AbstractWriteTx {
     }
 
     private void lock() {
-        final String operation = "Lock running";
         try {
-            invokeBlocking(operation, new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
+            invokeBlocking("Lock running", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
                 @Override
                 public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
-                    return perfomRequestWithTimeout(operation, input.lockRunning(new NetconfRpcFutureCallback(operation, id)));
+                    return input.lockRunning(new NetconfRpcFutureCallback("Lock running", id));
                 }
             });
         } catch (final NetconfDocumentedException e) {
@@ -97,14 +96,14 @@ public class WriteRunningTx extends AbstractWriteTx {
 
     @Override
     public synchronized CheckedFuture<Void, TransactionCommitFailedException> submit() {
-        final ListenableFuture<Void> commitFutureAsVoid = Futures.transform(commit(), new Function<RpcResult<TransactionStatus>, Void>() {
+        final ListenableFuture<Void> commmitFutureAsVoid = Futures.transform(commit(), new Function<RpcResult<TransactionStatus>, Void>() {
             @Override
             public Void apply(final RpcResult<TransactionStatus> input) {
                 return null;
             }
         });
 
-        return Futures.makeChecked(commitFutureAsVoid, new Function<Exception, TransactionCommitFailedException>() {
+        return Futures.makeChecked(commmitFutureAsVoid, new Function<Exception, TransactionCommitFailedException>() {
             @Override
             public TransactionCommitFailedException apply(final Exception input) {
                 return new TransactionCommitFailedException("Submit of transaction " + getIdentifier() + " failed", input);
@@ -120,26 +119,24 @@ public class WriteRunningTx extends AbstractWriteTx {
 
     @Override
     protected void editConfig(final DataContainerChild<?, ?> editStructure, final Optional<ModifyAction> defaultOperation) throws NetconfDocumentedException {
-        final String operation = "Edit running";
-        invokeBlocking(operation, new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
+        invokeBlocking("Edit running", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
             @Override
             public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
-                        return perfomRequestWithTimeout(operation, defaultOperation.isPresent()
-                                ? input.editConfigRunning(new NetconfRpcFutureCallback(operation, id), editStructure, defaultOperation.get(),
-                                rollbackSupport)
-                                : input.editConfigRunning(new NetconfRpcFutureCallback(operation, id), editStructure,
-                                rollbackSupport));
+                    return defaultOperation.isPresent()
+                            ? input.editConfigRunning(new NetconfRpcFutureCallback("Edit running", id), editStructure, defaultOperation.get(),
+                            rollbackSupport)
+                            : input.editConfigRunning(new NetconfRpcFutureCallback("Edit running", id), editStructure,
+                            rollbackSupport);
             }
         });
     }
 
     private void unlock() {
-        final String operation = "Unlocking running";
         try {
-            invokeBlocking(operation, new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
+            invokeBlocking("Unlocking running", new Function<NetconfBaseOps, ListenableFuture<DOMRpcResult>>() {
                 @Override
                 public ListenableFuture<DOMRpcResult> apply(final NetconfBaseOps input) {
-                    return perfomRequestWithTimeout(operation, input.unlockRunning(new NetconfRpcFutureCallback(operation, id)));
+                    return input.unlockRunning(new NetconfRpcFutureCallback("Unlock running", id));
                 }
             });
         } catch (final NetconfDocumentedException e) {
index e4fba0cb99facc07e57a76edc02fa55f77307b61..72d2b391a2c32276768789774c9bddde053c455e 100644 (file)
@@ -129,16 +129,6 @@ public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSource
 
         final CheckedFuture<YangTextSchemaSource, SchemaSourceException> checked = Futures.makeChecked(transformed, MAPPER);
 
-        // / FIXME remove this get, it is only present to wait until source is retrieved
-        // (goal is to limit concurrent schema download, since NetconfDevice listener does not handle concurrent messages properly)
-        // TODO retest this
-        try {
-            LOG.trace("{}: Blocking for {}", id, sourceIdentifier);
-            checked.checkedGet();
-        } catch (final SchemaSourceException e) {
-            return Futures.immediateFailedCheckedFuture(e);
-        }
-
         return checked;
     }
 
index 2a6ba4301fe3e3827aac2ce4e0521959e0228469..81e09708f3addcb6daaf89a913ea2ee6c288e8c4 100644 (file)
@@ -106,7 +106,7 @@ public class KeepaliveSalFacadeTest {
         doReturn(Futures.immediateCheckedFuture(result)).when(deviceRpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
 
         final KeepaliveSalFacade keepaliveSalFacade =
-                new KeepaliveSalFacade(REMOTE_DEVICE_ID, underlyingSalFacade, executorServiceSpy, 1L);
+                new KeepaliveSalFacade(REMOTE_DEVICE_ID, underlyingSalFacade, executorServiceSpy, 1L, 1L);
         keepaliveSalFacade.setListener(listener);
 
         keepaliveSalFacade.onDeviceConnected(null, null, deviceRpc);
@@ -133,7 +133,7 @@ public class KeepaliveSalFacadeTest {
                 .when(deviceRpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
 
         final KeepaliveSalFacade keepaliveSalFacade =
-                new KeepaliveSalFacade(REMOTE_DEVICE_ID, underlyingSalFacade, executorServiceSpy, 1L);
+                new KeepaliveSalFacade(REMOTE_DEVICE_ID, underlyingSalFacade, executorServiceSpy, 1L, 1L);
         keepaliveSalFacade.setListener(listener);
 
         keepaliveSalFacade.onDeviceConnected(null, null, deviceRpc);
@@ -190,7 +190,7 @@ public class KeepaliveSalFacadeTest {
                 .when(deviceRpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
 
         final KeepaliveSalFacade keepaliveSalFacade =
-                new KeepaliveSalFacade(REMOTE_DEVICE_ID, underlyingSalFacade, executorServiceSpy, 100L);
+                new KeepaliveSalFacade(REMOTE_DEVICE_ID, underlyingSalFacade, executorServiceSpy, 100L, 1L);
         keepaliveSalFacade.setListener(listener);
 
         keepaliveSalFacade.onDeviceConnected(null, null, deviceRpc);
index 6386a8834839c6a5be3943858b27e5a2a5a6decb..7e62990bc7288e619362040c2b04f78e31f11ac9 100644 (file)
@@ -67,27 +67,6 @@ public class MountInstanceTest {
         mountInstance = new NetconfDeviceSalProvider.MountInstance(service, new RemoteDeviceId("device-1", InetSocketAddress.createUnresolved("localhost", 17830)));
     }
 
-    @Test
-    public void testOnDeviceConnected() throws Exception {
-        mountInstance.onDeviceConnected(SCHEMA_CONTEXT, broker, rpcService, notificationService);
-        verify(mountPointBuilder).addInitialSchemaContext(SCHEMA_CONTEXT);
-        verify(mountPointBuilder).addService(DOMDataBroker.class, broker);
-        verify(mountPointBuilder).addService(DOMRpcService.class, rpcService);
-        verify(mountPointBuilder).addService(DOMNotificationService.class, notificationService);
-    }
-
-    @Test
-    public void testOnDeviceDisconnected() throws Exception {
-        mountInstance.onDeviceConnected(SCHEMA_CONTEXT, broker, rpcService, notificationService);
-        mountInstance.onDeviceDisconnected();
-        verify(registration).close();
-        try {
-            mountInstance.onDeviceConnected(SCHEMA_CONTEXT, broker, rpcService, notificationService);
-        } catch (IllegalStateException e) {
-            e.printStackTrace();
-            Assert.fail("Registration still present after disconnect ");
-        }
-    }
 
     @Test
     public void testOnTopologyDeviceConnected() throws Exception {
index 1e15e115f35cb479defdeb2ed9754300dc570b06..c32f4fea3339fa1a676fa7d49d1fd23d5a3200be 100644 (file)
@@ -103,7 +103,7 @@ public class NetconfDeviceDataBrokerTest {
     private NetconfDeviceDataBroker getDataBroker(String... caps) {
         NetconfSessionPreferences prefs = NetconfSessionPreferences.fromStrings(Arrays.asList(caps));
         final RemoteDeviceId id = new RemoteDeviceId("device-1", InetSocketAddress.createUnresolved("localhost", 17830));
-        return new NetconfDeviceDataBroker(id, schemaContext, rpcService, prefs, 1000);
+        return new NetconfDeviceDataBroker(id, schemaContext, rpcService, prefs);
     }
 
 }
\ No newline at end of file
index 82fe4b23e788c64ea3c1924536c1432c118c6306..ecb0eeb7796e4582c35690f263dc9a566db0dcfb 100644 (file)
@@ -73,7 +73,7 @@ public class NetconfDeviceWriteOnlyTxTest {
     @Test
     public void testIgnoreNonVisibleData() {
         final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc, mock(SchemaContext.class)),
-                false, 60000L);
+                false);
         final MapNode emptyList = ImmutableNodes.mapNodeBuilder(NETCONF_FILTER_QNAME).build();
         tx.merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(NETCONF_FILTER_QNAME)), emptyList);
         tx.put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(NETCONF_FILTER_QNAME)), emptyList);
@@ -84,7 +84,7 @@ public class NetconfDeviceWriteOnlyTxTest {
     @Test
     public void testDiscardChanges() {
         final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc, mock(SchemaContext.class)),
-                false, 60000L);
+                false);
         final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = tx.submit();
         try {
             submitFuture.checkedGet();
@@ -110,7 +110,7 @@ public class NetconfDeviceWriteOnlyTxTest {
         .doReturn(rpcErrorFuture).when(rpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
 
         final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc, mock(SchemaContext.class)),
-                false, 60000L);
+                false);
 
         final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = tx.submit();
         try {
@@ -129,7 +129,7 @@ public class NetconfDeviceWriteOnlyTxTest {
                 .when(rpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
 
         final WriteRunningTx tx = new WriteRunningTx(id, new NetconfBaseOps(rpc, NetconfMessageTransformer.BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS.getSchemaContext()),
-                false, 60000L);
+                false);
         try {
             tx.delete(LogicalDatastoreType.CONFIGURATION, yangIId);
         } catch (final Exception e) {
index c005b5bf232c49730c8c726eda330f695b3a222c..1e9c0fcd86f26a632d0d1ebcb76d10139821c6fd 100644 (file)
@@ -13,27 +13,17 @@ import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
 
 import java.net.InetSocketAddress;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
-import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfBaseOps;
@@ -43,12 +33,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
 
-@PrepareForTest({NetconfBaseOps.class})
-@RunWith(PowerMockRunner.class)
 public class ReadOnlyTxTest {
 
     private static final YangInstanceIdentifier path = YangInstanceIdentifier.create();
@@ -70,33 +55,11 @@ public class ReadOnlyTxTest {
     public void testRead() throws Exception {
         final NetconfBaseOps netconfOps = new NetconfBaseOps(rpc, mock(SchemaContext.class));
 
-        final ReadOnlyTx readOnlyTx = new ReadOnlyTx(netconfOps, new RemoteDeviceId("a", new InetSocketAddress("localhost", 196)), 60000L);
+        final ReadOnlyTx readOnlyTx = new ReadOnlyTx(netconfOps, new RemoteDeviceId("a", new InetSocketAddress("localhost", 196)));
 
         readOnlyTx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create());
         verify(rpc).invokeRpc(Mockito.eq(NetconfMessageTransformUtil.toPath(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME)), any(NormalizedNode.class));
         readOnlyTx.read(LogicalDatastoreType.OPERATIONAL, path);
         verify(rpc).invokeRpc(Mockito.eq(NetconfMessageTransformUtil.toPath(NetconfMessageTransformUtil.NETCONF_GET_QNAME)), any(NormalizedNode.class));
     }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testReadTimeout() throws Exception {
-        final ListenableFuture<DOMRpcResult> future = mock(ListenableFuture.class);
-
-        Mockito.when(future.get(Mockito.anyLong(), any(TimeUnit.class))).then(new Answer<DOMRpcResult>() {
-            @Override
-            public DOMRpcResult answer(InvocationOnMock invocation)
-                    throws Throwable {
-                throw new TimeoutException("Processing Timeout");
-            }
-        });
-
-        final NetconfBaseOps netconfOps = PowerMockito.mock(NetconfBaseOps.class);
-        Mockito.when(netconfOps.getConfigRunning(any(FutureCallback.class), any(Optional.class))).thenReturn(future);
-
-
-        final ReadOnlyTx readOnlyTx = new ReadOnlyTx(netconfOps, new RemoteDeviceId("a", new InetSocketAddress("localhost", 196)), 100L);
-        Assert.assertNull("Read operation didn't correctly timeout", readOnlyTx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create()));
-        readOnlyTx.close();
-    }
 }
\ No newline at end of file
index 3655b6beaea921b000597b029cfe0c0dbc9175c7..0f15d9f3d6cce00e46447bbd30c746294bc6e130 100644 (file)
     </dependencyManagement>
 
     <dependencies>
+        <dependency>
+            <groupId>org.codehaus.janino</groupId>
+            <artifactId>janino</artifactId>
+            <version>2.6.1</version>
+        </dependency>
         <dependency>
             <groupId>net.sourceforge.argparse4j</groupId>
             <artifactId>argparse4j</artifactId>
index adce359f088e7a3d706fe2ba8589ecfe45978940..a8040f95eedb4f90c1ba3e0cef5062130cf84486 100644 (file)
@@ -9,6 +9,10 @@
 package org.opendaylight.netconf.test.tool;
 
 import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.util.ContextInitializer;
+import ch.qos.logback.core.joran.spi.JoranException;
+import ch.qos.logback.core.util.StatusPrinter;
 import com.google.common.base.Stopwatch;
 import com.google.common.io.CharStreams;
 import com.ning.http.client.AsyncHttpClient;
@@ -36,8 +40,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class ScaleUtil {
-
-    private static final Logger RESULTS_LOG = LoggerFactory.getLogger("results");
+    private static Logger RESULTS_LOG ;
     private static final ScheduledExecutorService executor = new LoggingWrapperExecutor(4);
 
     private static final int deviceStep = 1000;
@@ -53,8 +56,7 @@ public class ScaleUtil {
     public static void main(final String[] args) {
         final TesttoolParameters params = TesttoolParameters.parseArgs(args, TesttoolParameters.getParser());
 
-        root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
-        root.setLevel(params.debug ? Level.DEBUG : Level.INFO);
+        setUpLoggers(params);
 
         // cleanup at the start in case controller was already running
         final Runtime runtime = Runtime.getRuntime();
@@ -130,6 +132,14 @@ public class ScaleUtil {
         }
     }
 
+    private static void setUpLoggers(final TesttoolParameters params) {
+        System.setProperty("log_file_name", "scale-util.log");
+
+        root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+        root.setLevel(params.debug ? Level.DEBUG : Level.INFO);
+        RESULTS_LOG = LoggerFactory.getLogger("results");
+    }
+
     private static void cleanup(final Runtime runtime, final TesttoolParameters params) {
         try {
             stopKaraf(runtime, params);
index d94c0ad158eed22eedfe1fd666a7ae4797b7be45..cb5710749d9de261f54cea5746c715a91c44fbd3 100644 (file)
@@ -15,6 +15,7 @@ import java.util.Map;
 import java.util.Set;
 import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService;
 import org.opendaylight.netconf.impl.NetconfServerSessionNegotiatorFactory;
+import org.opendaylight.netconf.impl.NetconfServerSessionNegotiatorFactoryBuilder;
 import org.opendaylight.netconf.impl.SessionIdProvider;
 import org.opendaylight.netconf.mapping.api.NetconfOperationService;
 import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
@@ -26,7 +27,7 @@ public class TesttoolNegotiationFactory extends NetconfServerSessionNegotiatorFa
     public TesttoolNegotiationFactory(final Timer timer, final NetconfOperationServiceFactory netconfOperationProvider,
                                       final SessionIdProvider idProvider, final long connectionTimeoutMillis,
                                       final NetconfMonitoringService monitoringService) {
-        super(timer, netconfOperationProvider, idProvider, connectionTimeoutMillis, monitoringService);
+        super(timer, netconfOperationProvider, idProvider, connectionTimeoutMillis, monitoringService, NetconfServerSessionNegotiatorFactory.DEFAULT_BASE_CAPABILITIES);
     }
 
     public TesttoolNegotiationFactory(final Timer timer, final NetconfOperationServiceFactory netconfOperationProvider,
index 5f6f490afc02069bfef37e9f61ae75433f0efc15..6dc684780fdb4c8b4d7e6d097460235b17b8222c 100644 (file)
@@ -130,30 +130,37 @@ public class RestPerfClient {
         final ExecutorService executorService = Executors.newFixedThreadPool(threadAmount);
 
         LOG.info("Starting performance test");
+        boolean allThreadsCompleted = true;
         final Stopwatch started = Stopwatch.createStarted();
         try {
             final List<Future<Void>> futures = executorService.invokeAll(callables, parameters.timeout, TimeUnit.MINUTES);
             for (int i = 0; i < futures.size(); i++) {
                 Future<Void> future = futures.get(i);
                 if (future.isCancelled()) {
+                    allThreadsCompleted = false;
                     LOG.info("{}. thread timed out.", i + 1);
                 } else {
                     try {
                         future.get();
                     } catch (final ExecutionException e) {
+                        allThreadsCompleted = false;
                         LOG.info("{}. thread failed.", i + 1, e);
                     }
                 }
             }
         } catch (final InterruptedException e) {
+            allThreadsCompleted = false;
             LOG.warn("Unable to execute requests", e);
         }
         executorService.shutdownNow();
         started.stop();
 
         LOG.info("FINISHED. Execution time: {}", started);
-        LOG.info("Requests per second: {}", (parameters.editCount * 1000.0 / started.elapsed(TimeUnit.MILLISECONDS)));
-
+        // If some threads failed or timed out, skip calculation of requests per second value
+        // and do not log it
+        if(allThreadsCompleted) {
+            LOG.info("Requests per second: {}", (parameters.editCount * 1000.0 / started.elapsed(TimeUnit.MILLISECONDS)));
+        }
         System.exit(0);
     }
 
index 1bdb2f02a0f0862d490ce7764b64bc8e0529651f..a793b8027380a26131e95ab9c54f1b2f06a2fe8e 100644 (file)
         <appender-ref ref="STDOUT"/>
     </root>
 
-    <appender name="RESULTS-FILE" class="ch.qos.logback.core.FileAppender">
-        <file>scale-results.log</file>
-        <append>true</append>
-        <encoder>
-          <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
-        </encoder>
-    </appender>
+    <if condition='property("log_file_name").contains("scale-util.log")'>
+        <then>
+            <appender name="RESULTS-FILE" class="ch.qos.logback.core.FileAppender">
+                <file>${log_file_name}</file>
+                <append>true</append>
+                <encoder>
+                    <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+                </encoder>
+            </appender>
 
-    <logger name="results" level="DEBUG" additivity="false">
-       <appender-ref ref="RESULTS-FILE"/>
-    </logger>
+            <logger name="results" level="DEBUG" additivity="false">
+                <appender-ref ref="RESULTS-FILE"/>
+            </logger>
+        </then>
+    </if>
 
     <logger name="com.ning.http.client" level="WARN"/>
 </configuration>
\ No newline at end of file
index aee9c961ebf4082e3d24dfbb9936410d4c1ffac2..86c8a3dd6723c799ce949f3cf89e8ab90f135a99 100644 (file)
     <dependency>
       <groupId>net.java.dev.stax-utils</groupId>
       <artifactId>stax-utils</artifactId>
-      <version>20070216</version>
     </dependency>
 
     <dependency>
               org.opendaylight.aaa.filterchain.filters,
               org.apache.shiro.web.env
             </Import-Package>
-            <Embed-Dependency>stax-utils</Embed-Dependency>
             <Web-ContextPath>/restconf</Web-ContextPath>
           </instructions>
         </configuration>
index fac2b1872e2c9c9cf3d179b0717188731b5f9e48..17731de775d582fa4c21e94c998c0acd3d43d8ed 100644 (file)
@@ -21,7 +21,11 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
+import org.opendaylight.netconf.sal.rest.api.Draft02.MediaTypes;
+import org.opendaylight.netconf.sal.rest.impl.PATCH;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
 
 /**
  * The URI hierarchy for the RESTCONF resources consists of an entry point container, 4 top-level resources, and 1
@@ -151,4 +155,16 @@ public interface RestconfService {
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     public NormalizedNodeContext getAvailableStreams(@Context UriInfo uriInfo);
 
+    @PATCH
+    @Path("/config/{identifier:.+}")
+    @Consumes({MediaTypes.PATCH + JSON, MediaTypes.PATCH + XML})
+    @Produces({MediaTypes.PATCH_STATUS + JSON, MediaTypes.PATCH_STATUS + XML})
+    PATCHStatusContext patchConfigurationData(@Encoded @PathParam("identifier") String identifier, PATCHContext
+            context, @Context UriInfo uriInfo);
+
+    @PATCH
+    @Path("/config")
+    @Consumes({MediaTypes.PATCH + JSON, MediaTypes.PATCH + XML})
+    @Produces({MediaTypes.PATCH_STATUS + JSON, MediaTypes.PATCH_STATUS + XML})
+    PATCHStatusContext patchConfigurationData(PATCHContext context, @Context UriInfo uriInfo);
 }
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonToPATCHBodyReader.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonToPATCHBodyReader.java
new file mode 100644 (file)
index 0000000..6d3f7ea
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.sal.rest.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.gson.stream.JsonReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.Provider;
+import org.opendaylight.netconf.sal.rest.api.Draft02;
+import org.opendaylight.netconf.sal.rest.api.RestconfService;
+import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHEntity;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.data.impl.schema.ResultAlreadySetException;
+import org.opendaylight.yangtools.yang.data.util.AbstractStringInstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Provider
+@Consumes({Draft02.MediaTypes.PATCH + RestconfService.JSON})
+public class JsonToPATCHBodyReader extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader<PATCHContext> {
+
+    private final static Logger LOG = LoggerFactory.getLogger(JsonToPATCHBodyReader.class);
+    private String patchId;
+
+    @Override
+    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return true;
+    }
+
+    @Override
+    public PATCHContext readFrom(Class<PATCHContext> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
+        try {
+            return readFrom(getInstanceIdentifierContext(), entityStream);
+        } catch (final Exception e) {
+            throw propagateExceptionAs(e);
+        }
+    }
+
+    private static RuntimeException propagateExceptionAs(Exception e) throws RestconfDocumentedException {
+        if(e instanceof RestconfDocumentedException) {
+            throw (RestconfDocumentedException)e;
+        }
+
+        if(e instanceof ResultAlreadySetException) {
+            LOG.debug("Error parsing json input:", e);
+
+            throw new RestconfDocumentedException("Error parsing json input: Failed to create new parse result data. ");
+        }
+
+        throw new RestconfDocumentedException("Error parsing json input: " + e.getMessage(), ErrorType.PROTOCOL,
+                ErrorTag.MALFORMED_MESSAGE, e);
+    }
+
+    public PATCHContext readFrom(final String uriPath, final InputStream entityStream) throws
+            RestconfDocumentedException {
+        try {
+            return readFrom(ControllerContext.getInstance().toInstanceIdentifier(uriPath), entityStream);
+        } catch (final Exception e) {
+            propagateExceptionAs(e);
+            return null; // no-op
+        }
+    }
+
+    private PATCHContext readFrom(final InstanceIdentifierContext<?> path, final InputStream entityStream) throws IOException {
+        if (entityStream.available() < 1) {
+            return new PATCHContext(path, null, null);
+        }
+
+        final JsonReader jsonReader = new JsonReader(new InputStreamReader(entityStream));
+        final List<PATCHEntity> resultList = read(jsonReader, path);
+        jsonReader.close();
+
+        return new PATCHContext(path, resultList, patchId);
+    }
+
+    private List<PATCHEntity> read(final JsonReader in, InstanceIdentifierContext path) throws
+            IOException {
+
+        boolean inEdit = false;
+        boolean inValue = false;
+        String operation = null;
+        String target = null;
+        String editId = null;
+        List<PATCHEntity> resultCollection = new ArrayList<>();
+
+        while (in.hasNext()) {
+            switch (in.peek()) {
+                case STRING:
+                case NUMBER:
+                    in.nextString();
+                    break;
+                case BOOLEAN:
+                    Boolean.toString(in.nextBoolean());
+                    break;
+                case NULL:
+                    in.nextNull();
+                    break;
+                case BEGIN_ARRAY:
+                    in.beginArray();
+                    break;
+                case BEGIN_OBJECT:
+                    if (inEdit && operation != null & target != null & inValue) {
+                        //let's do the stuff - find out target node
+//                      StringInstanceIdentifierCodec codec = new StringInstanceIdentifierCodec(path
+//                               .getSchemaContext());
+//                        if (path.getInstanceIdentifier().toString().equals("/")) {
+//                        final YangInstanceIdentifier deserialized = codec.deserialize(target);
+//                        }
+                        DataSchemaNode targetNode = ((DataNodeContainer)(path.getSchemaNode())).getDataChildByName
+                                (target.replace("/", ""));
+                        if (targetNode == null) {
+                            LOG.debug("Target node {} not found in path {} ", target, path.getSchemaNode());
+                            throw new RestconfDocumentedException("Error parsing input", ErrorType.PROTOCOL,
+                                    ErrorTag.MALFORMED_MESSAGE);
+                        } else {
+
+                            final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
+                            final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
+
+                            //keep on parsing json from place where target points
+                            final JsonParserStream jsonParser = JsonParserStream.create(writer, path.getSchemaContext(),
+                                    path.getSchemaNode());
+                            jsonParser.parse(in);
+
+                            final YangInstanceIdentifier targetII = path.getInstanceIdentifier().node(targetNode.getQName());
+                            resultCollection.add(new PATCHEntity(editId, operation, targetII, resultHolder.getResult
+                                    ()));
+                            inValue = false;
+
+                            operation = null;
+                            target = null;
+                        }
+                        in.endObject();
+                    } else {
+                        in.beginObject();
+                    }
+                    break;
+                case END_DOCUMENT:
+                    break;
+                case NAME:
+                    final String name = in.nextName();
+
+                    switch (name) {
+                        case "edit" : inEdit = true;
+                            break;
+                        case "operation" : operation = in.nextString();
+                            break;
+                        case "target" : target = in.nextString();
+                            break;
+                        case "value" : inValue = true;
+                            break;
+                        case "patch-id" : patchId = in.nextString();
+                            break;
+                        case "edit-id" : editId = in.nextString();
+                            break;
+                    }
+                    break;
+                case END_OBJECT:
+                    in.endObject();
+                    break;
+            case END_ARRAY:
+                in.endArray();
+                break;
+
+            default:
+                break;
+            }
+        }
+
+        return ImmutableList.copyOf(resultCollection);
+    }
+
+    private class StringInstanceIdentifierCodec extends AbstractStringInstanceIdentifierCodec {
+
+        private final DataSchemaContextTree dataContextTree;
+        private final SchemaContext context;
+
+        StringInstanceIdentifierCodec(SchemaContext context) {
+            this.context = Preconditions.checkNotNull(context);
+            this.dataContextTree = DataSchemaContextTree.from(context);
+        }
+
+        @Nonnull
+        @Override
+        protected DataSchemaContextTree getDataContextTree() {
+            return dataContextTree;
+        }
+
+        @Nullable
+        @Override
+        protected String prefixForNamespace(@Nonnull URI namespace) {
+            final Module module = context.findModuleByNamespaceAndRevision(namespace, null);
+            return module == null ? null : module.getName();
+        }
+
+        @Nullable
+        @Override
+        protected QName createQName(@Nonnull String prefix, @Nonnull String localName) {
+            throw new UnsupportedOperationException("Not implemented");
+        }
+
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/PATCH.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/PATCH.java
new file mode 100644 (file)
index 0000000..f282971
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.sal.rest.impl;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.ws.rs.HttpMethod;
+
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@HttpMethod("PATCH")
+@Documented
+public @interface PATCH {
+}
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/PATCHJsonBodyWriter.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/PATCHJsonBodyWriter.java
new file mode 100644 (file)
index 0000000..9f9ab94
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.sal.rest.impl;
+
+import com.google.common.base.Charsets;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.List;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import org.opendaylight.netconf.sal.rest.api.Draft02.MediaTypes;
+import org.opendaylight.netconf.sal.rest.api.RestconfService;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusEntity;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
+
+@Provider
+@Produces({MediaTypes.PATCH_STATUS + RestconfService.JSON})
+public class PATCHJsonBodyWriter implements MessageBodyWriter<PATCHStatusContext> {
+
+    @Override
+    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return type.equals(PATCHStatusContext.class);
+    }
+
+    @Override
+    public long getSize(PATCHStatusContext patchStatusContext, Class<?> type, Type genericType, Annotation[]
+            annotations, MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(PATCHStatusContext patchStatusContext, Class<?> type, Type genericType, Annotation[]
+            annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+                throws IOException, WebApplicationException {
+
+        final JsonWriter jsonWriter = createJsonWriter(entityStream);
+        jsonWriter.beginObject().name("ietf-yang-patch:yang-patch-status");
+        jsonWriter.beginObject();
+        jsonWriter.name("patch-id").value(patchStatusContext.getPatchId());
+        if (patchStatusContext.isOk()) {
+            jsonWriter.name("ok").nullValue();
+        } else {
+            if (patchStatusContext.getGlobalErrors() != null) {
+                reportErrors(patchStatusContext.getGlobalErrors(), jsonWriter);
+            }
+
+            jsonWriter.name("edit-status");
+            jsonWriter.beginObject();
+            jsonWriter.name("edit");
+            jsonWriter.beginArray();
+            for (PATCHStatusEntity patchStatusEntity : patchStatusContext.getEditCollection()) {
+                jsonWriter.beginObject();
+                jsonWriter.name("edit-id").value(patchStatusEntity.getEditId());
+                if (patchStatusEntity.getEditErrors() != null) {
+                    reportErrors(patchStatusEntity.getEditErrors(), jsonWriter);
+                } else {
+                    if (patchStatusEntity.isOk()) {
+                        jsonWriter.name("ok").nullValue();
+                    }
+                }
+                jsonWriter.endObject();
+            }
+            jsonWriter.endArray();
+            jsonWriter.endObject();
+        }
+        jsonWriter.endObject();
+        jsonWriter.endObject();
+        jsonWriter.flush();
+
+    }
+
+    private static void reportErrors(List<RestconfError> errors, JsonWriter jsonWriter) throws IOException {
+        jsonWriter.name("errors");
+        jsonWriter.beginObject();
+        jsonWriter.name("error");
+        jsonWriter.beginArray();
+
+        for (RestconfError restconfError : errors) {
+            jsonWriter.beginObject();
+            jsonWriter.name("error-type").value(restconfError.getErrorType().getErrorTypeTag());
+            jsonWriter.name("error-tag").value(restconfError.getErrorTag().getTagValue());
+            //TODO: fix error-path reporting (separate error-path from error-message)
+            //jsonWriter.name("error-path").value(restconfError.getErrorPath());
+            jsonWriter.name("error-message").value(restconfError.getErrorMessage());
+            jsonWriter.endObject();
+        }
+
+        jsonWriter.endArray();
+        jsonWriter.endObject();
+    }
+
+    private static JsonWriter createJsonWriter(final OutputStream entityStream) {
+        return JsonWriterFactory.createJsonWriter(new OutputStreamWriter(entityStream, Charsets.UTF_8));
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/PATCHXmlBodyWriter.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/PATCHXmlBodyWriter.java
new file mode 100644 (file)
index 0000000..2d094a6
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netconf.sal.rest.impl;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.List;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.netconf.sal.rest.api.Draft02.MediaTypes;
+import org.opendaylight.netconf.sal.rest.api.RestconfService;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusEntity;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
+
+@Provider
+@Produces({ MediaTypes.PATCH_STATUS + RestconfService.XML})
+public class PATCHXmlBodyWriter implements MessageBodyWriter<PATCHStatusContext> {
+
+    private static final XMLOutputFactory XML_FACTORY;
+
+    static {
+        XML_FACTORY = XMLOutputFactory.newFactory();
+        XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
+    }
+
+    @Override
+    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return type.equals(PATCHStatusContext.class);
+    }
+
+    @Override
+    public long getSize(PATCHStatusContext patchStatusContext, Class<?> type, Type genericType, Annotation[]
+            annotations, MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(PATCHStatusContext patchStatusContext, Class<?> type, Type genericType, Annotation[]
+            annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+                throws IOException, WebApplicationException {
+
+        try {
+            XMLStreamWriter xmlWriter = XML_FACTORY.createXMLStreamWriter(entityStream);
+            writeDocument(xmlWriter, patchStatusContext);
+        } catch (final XMLStreamException e) {
+            throw new IllegalStateException(e);
+        } catch (final FactoryConfigurationError e) {
+            throw new IllegalStateException(e);
+        }
+
+    }
+
+    private static void writeDocument(XMLStreamWriter writer, PATCHStatusContext context) throws XMLStreamException, IOException {
+        writer.writeStartElement("", "yang-patch-status", "urn:ietf:params:xml:ns:yang:ietf-yang-patch");
+        writer.writeStartElement("patch-id");
+        writer.writeCharacters(context.getPatchId());
+        writer.writeEndElement();
+
+        if (context.isOk()) {
+            writer.writeEmptyElement("ok");
+        } else {
+            if (context.getGlobalErrors() != null) {
+                reportErrors(context.getGlobalErrors(), writer);
+            }
+            writer.writeStartElement("edit-status");
+            for (PATCHStatusEntity patchStatusEntity : context.getEditCollection()) {
+                writer.writeStartElement("edit");
+                writer.writeStartElement("edit-id");
+                writer.writeCharacters(patchStatusEntity.getEditId());
+                writer.writeEndElement();
+                if (patchStatusEntity.getEditErrors() != null) {
+                    reportErrors(patchStatusEntity.getEditErrors(), writer);
+                } else {
+                    if (patchStatusEntity.isOk()) {
+                        writer.writeEmptyElement("ok");
+                    }
+                }
+                writer.writeEndElement();
+            }
+            writer.writeEndElement();
+
+        }
+        writer.writeEndElement();
+
+        writer.flush();
+    }
+
+    private static void reportErrors(List<RestconfError> errors, XMLStreamWriter writer) throws IOException, XMLStreamException {
+        writer.writeStartElement("errors");
+
+        for (RestconfError restconfError : errors) {
+            writer.writeStartElement("error-type");
+            writer.writeCharacters(restconfError.getErrorType().getErrorTypeTag());
+            writer.writeEndElement();
+            writer.writeStartElement("error-tag");
+            writer.writeCharacters(restconfError.getErrorTag().getTagValue());
+            writer.writeEndElement();
+            //TODO: fix error-path reporting (separate error-path from error-message)
+//            writer.writeStartElement("error-path");
+//            writer.writeCharacters(restconfError.getErrorPath());
+//            writer.writeEndElement();
+            writer.writeStartElement("error-message");
+            writer.writeCharacters(restconfError.getErrorMessage());
+            writer.writeEndElement();
+        }
+
+        writer.writeEndElement();
+    }
+}
index bfcd826f5d1ca9c295b1a11be4ae3c992472f4f3..46b8743832109605d6eb3399a3348a9c5f9108ad 100644 (file)
@@ -27,6 +27,10 @@ public class RestconfApplication extends Application {
                 .add(RestconfDocumentedExceptionMapper.class)
                 .add(XmlNormalizedNodeBodyReader.class)
                 .add(JsonNormalizedNodeBodyReader.class)
+                .add(JsonToPATCHBodyReader.class)
+                .add(XmlToPATCHBodyReader.class)
+                .add(PATCHJsonBodyWriter.class)
+                .add(PATCHXmlBodyWriter.class)
                 .add(NormalizedNodeJsonBodyWriter.class)
                 .add(NormalizedNodeXmlBodyWriter.class)
                 .add(SchemaExportContentYinBodyWriter.class)
index 4015d9ac22e59d04f49c26715e00fcc80a26a0bc..df56bc4c0e70a7ada81e6ab0d38169a6d60eed57 100644 (file)
@@ -9,12 +9,15 @@
 package org.opendaylight.netconf.sal.rest.impl;
 
 import com.google.common.base.Preconditions;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContext;
 import org.opendaylight.netconf.md.sal.rest.schema.SchemaRetrievalService;
 import org.opendaylight.netconf.sal.rest.api.RestconfService;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
 
 public class RestconfCompositeWrapper implements RestconfService, SchemaRetrievalService {
 
@@ -107,6 +110,16 @@ public class RestconfCompositeWrapper implements RestconfService, SchemaRetrieva
         return restconf.getAvailableStreams(uriInfo);
     }
 
+    @Override
+    public PATCHStatusContext patchConfigurationData(final String identifier, PATCHContext payload, UriInfo uriInfo) {
+        return restconf.patchConfigurationData(identifier, payload, uriInfo);
+    }
+
+    @Override
+    public PATCHStatusContext patchConfigurationData(final PATCHContext context, final UriInfo uriInfo) {
+        return restconf.patchConfigurationData(context, uriInfo);
+    }
+
     @Override
     public SchemaExportContext getSchema(final String mountId) {
         return schema.getSchema(mountId);
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlToPATCHBodyReader.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlToPATCHBodyReader.java
new file mode 100644 (file)
index 0000000..fe63f31
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.sal.rest.impl;
+
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.Provider;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHEntity;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.opendaylight.netconf.sal.rest.api.Draft02.MediaTypes;
+import org.opendaylight.netconf.sal.rest.api.RestconfService;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+@Provider
+@Consumes({MediaTypes.PATCH + RestconfService.XML})
+public class XmlToPATCHBodyReader extends AbstractIdentifierAwareJaxRsProvider implements
+        MessageBodyReader<PATCHContext> {
+
+    private final static Logger LOG = LoggerFactory.getLogger(XmlToPATCHBodyReader.class);
+    private static final DocumentBuilderFactory BUILDERFACTORY;
+
+    static {
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        try {
+            factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+            factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+            factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+            factory.setXIncludeAware(false);
+            factory.setExpandEntityReferences(false);
+        } catch (final ParserConfigurationException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+        factory.setNamespaceAware(true);
+        factory.setCoalescing(true);
+        factory.setIgnoringElementContentWhitespace(true);
+        factory.setIgnoringComments(true);
+        BUILDERFACTORY = factory;
+    }
+
+    @Override
+    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+        return true;
+    }
+
+    @Override
+    public PATCHContext readFrom(Class<PATCHContext> type, Type genericType, Annotation[] annotations, MediaType
+            mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException,
+            WebApplicationException {
+
+        try {
+            final InstanceIdentifierContext<?> path = getInstanceIdentifierContext();
+
+            if (entityStream.available() < 1) {
+                // represent empty nopayload input
+                return new PATCHContext(path, null, null);
+            }
+
+            final DocumentBuilder dBuilder;
+            try {
+                dBuilder = BUILDERFACTORY.newDocumentBuilder();
+            } catch (final ParserConfigurationException e) {
+                throw new IllegalStateException("Failed to parse XML document", e);
+            }
+            final Document doc = dBuilder.parse(entityStream);
+
+            return parse(path, doc);
+        } catch (final RestconfDocumentedException e) {
+            throw e;
+        } catch (final Exception e) {
+            LOG.debug("Error parsing xml input", e);
+
+            throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
+                    ErrorTag.MALFORMED_MESSAGE);
+        }
+    }
+
+    private PATCHContext parse(final InstanceIdentifierContext<?> pathContext, final Document doc) {
+        final List<PATCHEntity> resultCollection = new ArrayList<>();
+        final String patchId = doc.getElementsByTagName("patch-id").item(0).getFirstChild().getNodeValue();
+        final NodeList editNodes = doc.getElementsByTagName("edit");
+        final DataSchemaNode schemaNode = (DataSchemaNode) pathContext.getSchemaNode();
+        final DomToNormalizedNodeParserFactory parserFactory =
+                DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER,
+                        pathContext.getSchemaContext());
+
+        for (int i = 0; i < editNodes.getLength(); i++) {
+            Element element = (Element) editNodes.item(i);
+            final String operation = element.getElementsByTagName("operation").item(0).getFirstChild().getNodeValue();
+            final String editId = element.getElementsByTagName("edit-id").item(0).getFirstChild().getNodeValue();
+            final String target = element.getElementsByTagName("target").item(0).getFirstChild().getNodeValue();
+            DataSchemaNode targetNode = ((DataNodeContainer)(pathContext.getSchemaNode())).getDataChildByName
+                    (target.replace("/", ""));
+            if (targetNode == null) {
+                LOG.debug("Target node {} not found in path {} ", target, pathContext.getSchemaNode());
+                throw new RestconfDocumentedException("Error parsing input", ErrorType.PROTOCOL,
+                        ErrorTag.MALFORMED_MESSAGE);
+            } else {
+                final YangInstanceIdentifier targetII = pathContext.getInstanceIdentifier().node(targetNode.getQName());
+                final NodeList valueNodes = element.getElementsByTagName("value").item(0).getChildNodes();
+                Element value = null;
+                for (int j = 0; j < valueNodes.getLength(); j++) {
+                    if (valueNodes.item(j) instanceof Element) {
+                        value = (Element) valueNodes.item(j);
+                        break;
+                    }
+                }
+                NormalizedNode<?, ?> parsed = null;
+                if (schemaNode instanceof ContainerSchemaNode) {
+                    parsed = parserFactory.getContainerNodeParser().parse(Collections.singletonList(value),
+                            (ContainerSchemaNode) targetNode);
+                } else if (schemaNode instanceof ListSchemaNode) {
+                    NormalizedNode<?, ?> parsedValue = parserFactory.getMapEntryNodeParser().parse(Collections
+                            .singletonList(value), (ListSchemaNode) targetNode);
+                    parsed = ImmutableNodes.mapNodeBuilder().withNodeIdentifier(new NodeIdentifier
+                            (targetNode.getQName())).withChild((MapEntryNode) parsedValue).build();
+                }
+
+                resultCollection.add(new PATCHEntity(editId, operation, targetII, parsed));
+            }
+        }
+
+        return new PATCHContext(pathContext, ImmutableList.copyOf(resultCollection), patchId);
+    }
+}
index fcf82e5623c142bd41ab261e48ee44eda79e2514..e2097310f2ce485d422941dc05237f665a4a069e 100644 (file)
@@ -11,6 +11,7 @@ import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastor
 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.util.ArrayList;
@@ -126,6 +127,88 @@ public class BrokerFacade {
         throw new RestconfDocumentedException(errMsg);
     }
 
+    public PATCHStatusContext patchConfigurationDataWithinTransaction(final PATCHContext context,
+                                                                      final SchemaContext globalSchema) {
+        final DOMDataReadWriteTransaction patchTransaction = domDataBroker.newReadWriteTransaction();
+        List<PATCHStatusEntity> editCollection = new ArrayList<>();
+        List<RestconfError> editErrors;
+        List<RestconfError> globalErrors = null;
+        int errorCounter = 0;
+
+        for (PATCHEntity patchEntity : context.getData()) {
+            final PATCHEditOperation operation = PATCHEditOperation.valueOf(patchEntity.getOperation().toUpperCase());
+
+            switch (operation) {
+                case CREATE:
+                    if (errorCounter == 0) {
+                        try {
+                            postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
+                                    patchEntity.getNode(), globalSchema);
+                            editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
+                        } catch (RestconfDocumentedException e) {
+                            editErrors = new ArrayList<>();
+                            editErrors.addAll(e.getErrors());
+                            editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
+                            errorCounter++;
+                        }
+                    }
+                    break;
+                case REPLACE:
+                    if (errorCounter == 0) {
+                        try {
+                            putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
+                                    .getTargetNode(), patchEntity.getNode(), globalSchema);
+                            editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
+                        } catch (RestconfDocumentedException e) {
+                            editErrors = new ArrayList<>();
+                            editErrors.addAll(e.getErrors());
+                            editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
+                            errorCounter++;
+                        }
+                    }
+                    break;
+                case DELETE:
+                    if (errorCounter == 0) {
+                        try {
+                            deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
+                                    .getTargetNode());
+                            editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
+                        } catch (RestconfDocumentedException e) {
+                            editErrors = new ArrayList<>();
+                            editErrors.addAll(e.getErrors());
+                            editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
+                            errorCounter++;
+                        }
+                    }
+                    break;
+                case REMOVE:
+                    if (errorCounter == 0) {
+                        try {
+                            deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
+                                    .getTargetNode());
+                            editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
+                        } catch (RestconfDocumentedException e) {
+                            LOG.error("Error removing {} by {} operation", patchEntity.getTargetNode().toString(),
+                                    patchEntity.getEditId(), e);
+                        }
+                    }
+                    break;
+            }
+        }
+
+        //TODO: make sure possible global errors are filled up correctly and decide transaction submission based on that
+        //globalErrors = new ArrayList<>();
+        if (errorCounter == 0) {
+            final CheckedFuture<Void, TransactionCommitFailedException> submit = patchTransaction.submit();
+            return new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), true,
+                    globalErrors);
+        } else {
+            patchTransaction.cancel();
+            return new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false,
+                    globalErrors);
+        }
+    }
+
     // POST configuration
     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
             final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
@@ -190,7 +273,7 @@ public class BrokerFacade {
 
     private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
             final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
-        LOG.trace("Read " + datastore.name() + " via Restconf: {}", path);
+        LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
         final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
         if (listenableFuture != null) {
             Optional<NormalizedNode<?, ?>> optional;
@@ -198,7 +281,7 @@ public class BrokerFacade {
                 LOG.debug("Reading result data from transaction.");
                 optional = listenableFuture.get();
             } catch (InterruptedException | ExecutionException e) {
-                LOG.warn("Exception by reading " + datastore.name() + " via Restconf: {}", path, e);
+                LOG.warn("Exception by reading {} via Restconf: {}", datastore.name(), path, e);
                 throw new RestconfDocumentedException("Problem to get data from transaction.", e.getCause());
 
             }
@@ -217,7 +300,7 @@ public class BrokerFacade {
         // FIXME: This is doing correct post for container and list children
         //        not sure if this will work for choice case
         if(payload instanceof MapNode) {
-            LOG.trace("POST " + datastore.name() + " via Restconf: {} with payload {}", path, payload);
+            LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
             final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
             rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
             ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
@@ -234,18 +317,40 @@ public class BrokerFacade {
         return rWTransaction.submit();
     }
 
+    private void postDataWithinTransaction(
+            final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+        // FIXME: This is doing correct post for container and list children
+        //        not sure if this will work for choice case
+        if(payload instanceof MapNode) {
+            LOG.trace("POST {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
+            final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+            rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+            ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
+            for(final MapEntryNode child : ((MapNode) payload).getValue()) {
+                final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+                checkItemDoesNotExists(rWTransaction, datastore, childPath);
+                rWTransaction.put(datastore, childPath, child);
+            }
+        } else {
+            checkItemDoesNotExists(rWTransaction,datastore, path);
+            ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
+            rWTransaction.put(datastore, path, payload);
+        }
+    }
+
     private void checkItemDoesNotExists(final DOMDataReadWriteTransaction rWTransaction,final LogicalDatastoreType store, final YangInstanceIdentifier path) {
         final ListenableFuture<Boolean> futureDatastoreData = rWTransaction.exists(store, path);
         try {
             if (futureDatastoreData.get()) {
                 final String errMsg = "Post Configuration via Restconf was not executed because data already exists";
-                LOG.trace(errMsg + ":{}", path);
+                LOG.trace("{}:{}", errMsg, path);
                 rWTransaction.cancel();
                 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
                         ErrorTag.DATA_EXISTS);
             }
         } catch (InterruptedException | ExecutionException e) {
-            LOG.warn("It wasn't possible to get data loaded from datastore at path " + path, e);
+            LOG.warn("It wasn't possible to get data loaded from datastore at path {}", path, e);
         }
 
     }
@@ -253,20 +358,35 @@ public class BrokerFacade {
     private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
             final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
-        LOG.trace("Put " + datastore.name() + " via Restconf: {} with payload {}", path, payload);
+        LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
         ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
         writeTransaction.put(datastore, path, payload);
         return writeTransaction.submit();
     }
 
+    private void putDataWithinTransaction(
+            final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+        LOG.trace("Put {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
+        ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
+        writeTransaction.put(datastore, path, payload);
+    }
+
     private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
             final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
             final YangInstanceIdentifier path) {
-        LOG.trace("Delete " + datastore.name() + " via Restconf: {}", path);
+        LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
         writeTransaction.delete(datastore, path);
         return writeTransaction.submit();
     }
 
+    private void deleteDataWithinTransaction(
+            final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path) {
+        LOG.trace("Delete {} within Restconf PATCH: {}", datastore.name(), path);
+        writeTransaction.delete(datastore, path);
+    }
+
     public void setDomDataBroker(final DOMDataBroker domDataBroker) {
         this.domDataBroker = domDataBroker;
     }
index f21823cda081bc2d5db4d59247092092687430f8..e4f71a7ff03e00a07df93422f9bd8b0eb7826cc2 100644 (file)
@@ -596,7 +596,13 @@ public class ControllerContext implements SchemaContextListener {
             targetNode = findInstanceDataChildByNameAndNamespace(parentNode, nodeName, module.getNamespace());
 
             if (targetNode == null && parentNode instanceof Module) {
-                final RpcDefinition rpc = ControllerContext.getInstance().getRpcDefinition(head, module.getRevision());
+                final RpcDefinition rpc;
+                if (mountPoint == null) {
+                    rpc = ControllerContext.getInstance().getRpcDefinition(head, module.getRevision());
+                } else {
+                    final String rpcName = toNodeName(head);
+                    rpc = ControllerContext.getInstance().getRpcDefinition(module, rpcName);
+                }
                 if (rpc != null) {
                     return new InstanceIdentifierContext<RpcDefinition>(builder.build(), rpc, mountPoint,
                             mountPoint != null ? mountPoint.getSchemaContext() : globalSchema);
@@ -829,6 +835,16 @@ public class ControllerContext implements SchemaContextListener {
         return validName == null ? null : qnameToRpc.get().get(validName);
     }
 
+    private RpcDefinition getRpcDefinition(final Module module, final String rpcName) {
+        QName rpcQName = QName.create(module.getQNameModule(), rpcName);
+        for (RpcDefinition rpcDefinition : module.getRpcs()) {
+            if (rpcQName.equals(rpcDefinition.getQName())) {
+                return rpcDefinition;
+            }
+        }
+        return null;
+    }
+
     @Override
     public void onGlobalContextUpdated(final SchemaContext context) {
         if (context != null) {
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHContext.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHContext.java
new file mode 100644 (file)
index 0000000..adba67d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.sal.restconf.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+public class PATCHContext {
+
+    private final InstanceIdentifierContext<? extends SchemaNode> context;
+    private final List<PATCHEntity> data;
+    private final String patchId;
+
+    public PATCHContext(final InstanceIdentifierContext<? extends SchemaNode> context,
+                        final List<PATCHEntity> data, final String patchId) {
+        this.context = Preconditions.checkNotNull(context);
+        this.data = Preconditions.checkNotNull(data);
+        this.patchId = Preconditions.checkNotNull(patchId);
+    }
+
+    public InstanceIdentifierContext<? extends SchemaNode> getInstanceIdentifierContext() {
+        return context;
+    }
+
+    public List<PATCHEntity> getData() {
+        return data;
+    }
+
+    public String getPatchId() {
+        return patchId;
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHEditOperation.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHEditOperation.java
new file mode 100644 (file)
index 0000000..e0bdfd5
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.sal.restconf.impl;
+
+/**
+ *
+ * Each YANG patch edit specifies one edit operation on the target data
+ * node.  The set of operations is aligned with the NETCONF edit
+ * operations, but also includes some new operations.
+ *
+ */
+enum PATCHEditOperation {
+    CREATE,  //post
+    DELETE,  //delete
+    INSERT,  //post
+    MERGE,
+    MOVE,    //delete+post
+    REPLACE, //put
+    REMOVE   //delete
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHEntity.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHEntity.java
new file mode 100644 (file)
index 0000000..ae16edc
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.sal.restconf.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class PATCHEntity {
+
+    private final String operation;
+    private final String editId;
+    private final YangInstanceIdentifier targetNode;
+    private final NormalizedNode<?,?> node;
+
+    public PATCHEntity(final String editId, final String operation, final YangInstanceIdentifier targetNode, final
+    NormalizedNode<?, ?> node) {
+        this.editId = Preconditions.checkNotNull(editId);
+        this.operation = Preconditions.checkNotNull(operation);
+        this.targetNode = Preconditions.checkNotNull(targetNode);
+        this.node = Preconditions.checkNotNull(node);
+    }
+
+    public String getOperation() {
+        return operation;
+    }
+
+    public String getEditId() {
+        return editId;
+    }
+
+    public YangInstanceIdentifier getTargetNode() {
+        return targetNode;
+    }
+
+    public NormalizedNode<?, ?> getNode() {
+        return node;
+    }
+
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHStatusContext.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHStatusContext.java
new file mode 100644 (file)
index 0000000..f93dca1
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.sal.restconf.impl;
+
+import java.util.List;
+
+public class PATCHStatusContext {
+
+    private final String patchId;
+    private final List<PATCHStatusEntity> editCollection;
+    private boolean ok;
+    private List<RestconfError> globalErrors;
+
+    public PATCHStatusContext(final String patchId, final List<PATCHStatusEntity> editCollection,
+                              final boolean ok, final List<RestconfError> globalErrors) {
+        this.patchId = patchId;
+        this.editCollection = editCollection;
+        this.ok = ok;
+        this.globalErrors = globalErrors;
+    }
+
+    public String getPatchId() {
+        return patchId;
+    }
+
+    public List<PATCHStatusEntity> getEditCollection() {
+        return editCollection;
+    }
+
+    public boolean isOk() {
+        return ok;
+    }
+
+    public List<RestconfError> getGlobalErrors() {
+        return globalErrors;
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHStatusEntity.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PATCHStatusEntity.java
new file mode 100644 (file)
index 0000000..f232907
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.sal.restconf.impl;
+
+import java.util.List;
+
+public class PATCHStatusEntity {
+
+    private final String editId;
+    private final List<RestconfError> editErrors;
+    private final boolean ok;
+
+    public PATCHStatusEntity(final String editId, final boolean ok, final List<RestconfError> editErrors) {
+        this.editId = editId;
+        this.ok = ok;
+        this.editErrors = editErrors;
+    }
+
+    public String getEditId() {
+        return editId;
+    }
+
+    public boolean isOk() {
+        return ok;
+    }
+
+    public List<RestconfError> getEditErrors() {
+        return editErrors;
+    }
+}
index 42057463d6ecf78d62d092713a0da3cd20662ed1..fd39da65ba574849975b094c39b068e3b7f718c3 100644 (file)
@@ -1,4 +1,4 @@
-/**
+/*
  * Copyright (c) 2014, 2015 Brocade Communication Systems, Inc., Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
@@ -36,6 +36,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.ResponseBuilder;
 import javax.ws.rs.core.Response.Status;
@@ -981,6 +982,22 @@ public class RestconfImpl implements RestconfService {
         return Response.status(Status.OK).location(uriToWebsocketServer).build();
     }
 
+    @Override
+    public PATCHStatusContext patchConfigurationData(String identifier, PATCHContext context, UriInfo uriInfo) {
+        if (context == null) {
+            throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+        }
+        return broker.patchConfigurationDataWithinTransaction(context, controllerContext.getGlobalSchema());
+    }
+
+    @Override
+    public PATCHStatusContext patchConfigurationData(PATCHContext context, @Context UriInfo uriInfo) {
+        if (context == null) {
+            throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+        }
+        return broker.patchConfigurationDataWithinTransaction(context, controllerContext.getGlobalSchema());
+    }
+
     /**
      * Load parameter for subscribing to stream from input composite node
      *
index 808a4afde6db7a9aac83b4973c01e639d6eb9301..75af8fe32042f9ac49a7567ac8adcf47315fd959 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.netconf.sal.restconf.impl;
 
 import java.math.BigInteger;
 import java.util.concurrent.atomic.AtomicLong;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
@@ -211,6 +212,17 @@ public class StatisticsRestconfServiceWrapper implements RestconfService {
         return delegate.getAvailableStreams(uriInfo);
     }
 
+    @Override
+    public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext payload, final UriInfo
+            uriInfo) {
+        return delegate.patchConfigurationData(identifier, payload, uriInfo);
+    }
+
+    @Override
+    public PATCHStatusContext patchConfigurationData(final PATCHContext payload, final UriInfo uriInfo) {
+        return delegate.patchConfigurationData(payload, uriInfo);
+    }
+
     public BigInteger getConfigDelete() {
         return BigInteger.valueOf(configDelete.get());
     }
diff --git a/restconf/sal-rest-connector/src/main/yang/instance-identifier-patch-module.yang b/restconf/sal-rest-connector/src/main/yang/instance-identifier-patch-module.yang
new file mode 100644 (file)
index 0000000..1eb39fe
--- /dev/null
@@ -0,0 +1,50 @@
+module instance-identifier-patch-module {
+  namespace "instance:identifier:patch:module";
+
+  prefix "iipmodule";
+  revision 2015-11-21 {
+  }
+
+  container patch-cont {
+    container patch-cont2 {
+        leaf cont-leaf {
+            type string;
+        }
+    }
+
+    list my-list1 {
+
+        description "PATCH /restconf/config/instance-identifier-patch-module:patch-cont/my-list1/leaf1";
+
+        key name;
+
+        leaf name {
+            type string;
+        }
+
+        leaf my-leaf11 {
+            type string;
+        }
+
+        leaf my-leaf12 {
+            type string;
+        }
+
+        list my-list2 {
+            key name;
+
+            leaf name {
+                type string;
+            }
+
+            leaf my-leaf21 {
+                type string;
+            }
+
+            leaf my-leaf22 {
+                type string;
+            }
+        }
+    }
+  }
+}
\ No newline at end of file
index 9dea0b95cd0a517b45f5ad50a6a11c4a9b0769e8..029c3c9bf7487c375beca84f60a4f79a98f4936e 100644 (file)
@@ -1,4 +1,4 @@
-/**
+/*
  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
index f9a31955a59986ec533ce69ab18fb13bb1a7205c..6a728dc40c3055ceac51328eb4d40a68a886f54f 100644 (file)
@@ -1,4 +1,4 @@
-/**
+/*
  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
@@ -26,6 +26,7 @@ import org.opendaylight.netconf.sal.rest.api.RestconfConstants;
 import org.opendaylight.netconf.sal.rest.impl.AbstractIdentifierAwareJaxRsProvider;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
@@ -100,4 +101,11 @@ public abstract class AbstractBodyReaderTest {
                 .getSchemaContext());
         assertNotNull(nnContext.getInstanceIdentifierContext().getSchemaNode());
     }
+
+    protected static void checkPATCHContext(final PATCHContext patchContext) {
+        assertNotNull(patchContext.getData());
+        assertNotNull(patchContext.getInstanceIdentifierContext().getInstanceIdentifier());
+        assertNotNull(patchContext.getInstanceIdentifierContext().getSchemaContext());
+        assertNotNull(patchContext.getInstanceIdentifierContext().getSchemaNode());
+    }
 }
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonPATCHBodyReader.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonPATCHBodyReader.java
new file mode 100644 (file)
index 0000000..bcceb07
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.rest.impl.test.providers;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import java.io.InputStream;
+import javax.ws.rs.core.MediaType;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.netconf.sal.rest.impl.JsonToPATCHBodyReader;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class TestJsonPATCHBodyReader extends AbstractBodyReaderTest {
+
+    private final JsonToPATCHBodyReader jsonPATCHBodyReader;
+    private static SchemaContext schemaContext;
+
+    public TestJsonPATCHBodyReader() throws NoSuchFieldException, SecurityException {
+        super();
+        jsonPATCHBodyReader = new JsonToPATCHBodyReader();
+    }
+
+    @Override
+    protected MediaType getMediaType() {
+        return new MediaType(APPLICATION_JSON, null);
+    }
+
+    @BeforeClass
+    public static void initialization() {
+        schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+        controllerContext.setSchemas(schemaContext);
+    }
+
+    @Test
+    public void modulePATCHDataTest() throws Exception {
+        final String uri = "instance-identifier-patch-module:patch-cont/my-list1/leaf1";
+        mockBodyReader(uri, jsonPATCHBodyReader, false);
+
+        final InputStream inputStream = TestJsonBodyReader.class
+                .getResourceAsStream("/instanceidentifier/json/jsonPATCHdata.json");
+
+        final PATCHContext returnValue = jsonPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContext(returnValue);
+    }
+}
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlPATCHBodyReader.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlPATCHBodyReader.java
new file mode 100644 (file)
index 0000000..9b98dd8
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.rest.impl.test.providers;
+
+import java.io.InputStream;
+import javax.ws.rs.core.MediaType;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.netconf.sal.rest.impl.XmlToPATCHBodyReader;
+import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class TestXmlPATCHBodyReader extends AbstractBodyReaderTest {
+
+    private final XmlToPATCHBodyReader xmlPATCHBodyReader;
+    private static SchemaContext schemaContext;
+
+    public TestXmlPATCHBodyReader() throws NoSuchFieldException, SecurityException {
+        super();
+        xmlPATCHBodyReader = new XmlToPATCHBodyReader();
+    }
+
+    @Override
+    protected MediaType getMediaType() {
+        return new MediaType(MediaType.APPLICATION_XML, null);
+    }
+
+    @BeforeClass
+    public static void initialization() throws NoSuchFieldException, SecurityException {
+        schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+        controllerContext.setSchemas(schemaContext);
+    }
+
+    @Test
+    public void moduleDataTest() throws Exception {
+        final String uri = "instance-identifier-patch-module:patch-cont/my-list1/leaf1";
+        mockBodyReader(uri, xmlPATCHBodyReader, false);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdata.xml");
+        final PATCHContext returnValue = xmlPATCHBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+        checkPATCHContext(returnValue);
+    }
+}
diff --git a/restconf/sal-rest-connector/src/test/resources/instanceidentifier/json/jsonPATCHdata.json b/restconf/sal-rest-connector/src/test/resources/instanceidentifier/json/jsonPATCHdata.json
new file mode 100644 (file)
index 0000000..cf1530e
--- /dev/null
@@ -0,0 +1,34 @@
+{
+  "ietf-yang-patch:yang-patch" : {
+
+    "patch-id" : "test-patch",
+    "comment" : "this is test patch",
+    "edit" : [
+      {
+        "edit-id": "edit1",
+        "operation": "create",
+        "target": "/my-list2",
+        "value": {
+          "my-list2": {
+            "name": "my-leaf20",
+            "my-leaf21": "I am leaf21-0",
+            "my-leaf22": "I am leaf22-0"
+          }
+        }
+      },
+
+      {
+        "edit-id": "edit2",
+        "operation": "create",
+        "target": "/my-list2",
+        "value": {
+          "my-list2": {
+            "name": "my-leaf21",
+            "my-leaf21": "I am leaf21-1",
+            "my-leaf22": "I am leaf22-1"
+          }
+        }
+      }
+    ]
+  }
+}
diff --git a/restconf/sal-rest-connector/src/test/resources/instanceidentifier/xml/xmlPATCHdata.xml b/restconf/sal-rest-connector/src/test/resources/instanceidentifier/xml/xmlPATCHdata.xml
new file mode 100644 (file)
index 0000000..d7d3a6b
--- /dev/null
@@ -0,0 +1,28 @@
+<yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
+    <patch-id>test-patch</patch-id>
+    <comment>this is test patch</comment>
+    <edit>
+        <edit-id>edit1</edit-id>
+        <operation>create</operation>
+        <target>/my-list2</target>
+        <value>
+            <my-list2 xmlns="instance:identifier:patch:module">
+                <name>my-leaf20</name>
+                <my-leaf21>I am leaf21-0</my-leaf21>
+                <my-leaf22>I am leaf22-0</my-leaf22>
+            </my-list2>
+        </value>
+    </edit>
+    <edit>
+        <edit-id>edit2</edit-id>
+        <operation>create</operation>
+        <target>/my-list2</target>
+        <value>
+            <my-list2 xmlns="instance:identifier:patch:module">
+                <name>my-leaf21</name>
+                <my-leaf21>I am leaf21-1</my-leaf21>
+                <my-leaf22>I am leaf22-1</my-leaf22>
+            </my-list2>
+        </value>
+    </edit>
+</yang-patch>
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/test/resources/instanceidentifier/yang/instance-identifier-patch-module.yang b/restconf/sal-rest-connector/src/test/resources/instanceidentifier/yang/instance-identifier-patch-module.yang
new file mode 100644 (file)
index 0000000..11b4684
--- /dev/null
@@ -0,0 +1,44 @@
+module instance-identifier-patch-module {
+  namespace "instance:identifier:patch:module";
+
+  prefix "iipmodule";
+  revision 2015-11-21 {
+  }
+
+  container patch-cont {
+    list my-list1 {
+
+        description "PATCH /restconf/config/instance-identifier-patch-module:patch-cont/my-list1/leaf1";
+
+        key name;
+
+        leaf name {
+            type string;
+        }
+
+        leaf my-leaf11 {
+            type string;
+        }
+
+        leaf my-leaf12 {
+            type string;
+        }
+
+        list my-list2 {
+            key name;
+
+            leaf name {
+                type string;
+            }
+
+            leaf my-leaf21 {
+                type string;
+            }
+
+            leaf my-leaf22 {
+                type string;
+            }
+        }
+    }
+  }
+}
\ No newline at end of file
index 1726e2aad0a32a3e637cfe09ec715ac576e272b5..b20aa967b9e66f38a37cb1ee59e353a51190c15d 100644 (file)
@@ -90,25 +90,19 @@ public class MountPointSwagger extends BaseYangSwaggerGenerator implements Mount
             builder.append(moduleName);
             builder.append(':');
         }
-        boolean first = true;
         for (PathArgument arg : key.getPathArguments()) {
-
             String name = arg.getNodeType().getLocalName();
-            if (first) {
-                first = false;
-            } else {
-                builder.append('/');
-            }
-            builder.append(name);
             if (arg instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) {
                 NodeIdentifierWithPredicates nodeId = (NodeIdentifierWithPredicates) arg;
                 for (Entry<QName, Object> entry : nodeId.getKeyValues().entrySet()) {
-                    builder.append('/').append(entry.getValue());
+                    builder.append(entry.getValue()).append('/');
                 }
+            } else {
+                builder.append(name);
+                builder.append('/');
             }
         }
-
-        return builder.append('/').toString();
+        return builder.toString();
     }
 
     private String getYangMountUrl(final YangInstanceIdentifier key) {
index 7b3344348ae96f38ba2ecca2df7d87157974d44a..881f9e95657e0ae3df0fc9a0a80a813b62c7334c 100644 (file)
@@ -42,6 +42,7 @@ public class MountPointSwaggerTest {
     private static final String HTTP_URL = "http://localhost/path";
     private static final YangInstanceIdentifier instanceId = YangInstanceIdentifier.builder()
             .node(QName.create("nodes"))
+            .node(QName.create("node"))
             .nodeWithKey(QName.create("node"), QName.create("id"), "123").build();
     private static final String INSTANCE_URL = "nodes/node/123/";
     private MountPointSwagger swagger;