From: Maros Marsalek Date: Wed, 16 Apr 2014 09:29:09 +0000 (+0200) Subject: BUG-758 Eliminate xtend from sal-netconf-connector X-Git-Tag: autorelease-tag-v20140601202136_82eb3f9~184^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=2c975930426e129f4bd86e9fa5dec257cbe2d6b6;ds=sidebyside BUG-758 Eliminate xtend from sal-netconf-connector Change-Id: I6add3e5ef2790aab5f0cda886f40bc400ff81f8d Signed-off-by: Maros Marsalek --- diff --git a/opendaylight/md-sal/sal-netconf-connector/pom.xml b/opendaylight/md-sal/sal-netconf-connector/pom.xml index 9b701203ce..27d320f03e 100644 --- a/opendaylight/md-sal/sal-netconf-connector/pom.xml +++ b/opendaylight/md-sal/sal-netconf-connector/pom.xml @@ -26,10 +26,6 @@ ${project.groupId} sal-connector-api - - org.eclipse.xtend - org.eclipse.xtend.lib - org.opendaylight.controller ietf-netconf-monitoring @@ -175,11 +171,6 @@ - - org.eclipse.xtend - xtend-maven-plugin - - org.opendaylight.yangtools yang-maven-plugin 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 index 0000000000..abbbb68265 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java @@ -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, // + DataCommitHandler, // + RpcImplementation, // + AutoCloseable { + + InetSocketAddress socketAddress; + + MountProvisionInstance mountInstance; + + EventExecutor eventExecutor; + + ExecutorService processingExecutor; + + InstanceIdentifier path; + + ReconnectStrategy reconnectStrategy; + + AbstractCachingSchemaSourceProvider schemaSourceProvider; + + private NetconfDeviceSchemaContextProvider deviceContextProvider; + + protected Logger logger; + + Registration> operReaderReg; + Registration> confReaderReg; + Registration> commitHandlerReg; + List rpcReg; + + String name; + + MountProvisionService mountService; + + NetconfClientDispatcher dispatcher; + + static InstanceIdentifier ROOT_PATH = InstanceIdentifier.builder().toInstance(); + + SchemaSourceProvider 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.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 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. 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 delegate, Set 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 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 capabilities) { + DataModificationTransaction transaction = dataBroker.beginTransaction(); + + CompositeNodeBuilder 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 = 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 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 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 getSupportedRpcs() { + return Collections.emptySet(); + } + + @Override + public ListenableFuture> invokeRpc(QName rpc, CompositeNode input) { + return listener.sendRequest(toRpcMessage(rpc, input, getSchemaContext())); + } + + @Override + public Collection 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.> 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 requestCommit( + DataModification 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 getCapabilities(Collection capabilities) { + return FluentIterable.from(capabilities).filter(new Predicate() { + @Override + public boolean apply(final String capability) { + return capability.contains("?") && capability.contains("module=") && capability.contains("revision="); + } + }).transform(new Function() { + @Override + public QName apply(final String capability) { + String[] parts = capability.split("\\?"); + String namespace = parts[0]; + FluentIterable 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 queryParams, final String match, + final String substringToRemove) { + Optional found = Iterables.tryFind(queryParams, new Predicate() { + @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 schemaSourceProvider) { + this.schemaSourceProvider = schemaSourceProvider; + } + + public void setDispatcher(final NetconfClientDispatcher dispatcher) { + this.dispatcher = dispatcher; + } +} + +class NetconfDeviceSchemaContextProvider { + + NetconfDevice device; + + SchemaSourceProvider sourceProvider; + + Optional currentContext; + + NetconfDeviceSchemaContextProvider(NetconfDevice device, SchemaSourceProvider sourceProvider) { + this.device = device; + this.sourceProvider = sourceProvider; + this.currentContext = Optional.absent(); + } + + void createContextFromCapabilities(Iterable 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 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 modelsToParse) { + YangParserImpl parser = new YangParserImpl(); + try { + + Set 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 index 0b88c66b45..0000000000 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.xtend +++ /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, // -DataCommitHandler, // -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 schemaSourceProvider; - - @Property - private NetconfDeviceSchemaContextProvider deviceContextProvider - - protected val Logger logger - - Registration> operReaderReg - Registration> confReaderReg - Registration> commitHandlerReg - List rpcReg - - @Property - val String name - - MountProvisionService mountService - - @Property - var NetconfClientDispatcher dispatcher - - static val InstanceIdentifier ROOT_PATH = InstanceIdentifier.builder().toInstance(); - - @Property - var SchemaSourceProvider 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 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 delegate, Set 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(); - 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 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 modification) { - val twoPhaseCommit = new NetconfDeviceTwoPhaseCommitTransaction(this, modification, true); - twoPhaseCommit.prepare() - return twoPhaseCommit; - } - - def getCapabilities(Collection 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("&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 sourceProvider; - - @Property - var Optional currentContext; - - new(NetconfDevice device, SchemaSourceProvider sourceProvider) { - _device = device - _sourceProvider = sourceProvider - _currentContext = Optional.absent(); - } - - def createContextFromCapabilities(Iterable 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 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; - } - } -} 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 index 8c65aa309f..7ef4569600 100644 --- 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 @@ -64,6 +64,7 @@ class NetconfDeviceListener implements NetconfClientSessionListener { device.getName(), device.getSocketAddress(), session.getSessionId()); this.session = session; + final Set caps = device.getCapabilities(session.getServerCapabilities()); LOG.trace("Server {} advertized capabilities {}", device.getName(), caps); @@ -71,16 +72,15 @@ class NetconfDeviceListener implements NetconfClientSessionListener { final SchemaSourceProvider 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.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 index 0000000000..ce2661a7b3 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.java @@ -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> RUNNING = Collections.> 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> list = new ArrayList<>(); + for (Map.Entry 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.> singletonList(node)); + } else { + return new SimpleNodeTOImpl(argument.getNodeType(), null, null); + } + } + + static CompositeNode toCompositeNode(NetconfMessage message, Optional ctx) { + // TODO: implement general normalization to normalize incoming Netconf + // Message + // for Schema Context counterpart + return null; + } + + static CompositeNode toNotificationNode(NetconfMessage message, Optional ctx) { + if (ctx.isPresent()) { + SchemaContext schemaContext = ctx.get(); + Set notifications = schemaContext.getNotifications(); + Document document = message.getDocument(); + return XmlDocumentUtils.notificationToDomNodes(document, Optional.fromNullable(notifications)); + } + return null; + } + + static NetconfMessage toRpcMessage(QName rpc, CompositeNode node, Optional 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> nodes = ImmutableList.> builder() // + .addAll(input.getChildren()) // + .addAll(Collections2.filter(node.getChildren(), new Predicate>() { + @Override + public boolean apply(@Nullable final Node input) { + return input.getNodeType() != inputQName; + } + })) // + .build(); + + return ImmutableCompositeNode.create(node.getNodeType(), nodes); + } + + return input; + } + + static RpcResult toRpcResult(NetconfMessage message, final QName rpc, Optional context) { + CompositeNode rawRpc; + if (context.isPresent()) + if (isDataRetrieQNameReply(rpc)) { + + Element xmlData = getDataSubtree(message.getDocument()); + + List> dataNodes = XmlDocumentUtils.toDomNodes(xmlData, + Optional.of(context.get().getDataDefinitions())); + + CompositeNodeBuilder 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() { + @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. 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.> singletonList(node)); + } else { + return new CompositeNodeTOImpl(name, null, Collections.> 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.> of(additional)); + } + } + + static ImmutableCompositeNode filter(String type, Node node) { + CompositeNodeBuilder 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 index 228a01eb4c..0000000000 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.xtend +++ /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> RUNNING = Collections.>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>(); - 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 ctx) { - //TODO: implement general normalization to normalize incoming Netconf Message - // for Schema Context counterpart - return null - } - - static def CompositeNode toNotificationNode(NetconfMessage message,Optional ctx) { - if (ctx.present) { - val schemaContext = ctx.get - val notifications = schemaContext.notifications - val document = message.document - return XmlDocumentUtils.notificationToDomNodes(document, Optional.>fromNullable(notifications)) - } - return null - } - - static def NetconfMessage toRpcMessage(QName rpc, CompositeNode node,Optional 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 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) { - 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."); - - } - -}