From: Tony Tkacik Date: Wed, 15 Jan 2014 13:04:02 +0000 (+0100) Subject: Updated implementation of Netconf, fixed DOM Mountpoint X-Git-Tag: jenkins-controller-bulk-release-prepare-only-2-1~90 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=43a4eb1e085754c92a88b0d9740610fe382298f8 Updated implementation of Netconf, fixed DOM Mountpoint Change-Id: Ia0d4f92a65174e891a8111571ced24182d9f47c6 Signed-off-by: Tony Tkacik --- diff --git a/opendaylight/md-sal/sal-common-util/pom.xml b/opendaylight/md-sal/sal-common-util/pom.xml index ff15e72ba6..adbe3d2a49 100644 --- a/opendaylight/md-sal/sal-common-util/pom.xml +++ b/opendaylight/md-sal/sal-common-util/pom.xml @@ -27,6 +27,12 @@ com.google.guava guava + + org.opendaylight.controller + sal-common-api + ${project.version} + + bundle diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Arguments.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Arguments.java index 2d10fba8a5..902665d1a6 100644 --- a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Arguments.java +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Arguments.java @@ -1,3 +1,10 @@ +/* + * 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.controller.sal.common.util; public class Arguments { diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/CommitHandlerTransactions.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/CommitHandlerTransactions.java new file mode 100644 index 0000000000..bffeb59fab --- /dev/null +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/CommitHandlerTransactions.java @@ -0,0 +1,38 @@ +package org.opendaylight.controller.sal.common.util; + +import java.util.Collections; + +import org.opendaylight.controller.md.sal.common.api.data.DataModification; +import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction; +import org.opendaylight.yangtools.concepts.Path; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; + +public class CommitHandlerTransactions { + + private static class AllwaysSuccessfulTransaction

,D> implements DataCommitTransaction { + + private final DataModification modification; + + public AllwaysSuccessfulTransaction(DataModification modification) { + this.modification = modification; + } + @Override + public RpcResult rollback() throws IllegalStateException { + return Rpcs.getRpcResult(true, null, Collections.emptyList()); + } + @Override + public RpcResult finish() throws IllegalStateException { + return Rpcs.getRpcResult(true, null, Collections.emptyList()); + } + + @Override + public DataModification getModification() { + return modification; + } + } + + public static final

,D> AllwaysSuccessfulTransaction allwaysSuccessfulTransaction(DataModification modification) { + return new AllwaysSuccessfulTransaction<>(modification); + } +} diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Futures.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Futures.java index d9cf5cc477..42b00ba03d 100644 --- a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Futures.java +++ b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Futures.java @@ -1,3 +1,10 @@ +/* + * 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.controller.sal.common.util; import java.util.concurrent.ExecutionException; diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcProvisionRegistry.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcProvisionRegistry.java index c326bab7a4..951d5b142e 100644 --- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcProvisionRegistry.java +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcProvisionRegistry.java @@ -4,7 +4,7 @@ import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration; import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration; import org.opendaylight.yangtools.yang.common.QName; -public interface RpcProvisionRegistry { +public interface RpcProvisionRegistry extends BrokerService { /** * Registers an implementation of the rpc. diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountInstance.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountInstance.java index 910c7cb623..18c854646c 100644 --- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountInstance.java +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountInstance.java @@ -6,7 +6,6 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ - package org.opendaylight.controller.sal.core.api.mount; import java.util.concurrent.Future; @@ -18,9 +17,11 @@ import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -public interface MountInstance extends NotificationService, DataBrokerService { +public interface MountInstance extends // + NotificationService, // + DataBrokerService { Future> rpc(QName type, CompositeNode input); - + SchemaContext getSchemaContext(); } diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionService.java index fade7d341b..c1f873c3af 100644 --- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionService.java +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionService.java @@ -1,5 +1,8 @@ package org.opendaylight.controller.sal.core.api.mount; +import java.util.EventListener; + +import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; public interface MountProvisionService extends MountService { @@ -10,4 +13,14 @@ public interface MountProvisionService extends MountService { MountProvisionInstance createMountPoint(InstanceIdentifier path); MountProvisionInstance createOrGetMountPoint(InstanceIdentifier path); + + ListenerRegistration registerProvisionListener(MountProvisionListener listener); + + public interface MountProvisionListener extends EventListener { + + void onMountPointCreated(InstanceIdentifier path); + + void onMountPointRemoved(InstanceIdentifier path); + + } } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/$ModuleInfo.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/$ModuleInfo.java new file mode 100644 index 0000000000..3cc5a61afb --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/$ModuleInfo.java @@ -0,0 +1,6 @@ +package org.opendaylight.controller.sal.dom.broker; + +public class $ModuleInfo { + + +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/DataBrokerImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/DataBrokerImpl.java index 56eae97848..8f62be97d8 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/DataBrokerImpl.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/DataBrokerImpl.java @@ -13,6 +13,8 @@ import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import com.google.common.util.concurrent.MoreExecutors; + public class DataBrokerImpl extends AbstractDataBroker implements DataProviderService, AutoCloseable { @@ -21,6 +23,7 @@ public class DataBrokerImpl extends AbstractDataBroker readWrapper; @@ -48,7 +48,7 @@ public class MountPointImpl implements MountProvisionInstance { public MountPointImpl(InstanceIdentifier path) { this.mountPath = path; rpcs = new RpcRouterImpl(""); - dataReader = new DataReaderRouter(); + dataReader = new DataBrokerImpl(); notificationRouter = new NotificationRouterImpl(); readWrapper = new ReadWrapper(); } @@ -124,15 +124,13 @@ public class MountPointImpl implements MountProvisionInstance { @Override public DataModificationTransaction beginTransaction() { - // TODO Auto-generated method stub - return null; + return dataReader.beginTransaction(); } @Override public ListenerRegistration registerDataChangeListener(InstanceIdentifier path, DataChangeListener listener) { - // TODO Auto-generated method stub - return null; + return dataReader.registerDataChangeListener(path, listener); } @Override @@ -143,8 +141,7 @@ public class MountPointImpl implements MountProvisionInstance { @Override public Registration> registerCommitHandler( InstanceIdentifier path, DataCommitHandler commitHandler) { - // TODO Auto-generated method stub - return null; + return dataReader.registerCommitHandler(path, commitHandler); } @Override @@ -208,7 +205,6 @@ public class MountPointImpl implements MountProvisionInstance { @Override public ListenerRegistration>> registerCommitHandlerListener( RegistrationListener> commitHandlerListener) { - // TODO Auto-generated method stub - return null; + return dataReader.registerCommitHandlerListener(commitHandlerListener); } } diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.xtend index 19634d79c2..5d441bddbd 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.xtend +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.xtend @@ -7,12 +7,16 @@ import java.util.concurrent.ConcurrentMap import java.util.concurrent.ConcurrentHashMap import static com.google.common.base.Preconditions.*; import org.opendaylight.controller.sal.core.api.data.DataProviderService +import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener +import org.opendaylight.yangtools.concepts.util.ListenerRegistry class MountPointManagerImpl implements MountProvisionService { @Property DataProviderService dataBroker; + val ListenerRegistry listeners = ListenerRegistry.create() + ConcurrentMap mounts = new ConcurrentHashMap(); override createMountPoint(InstanceIdentifier path) { @@ -20,15 +24,26 @@ class MountPointManagerImpl implements MountProvisionService { val mount = new MountPointImpl(path); registerMountPoint(mount); mounts.put(path,mount); + notifyMountCreated(path); return mount; } + def notifyMountCreated(InstanceIdentifier identifier) { + for(listener : listeners) { + listener.instance.onMountPointCreated(identifier); + } + } + def registerMountPoint(MountPointImpl impl) { dataBroker?.registerConfigurationReader(impl.mountPath,impl.readWrapper); dataBroker?.registerOperationalReader(impl.mountPath,impl.readWrapper); } + override registerProvisionListener(MountProvisionListener listener) { + listeners.register(listener) + } + override createOrGetMountPoint(InstanceIdentifier path) { val mount = mounts.get(path); diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/MountProviderServiceProxy.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/MountProviderServiceProxy.java index 0d18cb323a..0021dd8f0f 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/MountProviderServiceProxy.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/MountProviderServiceProxy.java @@ -3,6 +3,7 @@ package org.opendaylight.controller.sal.dom.broker.osgi; import org.opendaylight.controller.sal.core.api.data.DataBrokerService; import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance; import org.opendaylight.controller.sal.core.api.mount.MountProvisionService; +import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.osgi.framework.ServiceReference; @@ -24,4 +25,9 @@ public class MountProviderServiceProxy extends AbstractBrokerServiceProxy registerProvisionListener(MountProvisionListener listener) { + return getDelegate().registerProvisionListener(listener); + } } 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 index bfe352ad41..da0790c599 100644 --- 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 @@ -1,61 +1,55 @@ package org.opendaylight.controller.sal.connect.netconf -import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier -import org.opendaylight.controller.md.sal.common.api.data.DataReader -import org.opendaylight.yangtools.yang.data.api.CompositeNode -import org.opendaylight.controller.netconf.client.NetconfClient -import org.opendaylight.controller.sal.core.api.RpcImplementation -import static extension org.opendaylight.controller.sal.connect.netconf.NetconfMapping.* +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 org.opendaylight.yangtools.yang.data.api.Node -import org.opendaylight.yangtools.yang.data.api.SimpleNode -import org.opendaylight.yangtools.yang.common.QName +import java.net.URI import java.util.Collections +import java.util.List +import java.util.Set +import java.util.concurrent.ExecutorService +import java.util.concurrent.Future +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.api.NetconfMessage +import org.opendaylight.controller.netconf.client.NetconfClient import org.opendaylight.controller.netconf.client.NetconfClientDispatcher -import org.opendaylight.yangtools.concepts.Registration -import org.opendaylight.controller.sal.core.api.Provider import org.opendaylight.controller.sal.core.api.Broker.ProviderSession -import org.opendaylight.controller.sal.core.api.mount.MountProvisionService -import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.*; +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.yangtools.yang.data.impl.SimpleNodeTOImpl -import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl +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.controller.md.sal.common.api.data.DataCommitHandler -import org.opendaylight.controller.md.sal.common.api.data.DataModification -import com.google.common.collect.FluentIterable -import org.opendaylight.yangtools.yang.model.api.SchemaContext -import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState +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.model.util.repo.SchemaSourceProviders +import org.opendaylight.yangtools.yang.model.util.repo.SourceIdentifier import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl -import java.io.InputStream -import org.slf4j.LoggerFactory +import org.opendaylight.yangtools.yang.parser.impl.util.YangSourceContext import org.slf4j.Logger -import org.opendaylight.controller.netconf.client.AbstractNetconfClientNotifySessionListener -import org.opendaylight.controller.netconf.client.NetconfClientSession -import org.opendaylight.controller.netconf.api.NetconfMessage -import io.netty.util.concurrent.EventExecutor +import org.slf4j.LoggerFactory -import java.util.Map -import java.util.Set -import com.google.common.collect.ImmutableMap +import static com.google.common.base.Preconditions.* +import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.* -import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider -import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider -import com.google.common.base.Optional -import com.google.common.collect.ImmutableList -import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProviders -import static com.google.common.base.Preconditions.*; -import java.util.concurrent.ExecutorService -import java.util.concurrent.Future -import org.opendaylight.controller.netconf.client.NetconfClientSessionListener -import io.netty.util.concurrent.Promise -import org.opendaylight.controller.netconf.util.xml.XmlElement -import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants -import java.util.concurrent.ExecutionException -import java.util.concurrent.locks.ReentrantLock +import static extension org.opendaylight.controller.sal.connect.netconf.NetconfMapping.* +import org.opendaylight.controller.netconf.util.xml.XmlUtil class NetconfDevice implements Provider, // DataReader, // @@ -86,7 +80,8 @@ AutoCloseable { @Property var AbstractCachingSchemaSourceProvider schemaSourceProvider; - private NetconfDeviceSchemaContextProvider schemaContextProvider + @Property + private NetconfDeviceSchemaContextProvider deviceContextProvider protected val Logger logger @@ -105,9 +100,12 @@ AutoCloseable { @Property var NetconfClientDispatcher dispatcher - + static val InstanceIdentifier ROOT_PATH = InstanceIdentifier.builder().toInstance(); + @Property + var SchemaSourceProvider remoteSourceProvider + public new(String name) { this.name = name; this.logger = LoggerFactory.getLogger(NetconfDevice.name + "#" + name); @@ -120,11 +118,12 @@ AutoCloseable { checkState(schemaSourceProvider != null, "Schema Source Provider must be set.") checkState(eventExecutor != null, "Event executor must be set."); - val listener = new NetconfDeviceListener(this,eventExecutor); + val listener = new NetconfDeviceListener(this, eventExecutor); val task = startClientTask(dispatcher, listener) - if(mountInstance != null) { + if (mountInstance != null) { confReaderReg = mountInstance.registerConfigurationReader(ROOT_PATH, this); operReaderReg = mountInstance.registerOperationalReader(ROOT_PATH, this); + commitHandlerReg = mountInstance.registerCommitHandler(ROOT_PATH, this) } return processingExecutor.submit(task) as Future; @@ -132,27 +131,28 @@ AutoCloseable { } def Optional getSchemaContext() { - if (schemaContextProvider == null) { + if (deviceContextProvider == null) { return Optional.absent(); } - return schemaContextProvider.currentContext; + return deviceContextProvider.currentContext; } private def Runnable startClientTask(NetconfClientDispatcher dispatcher, NetconfDeviceListener listener) { + return [ | logger.info("Starting Netconf Client on: {}", socketAddress); client = NetconfClient.clientFor(name, socketAddress, reconnectStrategy, dispatcher, listener); logger.debug("Initial capabilities {}", initialCapabilities); var SchemaSourceProvider delegate; - if (initialCapabilities.contains(NetconfMapping.IETF_NETCONF_MONITORING_MODULE)) { - delegate = new NetconfDeviceSchemaSourceProvider(this); - } else { - logger.info("Device does not support IETF Netconf Monitoring.", socketAddress); + if (NetconfRemoteSchemaSourceProvider.isSupportedFor(initialCapabilities)) { + delegate = new NetconfRemoteSchemaSourceProvider(this); + } else { + logger.info("Netconf server {} does not support IETF Netconf Monitoring", socketAddress); delegate = SchemaSourceProviders.noopProvider(); } - val sourceProvider = schemaSourceProvider.createInstanceFor(delegate); - schemaContextProvider = new NetconfDeviceSchemaContextProvider(this, sourceProvider); - schemaContextProvider.createContextFromCapabilities(initialCapabilities); + remoteSourceProvider = schemaSourceProvider.createInstanceFor(delegate); + deviceContextProvider = new NetconfDeviceSchemaContextProvider(this, remoteSourceProvider); + deviceContextProvider.createContextFromCapabilities(initialCapabilities); if (mountInstance != null && schemaContext.isPresent) { mountInstance.schemaContext = schemaContext.get(); } @@ -175,18 +175,31 @@ AutoCloseable { override getSupportedRpcs() { Collections.emptySet; } - + def createSubscription(String streamName) { val it = ImmutableCompositeNode.builder() QName = NETCONF_CREATE_SUBSCRIPTION_QNAME - addLeaf("stream",streamName); - invokeRpc(QName,toInstance()) + addLeaf("stream", streamName); + invokeRpc(QName, toInstance()) } override invokeRpc(QName rpc, CompositeNode input) { - val message = rpc.toRpcMessage(input); - val result = client.sendMessage(message, messegeRetryCount, messageTimeoutCount); - return result.toRpcResult(); + try { + val message = rpc.toRpcMessage(input,schemaContext); + val result = sendMessageImpl(message, messegeRetryCount, messageTimeoutCount); + return result.toRpcResult(rpc, schemaContext); + + } catch (Exception e) { + logger.error("Rpc was not processed correctly.", e) + throw e; + } + } + + def NetconfMessage sendMessageImpl(NetconfMessage message, int retryCount, int timeout) { + logger.debug("Send message {}",XmlUtil.toString(message.document)) + val result = client.sendMessage(message, retryCount, timeout); + NetconfMapping.checkValidReply(message, result) + return result; } override getProviderFunctionality() { @@ -221,7 +234,7 @@ AutoCloseable { return null === transaction.readOperationalData(path); } - def Node findNode(CompositeNode node, InstanceIdentifier identifier) { + static def Node findNode(CompositeNode node, InstanceIdentifier identifier) { var Node current = node; for (arg : identifier.path) { @@ -229,12 +242,17 @@ AutoCloseable { return null; } else if (current instanceof CompositeNode) { val currentComposite = (current as CompositeNode); - - current = currentComposite.getFirstCompositeByName(arg.nodeType.withoutRevision()); - if (current == null) { - current = currentComposite.getFirstSimpleByName(arg.nodeType.withoutRevision()); + + 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; } } @@ -243,7 +261,9 @@ AutoCloseable { } override requestCommit(DataModification modification) { - throw new UnsupportedOperationException("TODO: auto-generated method stub") + val twoPhaseCommit = new NetconfDeviceTwoPhaseCommitTransaction(this, modification); + twoPhaseCommit.prepare() + return twoPhaseCommit; } def getInitialCapabilities() { @@ -257,8 +277,18 @@ AutoCloseable { val parts = split("\\?"); val namespace = parts.get(0); val queryParams = FluentIterable.from(parts.get(1).split("&")); - val revision = queryParams.findFirst[startsWith("revision=")].replaceAll("revision=", ""); - val moduleName = queryParams.findFirst[startsWith("module=")].replaceAll("module=", ""); + 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("&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(); } @@ -273,96 +303,6 @@ AutoCloseable { } -package class NetconfDeviceListener extends NetconfClientSessionListener { - - val NetconfDevice device - val EventExecutor eventExecutor - - new(NetconfDevice device,EventExecutor eventExecutor) { - this.device = device - this.eventExecutor = eventExecutor - } - - var Promise messagePromise; - val promiseLock = new ReentrantLock; - - override onMessage(NetconfClientSession session, NetconfMessage message) { - if (isNotification(message)) { - onNotification(session, message); - } else try { - promiseLock.lock - if (messagePromise != null) { - messagePromise.setSuccess(message); - messagePromise = null; - } - } finally { - promiseLock.unlock - } - } - - /** - * Method intended to customize notification processing. - * - * @param session - * {@see - * NetconfClientSessionListener#onMessage(NetconfClientSession, - * NetconfMessage)} - * @param message - * {@see - * NetconfClientSessionListener#onMessage(NetconfClientSession, - * NetconfMessage)} - */ - def void onNotification(NetconfClientSession session, NetconfMessage message) { - device.logger.debug("Received NETCONF notification.",message); - val domNotification = message?.toCompositeNode?.notificationBody; - if(domNotification != null) { - device?.mountInstance?.publish(domNotification); - } - } - - private static def CompositeNode getNotificationBody(CompositeNode node) { - for(child : node.children) { - if(child instanceof CompositeNode) { - return child as CompositeNode; - } - } - } - - override getLastMessage(int attempts, int attemptMsDelay) throws InterruptedException { - val promise = promiseReply(); - val messageAvailable = promise.await(attempts + attemptMsDelay); - if (messageAvailable) { - try { - return promise.get(); - } catch (ExecutionException e) { - throw new IllegalStateException(e); - } - } - - throw new IllegalStateException("Unsuccessful after " + attempts + " attempts."); - - // throw new TimeoutException("Message was not received on time."); - } - - def Promise promiseReply() { - promiseLock.lock - try { - if (messagePromise == null) { - messagePromise = eventExecutor.newPromise(); - return messagePromise; - } - return messagePromise; - } finally { - promiseLock.unlock - } - } - - def boolean isNotification(NetconfMessage message) { - val xmle = XmlElement.fromDomDocument(message.getDocument()); - return XmlNetconfConstants.NOTIFICATION_ELEMENT_NAME.equals(xmle.getName()); - } -} - package class NetconfDeviceSchemaContextProvider { @Property @@ -380,22 +320,29 @@ package class NetconfDeviceSchemaContextProvider { } def createContextFromCapabilities(Iterable capabilities) { - - val modelsToParse = ImmutableMap.builder(); - for (cap : capabilities) { - val source = sourceProvider.getSchemaSource(cap.localName, Optional.fromNullable(cap.formattedRevision)); - if (source.present) { - modelsToParse.put(cap, source.get()); - } + 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(); } - val context = tryToCreateContext(modelsToParse.build); - currentContext = Optional.fromNullable(context); + if (currentContext.present) { + device.logger.debug("Schema context successfully created."); + } + } - def SchemaContext tryToCreateContext(Map modelsToParse) { + def SchemaContext tryToCreateContext(List modelsToParse) { val parser = new YangParserImpl(); try { - val models = parser.parseYangModelsFromStreams(ImmutableList.copyOf(modelsToParse.values)); + + val models = parser.parseYangModelsFromStreams(modelsToParse); val result = parser.resolveSchemaContext(models); return result; } catch (Exception e) { @@ -404,33 +351,3 @@ package class NetconfDeviceSchemaContextProvider { } } } - -package class NetconfDeviceSchemaSourceProvider implements SchemaSourceProvider { - - val NetconfDevice device; - - new(NetconfDevice device) { - this.device = device; - } - - override getSchemaSource(String moduleName, Optional revision) { - val it = ImmutableCompositeNode.builder() // - setQName(QName::create(NetconfState.QNAME, "get-schema")) // - addLeaf("format", "yang") - addLeaf("identifier", moduleName) - if (revision.present) { - addLeaf("version", revision.get()) - } - - device.logger.info("Loading YANG schema source for {}:{}", moduleName, revision) - val schemaReply = device.invokeRpc(getQName(), toInstance()); - - if (schemaReply.successful) { - val schemaBody = schemaReply.result.getFirstSimpleByName( - QName::create(NetconfState.QNAME.namespace, null, "data"))?.value; - device.logger.info("YANG Schema successfully received for: {}:{}", moduleName, revision); - return Optional.of(schemaBody as String); - } - return Optional.absent(); - } -} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java new file mode 100644 index 0000000000..8623d90fe5 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java @@ -0,0 +1,155 @@ +package org.opendaylight.controller.sal.connect.netconf; + +import com.google.common.base.Objects; + +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.Promise; + +import java.util.List; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.locks.ReentrantLock; + +import org.eclipse.xtext.xbase.lib.Exceptions; +import org.eclipse.xtext.xbase.lib.Functions.Function0; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.client.NetconfClientSession; +import org.opendaylight.controller.netconf.client.NetconfClientSessionListener; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.sal.connect.netconf.NetconfDevice; +import org.opendaylight.controller.sal.connect.netconf.NetconfMapping; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.w3c.dom.Document; + +@SuppressWarnings("all") +class NetconfDeviceListener extends NetconfClientSessionListener { + private final NetconfDevice device; + private final EventExecutor eventExecutor; + + public NetconfDeviceListener(final NetconfDevice device, final EventExecutor eventExecutor) { + this.device = device; + this.eventExecutor = eventExecutor; + } + + private Promise messagePromise; + private ConcurrentMap> promisedMessages; + + private final ReentrantLock promiseLock = new ReentrantLock(); + + public void onMessage(final NetconfClientSession session, final NetconfMessage message) { + if (isNotification(message)) { + this.onNotification(session, message); + } else { + try { + this.promiseLock.lock(); + boolean _notEquals = (!Objects.equal(this.messagePromise, null)); + if (_notEquals) { + this.device.logger.debug("Setting promised reply {} with message {}", this.messagePromise, message); + this.messagePromise.setSuccess(message); + this.messagePromise = null; + } + } finally { + this.promiseLock.unlock(); + } + } + } + + /** + * Method intended to customize notification processing. + * + * @param session + * {@see + * NetconfClientSessionListener#onMessage(NetconfClientSession, + * NetconfMessage)} + * @param message + * {@see + * NetconfClientSessionListener#onMessage(NetconfClientSession, + * NetconfMessage)} + */ + public void onNotification(final NetconfClientSession session, final NetconfMessage message) { + this.device.logger.debug("Received NETCONF notification.", message); + CompositeNode _notificationBody = null; + CompositeNode _compositeNode = null; + if (message != null) { + _compositeNode = NetconfMapping.toCompositeNode(message,device.getSchemaContext()); + } + if (_compositeNode != null) { + _notificationBody = NetconfDeviceListener.getNotificationBody(_compositeNode); + } + final CompositeNode domNotification = _notificationBody; + boolean _notEquals = (!Objects.equal(domNotification, null)); + if (_notEquals) { + MountProvisionInstance _mountInstance = null; + if (this.device != null) { + _mountInstance = this.device.getMountInstance(); + } + if (_mountInstance != null) { + _mountInstance.publish(domNotification); + } + } + } + + private static CompositeNode getNotificationBody(final CompositeNode node) { + List> _children = node.getChildren(); + for (final Node child : _children) { + if ((child instanceof CompositeNode)) { + return ((CompositeNode) child); + } + } + return null; + } + + public NetconfMessage getLastMessage(final int attempts, final int attemptMsDelay) throws InterruptedException { + final Promise promise = this.promiseReply(); + this.device.logger.debug("Waiting for reply {}", promise); + int _plus = (attempts * attemptMsDelay); + final boolean messageAvailable = promise.await(_plus); + if (messageAvailable) { + try { + try { + return promise.get(); + } catch (Throwable _e) { + throw Exceptions.sneakyThrow(_e); + } + } catch (final Throwable _t) { + if (_t instanceof ExecutionException) { + final ExecutionException e = (ExecutionException) _t; + IllegalStateException _illegalStateException = new IllegalStateException(e); + throw _illegalStateException; + } else { + throw Exceptions.sneakyThrow(_t); + } + } + } + String _plus_1 = ("Unsuccessful after " + Integer.valueOf(attempts)); + String _plus_2 = (_plus_1 + " attempts."); + IllegalStateException _illegalStateException_1 = new IllegalStateException(_plus_2); + throw _illegalStateException_1; + } + + public synchronized Promise promiseReply() { + this.device.logger.debug("Promising reply."); + this.promiseLock.lock(); + try { + boolean _equals = Objects.equal(this.messagePromise, null); + if (_equals) { + Promise _newPromise = this.eventExecutor. newPromise(); + this.messagePromise = _newPromise; + return this.messagePromise; + } + return this.messagePromise; + } finally { + this.promiseLock.unlock(); + } + } + + public boolean isNotification(final NetconfMessage message) { + Document _document = message.getDocument(); + final XmlElement xmle = XmlElement.fromDomDocument(_document); + String _name = xmle.getName(); + return XmlNetconfConstants.NOTIFICATION_ELEMENT_NAME.equals(_name); + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java new file mode 100644 index 0000000000..216a27aaaa --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java @@ -0,0 +1,134 @@ +package org.opendaylight.controller.sal.connect.netconf; + +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +import org.eclipse.xtext.xbase.lib.IterableExtensions; +import org.opendaylight.controller.md.sal.common.api.data.DataModification; +import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction; +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.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.*; + +public class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransaction { + + private NetconfDevice device; + private final DataModification modification; + private boolean candidateSupported = true; + + public NetconfDeviceTwoPhaseCommitTransaction(NetconfDevice device, + DataModification modification) { + super(); + this.device = device; + this.modification = modification; + } + + public void prepare() { + for (InstanceIdentifier toRemove : modification.getRemovedConfigurationData()) { + sendRemove(toRemove); + } + for(Entry toUpdate : modification.getUpdatedConfigurationData().entrySet()) { + sendMerge(toUpdate.getKey(),toUpdate.getValue()); + } + + } + + private void sendMerge(InstanceIdentifier key, CompositeNode value) { + sendEditRpc(createEditStructure(key, Optional.absent(), Optional.of(value))); + } + + private void sendRemove(InstanceIdentifier toRemove) { + sendEditRpc(createEditStructure(toRemove, Optional.of("remove"), Optional. absent())); + } + + private void sendEditRpc(CompositeNode editStructure) { + CompositeNodeBuilder builder = configurationRpcBuilder(); + builder.setQName(NETCONF_EDIT_CONFIG_QNAME); + builder.add(editStructure); + + RpcResult rpcResult = device.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, builder.toInstance()); + Preconditions.checkState(rpcResult.isSuccessful(),"Rpc Result was unsuccessful"); + + } + + private CompositeNodeBuilder configurationRpcBuilder() { + CompositeNodeBuilder ret = ImmutableCompositeNode.builder(); + + Node targetNode; + if(candidateSupported) { + targetNode = ImmutableCompositeNode.create(NETCONF_CANDIDATE_QNAME, ImmutableList.>of()); + } else { + targetNode = ImmutableCompositeNode.create(NETCONF_RUNNING_QNAME, ImmutableList.>of()); + } + Node targetWrapperNode = ImmutableCompositeNode.create(NETCONF_TARGET_QNAME, ImmutableList.>of(targetNode)); + ret.add(targetWrapperNode); + return ret; + } + + private CompositeNode createEditStructure(InstanceIdentifier dataPath, Optional action, + Optional lastChildOverride) { + List path = dataPath.getPath(); + List reversed = Lists.reverse(path); + CompositeNode previous = null; + boolean isLast = true; + for (PathArgument arg : reversed) { + CompositeNodeBuilder builder = ImmutableCompositeNode.builder(); + builder.setQName(arg.getNodeType()); + + if (arg instanceof NodeIdentifierWithPredicates) { + for (Entry entry : ((NodeIdentifierWithPredicates) arg).getKeyValues().entrySet()) { + builder.addLeaf(entry.getKey(), entry.getValue()); + } + } + if (isLast) { + if (action.isPresent()) { + builder.setAttribute(NETCONF_ACTION_QNAME, action.get()); + } + if (lastChildOverride.isPresent()) { + List> children = lastChildOverride.get().getChildren(); + builder.addAll(children); + } + } else { + builder.add(previous); + } + previous = builder.toInstance(); + isLast = false; + } + return ImmutableCompositeNode.create(NETCONF_CONFIG_QNAME, ImmutableList.>of(previous)); + } + + @Override + public RpcResult finish() throws IllegalStateException { + CompositeNodeBuilder commitInput = ImmutableCompositeNode.builder(); + commitInput.setQName(NETCONF_COMMIT_QNAME); + RpcResult rpcResult = device.invokeRpc(NetconfMapping.NETCONF_COMMIT_QNAME, commitInput.toInstance()); + return (RpcResult) rpcResult; + } + + @Override + public DataModification getModification() { + return this.modification; + } + + @Override + public RpcResult rollback() throws IllegalStateException { + // TODO Auto-generated method stub + return null; + } +} 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 index 794b58294e..c151e42fbb 100644 --- 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 @@ -22,6 +22,10 @@ import java.util.List import com.google.common.collect.ImmutableList import org.opendaylight.yangtools.yang.data.api.SimpleNode import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode +import com.google.common.base.Preconditions +import com.google.common.base.Optional +import org.opendaylight.yangtools.yang.model.api.SchemaContext +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils class NetconfMapping { @@ -36,8 +40,19 @@ class NetconfMapping { 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_ACTION_QNAME = QName.create(NETCONF_QNAME, "action"); + 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"); @@ -80,11 +95,11 @@ class NetconfMapping { } } - static def CompositeNode toCompositeNode(NetconfMessage message) { - return message.toRpcResult().result; + static def CompositeNode toCompositeNode(NetconfMessage message,Optional ctx) { + return null//message.toRpcResult().result; } - static def NetconfMessage toRpcMessage(QName rpc, CompositeNode node) { + static def NetconfMessage toRpcMessage(QName rpc, CompositeNode node,Optional ctx) { val rpcPayload = wrap(NETCONF_RPC_QNAME, flattenInput(node)); val w3cPayload = NodeUtils.buildShadowDomTree(rpcPayload); w3cPayload.documentElement.setAttribute("message-id", "m-" + messageId.andIncrement); @@ -106,12 +121,41 @@ class NetconfMapping { } - static def RpcResult toRpcResult(NetconfMessage message) { - val rawRpc = message.document.toCompositeNode() as CompositeNode; - + static def RpcResult toRpcResult(NetconfMessage message,QName rpc,Optional 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) { @@ -141,6 +185,14 @@ class NetconfMapping { } public static def Node toCompositeNode(Document document) { - return XmlDocumentUtils.toNode(document) as Node + 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."); + + } + } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfRemoteSchemaSourceProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfRemoteSchemaSourceProvider.java new file mode 100644 index 0000000000..12be689cb9 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfRemoteSchemaSourceProvider.java @@ -0,0 +1,69 @@ +package org.opendaylight.controller.sal.connect.netconf; + +import java.util.Set; + +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState; +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.SimpleNode; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; +import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; + +class NetconfRemoteSchemaSourceProvider implements SchemaSourceProvider { + + public static final QName IETF_NETCONF_MONITORING = QName.create( + "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", "2010-10-04", "ietf-netconf-monitoring"); + public static final QName GET_SCHEMA_QNAME = QName.create(IETF_NETCONF_MONITORING, "get-schema"); + public static final QName GET_DATA_QNAME = QName.create(IETF_NETCONF_MONITORING, "data"); + + NetconfDevice device; + + public NetconfRemoteSchemaSourceProvider(NetconfDevice device) { + super(); + this.device = device; + } + + @Override + public Optional getSchemaSource(String moduleName, Optional revision) { + CompositeNodeBuilder request = ImmutableCompositeNode.builder(); // + request.setQName(GET_SCHEMA_QNAME) // + .addLeaf("format", "yang") // + .addLeaf("identifier", moduleName); // + if (revision.isPresent()) { + request.addLeaf("version", revision.get()); + } + + device.logger.info("Loading YANG schema source for {}:{}", moduleName, revision); + RpcResult schemaReply = device.invokeRpc(GET_SCHEMA_QNAME, request.toInstance()); + if (schemaReply.isSuccessful()) { + String schemaBody = getSchemaFromRpc(schemaReply.getResult()); + if (schemaBody != null) { + device.logger.info("YANG Schema successfully retrieved from remote for {}:{}", moduleName, revision); + return Optional.of(schemaBody); + } + } + device.logger.info("YANG shcema was not successfully retrieved."); + return Optional.absent(); + } + + private String getSchemaFromRpc(CompositeNode result) { + if (result == null) { + return null; + } + SimpleNode simpleNode = result.getFirstSimpleByName(GET_DATA_QNAME.withoutRevision()); + Object potential = simpleNode.getValue(); + if (potential instanceof String) { + return (String) potential; + } + return null; + } + + public static final boolean isSupportedFor(Set capabilities) { + return capabilities.contains(IETF_NETCONF_MONITORING); + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/XmlDocumentUtils.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/XmlDocumentUtils.java deleted file mode 100644 index e151fca009..0000000000 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/XmlDocumentUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.opendaylight.controller.sal.connect.netconf; - -import java.net.URI; -import java.util.ArrayList; -import java.util.List; - -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.Node; -import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; -import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; - -import com.google.common.base.Strings; - -public class XmlDocumentUtils { - - public static Node toNode(Document doc) { - return toCompositeNode(doc.getDocumentElement()); - } - - private static Node toCompositeNode(Element element) { - String orgNamespace = element.getNamespaceURI(); - URI biNamespace = null; - if (orgNamespace != null) { - biNamespace = URI.create(orgNamespace); - } - QName qname = new QName(biNamespace, element.getLocalName()); - - List> values = new ArrayList<>(); - NodeList nodes = element.getChildNodes(); - boolean isSimpleObject = true; - String value = null; - for (int i = 0; i < nodes.getLength(); i++) { - org.w3c.dom.Node child = nodes.item(i); - if (child instanceof Element) { - isSimpleObject = false; - values.add(toCompositeNode((Element) child)); - } - if (isSimpleObject && child instanceof org.w3c.dom.Text) { - value = element.getTextContent(); - if (!Strings.isNullOrEmpty(value)) { - isSimpleObject = true; - } - } - } - - if (isSimpleObject) { - return new SimpleNodeTOImpl<>(qname, null, value); - } - return new CompositeNodeTOImpl(qname, null, values); - } -}