*/
package org.opendaylight.netconf.sal.connect.netconf;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.base.Predicates;
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 java.util.Collections;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
-import javax.annotation.concurrent.GuardedBy;
-import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
-import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
-import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
-import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.checkerframework.checker.lock.qual.GuardedBy;
+import org.opendaylight.mdsal.dom.api.DOMNotification;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.mdsal.dom.api.DOMRpcService;
import org.opendaylight.netconf.api.NetconfMessage;
+import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
import org.opendaylight.netconf.sal.connect.api.MessageTransformer;
import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemas;
import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemasResolver;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
-import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
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.SchemaSourceProvider;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
-import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger LOG = LoggerFactory.getLogger(NetconfDevice.class);
protected final RemoteDeviceId id;
- private final boolean reconnectOnSchemasChange;
-
protected final SchemaContextFactory schemaContextFactory;
- private final RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
- private final ListeningExecutorService processingExecutor;
protected final SchemaSourceRegistry schemaRegistry;
protected final SchemaRepository schemaRepository;
- private final NetconfDeviceSchemasResolver stateSchemasResolver;
- private final NotificationHandler notificationHandler;
+
protected final List<SchemaSourceRegistration<? extends SchemaSourceRepresentation>> sourceRegistrations =
new ArrayList<>();
+
+ private final RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
+ private final ListeningExecutorService processingExecutor;
+ private final DeviceActionFactory deviceActionFactory;
+ private final NetconfDeviceSchemasResolver stateSchemasResolver;
+ private final NotificationHandler notificationHandler;
+ private final boolean reconnectOnSchemasChange;
+
@GuardedBy("this")
private boolean connected = false;
public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id,
final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
- final ExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange) {
+ final ListeningExecutorService globalProcessingExecutor,
+ final boolean reconnectOnSchemasChange) {
+ this(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, reconnectOnSchemasChange, null);
+ }
+
+ public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id,
+ final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
+ final ListeningExecutorService globalProcessingExecutor, final boolean reconnectOnSchemasChange,
+ final DeviceActionFactory deviceActionFactory) {
this.id = id;
this.reconnectOnSchemasChange = reconnectOnSchemasChange;
+ this.deviceActionFactory = deviceActionFactory;
this.schemaRegistry = schemaResourcesDTO.getSchemaRegistry();
this.schemaRepository = schemaResourcesDTO.getSchemaRepository();
this.schemaContextFactory = schemaResourcesDTO.getSchemaContextFactory();
this.salFacade = salFacade;
this.stateSchemasResolver = schemaResourcesDTO.getStateSchemasResolver();
- this.processingExecutor = MoreExecutors.listeningDecorator(globalProcessingExecutor);
+ this.processingExecutor = requireNonNull(globalProcessingExecutor);
this.notificationHandler = new NotificationHandler(salFacade, id);
}
@Override
public void onFailure(final Throwable throwable) {
- LOG.warn("{}: Unexpected error resolving device sources: {}", id, throwable);
+ LOG.warn("{}: Unexpected error resolving device sources", id, throwable);
handleSalInitializationFailure(throwable, listener);
}
};
- Futures.addCallback(sourceResolverFuture, resolvedSourceCallback);
+ Futures.addCallback(sourceResolverFuture, resolvedSourceCallback, MoreExecutors.directExecutor());
}
private void registerToBaseNetconfStream(final NetconfDeviceRpc deviceRpc,
// TODO check whether the model describing create subscription is present in schema
// Perhaps add a default schema context to support create-subscription if the model was not provided
// (same as what we do for base netconf operations in transformer)
- final CheckedFuture<DOMRpcResult, DOMRpcException> rpcResultListenableFuture = deviceRpc.invokeRpc(
+ final ListenableFuture<DOMRpcResult> rpcResultListenableFuture = deviceRpc.invokeRpc(
NetconfMessageTransformUtil.toPath(NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_QNAME),
NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_CONTENT);
// Only disconnect is enough,
// the reconnecting nature of the connector will take care of reconnecting
listener.disconnect();
- return Optional.absent();
+ return Optional.empty();
}
return Optional.of(notification);
}
LOG.warn("Unable to subscribe to base notification stream. Schemas will not be reloaded on the fly",
throwable);
}
- });
+ }, MoreExecutors.directExecutor());
}
private boolean shouldListenOnSchemaChange(final NetconfSessionPreferences remoteSessionCapabilities) {
private synchronized void handleSalInitializationSuccess(final SchemaContext result,
final NetconfSessionPreferences remoteSessionCapabilities,
- final DOMRpcService deviceRpc) {
+ final DOMRpcService deviceRpc,
+ final RemoteDeviceCommunicator<NetconfMessage> listener) {
//NetconfDevice.SchemaSetup can complete after NetconfDeviceCommunicator was closed. In that case do nothing,
//since salFacade.onDeviceDisconnected was already called.
if (connected) {
final BaseSchema baseSchema =
remoteSessionCapabilities.isNotificationsSupported()
? BaseSchema.BASE_NETCONF_CTX_WITH_NOTIFICATIONS : BaseSchema.BASE_NETCONF_CTX;
- messageTransformer = new NetconfMessageTransformer(result, true, baseSchema);
+ this.messageTransformer = new NetconfMessageTransformer(result, true, baseSchema);
- updateTransformer(messageTransformer);
// salFacade.onDeviceConnected has to be called before the notification handler is initialized
- salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc);
- notificationHandler.onRemoteSchemaUp(messageTransformer);
+ this.salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc,
+ this.deviceActionFactory == null ? null : this.deviceActionFactory.createDeviceAction(
+ this.messageTransformer, listener, result));
+ this.notificationHandler.onRemoteSchemaUp(this.messageTransformer);
LOG.info("{}: Netconf connector initialized successfully", id);
} else {
updateTransformer(null);
}
- private void updateTransformer(final MessageTransformer<NetconfMessage> transformer) {
+ private synchronized void updateTransformer(final MessageTransformer<NetconfMessage> transformer) {
messageTransformer = transformer;
}
final SchemaRepository schemaRepository,
final SchemaContextFactory schemaContextFactory,
final NetconfDeviceSchemasResolver deviceSchemasResolver) {
- this.schemaRegistry = Preconditions.checkNotNull(schemaRegistry);
- this.schemaRepository = Preconditions.checkNotNull(schemaRepository);
- this.schemaContextFactory = Preconditions.checkNotNull(schemaContextFactory);
- this.stateSchemasResolver = Preconditions.checkNotNull(deviceSchemasResolver);
+ this.schemaRegistry = requireNonNull(schemaRegistry);
+ this.schemaRepository = requireNonNull(schemaRepository);
+ this.schemaContextFactory = requireNonNull(schemaContextFactory);
+ this.stateSchemasResolver = requireNonNull(deviceSchemasResolver);
}
public SchemaSourceRegistry getSchemaRegistry() {
}
@Override
- public DeviceSources call() throws Exception {
+ public DeviceSources call() {
final NetconfDeviceSchemas availableSchemas =
stateSchemasResolver.resolve(deviceRpc, remoteSessionCapabilities, id);
LOG.debug("{}: Schemas exposed by ietf-netconf-monitoring: {}", id,
}
private static SourceIdentifier toSourceId(final QName input) {
- return RevisionSourceIdentifier.create(input.getLocalName(),
- Optional.fromNullable(input.getFormattedRevision()));
+ return RevisionSourceIdentifier.create(input.getLocalName(), input.getRevision());
}
}
}
private Collection<SourceIdentifier> filterMissingSources(final Collection<SourceIdentifier> requiredSources) {
-
return requiredSources.parallelStream().filter(sourceIdentifier -> {
- boolean remove = false;
try {
- schemaRepository.getSchemaSource(sourceIdentifier, ASTSchemaSource.class).checkedGet();
- } catch (SchemaSourceException e) {
- remove = true;
+ schemaRepository.getSchemaSource(sourceIdentifier, YangTextSchemaSource.class).get();
+ return false;
+ } catch (InterruptedException | ExecutionException e) {
+ return true;
}
- return remove;
}).collect(Collectors.toList());
}
/**
* Build schema context, in case of success or final failure notify device.
+ *
+ * @param requiredSources required sources
*/
@SuppressWarnings("checkstyle:IllegalCatch")
private void setUpSchema(Collection<SourceIdentifier> requiredSources) {
while (!requiredSources.isEmpty()) {
LOG.trace("{}: Trying to build schema context from {}", id, requiredSources);
try {
- final CheckedFuture<SchemaContext, SchemaResolutionException> schemaBuilderFuture =
- schemaContextFactory.createSchemaContext(requiredSources);
- final SchemaContext result = schemaBuilderFuture.checkedGet();
+ final ListenableFuture<SchemaContext> schemaBuilderFuture = schemaContextFactory
+ .createSchemaContext(requiredSources);
+ final SchemaContext result = schemaBuilderFuture.get();
LOG.debug("{}: Schema context built successfully from {}", id, requiredSources);
final Collection<QName> filteredQNames = Sets.difference(deviceSources.getRequiredSourcesQName(),
capabilities.getUnresolvedCapabilites().keySet());
remoteSessionCapabilities.getNonModuleBasedCapsOrigin().get(entry)).build())
.collect(Collectors.toList()));
- handleSalInitializationSuccess(result, remoteSessionCapabilities, getDeviceSpecificRpc(result));
+ handleSalInitializationSuccess(result, remoteSessionCapabilities, getDeviceSpecificRpc(result),
+ listener);
return;
- } catch (final SchemaResolutionException e) {
+ } catch (final ExecutionException e) {
// schemaBuilderFuture.checkedGet() throws only SchemaResolutionException
// that might be wrapping a MissingSchemaSourceException so we need to look
// at the cause of the exception to make sure we don't misinterpret it.
- if (e.getCause() instanceof MissingSchemaSourceException) {
+ final Throwable cause = e.getCause();
+
+ if (cause instanceof MissingSchemaSourceException) {
requiredSources = handleMissingSchemaSourceException(
- requiredSources, (MissingSchemaSourceException) e.getCause());
+ requiredSources, (MissingSchemaSourceException) cause);
continue;
}
- requiredSources = handleSchemaResolutionException(requiredSources, e);
+ if (cause instanceof SchemaResolutionException) {
+ requiredSources = handleSchemaResolutionException(requiredSources,
+ (SchemaResolutionException) cause);
+ } else {
+ handleSalInitializationFailure(e, listener);
+ return;
+ }
} catch (final Exception e) {
// unknown error, fail
handleSalInitializationFailure(e, listener);
final Collection<SourceIdentifier> requiredSources, final MissingSchemaSourceException exception) {
// In case source missing, try without it
final SourceIdentifier missingSource = exception.getSourceId();
- LOG.warn("{}: Unable to build schema context, missing source {}, will reattempt without it", id,
- missingSource);
+ LOG.warn("{}: Unable to build schema context, missing source {}, will reattempt without it",
+ id, missingSource);
LOG.debug("{}: Unable to build schema context, missing source {}, will reattempt without it",
- exception);
+ id, missingSource, exception);
final Collection<QName> qNameOfMissingSource =
- getQNameFromSourceIdentifiers(Sets.newHashSet(missingSource));
+ getQNameFromSourceIdentifiers(Sets.newHashSet(missingSource));
if (!qNameOfMissingSource.isEmpty()) {
capabilities.addUnresolvedCapabilities(
qNameOfMissingSource, UnavailableCapability.FailureReason.MissingSource);
// flawed model - exclude it
final SourceIdentifier failedSourceId = resolutionException.getFailedSource();
LOG.warn("{}: Unable to build schema context, failed to resolve source {}, will reattempt without it",
- id, failedSourceId);
+ id, failedSourceId);
LOG.warn("{}: Unable to build schema context, failed to resolve source {}, will reattempt without it",
- id, resolutionException);
- capabilities.addUnresolvedCapabilities(getQNameFromSourceIdentifiers(
- Collections.singleton(failedSourceId)), UnavailableCapability.FailureReason.UnableToResolve);
+ id, failedSourceId, resolutionException);
+ capabilities.addUnresolvedCapabilities(
+ getQNameFromSourceIdentifiers(Collections.singleton(failedSourceId)),
+ UnavailableCapability.FailureReason.UnableToResolve);
return stripUnavailableSource(requiredSources, resolutionException.getFailedSource());
}
// unsatisfied imports
final Set<SourceIdentifier> unresolvedSources = resolutionException.getUnsatisfiedImports().keySet();
- capabilities.addUnresolvedCapabilities(
- getQNameFromSourceIdentifiers(unresolvedSources), UnavailableCapability.FailureReason.UnableToResolve);
+ capabilities.addUnresolvedCapabilities(getQNameFromSourceIdentifiers(unresolvedSources),
+ UnavailableCapability.FailureReason.UnableToResolve);
LOG.warn("{}: Unable to build schema context, unsatisfied imports {}, will reattempt with resolved only",
- id, resolutionException.getUnsatisfiedImports());
+ id, resolutionException.getUnsatisfiedImports());
LOG.debug("{}: Unable to build schema context, unsatisfied imports {}, will reattempt with resolved only",
- resolutionException);
+ id, resolutionException.getUnsatisfiedImports(), resolutionException);
return resolutionException.getResolvedSources();
}
private Collection<SourceIdentifier> stripUnavailableSource(final Collection<SourceIdentifier> requiredSources,
final SourceIdentifier sourceIdToRemove) {
final LinkedList<SourceIdentifier> sourceIdentifiers = Lists.newLinkedList(requiredSources);
- final boolean removed = sourceIdentifiers.remove(sourceIdToRemove);
- Preconditions.checkState(
- removed, "{}: Trying to remove {} from {} failed", id, sourceIdToRemove, requiredSources);
+ checkState(sourceIdentifiers.remove(sourceIdToRemove),
+ "%s: Trying to remove %s from %s failed", id, sourceIdToRemove, requiredSources);
return sourceIdentifiers;
}
continue;
}
- final String rev = getNullableRev(identifier);
- if (rev == null) {
- if (qname.getRevision() == null) {
- return qname;
- }
- } else if (qname.getFormattedRevision().equals(rev)) {
+ if (identifier.getRevision().equals(qname.getRevision())) {
return qname;
}
}
// this capability will be removed from required sources and not reported as unresolved-capability
return null;
}
-
- private String getNullableRev(final SourceIdentifier identifier) {
- final String rev = identifier.getRevision();
- return rev == null || SourceIdentifier.NOT_PRESENT_FORMATTED_REVISION.equals(rev) ? null : rev;
- }
}
}