+ Fix disconnect by netconf-connector. Until now, the connection to remote device was still open even if initialization failed.
Change-Id: I23ee999c7898665498519abf19610deef3d410a3
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
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;
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;
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;
-
/**
*
*/
{
private static final Logger logger = LoggerFactory.getLogger(NetconfConnectorModule.class);
- private static AbstractCachingSchemaSourceProvider<String, InputStream> GLOBAL_NETCONF_SOURCE_PROVIDER = null;
private BundleContext bundleContext;
private Optional<NetconfSessionCapabilities> 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);
final RemoteDeviceHandler<NetconfSessionCapabilities> 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);
return Optional.of(parsedOverrideCapabilities);
}
- private synchronized AbstractCachingSchemaSourceProvider<String, InputStream> 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<String> 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;
}
return new InetSocketAddress(ip, getPort().getValue());
}
}
+
+ public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
+ this.schemaRegistry = schemaRegistry;
+ }
+
+ public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
+ this.schemaContextFactory = schemaContextFactory;
+ }
}
*/
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;
/**
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<YangTextSchemaSource> 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;
}
}
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<PREF> extends AutoCloseable {
- void onDeviceConnected(SchemaContextProvider remoteSchemaContextProvider,
+ void onDeviceConnected(SchemaContext remoteSchemaContext,
PREF netconfSessionPreferences, RpcImplementation deviceRpc);
void onDeviceDisconnected();
*/
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
*/
private static final Logger logger = LoggerFactory.getLogger(NetconfDevice.class);
+ public static final Function<QName, SourceIdentifier> QNAME_TO_SOURCE_ID_FUNCTION = new Function<QName, SourceIdentifier>() {
+ @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<NetconfSessionCapabilities> salFacade;
private final ListeningExecutorService processingExecutor;
+ private final SchemaSourceRegistry schemaRegistry;
private final MessageTransformer<NetconfMessage> messageTransformer;
- private final SchemaContextProviderFactory schemaContextProviderFactory;
- private final SchemaSourceProviderFactory<InputStream> sourceProviderFactory;
private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver;
private final NotificationHandler notificationHandler;
+ private final List<SchemaSourceRegistration<? extends SchemaSourceRepresentation>> sourceRegistrations = Lists.newArrayList();
- public static NetconfDevice createNetconfDevice(final RemoteDeviceId id,
- final AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider,
- final ExecutorService executor, final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade) {
- return createNetconfDevice(id, schemaSourceProvider, executor, salFacade, new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
- }
-
- @VisibleForTesting
- protected static NetconfDevice createNetconfDevice(final RemoteDeviceId id,
- final AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider,
- final ExecutorService executor, final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade,
- final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) {
-
- return new NetconfDevice(id, salFacade, executor, new NetconfMessageTransformer(),
- new NetconfDeviceSchemaProviderFactory(id), new SchemaSourceProviderFactory<InputStream>() {
- @Override
- public SchemaSourceProvider<InputStream> createSourceProvider(final RpcImplementation deviceRpc) {
- return schemaSourceProvider.createInstanceFor(new NetconfRemoteSchemaSourceProvider(id,
- deviceRpc));
- }
- }, stateSchemasResolver);
- }
-
- @VisibleForTesting
- protected NetconfDevice(final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade,
- final ExecutorService processingExecutor, final MessageTransformer<NetconfMessage> messageTransformer,
- final SchemaContextProviderFactory schemaContextProviderFactory,
- final SchemaSourceProviderFactory<InputStream> sourceProviderFactory,
- final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) {
+ public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade,
+ final ExecutorService globalProcessingExecutor, final MessageTransformer<NetconfMessage> 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);
}
// http://netty.io/wiki/thread-model.html
logger.debug("{}: Session to remote device established with {}", id, remoteSessionCapabilities);
- final ListenableFuture<?> salInitializationFuture = processingExecutor.submit(new Runnable() {
+ final NetconfDeviceRpc deviceRpc = setUpDeviceRpc(listener);
+
+ final DeviceSourcesResolver task = new DeviceSourcesResolver(deviceRpc, remoteSessionCapabilities, id, stateSchemasResolver);
+ final ListenableFuture<DeviceSources> sourceResolverFuture = processingExecutor.submit(task);
+
+ final FutureCallback<DeviceSources> resolvedSourceCallback = new FutureCallback<DeviceSources>() {
@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<InputStream> 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<Object>() {
- @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<NetconfMessage> 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<InputStream> 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<NetconfMessage> 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<NetconfMessage> listener) {
+ return new NetconfDeviceRpc(listener, messageTransformer);
}
@Override
public void onRemoteSessionDown() {
salFacade.onDeviceDisconnected();
+ for (final SchemaSourceRegistration<? extends SchemaSourceRepresentation> sourceRegistration : sourceRegistrations) {
+ sourceRegistration.close();
+ }
}
@Override
}
/**
- * Handles incoming notifications. Either caches them(until onRemoteSchemaUp is called) or passes to sal Facade.
+ * Just a transfer object containing schema related dependencies. Injected in constructor.
*/
- private final static class NotificationHandler {
+ public static class SchemaResourcesDTO {
+ private final SchemaSourceRegistry schemaRegistry;
+ private final SchemaContextFactory schemaContextFactory;
+ private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver;
+
+ public SchemaResourcesDTO(final SchemaSourceRegistry schemaRegistry, final SchemaContextFactory schemaContextFactory, final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver) {
+ this.schemaRegistry = Preconditions.checkNotNull(schemaRegistry);
+ this.schemaContextFactory = Preconditions.checkNotNull(schemaContextFactory);
+ this.stateSchemasResolver = Preconditions.checkNotNull(stateSchemasResolver);
+ }
+
+ public SchemaSourceRegistry getSchemaRegistry() {
+ return schemaRegistry;
+ }
+
+ public SchemaContextFactory getSchemaContextFactory() {
+ return schemaContextFactory;
+ }
- private final RemoteDeviceHandler<?> salFacade;
- private final List<NetconfMessage> cache = new LinkedList<>();
- private final MessageTransformer<NetconfMessage> messageTransformer;
- private boolean passNotifications = false;
+ public NetconfStateSchemas.NetconfStateSchemasResolver getStateSchemasResolver() {
+ return stateSchemasResolver;
+ }
+ }
+
+ /**
+ * Schema building callable.
+ */
+ private static class DeviceSourcesResolver implements Callable<DeviceSources> {
+ private final NetconfDeviceRpc deviceRpc;
+ private final NetconfSessionCapabilities remoteSessionCapabilities;
private final RemoteDeviceId id;
+ private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver;
- NotificationHandler(final RemoteDeviceHandler<?> salFacade, final MessageTransformer<NetconfMessage> 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<SourceIdentifier> 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<SourceIdentifier> providedSources = Sets.newHashSet(Collections2.transform(
+ availableSchemas.getAvailableYangSchemasQNames(), QNAME_TO_SOURCE_ID_FUNCTION));
+
+ final Set<SourceIdentifier> 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<SourceIdentifier> 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<SourceIdentifier> requiredSources;
+ private final Collection<SourceIdentifier> 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<SourceIdentifier> requiredSources, final Collection<SourceIdentifier> providedSources) {
+ this.requiredSources = requiredSources;
+ this.providedSources = providedSources;
+ }
- cache.add(notification);
+ public Collection<SourceIdentifier> getRequiredSources() {
+ return requiredSources;
}
- private void passNotification(final CompositeNode parsedNotification) {
- logger.debug("{}: Forwarding notification {}", id, parsedNotification);
- Preconditions.checkNotNull(parsedNotification);
- salFacade.onNotification(parsedNotification);
+ public Collection<SourceIdentifier> 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<NetconfMessage> listener;
+
+ public RecursiveSchemaSetup(final DeviceSources deviceSources, final NetconfSessionCapabilities remoteSessionCapabilities, final NetconfDeviceRpc deviceRpc, final RemoteDeviceCommunicator<NetconfMessage> 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<SourceIdentifier> 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<SchemaContext, SchemaResolutionException> schemaBuilderFuture = schemaContextFactory.createSchemaContext(requiredSources);
+
+ final FutureCallback<SchemaContext> RecursiveSchemaBuilderCallback = new FutureCallback<SchemaContext>() {
+
+ @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<SourceIdentifier> stripMissingSource(final Collection<SourceIdentifier> requiredSources, final SourceIdentifier sIdToRemove) {
+ final LinkedList<SourceIdentifier> sourceIdentifiers = Lists.newLinkedList(requiredSources);
+ final boolean removed = sourceIdentifiers.remove(sIdToRemove);
+ Preconditions.checkState(removed, "{}: Trying to remove {} from {} failed", id, sIdToRemove, requiredSources);
+ return sourceIdentifiers;
+ }
+ }
}
*/
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;
}
--- /dev/null
+/*
+ * 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<NetconfMessage> queue = new LinkedList<>();
+ private final MessageTransformer<NetconfMessage> messageTransformer;
+ private final RemoteDeviceId id;
+ private boolean passNotifications = false;
+
+ NotificationHandler(final RemoteDeviceHandler<?> salFacade, final MessageTransformer<NetconfMessage> 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);
+ }
+}
private final RemoteDeviceId id;
private final Lock sessionLock = new ReentrantLock();
+ // TODO implement concurrent message limit
private final Queue<Request> requests = new ArrayDeque<>();
private NetconfClientSession session;
+ private Future<?> initFuture;
public NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice,
final NetconfSessionCapabilities netconfSessionCapabilities) {
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);
}
}
@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
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));
}
*/
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;
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<NetconfSessionCapabilities> {
private static final Logger logger= LoggerFactory.getLogger(NetconfDeviceSalFacade.class);
}
@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
final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_GET_CONFIG_QNAME,
NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, toFilterStructure(path)));
- ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(future, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
+ final ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(future, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
@Override
public Optional<NormalizedNode<?, ?>> apply(final RpcResult<CompositeNode> result) {
checkReadSuccess(result, path);
final YangInstanceIdentifier path) {
final ListenableFuture<RpcResult<CompositeNode>> future = rpc.invokeRpc(NETCONF_GET_QNAME, NetconfMessageTransformUtil.wrap(NETCONF_GET_QNAME, toFilterStructure(path)));
- ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(future, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
+ final ListenableFuture<Optional<NormalizedNode<?, ?>>> transformedFuture = Futures.transform(future, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
@Override
public Optional<NormalizedNode<?, ?>> apply(final RpcResult<CompositeNode> result) {
checkReadSuccess(result, path);
+++ /dev/null
-/*
- * 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<QName> capabilities, final SchemaSourceProvider<InputStream> 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<InputStream> 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<InputStream> modelsToParse) {
- final YangParserImpl parser = new YangParserImpl();
- final Set<Module> 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;
- }
- }
-}
+++ /dev/null
-/*
- * 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<String> {
-
- 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<String> getSchemaSource(final String moduleName, final Optional<String> revision) {
- final ImmutableCompositeNode getSchemaRequest = createGetSchemaRequest(moduleName, revision);
-
- logger.trace("{}: Loading YANG schema source for {}:{}", id, moduleName, revision);
- try {
- final RpcResult<CompositeNode> schemaReply = rpc.invokeRpc(GET_SCHEMA_QNAME, getSchemaRequest).get();
- if (schemaReply.isSuccessful()) {
- final Optional<String> 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<String> revision) {
- final CompositeNodeBuilder<ImmutableCompositeNode> 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<String> 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.<String>absent();
- }
-}
--- /dev/null
+/*
+ * 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<YangTextSchemaSource> {
+
+ 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<SchemaSourceException> MAPPER = new ExceptionMapper<SchemaSourceException>(
+ "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<String> revision) {
+ final CompositeNodeBuilder<ImmutableCompositeNode> 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<String> 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.<String> absent();
+ }
+
+ @Override
+ public CheckedFuture<YangTextSchemaSource, SchemaSourceException> 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<String> revision = Optional.fromNullable(formattedRevision);
+ final ImmutableCompositeNode getSchemaRequest = createGetSchemaRequest(moduleName, revision);
+
+ logger.trace("{}: Loading YANG schema source for {}:{}", id, moduleName, revision);
+
+ final ListenableFuture<YangTextSchemaSource> 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<RpcResult<CompositeNode>, YangTextSchemaSource> {
+
+ private final RemoteDeviceId id;
+ private final SourceIdentifier sourceIdentifier;
+ private final String moduleName;
+ private final Optional<String> revision;
+
+ public ResultToYangSourceTransformer(final RemoteDeviceId id, final SourceIdentifier sourceIdentifier,
+ final String moduleName, final Optional<String> revision) {
+ this.id = id;
+ this.sourceIdentifier = sourceIdentifier;
+ this.moduleName = moduleName;
+ this.revision = revision;
+ }
+
+ @Override
+ public YangTextSchemaSource apply(final RpcResult<CompositeNode> input) {
+
+ if (input.isSuccessful()) {
+
+ final Optional<String> 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<String> schemaString;
+
+ public NetconfYangTextSchemaSource(final RemoteDeviceId id, final SourceIdentifier sId, final Optional<String> 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());
+ }
+ }
+}
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");
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<String,String> errorInfo = ImmutableMap.<String,String>builder()
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;
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;
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;
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) {
};
@Test
- public void testNetconfDeviceWithoutMonitoring() throws Exception {
+ public void testNetconfDeviceFailFirstSchemaFailSecondEmpty() throws Exception {
+ final ArrayList<String> capList = Lists.newArrayList(TEST_CAPABILITY);
+
final RemoteDeviceHandler<NetconfSessionCapabilities> facade = getFacade();
final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
- final NetconfDevice device = new NetconfDevice(getId(), facade, getExecutor(), getMessageTransformer(), getSchemaContextProviderFactory(), getSourceProviderFactory(), stateSchemasResolver);
- device.onRemoteSessionUp(getSessionCaps(false, Collections.<String>emptyList()), listener);
+ final SchemaContextFactory schemaFactory = getSchemaFactory();
+
+ // Make fallback attempt to fail due to empty resolved sources
+ final SchemaResolutionException schemaResolutionException
+ = new SchemaResolutionException("fail first",
+ Collections.<SourceIdentifier>emptyList(), HashMultimap.<SourceIdentifier, ModuleImport>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<NetconfSessionCapabilities> facade = getFacade();
+ final RemoteDeviceCommunicator<NetconfMessage> 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
final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
final MessageTransformer<NetconfMessage> 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);
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);
final RemoteDeviceHandler<NetconfSessionCapabilities> facade = getFacade();
final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
- final SchemaContextProviderFactory schemaContextProviderFactory = getSchemaContextProviderFactory();
- final SchemaSourceProviderFactory<InputStream> sourceProviderFactory = getSourceProviderFactory();
+ final SchemaContextFactory schemaContextProviderFactory = getSchemaFactory();
final MessageTransformer<NetconfMessage> 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() {
private RemoteDeviceHandler<NetconfSessionCapabilities> getFacade() throws Exception {
final RemoteDeviceHandler<NetconfSessionCapabilities> 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;
}
private static <T> T mockClass(final Class<T> remoteDeviceHandlerClass) {
- final T mock = Mockito.mock(remoteDeviceHandlerClass);
+ final T mock = mock(remoteDeviceHandlerClass);
Mockito.doReturn(remoteDeviceHandlerClass.getSimpleName()).when(mock).toString();
return mock;
}
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
}
@Override
- public synchronized void onDeviceConnected(final SchemaContextProvider contextProvider,
+ public synchronized void onDeviceConnected(final SchemaContext context,
final NetconfSessionCapabilities capabilities, final RpcImplementation rpcImplementation) {
console.enterRootContext(new ConsoleContext() {
// possible
// TODO detect netconf base version
// TODO detect inet types version
- commandDispatcher.addRemoteCommands(rpcImplementation, contextProvider.getSchemaContext());
- schemaContextRegistry.setRemoteSchemaContext(contextProvider.getSchemaContext());
+ commandDispatcher.addRemoteCommands(rpcImplementation, context);
+ schemaContextRegistry.setRemoteSchemaContext(context);
up = true;
this.notify();
}
<modules>
<module>netconf-api</module>
- <module>netconf-cli</module>
+ <!--FIXME make compilable-->
+ <!--<module>netconf-cli</module>-->
<module>netconf-config</module>
<module>netconf-impl</module>
<module>config-netconf-connector</module>