From: Ed Warnicke Date: Wed, 13 Aug 2014 13:43:31 +0000 (+0000) Subject: Merge "BUG-997 Use shared schema context factory in netconf-connector" X-Git-Tag: release/helium~298 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=971b179000ef1cc56699de35061cf6f97d4cf36f;hp=a3ff4b68093e6d675a92159e0efa2525af32d644 Merge "BUG-997 Use shared schema context factory in netconf-connector" --- diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java index b75df80f4e..bca47af5c0 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java @@ -10,12 +10,10 @@ package org.opendaylight.controller.config.yang.md.sal.connector.netconf; import static org.opendaylight.controller.config.api.JmxAttributeValidationException.checkCondition; import static org.opendaylight.controller.config.api.JmxAttributeValidationException.checkNotNull; -import java.io.File; -import java.io.InputStream; +import com.google.common.base.Optional; import java.net.InetSocketAddress; import java.util.List; import java.util.concurrent.ExecutorService; - import org.opendaylight.controller.config.api.JmxAttributeValidationException; import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; @@ -25,9 +23,11 @@ import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication. import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler; import org.opendaylight.controller.sal.connect.netconf.NetconfDevice; +import org.opendaylight.controller.sal.connect.netconf.NetconfStateSchemas; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities; import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceSalFacade; +import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.protocol.framework.ReconnectStrategy; @@ -35,16 +35,12 @@ import org.opendaylight.protocol.framework.ReconnectStrategyFactory; import org.opendaylight.protocol.framework.TimedReconnectStrategy; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress; -import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider; -import org.opendaylight.yangtools.yang.model.util.repo.FilesystemSchemaCachingProvider; -import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; -import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProviders; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; - /** * */ @@ -52,9 +48,10 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co { private static final Logger logger = LoggerFactory.getLogger(NetconfConnectorModule.class); - private static AbstractCachingSchemaSourceProvider GLOBAL_NETCONF_SOURCE_PROVIDER = null; private BundleContext bundleContext; private Optional userCapabilities; + private SchemaSourceRegistry schemaRegistry; + private SchemaContextFactory schemaContextFactory; public NetconfConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); @@ -108,8 +105,12 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co final RemoteDeviceHandler salFacade = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor); + + final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = + new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory, new NetconfStateSchemas.NetconfStateSchemasResolverImpl()); + final NetconfDevice device = - NetconfDevice.createNetconfDevice(id, getGlobalNetconfSchemaProvider(), globalProcessingExecutor, salFacade); + new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, new NetconfMessageTransformer()); final NetconfDeviceCommunicator listener = userCapabilities.isPresent() ? new NetconfDeviceCommunicator(id, device, userCapabilities.get()) : new NetconfDeviceCommunicator(id, device); @@ -148,17 +149,6 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co return Optional.of(parsedOverrideCapabilities); } - private synchronized AbstractCachingSchemaSourceProvider getGlobalNetconfSchemaProvider() { - if(GLOBAL_NETCONF_SOURCE_PROVIDER == null) { - final String storageFile = "cache/schema"; - // File directory = bundleContext.getDataFile(storageFile); - final File directory = new File(storageFile); - final SchemaSourceProvider defaultProvider = SchemaSourceProviders.noopProvider(); - GLOBAL_NETCONF_SOURCE_PROVIDER = FilesystemSchemaCachingProvider.createFromStringSourceProvider(defaultProvider, directory); - } - return GLOBAL_NETCONF_SOURCE_PROVIDER; - } - public void setBundleContext(final BundleContext bundleContext) { this.bundleContext = bundleContext; } @@ -212,4 +202,12 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co return new InetSocketAddress(ip, getPort().getValue()); } } + + public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) { + this.schemaRegistry = schemaRegistry; + } + + public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) { + this.schemaContextFactory = schemaContextFactory; + } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModuleFactory.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModuleFactory.java index 9842139dab..b6299697cc 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModuleFactory.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModuleFactory.java @@ -7,9 +7,17 @@ */ package org.opendaylight.controller.config.yang.md.sal.connector.netconf; +import java.io.File; + import org.opendaylight.controller.config.api.DependencyResolver; import org.opendaylight.controller.config.api.DynamicMBeanWithInstance; import org.opendaylight.controller.config.spi.Module; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache; +import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository; +import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer; import org.osgi.framework.BundleContext; /** @@ -18,20 +26,38 @@ import org.osgi.framework.BundleContext; public class NetconfConnectorModuleFactory extends org.opendaylight.controller.config.yang.md.sal.connector.netconf.AbstractNetconfConnectorModuleFactory { + // TODO this should be injected + // Netconf devices have separated schema registry + factory from controller + private final SharedSchemaRepository repository = new SharedSchemaRepository(NAME); + private final SchemaContextFactory schemaContextFactory + = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT); + + public NetconfConnectorModuleFactory() { + // Start cache and Text to AST transformer + final FilesystemSchemaSourceCache cache = new FilesystemSchemaSourceCache<>(repository, YangTextSchemaSource.class, new File("cache/schema")); + repository.registerSchemaSourceListener(cache); + repository.registerSchemaSourceListener(TextToASTTransformer.create(repository, repository)); + } + @Override - public Module createModule(String instanceName, DependencyResolver dependencyResolver, - DynamicMBeanWithInstance old, BundleContext bundleContext) throws Exception { - NetconfConnectorModule module = (NetconfConnectorModule) super.createModule(instanceName, dependencyResolver, + public Module createModule(final String instanceName, final DependencyResolver dependencyResolver, + final DynamicMBeanWithInstance old, final BundleContext bundleContext) throws Exception { + final NetconfConnectorModule module = (NetconfConnectorModule) super.createModule(instanceName, dependencyResolver, old, bundleContext); + module.setBundleContext(bundleContext); + module.setSchemaRegistry(repository); + module.setSchemaContextFactory(schemaContextFactory); return module; } @Override - public Module createModule(String instanceName, DependencyResolver dependencyResolver, BundleContext bundleContext) { - NetconfConnectorModule module = (NetconfConnectorModule) super.createModule(instanceName, dependencyResolver, + public Module createModule(final String instanceName, final DependencyResolver dependencyResolver, final BundleContext bundleContext) { + final NetconfConnectorModule module = (NetconfConnectorModule) super.createModule(instanceName, dependencyResolver, bundleContext); module.setBundleContext(bundleContext); + module.setSchemaRegistry(repository); + module.setSchemaContextFactory(schemaContextFactory); return module; } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java index b2845d5533..269c4af82f 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/api/RemoteDeviceHandler.java @@ -9,11 +9,11 @@ package org.opendaylight.controller.sal.connect.api; import org.opendaylight.controller.sal.core.api.RpcImplementation; import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; public interface RemoteDeviceHandler extends AutoCloseable { - void onDeviceConnected(SchemaContextProvider remoteSchemaContextProvider, + void onDeviceConnected(SchemaContext remoteSchemaContext, PREF netconfSessionPreferences, RpcImplementation deviceRpc); void onDeviceDisconnected(); 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 index 350132cf99..cc9eb5a851 100644 --- 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 @@ -7,41 +7,47 @@ */ package org.opendaylight.controller.sal.connect.netconf; -import java.io.InputStream; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import java.util.Collection; import java.util.LinkedList; import java.util.List; +import java.util.Set; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; - import org.opendaylight.controller.netconf.api.NetconfMessage; -import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.opendaylight.controller.sal.connect.api.MessageTransformer; import org.opendaylight.controller.sal.connect.api.RemoteDevice; import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator; import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler; -import org.opendaylight.controller.sal.connect.api.SchemaContextProviderFactory; -import org.opendaylight.controller.sal.connect.api.SchemaSourceProviderFactory; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities; import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc; -import org.opendaylight.controller.sal.connect.netconf.schema.NetconfDeviceSchemaProviderFactory; -import org.opendaylight.controller.sal.connect.netconf.schema.NetconfRemoteSchemaSourceProvider; -import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer; +import org.opendaylight.controller.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider; import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; -import org.opendaylight.controller.sal.core.api.RpcImplementation; -import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; -import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider; -import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; - /** * This is a mediator between NetconfDeviceCommunicator and NetconfDeviceSalFacade */ @@ -49,51 +55,33 @@ public final class NetconfDevice implements RemoteDevice QNAME_TO_SOURCE_ID_FUNCTION = new Function() { + @Override + public SourceIdentifier apply(final QName input) { + return new SourceIdentifier(input.getLocalName(), Optional.fromNullable(input.getFormattedRevision())); + } + }; + private final RemoteDeviceId id; + private final SchemaContextFactory schemaContextFactory; private final RemoteDeviceHandler salFacade; private final ListeningExecutorService processingExecutor; + private final SchemaSourceRegistry schemaRegistry; private final MessageTransformer messageTransformer; - private final SchemaContextProviderFactory schemaContextProviderFactory; - private final SchemaSourceProviderFactory sourceProviderFactory; private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver; private final NotificationHandler notificationHandler; + private final List> sourceRegistrations = Lists.newArrayList(); - public static NetconfDevice createNetconfDevice(final RemoteDeviceId id, - final AbstractCachingSchemaSourceProvider schemaSourceProvider, - final ExecutorService executor, final RemoteDeviceHandler salFacade) { - return createNetconfDevice(id, schemaSourceProvider, executor, salFacade, new NetconfStateSchemas.NetconfStateSchemasResolverImpl()); - } - - @VisibleForTesting - protected static NetconfDevice createNetconfDevice(final RemoteDeviceId id, - final AbstractCachingSchemaSourceProvider schemaSourceProvider, - final ExecutorService executor, final RemoteDeviceHandler salFacade, - final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) { - - return new NetconfDevice(id, salFacade, executor, new NetconfMessageTransformer(), - new NetconfDeviceSchemaProviderFactory(id), new SchemaSourceProviderFactory() { - @Override - public SchemaSourceProvider createSourceProvider(final RpcImplementation deviceRpc) { - return schemaSourceProvider.createInstanceFor(new NetconfRemoteSchemaSourceProvider(id, - deviceRpc)); - } - }, stateSchemasResolver); - } - - @VisibleForTesting - protected NetconfDevice(final RemoteDeviceId id, final RemoteDeviceHandler salFacade, - final ExecutorService processingExecutor, final MessageTransformer messageTransformer, - final SchemaContextProviderFactory schemaContextProviderFactory, - final SchemaSourceProviderFactory sourceProviderFactory, - final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) { + public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler salFacade, + final ExecutorService globalProcessingExecutor, final MessageTransformer messageTransformer) { this.id = id; + this.schemaRegistry = schemaResourcesDTO.getSchemaRegistry(); this.messageTransformer = messageTransformer; + this.schemaContextFactory = schemaResourcesDTO.getSchemaContextFactory(); this.salFacade = salFacade; - this.sourceProviderFactory = sourceProviderFactory; - this.stateSchemasResolver = stateSchemasResolver; - this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor); - this.schemaContextProviderFactory = schemaContextProviderFactory; + this.stateSchemasResolver = schemaResourcesDTO.getStateSchemasResolver(); + this.processingExecutor = MoreExecutors.listeningDecorator(globalProcessingExecutor); this.notificationHandler = new NotificationHandler(salFacade, messageTransformer, id); } @@ -107,60 +95,73 @@ public final class NetconfDevice implements RemoteDevice salInitializationFuture = processingExecutor.submit(new Runnable() { + final NetconfDeviceRpc deviceRpc = setUpDeviceRpc(listener); + + final DeviceSourcesResolver task = new DeviceSourcesResolver(deviceRpc, remoteSessionCapabilities, id, stateSchemasResolver); + final ListenableFuture sourceResolverFuture = processingExecutor.submit(task); + + final FutureCallback resolvedSourceCallback = new FutureCallback() { @Override - public void run() { - final NetconfDeviceRpc deviceRpc = setUpDeviceRpc(remoteSessionCapabilities, listener); - - final NetconfStateSchemas availableSchemas = stateSchemasResolver.resolve(deviceRpc, remoteSessionCapabilities, id); - logger.warn("{}: Schemas exposed by ietf-netconf-monitoring: {}", id, availableSchemas.getAvailableYangSchemasQNames()); - // TODO use this for shared schema context - - final SchemaSourceProvider delegate = sourceProviderFactory.createSourceProvider(deviceRpc); - final SchemaContextProvider schemaContextProvider = setUpSchemaContext(delegate, remoteSessionCapabilities); - updateMessageTransformer(schemaContextProvider); - salFacade.onDeviceConnected(schemaContextProvider, remoteSessionCapabilities, deviceRpc); - notificationHandler.onRemoteSchemaUp(); + public void onSuccess(final DeviceSources result) { + addProvidedSourcesToSchemaRegistry(deviceRpc, result); + setUpSchema(result); } - }); - Futures.addCallback(salInitializationFuture, new FutureCallback() { - @Override - public void onSuccess(final Object result) { - logger.debug("{}: Initialization in sal successful", id); - logger.info("{}: Netconf connector initialized successfully", id); + private void setUpSchema(final DeviceSources result) { + processingExecutor.submit(new RecursiveSchemaSetup(result, remoteSessionCapabilities, deviceRpc, listener)); } @Override public void onFailure(final Throwable t) { - // Unable to initialize device, set as disconnected - logger.error("{}: Initialization failed", id, t); - salFacade.onDeviceDisconnected(); - // TODO ssh connection is still open if sal initialization fails + logger.warn("{}: Unexpected error resolving device sources: {}", id, t); + handleSalInitializationFailure(t, listener); } - }); + }; + + Futures.addCallback(sourceResolverFuture, resolvedSourceCallback); + } + + private void handleSalInitializationSuccess(final SchemaContext result, final NetconfSessionCapabilities remoteSessionCapabilities, final NetconfDeviceRpc deviceRpc) { + updateMessageTransformer(result); + salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc); + notificationHandler.onRemoteSchemaUp(); + + logger.debug("{}: Initialization in sal successful", id); + logger.info("{}: Netconf connector initialized successfully", id); + } + + private void handleSalInitializationFailure(final Throwable t, final RemoteDeviceCommunicator listener) { + logger.error("{}: Initialization in sal failed, disconnecting from device", id, t); + listener.close(); + onRemoteSessionDown(); } /** * Update initial message transformer to use retrieved schema + * @param currentSchemaContext */ - private void updateMessageTransformer(final SchemaContextProvider schemaContextProvider) { - messageTransformer.onGlobalContextUpdated(schemaContextProvider.getSchemaContext()); + private void updateMessageTransformer(final SchemaContext currentSchemaContext) { + messageTransformer.onGlobalContextUpdated(currentSchemaContext); } - private SchemaContextProvider setUpSchemaContext(final SchemaSourceProvider sourceProvider, final NetconfSessionCapabilities capabilities) { - return schemaContextProviderFactory.createContextProvider(capabilities.getModuleBasedCaps(), sourceProvider); + private void addProvidedSourcesToSchemaRegistry(final NetconfDeviceRpc deviceRpc, final DeviceSources deviceSources) { + final NetconfRemoteSchemaYangSourceProvider yangProvider = new NetconfRemoteSchemaYangSourceProvider(id, deviceRpc); + for (final SourceIdentifier sourceId : deviceSources.getProvidedSources()) { + sourceRegistrations.add(schemaRegistry.registerSchemaSource(yangProvider, + PotentialSchemaSource.create(sourceId, YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue()))); + } } - private NetconfDeviceRpc setUpDeviceRpc(final NetconfSessionCapabilities capHolder, final RemoteDeviceCommunicator listener) { - Preconditions.checkArgument(capHolder.isMonitoringSupported(), - "%s: Netconf device does not support netconf monitoring, yang schemas cannot be acquired. Netconf device capabilities", capHolder); - return new NetconfDeviceRpc(listener, messageTransformer); + private NetconfDeviceRpc setUpDeviceRpc(final RemoteDeviceCommunicator listener) { + return new NetconfDeviceRpc(listener, messageTransformer); } @Override public void onRemoteSessionDown() { salFacade.onDeviceDisconnected(); + for (final SchemaSourceRegistration sourceRegistration : sourceRegistrations) { + sourceRegistration.close(); + } } @Override @@ -169,59 +170,181 @@ public final class NetconfDevice implements RemoteDevice salFacade; - private final List cache = new LinkedList<>(); - private final MessageTransformer messageTransformer; - private boolean passNotifications = false; + public NetconfStateSchemas.NetconfStateSchemasResolver getStateSchemasResolver() { + return stateSchemasResolver; + } + } + + /** + * Schema building callable. + */ + private static class DeviceSourcesResolver implements Callable { + private final NetconfDeviceRpc deviceRpc; + private final NetconfSessionCapabilities remoteSessionCapabilities; private final RemoteDeviceId id; + private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver; - NotificationHandler(final RemoteDeviceHandler salFacade, final MessageTransformer messageTransformer, final RemoteDeviceId id) { - this.salFacade = salFacade; - this.messageTransformer = messageTransformer; + public DeviceSourcesResolver(final NetconfDeviceRpc deviceRpc, final NetconfSessionCapabilities remoteSessionCapabilities, final RemoteDeviceId id, final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) { + this.deviceRpc = deviceRpc; + this.remoteSessionCapabilities = remoteSessionCapabilities; this.id = id; + this.stateSchemasResolver = stateSchemasResolver; } - synchronized void handleNotification(final NetconfMessage notification) { - if(passNotifications) { - passNotification(messageTransformer.toNotification(notification)); - } else { - cacheNotification(notification); + @Override + public DeviceSources call() throws Exception { + + final Set requiredSources = Sets.newHashSet(Collections2.transform( + remoteSessionCapabilities.getModuleBasedCaps(), QNAME_TO_SOURCE_ID_FUNCTION)); + + // If monitoring is not supported, we will still attempt to create schema, sources might be already provided + final NetconfStateSchemas availableSchemas = stateSchemasResolver.resolve(deviceRpc, remoteSessionCapabilities, id); + logger.debug("{}: Schemas exposed by ietf-netconf-monitoring: {}", id, availableSchemas.getAvailableYangSchemasQNames()); + + final Set providedSources = Sets.newHashSet(Collections2.transform( + availableSchemas.getAvailableYangSchemasQNames(), QNAME_TO_SOURCE_ID_FUNCTION)); + + final Set requiredSourcesNotProvided = Sets.difference(requiredSources, providedSources); + + if (!requiredSourcesNotProvided.isEmpty()) { + logger.warn("{}: Netconf device does not provide all yang models reported in hello message capabilities, required but not provided: {}", + id, requiredSourcesNotProvided); + logger.warn("{}: Attempting to build schema context from required sources", id); } - } - /** - * Forward all cached notifications and pass all notifications from this point directly to sal facade. - */ - synchronized void onRemoteSchemaUp() { - passNotifications = true; - for (final NetconfMessage cachedNotification : cache) { - passNotification(messageTransformer.toNotification(cachedNotification)); + // TODO should we perform this ? We have a mechanism to fix initialization of devices not reporting or required modules in hello + // That is overriding capabilities in configuration using attribute yang-module-capabilities + // This is more user friendly even though it clashes with attribute yang-module-capabilities + // Some devices do not report all required models in hello message, but provide them + final Set providedSourcesNotRequired = Sets.difference(providedSources, requiredSources); + if (!providedSourcesNotRequired.isEmpty()) { + logger.warn("{}: Netconf device provides additional yang models not reported in hello message capabilities: {}", + id, providedSourcesNotRequired); + logger.warn("{}: Adding provided but not required sources as required to prevent failures", id); + requiredSources.addAll(providedSourcesNotRequired); } - cache.clear(); + return new DeviceSources(requiredSources, providedSources); } + } - private void cacheNotification(final NetconfMessage notification) { - Preconditions.checkState(passNotifications == false); + /** + * Contains RequiredSources - sources from capabilities. + * + */ + private static final class DeviceSources { + private final Collection requiredSources; + private final Collection providedSources; - logger.debug("{}: Caching notification {}, remote schema not yet fully built", id, notification); - if(logger.isTraceEnabled()) { - logger.trace("{}: Caching notification {}", id, XmlUtil.toString(notification.getDocument())); - } + public DeviceSources(final Collection requiredSources, final Collection providedSources) { + this.requiredSources = requiredSources; + this.providedSources = providedSources; + } - cache.add(notification); + public Collection getRequiredSources() { + return requiredSources; } - private void passNotification(final CompositeNode parsedNotification) { - logger.debug("{}: Forwarding notification {}", id, parsedNotification); - Preconditions.checkNotNull(parsedNotification); - salFacade.onNotification(parsedNotification); + public Collection getProvidedSources() { + return providedSources; } + } + /** + * Schema builder that tries to build schema context from provided sources or biggest subset of it. + */ + private final class RecursiveSchemaSetup implements Runnable { + private final DeviceSources deviceSources; + private final NetconfSessionCapabilities remoteSessionCapabilities; + private final NetconfDeviceRpc deviceRpc; + private final RemoteDeviceCommunicator listener; + + public RecursiveSchemaSetup(final DeviceSources deviceSources, final NetconfSessionCapabilities remoteSessionCapabilities, final NetconfDeviceRpc deviceRpc, final RemoteDeviceCommunicator listener) { + this.deviceSources = deviceSources; + this.remoteSessionCapabilities = remoteSessionCapabilities; + this.deviceRpc = deviceRpc; + this.listener = listener; + } + + @Override + public void run() { + setUpSchema(deviceSources.getRequiredSources()); + } + + /** + * Recursively build schema context, in case of success or final failure notify device + */ + private void setUpSchema(final Collection requiredSources) { + logger.trace("{}: Trying to build schema context from {}", id, requiredSources); + + // If no more sources, fail + if(requiredSources.isEmpty()) { + handleSalInitializationFailure(new IllegalStateException(id + ": No more sources for schema context"), listener); + return; + } + + final CheckedFuture schemaBuilderFuture = schemaContextFactory.createSchemaContext(requiredSources); + + final FutureCallback RecursiveSchemaBuilderCallback = new FutureCallback() { + + @Override + public void onSuccess(final SchemaContext result) { + logger.debug("{}: Schema context built successfully from {}", id, requiredSources); + handleSalInitializationSuccess(result, remoteSessionCapabilities, deviceRpc); + } + + @Override + public void onFailure(final Throwable t) { + // In case source missing, try without it + if (t instanceof MissingSchemaSourceException) { + final SourceIdentifier missingSource = ((MissingSchemaSourceException) t).getSourceId(); + logger.warn("{}: Unable to build schema context, missing source {}, will reattempt without it", id, missingSource); + setUpSchema(stripMissingSource(requiredSources, missingSource)); + + // In case resolution error, try only with resolved sources + } else if (t instanceof SchemaResolutionException) { + // TODO check for infinite loop + final SchemaResolutionException resolutionException = (SchemaResolutionException) t; + logger.warn("{}: Unable to build schema context, unsatisfied imports {}, will reattempt with resolved only", id, resolutionException.getUnsatisfiedImports()); + setUpSchema(resolutionException.getResolvedSources()); + // unknown error, fail + } else { + handleSalInitializationFailure(t, listener); + } + } + }; + + Futures.addCallback(schemaBuilderFuture, RecursiveSchemaBuilderCallback); + } + + private Collection stripMissingSource(final Collection requiredSources, final SourceIdentifier sIdToRemove) { + final LinkedList sourceIdentifiers = Lists.newLinkedList(requiredSources); + final boolean removed = sourceIdentifiers.remove(sIdToRemove); + Preconditions.checkState(removed, "{}: Trying to remove {} from {} failed", id, sIdToRemove, requiredSources); + return sourceIdentifiers; + } + } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java index b5400347e7..77e342641e 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java @@ -93,7 +93,7 @@ public final class NetconfStateSchemas { */ private static NetconfStateSchemas create(final NetconfDeviceRpc deviceRpc, final NetconfSessionCapabilities remoteSessionCapabilities, final RemoteDeviceId id) { if(remoteSessionCapabilities.isMonitoringSupported() == false) { - logger.warn("{}: Netconf monitoring not supported on device, cannot detect available schemas"); + logger.warn("{}: Netconf monitoring not supported on device, cannot detect provided schemas"); return EMPTY; } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NotificationHandler.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NotificationHandler.java new file mode 100644 index 0000000000..cc8960fb4f --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NotificationHandler.java @@ -0,0 +1,78 @@ +/* + * 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.Preconditions; +import java.util.LinkedList; +import java.util.List; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.controller.sal.connect.api.MessageTransformer; +import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handles incoming notifications. Either caches them(until onRemoteSchemaUp is called) or passes to sal Facade. + */ +final class NotificationHandler { + + private static final Logger logger = LoggerFactory.getLogger(NotificationHandler.class); + + private final RemoteDeviceHandler salFacade; + private final List queue = new LinkedList<>(); + private final MessageTransformer messageTransformer; + private final RemoteDeviceId id; + private boolean passNotifications = false; + + NotificationHandler(final RemoteDeviceHandler salFacade, final MessageTransformer messageTransformer, final RemoteDeviceId id) { + this.salFacade = Preconditions.checkNotNull(salFacade); + this.messageTransformer = Preconditions.checkNotNull(messageTransformer); + this.id = Preconditions.checkNotNull(id); + } + + synchronized void handleNotification(final NetconfMessage notification) { + if(passNotifications) { + passNotification(messageTransformer.toNotification(notification)); + } else { + queueNotification(notification); + } + } + + /** + * Forward all cached notifications and pass all notifications from this point directly to sal facade. + */ + synchronized void onRemoteSchemaUp() { + passNotifications = true; + + for (final NetconfMessage cachedNotification : queue) { + passNotification(messageTransformer.toNotification(cachedNotification)); + } + + queue.clear(); + } + + private void queueNotification(final NetconfMessage notification) { + Preconditions.checkState(passNotifications == false); + + logger.debug("{}: Caching notification {}, remote schema not yet fully built", id, notification); + if(logger.isTraceEnabled()) { + logger.trace("{}: Caching notification {}", id, XmlUtil.toString(notification.getDocument())); + } + + queue.add(notification); + } + + private void passNotification(final CompositeNode parsedNotification) { + logger.debug("{}: Forwarding notification {}", id, parsedNotification); + Preconditions.checkNotNull(parsedNotification); + salFacade.onNotification(parsedNotification); + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java index 2f24adcdbe..aadb911f45 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java @@ -51,8 +51,10 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, private final RemoteDeviceId id; private final Lock sessionLock = new ReentrantLock(); + // TODO implement concurrent message limit private final Queue requests = new ArrayDeque<>(); private NetconfClientSession session; + private Future initFuture; public NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice remoteDevice, final NetconfSessionCapabilities netconfSessionCapabilities) { @@ -97,9 +99,9 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, public void initializeRemoteConnection(final NetconfClientDispatcher dispatch, final NetconfClientConfiguration config) { if(config instanceof NetconfReconnectingClientConfiguration) { - dispatch.createReconnectingClient((NetconfReconnectingClientConfiguration) config); + initFuture = dispatch.createReconnectingClient((NetconfReconnectingClientConfiguration) config); } else { - dispatch.createClient(config); + initFuture = dispatch.createClient(config); } } @@ -172,7 +174,15 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, @Override public void close() { - tearDown( String.format( "The netconf session to %1$s has been closed", id.getName() ) ); + // Cancel reconnect if in progress + if(initFuture != null) { + initFuture.cancel(false); + } + // Disconnect from device + if(session != null) { + session.close(); + } + tearDown(id + ": Netconf session closed"); } @Override @@ -191,12 +201,12 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, private void processMessage(final NetconfMessage message) { Request request = null; sessionLock.lock(); + try { request = requests.peek(); - if (request.future.isUncancellable()) { + if (request != null && request.future.isUncancellable()) { requests.poll(); - } - else { + } else { request = null; logger.warn("{}: Ignoring unsolicited message {}", id, msgToS(message)); } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java index dbef290197..3cc513600d 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceSalFacade.java @@ -7,11 +7,12 @@ */ package org.opendaylight.controller.sal.connect.netconf.sal; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; - import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler; @@ -30,14 +31,10 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDeviceHandler { private static final Logger logger= LoggerFactory.getLogger(NetconfDeviceSalFacade.class); @@ -64,11 +61,9 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice } @Override - public synchronized void onDeviceConnected(final SchemaContextProvider remoteSchemaContextProvider, + public synchronized void onDeviceConnected(final SchemaContext schemaContext, final NetconfSessionCapabilities netconfSessionPreferences, final RpcImplementation deviceRpc) { - final SchemaContext schemaContext = remoteSchemaContextProvider.getSchemaContext(); - // TODO remove deprecated SchemaContextProvider from SchemaAwareRpcBroker // TODO move SchemaAwareRpcBroker from sal-broker-impl, now we have depend on the whole sal-broker-impl final RpcProvisionRegistry rpcRegistry = new SchemaAwareRpcBroker(id.getPath().toString(), new org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProvider() { @Override diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java index 70d4f43365..6c46bed762 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java @@ -57,7 +57,7 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction final ListenableFuture> future = rpc.invokeRpc(NETCONF_GET_CONFIG_QNAME, NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, toFilterStructure(path))); - ListenableFuture>> transformedFuture = Futures.transform(future, new Function, Optional>>() { + final ListenableFuture>> transformedFuture = Futures.transform(future, new Function, Optional>>() { @Override public Optional> apply(final RpcResult result) { checkReadSuccess(result, path); @@ -99,7 +99,7 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction final YangInstanceIdentifier path) { final ListenableFuture> future = rpc.invokeRpc(NETCONF_GET_QNAME, NetconfMessageTransformUtil.wrap(NETCONF_GET_QNAME, toFilterStructure(path))); - ListenableFuture>> transformedFuture = Futures.transform(future, new Function, Optional>>() { + final ListenableFuture>> transformedFuture = Futures.transform(future, new Function, Optional>>() { @Override public Optional> apply(final RpcResult result) { checkReadSuccess(result, path); diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfDeviceSchemaProviderFactory.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfDeviceSchemaProviderFactory.java deleted file mode 100644 index e7d64646ea..0000000000 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfDeviceSchemaProviderFactory.java +++ /dev/null @@ -1,81 +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.schema; - -import java.io.InputStream; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import org.opendaylight.controller.sal.connect.api.SchemaContextProviderFactory; -import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; -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.Preconditions; - -public final class NetconfDeviceSchemaProviderFactory implements SchemaContextProviderFactory { - - private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceSchemaProviderFactory.class); - - private final RemoteDeviceId id; - - public NetconfDeviceSchemaProviderFactory(final RemoteDeviceId id) { - this.id = id; - } - - @Override - public SchemaContextProvider createContextProvider(final Collection capabilities, final SchemaSourceProvider sourceProvider) { - - final YangSourceContext sourceContext = YangSourceContext.createFrom(capabilities, sourceProvider); - - if (sourceContext.getMissingSources().isEmpty() == false) { - logger.warn("{}: Sources for following models are missing {}", id, sourceContext.getMissingSources()); - } - - logger.debug("{}: Trying to create schema context from {}", id, sourceContext.getValidSources()); - final List modelsToParse = YangSourceContext.getValidInputStreams(sourceContext); - - Preconditions.checkState(sourceContext.getValidSources().isEmpty() == false, - "%s: Unable to create schema context, no sources provided by device", id); - try { - final SchemaContext schemaContext = tryToParseContext(modelsToParse); - logger.debug("{}: Schema context successfully created.", id); - return new NetconfSchemaContextProvider(schemaContext); - } catch (final RuntimeException e) { - logger.error("{}: Unable to create schema context, unexpected error", id, e); - throw new IllegalStateException(id + ": Unable to create schema context", e); - } - } - - private static SchemaContext tryToParseContext(final List modelsToParse) { - final YangParserImpl parser = new YangParserImpl(); - final Set models = parser.parseYangModelsFromStreams(modelsToParse); - return parser.resolveSchemaContext(models); - } - - private static final class NetconfSchemaContextProvider implements SchemaContextProvider { - private final SchemaContext schemaContext; - - public NetconfSchemaContextProvider(final SchemaContext schemaContext) { - this.schemaContext = schemaContext; - } - - @Override - public SchemaContext getSchemaContext() { - return schemaContext; - } - } -} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaSourceProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaSourceProvider.java deleted file mode 100644 index 44ff2ef985..0000000000 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaSourceProvider.java +++ /dev/null @@ -1,93 +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.schema; - -import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; -import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; -import org.opendaylight.controller.sal.core.api.RpcImplementation; -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 org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; - -public final class NetconfRemoteSchemaSourceProvider implements SchemaSourceProvider { - - public static final QName GET_SCHEMA_QNAME = QName.create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING, - "get-schema"); - public static final QName GET_DATA_QNAME = QName - .create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING, "data"); - - private static final Logger logger = LoggerFactory.getLogger(NetconfRemoteSchemaSourceProvider.class); - - private final RpcImplementation rpc; - private final RemoteDeviceId id; - - public NetconfRemoteSchemaSourceProvider(final RemoteDeviceId id, final RpcImplementation rpc) { - this.id = id; - this.rpc = Preconditions.checkNotNull(rpc); - } - - @Override - public Optional getSchemaSource(final String moduleName, final Optional revision) { - final ImmutableCompositeNode getSchemaRequest = createGetSchemaRequest(moduleName, revision); - - logger.trace("{}: Loading YANG schema source for {}:{}", id, moduleName, revision); - try { - final RpcResult schemaReply = rpc.invokeRpc(GET_SCHEMA_QNAME, getSchemaRequest).get(); - if (schemaReply.isSuccessful()) { - final Optional schemaBody = getSchemaFromRpc(id, schemaReply.getResult()); - if (schemaBody.isPresent()) { - logger.debug("{}: YANG Schema successfully retrieved for {}:{}", id, moduleName, revision); - return schemaBody; - } - } else { - logger.warn("{}: YANG schema was not successfully retrieved for {}:{}. Errors: {}", id, moduleName, - revision, schemaReply.getErrors()); - } - return Optional.absent(); - } catch (final InterruptedException e){ - Thread.currentThread().interrupt(); - throw new IllegalStateException(e); - } catch (final Exception e) { - logger.error("{}: YANG schema was not successfully retrieved for {}:{}", id, moduleName, revision, e); - throw new IllegalStateException(e); - } - } - - private ImmutableCompositeNode createGetSchemaRequest(final String moduleName, final Optional revision) { - final CompositeNodeBuilder request = ImmutableCompositeNode.builder(); - request.setQName(GET_SCHEMA_QNAME).addLeaf("identifier", moduleName); - if (revision.isPresent()) { - request.addLeaf("version", revision.get()); - } - request.addLeaf("format", "yang"); - return request.toInstance(); - } - - private static Optional getSchemaFromRpc(final RemoteDeviceId id, final CompositeNode result) { - if (result == null) { - return Optional.absent(); - } - final SimpleNode simpleNode = result.getFirstSimpleByName(GET_DATA_QNAME.withoutRevision()); - - Preconditions.checkNotNull(simpleNode, - "%s Unexpected response to get-schema, expected response with one child %s, but was %s", - id, GET_DATA_QNAME.withoutRevision(), result); - - final Object potential = simpleNode.getValue(); - return potential instanceof String ? Optional.of((String) potential) : Optional.absent(); - } -} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java new file mode 100644 index 0000000000..dc90fd3826 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/NetconfRemoteSchemaYangSourceProvider.java @@ -0,0 +1,179 @@ +/* + * 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.schema; + +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.ExecutionException; +import org.apache.commons.io.IOUtils; +import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.controller.sal.core.api.RpcImplementation; +import org.opendaylight.yangtools.util.concurrent.ExceptionMapper; +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.repo.api.SchemaSourceException; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class NetconfRemoteSchemaYangSourceProvider implements SchemaSourceProvider { + + public static final QName GET_SCHEMA_QNAME = QName.create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING,"get-schema"); + public static final QName GET_DATA_QNAME = QName.create(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING, "data"); + + private static final Logger logger = LoggerFactory.getLogger(NetconfRemoteSchemaYangSourceProvider.class); + + private static final ExceptionMapper MAPPER = new ExceptionMapper( + "schemaDownload", SchemaSourceException.class) { + @Override + protected SchemaSourceException newWithCause(final String s, final Throwable throwable) { + return new SchemaSourceException(s, throwable); + } + }; + + private final RpcImplementation rpc; + private final RemoteDeviceId id; + + public NetconfRemoteSchemaYangSourceProvider(final RemoteDeviceId id, final RpcImplementation rpc) { + this.id = id; + this.rpc = Preconditions.checkNotNull(rpc); + } + + private ImmutableCompositeNode createGetSchemaRequest(final String moduleName, final Optional revision) { + final CompositeNodeBuilder request = ImmutableCompositeNode.builder(); + request.setQName(GET_SCHEMA_QNAME).addLeaf("identifier", moduleName); + if (revision.isPresent()) { + request.addLeaf("version", revision.get()); + } + request.addLeaf("format", "yang"); + return request.toInstance(); + } + + private static Optional getSchemaFromRpc(final RemoteDeviceId id, final CompositeNode result) { + if (result == null) { + return Optional.absent(); + } + final SimpleNode simpleNode = result.getFirstSimpleByName(GET_DATA_QNAME.withoutRevision()); + + Preconditions.checkNotNull(simpleNode, + "%s Unexpected response to get-schema, expected response with one child %s, but was %s", id, + GET_DATA_QNAME.withoutRevision(), result); + + final Object potential = simpleNode.getValue(); + return potential instanceof String ? Optional.of((String) potential) : Optional. absent(); + } + + @Override + public CheckedFuture getSource(final SourceIdentifier sourceIdentifier) { + final String moduleName = sourceIdentifier.getName(); + + // If formatted revision is SourceIdentifier.NOT_PRESENT_FORMATTED_REVISION, we have to omit it from request + final String formattedRevision = sourceIdentifier.getRevision().equals(SourceIdentifier.NOT_PRESENT_FORMATTED_REVISION) ? null : sourceIdentifier.getRevision(); + final Optional revision = Optional.fromNullable(formattedRevision); + final ImmutableCompositeNode getSchemaRequest = createGetSchemaRequest(moduleName, revision); + + logger.trace("{}: Loading YANG schema source for {}:{}", id, moduleName, revision); + + final ListenableFuture transformed = Futures.transform( + rpc.invokeRpc(GET_SCHEMA_QNAME, getSchemaRequest), + new ResultToYangSourceTransformer(id, sourceIdentifier, moduleName, revision)); + + // FIXME remove this get, it is only present to wait until source is retrieved + // (goal is to limit concurrent schema download, since NetconfDevice listener does not handle concurrent messages properly) + try { + logger.trace("{}: Blocking for {}", id, sourceIdentifier); + transformed.get(); + } catch (final InterruptedException e) { + throw new RuntimeException(e); + } catch (final ExecutionException e) { + throw new IllegalStateException(id + ": Failed while getting source: " + sourceIdentifier, e); + } + + return Futures.makeChecked(transformed, MAPPER); + } + + /** + * Transform composite node to string schema representation and then to ASTSchemaSource + */ + private static final class ResultToYangSourceTransformer implements + Function, YangTextSchemaSource> { + + private final RemoteDeviceId id; + private final SourceIdentifier sourceIdentifier; + private final String moduleName; + private final Optional revision; + + public ResultToYangSourceTransformer(final RemoteDeviceId id, final SourceIdentifier sourceIdentifier, + final String moduleName, final Optional revision) { + this.id = id; + this.sourceIdentifier = sourceIdentifier; + this.moduleName = moduleName; + this.revision = revision; + } + + @Override + public YangTextSchemaSource apply(final RpcResult input) { + + if (input.isSuccessful()) { + + final Optional schemaString = getSchemaFromRpc(id, input.getResult()); + + Preconditions.checkState(schemaString.isPresent(), + "%s: Unexpected response to get-schema, schema not present in message for: %s", id, sourceIdentifier); + + logger.debug("{}: YANG Schema successfully retrieved for {}:{}", id, moduleName, revision); + + return new NetconfYangTextSchemaSource(id, sourceIdentifier, schemaString); + } + + logger.warn("{}: YANG schema was not successfully retrieved for {}. Errors: {}", id, sourceIdentifier, + input.getErrors()); + + throw new IllegalStateException(String.format( + "%s: YANG schema was not successfully retrieved for %s. Errors: %s", id, sourceIdentifier, + input.getErrors())); + + } + + } + + private static class NetconfYangTextSchemaSource extends YangTextSchemaSource { + private final RemoteDeviceId id; + private final Optional schemaString; + + public NetconfYangTextSchemaSource(final RemoteDeviceId id, final SourceIdentifier sId, final Optional schemaString) { + super(sId); + this.id = id; + this.schemaString = schemaString; + } + + @Override + protected Objects.ToStringHelper addToStringAttributes(final Objects.ToStringHelper toStringHelper) { + return toStringHelper.add("device", id); + } + + @Override + public InputStream openStream() throws IOException { + return IOUtils.toInputStream(schemaString.get()); + } + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java index 1e3cf4b6fc..893a45aaa2 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java @@ -49,6 +49,8 @@ import org.w3c.dom.Element; public class NetconfMessageTransformUtil { + public static final String MESSAGE_ID_ATTR = "message-id"; + private NetconfMessageTransformUtil() {} public static final QName IETF_NETCONF_MONITORING = QName.create(NetconfState.QNAME, "ietf-netconf-monitoring"); @@ -125,8 +127,8 @@ public class NetconfMessageTransformUtil { public static void checkValidReply(final NetconfMessage input, final NetconfMessage output) throws NetconfDocumentedException { - final String inputMsgId = input.getDocument().getDocumentElement().getAttribute("message-id"); - final String outputMsgId = output.getDocument().getDocumentElement().getAttribute("message-id"); + final String inputMsgId = input.getDocument().getDocumentElement().getAttribute(MESSAGE_ID_ATTR); + final String outputMsgId = output.getDocument().getDocumentElement().getAttribute(MESSAGE_ID_ATTR); if(inputMsgId.equals(outputMsgId) == false) { Map errorInfo = ImmutableMap.builder() diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java index fa488dadd3..218ec0be8d 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java @@ -8,14 +8,18 @@ package org.opendaylight.controller.sal.connect.netconf; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyCollectionOf; import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import com.google.common.base.Optional; +import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; import java.io.InputStream; @@ -28,12 +32,13 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.Test; import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; import org.opendaylight.controller.sal.connect.api.MessageTransformer; import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator; import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler; -import org.opendaylight.controller.sal.connect.api.SchemaContextProviderFactory; import org.opendaylight.controller.sal.connect.api.SchemaSourceProviderFactory; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities; import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc; @@ -45,8 +50,15 @@ import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.ModuleImport; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; +import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; @@ -70,7 +82,13 @@ public class NetconfDeviceTest { public static final String TEST_NAMESPACE = "test:namespace"; public static final String TEST_MODULE = "test-module"; public static final String TEST_REVISION = "2013-07-22"; - private NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver = new NetconfStateSchemas.NetconfStateSchemasResolver() { + public static final SourceIdentifier TEST_SID = new SourceIdentifier(TEST_MODULE, Optional.of(TEST_REVISION)); + public static final String TEST_CAPABILITY = TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION; + + public static final SourceIdentifier TEST_SID2 = new SourceIdentifier(TEST_MODULE + "2", Optional.of(TEST_REVISION)); + public static final String TEST_CAPABILITY2 = TEST_NAMESPACE + "?module=" + TEST_MODULE + "2" + "&revision=" + TEST_REVISION; + + private static final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver = new NetconfStateSchemas.NetconfStateSchemasResolver() { @Override public NetconfStateSchemas resolve(final NetconfDeviceRpc deviceRpc, final NetconfSessionCapabilities remoteSessionCapabilities, final RemoteDeviceId id) { @@ -79,14 +97,71 @@ public class NetconfDeviceTest { }; @Test - public void testNetconfDeviceWithoutMonitoring() throws Exception { + public void testNetconfDeviceFailFirstSchemaFailSecondEmpty() throws Exception { + final ArrayList capList = Lists.newArrayList(TEST_CAPABILITY); + final RemoteDeviceHandler facade = getFacade(); final RemoteDeviceCommunicator listener = getListener(); - final NetconfDevice device = new NetconfDevice(getId(), facade, getExecutor(), getMessageTransformer(), getSchemaContextProviderFactory(), getSourceProviderFactory(), stateSchemasResolver); - device.onRemoteSessionUp(getSessionCaps(false, Collections.emptyList()), listener); + final SchemaContextFactory schemaFactory = getSchemaFactory(); + + // Make fallback attempt to fail due to empty resolved sources + final SchemaResolutionException schemaResolutionException + = new SchemaResolutionException("fail first", + Collections.emptyList(), HashMultimap.create()); + doReturn(Futures.immediateFailedCheckedFuture( + schemaResolutionException)) + .when(schemaFactory).createSchemaContext(anyCollectionOf(SourceIdentifier.class)); + + final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO + = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver); + final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), getMessageTransformer()); + // Monitoring not supported + final NetconfSessionCapabilities sessionCaps = getSessionCaps(false, capList); + device.onRemoteSessionUp(sessionCaps, listener); Mockito.verify(facade, Mockito.timeout(5000)).onDeviceDisconnected(); + Mockito.verify(listener, Mockito.timeout(5000)).close(); + Mockito.verify(schemaFactory, times(1)).createSchemaContext(anyCollectionOf(SourceIdentifier.class)); + } + + @Test + public void testNetconfDeviceMissingSource() throws Exception { + final RemoteDeviceHandler facade = getFacade(); + final RemoteDeviceCommunicator listener = getListener(); + + final SchemaContextFactory schemaFactory = getSchemaFactory(); + + // Make fallback attempt to fail due to empty resolved sources + final MissingSchemaSourceException schemaResolutionException = new MissingSchemaSourceException("fail first", TEST_SID); + doAnswer(new Answer() { + @Override + public Object answer(final InvocationOnMock invocation) throws Throwable { + if(((Collection) invocation.getArguments()[0]).size() == 2) { + return Futures.immediateFailedCheckedFuture(schemaResolutionException); + } else { + return Futures.immediateCheckedFuture(getSchema()); + } + } + }).when(schemaFactory).createSchemaContext(anyCollectionOf(SourceIdentifier.class)); + + final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO + = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver); + final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), getMessageTransformer()); + // Monitoring supported + final NetconfSessionCapabilities sessionCaps = getSessionCaps(true, Lists.newArrayList(TEST_CAPABILITY, TEST_CAPABILITY2)); + device.onRemoteSessionUp(sessionCaps, listener); + + Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class)); + Mockito.verify(schemaFactory, times(2)).createSchemaContext(anyCollectionOf(SourceIdentifier.class)); + } + + private SchemaSourceRegistry getSchemaRegistry() { + final SchemaSourceRegistry mock = mock(SchemaSourceRegistry.class); + final SchemaSourceRegistration mockReg = mock(SchemaSourceRegistration.class); + doNothing().when(mockReg).close(); + doReturn(mockReg).when(mock).registerSchemaSource(any(org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider.class), any(PotentialSchemaSource.class)); + return mock; } @Test @@ -95,7 +170,10 @@ public class NetconfDeviceTest { final RemoteDeviceCommunicator listener = getListener(); final MessageTransformer messageTransformer = getMessageTransformer(); - final NetconfDevice device = new NetconfDevice(getId(), facade, getExecutor(), messageTransformer, getSchemaContextProviderFactory(), getSourceProviderFactory(), stateSchemasResolver); + + final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO + = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), getSchemaFactory(), stateSchemasResolver); + final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), messageTransformer); device.onNotification(netconfMessage); device.onNotification(netconfMessage); @@ -103,7 +181,7 @@ public class NetconfDeviceTest { verify(facade, times(0)).onNotification(any(CompositeNode.class)); final NetconfSessionCapabilities sessionCaps = getSessionCaps(true, - Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION)); + Lists.newArrayList(TEST_CAPABILITY)); device.onRemoteSessionUp(sessionCaps, listener); @@ -120,40 +198,34 @@ public class NetconfDeviceTest { final RemoteDeviceHandler facade = getFacade(); final RemoteDeviceCommunicator listener = getListener(); - final SchemaContextProviderFactory schemaContextProviderFactory = getSchemaContextProviderFactory(); - final SchemaSourceProviderFactory sourceProviderFactory = getSourceProviderFactory(); + final SchemaContextFactory schemaContextProviderFactory = getSchemaFactory(); final MessageTransformer messageTransformer = getMessageTransformer(); - final NetconfDevice device = new NetconfDevice(getId(), facade, getExecutor(), messageTransformer, schemaContextProviderFactory, sourceProviderFactory, stateSchemasResolver); + final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO + = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaContextProviderFactory, stateSchemasResolver); + final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), messageTransformer); final NetconfSessionCapabilities sessionCaps = getSessionCaps(true, Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION)); device.onRemoteSessionUp(sessionCaps, listener); - verify(sourceProviderFactory, timeout(5000)).createSourceProvider(any(RpcImplementation.class)); - verify(schemaContextProviderFactory, timeout(5000)).createContextProvider(any(Collection.class), any(SchemaSourceProvider.class)); + verify(schemaContextProviderFactory, timeout(5000)).createSchemaContext(any(Collection.class)); verify(messageTransformer, timeout(5000)).onGlobalContextUpdated(any(SchemaContext.class)); - verify(facade, timeout(5000)).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class)); + verify(facade, timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class)); device.onRemoteSessionDown(); verify(facade, timeout(5000)).onDeviceDisconnected(); device.onRemoteSessionUp(sessionCaps, listener); - verify(sourceProviderFactory, timeout(5000).times(2)).createSourceProvider(any(RpcImplementation.class)); - verify(schemaContextProviderFactory, timeout(5000).times(2)).createContextProvider(any(Collection.class), any(SchemaSourceProvider.class)); + verify(schemaContextProviderFactory, timeout(5000).times(2)).createSchemaContext(any(Collection.class)); verify(messageTransformer, timeout(5000).times(2)).onGlobalContextUpdated(any(SchemaContext.class)); - verify(facade, timeout(5000).times(2)).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class)); + verify(facade, timeout(5000).times(2)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class)); } - private SchemaContextProviderFactory getSchemaContextProviderFactory() { - final SchemaContextProviderFactory schemaContextProviderFactory = mockClass(SchemaContextProviderFactory.class); - doReturn(new SchemaContextProvider() { - @Override - public SchemaContext getSchemaContext() { - return getSchema(); - } - }).when(schemaContextProviderFactory).createContextProvider(any(Collection.class), any(SchemaSourceProvider.class)); - return schemaContextProviderFactory; + private SchemaContextFactory getSchemaFactory() { + final SchemaContextFactory schemaFactory = mockClass(SchemaContextFactory.class); + doReturn(Futures.immediateCheckedFuture(getSchema())).when(schemaFactory).createSchemaContext(any(Collection.class)); + return schemaFactory; } public static SchemaContext getSchema() { @@ -167,7 +239,7 @@ public class NetconfDeviceTest { private RemoteDeviceHandler getFacade() throws Exception { final RemoteDeviceHandler remoteDeviceHandler = mockCloseableClass(RemoteDeviceHandler.class); - doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class)); + doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class)); doNothing().when(remoteDeviceHandler).onDeviceDisconnected(); doNothing().when(remoteDeviceHandler).onNotification(any(CompositeNode.class)); return remoteDeviceHandler; @@ -190,7 +262,7 @@ public class NetconfDeviceTest { } private static T mockClass(final Class remoteDeviceHandlerClass) { - final T mock = Mockito.mock(remoteDeviceHandlerClass); + final T mock = mock(remoteDeviceHandlerClass); Mockito.doReturn(remoteDeviceHandlerClass.getSimpleName()).when(mock).toString(); return mock; } diff --git a/opendaylight/netconf/netconf-cli/src/main/java/org/opendaylight/controller/netconf/cli/NetconfDeviceConnectionHandler.java b/opendaylight/netconf/netconf-cli/src/main/java/org/opendaylight/controller/netconf/cli/NetconfDeviceConnectionHandler.java index bd092bc5bd..d5c9dc6fc7 100644 --- a/opendaylight/netconf/netconf-cli/src/main/java/org/opendaylight/controller/netconf/cli/NetconfDeviceConnectionHandler.java +++ b/opendaylight/netconf/netconf-cli/src/main/java/org/opendaylight/controller/netconf/cli/NetconfDeviceConnectionHandler.java @@ -17,7 +17,7 @@ import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities; import org.opendaylight.controller.sal.core.api.RpcImplementation; import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; /** * Implementation of RemoteDeviceHandler. Integrates cli with @@ -41,7 +41,7 @@ public class NetconfDeviceConnectionHandler implements RemoteDeviceHandler netconf-api - netconf-cli + + netconf-config netconf-impl config-netconf-connector