Merge "BUG-758 Eliminate xtend from sal-netconf-connector"
authorTony Tkacik <ttkacik@cisco.com>
Wed, 23 Apr 2014 08:14:25 +0000 (08:14 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 23 Apr 2014 08:14:25 +0000 (08:14 +0000)
opendaylight/md-sal/sal-netconf-connector/pom.xml
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.xtend [deleted file]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.xtend [deleted file]

index 9b701203ce44ee29c86ddd83a9b72a6a3ed3218b..27d320f03e656587bfabfc4b6ad95d369e55cd99 100644 (file)
       <groupId>${project.groupId}</groupId>
       <artifactId>sal-connector-api</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.eclipse.xtend</groupId>
-      <artifactId>org.eclipse.xtend.lib</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>ietf-netconf-monitoring</artifactId>
         </configuration>
       </plugin>
 
-      <plugin>
-        <groupId>org.eclipse.xtend</groupId>
-        <artifactId>xtend-maven-plugin</artifactId>
-      </plugin>
-
       <plugin>
         <groupId>org.opendaylight.yangtools</groupId>
         <artifactId>yang-maven-plugin</artifactId>
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java
new file mode 100644 (file)
index 0000000..abbbb68
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ * 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.controller.sal.connect.netconf;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.INVENTORY_CONNECTED;
+import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.INVENTORY_ID;
+import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.INVENTORY_NODE;
+import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.INVENTORY_PATH;
+import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.NETCONF_INVENTORY_INITIAL_CAPABILITY;
+import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.CONFIG_SOURCE_RUNNING;
+import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_DATA_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_GET_CONFIG_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_GET_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.toFilterStructure;
+import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.toRpcMessage;
+import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.wrap;
+
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
+import org.opendaylight.protocol.framework.ReconnectStrategy;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangSourceContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.ListenableFuture;
+import io.netty.util.concurrent.EventExecutor;
+
+public class NetconfDevice implements Provider, //
+        DataReader<InstanceIdentifier, CompositeNode>, //
+        DataCommitHandler<InstanceIdentifier, CompositeNode>, //
+        RpcImplementation, //
+        AutoCloseable {
+
+    InetSocketAddress socketAddress;
+
+    MountProvisionInstance mountInstance;
+
+    EventExecutor eventExecutor;
+
+    ExecutorService processingExecutor;
+
+    InstanceIdentifier path;
+
+    ReconnectStrategy reconnectStrategy;
+
+    AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider;
+
+    private NetconfDeviceSchemaContextProvider deviceContextProvider;
+
+    protected Logger logger;
+
+    Registration<DataReader<InstanceIdentifier, CompositeNode>> operReaderReg;
+    Registration<DataReader<InstanceIdentifier, CompositeNode>> confReaderReg;
+    Registration<DataCommitHandler<InstanceIdentifier, CompositeNode>> commitHandlerReg;
+    List<RpcRegistration> rpcReg;
+
+    String name;
+
+    MountProvisionService mountService;
+
+    NetconfClientDispatcher dispatcher;
+
+    static InstanceIdentifier ROOT_PATH = InstanceIdentifier.builder().toInstance();
+
+    SchemaSourceProvider<InputStream> remoteSourceProvider;
+
+    DataBrokerService dataBroker;
+
+    NetconfDeviceListener listener;
+
+    public NetconfDevice(String name) {
+        this.name = name;
+        this.logger = LoggerFactory.getLogger(NetconfDevice.class + "#" + name);
+        this.path = InstanceIdentifier.builder(INVENTORY_PATH)
+                .nodeWithKey(INVENTORY_NODE, Collections.<QName, Object>singletonMap(INVENTORY_ID, name)).toInstance();
+    }
+
+    public void start() {
+        checkState(dispatcher != null, "Dispatcher must be set.");
+        checkState(schemaSourceProvider != null, "Schema Source Provider must be set.");
+        checkState(eventExecutor != null, "Event executor must be set.");
+
+        listener = new NetconfDeviceListener(this);
+
+        logger.info("Starting NETCONF Client {} for address {}", name, socketAddress);
+
+        dispatcher.createClient(socketAddress, listener, reconnectStrategy);
+    }
+
+    Optional<SchemaContext> getSchemaContext() {
+        if (deviceContextProvider == null) {
+            return Optional.absent();
+        }
+        return deviceContextProvider.currentContext;
+    }
+
+    void bringDown() {
+        if (rpcReg != null) {
+            for (RpcRegistration reg : rpcReg) {
+                reg.close();
+            }
+            rpcReg = null;
+        }
+        closeGracefully(confReaderReg);
+        confReaderReg = null;
+        closeGracefully(operReaderReg);
+        operReaderReg = null;
+        closeGracefully(commitHandlerReg);
+        commitHandlerReg = null;
+
+        updateDeviceState(false, Collections.<QName> emptySet());
+    }
+
+    private void closeGracefully(final AutoCloseable resource) {
+        if (resource != null) {
+            try {
+                resource.close();
+            } catch (Exception e) {
+                logger.warn("Ignoring exception while closing {}", resource, e);
+            }
+        }
+    }
+
+    void bringUp(SchemaSourceProvider<String> delegate, Set<QName> capabilities) {
+        remoteSourceProvider = schemaSourceProvider.createInstanceFor(delegate);
+        deviceContextProvider = new NetconfDeviceSchemaContextProvider(this, remoteSourceProvider);
+        deviceContextProvider.createContextFromCapabilities(capabilities);
+        if (mountInstance != null && getSchemaContext().isPresent()) {
+            mountInstance.setSchemaContext(getSchemaContext().get());
+        }
+
+        updateDeviceState(true, capabilities);
+
+        if (mountInstance != null) {
+            confReaderReg = mountInstance.registerConfigurationReader(ROOT_PATH, this);
+            operReaderReg = mountInstance.registerOperationalReader(ROOT_PATH, this);
+            commitHandlerReg = mountInstance.registerCommitHandler(ROOT_PATH, this);
+
+            List<RpcRegistration> rpcs = new ArrayList<>();
+            // TODO same condition twice
+            if (mountInstance != null && getSchemaContext().isPresent()) {
+                for (RpcDefinition rpc : mountInstance.getSchemaContext().getOperations()) {
+                    rpcs.add(mountInstance.addRpcImplementation(rpc.getQName(), this));
+                }
+            }
+            rpcReg = rpcs;
+        }
+    }
+
+    private void updateDeviceState(boolean up, Set<QName> capabilities) {
+        DataModificationTransaction transaction = dataBroker.beginTransaction();
+
+        CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder();
+        it.setQName(INVENTORY_NODE);
+        it.addLeaf(INVENTORY_ID, name);
+        it.addLeaf(INVENTORY_CONNECTED, up);
+
+        logger.debug("Client capabilities {}", capabilities);
+        for (QName capability : capabilities) {
+            it.addLeaf(NETCONF_INVENTORY_INITIAL_CAPABILITY, capability);
+        }
+
+        logger.debug("Update device state transaction " + transaction.getIdentifier()
+                + " putting operational data started.");
+        transaction.removeOperationalData(path);
+        transaction.putOperationalData(path, it.toInstance());
+        logger.debug("Update device state transaction " + transaction.getIdentifier()
+                + " putting operational data ended.");
+
+        // FIXME: this has to be asynchronous
+        RpcResult<TransactionStatus> transactionStatus = null;
+        try {
+            transactionStatus = transaction.commit().get();
+        } catch (InterruptedException e) {
+            throw new RuntimeException("Interrupted while waiting for response", e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException("Read configuration data " + path + " failed", e);
+        }
+        // TODO better ex handling
+
+        if (transactionStatus.isSuccessful()) {
+            logger.debug("Update device state transaction " + transaction.getIdentifier() + " SUCCESSFUL.");
+        } else {
+            logger.debug("Update device state transaction " + transaction.getIdentifier() + " FAILED!");
+            logger.debug("Update device state transaction status " + transaction.getStatus());
+        }
+    }
+
+    @Override
+    public CompositeNode readConfigurationData(InstanceIdentifier path) {
+        RpcResult<CompositeNode> result = null;
+        try {
+            result = this.invokeRpc(NETCONF_GET_CONFIG_QNAME,
+                    wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, toFilterStructure(path))).get();
+        } catch (InterruptedException e) {
+            throw new RuntimeException("Interrupted while waiting for response", e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException("Read configuration data " + path + " failed", e);
+        }
+
+        CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
+        return data == null ? null : (CompositeNode) findNode(data, path);
+    }
+
+    @Override
+    public CompositeNode readOperationalData(InstanceIdentifier path) {
+        RpcResult<CompositeNode> result = null;
+        try {
+            result = invokeRpc(NETCONF_GET_QNAME, wrap(NETCONF_GET_QNAME, toFilterStructure(path))).get();
+        } catch (InterruptedException e) {
+            throw new RuntimeException("Interrupted while waiting for response", e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException("Read configuration data " + path + " failed", e);
+        }
+
+        CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
+        return (CompositeNode) findNode(data, path);
+    }
+
+    @Override
+    public Set<QName> getSupportedRpcs() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, CompositeNode input) {
+        return listener.sendRequest(toRpcMessage(rpc, input, getSchemaContext()));
+    }
+
+    @Override
+    public Collection<ProviderFunctionality> getProviderFunctionality() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public void onSessionInitiated(ProviderSession session) {
+        dataBroker = session.getService(DataBrokerService.class);
+
+        DataModificationTransaction transaction = dataBroker.beginTransaction();
+        if (operationalNodeNotExisting(transaction)) {
+            transaction.putOperationalData(path, getNodeWithId());
+        }
+        if (configurationNodeNotExisting(transaction)) {
+            transaction.putConfigurationData(path, getNodeWithId());
+        }
+
+        try {
+            transaction.commit().get();
+        } catch (InterruptedException e) {
+            throw new RuntimeException("Interrupted while waiting for response", e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException("Read configuration data " + path + " failed", e);
+        }
+
+        mountService = session.getService(MountProvisionService.class);
+        if (mountService != null) {
+            mountInstance = mountService.createOrGetMountPoint(path);
+        }
+    }
+
+    CompositeNode getNodeWithId() {
+        SimpleNodeTOImpl id = new SimpleNodeTOImpl(INVENTORY_ID, null, name);
+        return new CompositeNodeTOImpl(INVENTORY_NODE, null, Collections.<Node<?>> singletonList(id));
+    }
+
+    boolean configurationNodeNotExisting(DataModificationTransaction transaction) {
+        return null == transaction.readConfigurationData(path);
+    }
+
+    boolean operationalNodeNotExisting(DataModificationTransaction transaction) {
+        return null == transaction.readOperationalData(path);
+    }
+
+    static Node<?> findNode(CompositeNode node, InstanceIdentifier identifier) {
+
+        Node<?> current = node;
+        for (InstanceIdentifier.PathArgument arg : identifier.getPath()) {
+            if (current instanceof SimpleNode<?>) {
+                return null;
+            } else if (current instanceof CompositeNode) {
+                CompositeNode currentComposite = (CompositeNode) current;
+
+                current = currentComposite.getFirstCompositeByName(arg.getNodeType());
+                if (current == null) {
+                    current = currentComposite.getFirstCompositeByName(arg.getNodeType().withoutRevision());
+                }
+                if (current == null) {
+                    current = currentComposite.getFirstSimpleByName(arg.getNodeType());
+                }
+                if (current == null) {
+                    current = currentComposite.getFirstSimpleByName(arg.getNodeType().withoutRevision());
+                }
+                if (current == null) {
+                    return null;
+                }
+            }
+        }
+        return current;
+    }
+
+    @Override
+    public DataCommitTransaction<InstanceIdentifier, CompositeNode> requestCommit(
+            DataModification<InstanceIdentifier, CompositeNode> modification) {
+        NetconfDeviceTwoPhaseCommitTransaction twoPhaseCommit = new NetconfDeviceTwoPhaseCommitTransaction(this,
+                modification, true);
+        try {
+            twoPhaseCommit.prepare();
+        } catch (InterruptedException e) {
+            throw new RuntimeException("Interrupted while waiting for response", e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException("Read configuration data " + path + " failed", e);
+        }
+         return twoPhaseCommit;
+    }
+
+    Set<QName> getCapabilities(Collection<String> capabilities) {
+        return FluentIterable.from(capabilities).filter(new Predicate<String>() {
+            @Override
+            public boolean apply(final String capability) {
+                return capability.contains("?") && capability.contains("module=") && capability.contains("revision=");
+            }
+        }).transform(new Function<String, QName>() {
+            @Override
+            public QName apply(final String capability) {
+                String[] parts = capability.split("\\?");
+                String namespace = parts[0];
+                FluentIterable<String> queryParams = FluentIterable.from(Arrays.asList(parts[1].split("&")));
+
+                String revision = getStringAndTransform(queryParams, "revision=", "revision=");
+
+                String moduleName = getStringAndTransform(queryParams, "module=", "module=");
+
+                if (revision == null) {
+                    logger.warn("Netconf device was not reporting revision correctly, trying to get amp;revision=");
+                    revision = getStringAndTransform(queryParams, "amp;revision==", "revision=");
+
+                    if (revision != null) {
+                        logger.warn("Netconf device returned revision incorectly escaped for {}", capability);
+                    }
+                }
+                if (revision == null) {
+                    return QName.create(URI.create(namespace), null, moduleName);
+                }
+                return QName.create(namespace, revision, moduleName);
+            }
+
+            private String getStringAndTransform(final Iterable<String> queryParams, final String match,
+                    final String substringToRemove) {
+                Optional<String> found = Iterables.tryFind(queryParams, new Predicate<String>() {
+                    @Override
+                    public boolean apply(final String input) {
+                        return input.startsWith(match);
+                    }
+                });
+
+                return found.isPresent() ? found.get().replaceAll(substringToRemove, "") : null;
+            }
+
+        }).toSet();
+    }
+
+    @Override
+    public void close() {
+        bringDown();
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public InetSocketAddress getSocketAddress() {
+        return socketAddress;
+    }
+
+    public MountProvisionInstance getMountInstance() {
+        return mountInstance;
+    }
+
+    public void setReconnectStrategy(final ReconnectStrategy reconnectStrategy) {
+        this.reconnectStrategy = reconnectStrategy;
+    }
+
+    public void setProcessingExecutor(final ExecutorService processingExecutor) {
+        this.processingExecutor = processingExecutor;
+    }
+
+    public void setSocketAddress(final InetSocketAddress socketAddress) {
+        this.socketAddress = socketAddress;
+    }
+
+    public void setEventExecutor(final EventExecutor eventExecutor) {
+        this.eventExecutor = eventExecutor;
+    }
+
+    public void setSchemaSourceProvider(final AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider) {
+        this.schemaSourceProvider = schemaSourceProvider;
+    }
+
+    public void setDispatcher(final NetconfClientDispatcher dispatcher) {
+        this.dispatcher = dispatcher;
+    }
+}
+
+class NetconfDeviceSchemaContextProvider {
+
+    NetconfDevice device;
+
+    SchemaSourceProvider<InputStream> sourceProvider;
+
+    Optional<SchemaContext> currentContext;
+
+    NetconfDeviceSchemaContextProvider(NetconfDevice device, SchemaSourceProvider<InputStream> sourceProvider) {
+        this.device = device;
+        this.sourceProvider = sourceProvider;
+        this.currentContext = Optional.absent();
+    }
+
+    void createContextFromCapabilities(Iterable<QName> capabilities) {
+        YangSourceContext sourceContext = YangSourceContext.createFrom(capabilities, sourceProvider);
+        if (!sourceContext.getMissingSources().isEmpty()) {
+            device.logger.warn("Sources for following models are missing {}", sourceContext.getMissingSources());
+        }
+        device.logger.debug("Trying to create schema context from {}", sourceContext.getValidSources());
+        List<InputStream> modelsToParse = YangSourceContext.getValidInputStreams(sourceContext);
+        if (!sourceContext.getValidSources().isEmpty()) {
+            SchemaContext schemaContext = tryToCreateContext(modelsToParse);
+            currentContext = Optional.fromNullable(schemaContext);
+        } else {
+            currentContext = Optional.absent();
+        }
+        if (currentContext.isPresent()) {
+            device.logger.debug("Schema context successfully created.");
+        }
+    }
+
+    SchemaContext tryToCreateContext(List<InputStream> modelsToParse) {
+        YangParserImpl parser = new YangParserImpl();
+        try {
+
+            Set<Module> models = parser.parseYangModelsFromStreams(modelsToParse);
+            return parser.resolveSchemaContext(models);
+        } catch (Exception e) {
+            device.logger.debug("Error occured during parsing YANG schemas", e);
+            return null;
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.xtend b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.xtend
deleted file mode 100644 (file)
index 0b88c66..0000000
+++ /dev/null
@@ -1,368 +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.controller.sal.connect.netconf
-
-import com.google.common.base.Optional
-import com.google.common.collect.FluentIterable
-import io.netty.util.concurrent.EventExecutor
-import java.io.InputStream
-import java.net.InetSocketAddress
-import java.net.URI
-import java.util.ArrayList
-import java.util.Collection
-import java.util.Collections
-import java.util.List
-import java.util.Set
-import java.util.concurrent.ExecutorService
-import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler
-import org.opendaylight.controller.md.sal.common.api.data.DataModification
-import org.opendaylight.controller.md.sal.common.api.data.DataReader
-import org.opendaylight.controller.netconf.client.NetconfClientDispatcher
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession
-import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration
-import org.opendaylight.controller.sal.core.api.Provider
-import org.opendaylight.controller.sal.core.api.RpcImplementation
-import org.opendaylight.controller.sal.core.api.data.DataBrokerService
-import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionService
-import org.opendaylight.protocol.framework.ReconnectStrategy
-import org.opendaylight.yangtools.concepts.Registration
-import org.opendaylight.yangtools.yang.common.QName
-import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
-import org.opendaylight.yangtools.yang.data.api.Node
-import org.opendaylight.yangtools.yang.data.api.SimpleNode
-import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
-import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode
-import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
-import org.opendaylight.yangtools.yang.model.api.SchemaContext
-import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider
-import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl
-import org.opendaylight.yangtools.yang.parser.impl.util.YangSourceContext
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-
-import static com.google.common.base.Preconditions.*
-import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.*
-
-import static extension org.opendaylight.controller.sal.connect.netconf.NetconfMapping.*
-
-class NetconfDevice implements Provider, //
-DataReader<InstanceIdentifier, CompositeNode>, //
-DataCommitHandler<InstanceIdentifier, CompositeNode>, //
-RpcImplementation, //
-AutoCloseable {
-
-    @Property
-    var InetSocketAddress socketAddress;
-
-    @Property
-    var MountProvisionInstance mountInstance;
-
-    @Property
-    var EventExecutor eventExecutor;
-
-    @Property
-    var ExecutorService processingExecutor;
-
-    @Property
-    var InstanceIdentifier path;
-
-    @Property
-    var ReconnectStrategy reconnectStrategy;
-
-    @Property
-    var AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider;
-
-    @Property
-    private NetconfDeviceSchemaContextProvider deviceContextProvider
-
-    protected val Logger logger
-
-    Registration<DataReader<InstanceIdentifier, CompositeNode>> operReaderReg
-    Registration<DataReader<InstanceIdentifier, CompositeNode>> confReaderReg
-    Registration<DataCommitHandler<InstanceIdentifier, CompositeNode>> commitHandlerReg
-    List<RpcRegistration> rpcReg
-
-    @Property
-    val String name
-
-    MountProvisionService mountService
-
-    @Property
-    var NetconfClientDispatcher dispatcher
-
-    static val InstanceIdentifier ROOT_PATH = InstanceIdentifier.builder().toInstance();
-
-    @Property
-    var SchemaSourceProvider<InputStream> remoteSourceProvider
-
-    DataBrokerService dataBroker
-
-    var NetconfDeviceListener listener;
-
-    public new(String name) {
-        this._name = name;
-        this.logger = LoggerFactory.getLogger(NetconfDevice.name + "#" + name);
-        this.path = InstanceIdentifier.builder(INVENTORY_PATH).nodeWithKey(INVENTORY_NODE,
-            Collections.singletonMap(INVENTORY_ID, name)).toInstance;
-    }
-
-    def start() {
-        checkState(dispatcher != null, "Dispatcher must be set.");
-        checkState(schemaSourceProvider != null, "Schema Source Provider must be set.")
-        checkState(eventExecutor != null, "Event executor must be set.");
-
-        listener = new NetconfDeviceListener(this);
-
-        logger.info("Starting NETCONF Client {} for address {}", name, socketAddress);
-
-        dispatcher.createClient(socketAddress, listener, reconnectStrategy);
-    }
-
-    def Optional<SchemaContext> getSchemaContext() {
-        if (deviceContextProvider == null) {
-            return Optional.absent();
-        }
-        return deviceContextProvider.currentContext;
-    }
-
-    def bringDown() {
-        if (rpcReg != null) {
-            for (reg : rpcReg) {
-                reg.close()
-            }
-            rpcReg = null
-        }
-        confReaderReg?.close()
-        confReaderReg = null
-        operReaderReg?.close()
-        operReaderReg = null
-        commitHandlerReg?.close()
-        commitHandlerReg = null
-
-        updateDeviceState(false, Collections.emptySet())
-    }
-
-    def bringUp(SchemaSourceProvider<String> delegate, Set<QName> capabilities) {
-        remoteSourceProvider = schemaSourceProvider.createInstanceFor(delegate);
-        deviceContextProvider = new NetconfDeviceSchemaContextProvider(this, remoteSourceProvider);
-        deviceContextProvider.createContextFromCapabilities(capabilities);
-        if (mountInstance != null && schemaContext.isPresent) {
-            mountInstance.schemaContext = schemaContext.get();
-        }
-
-        updateDeviceState(true, capabilities)
-
-        if (mountInstance != null) {
-            confReaderReg = mountInstance.registerConfigurationReader(ROOT_PATH, this);
-            operReaderReg = mountInstance.registerOperationalReader(ROOT_PATH, this);
-            commitHandlerReg = mountInstance.registerCommitHandler(ROOT_PATH, this);
-
-            val rpcs = new ArrayList<RpcRegistration>();
-            if (mountInstance != null && schemaContext.isPresent) {
-                for (rpc : mountInstance.schemaContext.operations) {
-                    rpcs.add(mountInstance.addRpcImplementation(rpc.QName, this));
-                }
-            }
-            rpcReg = rpcs
-        }
-    }
-
-    private def updateDeviceState(boolean up, Set<QName> capabilities) {
-        val transaction = dataBroker.beginTransaction
-
-        val it = ImmutableCompositeNode.builder
-        setQName(INVENTORY_NODE)
-        addLeaf(INVENTORY_ID, name)
-        addLeaf(INVENTORY_CONNECTED, up)
-
-        logger.debug("Client capabilities {}", capabilities)
-        for (capability : capabilities) {
-            addLeaf(NETCONF_INVENTORY_INITIAL_CAPABILITY, capability)
-        }
-
-        logger.debug("Update device state transaction " + transaction.identifier + " putting operational data started.")
-        transaction.removeOperationalData(path)
-        transaction.putOperationalData(path, it.toInstance)
-        logger.debug("Update device state transaction " + transaction.identifier + " putting operational data ended.")
-
-        // FIXME: this has to be asynchronous
-        val transactionStatus = transaction.commit.get;
-
-        if (transactionStatus.successful) {
-            logger.debug("Update device state transaction " + transaction.identifier + " SUCCESSFUL.")
-        } else {
-            logger.debug("Update device state transaction " + transaction.identifier + " FAILED!")
-            logger.debug("Update device state transaction status " + transaction.status)
-        }
-    }
-
-    override readConfigurationData(InstanceIdentifier path) {
-        val result = invokeRpc(NETCONF_GET_CONFIG_QNAME,
-            wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, path.toFilterStructure())).get();
-        val data = result.result.getFirstCompositeByName(NETCONF_DATA_QNAME);
-        return data?.findNode(path) as CompositeNode;
-    }
-
-    override readOperationalData(InstanceIdentifier path) {
-        val result = invokeRpc(NETCONF_GET_QNAME, wrap(NETCONF_GET_QNAME, path.toFilterStructure())).get();
-        val data = result.result.getFirstCompositeByName(NETCONF_DATA_QNAME);
-        return data?.findNode(path) as CompositeNode;
-    }
-
-    override getSupportedRpcs() {
-        Collections.emptySet;
-    }
-
-    override invokeRpc(QName rpc, CompositeNode input) {
-        return listener.sendRequest(rpc.toRpcMessage(input,schemaContext));
-    }
-
-    override getProviderFunctionality() {
-        Collections.emptySet
-    }
-
-    override onSessionInitiated(ProviderSession session) {
-        dataBroker = session.getService(DataBrokerService);
-
-        val transaction = dataBroker.beginTransaction
-        if (transaction.operationalNodeNotExisting) {
-            transaction.putOperationalData(path, nodeWithId)
-        }
-        if (transaction.configurationNodeNotExisting) {
-            transaction.putConfigurationData(path, nodeWithId)
-        }
-        transaction.commit().get();
-        mountService = session.getService(MountProvisionService);
-        mountInstance = mountService?.createOrGetMountPoint(path);
-    }
-
-    def getNodeWithId() {
-        val id = new SimpleNodeTOImpl(INVENTORY_ID, null, name);
-        return new CompositeNodeTOImpl(INVENTORY_NODE, null, Collections.singletonList(id));
-    }
-
-    def boolean configurationNodeNotExisting(DataModificationTransaction transaction) {
-        return null === transaction.readConfigurationData(path);
-    }
-
-    def boolean operationalNodeNotExisting(DataModificationTransaction transaction) {
-        return null === transaction.readOperationalData(path);
-    }
-
-    static def Node<?> findNode(CompositeNode node, InstanceIdentifier identifier) {
-
-        var Node<?> current = node;
-        for (arg : identifier.path) {
-            if (current instanceof SimpleNode<?>) {
-                return null;
-            } else if (current instanceof CompositeNode) {
-                val currentComposite = (current as CompositeNode);
-
-                current = currentComposite.getFirstCompositeByName(arg.nodeType);
-                if(current == null) {
-                    current = currentComposite.getFirstCompositeByName(arg.nodeType.withoutRevision());
-                }
-                if(current == null) {
-                    current = currentComposite.getFirstSimpleByName(arg.nodeType);
-                }
-                if (current == null) {
-                    current = currentComposite.getFirstSimpleByName(arg.nodeType.withoutRevision());
-                } if (current == null) {
-                    return null;
-                }
-            }
-        }
-        return current;
-    }
-
-    override requestCommit(DataModification<InstanceIdentifier, CompositeNode> modification) {
-        val twoPhaseCommit = new NetconfDeviceTwoPhaseCommitTransaction(this, modification, true);
-        twoPhaseCommit.prepare()
-        return twoPhaseCommit;
-    }
-
-    def getCapabilities(Collection<String> capabilities) {
-        return FluentIterable.from(capabilities).filter[
-                contains("?") && contains("module=") && contains("revision=")].transform [
-                val parts = split("\\?");
-                val namespace = parts.get(0);
-                val queryParams = FluentIterable.from(parts.get(1).split("&"));
-                var revision = queryParams.findFirst[startsWith("revision=")]?.replaceAll("revision=", "");
-                val moduleName = queryParams.findFirst[startsWith("module=")]?.replaceAll("module=", "");
-                if (revision === null) {
-                    logger.warn("Netconf device was not reporting revision correctly, trying to get amp;revision=");
-                    revision = queryParams.findFirst[startsWith("&amp;revision=")]?.replaceAll("revision=", "");
-                    if (revision != null) {
-                        logger.warn("Netconf device returned revision incorectly escaped for {}", it)
-                    }
-                }
-                if (revision == null) {
-                    return QName.create(URI.create(namespace), null, moduleName);
-                }
-                return QName.create(namespace, revision, moduleName);
-            ].toSet();
-    }
-
-    override close() {
-        bringDown()
-    }
-}
-
-package class NetconfDeviceSchemaContextProvider {
-
-    @Property
-    val NetconfDevice device;
-
-    @Property
-    val SchemaSourceProvider<InputStream> sourceProvider;
-
-    @Property
-    var Optional<SchemaContext> currentContext;
-
-    new(NetconfDevice device, SchemaSourceProvider<InputStream> sourceProvider) {
-        _device = device
-        _sourceProvider = sourceProvider
-        _currentContext = Optional.absent();
-    }
-
-    def createContextFromCapabilities(Iterable<QName> capabilities) {
-        val sourceContext = YangSourceContext.createFrom(capabilities, sourceProvider)
-        if (!sourceContext.missingSources.empty) {
-            device.logger.warn("Sources for following models are missing {}", sourceContext.missingSources);
-        }
-        device.logger.debug("Trying to create schema context from {}", sourceContext.validSources)
-        val modelsToParse = YangSourceContext.getValidInputStreams(sourceContext);
-        if (!sourceContext.validSources.empty) {
-            val schemaContext = tryToCreateContext(modelsToParse);
-            currentContext = Optional.fromNullable(schemaContext);
-        } else {
-            currentContext = Optional.absent();
-        }
-        if (currentContext.present) {
-            device.logger.debug("Schema context successfully created.");
-        }
-
-    }
-
-    def SchemaContext tryToCreateContext(List<InputStream> modelsToParse) {
-        val parser = new YangParserImpl();
-        try {
-
-            val models = parser.parseYangModelsFromStreams(modelsToParse);
-            val result = parser.resolveSchemaContext(models);
-            return result;
-        } catch (Exception e) {
-            device.logger.debug("Error occured during parsing YANG schemas", e);
-            return null;
-        }
-    }
-}
index 8c65aa309f125bd1d17bb7efde317f54a0b6cc60..7ef4569600cb6c10433ec439f16ec6d98e220fa6 100644 (file)
@@ -64,6 +64,7 @@ class NetconfDeviceListener implements NetconfClientSessionListener {
                 device.getName(), device.getSocketAddress(), session.getSessionId());
 
         this.session = session;
+
         final Set<QName> caps = device.getCapabilities(session.getServerCapabilities());
         LOG.trace("Server {} advertized capabilities {}", device.getName(), caps);
 
@@ -71,16 +72,15 @@ class NetconfDeviceListener implements NetconfClientSessionListener {
         final SchemaSourceProvider<String> delegate;
         if (NetconfRemoteSchemaSourceProvider.isSupportedFor(caps)) {
             delegate = new NetconfRemoteSchemaSourceProvider(device);
-            // FIXME parsed caps contain only module-based capabilities
+            // FIXME caps do not contain urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring, since it is filtered out in getCapabilitites
         } else if(session.getServerCapabilities().contains(NetconfRemoteSchemaSourceProvider.IETF_NETCONF_MONITORING.getNamespace().toString())) {
             delegate = new NetconfRemoteSchemaSourceProvider(device);
         } else {
             LOG.info("Netconf server {} does not support IETF Netconf Monitoring", device.getName());
-            delegate = SchemaSourceProviders.<String>noopProvider();
+            delegate = SchemaSourceProviders.noopProvider();
         }
 
         device.bringUp(delegate, caps);
-
     }
 
     private synchronized void tearDown(final Exception e) {
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.java
new file mode 100644 (file)
index 0000000..ce2661a
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * 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.controller.sal.connect.netconf;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.activation.UnsupportedDataTypeException;
+import javax.annotation.Nullable;
+
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+public class NetconfMapping {
+
+    public static URI NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
+    public static String NETCONF_MONITORING_URI = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
+    public static URI NETCONF_NOTIFICATION_URI = URI.create("urn:ietf:params:xml:ns:netconf:notification:1.0");
+
+    public static QName NETCONF_QNAME = QName.create(NETCONF_URI, null, "netconf");
+    public static QName NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc");
+    public static QName NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get");
+    public static QName NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter");
+    public static QName NETCONF_TYPE_QNAME = QName.create(NETCONF_QNAME, "type");
+    public static QName NETCONF_GET_CONFIG_QNAME = QName.create(NETCONF_QNAME, "get-config");
+    public static QName NETCONF_EDIT_CONFIG_QNAME = QName.create(NETCONF_QNAME, "edit-config");
+    public static QName NETCONF_DELETE_CONFIG_QNAME = QName.create(NETCONF_QNAME, "delete-config");
+    public static QName NETCONF_OPERATION_QNAME = QName.create(NETCONF_QNAME, "operation");
+    public static QName NETCONF_COMMIT_QNAME = QName.create(NETCONF_QNAME, "commit");
+
+    public static QName NETCONF_CONFIG_QNAME = QName.create(NETCONF_QNAME, "config");
+    public static QName NETCONF_SOURCE_QNAME = QName.create(NETCONF_QNAME, "source");
+    public static QName NETCONF_TARGET_QNAME = QName.create(NETCONF_QNAME, "target");
+
+    public static QName NETCONF_CANDIDATE_QNAME = QName.create(NETCONF_QNAME, "candidate");
+    public static QName NETCONF_RUNNING_QNAME = QName.create(NETCONF_QNAME, "running");
+
+    public static QName NETCONF_RPC_REPLY_QNAME = QName.create(NETCONF_QNAME, "rpc-reply");
+    public static QName NETCONF_OK_QNAME = QName.create(NETCONF_QNAME, "ok");
+    public static QName NETCONF_DATA_QNAME = QName.create(NETCONF_QNAME, "data");
+    public static QName NETCONF_CREATE_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI, null,
+            "create-subscription");
+    public static QName NETCONF_CANCEL_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI, null,
+            "cancel-subscription");
+    public static QName IETF_NETCONF_MONITORING_MODULE = QName.create(NETCONF_MONITORING_URI, "2010-10-04",
+            "ietf-netconf-monitoring");
+
+    static List<Node<?>> RUNNING = Collections.<Node<?>> singletonList(new SimpleNodeTOImpl(NETCONF_RUNNING_QNAME,
+            null, null));
+
+    public static CompositeNode CONFIG_SOURCE_RUNNING = new CompositeNodeTOImpl(NETCONF_SOURCE_QNAME, null, RUNNING);
+
+    static AtomicInteger messageId = new AtomicInteger(0);
+
+    static Node<?> toFilterStructure(InstanceIdentifier identifier) {
+        Node<?> previous = null;
+        if (identifier.getPath().isEmpty()) {
+            return null;
+        }
+
+        for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument component : Lists
+                .reverse(identifier.getPath())) {
+            previous = toNode(component, previous);
+        }
+        return filter("subtree", previous);
+    }
+
+    static Node<?> toNode(NodeIdentifierWithPredicates argument, Node<?> node) {
+        List<Node<?>> list = new ArrayList<>();
+        for (Map.Entry<QName, Object> arg : argument.getKeyValues().entrySet()) {
+            list.add(new SimpleNodeTOImpl(arg.getKey(), null, arg.getValue()));
+        }
+        if (node != null) {
+            list.add(node);
+        }
+        return new CompositeNodeTOImpl(argument.getNodeType(), null, list);
+    }
+
+    static Node<?> toNode(PathArgument argument, Node<?> node) {
+        if (node != null) {
+            return new CompositeNodeTOImpl(argument.getNodeType(), null, Collections.<Node<?>> singletonList(node));
+        } else {
+            return new SimpleNodeTOImpl(argument.getNodeType(), null, null);
+        }
+    }
+
+    static CompositeNode toCompositeNode(NetconfMessage message, Optional<SchemaContext> ctx) {
+        // TODO: implement general normalization to normalize incoming Netconf
+        // Message
+        // for Schema Context counterpart
+        return null;
+    }
+
+    static CompositeNode toNotificationNode(NetconfMessage message, Optional<SchemaContext> ctx) {
+        if (ctx.isPresent()) {
+            SchemaContext schemaContext = ctx.get();
+            Set<NotificationDefinition> notifications = schemaContext.getNotifications();
+            Document document = message.getDocument();
+            return XmlDocumentUtils.notificationToDomNodes(document, Optional.fromNullable(notifications));
+        }
+        return null;
+    }
+
+    static NetconfMessage toRpcMessage(QName rpc, CompositeNode node, Optional<SchemaContext> ctx) {
+        CompositeNodeTOImpl rpcPayload = wrap(NETCONF_RPC_QNAME, flattenInput(node));
+        Document w3cPayload = null;
+        try {
+            w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, XmlDocumentUtils.defaultValueCodecProvider());
+        } catch (UnsupportedDataTypeException e) {
+            // FIXME Ex handling
+            e.printStackTrace();
+        }
+        w3cPayload.getDocumentElement().setAttribute("message-id", "m-" + messageId.getAndIncrement());
+        return new NetconfMessage(w3cPayload);
+    }
+
+    static CompositeNode flattenInput(final CompositeNode node) {
+        final QName inputQName = QName.create(node.getNodeType(), "input");
+        CompositeNode input = node.getFirstCompositeByName(inputQName);
+        if (input == null)
+            return node;
+        if (input instanceof CompositeNode) {
+
+            List<Node<?>> nodes = ImmutableList.<Node<?>> builder() //
+                    .addAll(input.getChildren()) //
+                    .addAll(Collections2.filter(node.getChildren(), new Predicate<Node<?>>() {
+                        @Override
+                        public boolean apply(@Nullable final Node<?> input) {
+                            return input.getNodeType() != inputQName;
+                        }
+                    })) //
+                    .build();
+
+            return ImmutableCompositeNode.create(node.getNodeType(), nodes);
+        }
+
+        return input;
+    }
+
+    static RpcResult<CompositeNode> toRpcResult(NetconfMessage message, final QName rpc, Optional<SchemaContext> context) {
+        CompositeNode rawRpc;
+        if (context.isPresent())
+            if (isDataRetrieQNameReply(rpc)) {
+
+                Element xmlData = getDataSubtree(message.getDocument());
+
+                List<org.opendaylight.yangtools.yang.data.api.Node<?>> dataNodes = XmlDocumentUtils.toDomNodes(xmlData,
+                        Optional.of(context.get().getDataDefinitions()));
+
+                CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder();
+                it.setQName(NETCONF_RPC_REPLY_QNAME);
+                it.add(ImmutableCompositeNode.create(NETCONF_DATA_QNAME, dataNodes));
+
+                rawRpc = it.toInstance();
+                // sys(xmlData)
+            } else {
+                RpcDefinition rpcSchema = Iterables.find(context.get().getOperations(), new Predicate<RpcDefinition>() {
+                    @Override
+                    public boolean apply(final RpcDefinition input) {
+                        return rpc == input.getQName();
+                    }
+                });
+                rawRpc = (CompositeNode) toCompositeNode(message.getDocument());
+            }
+        else {
+            rawRpc = (CompositeNode) toCompositeNode(message.getDocument());
+        }
+        // rawRpc.
+        return Rpcs.getRpcResult(true, rawRpc, Collections.<RpcError> emptySet());
+    }
+
+    static Element getDataSubtree(Document doc) {
+        return (Element) doc.getElementsByTagNameNS(NETCONF_URI.toString(), "data").item(0);
+    }
+
+    static boolean isDataRetrieQNameReply(QName it) {
+        return NETCONF_URI == it.getNamespace()
+                && (it.getLocalName() == NETCONF_GET_CONFIG_QNAME.getLocalName() || it.getLocalName() == NETCONF_GET_QNAME
+                        .getLocalName());
+    }
+
+    static CompositeNodeTOImpl wrap(QName name, Node<?> node) {
+        if (node != null) {
+            return new CompositeNodeTOImpl(name, null, Collections.<Node<?>> singletonList(node));
+        } else {
+            return new CompositeNodeTOImpl(name, null, Collections.<Node<?>> emptyList());
+        }
+    }
+
+    static CompositeNodeTOImpl wrap(QName name, Node<?> additional, Node<?> node) {
+        if (node != null) {
+            return new CompositeNodeTOImpl(name, null, ImmutableList.of(additional, node));
+        } else {
+            return new CompositeNodeTOImpl(name, null, ImmutableList.<Node<?>> of(additional));
+        }
+    }
+
+    static ImmutableCompositeNode filter(String type, Node<?> node) {
+        CompositeNodeBuilder<ImmutableCompositeNode> it = ImmutableCompositeNode.builder(); //
+        it.setQName(NETCONF_FILTER_QNAME);
+        it.setAttribute(NETCONF_TYPE_QNAME, type);
+        if (node != null) {
+            return it.add(node).toInstance();
+        } else {
+            return it.toInstance();
+        }
+    }
+
+    public static Node<?> toCompositeNode(Document document) {
+        return XmlDocumentUtils.toDomNode(document);
+    }
+
+    public static void checkValidReply(NetconfMessage input, NetconfMessage output) {
+        String inputMsgId = input.getDocument().getDocumentElement().getAttribute("message-id");
+        String outputMsgId = output.getDocument().getDocumentElement().getAttribute("message-id");
+        Preconditions.checkState(inputMsgId == outputMsgId, "Rpc request and reply message IDs must be same.");
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.xtend b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.xtend
deleted file mode 100644 (file)
index 228a01e..0000000
+++ /dev/null
@@ -1,217 +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.controller.sal.connect.netconf
-
-import com.google.common.base.Optional
-import com.google.common.base.Preconditions
-import com.google.common.collect.ImmutableList
-import java.net.URI
-import java.util.ArrayList
-import java.util.Collections
-import java.util.List
-import java.util.Set
-import java.util.concurrent.atomic.AtomicInteger
-import org.opendaylight.controller.netconf.api.NetconfMessage
-import org.opendaylight.controller.sal.common.util.Rpcs
-import org.opendaylight.yangtools.yang.common.QName
-import org.opendaylight.yangtools.yang.common.RpcResult
-import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument
-import org.opendaylight.yangtools.yang.data.api.Node
-import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
-import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode
-import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
-import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils
-import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
-import org.opendaylight.yangtools.yang.model.api.SchemaContext
-import org.w3c.dom.Document
-import org.w3c.dom.Element
-
-class NetconfMapping {
-
-    public static val NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0")
-    public static val NETCONF_MONITORING_URI = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"
-    public static val NETCONF_NOTIFICATION_URI = URI.create("urn:ietf:params:xml:ns:netconf:notification:1.0")
-
-
-    public static val NETCONF_QNAME = QName.create(NETCONF_URI, null, "netconf");
-    public static val NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc");
-    public static val NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get");
-    public static val NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter");
-    public static val NETCONF_TYPE_QNAME = QName.create(NETCONF_QNAME, "type");
-    public static val NETCONF_GET_CONFIG_QNAME = QName.create(NETCONF_QNAME, "get-config");
-    public static val NETCONF_EDIT_CONFIG_QNAME = QName.create(NETCONF_QNAME, "edit-config");
-    public static val NETCONF_DELETE_CONFIG_QNAME = QName.create(NETCONF_QNAME, "delete-config");
-    public static val NETCONF_OPERATION_QNAME = QName.create(NETCONF_QNAME, "operation");
-    public static val NETCONF_COMMIT_QNAME = QName.create(NETCONF_QNAME, "commit");
-
-    public static val NETCONF_CONFIG_QNAME = QName.create(NETCONF_QNAME, "config");
-    public static val NETCONF_SOURCE_QNAME = QName.create(NETCONF_QNAME, "source");
-    public static val NETCONF_TARGET_QNAME = QName.create(NETCONF_QNAME, "target");
-
-    public static val NETCONF_CANDIDATE_QNAME = QName.create(NETCONF_QNAME, "candidate");
-    public static val NETCONF_RUNNING_QNAME = QName.create(NETCONF_QNAME, "running");
-
-
-    public static val NETCONF_RPC_REPLY_QNAME = QName.create(NETCONF_QNAME, "rpc-reply");
-    public static val NETCONF_OK_QNAME = QName.create(NETCONF_QNAME, "ok");
-    public static val NETCONF_DATA_QNAME = QName.create(NETCONF_QNAME, "data");
-    public static val NETCONF_CREATE_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI,null,"create-subscription");
-    public static val NETCONF_CANCEL_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI,null,"cancel-subscription");
-    public static val IETF_NETCONF_MONITORING_MODULE = QName.create(NETCONF_MONITORING_URI, "2010-10-04","ietf-netconf-monitoring");
-
-    static List<Node<?>> RUNNING = Collections.<Node<?>>singletonList(
-        new SimpleNodeTOImpl(NETCONF_RUNNING_QNAME, null, null));
-    public static val CONFIG_SOURCE_RUNNING = new CompositeNodeTOImpl(NETCONF_SOURCE_QNAME, null, RUNNING);
-
-    static val messageId = new AtomicInteger(0);
-
-    static def Node<?> toFilterStructure(InstanceIdentifier identifier) {
-        var Node<?> previous = null;
-        if(identifier.path.empty) {
-            return null;
-        }
-
-        for (component : identifier.path.reverseView) {
-            val Node<?> current = component.toNode(previous);
-            previous = current;
-        }
-        return filter("subtree",previous);
-    }
-
-    static def dispatch Node<?> toNode(NodeIdentifierWithPredicates argument, Node<?> node) {
-        val list = new ArrayList<Node<?>>();
-        for (arg : argument.keyValues.entrySet) {
-            list.add = new SimpleNodeTOImpl(arg.key, null, arg.value);
-        }
-        if (node != null) {
-            list.add(node);
-        }
-        return new CompositeNodeTOImpl(argument.nodeType, null, list)
-    }
-
-    static def dispatch Node<?> toNode(PathArgument argument, Node<?> node) {
-        if (node != null) {
-            return new CompositeNodeTOImpl(argument.nodeType, null, Collections.singletonList(node));
-        } else {
-            return new SimpleNodeTOImpl(argument.nodeType, null, null);
-        }
-    }
-
-    static def CompositeNode toCompositeNode(NetconfMessage message,Optional<SchemaContext> ctx) {
-        //TODO: implement general normalization to normalize incoming Netconf Message
-        // for Schema Context counterpart
-        return null
-    }
-
-    static def CompositeNode toNotificationNode(NetconfMessage message,Optional<SchemaContext> ctx) {
-        if (ctx.present) {
-            val schemaContext = ctx.get
-            val notifications = schemaContext.notifications
-            val document = message.document
-            return XmlDocumentUtils.notificationToDomNodes(document, Optional.<Set<NotificationDefinition>>fromNullable(notifications))
-        }
-        return null
-    }
-
-    static def NetconfMessage toRpcMessage(QName rpc, CompositeNode node,Optional<SchemaContext> ctx) {
-        val rpcPayload = wrap(NETCONF_RPC_QNAME, flattenInput(node))
-        val w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, XmlDocumentUtils.defaultValueCodecProvider)
-        w3cPayload.documentElement.setAttribute("message-id", "m-" + messageId.andIncrement)
-        return new NetconfMessage(w3cPayload);
-    }
-
-    def static flattenInput(CompositeNode node) {
-        val inputQName = QName.create(node.nodeType,"input");
-        val input = node.getFirstCompositeByName(inputQName);
-        if(input == null) return node;
-        if(input instanceof CompositeNode) {
-
-            val nodes = ImmutableList.builder() //
-                .addAll(input.children) //
-                .addAll(node.children.filter[nodeType != inputQName]) //
-                .build()
-            return ImmutableCompositeNode.create(node.nodeType,nodes);
-        }
-
-    }
-
-    static def RpcResult<CompositeNode> toRpcResult(NetconfMessage message,QName rpc,Optional<SchemaContext> context) {
-        var CompositeNode rawRpc;
-        if(context.present) {
-            if(isDataRetrievalReply(rpc)) {
-
-                val xmlData = message.document.dataSubtree
-                val dataNodes = XmlDocumentUtils.toDomNodes(xmlData, Optional.of(context.get.dataDefinitions))
-
-                val it = ImmutableCompositeNode.builder()
-                setQName(NETCONF_RPC_REPLY_QNAME)
-                add(ImmutableCompositeNode.create(NETCONF_DATA_QNAME, dataNodes));
-
-                rawRpc = it.toInstance;
-                //sys(xmlData)
-            } else {
-                val rpcSchema = context.get.operations.findFirst[QName == rpc]
-                rawRpc = message.document.toCompositeNode() as CompositeNode;
-            }
-        } else {
-            rawRpc = message.document.toCompositeNode() as CompositeNode;
-        }
-        //rawRpc.
-        return Rpcs.getRpcResult(true, rawRpc, Collections.emptySet());
-    }
-
-    def static Element getDataSubtree(Document doc) {
-        doc.getElementsByTagNameNS(NETCONF_URI.toString,"data").item(0) as Element
-    }
-
-    def static boolean isDataRetrievalReply(QName it) {
-        return NETCONF_URI == namespace && ( localName == NETCONF_GET_CONFIG_QNAME.localName || localName == NETCONF_GET_QNAME.localName)
-    }
-
-    static def wrap(QName name, Node<?> node) {
-        if (node != null) {
-            return new CompositeNodeTOImpl(name, null, Collections.singletonList(node));
-        } else {
-            return new CompositeNodeTOImpl(name, null, Collections.emptyList());
-        }
-    }
-
-    static def wrap(QName name, Node<?> additional, Node<?> node) {
-        if (node != null) {
-            return new CompositeNodeTOImpl(name, null, ImmutableList.of(additional, node));
-        } else {
-            return new CompositeNodeTOImpl(name, null, ImmutableList.of(additional));
-        }
-    }
-
-    static def filter(String type, Node<?> node) {
-        val it = ImmutableCompositeNode.builder(); //
-        setQName(NETCONF_FILTER_QNAME);
-        setAttribute(NETCONF_TYPE_QNAME,type);
-        if (node != null) {
-            return add(node).toInstance();
-        } else {
-            return toInstance();
-        }
-    }
-
-    public static def Node<?> toCompositeNode(Document document) {
-        return XmlDocumentUtils.toDomNode(document) as Node<?>
-    }
-
-    public static def checkValidReply(NetconfMessage input, NetconfMessage output) {
-        val inputMsgId = input.document.documentElement.getAttribute("message-id")
-        val outputMsgId = output.document.documentElement.getAttribute("message-id")
-        Preconditions.checkState(inputMsgId == outputMsgId,"Rpc request and reply message IDs must be same.");
-
-    }
-
-}