import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.Collection;
+import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
if (!qNameOfMissingSource.isEmpty()) {
capabilities.addUnresolvedCapabilities(qNameOfMissingSource, UnavailableCapability.FailureReason.MissingSource);
}
- return stripMissingSource(requiredSources, missingSource);
+ return stripUnavailableSource(requiredSources, missingSource);
}
private Collection<SourceIdentifier> handleSchemaResolutionException(final Collection<SourceIdentifier> requiredSources, final SchemaResolutionException resolutionException) {
// In case resolution error, try only with resolved sources
+ // There are two options why schema resolution exception occurred : unsatisfied imports or flawed model
+ // FIXME Do we really have assurance that these two cases cannot happen at once?
+ if (resolutionException.getFailedSource() != null) {
+ // 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);
+ 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);
+ return stripUnavailableSource(requiredSources, resolutionException.getFailedSource());
+ }
+ // unsatisfied imports
final Set<SourceIdentifier> unresolvedSources = resolutionException.getUnsatisfiedImports().keySet();
capabilities.addUnresolvedCapabilities(getQNameFromSourceIdentifiers(unresolvedSources), UnavailableCapability.FailureReason.UnableToResolve);
LOG.warn("{}: Unable to build schema context, unsatisfied imports {}, will reattempt with resolved only", id, resolutionException.getUnsatisfiedImports());
return new NetconfDeviceRpc(result, listener, new NetconfMessageTransformer(result, true));
}
- private Collection<SourceIdentifier> stripMissingSource(final Collection<SourceIdentifier> requiredSources, final SourceIdentifier sIdToRemove) {
+ private Collection<SourceIdentifier> stripUnavailableSource(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);
}
};
+ @Test
+ public void testNetconfDeviceFlawedModelFailedResolution() throws Exception {
+ final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();
+ final NetconfDeviceCommunicator listener = getListener();
+
+ final SchemaContextFactory schemaFactory = getSchemaFactory();
+ final SchemaContext schema = getSchema();
+ final SchemaRepository schemaRepository = getSchemaRepository();
+
+ final SchemaResolutionException schemaResolutionException =
+ new SchemaResolutionException("fail first", TEST_SID, new Throwable("YangTools parser fail"));
+ doAnswer(invocation -> {
+ if (((Collection<?>) invocation.getArguments()[0]).size() == 2) {
+ return Futures.immediateFailedCheckedFuture(schemaResolutionException);
+ } else {
+ return Futures.immediateCheckedFuture(schema);
+ }
+ }).when(schemaFactory).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
+
+ final NetconfDeviceSchemasResolver stateSchemasResolver = (deviceRpc, remoteSessionCapabilities, id) -> {
+ final Module first = Iterables.getFirst(schema.getModules(), null);
+ final QName qName = QName.create(first.getQNameModule(), first.getName());
+ final NetconfStateSchemas.RemoteYangSchema source1 = new NetconfStateSchemas.RemoteYangSchema(qName);
+ final NetconfStateSchemas.RemoteYangSchema source2 = new NetconfStateSchemas.RemoteYangSchema(QName.create(first.getQNameModule(), "test-module2"));
+ return new NetconfStateSchemas(Sets.newHashSet(source1, source2));
+ };
+
+ final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
+ = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaRepository, schemaFactory, stateSchemasResolver);
+
+ final NetconfDevice device = new NetconfDeviceBuilder()
+ .setReconnectOnSchemasChange(true)
+ .setSchemaResourcesDTO(schemaResourcesDTO)
+ .setGlobalProcessingExecutor(getExecutor())
+ .setId(getId())
+ .setSalFacade(facade)
+ .build();
+ // Monitoring supported
+ final NetconfSessionPreferences sessionCaps = getSessionCaps(true, Lists.newArrayList(TEST_CAPABILITY, TEST_CAPABILITY2));
+ device.onRemoteSessionUp(sessionCaps, listener);
+
+ Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
+ Mockito.verify(schemaFactory, times(2)).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
+ }
+
@Test
public void testNetconfDeviceFailFirstSchemaFailSecondEmpty() throws Exception {
final ArrayList<String> capList = Lists.newArrayList(TEST_CAPABILITY);