<groupId>org.opendaylight.controller</groupId>
<artifactId>mdsal-netconf-monitoring</artifactId>
</dependency>
-
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-mdsal-config</artifactId>
+ <version>${netconf.version}</version>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-netconf-connector</artifactId>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-config</artifactId>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netconf-mdsal-config</artifactId>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-auth</artifactId>
<groupId>org.opendaylight.yangtools.model</groupId>
<artifactId>ietf-yang-types</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-api</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-mapping-api</artifactId>
</feature>
<feature name='odl-netconf-util' version='${project.version}'>
<feature version='${project.version}'>odl-netconf-mapping-api</feature>
+ <bundle>mvn:org.opendaylight.yangtools/yang-model-api/${yangtools.version}</bundle>
<bundle>mvn:org.opendaylight.controller/netconf-util/${project.version}</bundle>
</feature>
<feature name='odl-netconf-impl' version='${project.version}' description="OpenDaylight :: Netconf :: Impl">
</dependency>
<!-- test to validate features.xml -->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
</dependency>
<!-- dependency for opendaylight-karaf-empty for use by testing -->
<karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
<!-- test to validate features.xml -->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
</dependency>
<!-- dependency for opendaylight-karaf-empty for use by testing -->
<karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
<dependencies>
<!-- test to validate features.xml -->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
</dependency>
<!-- dependency for opendaylight-karaf-empty for use by testing -->
<karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
<branding.version>1.1.0-SNAPSHOT</branding.version>
<karaf.resources.version>1.5.0-SNAPSHOT</karaf.resources.version>
<karaf.version>3.0.1</karaf.version>
- <feature.test.version>0.7.0-SNAPSHOT</feature.test.version>
+ <feature.test.version>1.5.0-SNAPSHOT</feature.test.version>
<karaf.empty.version>1.5.0-SNAPSHOT</karaf.empty.version>
<surefire.version>2.16</surefire.version>
</properties>
-->
<!-- test to validate features.xml -->
<dependency>
- <groupId>org.opendaylight.yangtools</groupId>
+ <groupId>org.opendaylight.odlparent</groupId>
<artifactId>features-test</artifactId>
<version>${feature.test.version}</version>
<scope>test</scope>
<karaf.distro.version>${karaf.empty.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
- <dependency>org.opendaylight.yangtools:features-test</dependency>
+ <dependency>org.opendaylight.odlparent:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
<artifactId>guava</artifactId>
</dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- </dependency>
-
<!--Dependencies regardign RuntimeGeneratedMappingService-->
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
package org.opendaylight.controller.config.manager.impl.osgi;
import static java.lang.String.format;
-
import com.google.common.annotations.VisibleForTesting;
-import java.io.InputStream;
+import com.google.common.base.Charsets;
+import com.google.common.io.Resources;
+import java.io.IOException;
import java.net.URL;
-import java.util.List;
-import org.apache.commons.io.IOUtils;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
/**
* OSGi extender that listens for bundle activation events. Reads file
* META-INF/services/org.opendaylight.controller.config.spi.ModuleFactory, each
LOG.trace("Got addingBundle event of bundle {}, resource {}, event {}",
bundle, resource, event);
if (resource != null) {
- try (InputStream inputStream = resource.openStream()) {
- List<String> lines = IOUtils.readLines(inputStream);
- for (String factoryClassName : lines) {
+ try {
+ for (String factoryClassName : Resources.readLines(resource, Charsets.UTF_8)) {
registerFactory(factoryClassName, bundle);
}
- } catch (Exception e) {
+ } catch (IOException e) {
LOG.error("Error while reading {}", resource, e);
throw new RuntimeException(e);
}
package org.opendaylight.controller.config.manager.impl.osgi.mapping;
import static java.lang.String.format;
-
-import java.io.InputStream;
+import com.google.common.base.Charsets;
+import com.google.common.io.Resources;
+import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
-import org.apache.commons.io.IOUtils;
import org.opendaylight.yangtools.concepts.ObjectRegistration;
import org.opendaylight.yangtools.sal.binding.generator.api.ModuleInfoRegistry;
import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
}
List<ObjectRegistration<YangModuleInfo>> registrations = new LinkedList<>();
- try (InputStream inputStream = resource.openStream()) {
- List<String> lines = IOUtils.readLines(inputStream);
- for (String moduleInfoName : lines) {
+ try {
+ for (String moduleInfoName : Resources.readLines(resource, Charsets.UTF_8)) {
LOG.trace("Retrieve ModuleInfo({}, {})", moduleInfoName, bundle);
YangModuleInfo moduleInfo = retrieveModuleInfo(moduleInfoName, bundle);
registrations.add(moduleInfoRegistry.registerModuleInfo(moduleInfo));
}
-
- } catch (Exception e) {
+ } catch (IOException e) {
LOG.error("Error while reading {}", resource, e);
throw new RuntimeException(e);
}
+
LOG.trace("Got following registrations {}", registrations);
return registrations;
}
private LinkedHashSet<FeatureConfigSnapshotHolder> pushConfig(final Feature feature) throws Exception, InterruptedException {
LinkedHashSet<FeatureConfigSnapshotHolder> configs = new LinkedHashSet<FeatureConfigSnapshotHolder>();
if(isInstalled(feature)) {
- ChildAwareFeatureWrapper wrappedFeature = new ChildAwareFeatureWrapper(feature,featuresService);
- configs = wrappedFeature.getFeatureConfigSnapshotHolders();
- if(!configs.isEmpty()) {
- configs = pushConfig(configs);
- feature2configs.putAll(feature, configs);
+ // FIXME Workaround for BUG-2836, features service returns null for feature: standard-condition-webconsole_0_0_0, 3.0.1
+ if(featuresService.getFeature(feature.getName(), feature.getVersion()) == null) {
+ LOG.warn("Feature: {}, {} is missing from features service. Skipping", feature.getName(), feature.getVersion());
+ } else {
+ ChildAwareFeatureWrapper wrappedFeature = new ChildAwareFeatureWrapper(feature, featuresService);
+ configs = wrappedFeature.getFeatureConfigSnapshotHolders();
+ if (!configs.isEmpty()) {
+ configs = pushConfig(configs);
+ feature2configs.putAll(feature, configs);
+ }
}
}
return configs;
long decrNextIndex();
/**
+ * Sets the index of the next log entry for this follower.
*
* @param nextIndex
+ * @return true if the new index differed from the current index and the current index was updated, false
+ * otherwise.
*/
- void setNextIndex(long nextIndex);
+ boolean setNextIndex(long nextIndex);
/**
* Increment the value of the matchIndex
*/
long incrMatchIndex();
- void setMatchIndex(long matchIndex);
+ /**
+ * Sets the index of the highest log entry for this follower.
+ *
+ * @param matchIndex
+ * @return true if the new index differed from the current index and the current index was updated, false
+ * otherwise.
+ */
+ boolean setMatchIndex(long matchIndex);
/**
* The identifier of the follower
}
@Override
- public void setNextIndex(long nextIndex) {
- this.nextIndex = nextIndex;
+ public boolean setNextIndex(long nextIndex) {
+ if(this.nextIndex != nextIndex) {
+ this.nextIndex = nextIndex;
+ return true;
+ }
+
+ return false;
}
@Override
}
@Override
- public void setMatchIndex(long matchIndex) {
- this.matchIndex = matchIndex;
+ public boolean setMatchIndex(long matchIndex) {
+ if(this.matchIndex != matchIndex) {
+ this.matchIndex = matchIndex;
+ return true;
+ }
+
+ return false;
}
@Override
private static final long APPLY_STATE_DELAY_THRESHOLD_IN_NANOS = TimeUnit.MILLISECONDS.toNanos(50L); // 50 millis
+ private static final Procedure<ApplyJournalEntries> APPLY_JOURNAL_ENTRIES_PERSIST_CALLBACK =
+ new Procedure<ApplyJournalEntries>() {
+ @Override
+ public void apply(ApplyJournalEntries param) throws Exception {
+ }
+ };
+
protected final Logger LOG = LoggerFactory.getLogger(getClass());
/**
if(LOG.isDebugEnabled()) {
LOG.debug("{}: Persisting ApplyLogEntries with index={}", persistenceId(), applyEntries.getToIndex());
}
- persistence().persist(applyEntries, new Procedure<ApplyJournalEntries>() {
- @Override
- public void apply(ApplyJournalEntries param) throws Exception {
- }
- });
+
+ persistence().persist(applyEntries, APPLY_JOURNAL_ENTRIES_PERSIST_CALLBACK);
} else if(message instanceof ApplySnapshot ) {
Snapshot snapshot = ((ApplySnapshot) message).getSnapshot();
context.getReplicatedLog().size());
} else if (message instanceof CaptureSnapshot) {
- LOG.info("{}: CaptureSnapshot received by actor", persistenceId());
+ LOG.debug("{}: CaptureSnapshot received by actor: {}", persistenceId(), message);
if(captureSnapshot == null) {
captureSnapshot = (CaptureSnapshot)message;
}
private void handleCaptureSnapshotReply(byte[] snapshotBytes) {
- LOG.info("{}: CaptureSnapshotReply received by actor: snapshot size {}", persistenceId(), snapshotBytes.length);
+ LOG.debug("{}: CaptureSnapshotReply received by actor: snapshot size {}", persistenceId(), snapshotBytes.length);
// create a snapshot object from the state provided and save it
// when snapshot is saved async, SaveSnapshotSuccess is raised.
long dataThreshold = Runtime.getRuntime().totalMemory() *
getRaftActorContext().getConfigParams().getSnapshotDataThresholdPercentage() / 100;
if (context.getReplicatedLog().dataSize() > dataThreshold) {
+
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("{}: dataSize {} exceeds dataThreshold {} - doing snapshotPreCommit with index {}",
+ persistenceId(), context.getReplicatedLog().dataSize(), dataThreshold,
+ captureSnapshot.getLastAppliedIndex());
+ }
+
// if memory is less, clear the log based on lastApplied.
// this could/should only happen if one of the followers is down
// as normally we keep removing from the log when its replicated to all.
context.getReplicatedLog().snapshotPreCommit(captureSnapshot.getLastAppliedIndex(),
captureSnapshot.getLastAppliedTerm());
- getCurrentBehavior().setReplicatedToAllIndex(captureSnapshot.getReplicatedToAllIndex());
+ // Don't reset replicatedToAllIndex to -1 as this may prevent us from trimming the log after an
+ // install snapshot to a follower.
+ if(captureSnapshot.getReplicatedToAllIndex() >= 0) {
+ getCurrentBehavior().setReplicatedToAllIndex(captureSnapshot.getReplicatedToAllIndex());
+ }
} else if(captureSnapshot.getReplicatedToAllIndex() != -1){
// clear the log based on replicatedToAllIndex
context.getReplicatedLog().snapshotPreCommit(captureSnapshot.getReplicatedToAllIndex(),
}
- LOG.info("{}: Removed in-memory snapshotted entries, adjusted snaphsotIndex:{} " +
- "and term:{}", persistenceId(), captureSnapshot.getLastAppliedIndex(),
- captureSnapshot.getLastAppliedTerm());
+ LOG.info("{}: Removed in-memory snapshotted entries, adjusted snaphsotIndex: {} " +
+ "and term: {}", persistenceId(), replicatedLog.getSnapshotIndex(),
+ replicatedLog.getSnapshotTerm());
if (isLeader() && captureSnapshot.isInstallSnapshotInitiated()) {
// this would be call straight to the leader and won't initiate in serialization
if(LOG.isTraceEnabled()) {
LOG.trace("{}: handleAppendEntriesReply: {}", logName(), appendEntriesReply);
- } else if(LOG.isDebugEnabled() && !appendEntriesReply.isSuccess()) {
- LOG.debug("{}: handleAppendEntriesReply: {}", logName(), appendEntriesReply);
}
// Update the FollowerLogInformation
if(followerLogInformation.timeSinceLastActivity() >
context.getConfigParams().getElectionTimeOutInterval().toMillis()) {
- LOG.error("{} : handleAppendEntriesReply delayed beyond election timeout, " +
+ LOG.warn("{} : handleAppendEntriesReply delayed beyond election timeout, " +
"appendEntriesReply : {}, timeSinceLastActivity : {}, lastApplied : {}, commitIndex : {}",
logName(), appendEntriesReply, followerLogInformation.timeSinceLastActivity(),
context.getLastApplied(), context.getCommitIndex());
followerLogInformation.markFollowerActive();
+ boolean updated = false;
if (appendEntriesReply.isSuccess()) {
- followerLogInformation
- .setMatchIndex(appendEntriesReply.getLogLastIndex());
- followerLogInformation
- .setNextIndex(appendEntriesReply.getLogLastIndex() + 1);
+ updated = followerLogInformation.setMatchIndex(appendEntriesReply.getLogLastIndex());
+ updated = followerLogInformation.setNextIndex(appendEntriesReply.getLogLastIndex() + 1) || updated;
+
+ if(updated && LOG.isDebugEnabled()) {
+ LOG.debug("{}: handleAppendEntriesReply - FollowerLogInformation for {} updated: matchIndex: {}, nextIndex: {}", logName(),
+ followerId, followerLogInformation.getMatchIndex(), followerLogInformation.getNextIndex());
+ }
} else {
+ LOG.debug("{}: handleAppendEntriesReply: received unsuccessful reply: {}", logName(), appendEntriesReply);
// TODO: When we find that the follower is out of sync with the
// Leader we simply decrement that followers next index by 1.
// Apply the change to the state machine
if (context.getCommitIndex() > context.getLastApplied()) {
- LOG.debug("{}: handleAppendEntriesReply: applying to log - commitIndex: {}, lastAppliedIndex: {}",
- logName(), context.getCommitIndex(), context.getLastApplied());
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("{}: handleAppendEntriesReply from {}: applying to log - commitIndex: {}, lastAppliedIndex: {}",
+ logName(), followerId, context.getCommitIndex(), context.getLastApplied());
+ }
applyLogToStateMachine(context.getCommitIndex());
}
}
//Send the next log entry immediately, if possible, no need to wait for heartbeat to trigger that event
- sendUpdatesToFollower(followerId, followerLogInformation, false, false);
+ sendUpdatesToFollower(followerId, followerLogInformation, false, !updated);
return this;
}
followerToSnapshot.markSendStatus(false);
}
- if (!wasLastChunk && followerToSnapshot.canSendNextChunk()) {
+ if (wasLastChunk && !context.isSnapshotCaptureInitiated()) {
+ // Since the follower is now caught up try to purge the log.
+ purgeInMemoryLog();
+ } else if (!wasLastChunk && followerToSnapshot.canSendNextChunk()) {
ActorSelection followerActor = context.getPeerActorSelection(followerId);
if(followerActor != null) {
sendSnapshotChunk(followerActor, followerId);
long leaderLastIndex = context.getReplicatedLog().lastIndex();
long leaderSnapShotIndex = context.getReplicatedLog().getSnapshotIndex();
- if(!isHeartbeat || LOG.isTraceEnabled()) {
- LOG.debug("{}: Checking sendAppendEntries for follower {}, leaderLastIndex: {}, leaderSnapShotIndex: {}",
- logName(), followerId, leaderLastIndex, leaderSnapShotIndex);
+ if((!isHeartbeat && LOG.isDebugEnabled()) || LOG.isTraceEnabled()) {
+ LOG.debug("{}: Checking sendAppendEntries for follower {}, followerNextIndex {}, leaderLastIndex: {}, leaderSnapShotIndex: {}",
+ logName(), followerId, followerNextIndex, leaderLastIndex, leaderSnapShotIndex);
}
if (isFollowerActive && context.getReplicatedLog().isPresent(followerNextIndex)) {
} else if (!context.isSnapshotCaptureInitiated()) {
- LOG.info("{}: Initiating Snapshot Capture to Install Snapshot, Leader:{}", logName(), getLeaderId());
ReplicatedLogEntry lastAppliedEntry = context.getReplicatedLog().get(context.getLastApplied());
long lastAppliedIndex = -1;
long lastAppliedTerm = -1;
boolean isInstallSnapshotInitiated = true;
long replicatedToAllIndex = super.getReplicatedToAllIndex();
ReplicatedLogEntry replicatedToAllEntry = context.getReplicatedLog().get(replicatedToAllIndex);
- actor().tell(new CaptureSnapshot(lastIndex(), lastTerm(), lastAppliedIndex, lastAppliedTerm,
- (replicatedToAllEntry != null ? replicatedToAllEntry.getIndex() : -1),
- (replicatedToAllEntry != null ? replicatedToAllEntry.getTerm() : -1),
- isInstallSnapshotInitiated), actor());
+
+ CaptureSnapshot captureSnapshot = new CaptureSnapshot(
+ lastIndex(), lastTerm(), lastAppliedIndex, lastAppliedTerm,
+ (replicatedToAllEntry != null ? replicatedToAllEntry.getIndex() : -1),
+ (replicatedToAllEntry != null ? replicatedToAllEntry.getTerm() : -1),
+ isInstallSnapshotInitiated);
+
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("{}: Initiating install snapshot to follower {}: {}", logName(), followerId,
+ captureSnapshot);
+ }
+
+ actor().tell(captureSnapshot, actor());
context.setSnapshotCaptureInitiated(true);
}
}
).toSerializable(),
actor()
);
- LOG.info("{}: InstallSnapshot sent to follower {}, Chunk: {}/{}",
- logName(), followerActor.path(),
- followerToSnapshot.getChunkIndex(),
- followerToSnapshot.getTotalChunks());
+
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("{}: InstallSnapshot sent to follower {}, Chunk: {}/{}",
+ logName(), followerActor.path(), followerToSnapshot.getChunkIndex(),
+ followerToSnapshot.getTotalChunks());
+ }
}
} catch (IOException e) {
LOG.error("{}: InstallSnapshot failed for Leader.", logName(), e);
long lastApplied = context.getLastApplied();
long tempMin = Math.min(snapshotCapturedIndex, (lastApplied > -1 ? lastApplied - 1 : -1));
+ if(LOG.isTraceEnabled()) {
+ LOG.trace("{}: performSnapshotWithoutCapture: snapshotCapturedIndex: {}, lastApplied: {}, tempMin: {}",
+ logName, snapshotCapturedIndex, lastApplied, tempMin);
+ }
+
if (tempMin > -1 && context.getReplicatedLog().isPresent(tempMin)) {
LOG.debug("{}: fakeSnapshot purging log to {} for term {}", logName(), tempMin,
context.getTermInformation().getCurrentTerm());
context.getReplicatedLog().snapshotPreCommit(tempMin, entry.getTerm());
context.getReplicatedLog().snapshotCommit();
setReplicatedToAllIndex(tempMin);
+ } else if(tempMin > getReplicatedToAllIndex()) {
+ // It's possible a follower was lagging and an install snapshot advanced its match index past
+ // the current replicatedToAllIndex. Since the follower is now caught up we should advance the
+ // replicatedToAllIndex (to tempMin). The fact that tempMin wasn't found in the log is likely
+ // due to a previous snapshot triggered by the memory threshold exceeded, in that case we
+ // trim the log to the last applied index even if previous entries weren't replicated to all followers.
+ setReplicatedToAllIndex(tempMin);
}
}
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-core-api</artifactId>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-test-model</artifactId>
- <scope>test</scope>
- </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>binding-generator-impl</artifactId>
public <T extends RpcService> BindingDOMRpcImplementationAdapter(final BindingNormalizedNodeCodecRegistry codec, final Class<T> type ,final T delegate) {
this.codec = codec;
this.delegate = delegate;
- this.invoker = RpcServiceInvoker.from(type);
- this.module = BindingReflections.getQNameModule(type);
+ invoker = RpcServiceInvoker.from(type);
+ module = BindingReflections.getQNameModule(type);
}
public QNameModule getQNameModule() {
@Override
public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final DOMRpcIdentifier rpc, final NormalizedNode<?, ?> input) {
final SchemaPath schemaPath = rpc.getType();
- final DataObject bindingInput = deserilialize(rpc.getType(),input);
+ final DataObject bindingInput = input != null ? deserilialize(rpc.getType(),input) : null;
final ListenableFuture<RpcResult<?>> bindingResult = invoke(schemaPath,bindingInput);
return transformResult(schemaPath,bindingResult);
}
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.JdkFutureAdapters;
import com.google.common.util.concurrent.ListenableFuture;
+import javax.annotation.Nullable;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
private static final Function<? super Exception, DOMRpcException> EXCEPTION_MAPPER = new Function<Exception, DOMRpcException>() {
@Override
- public DOMRpcException apply(Exception input) {
+ public DOMRpcException apply(final Exception input) {
// FIXME: Return correct exception
return null;
}
private final RpcService delegate;
private final QNameModule module;
- private Function<RpcResult<?>,DOMRpcResult> lazySerializedMapper = new Function<RpcResult<?>,DOMRpcResult>() {
+ private final Function<RpcResult<?>,DOMRpcResult> lazySerializedMapper = new Function<RpcResult<?>,DOMRpcResult>() {
@Override
public DOMRpcResult apply(final RpcResult<?> input) {
}
};
- public <T extends RpcService> BindingRpcImplementationAdapter(BindingNormalizedNodeCodecRegistry codec, Class<T> type ,T delegate) {
+ public <T extends RpcService> BindingRpcImplementationAdapter(final BindingNormalizedNodeCodecRegistry codec, final Class<T> type ,final T delegate) {
this.codec = codec;
this.delegate = delegate;
- this.invoker = RpcServiceInvoker.from(type);
- this.module = BindingReflections.getQNameModule(type);
+ invoker = RpcServiceInvoker.from(type);
+ module = BindingReflections.getQNameModule(type);
}
public QNameModule getQNameModule() {
}
@Override
- public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(DOMRpcIdentifier rpc, NormalizedNode<?, ?> input) {
- SchemaPath schemaPath = rpc.getType();
- DataObject bindingInput = deserilialize(rpc.getType(),input);
- ListenableFuture<RpcResult<?>> bindingResult = invoke(schemaPath,bindingInput);
+ public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final DOMRpcIdentifier rpc, @Nullable final NormalizedNode<?, ?> input) {
+ final SchemaPath schemaPath = rpc.getType();
+ final DataObject bindingInput = input != null ? deserilialize(rpc.getType(),input) : null;
+ final ListenableFuture<RpcResult<?>> bindingResult = invoke(schemaPath, bindingInput);
return transformResult(schemaPath,bindingResult);
}
- private DataObject deserilialize(SchemaPath rpcPath, NormalizedNode<?, ?> input) {
+ private DataObject deserilialize(final SchemaPath rpcPath, final NormalizedNode<?, ?> input) {
if(input instanceof LazySerializedContainerNode) {
return ((LazySerializedContainerNode) input).bindingData();
}
- SchemaPath inputSchemaPath = rpcPath.createChild(QName.create(module,"input"));
+ final SchemaPath inputSchemaPath = rpcPath.createChild(QName.create(module,"input"));
return codec.fromNormalizedNodeRpcData(inputSchemaPath, (ContainerNode) input);
}
- private ListenableFuture<RpcResult<?>> invoke(SchemaPath schemaPath, DataObject input) {
+ private ListenableFuture<RpcResult<?>> invoke(final SchemaPath schemaPath, final DataObject input) {
return JdkFutureAdapters.listenInPoolThread(invoker.invokeRpc(delegate, schemaPath.getLastComponent(), input));
}
- private CheckedFuture<DOMRpcResult, DOMRpcException> transformResult(SchemaPath schemaPath,
- ListenableFuture<RpcResult<?>> bindingResult) {
- ListenableFuture<DOMRpcResult> transformed = Futures.transform(bindingResult, lazySerializedMapper);
+ private CheckedFuture<DOMRpcResult, DOMRpcException> transformResult(final SchemaPath schemaPath,
+ final ListenableFuture<RpcResult<?>> bindingResult) {
+ final ListenableFuture<DOMRpcResult> transformed = Futures.transform(bindingResult, lazySerializedMapper);
return Futures.makeChecked(transformed, EXCEPTION_MAPPER);
}
import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
}
public static final QName OPERATION_ATTRIBUTE_QNAME = QName.create(URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"), null, "operation");
- private static final Logger logger = LoggerFactory.getLogger(XmlDocumentUtils.class);
+ private static final Logger LOG = LoggerFactory.getLogger(XmlDocumentUtils.class);
private static final XMLOutputFactory FACTORY = XMLOutputFactory.newFactory();
/**
writer.close();
return (Document)result.getNode();
} catch (XMLStreamException e) {
- logger.error("Failed to serialize data {}", data, e);
+ LOG.error("Failed to serialize data {}", data, e);
return null;
}
}
String text = xmlElement.getTextContent();
Object value = null;
if (codec != null) {
- logger.debug("toSimpleNodeWithType: found codec, deserializing text {}", text);
+ LOG.debug("toSimpleNodeWithType: found codec, deserializing text {}", text);
value = codec.deserialize(text);
}
final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(schema.getType());
if (baseType instanceof InstanceIdentifierType) {
- logger.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement);
+ LOG.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement);
value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
} else if(baseType instanceof IdentityrefTypeDefinition){
- logger.debug("toSimpleNodeWithType: base type of node is IdentityrefTypeDefinition, deserializing element", xmlElement);
+ LOG.debug("toSimpleNodeWithType: base type of node is IdentityrefTypeDefinition, deserializing element", xmlElement);
value = InstanceIdentifierForXmlCodec.toIdentity(xmlElement.getTextContent(), xmlElement, schemaCtx);
}
if (value == null) {
- logger.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement);
+ LOG.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement);
value = xmlElement.getTextContent();
}
String text = xmlElement.getTextContent();
Object value = null;
if (codec != null) {
- logger.debug("toSimpleNodeWithType: found codec, deserializing text {}", text);
+ LOG.debug("toSimpleNodeWithType: found codec, deserializing text {}", text);
value = codec.deserialize(text);
}
final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(schema.getType());
if (baseType instanceof InstanceIdentifierType) {
- logger.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement);
+ LOG.debug("toSimpleNodeWithType: base type of node is instance identifier, deserializing element", xmlElement);
value = InstanceIdentifierForXmlCodec.deserialize(xmlElement,schemaCtx);
}
if (value == null) {
- logger.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement);
+ LOG.debug("toSimpleNodeWithType: no type found for element, returning just the text string value of element {}", xmlElement);
value = xmlElement.getTextContent();
}
for (DataSchemaNode dsn : dataSchemaNode) {
if (qname.isEqualWithoutRevision(dsn.getQName())) {
return Optional.<DataSchemaNode> of(dsn);
- } else if (dsn instanceof ChoiceNode) {
- for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
+ } else if (dsn instanceof ChoiceSchemaNode) {
+ for (ChoiceCaseNode choiceCase : ((ChoiceSchemaNode) dsn).getCases()) {
Optional<DataSchemaNode> foundDsn = findFirstSchema(qname, choiceCase.getChildNodes());
if (foundDsn != null && foundDsn.isPresent()) {
return foundDsn;
import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
private final ImmutableMap<QName, DataNormalizationOperation<?>> byQName;
private final ImmutableMap<PathArgument, DataNormalizationOperation<?>> byArg;
- protected ChoiceNodeNormalization(final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) {
+ protected ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
super(new NodeIdentifier(schema.getQName()),schema);
ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder();
ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder = ImmutableMap.builder();
private static final Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent,final QName child) {
DataSchemaNode potential = parent.getDataChildByName(child);
if (potential == null) {
- Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices = FluentIterable.from(
- parent.getChildNodes()).filter(org.opendaylight.yangtools.yang.model.api.ChoiceNode.class);
+ Iterable<ChoiceSchemaNode> choices = FluentIterable.from(parent.getChildNodes()).filter(ChoiceSchemaNode.class);
potential = findChoice(choices, child);
}
return Optional.fromNullable(potential);
return fromDataSchemaNode(result);
}
- private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice(
- final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices, final QName child) {
- org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null;
- choiceLoop: for (org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) {
+ private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
+ ChoiceSchemaNode foundChoice = null;
+ choiceLoop: for (ChoiceSchemaNode choice : choices) {
for (ChoiceCaseNode caze : choice.getCases()) {
if (findChildSchemaNode(caze, child).isPresent()) {
foundChoice = choice;
return fromListSchemaNode((ListSchemaNode) potential);
} else if (potential instanceof LeafSchemaNode) {
return new LeafNormalization((LeafSchemaNode) potential);
- } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
- return new ChoiceNodeNormalization((org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential);
+ } else if (potential instanceof ChoiceSchemaNode) {
+ return new ChoiceNodeNormalization((ChoiceSchemaNode) potential);
} else if (potential instanceof LeafListSchemaNode) {
return fromLeafListSchemaNode((LeafListSchemaNode) potential);
} else if (potential instanceof AnyXmlSchemaNode) {
<artifactId>sal-akka-raft</artifactId>
<version>1.2.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-akka-raft</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-api</artifactId>
}
@VisibleForTesting
- InMemoryDOMDataStore getDataStore() {
+ public InMemoryDOMDataStore getDataStore() {
return store;
}
public class ShardStats extends AbstractMXBean implements ShardStatsMXBean {
public static String JMX_CATEGORY_SHARD = "Shards";
- private final AtomicLong committedTransactionsCount = new AtomicLong();
+ private long committedTransactionsCount;
- private final AtomicLong readOnlyTransactionCount = new AtomicLong();
+ private long readOnlyTransactionCount;
- private final AtomicLong writeOnlyTransactionCount = new AtomicLong();
+ private long writeOnlyTransactionCount;
- private final AtomicLong readWriteTransactionCount = new AtomicLong();
+ private long readWriteTransactionCount;
private String leader;
private String raftState;
- private volatile long lastLogTerm = -1L;
+ private long lastLogTerm = -1L;
- private volatile long lastLogIndex = -1L;
+ private long lastLogIndex = -1L;
- private volatile long currentTerm = -1L;
+ private long currentTerm = -1L;
- private volatile long commitIndex = -1L;
+ private long commitIndex = -1L;
- private volatile long lastApplied = -1L;
+ private long lastApplied = -1L;
- private volatile long lastCommittedTransactionTime;
+ private long lastCommittedTransactionTime;
- private final AtomicLong failedTransactionsCount = new AtomicLong();
+ private long failedTransactionsCount;
private final AtomicLong failedReadTransactionsCount = new AtomicLong();
- private final AtomicLong abortTransactionsCount = new AtomicLong();
+ private long abortTransactionsCount;
private ThreadExecutorStatsMXBeanImpl notificationExecutorStatsBean;
private QueuedNotificationManagerMXBeanImpl notificationManagerStatsBean;
- private volatile long dataSize = 0;
+ private long dataSize = 0;
private final SimpleDateFormat sdf =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
@Override
public long getCommittedTransactionsCount() {
- return committedTransactionsCount.get();
+ return committedTransactionsCount;
}
@Override
@Override
public long getReadOnlyTransactionCount() {
- return readOnlyTransactionCount.get();
+ return readOnlyTransactionCount;
}
@Override
public long getWriteOnlyTransactionCount() {
- return writeOnlyTransactionCount.get();
+ return writeOnlyTransactionCount;
}
@Override
public long getReadWriteTransactionCount() {
- return readWriteTransactionCount.get();
+ return readWriteTransactionCount;
}
@Override
@Override
public long getFailedTransactionsCount() {
- return failedTransactionsCount.get();
+ return failedTransactionsCount;
}
@Override
@Override
public long getAbortTransactionsCount() {
- return abortTransactionsCount.get();
+ return abortTransactionsCount;
}
public long incrementCommittedTransactionCount() {
- return committedTransactionsCount.incrementAndGet();
+ return ++committedTransactionsCount;
}
public long incrementReadOnlyTransactionCount() {
- return readOnlyTransactionCount.incrementAndGet();
+ return ++readOnlyTransactionCount;
}
public long incrementWriteOnlyTransactionCount() {
- return writeOnlyTransactionCount.incrementAndGet();
+ return ++writeOnlyTransactionCount;
}
public long incrementReadWriteTransactionCount() {
- return readWriteTransactionCount.incrementAndGet();
+ return ++readWriteTransactionCount;
}
public long incrementFailedTransactionsCount() {
- return failedTransactionsCount.incrementAndGet();
+ return ++failedTransactionsCount;
}
public long incrementFailedReadTransactionsCount() {
public long incrementAbortTransactionsCount ()
{
- return abortTransactionsCount.incrementAndGet();
+ return ++abortTransactionsCount;
}
public void setLeader(final String leader) {
*/
@Override
public void resetTransactionCounters(){
- committedTransactionsCount.set(0);
+ committedTransactionsCount = 0;
- readOnlyTransactionCount.set(0);
+ readOnlyTransactionCount = 0;
- writeOnlyTransactionCount.set(0);
+ writeOnlyTransactionCount = 0;
- readWriteTransactionCount.set(0);
+ readWriteTransactionCount = 0;
lastCommittedTransactionTime = 0;
- failedTransactionsCount.set(0);
+ failedTransactionsCount = 0;
failedReadTransactionsCount.set(0);
- abortTransactionsCount.set(0);
+ abortTransactionsCount = 0;
}
--- /dev/null
+/*
+ * Copyright (c) 2015 Brocade Communications 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.cluster.datastore;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import akka.actor.ActorRef;
+import akka.actor.PoisonPill;
+import akka.actor.Props;
+import akka.japi.Creator;
+import akka.testkit.TestActorRef;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.Uninterruptibles;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.After;
+import org.junit.Before;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.cluster.datastore.DatastoreContext.Builder;
+import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier;
+import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification;
+import org.opendaylight.controller.cluster.datastore.modification.WriteModification;
+import org.opendaylight.controller.cluster.datastore.utils.InMemoryJournal;
+import org.opendaylight.controller.cluster.datastore.utils.InMemorySnapshotStore;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Abstract base for shard unit tests.
+ *
+ * @author Thomas Pantelis
+ */
+public abstract class AbstractShardTest extends AbstractActorTest{
+ protected static final SchemaContext SCHEMA_CONTEXT = TestModel.createTestContext();
+
+ private static final AtomicInteger NEXT_SHARD_NUM = new AtomicInteger();
+
+ protected final ShardIdentifier shardID = ShardIdentifier.builder().memberName("member-1")
+ .shardName("inventory").type("config" + NEXT_SHARD_NUM.getAndIncrement()).build();
+
+ protected final Builder dataStoreContextBuilder = DatastoreContext.newBuilder().
+ shardJournalRecoveryLogBatchSize(3).shardSnapshotBatchCount(5000).
+ shardHeartbeatIntervalInMillis(100);
+
+ @Before
+ public void setUp() {
+ InMemorySnapshotStore.clear();
+ InMemoryJournal.clear();
+ }
+
+ @After
+ public void tearDown() {
+ InMemorySnapshotStore.clear();
+ InMemoryJournal.clear();
+ }
+
+ protected DatastoreContext newDatastoreContext() {
+ return dataStoreContextBuilder.build();
+ }
+
+ protected Props newShardProps() {
+ return Shard.props(shardID, Collections.<ShardIdentifier,String>emptyMap(),
+ newDatastoreContext(), SCHEMA_CONTEXT);
+ }
+
+ protected void testRecovery(Set<Integer> listEntryKeys) throws Exception {
+ // Create the actor and wait for recovery complete.
+
+ int nListEntries = listEntryKeys.size();
+
+ final CountDownLatch recoveryComplete = new CountDownLatch(1);
+
+ @SuppressWarnings("serial")
+ Creator<Shard> creator = new Creator<Shard>() {
+ @Override
+ public Shard create() throws Exception {
+ return new Shard(shardID, Collections.<ShardIdentifier,String>emptyMap(),
+ newDatastoreContext(), SCHEMA_CONTEXT) {
+ @Override
+ protected void onRecoveryComplete() {
+ try {
+ super.onRecoveryComplete();
+ } finally {
+ recoveryComplete.countDown();
+ }
+ }
+ };
+ }
+ };
+
+ TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
+ Props.create(new DelegatingShardCreator(creator)), "testRecovery");
+
+ assertEquals("Recovery complete", true, recoveryComplete.await(5, TimeUnit.SECONDS));
+
+ // Verify data in the data store.
+
+ NormalizedNode<?, ?> outerList = readStore(shard, TestModel.OUTER_LIST_PATH);
+ assertNotNull(TestModel.OUTER_LIST_QNAME.getLocalName() + " not found", outerList);
+ assertTrue(TestModel.OUTER_LIST_QNAME.getLocalName() + " value is not Iterable",
+ outerList.getValue() instanceof Iterable);
+ for(Object entry: (Iterable<?>) outerList.getValue()) {
+ assertTrue(TestModel.OUTER_LIST_QNAME.getLocalName() + " entry is not MapEntryNode",
+ entry instanceof MapEntryNode);
+ MapEntryNode mapEntry = (MapEntryNode)entry;
+ Optional<DataContainerChild<? extends PathArgument, ?>> idLeaf =
+ mapEntry.getChild(new YangInstanceIdentifier.NodeIdentifier(TestModel.ID_QNAME));
+ assertTrue("Missing leaf " + TestModel.ID_QNAME.getLocalName(), idLeaf.isPresent());
+ Object value = idLeaf.get().getValue();
+ assertTrue("Unexpected value for leaf "+ TestModel.ID_QNAME.getLocalName() + ": " + value,
+ listEntryKeys.remove(value));
+ }
+
+ if(!listEntryKeys.isEmpty()) {
+ fail("Missing " + TestModel.OUTER_LIST_QNAME.getLocalName() + " entries with keys: " +
+ listEntryKeys);
+ }
+
+ assertEquals("Last log index", nListEntries,
+ shard.underlyingActor().getShardMBean().getLastLogIndex());
+ assertEquals("Commit index", nListEntries,
+ shard.underlyingActor().getShardMBean().getCommitIndex());
+ assertEquals("Last applied", nListEntries,
+ shard.underlyingActor().getShardMBean().getLastApplied());
+
+ shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
+ }
+
+ protected void verifyLastLogIndex(TestActorRef<Shard> shard, long expectedValue) {
+ for(int i = 0; i < 20 * 5; i++) {
+ long lastLogIndex = shard.underlyingActor().getShardMBean().getLastLogIndex();
+ if(lastLogIndex == expectedValue) {
+ break;
+ }
+ Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS);
+ }
+
+ assertEquals("Last log index", expectedValue, shard.underlyingActor().getShardMBean().getLastLogIndex());
+ }
+
+ protected NormalizedNode<?, ?> readStore(final InMemoryDOMDataStore store) throws ReadFailedException {
+ DOMStoreReadTransaction transaction = store.newReadOnlyTransaction();
+ CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read =
+ transaction.read(YangInstanceIdentifier.builder().build());
+
+ Optional<NormalizedNode<?, ?>> optional = read.checkedGet();
+
+ NormalizedNode<?, ?> normalizedNode = optional.get();
+
+ transaction.close();
+
+ return normalizedNode;
+ }
+
+ protected DOMStoreThreePhaseCommitCohort setupMockWriteTransaction(final String cohortName,
+ final InMemoryDOMDataStore dataStore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
+ final MutableCompositeModification modification) {
+ return setupMockWriteTransaction(cohortName, dataStore, path, data, modification, null);
+ }
+
+ protected DOMStoreThreePhaseCommitCohort setupMockWriteTransaction(final String cohortName,
+ final InMemoryDOMDataStore dataStore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
+ final MutableCompositeModification modification,
+ final Function<DOMStoreThreePhaseCommitCohort,ListenableFuture<Void>> preCommit) {
+
+ DOMStoreWriteTransaction tx = dataStore.newWriteOnlyTransaction();
+ tx.write(path, data);
+ DOMStoreThreePhaseCommitCohort cohort = createDelegatingMockCohort(cohortName, tx.ready(), preCommit);
+
+ modification.addModification(new WriteModification(path, data));
+
+ return cohort;
+ }
+
+ protected DOMStoreThreePhaseCommitCohort createDelegatingMockCohort(final String cohortName,
+ final DOMStoreThreePhaseCommitCohort actual) {
+ return createDelegatingMockCohort(cohortName, actual, null);
+ }
+
+ protected DOMStoreThreePhaseCommitCohort createDelegatingMockCohort(final String cohortName,
+ final DOMStoreThreePhaseCommitCohort actual,
+ final Function<DOMStoreThreePhaseCommitCohort,ListenableFuture<Void>> preCommit) {
+ DOMStoreThreePhaseCommitCohort cohort = mock(DOMStoreThreePhaseCommitCohort.class, cohortName);
+
+ doAnswer(new Answer<ListenableFuture<Boolean>>() {
+ @Override
+ public ListenableFuture<Boolean> answer(final InvocationOnMock invocation) {
+ return actual.canCommit();
+ }
+ }).when(cohort).canCommit();
+
+ doAnswer(new Answer<ListenableFuture<Void>>() {
+ @Override
+ public ListenableFuture<Void> answer(final InvocationOnMock invocation) throws Throwable {
+ return actual.preCommit();
+ }
+ }).when(cohort).preCommit();
+
+ doAnswer(new Answer<ListenableFuture<Void>>() {
+ @Override
+ public ListenableFuture<Void> answer(final InvocationOnMock invocation) throws Throwable {
+ return actual.commit();
+ }
+ }).when(cohort).commit();
+
+ doAnswer(new Answer<ListenableFuture<Void>>() {
+ @Override
+ public ListenableFuture<Void> answer(final InvocationOnMock invocation) throws Throwable {
+ return actual.abort();
+ }
+ }).when(cohort).abort();
+
+ return cohort;
+ }
+
+ public static NormalizedNode<?,?> readStore(final TestActorRef<Shard> shard, final YangInstanceIdentifier id)
+ throws ExecutionException, InterruptedException {
+ return readStore(shard.underlyingActor().getDataStore(), id);
+ }
+
+ public static NormalizedNode<?,?> readStore(final InMemoryDOMDataStore store, final YangInstanceIdentifier id)
+ throws ExecutionException, InterruptedException {
+ DOMStoreReadTransaction transaction = store.newReadOnlyTransaction();
+
+ CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
+ transaction.read(id);
+
+ Optional<NormalizedNode<?, ?>> optional = future.get();
+ NormalizedNode<?, ?> node = optional.isPresent()? optional.get() : null;
+
+ transaction.close();
+
+ return node;
+ }
+
+ public static void writeToStore(final TestActorRef<Shard> shard, final YangInstanceIdentifier id,
+ final NormalizedNode<?,?> node) throws ExecutionException, InterruptedException {
+ writeToStore(shard.underlyingActor().getDataStore(), id, node);
+ }
+
+ public static void writeToStore(final InMemoryDOMDataStore store, final YangInstanceIdentifier id,
+ final NormalizedNode<?,?> node) throws ExecutionException, InterruptedException {
+ DOMStoreWriteTransaction transaction = store.newWriteOnlyTransaction();
+
+ transaction.write(id, node);
+
+ DOMStoreThreePhaseCommitCohort commitCohort = transaction.ready();
+ commitCohort.preCommit().get();
+ commitCohort.commit().get();
+ }
+
+ @SuppressWarnings("serial")
+ public static final class DelegatingShardCreator implements Creator<Shard> {
+ private final Creator<Shard> delegate;
+
+ DelegatingShardCreator(final Creator<Shard> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public Shard create() throws Exception {
+ return delegate.create();
+ }
+ }
+}
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import akka.util.Timeout;
import com.google.common.base.Function;
import com.google.common.base.Optional;
-import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import org.opendaylight.controller.cluster.DataPersistenceProvider;
-import org.opendaylight.controller.cluster.datastore.DatastoreContext.Builder;
import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier;
import org.opendaylight.controller.cluster.datastore.messages.AbortTransaction;
import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply;
import org.opendaylight.controller.cluster.datastore.modification.ModificationPayload;
import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification;
import org.opendaylight.controller.cluster.datastore.modification.WriteModification;
-import org.opendaylight.controller.cluster.datastore.node.NormalizedNodeToNodeCodec;
import org.opendaylight.controller.cluster.datastore.utils.InMemoryJournal;
import org.opendaylight.controller.cluster.datastore.utils.InMemorySnapshotStore;
import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor;
import org.opendaylight.controller.cluster.raft.ReplicatedLogImplEntry;
import org.opendaylight.controller.cluster.raft.Snapshot;
import org.opendaylight.controller.cluster.raft.base.messages.ApplyJournalEntries;
-import org.opendaylight.controller.cluster.raft.base.messages.ApplyLogEntries;
import org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot;
import org.opendaylight.controller.cluster.raft.base.messages.ApplyState;
import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot;
import org.opendaylight.controller.cluster.raft.base.messages.FollowerInitialSyncUpStatus;
import org.opendaylight.controller.cluster.raft.client.messages.FindLeader;
import org.opendaylight.controller.cluster.raft.client.messages.FindLeaderReply;
-import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationByteStringPayload;
-import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload;
import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelper;
import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory;
-import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages;
import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages.CreateTransactionReply;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.FiniteDuration;
-public class ShardTest extends AbstractActorTest {
-
- private static final SchemaContext SCHEMA_CONTEXT = TestModel.createTestContext();
-
- private static final AtomicInteger NEXT_SHARD_NUM = new AtomicInteger();
-
- private final ShardIdentifier shardID = ShardIdentifier.builder().memberName("member-1")
- .shardName("inventory").type("config" + NEXT_SHARD_NUM.getAndIncrement()).build();
-
- private final Builder dataStoreContextBuilder = DatastoreContext.newBuilder().
- shardJournalRecoveryLogBatchSize(3).shardSnapshotBatchCount(5000).
- shardHeartbeatIntervalInMillis(100);
-
- @Before
- public void setUp() {
- Builder newBuilder = DatastoreContext.newBuilder();
- InMemorySnapshotStore.clear();
- InMemoryJournal.clear();
- }
-
- @After
- public void tearDown() {
- InMemorySnapshotStore.clear();
- InMemoryJournal.clear();
- }
-
- private DatastoreContext newDatastoreContext() {
- return dataStoreContextBuilder.build();
- }
-
- private Props newShardProps() {
- return Shard.props(shardID, Collections.<ShardIdentifier,String>emptyMap(),
- newDatastoreContext(), SCHEMA_CONTEXT);
- }
-
+public class ShardTest extends AbstractShardTest {
@Test
public void testRegisterChangeListener() throws Exception {
new ShardTestKit(getSystem()) {{
shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
}
- @Test
- public void testApplyHelium2VersionSnapshot() throws Exception {
- TestActorRef<Shard> shard = TestActorRef.create(getSystem(), newShardProps(),
- "testApplySnapshot");
-
- NormalizedNodeToNodeCodec codec = new NormalizedNodeToNodeCodec(SCHEMA_CONTEXT);
-
- InMemoryDOMDataStore store = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
- store.onGlobalContextUpdated(SCHEMA_CONTEXT);
-
- writeToStore(store, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
-
- YangInstanceIdentifier root = YangInstanceIdentifier.builder().build();
- NormalizedNode<?,?> expected = readStore(store, root);
-
- NormalizedNodeMessages.Container encode = codec.encode(expected);
-
- ApplySnapshot applySnapshot = new ApplySnapshot(Snapshot.create(
- encode.getNormalizedNode().toByteString().toByteArray(),
- Collections.<ReplicatedLogEntry>emptyList(), 1, 2, 3, 4));
-
- shard.underlyingActor().onReceiveCommand(applySnapshot);
-
- NormalizedNode<?,?> actual = readStore(shard, root);
-
- assertEquals("Root node", expected, actual);
-
- shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
- }
-
@Test
public void testApplyState() throws Exception {
shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
}
- @Test
- public void testApplyStateLegacy() throws Exception {
-
- TestActorRef<Shard> shard = TestActorRef.create(getSystem(), newShardProps(), "testApplyStateLegacy");
-
- NormalizedNode<?, ?> node = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
-
- ApplyState applyState = new ApplyState(null, "test", new ReplicatedLogImplEntry(1, 2,
- newLegacyByteStringPayload(new WriteModification(TestModel.TEST_PATH, node))));
-
- shard.underlyingActor().onReceiveCommand(applyState);
-
- NormalizedNode<?,?> actual = readStore(shard, TestModel.TEST_PATH);
- assertEquals("Applied state", node, actual);
-
- shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
- }
-
@Test
public void testRecovery() throws Exception {
// Set up the InMemoryJournal.
- InMemoryJournal.addEntry(shardID.toString(), 0, new ReplicatedLogImplEntry(0, 1, newLegacyPayload(
+ InMemoryJournal.addEntry(shardID.toString(), 0, new ReplicatedLogImplEntry(0, 1, newModificationPayload(
new WriteModification(TestModel.OUTER_LIST_PATH,
ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build()))));
testRecovery(listEntryKeys);
}
- @Test
- public void testHelium2VersionRecovery() throws Exception {
-
- // Set up the InMemorySnapshotStore.
-
- InMemoryDOMDataStore testStore = InMemoryDOMDataStoreFactory.create("Test", null, null);
- testStore.onGlobalContextUpdated(SCHEMA_CONTEXT);
-
- writeToStore(testStore, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
-
- NormalizedNode<?, ?> root = readStore(testStore, YangInstanceIdentifier.builder().build());
-
- InMemorySnapshotStore.addSnapshot(shardID.toString(), Snapshot.create(
- new NormalizedNodeToNodeCodec(SCHEMA_CONTEXT).encode(root).
- getNormalizedNode().toByteString().toByteArray(),
- Collections.<ReplicatedLogEntry>emptyList(), 0, 1, -1, -1));
-
- // Set up the InMemoryJournal.
-
- InMemoryJournal.addEntry(shardID.toString(), 0, new ReplicatedLogImplEntry(0, 1, newLegacyPayload(
- new WriteModification(TestModel.OUTER_LIST_PATH,
- ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build()))));
-
- int nListEntries = 16;
- Set<Integer> listEntryKeys = new HashSet<>();
- int i = 1;
-
- // Add some CompositeModificationPayload entries
- for(; i <= 8; i++) {
- listEntryKeys.add(Integer.valueOf(i));
- YangInstanceIdentifier path = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
- .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i).build();
- Modification mod = new MergeModification(path,
- ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i));
- InMemoryJournal.addEntry(shardID.toString(), i, new ReplicatedLogImplEntry(i, 1,
- newLegacyPayload(mod)));
- }
-
- // Add some CompositeModificationByteStringPayload entries
- for(; i <= nListEntries; i++) {
- listEntryKeys.add(Integer.valueOf(i));
- YangInstanceIdentifier path = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
- .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i).build();
- Modification mod = new MergeModification(path,
- ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i));
- InMemoryJournal.addEntry(shardID.toString(), i, new ReplicatedLogImplEntry(i, 1,
- newLegacyByteStringPayload(mod)));
- }
-
- InMemoryJournal.addEntry(shardID.toString(), nListEntries + 1, new ApplyLogEntries(nListEntries));
-
- testRecovery(listEntryKeys);
- }
-
- private void testRecovery(Set<Integer> listEntryKeys) throws Exception {
- // Create the actor and wait for recovery complete.
-
- int nListEntries = listEntryKeys.size();
-
- final CountDownLatch recoveryComplete = new CountDownLatch(1);
-
- @SuppressWarnings("serial")
- Creator<Shard> creator = new Creator<Shard>() {
- @Override
- public Shard create() throws Exception {
- return new Shard(shardID, Collections.<ShardIdentifier,String>emptyMap(),
- newDatastoreContext(), SCHEMA_CONTEXT) {
- @Override
- protected void onRecoveryComplete() {
- try {
- super.onRecoveryComplete();
- } finally {
- recoveryComplete.countDown();
- }
- }
- };
- }
- };
-
- TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
- Props.create(new DelegatingShardCreator(creator)), "testRecovery");
-
- assertEquals("Recovery complete", true, recoveryComplete.await(5, TimeUnit.SECONDS));
-
- // Verify data in the data store.
-
- NormalizedNode<?, ?> outerList = readStore(shard, TestModel.OUTER_LIST_PATH);
- assertNotNull(TestModel.OUTER_LIST_QNAME.getLocalName() + " not found", outerList);
- assertTrue(TestModel.OUTER_LIST_QNAME.getLocalName() + " value is not Iterable",
- outerList.getValue() instanceof Iterable);
- for(Object entry: (Iterable<?>) outerList.getValue()) {
- assertTrue(TestModel.OUTER_LIST_QNAME.getLocalName() + " entry is not MapEntryNode",
- entry instanceof MapEntryNode);
- MapEntryNode mapEntry = (MapEntryNode)entry;
- Optional<DataContainerChild<? extends PathArgument, ?>> idLeaf =
- mapEntry.getChild(new YangInstanceIdentifier.NodeIdentifier(TestModel.ID_QNAME));
- assertTrue("Missing leaf " + TestModel.ID_QNAME.getLocalName(), idLeaf.isPresent());
- Object value = idLeaf.get().getValue();
- assertTrue("Unexpected value for leaf "+ TestModel.ID_QNAME.getLocalName() + ": " + value,
- listEntryKeys.remove(value));
- }
-
- if(!listEntryKeys.isEmpty()) {
- fail("Missing " + TestModel.OUTER_LIST_QNAME.getLocalName() + " entries with keys: " +
- listEntryKeys);
- }
-
- assertEquals("Last log index", nListEntries,
- shard.underlyingActor().getShardMBean().getLastLogIndex());
- assertEquals("Commit index", nListEntries,
- shard.underlyingActor().getShardMBean().getCommitIndex());
- assertEquals("Last applied", nListEntries,
- shard.underlyingActor().getShardMBean().getLastApplied());
-
- shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
- }
-
- private CompositeModificationPayload newLegacyPayload(final Modification... mods) {
- MutableCompositeModification compMod = new MutableCompositeModification();
- for(Modification mod: mods) {
- compMod.addModification(mod);
- }
-
- return new CompositeModificationPayload(compMod.toSerializable());
- }
-
- private CompositeModificationByteStringPayload newLegacyByteStringPayload(final Modification... mods) {
- MutableCompositeModification compMod = new MutableCompositeModification();
- for(Modification mod: mods) {
- compMod.addModification(mod);
- }
-
- return new CompositeModificationByteStringPayload(compMod.toSerializable());
- }
-
private ModificationPayload newModificationPayload(final Modification... mods) throws IOException {
MutableCompositeModification compMod = new MutableCompositeModification();
for(Modification mod: mods) {
return new ModificationPayload(compMod);
}
- private DOMStoreThreePhaseCommitCohort setupMockWriteTransaction(final String cohortName,
- final InMemoryDOMDataStore dataStore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
- final MutableCompositeModification modification) {
- return setupMockWriteTransaction(cohortName, dataStore, path, data, modification, null);
- }
-
- private DOMStoreThreePhaseCommitCohort setupMockWriteTransaction(final String cohortName,
- final InMemoryDOMDataStore dataStore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
- final MutableCompositeModification modification,
- final Function<DOMStoreThreePhaseCommitCohort,ListenableFuture<Void>> preCommit) {
-
- DOMStoreWriteTransaction tx = dataStore.newWriteOnlyTransaction();
- tx.write(path, data);
- final DOMStoreThreePhaseCommitCohort realCohort = tx.ready();
- DOMStoreThreePhaseCommitCohort cohort = mock(DOMStoreThreePhaseCommitCohort.class, cohortName);
-
- doAnswer(new Answer<ListenableFuture<Boolean>>() {
- @Override
- public ListenableFuture<Boolean> answer(final InvocationOnMock invocation) {
- return realCohort.canCommit();
- }
- }).when(cohort).canCommit();
-
- doAnswer(new Answer<ListenableFuture<Void>>() {
- @Override
- public ListenableFuture<Void> answer(final InvocationOnMock invocation) throws Throwable {
- if(preCommit != null) {
- return preCommit.apply(realCohort);
- } else {
- return realCohort.preCommit();
- }
- }
- }).when(cohort).preCommit();
-
- doAnswer(new Answer<ListenableFuture<Void>>() {
- @Override
- public ListenableFuture<Void> answer(final InvocationOnMock invocation) throws Throwable {
- return realCohort.commit();
- }
- }).when(cohort).commit();
-
- doAnswer(new Answer<ListenableFuture<Void>>() {
- @Override
- public ListenableFuture<Void> answer(final InvocationOnMock invocation) throws Throwable {
- return realCohort.abort();
- }
- }).when(cohort).abort();
-
- modification.addModification(new WriteModification(path, data));
-
- return cohort;
- }
-
@SuppressWarnings({ "unchecked" })
@Test
public void testConcurrentThreePhaseCommits() throws Throwable {
}};
}
- private void verifyLastLogIndex(TestActorRef<Shard> shard, long expectedValue) {
- for(int i = 0; i < 20 * 5; i++) {
- long lastLogIndex = shard.underlyingActor().getShardMBean().getLastLogIndex();
- if(lastLogIndex == expectedValue) {
- break;
- }
- Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS);
- }
-
- assertEquals("Last log index", expectedValue, shard.underlyingActor().getShardMBean().getLastLogIndex());
- }
-
@Test
public void testCommitWithPersistenceDisabled() throws Throwable {
dataStoreContextBuilder.persistent(false);
shard2.tell(PoisonPill.getInstance(), ActorRef.noSender());
}};
-
}
@Test
shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
}
-
- private NormalizedNode<?, ?> readStore(final InMemoryDOMDataStore store) throws ReadFailedException {
- DOMStoreReadTransaction transaction = store.newReadOnlyTransaction();
- CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read =
- transaction.read(YangInstanceIdentifier.builder().build());
-
- Optional<NormalizedNode<?, ?>> optional = read.checkedGet();
-
- NormalizedNode<?, ?> normalizedNode = optional.get();
-
- transaction.close();
-
- return normalizedNode;
- }
-
private void commitTransaction(final DOMStoreWriteTransaction transaction) {
DOMStoreThreePhaseCommitCohort commitCohort = transaction.ready();
ListenableFuture<Void> future =
} catch (InterruptedException | ExecutionException e) {
}
}
-
- private AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> noOpDataChangeListener() {
- return new AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>() {
- @Override
- public void onDataChanged(
- final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
-
- }
- };
- }
-
- static NormalizedNode<?,?> readStore(final TestActorRef<Shard> shard, final YangInstanceIdentifier id)
- throws ExecutionException, InterruptedException {
- return readStore(shard.underlyingActor().getDataStore(), id);
- }
-
- public static NormalizedNode<?,?> readStore(final InMemoryDOMDataStore store, final YangInstanceIdentifier id)
- throws ExecutionException, InterruptedException {
- DOMStoreReadTransaction transaction = store.newReadOnlyTransaction();
-
- CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
- transaction.read(id);
-
- Optional<NormalizedNode<?, ?>> optional = future.get();
- NormalizedNode<?, ?> node = optional.isPresent()? optional.get() : null;
-
- transaction.close();
-
- return node;
- }
-
- static void writeToStore(final TestActorRef<Shard> shard, final YangInstanceIdentifier id,
- final NormalizedNode<?,?> node) throws ExecutionException, InterruptedException {
- writeToStore(shard.underlyingActor().getDataStore(), id, node);
- }
-
- public static void writeToStore(final InMemoryDOMDataStore store, final YangInstanceIdentifier id,
- final NormalizedNode<?,?> node) throws ExecutionException, InterruptedException {
- DOMStoreWriteTransaction transaction = store.newWriteOnlyTransaction();
-
- transaction.write(id, node);
-
- DOMStoreThreePhaseCommitCohort commitCohort = transaction.ready();
- commitCohort.preCommit().get();
- commitCohort.commit().get();
- }
-
- @SuppressWarnings("serial")
- private static final class DelegatingShardCreator implements Creator<Shard> {
- private final Creator<Shard> delegate;
-
- DelegatingShardCreator(final Creator<Shard> delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public Shard create() throws Exception {
- return delegate.create();
- }
- }
}
import akka.testkit.JavaTestKit;
import akka.util.Timeout;
import com.google.common.util.concurrent.Uninterruptibles;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import org.junit.Assert;
import org.opendaylight.controller.cluster.raft.client.messages.FindLeader;
import org.opendaylight.controller.cluster.raft.client.messages.FindLeaderReply;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-class ShardTestKit extends JavaTestKit {
+public class ShardTestKit extends JavaTestKit {
- ShardTestKit(ActorSystem actorSystem) {
+ protected ShardTestKit(ActorSystem actorSystem) {
super(actorSystem);
}
--- /dev/null
+/*
+ * Copyright (c) 2015 Brocade Communications 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.cluster.datastore.compat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.inOrder;
+import static org.opendaylight.controller.cluster.datastore.DataStoreVersions.CURRENT_VERSION;
+import akka.actor.ActorRef;
+import akka.actor.PoisonPill;
+import akka.dispatch.Dispatchers;
+import akka.dispatch.OnComplete;
+import akka.pattern.Patterns;
+import akka.testkit.TestActorRef;
+import akka.util.Timeout;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.opendaylight.controller.cluster.datastore.AbstractShardTest;
+import org.opendaylight.controller.cluster.datastore.Shard;
+import org.opendaylight.controller.cluster.datastore.ShardTestKit;
+import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.CommitTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CommitTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.ForwardedReadyTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply;
+import org.opendaylight.controller.cluster.datastore.modification.MergeModification;
+import org.opendaylight.controller.cluster.datastore.modification.Modification;
+import org.opendaylight.controller.cluster.datastore.modification.ModificationPayload;
+import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification;
+import org.opendaylight.controller.cluster.datastore.modification.WriteModification;
+import org.opendaylight.controller.cluster.datastore.node.NormalizedNodeToNodeCodec;
+import org.opendaylight.controller.cluster.datastore.utils.InMemoryJournal;
+import org.opendaylight.controller.cluster.datastore.utils.InMemorySnapshotStore;
+import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
+import org.opendaylight.controller.cluster.raft.ReplicatedLogImplEntry;
+import org.opendaylight.controller.cluster.raft.Snapshot;
+import org.opendaylight.controller.cluster.raft.base.messages.ApplyLogEntries;
+import org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot;
+import org.opendaylight.controller.cluster.raft.base.messages.ApplyState;
+import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationByteStringPayload;
+import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory;
+import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import scala.concurrent.Future;
+import scala.concurrent.duration.FiniteDuration;
+
+/**
+ * Unit tests for backwards compatibility with pre-Lithium versions.
+ *
+ * @author Thomas Pantelis
+ */
+public class PreLithiumShardTest extends AbstractShardTest {
+
+ private CompositeModificationPayload newLegacyPayload(final Modification... mods) {
+ MutableCompositeModification compMod = new MutableCompositeModification();
+ for(Modification mod: mods) {
+ compMod.addModification(mod);
+ }
+
+ return new CompositeModificationPayload(compMod.toSerializable());
+ }
+
+ private CompositeModificationByteStringPayload newLegacyByteStringPayload(final Modification... mods) {
+ MutableCompositeModification compMod = new MutableCompositeModification();
+ for(Modification mod: mods) {
+ compMod.addModification(mod);
+ }
+
+ return new CompositeModificationByteStringPayload(compMod.toSerializable());
+ }
+
+ private ModificationPayload newModificationPayload(final Modification... mods) throws IOException {
+ MutableCompositeModification compMod = new MutableCompositeModification();
+ for(Modification mod: mods) {
+ compMod.addModification(mod);
+ }
+
+ return new ModificationPayload(compMod);
+ }
+
+ @Test
+ public void testApplyHelium2VersionSnapshot() throws Exception {
+ TestActorRef<Shard> shard = TestActorRef.create(getSystem(), newShardProps(),
+ "testApplyHelium2VersionSnapshot");
+
+ NormalizedNodeToNodeCodec codec = new NormalizedNodeToNodeCodec(SCHEMA_CONTEXT);
+
+ InMemoryDOMDataStore store = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
+ store.onGlobalContextUpdated(SCHEMA_CONTEXT);
+
+ writeToStore(store, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+ YangInstanceIdentifier root = YangInstanceIdentifier.builder().build();
+ NormalizedNode<?,?> expected = readStore(store, root);
+
+ NormalizedNodeMessages.Container encode = codec.encode(expected);
+
+ ApplySnapshot applySnapshot = new ApplySnapshot(Snapshot.create(
+ encode.getNormalizedNode().toByteString().toByteArray(),
+ Collections.<ReplicatedLogEntry>emptyList(), 1, 2, 3, 4));
+
+ shard.underlyingActor().onReceiveCommand(applySnapshot);
+
+ NormalizedNode<?,?> actual = readStore(shard, root);
+
+ assertEquals("Root node", expected, actual);
+
+ shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
+ }
+
+ @Test
+ public void testHelium2VersionApplyStateLegacy() throws Exception {
+
+ TestActorRef<Shard> shard = TestActorRef.create(getSystem(), newShardProps(), "testHelium2VersionApplyStateLegacy");
+
+ NormalizedNode<?, ?> node = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
+
+ ApplyState applyState = new ApplyState(null, "test", new ReplicatedLogImplEntry(1, 2,
+ newLegacyByteStringPayload(new WriteModification(TestModel.TEST_PATH, node))));
+
+ shard.underlyingActor().onReceiveCommand(applyState);
+
+ NormalizedNode<?,?> actual = readStore(shard, TestModel.TEST_PATH);
+ assertEquals("Applied state", node, actual);
+
+ shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
+ }
+
+ @Test
+ public void testHelium2VersionRecovery() throws Exception {
+
+ // Set up the InMemorySnapshotStore.
+
+ InMemoryDOMDataStore testStore = InMemoryDOMDataStoreFactory.create("Test", null, null);
+ testStore.onGlobalContextUpdated(SCHEMA_CONTEXT);
+
+ writeToStore(testStore, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+ NormalizedNode<?, ?> root = readStore(testStore, YangInstanceIdentifier.builder().build());
+
+ InMemorySnapshotStore.addSnapshot(shardID.toString(), Snapshot.create(
+ new NormalizedNodeToNodeCodec(SCHEMA_CONTEXT).encode(root).
+ getNormalizedNode().toByteString().toByteArray(),
+ Collections.<ReplicatedLogEntry>emptyList(), 0, 1, -1, -1));
+
+ // Set up the InMemoryJournal.
+
+ InMemoryJournal.addEntry(shardID.toString(), 0, new ReplicatedLogImplEntry(0, 1, newLegacyPayload(
+ new WriteModification(TestModel.OUTER_LIST_PATH,
+ ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build()))));
+
+ int nListEntries = 16;
+ Set<Integer> listEntryKeys = new HashSet<>();
+ int i = 1;
+
+ // Add some CompositeModificationPayload entries
+ for(; i <= 8; i++) {
+ listEntryKeys.add(Integer.valueOf(i));
+ YangInstanceIdentifier path = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+ .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i).build();
+ Modification mod = new MergeModification(path,
+ ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i));
+ InMemoryJournal.addEntry(shardID.toString(), i, new ReplicatedLogImplEntry(i, 1,
+ newLegacyPayload(mod)));
+ }
+
+ // Add some CompositeModificationByteStringPayload entries
+ for(; i <= nListEntries; i++) {
+ listEntryKeys.add(Integer.valueOf(i));
+ YangInstanceIdentifier path = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+ .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i).build();
+ Modification mod = new MergeModification(path,
+ ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i));
+ InMemoryJournal.addEntry(shardID.toString(), i, new ReplicatedLogImplEntry(i, 1,
+ newLegacyByteStringPayload(mod)));
+ }
+
+ InMemoryJournal.addEntry(shardID.toString(), nListEntries + 1, new ApplyLogEntries(nListEntries));
+
+ testRecovery(listEntryKeys);
+ }
+
+ @SuppressWarnings({ "unchecked" })
+ @Test
+ public void testPreLithiumConcurrentThreePhaseCommits() throws Throwable {
+ new ShardTestKit(getSystem()) {{
+ final TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
+ newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()),
+ "testConcurrentThreePhaseCommits");
+
+ waitUntilLeader(shard);
+
+ // Setup 3 simulated transactions with mock cohorts backed by real cohorts.
+
+ InMemoryDOMDataStore dataStore = shard.underlyingActor().getDataStore();
+
+ String transactionID1 = "tx1";
+ MutableCompositeModification modification1 = new MutableCompositeModification();
+ DOMStoreThreePhaseCommitCohort cohort1 = setupMockWriteTransaction("cohort1", dataStore,
+ TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME), modification1);
+
+ String transactionID2 = "tx2";
+ MutableCompositeModification modification2 = new MutableCompositeModification();
+ DOMStoreThreePhaseCommitCohort cohort2 = setupMockWriteTransaction("cohort2", dataStore,
+ TestModel.OUTER_LIST_PATH,
+ ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build(),
+ modification2);
+
+ String transactionID3 = "tx3";
+ MutableCompositeModification modification3 = new MutableCompositeModification();
+ DOMStoreThreePhaseCommitCohort cohort3 = setupMockWriteTransaction("cohort3", dataStore,
+ YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+ .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).build(),
+ ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1),
+ modification3);
+
+ long timeoutSec = 5;
+ final FiniteDuration duration = FiniteDuration.create(timeoutSec, TimeUnit.SECONDS);
+ final Timeout timeout = new Timeout(duration);
+
+ // Simulate the ForwardedReadyTransaction message for the first Tx that would be sent
+ // by the ShardTransaction.
+
+ shard.tell(new ForwardedReadyTransaction(transactionID1, CURRENT_VERSION,
+ cohort1, modification1, true), getRef());
+ ReadyTransactionReply readyReply = ReadyTransactionReply.fromSerializable(
+ expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS));
+ assertEquals("Cohort path", shard.path().toString(), readyReply.getCohortPath());
+
+ // Send the CanCommitTransaction message for the first Tx.
+
+ shard.tell(new CanCommitTransaction(transactionID1).toSerializable(), getRef());
+ CanCommitTransactionReply canCommitReply = CanCommitTransactionReply.fromSerializable(
+ expectMsgClass(duration, CanCommitTransactionReply.SERIALIZABLE_CLASS));
+ assertEquals("Can commit", true, canCommitReply.getCanCommit());
+
+ // Send the ForwardedReadyTransaction for the next 2 Tx's.
+
+ shard.tell(new ForwardedReadyTransaction(transactionID2, CURRENT_VERSION,
+ cohort2, modification2, true), getRef());
+ expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
+
+ shard.tell(new ForwardedReadyTransaction(transactionID3, CURRENT_VERSION,
+ cohort3, modification3, true), getRef());
+ expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
+
+ // Send the CanCommitTransaction message for the next 2 Tx's. These should get queued and
+ // processed after the first Tx completes.
+
+ Future<Object> canCommitFuture1 = Patterns.ask(shard,
+ new CanCommitTransaction(transactionID2).toSerializable(), timeout);
+
+ Future<Object> canCommitFuture2 = Patterns.ask(shard,
+ new CanCommitTransaction(transactionID3).toSerializable(), timeout);
+
+ // Send the CommitTransaction message for the first Tx. After it completes, it should
+ // trigger the 2nd Tx to proceed which should in turn then trigger the 3rd.
+
+ shard.tell(new CommitTransaction(transactionID1).toSerializable(), getRef());
+ expectMsgClass(duration, CommitTransactionReply.SERIALIZABLE_CLASS);
+
+ // Wait for the next 2 Tx's to complete.
+
+ final AtomicReference<Throwable> caughtEx = new AtomicReference<>();
+ final CountDownLatch commitLatch = new CountDownLatch(2);
+
+ class OnFutureComplete extends OnComplete<Object> {
+ private final Class<?> expRespType;
+
+ OnFutureComplete(final Class<?> expRespType) {
+ this.expRespType = expRespType;
+ }
+
+ @Override
+ public void onComplete(final Throwable error, final Object resp) {
+ if(error != null) {
+ caughtEx.set(new AssertionError(getClass().getSimpleName() + " failure", error));
+ } else {
+ try {
+ assertEquals("Commit response type", expRespType, resp.getClass());
+ onSuccess(resp);
+ } catch (Exception e) {
+ caughtEx.set(e);
+ }
+ }
+ }
+
+ void onSuccess(final Object resp) throws Exception {
+ }
+ }
+
+ class OnCommitFutureComplete extends OnFutureComplete {
+ OnCommitFutureComplete() {
+ super(CommitTransactionReply.SERIALIZABLE_CLASS);
+ }
+
+ @Override
+ public void onComplete(final Throwable error, final Object resp) {
+ super.onComplete(error, resp);
+ commitLatch.countDown();
+ }
+ }
+
+ class OnCanCommitFutureComplete extends OnFutureComplete {
+ private final String transactionID;
+
+ OnCanCommitFutureComplete(final String transactionID) {
+ super(CanCommitTransactionReply.SERIALIZABLE_CLASS);
+ this.transactionID = transactionID;
+ }
+
+ @Override
+ void onSuccess(final Object resp) throws Exception {
+ CanCommitTransactionReply canCommitReply =
+ CanCommitTransactionReply.fromSerializable(resp);
+ assertEquals("Can commit", true, canCommitReply.getCanCommit());
+
+ Future<Object> commitFuture = Patterns.ask(shard,
+ new CommitTransaction(transactionID).toSerializable(), timeout);
+ commitFuture.onComplete(new OnCommitFutureComplete(), getSystem().dispatcher());
+ }
+ }
+
+ canCommitFuture1.onComplete(new OnCanCommitFutureComplete(transactionID2),
+ getSystem().dispatcher());
+
+ canCommitFuture2.onComplete(new OnCanCommitFutureComplete(transactionID3),
+ getSystem().dispatcher());
+
+ boolean done = commitLatch.await(timeoutSec, TimeUnit.SECONDS);
+
+ if(caughtEx.get() != null) {
+ throw caughtEx.get();
+ }
+
+ assertEquals("Commits complete", true, done);
+
+ InOrder inOrder = inOrder(cohort1, cohort2, cohort3);
+ inOrder.verify(cohort1).canCommit();
+ inOrder.verify(cohort1).preCommit();
+ inOrder.verify(cohort1).commit();
+ inOrder.verify(cohort2).canCommit();
+ inOrder.verify(cohort2).preCommit();
+ inOrder.verify(cohort2).commit();
+ inOrder.verify(cohort3).canCommit();
+ inOrder.verify(cohort3).preCommit();
+ inOrder.verify(cohort3).commit();
+
+ // Verify data in the data store.
+
+ NormalizedNode<?, ?> outerList = readStore(shard, TestModel.OUTER_LIST_PATH);
+ assertNotNull(TestModel.OUTER_LIST_QNAME.getLocalName() + " not found", outerList);
+ assertTrue(TestModel.OUTER_LIST_QNAME.getLocalName() + " value is not Iterable",
+ outerList.getValue() instanceof Iterable);
+ Object entry = ((Iterable<Object>)outerList.getValue()).iterator().next();
+ assertTrue(TestModel.OUTER_LIST_QNAME.getLocalName() + " entry is not MapEntryNode",
+ entry instanceof MapEntryNode);
+ MapEntryNode mapEntry = (MapEntryNode)entry;
+ Optional<DataContainerChild<? extends PathArgument, ?>> idLeaf =
+ mapEntry.getChild(new YangInstanceIdentifier.NodeIdentifier(TestModel.ID_QNAME));
+ assertTrue("Missing leaf " + TestModel.ID_QNAME.getLocalName(), idLeaf.isPresent());
+ assertEquals(TestModel.ID_QNAME.getLocalName() + " value", 1, idLeaf.get().getValue());
+
+ verifyLastLogIndex(shard, 2);
+
+ shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
+ }};
+ }
+}
* 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.cluster.datastore;
+package org.opendaylight.controller.cluster.datastore.compat;
import akka.actor.ActorRef;
import akka.actor.ActorSelection;
import java.util.Collections;
import org.junit.Assert;
import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.AbstractActorTest;
+import org.opendaylight.controller.cluster.datastore.DataStoreVersions;
+import org.opendaylight.controller.cluster.datastore.DatastoreContext;
+import org.opendaylight.controller.cluster.datastore.Shard;
+import org.opendaylight.controller.cluster.datastore.ShardTest;
+import org.opendaylight.controller.cluster.datastore.ShardTestKit;
+import org.opendaylight.controller.cluster.datastore.TransactionProxy;
import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier;
import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply;
import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransactionReply;
*/
public class ShardTransactionHeliumBackwardsCompatibilityTest extends AbstractActorTest {
- @SuppressWarnings("unchecked")
@Test
public void testTransactionCommit() throws Exception {
new ShardTestKit(getSystem()) {{
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
private static DataSchemaNode searchInChoices(final DataNodeContainer node, final QName arg) {
for (DataSchemaNode child : node.getChildNodes()) {
- if (child instanceof ChoiceNode) {
- ChoiceNode choiceNode = (ChoiceNode) child;
+ if (child instanceof ChoiceSchemaNode) {
+ ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) child;
DataSchemaNode potential = searchInCases(choiceNode, arg);
if (potential != null) {
return potential;
return null;
}
- private static DataSchemaNode searchInCases(final ChoiceNode choiceNode, final QName arg) {
+ private static DataSchemaNode searchInCases(final ChoiceSchemaNode choiceNode, final QName arg) {
Set<ChoiceCaseNode> cases = choiceNode.getCases();
for (ChoiceCaseNode caseNode : cases) {
DataSchemaNode node = caseNode.getDataChildByName(arg);
--- /dev/null
+/*
+ * Copyright (c) 2015 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.md.sal.dom.spi;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+
+/**
+ * Abstract implementation of a ListenerRegistration constrained to subclasses
+ * of {@link DOMDataTreeChangeListener}.
+ *
+ * @param <T> type of listener
+ */
+public abstract class AbstractDOMDataTreeChangeListenerRegistration<T extends DOMDataTreeChangeListener> extends AbstractListenerRegistration<T> {
+ protected AbstractDOMDataTreeChangeListenerRegistration(final T listener) {
+ super(listener);
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2015 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.md.sal.dom.spi;
+
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * An abstract tree of registrations. Allows a read-only snapshot to be taken.
+ *
+ * @param <T> Type of registered object
+ */
+public abstract class AbstractRegistrationTree<T> {
+ private final ReadWriteLock rwLock = new ReentrantReadWriteLock(true);
+ private final RegistrationTreeNode<T> rootNode = new RegistrationTreeNode<>(null, null);
+
+ protected AbstractRegistrationTree() {
+
+ }
+
+ /**
+ * Acquire the read-write lock. This should be done before invoking {@link #findNodeFor(Iterable)}.
+ */
+ protected final void takeLock() {
+ rwLock.writeLock().lock();
+ }
+
+ /**
+ * Release the read-write lock. This should be done after invocation of {@link #findNodeFor(Iterable)}
+ * and modification of the returned node. Note that callers should do so in a finally block.
+ */
+ protected final void releaseLock() {
+ rwLock.writeLock().unlock();
+ }
+
+ /**
+ * Find an existing, or allocate a fresh, node for a particular path. Must be called with the
+ * read-write lock held.
+ *
+ * @param path Path to find a node for
+ * @return A registration node for the specified path
+ */
+ @Nonnull protected final RegistrationTreeNode<T> findNodeFor(@Nonnull final Iterable<PathArgument> path) {
+ RegistrationTreeNode<T> walkNode = rootNode;
+ for (final PathArgument arg : path) {
+ walkNode = walkNode.ensureChild(arg);
+ }
+
+ return walkNode;
+ }
+
+ /**
+ * Add a registration to a particular node. The node must have been returned via {@link #findNodeFor(Iterable)}
+ * and the lock must still be held.
+ *
+ * @param node Tree node
+ * @param registration Registration instance
+ */
+ protected final void addRegistration(@Nonnull final RegistrationTreeNode<T> node, @Nonnull final T registration) {
+ node.addRegistration(registration);
+ }
+
+ /**
+ * Remove a registration from a particular node. This method must not be called while the read-write lock
+ * is held.
+ *
+ * @param node Tree node
+ * @param registration Registration instance
+ */
+ protected final void removeRegistration(@Nonnull final RegistrationTreeNode<T> node, @Nonnull final T registration) {
+ // Take the write lock
+ rwLock.writeLock().lock();
+ try {
+ node.removeRegistration(registration);
+ } finally {
+ // Always release the lock
+ rwLock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Obtain a tree snapshot. This snapshot ensures a consistent view of
+ * registrations. The snapshot should be closed as soon as it is not required,
+ * because each unclosed instance blocks modification of this tree.
+ *
+ * @return A snapshot instance.
+ */
+ @Nonnull public final RegistrationTreeSnapshot<T> takeSnapshot() {
+ final RegistrationTreeSnapshot<T> ret = new RegistrationTreeSnapshot<>(rwLock.readLock(), rootNode);
+ rwLock.readLock().lock();
+ return ret;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2015 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.md.sal.dom.spi;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is a single node within the registration tree. Note that the data returned from
+ * and instance of this class is guaranteed to have any relevance or consistency
+ * only as long as the {@link RegistrationTreeSnapshot} instance through which it is reached
+ * remains unclosed.
+ *
+ * @param <T> registration type
+ * @author Robert Varga
+ */
+public final class RegistrationTreeNode<T> implements Identifiable<PathArgument> {
+ private static final Logger LOG = LoggerFactory.getLogger(RegistrationTreeNode.class);
+
+ private final Map<PathArgument, RegistrationTreeNode<T>> children = new HashMap<>();
+ private final Collection<T> registrations = new ArrayList<>(2);
+ private final Collection<T> publicRegistrations = Collections.unmodifiableCollection(registrations);
+ private final Reference<RegistrationTreeNode<T>> parent;
+ private final PathArgument identifier;
+
+ RegistrationTreeNode(final RegistrationTreeNode<T> parent, final PathArgument identifier) {
+ this.parent = new WeakReference<>(parent);
+ this.identifier = identifier;
+ }
+
+ @Override
+ public PathArgument getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Return the child matching a {@link PathArgument} specification.
+ *
+ * @param arg Child identifier
+ * @return Child matching exactly, or null.
+ */
+ public RegistrationTreeNode<T> getExactChild(@Nonnull final PathArgument arg) {
+ return children.get(Preconditions.checkNotNull(arg));
+ }
+
+ /**
+ * Return a collection children which match a {@link PathArgument} specification inexactly.
+ * This explicitly excludes the child returned by {@link #getExactChild(PathArgument)}.
+ *
+ * @param arg Child identifier
+ * @return Collection of children, guaranteed to be non-null.
+ */
+ public @Nonnull Collection<RegistrationTreeNode<T>> getInexactChildren(@Nonnull final PathArgument arg) {
+ Preconditions.checkNotNull(arg);
+ if (arg instanceof NodeWithValue || arg instanceof NodeIdentifierWithPredicates) {
+ /*
+ * TODO: This just all-or-nothing wildcards, which we have historically supported. Given
+ * that the argument is supposed to have all the elements filled out, we could support
+ * partial wildcards by iterating over the registrations and matching the maps for
+ * partial matches.
+ */
+ final RegistrationTreeNode<T> child = children.get(new NodeIdentifier(arg.getNodeType()));
+ if (child == null) {
+ return Collections.emptyList();
+ } else {
+ return Collections.singletonList(child);
+ }
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ public Collection<T> getRegistrations() {
+ return publicRegistrations;
+ }
+
+ RegistrationTreeNode<T> ensureChild(@Nonnull final PathArgument child) {
+ RegistrationTreeNode<T> potential = children.get(Preconditions.checkNotNull(child));
+ if (potential == null) {
+ potential = new RegistrationTreeNode<T>(this, child);
+ children.put(child, potential);
+ }
+ return potential;
+ }
+
+ void addRegistration(@Nonnull final T registration) {
+ registrations.add(Preconditions.checkNotNull(registration));
+ LOG.debug("Registration {} added", registration);
+ }
+
+ void removeRegistration(@Nonnull final T registration) {
+ registrations.remove(Preconditions.checkNotNull(registration));
+ LOG.debug("Registration {} removed", registration);
+
+ // We have been called with the write-lock held, so we can perform some cleanup.
+ removeThisIfUnused();
+ }
+
+ private void removeThisIfUnused() {
+ final RegistrationTreeNode<T> p = parent.get();
+ if (p != null && registrations.isEmpty() && children.isEmpty()) {
+ p.removeChild(identifier);
+ }
+ }
+
+ private void removeChild(final PathArgument arg) {
+ children.remove(arg);
+ removeThisIfUnused();
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("identifier", identifier)
+ .add("registrations", registrations.size())
+ .add("children", children.size()).toString();
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2015 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.md.sal.dom.spi;
+
+import com.google.common.base.Preconditions;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * A stable read-only snapshot of a {@link AbstractRegistrationTree}.
+ *
+ * @author Robert Varga
+ */
+public final class RegistrationTreeSnapshot<T> implements AutoCloseable {
+ @SuppressWarnings("rawtypes")
+ private static final AtomicIntegerFieldUpdater<RegistrationTreeSnapshot> CLOSED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(RegistrationTreeSnapshot.class, "closed");
+ private final RegistrationTreeNode<T> node;
+ private final Lock lock;
+
+ // Used via CLOSED_UPDATER
+ @SuppressWarnings("unused")
+ private volatile int closed = 0;
+
+ RegistrationTreeSnapshot(final Lock lock, final RegistrationTreeNode<T> node) {
+ this.lock = Preconditions.checkNotNull(lock);
+ this.node = Preconditions.checkNotNull(node);
+ }
+
+ public RegistrationTreeNode<T> getRootNode() {
+ return node;
+ }
+
+ @Override
+ public void close() {
+ if (CLOSED_UPDATER.compareAndSet(this, 0, 1)) {
+ lock.unlock();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 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.core.spi;
+
+import java.util.concurrent.Future;
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+public abstract class ForwardingConsumerSession implements ConsumerSession {
+
+ protected abstract ConsumerSession delegate();
+
+ @Override
+ public void close() {
+ delegate().close();
+ }
+
+ @Override
+ public <T extends BrokerService> T getService(Class<T> arg0) {
+ return delegate().getService(arg0);
+ }
+
+ @Override
+ public boolean isClosed() {
+ return delegate().isClosed();
+ }
+
+ @Override
+ @Deprecated
+ public Future<RpcResult<CompositeNode>> rpc(QName arg0, CompositeNode arg1) {
+ return delegate().rpc(arg0, arg1);
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 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.core.spi;
+
+import java.util.Set;
+import java.util.concurrent.Future;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+
+public abstract class ForwardingProviderSession implements ProviderSession {
+
+
+ protected abstract ProviderSession delegate();
+
+ @Override
+ @Deprecated
+ public RoutedRpcRegistration addMountedRpcImplementation(QName arg0, RpcImplementation arg1) {
+ return delegate().addMountedRpcImplementation(arg0, arg1);
+ }
+
+ @Override
+ @Deprecated
+ public RoutedRpcRegistration addRoutedRpcImplementation(QName arg0, RpcImplementation arg1) {
+ return delegate().addRoutedRpcImplementation(arg0, arg1);
+ }
+
+ @Override
+ @Deprecated
+ public RpcRegistration addRpcImplementation(QName arg0, RpcImplementation arg1)
+ throws IllegalArgumentException {
+ return delegate().addRpcImplementation(arg0, arg1);
+ }
+
+ @Deprecated
+ @Override
+ public ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(
+ RpcRegistrationListener arg0) {
+ return delegate().addRpcRegistrationListener(arg0);
+ }
+
+ @Override
+ public void close() {
+ delegate().close();
+ }
+
+ @Override
+ public <T extends BrokerService> T getService(Class<T> arg0) {
+ return delegate().getService(arg0);
+ }
+
+ @Override
+ public Set<QName> getSupportedRpcs() {
+ return delegate().getSupportedRpcs();
+ }
+
+ @Override
+ public boolean isClosed() {
+ return delegate().isClosed();
+ }
+
+ @Override
+ public Future<RpcResult<CompositeNode>> rpc(QName arg0, CompositeNode arg1) {
+ return delegate().rpc(arg0, arg1);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 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.core.spi.data;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractDOMDataTreeChangeListenerRegistration;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractRegistrationTree;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeNode;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeSnapshot;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract base class for {@link DOMStoreTreeChangePublisher} implementations.
+ */
+public abstract class AbstractDOMStoreTreeChangePublisher extends AbstractRegistrationTree<AbstractDOMDataTreeChangeListenerRegistration<?>> implements DOMStoreTreeChangePublisher {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractDOMStoreTreeChangePublisher.class);
+
+ /**
+ * Callback for subclass to notify specified registrations of a candidate at a specified path. This method is guaranteed
+ * to be only called from within {@link #processCandidateTree(DataTreeCandidate)}.
+ *
+ * @param registrations Registrations which are affected by the candidate node
+ * @param path Path of changed candidate node. Guaranteed to match the path specified by the registration
+ * @param node Candidate node
+ */
+ protected abstract void notifyListeners(@Nonnull Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> registrations, @Nonnull YangInstanceIdentifier path, @Nonnull DataTreeCandidateNode node);
+
+ /**
+ * Callback notifying the subclass that the specified registration is being closed and it's user no longer
+ * wishes to receive notifications. This notification is invoked while the {@link ListenerRegistration#close()}
+ * method is executing. Subclasses can use this callback to properly remove any delayed notifications pending
+ * towards the registration.
+ *
+ * @param registration Registration which is being closed
+ */
+ protected abstract void registrationRemoved(@Nonnull AbstractDOMDataTreeChangeListenerRegistration<?> registration);
+
+ /**
+ * Process a candidate tree with respect to registered listeners.
+ *
+ * @param candidate candidate three which needs to be processed
+ */
+ protected final void processCandidateTree(@Nonnull final DataTreeCandidate candidate) {
+ final DataTreeCandidateNode node = candidate.getRootNode();
+ if (node.getModificationType() == ModificationType.UNMODIFIED) {
+ LOG.debug("Skipping unmodified candidate {}", candidate);
+ return;
+ }
+
+ try (final RegistrationTreeSnapshot<AbstractDOMDataTreeChangeListenerRegistration<?>> snapshot = takeSnapshot()) {
+ final List<PathArgument> toLookup = ImmutableList.copyOf(candidate.getRootPath().getPathArguments());
+ lookupAndNotify(toLookup, 0, snapshot.getRootNode(), candidate);
+ }
+ }
+
+ @Override
+ public final <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerTreeChangeListener(final YangInstanceIdentifier treeId, final L listener) {
+ // Take the write lock
+ takeLock();
+ try {
+ final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> node = findNodeFor(treeId.getPathArguments());
+ final AbstractDOMDataTreeChangeListenerRegistration<L> reg = new AbstractDOMDataTreeChangeListenerRegistration<L>(listener) {
+ @Override
+ protected void removeRegistration() {
+ AbstractDOMStoreTreeChangePublisher.this.removeRegistration(node, this);
+ registrationRemoved(this);
+ }
+ };
+
+ addRegistration(node, reg);
+ return reg;
+ } finally {
+ // Always release the lock
+ releaseLock();
+ }
+ }
+
+ private void lookupAndNotify(final List<PathArgument> args, final int offset, final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> node, final DataTreeCandidate candidate) {
+ if (args.size() != offset) {
+ final PathArgument arg = args.get(offset);
+
+ final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> exactChild = node.getExactChild(arg);
+ if (exactChild != null) {
+ lookupAndNotify(args, offset + 1, exactChild, candidate);
+ }
+
+ for (RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> c : node.getInexactChildren(arg)) {
+ lookupAndNotify(args, offset + 1, c, candidate);
+ }
+ } else {
+ notifyNode(candidate.getRootPath(), node, candidate.getRootNode());
+ }
+ }
+
+ private void notifyNode(final YangInstanceIdentifier path, final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> regNode, final DataTreeCandidateNode candNode) {
+ if (candNode.getModificationType() == ModificationType.UNMODIFIED) {
+ LOG.debug("Skipping unmodified candidate {}", path);
+ return;
+ }
+
+ final Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> regs = regNode.getRegistrations();
+ if (!regs.isEmpty()) {
+ notifyListeners(regs, path, candNode);
+ }
+
+ for (DataTreeCandidateNode candChild : candNode.getChildNodes()) {
+ if (candChild.getModificationType() != ModificationType.UNMODIFIED) {
+ final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> regChild = regNode.getExactChild(candChild.getIdentifier());
+ if (regChild != null) {
+ notifyNode(path.node(candChild.getIdentifier()), regChild, candChild);
+ }
+
+ for (RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> rc : regNode.getInexactChildren(candChild.getIdentifier())) {
+ notifyNode(path.node(candChild.getIdentifier()), rc, candChild);
+ }
+ }
+ }
+ }
+}
<version>2.0.29</version>
</dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- </dependency>
-
</dependencies>
<build>
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree;
import org.opendaylight.controller.sal.core.spi.data.DOMStore;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTreeChangePublisher;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
import org.opendaylight.yangtools.concepts.Identifiable;
* to implement {@link DOMStore} contract.
*
*/
-public class InMemoryDOMDataStore extends TransactionReadyPrototype implements DOMStore, Identifiable<String>, SchemaContextListener, AutoCloseable {
+public class InMemoryDOMDataStore extends TransactionReadyPrototype implements DOMStore, Identifiable<String>, SchemaContextListener, AutoCloseable, DOMStoreTreeChangePublisher {
private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataStore.class);
private static final ListenableFuture<Void> SUCCESSFUL_FUTURE = Futures.immediateFuture(null);
private static final ListenableFuture<Boolean> CAN_COMMIT_FUTURE = Futures.immediateFuture(Boolean.TRUE);
private final AtomicLong txCounter = new AtomicLong(0);
private final QueuedNotificationManager<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> dataChangeListenerNotificationManager;
+ private final InMemoryDOMStoreTreeChangePublisher changePublisher;
private final ExecutorService dataChangeListenerExecutor;
private final boolean debugTransactions;
private final String name;
new QueuedNotificationManager<>(this.dataChangeListenerExecutor,
DCL_NOTIFICATION_MGR_INVOKER, maxDataChangeListenerQueueSize,
"DataChangeListenerQueueMgr");
+ changePublisher = new InMemoryDOMStoreTreeChangePublisher(this.dataChangeListenerExecutor, maxDataChangeListenerQueueSize);
}
public void setCloseable(final AutoCloseable closeable) {
};
}
+ @Override
+ public <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerTreeChangeListener(final YangInstanceIdentifier treeId, final L listener) {
+ return changePublisher.registerTreeChangeListener(treeId, listener);
+ }
+
@Override
protected void transactionAborted(final SnapshotBackedWriteTransaction tx) {
LOG.debug("Tx: {} is closed.", tx.getIdentifier());
*/
synchronized (InMemoryDOMDataStore.this) {
dataTree.commit(candidate);
+ changePublisher.publishChange(candidate);
listenerResolver.resolve(dataChangeListenerNotificationManager);
}
--- /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.md.sal.dom.store.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.ExecutorService;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractDOMDataTreeChangeListenerRegistration;
+import org.opendaylight.controller.sal.core.spi.data.AbstractDOMStoreTreeChangePublisher;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager.Invoker;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class InMemoryDOMStoreTreeChangePublisher extends AbstractDOMStoreTreeChangePublisher {
+ private static final Invoker<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate> MANAGER_INVOKER =
+ new Invoker<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate>() {
+ @Override
+ public void invokeListener(final AbstractDOMDataTreeChangeListenerRegistration<?> listener, final DataTreeCandidate notification) {
+ // FIXME: this is inefficient, as we could grab the entire queue for the listener and post it
+ final DOMDataTreeChangeListener inst = listener.getInstance();
+ if (inst != null) {
+ inst.onDataTreeChanged(Collections.singletonList(notification));
+ }
+ }
+ };
+ private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMStoreTreeChangePublisher.class);
+ private final QueuedNotificationManager<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate> notificationManager;
+
+ InMemoryDOMStoreTreeChangePublisher(final ExecutorService listenerExecutor, final int maxQueueSize) {
+ notificationManager = new QueuedNotificationManager<>(listenerExecutor, MANAGER_INVOKER, maxQueueSize, "DataTreeChangeListenerQueueMgr");
+ }
+
+ @Override
+ protected void notifyListeners(final Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> registrations, final YangInstanceIdentifier path, final DataTreeCandidateNode node) {
+ final DataTreeCandidate candidate = new SimpleDataTreeCandidate(path, node);
+
+ for (AbstractDOMDataTreeChangeListenerRegistration<?> reg : registrations) {
+ LOG.debug("Enqueueing candidate {} to registration {}", candidate, registrations);
+ notificationManager.submitNotification(reg, candidate);
+ }
+ }
+
+ @Override
+ protected synchronized void registrationRemoved(final AbstractDOMDataTreeChangeListenerRegistration<?> registration) {
+ LOG.debug("Closing registration {}", registration);
+
+ // FIXME: remove the queue for this registration and make sure we clear it
+ }
+
+ synchronized void publishChange(@Nonnull final DataTreeCandidate candidate) {
+ // Runs synchronized with registrationRemoved()
+ processCandidateTree(candidate);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 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.md.sal.dom.store.impl;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+
+final class SimpleDataTreeCandidate implements DataTreeCandidate {
+ private final YangInstanceIdentifier rootPath;
+ private final DataTreeCandidateNode rootNode;
+
+ SimpleDataTreeCandidate(final YangInstanceIdentifier rootPath, final DataTreeCandidateNode rootNode) {
+ this.rootPath = Preconditions.checkNotNull(rootPath);
+ this.rootNode = Preconditions.checkNotNull(rootNode);
+ }
+
+ @Override
+ public DataTreeCandidateNode getRootNode() {
+ return rootNode;
+ }
+
+ @Override
+ public YangInstanceIdentifier getRootPath() {
+ return rootPath;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("rootPath", rootPath).add("rootNode", rootNode).toString();
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) 2015 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.md.sal.dom.store.impl.tree;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+abstract class DataChangeListenerRegistrationImpl<T extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> extends AbstractListenerRegistration<T> implements DataChangeListenerRegistration<T> {
+ public DataChangeListenerRegistrationImpl(final T listener) {
+ super(listener);
+ }
+}
\ No newline at end of file
package org.opendaylight.controller.md.sal.dom.store.impl.tree;
import com.google.common.base.Optional;
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
+import com.google.common.base.Preconditions;
import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeNode;
import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.DataChangeListenerRegistrationImpl;
import org.opendaylight.yangtools.concepts.Identifiable;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* This is a single node within the listener tree. Note that the data returned from
* @author Robert Varga
*/
public class ListenerNode implements StoreTreeNode<ListenerNode>, Identifiable<PathArgument> {
+ final RegistrationTreeNode<DataChangeListenerRegistration<?>> delegate;
- private static final Logger LOG = LoggerFactory.getLogger(ListenerNode.class);
-
- private final Collection<DataChangeListenerRegistration<?>> listeners = new ArrayList<>();
- private final Map<PathArgument, ListenerNode> children = new HashMap<>();
- private final PathArgument identifier;
- private final Reference<ListenerNode> parent;
-
- ListenerNode(final ListenerNode parent, final PathArgument identifier) {
- this.parent = new WeakReference<>(parent);
- this.identifier = identifier;
+ ListenerNode(final RegistrationTreeNode<DataChangeListenerRegistration<?>> delegate) {
+ this.delegate = Preconditions.checkNotNull(delegate);
}
@Override
public PathArgument getIdentifier() {
- return identifier;
+ return delegate.getIdentifier();
}
@Override
public Optional<ListenerNode> getChild(final PathArgument child) {
- return Optional.fromNullable(children.get(child));
+ final RegistrationTreeNode<DataChangeListenerRegistration<?>> c = delegate.getExactChild(child);
+ if (c == null) {
+ return Optional.absent();
+ }
+
+ return Optional.of(new ListenerNode(c));
}
/**
* @return the list of current listeners
*/
public Collection<DataChangeListenerRegistration<?>> getListeners() {
- return listeners;
+ return delegate.getRegistrations();
}
- ListenerNode ensureChild(final PathArgument child) {
- ListenerNode potential = children.get(child);
- if (potential == null) {
- potential = new ListenerNode(this, child);
- children.put(child, potential);
- }
- return potential;
- }
-
- void addListener(final DataChangeListenerRegistration<?> listener) {
- listeners.add(listener);
- LOG.debug("Listener {} registered", listener);
- }
-
- void removeListener(final DataChangeListenerRegistrationImpl<?> listener) {
- listeners.remove(listener);
- LOG.debug("Listener {} unregistered", listener);
-
- // We have been called with the write-lock held, so we can perform some cleanup.
- removeThisIfUnused();
- }
-
- private void removeThisIfUnused() {
- final ListenerNode p = parent.get();
- if (p != null && listeners.isEmpty() && children.isEmpty()) {
- p.removeChild(identifier);
- }
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
}
- private void removeChild(final PathArgument arg) {
- children.remove(arg);
- removeThisIfUnused();
+ @Override
+ public boolean equals(final Object obj) {
+ return delegate.equals(obj);
}
@Override
public String toString() {
- return "Node [identifier=" + identifier + ", listeners=" + listeners.size() + ", children=" + children.size() + "]";
+ return delegate.toString();
}
}
*/
package org.opendaylight.controller.md.sal.dom.store.impl.tree;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractRegistrationTree;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeNode;
import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
-import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* A set of listeners organized as a tree by node to which they listen. This class
*
* @author Robert Varga
*/
-public final class ListenerTree {
- private static final Logger LOG = LoggerFactory.getLogger(ListenerTree.class);
- private final ReadWriteLock rwLock = new ReentrantReadWriteLock(true);
- private final ListenerNode rootNode = new ListenerNode(null, null);
-
+public final class ListenerTree extends AbstractRegistrationTree<DataChangeListenerRegistration<?>> {
private ListenerTree() {
// Private to disallow direct instantiation
}
final L listener, final DataChangeScope scope) {
// Take the write lock
- rwLock.writeLock().lock();
-
+ takeLock();
try {
- ListenerNode walkNode = rootNode;
- for (final PathArgument arg : path.getPathArguments()) {
- walkNode = walkNode.ensureChild(arg);
- }
-
- final ListenerNode node = walkNode;
+ final RegistrationTreeNode<DataChangeListenerRegistration<?>> node = findNodeFor(path.getPathArguments());
DataChangeListenerRegistration<L> reg = new DataChangeListenerRegistrationImpl<L>(listener) {
@Override
public DataChangeScope getScope() {
* While this does not directly violate the ListenerRegistration
* contract, it is probably not going to be liked by the users.
*/
-
- // Take the write lock
- ListenerTree.this.rwLock.writeLock().lock();
- try {
- node.removeListener(this);
- } finally {
- // Always release the lock
- ListenerTree.this.rwLock.writeLock().unlock();
- }
+ ListenerTree.this.removeRegistration(node, this);
}
};
- node.addListener(reg);
+ addRegistration(node, reg);
return reg;
} finally {
// Always release the lock
- rwLock.writeLock().unlock();
+ releaseLock();
}
}
* external user exist, make the Walker a phantom reference, which
* will cleanup the lock if not told to do so.
*/
- final ListenerWalker ret = new ListenerWalker(rwLock.readLock(), rootNode);
- rwLock.readLock().lock();
- return ret;
- }
-
- abstract static class DataChangeListenerRegistrationImpl<T extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> extends AbstractListenerRegistration<T> //
- implements DataChangeListenerRegistration<T> {
- public DataChangeListenerRegistrationImpl(final T listener) {
- super(listener);
- }
+ return new ListenerWalker(takeSnapshot());
}
}
package org.opendaylight.controller.md.sal.dom.store.impl.tree;
import com.google.common.base.Preconditions;
-import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
-import java.util.concurrent.locks.Lock;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeSnapshot;
+import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
/**
* A walking context, pretty much equivalent to an iterator, but it
* @author Robert Varga
*/
public class ListenerWalker implements AutoCloseable {
- private static final AtomicIntegerFieldUpdater<ListenerWalker> CLOSED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(ListenerWalker.class, "closed");
- private final Lock lock;
- private final ListenerNode node;
+ private final RegistrationTreeSnapshot<DataChangeListenerRegistration<?>> delegate;
- // Used via CLOSED_UPDATER
- @SuppressWarnings("unused")
- private volatile int closed = 0;
-
- ListenerWalker(final Lock lock, final ListenerNode node) {
- this.lock = Preconditions.checkNotNull(lock);
- this.node = Preconditions.checkNotNull(node);
+ ListenerWalker(final RegistrationTreeSnapshot<DataChangeListenerRegistration<?>> delegate) {
+ this.delegate = Preconditions.checkNotNull(delegate);
}
public ListenerNode getRootNode() {
- return node;
+ return new ListenerNode(delegate.getRootNode());
}
@Override
public void close() {
- if (CLOSED_UPDATER.compareAndSet(this, 0, 1)) {
- lock.unlock();
- }
+ delegate.close();
}
}
\ No newline at end of file
import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
public interface MessageTransformer<M> {
ContainerNode toNotification(M message);
- M toRpcRequest(SchemaPath rpc, ContainerNode node);
+ M toRpcRequest(SchemaPath rpc, NormalizedNode<?, ?> node);
DOMRpcResult toRpcResult(M message, SchemaPath rpc);
}
private void registerToBaseNetconfStream(final NetconfDeviceRpc deviceRpc, final NetconfDeviceCommunicator listener) {
+ // 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(NetconfMessageTransformUtil.toPath(NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_QNAME), NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_CONTENT);
messageTransformer = new NetconfMessageTransformer(result);
updateTransformer(messageTransformer);
- notificationHandler.onRemoteSchemaUp(messageTransformer);
+ // salFacade.onDeviceConnected has to be called before the notification handler is initialized
salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc);
+ notificationHandler.onRemoteSchemaUp(messageTransformer);
logger.info("{}: Netconf connector initialized successfully", id);
}
logger.trace("{}: Session advertised capabilities: {}", id, netconfSessionPreferences);
if(overrideNetconfCapabilities.isPresent()) {
- netconfSessionPreferences = netconfSessionPreferences.replaceModuleCaps(overrideNetconfCapabilities.get());
+ netconfSessionPreferences = netconfSessionPreferences.addModuleCaps(overrideNetconfCapabilities.get());
logger.debug("{}: Session capabilities overridden, capabilities that will be used: {}", id, netconfSessionPreferences);
}
|| containsNonModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
}
- public NetconfSessionPreferences replaceModuleCaps(final NetconfSessionPreferences netconfSessionModuleCapabilities) {
- final Set<QName> moduleBasedCaps = Sets.newHashSet(netconfSessionModuleCapabilities.getModuleBasedCaps());
-
- // Preserve monitoring module, since it indicates support for ietf-netconf-monitoring
- if(containsModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)) {
- moduleBasedCaps.add(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING);
- }
- return new NetconfSessionPreferences(getNonModuleCaps(), moduleBasedCaps);
+ public NetconfSessionPreferences addModuleCaps(final NetconfSessionPreferences netconfSessionModuleCapabilities) {
+ final HashSet<QName> mergedCaps = Sets.newHashSetWithExpectedSize(moduleBasedCaps.size() + netconfSessionModuleCapabilities.getModuleBasedCaps().size());
+ mergedCaps.addAll(moduleBasedCaps);
+ mergedCaps.addAll(netconfSessionModuleCapabilities.getModuleBasedCaps());
+ return new NetconfSessionPreferences(getNonModuleCaps(), mergedCaps);
}
public static NetconfSessionPreferences fromNetconfSession(final NetconfClientSession session) {
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceDatastoreAdapter.class);
private final RemoteDeviceId id;
- private final DataBroker dataService;
+ private final BindingTransactionChain txChain;
NetconfDeviceDatastoreAdapter(final RemoteDeviceId deviceId, final DataBroker dataService) {
this.id = Preconditions.checkNotNull(deviceId);
- this.dataService = Preconditions.checkNotNull(dataService);
+ this.txChain = Preconditions.checkNotNull(dataService).createTransactionChain(new TransactionChainListener() {
+ @Override
+ public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction, Throwable cause) {
+ logger.error("{}: TransactionChain({}) {} FAILED!", id, chain, transaction.getIdentifier(), cause);
+ throw new IllegalStateException(id + " TransactionChain(" + chain + ") not committed correctly", cause);
+ }
+
+ @Override
+ public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {
+ logger.trace("{}: TransactionChain({}) {} SUCCESSFUL", id, chain);
+ }
+ });
initDeviceData();
}
final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node data = buildDataForDeviceState(
up, capabilities, id);
- final ReadWriteTransaction transaction = dataService.newReadWriteTransaction();
+ final ReadWriteTransaction transaction = txChain.newReadWriteTransaction();
logger.trace("{}: Update device state transaction {} merging operational data started.", id, transaction.getIdentifier());
transaction.put(LogicalDatastoreType.OPERATIONAL, id.getBindingPath(), data);
logger.trace("{}: Update device state transaction {} merging operational data ended.", id, transaction.getIdentifier());
}
private void removeDeviceConfigAndState() {
- final WriteTransaction transaction = dataService.newWriteOnlyTransaction();
+ final WriteTransaction transaction = txChain.newWriteOnlyTransaction();
logger.trace("{}: Close device state transaction {} removing all data started.", id, transaction.getIdentifier());
transaction.delete(LogicalDatastoreType.CONFIGURATION, id.getBindingPath());
transaction.delete(LogicalDatastoreType.OPERATIONAL, id.getBindingPath());
}
private void initDeviceData() {
- final WriteTransaction transaction = dataService.newWriteOnlyTransaction();
+ final WriteTransaction transaction = txChain.newWriteOnlyTransaction();
createNodesListIfNotPresent(transaction);
@Override
public void close() throws Exception {
removeDeviceConfigAndState();
+ txChain.close();
}
public static org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node buildDataForDeviceState(
package org.opendaylight.controller.sal.connect.netconf.sal;
import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
import com.google.common.collect.Collections2;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@Nonnull
@Override
public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(@Nonnull final SchemaPath type, @Nullable final NormalizedNode<?, ?> input) {
- Preconditions.checkArgument(input instanceof ContainerNode, "Epc payload has to be a %s, was %s", ContainerNode.class, input);
-
- final NetconfMessage message = transformer.toRpcRequest(type, (ContainerNode) input);
+ final NetconfMessage message = transformer.toRpcRequest(type, input);
final ListenableFuture<RpcResult<NetconfMessage>> delegateFutureWithPureResult = listener.sendRequest(message, type.getLastComponent());
final ListenableFuture<DOMRpcResult> transformed = Futures.transform(delegateFutureWithPureResult, new Function<RpcResult<NetconfMessage>, DOMRpcResult>() {
this.notificationService = notificationService;
registration = mountBuilder.register();
+ logger.debug("{}: Mountpoint exposed into MD-SAL {}", id, registration);
}
@Deprecated
synchronized void onDeviceDisconnected() {
if(registration == null) {
+ logger.trace("{}: Not removing mountpoint from MD-SAL, mountpoint was not registered yet", id);
return;
}
// Only log and ignore
logger.warn("Unable to unregister mount instance for {}. Ignoring exception", id.getPath(), e);
} finally {
+ logger.debug("{}: Mountpoint removed from MD-SAL {}", id, registration);
registration = null;
}
}
mountBuilder.addService(DOMNotificationService.class, notificationService);
topologyRegistration = mountBuilder.register();
+ logger.debug("{}: TOPOLOGY Mountpoint exposed into MD-SAL {}", id, registration);
+
}
synchronized void onTopologyDeviceDisconnected() {
if(topologyRegistration == null) {
+ logger.trace("{}: Not removing TOPOLOGY mountpoint from MD-SAL, mountpoint was not registered yet", id);
return;
}
// Only log and ignore
logger.warn("Unable to unregister mount instance for {}. Ignoring exception", id.getTopologyPath(), e);
} finally {
+ logger.debug("{}: TOPOLOGY Mountpoint removed from MD-SAL {}", id, registration);
topologyRegistration = null;
}
}
@Override
synchronized public void close() throws Exception {
- if(registration != null) {
- onDeviceDisconnected();
- onTopologyDeviceDisconnected();
- }
+ onDeviceDisconnected();
+ onTopologyDeviceDisconnected();
mountService = null;
}
package org.opendaylight.controller.sal.connect.netconf.sal;
import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
import com.google.common.collect.FluentIterable;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.FutureCallback;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCapabilities;
import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
};
private final RemoteDeviceId id;
- private final DataBroker dataService;
+ private final BindingTransactionChain txChain;
private final InstanceIdentifier<NetworkTopology> networkTopologyPath;
private final KeyedInstanceIdentifier<Topology, TopologyKey> topologyListPath;
NetconfDeviceTopologyAdapter(final RemoteDeviceId id, final DataBroker dataService) {
this.id = id;
- this.dataService = dataService;
+ this.txChain = Preconditions.checkNotNull(dataService).createTransactionChain(new TransactionChainListener() {
+ @Override
+ public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction, Throwable cause) {
+ logger.error("{}: TransactionChain({}) {} FAILED!", id, chain, transaction.getIdentifier(), cause);
+ throw new IllegalStateException(id + " TransactionChain(" + chain + ") not committed correctly", cause);
+ }
+
+ @Override
+ public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {
+ logger.trace("{}: TransactionChain({}) {} SUCCESSFUL", id, chain);
+ }
+ });
this.networkTopologyPath = InstanceIdentifier.builder(NetworkTopology.class).build();
this.topologyListPath = networkTopologyPath.child(Topology.class, new TopologyKey(new TopologyId(TopologyNetconf.QNAME.getLocalName())));
}
private void initDeviceData() {
- final WriteTransaction writeTx = dataService.newWriteOnlyTransaction();
+ final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
createNetworkTopologyIfNotPresent(writeTx);
public void updateDeviceData(boolean up, NetconfDeviceCapabilities capabilities) {
final Node data = buildDataForNetconfNode(up, capabilities);
- final WriteTransaction writeTx = dataService.newWriteOnlyTransaction();
+ final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
logger.trace("{}: Update device state transaction {} merging operational data started.", id, writeTx.getIdentifier());
writeTx.put(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath(), data);
logger.trace("{}: Update device state transaction {} merging operational data ended.", id, writeTx.getIdentifier());
final NetconfNode netconfNode = new NetconfNodeBuilder().setConnectionStatus(ConnectionStatus.UnableToConnect).setConnectedMessage(reason).build();
final Node data = getNodeIdBuilder(id).addAugmentation(NetconfNode.class, netconfNode).build();
- final WriteTransaction writeTx = dataService.newWriteOnlyTransaction();
+ final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
logger.trace("{}: Setting device state as failed {} putting operational data started.", id, writeTx.getIdentifier());
writeTx.put(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath(), data);
logger.trace("{}: Setting device state as failed {} putting operational data ended.", id, writeTx.getIdentifier());
}
public void removeDeviceConfiguration() {
- final WriteTransaction writeTx = dataService.newWriteOnlyTransaction();
+ final WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
logger.trace("{}: Close device state transaction {} removing all data started.", id, writeTx.getIdentifier());
writeTx.delete(LogicalDatastoreType.CONFIGURATION, id.getTopologyBindingPath());
@Override
public void close() throws Exception {
removeDeviceConfiguration();
+ txChain.close();
}
}
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.GET_SCHEMA_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DATA_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toId;
-
+import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.transform.dom.DOMSource;
-import org.apache.commons.io.IOUtils;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
@Override
public InputStream openStream() throws IOException {
- return IOUtils.toInputStream(schemaString.get());
+ return new ByteArrayInputStream(schemaString.get().getBytes(Charsets.UTF_8));
}
}
}
package org.opendaylight.controller.sal.connect.netconf.schema.mapping;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RPC_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_URI;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
static {
try {
final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
- // TODO this should be used only if the base is not present
moduleInfoBackedContext.addModuleInfos(
Lists.newArrayList(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.$YangModuleInfoImpl.getInstance()));
BASE_NETCONF_CTX = moduleInfoBackedContext.tryToCreateSchemaContext().get();
throw new ExceptionInInitializerError(e);
}
}
+ private static final Map<QName, RpcDefinition> MAPPED_BASE_RPCS = Maps.uniqueIndex(BASE_NETCONF_CTX.getOperations(), QNAME_FUNCTION);
private final SchemaContext schemaContext;
private final MessageCounter counter;
}
@Override
- public NetconfMessage toRpcRequest(SchemaPath rpc, final ContainerNode payload) {
+ public NetconfMessage toRpcRequest(SchemaPath rpc, final NormalizedNode<?, ?> payload) {
// In case no input for rpc is defined, we can simply construct the payload here
final QName rpcQName = rpc.getLastComponent();
- Preconditions.checkNotNull(mappedRpcs.get(rpcQName), "Unknown rpc %s, available rpcs: %s", rpcQName, mappedRpcs.keySet());
- if(mappedRpcs.get(rpcQName).getInput() == null) {
- final Document document = XmlUtil.newDocument();
- final Element elementNS = document.createElementNS(rpcQName.getNamespace().toString(), rpcQName.getLocalName());
- document.appendChild(elementNS);
- return new NetconfMessage(document);
+ Map<QName, RpcDefinition> currentMappedRpcs = mappedRpcs;
+
+ // Determine whether a base netconf operation is being invoked and also check if the device exposed model for base netconf
+ // If no, use pre built base netconf operations model
+ final boolean needToUseBaseCtx = mappedRpcs.get(rpcQName) == null && isBaseRpc(rpcQName);
+ if(needToUseBaseCtx) {
+ currentMappedRpcs = MAPPED_BASE_RPCS;
+ }
+
+ Preconditions.checkNotNull(currentMappedRpcs.get(rpcQName), "Unknown rpc %s, available rpcs: %s", rpcQName, currentMappedRpcs.keySet());
+ if(currentMappedRpcs.get(rpcQName).getInput() == null) {
+ return new NetconfMessage(prepareDomResultForRpcRequest(rpcQName).getNode().getOwnerDocument());
}
+ Preconditions.checkNotNull(payload, "Transforming an rpc with input: %s, payload cannot be null", rpcQName);
+ Preconditions.checkArgument(payload instanceof ContainerNode,
+ "Transforming an rpc with input: %s, payload has to be a container, but was: %s", rpcQName, payload);
+
// Set the path to the input of rpc for the node stream writer
rpc = rpc.createChild(QName.cachedReference(QName.create(rpcQName, "input")));
final DOMResult result = prepareDomResultForRpcRequest(rpcQName);
try {
- writeNormalizedRpc(payload, result, rpc, schemaContext);
+ // If the schema context for netconf device does not contain model for base netconf operations, use default pre build context with just the base model
+ // This way operations like lock/unlock are supported even if the source for base model was not provided
+ writeNormalizedRpc(((ContainerNode) payload), result, rpc, needToUseBaseCtx ? BASE_NETCONF_CTX : schemaContext);
} catch (final XMLStreamException | IOException | IllegalStateException e) {
throw new IllegalStateException("Unable to serialize " + rpc, e);
}
final Document node = result.getNode().getOwnerDocument();
- node.getDocumentElement().setAttribute(NetconfMessageTransformUtil.MESSAGE_ID_ATTR, counter.getNewMessageId(MESSAGE_ID_PREFIX));
return new NetconfMessage(node);
}
+ private static boolean isBaseRpc(final QName rpc) {
+ return rpc.getNamespace().equals(NETCONF_URI);
+ }
+
private DOMResult prepareDomResultForRpcRequest(final QName rpcQName) {
final Document document = XmlUtil.newDocument();
final Element rpcNS = document.createElementNS(NETCONF_RPC_QNAME.getNamespace().toString(), NETCONF_RPC_QNAME.getLocalName());
+ // set msg id
+ rpcNS.setAttribute(NetconfMessageTransformUtil.MESSAGE_ID_ATTR, counter.getNewMessageId(MESSAGE_ID_PREFIX));
final Element elementNS = document.createElementNS(rpcQName.getNamespace().toString(), rpcQName.getLocalName());
rpcNS.appendChild(elementNS);
document.appendChild(rpcNS);
@Override
public synchronized DOMRpcResult toRpcResult(final NetconfMessage message, final SchemaPath rpc) {
final NormalizedNode<?, ?> normalizedNode;
- if (NetconfMessageTransformUtil.isDataRetrievalOperation(rpc.getLastComponent())) {
+ final QName rpcQName = rpc.getLastComponent();
+ if (NetconfMessageTransformUtil.isDataRetrievalOperation(rpcQName)) {
final Element xmlData = NetconfMessageTransformUtil.getDataSubtree(message.getDocument());
final ContainerSchemaNode schemaForDataRead = NetconfMessageTransformUtil.createSchemaForDataRead(schemaContext);
final ContainerNode dataNode = parserFactory.getContainerNodeParser().parse(Collections.singleton(xmlData), schemaForDataRead);
.withChild(dataNode).build();
} else {
final Set<Element> documentElement = Collections.singleton(message.getDocument().getDocumentElement());
- final RpcDefinition rpcDefinition = mappedRpcs.get(rpc.getLastComponent());
- Preconditions.checkArgument(rpcDefinition != null, "Unable to parse response of %s, the rpc is unknown", rpc.getLastComponent());
+
+ Map<QName, RpcDefinition> currentMappedRpcs = mappedRpcs;
+
+ // Determine whether a base netconf operation is being invoked and also check if the device exposed model for base netconf
+ // If no, use pre built base netconf operations model
+ final boolean needToUseBaseCtx = mappedRpcs.get(rpcQName) == null && isBaseRpc(rpcQName);
+ if(needToUseBaseCtx) {
+ currentMappedRpcs = MAPPED_BASE_RPCS;
+ }
+
+ final RpcDefinition rpcDefinition = currentMappedRpcs.get(rpcQName);
+ Preconditions.checkArgument(rpcDefinition != null, "Unable to parse response of %s, the rpc is unknown", rpcQName);
// In case no input for rpc is defined, we can simply construct the payload here
if (rpcDefinition.getOutput() == null) {
import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
private final ImmutableMap<PathArgument, InstanceIdToNodes<?>> byArg;
- protected ChoiceNodeNormalization(final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) {
+ protected ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
super(new NodeIdentifier(schema.getQName()));
final ImmutableMap.Builder<PathArgument, InstanceIdToNodes<?>> byArgBuilder = ImmutableMap.builder();
private static Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent, final QName child) {
DataSchemaNode potential = parent.getDataChildByName(child);
if (potential == null) {
- final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices = FluentIterable.from(
- parent.getChildNodes()).filter(org.opendaylight.yangtools.yang.model.api.ChoiceNode.class);
+ final Iterable<ChoiceSchemaNode> choices = FluentIterable.from(parent.getChildNodes()).filter(ChoiceSchemaNode.class);
potential = findChoice(choices, child);
}
return Optional.fromNullable(potential);
return fromDataSchemaNode(result);
}
- private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice(
- final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices, final QName child) {
- org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null;
+ private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
+ org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode foundChoice = null;
choiceLoop:
- for (final org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) {
+ for (final ChoiceSchemaNode choice : choices) {
for (final ChoiceCaseNode caze : choice.getCases()) {
if (findChildSchemaNode(caze, child).isPresent()) {
foundChoice = choice;
return fromListSchemaNode((ListSchemaNode) potential);
} else if (potential instanceof LeafSchemaNode) {
return new LeafNormalization((LeafSchemaNode) potential);
- } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
- return new ChoiceNodeNormalization((org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential);
+ } else if (potential instanceof ChoiceSchemaNode) {
+ return new ChoiceNodeNormalization((ChoiceSchemaNode) potential);
} else if (potential instanceof LeafListSchemaNode) {
return fromLeafListSchemaNode((LeafListSchemaNode) potential);
} else if (potential instanceof AnyXmlSchemaNode) {
package org.opendaylight.controller.sal.connect.netconf.util;
-import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DEFAULT_OPERATION_QNAME;
public ListenableFuture<DOMRpcResult> discardChanges(final FutureCallback<DOMRpcResult> callback) {
Preconditions.checkNotNull(callback);
- final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_DISCARD_CHANGES_QNAME), DISCARD_CHANGES_RPC_CONTENT);
+ final ListenableFuture<DOMRpcResult> future = rpc.invokeRpc(toPath(NETCONF_DISCARD_CHANGES_QNAME), null);
Futures.addCallback(future, callback);
return future;
}
).build();
}
- public static NormalizedNode<?, ?> getLockContent(final QName datastore) {
+ public static ContainerNode getLockContent(final QName datastore) {
return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_LOCK_QNAME))
.withChild(getTargetNode(datastore)).build();
}
import com.google.common.util.concurrent.FutureCallback;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
-import org.opendaylight.controller.sal.connect.netconf.sal.tx.WriteRunningTx;
import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
* Simple Netconf rpc logging callback
*/
public class NetconfRpcFutureCallback implements FutureCallback<DOMRpcResult> {
- private static final Logger LOG = LoggerFactory.getLogger(WriteRunningTx.class);
+ private static final Logger LOG = LoggerFactory.getLogger(NetconfRpcFutureCallback.class);
private final String type;
private final RemoteDeviceId id;
private static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier createBIPath(final String name) {
final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder builder =
org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.builder();
- builder.node(Nodes.QNAME).nodeWithKey(Node.QNAME, QName.create(Node.QNAME.getNamespace(), Node.QNAME.getRevision(), "id"), name);
+ builder.node(Nodes.QNAME).node(Node.QNAME).nodeWithKey(Node.QNAME, QName.create(Node.QNAME.getNamespace(), Node.QNAME.getRevision(), "id"), name);
return builder.build();
}
import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.ModuleImport;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
public MessageTransformer<NetconfMessage> getMessageTransformer() throws Exception {
final MessageTransformer<NetconfMessage> messageTransformer = mockClass(MessageTransformer.class);
- doReturn(notification).when(messageTransformer).toRpcRequest(any(SchemaPath.class), any(ContainerNode.class));
+ doReturn(notification).when(messageTransformer).toRpcRequest(any(SchemaPath.class), any(NormalizedNode.class));
doReturn(rpcResultC).when(messageTransformer).toRpcResult(any(NetconfMessage.class), any(SchemaPath.class));
doReturn(compositeNode).when(messageTransformer).toNotification(any(NetconfMessage.class));
return messageTransformer;
final NetconfSessionPreferences sessionCaps2 = NetconfSessionPreferences.fromStrings(caps2);
assertCaps(sessionCaps2, 1, 2);
- final NetconfSessionPreferences merged = sessionCaps1.replaceModuleCaps(sessionCaps2);
- assertCaps(merged, 2, 2 + 1 /*Preserved monitoring*/);
+ final NetconfSessionPreferences merged = sessionCaps1.addModuleCaps(sessionCaps2);
+ assertCaps(merged, 2, 2 + 1 /*Preserved monitoring*/ + 2 /*already present*/);
for (final QName qName : sessionCaps2.getModuleBasedCaps()) {
assertThat(merged.getModuleBasedCaps(), hasItem(qName));
}
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCapabilities;
import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
@Mock
private WriteTransaction writeTx;
@Mock
+ private BindingTransactionChain txChain;
+ @Mock
private Node data;
private String txIdent = "test transaction";
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- doReturn(writeTx).when(broker).newWriteOnlyTransaction();
+ doReturn(txChain).when(broker).createTransactionChain(any(TransactionChainListener.class));
+ doReturn(writeTx).when(txChain).newWriteOnlyTransaction();
doNothing().when(writeTx).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class));
doNothing().when(writeTx).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class));
NetconfDeviceTopologyAdapter adapter = new NetconfDeviceTopologyAdapter(id, broker);
adapter.setDeviceAsFailed(null);
- verify(broker, times(2)).newWriteOnlyTransaction();
+ verify(txChain, times(2)).newWriteOnlyTransaction();
verify(writeTx, times(3)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class));
}
NetconfDeviceTopologyAdapter adapter = new NetconfDeviceTopologyAdapter(id, broker);
adapter.updateDeviceData(true, new NetconfDeviceCapabilities());
- verify(broker, times(2)).newWriteOnlyTransaction();
+ verify(txChain, times(2)).newWriteOnlyTransaction();
verify(writeTx, times(3)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class));
}
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
-import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath;
final InOrder inOrder = inOrder(rpc);
inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_LOCK_QNAME), NetconfBaseOps.getLockContent(NETCONF_CANDIDATE_QNAME));
inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME), NetconfMessageTransformUtil.COMMIT_RPC_CONTENT);
- inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME), DISCARD_CHANGES_RPC_CONTENT);
+ inOrder.verify(rpc).invokeRpc(eq(toPath(NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME)), any(NormalizedNode.class));
inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME), NetconfBaseOps.getUnLockContent(NETCONF_CANDIDATE_QNAME));
return;
}
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_LOCK_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.createEditConfigStructure;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toFilterStructure;
XMLUnit.setIgnoreAttributeOrder(true);
XMLUnit.setIgnoreComments(true);
- schema = getSchema();
+ schema = getSchema(true);
netconfMessageTransformer = getTransformer(schema);
}
+ @Test
+ public void testLockRequestBaseSchemaNotPresent() throws Exception {
+ final SchemaContext partialSchema = getSchema(false);
+ final NetconfMessageTransformer transformer = getTransformer(partialSchema);
+ final NetconfMessage netconfMessage = transformer.toRpcRequest(toPath(NETCONF_LOCK_QNAME),
+ NetconfBaseOps.getLockContent(NETCONF_CANDIDATE_QNAME));
+
+ assertThat(XmlUtil.toString(netconfMessage.getDocument()), CoreMatchers.containsString("<lock"));
+ assertThat(XmlUtil.toString(netconfMessage.getDocument()), CoreMatchers.containsString("<rpc"));
+ }
+
+ @Test
+ public void tesLockSchemaRequest() throws Exception {
+ final SchemaContext partialSchema = getSchema(false);
+ final NetconfMessageTransformer transformer = getTransformer(partialSchema);
+ final String result = "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><ok/></rpc-reply>";
+
+ transformer.toRpcResult(new NetconfMessage(XmlUtil.readXmlToDocument(result)), toPath(NETCONF_LOCK_QNAME));
+ }
+
@Test
public void testDiscardChangesRequest() throws Exception {
- final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(NETCONF_DISCARD_CHANGES_QNAME),
- NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT);
+ final NetconfMessage netconfMessage = netconfMessageTransformer.toRpcRequest(toPath(NETCONF_DISCARD_CHANGES_QNAME), null);
assertThat(XmlUtil.toString(netconfMessage.getDocument()), CoreMatchers.containsString("<discard"));
+ assertThat(XmlUtil.toString(netconfMessage.getDocument()), CoreMatchers.containsString("<rpc"));
+ assertThat(XmlUtil.toString(netconfMessage.getDocument()), CoreMatchers.containsString("message-id"));
}
@Test
@Test
public void tesGetSchemaResponse() throws Exception {
- final NetconfMessageTransformer netconfMessageTransformer = getTransformer(getSchema());
+ final NetconfMessageTransformer netconfMessageTransformer = getTransformer(getSchema(true));
final NetconfMessage response = new NetconfMessage(XmlUtil.readXmlToDocument(
"<rpc-reply message-id=\"101\"\n" +
"xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
"</data>\n" +
"</rpc-reply>"));
- final NetconfMessageTransformer netconfMessageTransformer = getTransformer(getSchema());
+ final NetconfMessageTransformer netconfMessageTransformer = getTransformer(getSchema(true));
final DOMRpcResult compositeNodeRpcResult = netconfMessageTransformer.toRpcResult(response, toPath(NETCONF_GET_CONFIG_QNAME));
assertTrue(compositeNodeRpcResult.getErrors().isEmpty());
assertNotNull(compositeNodeRpcResult.getResult());
assertNull(compositeNodeRpcResult.getResult());
}
- public SchemaContext getSchema() {
+ public SchemaContext getSchema(boolean addBase) {
final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
- moduleInfoBackedContext.addModuleInfos(Collections.singleton($YangModuleInfoImpl.getInstance()));
+ if(addBase) {
+ moduleInfoBackedContext.addModuleInfos(Collections.singleton($YangModuleInfoImpl.getInstance()));
+ }
moduleInfoBackedContext.addModuleInfos(Collections.singleton(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.$YangModuleInfoImpl.getInstance()));
return moduleInfoBackedContext.tryToCreateSchemaContext().get();
}
@Produces({ Draft02.MediaTypes.OPERATION + JSON, Draft02.MediaTypes.OPERATION + XML,
Draft02.MediaTypes.DATA + JSON, Draft02.MediaTypes.DATA + XML, MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+ @Deprecated // method isn't use anywhere
public NormalizedNodeContext invokeRpc(@Encoded @PathParam("identifier") String identifier,
@DefaultValue("") String noPayload, @Context UriInfo uriInfo);
import org.opendaylight.yangtools.yang.data.api.SimpleNode;
import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
if (node.getNodeType().equals(dsn.getQName())) {
return dsn;
}
- if (dsn instanceof ChoiceNode) {
- for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
+ if (dsn instanceof ChoiceSchemaNode) {
+ for (ChoiceCaseNode choiceCase : ((ChoiceSchemaNode) dsn).getCases()) {
DataSchemaNode foundDsn = findFirstSchemaForNode(node, choiceCase.getChildNodes());
if (foundDsn != null) {
return foundDsn;
import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
WebApplicationException {
try {
final InstanceIdentifierContext<?> path = getIdentifierWithSchema().get();
+ if (entityStream.available() < 1) {
+ return new NormalizedNodeContext(path, null);
+ }
final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
if(isPost()) {
// FIXME: We need dispatch for RPC.
parentSchema = path.getSchemaNode();
- } else if(path.getSchemaContext() instanceof SchemaContext) {
+ } else if(path.getSchemaNode() instanceof SchemaContext) {
parentSchema = path.getSchemaContext();
} else {
- parentSchema = SchemaContextUtil.findDataSchemaNode(path.getSchemaContext(), path.getSchemaNode().getPath().getParent());
+ if (SchemaPath.ROOT.equals(path.getSchemaNode().getPath().getParent())) {
+ parentSchema = path.getSchemaContext();
+ } else {
+ parentSchema = SchemaContextUtil.findDataSchemaNode(path.getSchemaContext(), path.getSchemaNode().getPath().getParent());
+ }
}
final JsonParserStream jsonParser = JsonParserStream.create(writer, path.getSchemaContext(), parentSchema);
import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
@Provider
return;
}
- final InstanceIdentifierContext<DataSchemaNode> context = (InstanceIdentifierContext<DataSchemaNode>) t.getInstanceIdentifierContext();
+ @SuppressWarnings("unchecked")
+ final InstanceIdentifierContext<SchemaNode> context = (InstanceIdentifierContext<SchemaNode>) t.getInstanceIdentifierContext();
SchemaPath path = context.getSchemaNode().getPath();
- boolean isDataRoot = false;
+ final JsonWriter jsonWriter = createJsonWriter(entityStream);
+ jsonWriter.beginObject();
+ writeNormalizedNode(jsonWriter,path,context,data);
+ jsonWriter.endObject();
+ jsonWriter.flush();
+ }
+
+ private void writeNormalizedNode(JsonWriter jsonWriter, SchemaPath path,
+ InstanceIdentifierContext<SchemaNode> context, NormalizedNode<?, ?> data) throws IOException {
+ final NormalizedNodeWriter nnWriter;
if (SchemaPath.ROOT.equals(path)) {
- isDataRoot = true;
+ /*
+ * Creates writer without initialNs and we write children of root data container
+ * which is not visible in restconf
+ */
+ nnWriter = createNormalizedNodeWriter(context,path,jsonWriter);
+ writeChildren(nnWriter,(ContainerNode) data);
+ } else if (context.getSchemaNode() instanceof RpcDefinition) {
+ /*
+ * RpcDefinition is not supported as initial codec in JSONStreamWriter,
+ * so we need to emit initial output declaratation..
+ */
+ path = ((RpcDefinition) context.getSchemaNode()).getOutput().getPath();
+ nnWriter = createNormalizedNodeWriter(context,path,jsonWriter);
+ jsonWriter.name("output");
+ jsonWriter.beginObject();
+ writeChildren(nnWriter, (ContainerNode) data);
+ jsonWriter.endObject();
} else {
path = path.getParent();
- // FIXME: Add proper handling of reading root.
- }
- final JsonWriter jsonWriter = createJsonWriter(entityStream);
- final NormalizedNodeWriter nnWriter = createNormalizedNodeWriter(context,path,jsonWriter);
-
- jsonWriter.beginObject();
- if(isDataRoot) {
- writeDataRoot(nnWriter,(ContainerNode) data);
- } else {
if(data instanceof MapEntryNode) {
data = ImmutableNodes.mapNodeBuilder(data.getNodeType()).withChild(((MapEntryNode) data)).build();
}
+ nnWriter = createNormalizedNodeWriter(context,path,jsonWriter);
nnWriter.write(data);
}
-
nnWriter.flush();
- jsonWriter.endObject();
- jsonWriter.flush();
}
- private NormalizedNodeWriter createNormalizedNodeWriter(final InstanceIdentifierContext<DataSchemaNode> context,
+ private void writeChildren(final NormalizedNodeWriter nnWriter, final ContainerNode data) throws IOException {
+ for(final DataContainerChild<? extends PathArgument, ?> child : data.getValue()) {
+ nnWriter.write(child);
+ }
+ }
+
+ private NormalizedNodeWriter createNormalizedNodeWriter(final InstanceIdentifierContext<SchemaNode> context,
final SchemaPath path, final JsonWriter jsonWriter) {
- final DataSchemaNode schema = context.getSchemaNode();
+ final SchemaNode schema = context.getSchemaNode();
final JSONCodecFactory codecs = getCodecFactory(context);
- URI initialNs = null;
- if(!schema.isAugmenting() && !(schema instanceof SchemaContext)) {
+ final URI initialNs;
+ if ((schema instanceof DataSchemaNode)
+ && !((DataSchemaNode)schema).isAugmenting()
+ && !(schema instanceof SchemaContext)) {
+ initialNs = schema.getQName().getNamespace();
+ } else if (schema instanceof RpcDefinition) {
initialNs = schema.getQName().getNamespace();
+ } else {
+ initialNs = null;
}
final NormalizedNodeStreamWriter streamWriter = JSONNormalizedNodeStreamWriter.createNestedWriter(codecs,path,initialNs,jsonWriter);
return NormalizedNodeWriter.forStreamWriter(streamWriter);
}
- private JSONCodecFactory getCodecFactory(final InstanceIdentifierContext context) {
+ private JSONCodecFactory getCodecFactory(final InstanceIdentifierContext<?> context) {
// TODO: Performance: Cache JSON Codec factory and schema context
return JSONCodecFactory.create(context.getSchemaContext());
}
- private void writeDataRoot(final NormalizedNodeWriter nnWriter, final ContainerNode data) throws IOException {
- for(final DataContainerChild<? extends PathArgument, ?> child : data.getValue()) {
- nnWriter.write(child);
- }
- }
-
}
package org.opendaylight.controller.sal.rest.impl;
import com.google.common.base.Throwables;
-import com.google.common.collect.Iterables;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
+import javax.xml.XMLConstants;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext;
import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
@Provider
@Produces({ Draft02.MediaTypes.API + RestconfService.XML, Draft02.MediaTypes.DATA + RestconfService.XML,
final Annotation[] annotations, final MediaType mediaType,
final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException,
WebApplicationException {
- final InstanceIdentifierContext pathContext = t.getInstanceIdentifierContext();
+ final InstanceIdentifierContext<?> pathContext = t.getInstanceIdentifierContext();
if (t.getData() == null) {
return;
}
NormalizedNode<?, ?> data = t.getData();
SchemaPath schemaPath = pathContext.getSchemaNode().getPath();
- // The utility method requires the path to be size of 2
- boolean isRpc = false;
- if(Iterables.size(schemaPath.getPathFromRoot()) > 1) {
- isRpc = SchemaContextUtil.getRpcDataSchema(t.getInstanceIdentifierContext().getSchemaContext(), schemaPath) != null;
- }
- boolean isDataRoot = false;
- if (SchemaPath.ROOT.equals(schemaPath)) {
- isDataRoot = true;
- // The rpc definitions required the schema path to point to the output container, not the parent (rpc itself)
- } else {
- if(!isRpc) {
- schemaPath = schemaPath.getParent();
- }
- }
- final NormalizedNodeStreamWriter jsonWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter,
- pathContext.getSchemaContext(), schemaPath);
- final NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(jsonWriter);
- if (isDataRoot) {
- writeRootElement(xmlWriter, nnWriter, (ContainerNode) data, SchemaContext.NAME);
- } else if(isRpc) {
- writeRootElement(xmlWriter, nnWriter, (ContainerNode) data, schemaPath.getLastComponent());
+ writeNormalizedNode(xmlWriter,schemaPath,pathContext,data);
+ }
+
+ private void writeNormalizedNode(XMLStreamWriter xmlWriter, SchemaPath schemaPath,InstanceIdentifierContext<?> pathContext, NormalizedNode<?, ?> data) throws IOException {
+ final NormalizedNodeWriter nnWriter;
+ final SchemaContext schemaCtx = pathContext.getSchemaContext();
+ if (SchemaPath.ROOT.equals(schemaPath)) {
+ nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, schemaPath);
+ writeElements(xmlWriter, nnWriter, (ContainerNode) data);
+ } else if (pathContext.getSchemaNode() instanceof RpcDefinition) {
+ nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, ((RpcDefinition) pathContext.getSchemaNode()).getOutput().getPath());
+ writeElements(xmlWriter, nnWriter, (ContainerNode) data);
} else {
+ nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, schemaPath.getParent());
if (data instanceof MapEntryNode) {
// Restconf allows returning one list item. We need to wrap it
// in map node in order to serialize it properly
data = ImmutableNodes.mapNodeBuilder(data.getNodeType()).addChild((MapEntryNode) data).build();
}
nnWriter.write(data);
- nnWriter.flush();
}
+ nnWriter.flush();
+ }
+
+ private NormalizedNodeWriter createNormalizedNodeWriter(XMLStreamWriter xmlWriter,
+ SchemaContext schemaContext, SchemaPath schemaPath) {
+ NormalizedNodeStreamWriter xmlStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, schemaContext, schemaPath);
+ return NormalizedNodeWriter.forStreamWriter(xmlStreamWriter);
}
- private void writeRootElement(final XMLStreamWriter xmlWriter, final NormalizedNodeWriter nnWriter, final ContainerNode data, final QName name)
+ private void writeElements(final XMLStreamWriter xmlWriter, final NormalizedNodeWriter nnWriter, final ContainerNode data)
throws IOException {
try {
- xmlWriter.writeStartElement(name.getNamespace().toString(), name.getLocalName());
- for (final DataContainerChild<? extends PathArgument, ?> child : data.getValue()) {
+ final QName name = data.getNodeType();
+ xmlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, name.getLocalName(), name.getNamespace().toString());
+ xmlWriter.writeDefaultNamespace(name.getNamespace().toString());
+ for(NormalizedNode<?,?> child : data.getValue()) {
nnWriter.write(child);
}
nnWriter.flush();
try {
final Optional<InstanceIdentifierContext> path = getIdentifierWithSchema();
+ if (entityStream.available() < 1) {
+ // represent empty nopayload input
+ return new NormalizedNodeContext(path.get(), null);
+ }
+
final DocumentBuilder dBuilder;
try {
dBuilder = BUILDERFACTORY.newDocumentBuilder();
final NormalizedNode<?, ?> result = parse(path.get(),doc);
return new NormalizedNodeContext(path.get(),result);
} catch (final Exception e) {
- LOG.debug("Error parsing json input", e);
+ LOG.debug("Error parsing xml input", e);
throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL,
ErrorTag.MALFORMED_MESSAGE);
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
return null;
}
- private static DataSchemaNode childByQName(final ChoiceNode container, final QName name) {
+ private static DataSchemaNode childByQName(final ChoiceSchemaNode container, final QName name) {
for (final ChoiceCaseNode caze : container.getCases()) {
final DataSchemaNode ret = ControllerContext.childByQName(caze, name);
if (ret != null) {
final DataSchemaNode ret = container.getDataChildByName(name);
if (ret == null) {
for (final DataSchemaNode node : container.getChildNodes()) {
- if ((node instanceof ChoiceNode)) {
- final ChoiceNode choiceNode = ((ChoiceNode) node);
+ if ((node instanceof ChoiceSchemaNode)) {
+ final ChoiceSchemaNode choiceNode = ((ChoiceSchemaNode) node);
final DataSchemaNode childByQName = ControllerContext.childByQName(choiceNode, name);
if (childByQName != null) {
return childByQName;
return instantiatedDataNodeContainers;
}
- private static final Function<ChoiceNode, Set<ChoiceCaseNode>> CHOICE_FUNCTION = new Function<ChoiceNode, Set<ChoiceCaseNode>>() {
+ private static final Function<ChoiceSchemaNode, Set<ChoiceCaseNode>> CHOICE_FUNCTION = new Function<ChoiceSchemaNode, Set<ChoiceCaseNode>>() {
@Override
- public Set<ChoiceCaseNode> apply(final ChoiceNode node) {
+ public Set<ChoiceCaseNode> apply(final ChoiceSchemaNode node) {
return node.getCases();
}
};
}
}
- final Iterable<ChoiceNode> choiceNodes = Iterables.filter(container.getChildNodes(), ChoiceNode.class);
+ final Iterable<ChoiceSchemaNode> choiceNodes = Iterables.filter(container.getChildNodes(), ChoiceSchemaNode.class);
final Iterable<Set<ChoiceCaseNode>> map = Iterables.transform(choiceNodes, CHOICE_FUNCTION);
final Iterable<ChoiceCaseNode> allCases = Iterables.<ChoiceCaseNode> concat(map);
private static DataSchemaNode childByQName(final Object container, final QName name) {
if (container instanceof ChoiceCaseNode) {
return childByQName((ChoiceCaseNode) container, name);
- } else if (container instanceof ChoiceNode) {
- return childByQName((ChoiceNode) container, name);
+ } else if (container instanceof ChoiceSchemaNode) {
+ return childByQName((ChoiceSchemaNode) container, name);
} else if (container instanceof ContainerSchemaNode) {
return childByQName((ContainerSchemaNode) container, name);
} else if (container instanceof ListSchemaNode) {
final DOMRpcResult result = checkRpcResponse(response);
- DataSchemaNode resultNodeSchema = null;
+ RpcDefinition resultNodeSchema = null;
final NormalizedNode<?, ?> resultData = result.getResult();
if (result != null && result.getResult() != null) {
- final RpcDefinition rpcDef = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode();
- resultNodeSchema = rpcDef.getOutput();
+ resultNodeSchema = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode();
}
- return new NormalizedNodeContext(new InstanceIdentifierContext(null, resultNodeSchema, mountPoint,
- schemaContext), resultData);
+ return new NormalizedNodeContext(new InstanceIdentifierContext<RpcDefinition>(null,
+ resultNodeSchema, mountPoint, schemaContext), resultData);
}
private DOMRpcResult checkRpcResponse(final CheckedFuture<DOMRpcResult, DOMRpcException> response) {
if (rpc.getInput() != null) {
// FIXME : find a correct Error from specification
- throw new IllegalStateException("RPC " + rpc + " needs input value!");
+ throw new IllegalStateException("RPC " + rpc + " does'n need input value!");
}
final CheckedFuture<DOMRpcResult, DOMRpcException> response;
final YangInstanceIdentifier resultII;
try {
if (mountPoint != null) {
- broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
-
+ broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData()).checkedGet();
} else {
- broker.commitConfigurationDataPost(normalizedII, payload.getData());
+ broker.commitConfigurationDataPost(normalizedII, payload.getData()).checkedGet();
}
} catch(final RestconfDocumentedException e) {
throw e;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.opendaylight.controller.sal.rest.impl.test.providers.TestJsonBodyWriter;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
/**
* sal-rest-connector
private static final Logger LOG = LoggerFactory.getLogger(TestRestconfUtils.class);
- private final static YangContextParser parser = new YangParserImpl();
+ private static final YangContextParser parser = new YangParserImpl();
+
+ private static final DocumentBuilderFactory BUILDERFACTORY;
+
+ static {
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ try {
+ factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ factory.setXIncludeAware(false);
+ factory.setExpandEntityReferences(false);
+ } catch (final ParserConfigurationException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ factory.setNamespaceAware(true);
+ factory.setCoalescing(true);
+ factory.setIgnoringElementContentWhitespace(true);
+ factory.setIgnoringComments(true);
+ BUILDERFACTORY = factory;
+ }
private TestRestconfUtils () {
throw new UnsupportedOperationException("Test utility class");
return schemaContext;
}
+ public static NormalizedNodeContext loadNormalizedContextFromJsonFile() {
+ throw new AbstractMethodError("Not implemented yet");
+ }
+
+ public static NormalizedNodeContext loadNormalizedContextFromXmlFile(final String pathToInputFile, final String uri) {
+ final InstanceIdentifierContext<?> iiContext = ControllerContext.getInstance().toInstanceIdentifier(uri);
+ final InputStream inputStream = TestJsonBodyWriter.class.getResourceAsStream(pathToInputFile);
+ try {
+ final DocumentBuilder dBuilder = BUILDERFACTORY.newDocumentBuilder();
+ final Document doc = dBuilder.parse(inputStream);
+ final NormalizedNode<?, ?> nn = parse(iiContext, doc);
+ return new NormalizedNodeContext(iiContext, nn);
+ }
+ catch (final Exception e) {
+ LOG.error("Load xml file " + pathToInputFile + " fail.", e);
+ }
+ return null;
+ }
+
+ private static NormalizedNode<?, ?> parse(final InstanceIdentifierContext<?> iiContext, final Document doc) {
+ final List<Element> elements = Collections.singletonList(doc.getDocumentElement());
+ final SchemaNode schemaNodeContext = iiContext.getSchemaNode();
+ DataSchemaNode schemaNode = null;
+ if (schemaNodeContext instanceof RpcDefinition) {
+ if ("input".equalsIgnoreCase(doc.getDocumentElement().getLocalName())) {
+ schemaNode = ((RpcDefinition) schemaNodeContext).getInput();
+ } else if ("output".equalsIgnoreCase(doc.getDocumentElement().getLocalName())) {
+ schemaNode = ((RpcDefinition) schemaNodeContext).getOutput();
+ } else {
+ throw new IllegalStateException("Unknown Rpc input node");
+ }
+
+ } else if (schemaNodeContext instanceof DataSchemaNode) {
+ schemaNode = (DataSchemaNode) schemaNodeContext;
+ } else {
+ throw new IllegalStateException("Unknow SchemaNode");
+ }
+
+ final String docRootElm = doc.getDocumentElement().getLocalName();
+ final String schemaNodeName = iiContext.getSchemaNode().getQName().getLocalName();
+
+ if (!schemaNodeName.equalsIgnoreCase(docRootElm)) {
+ final Collection<DataSchemaNode> children = ((DataNodeContainer) schemaNode).getChildNodes();
+ for (final DataSchemaNode child : children) {
+ if (child.getQName().getLocalName().equalsIgnoreCase(docRootElm)) {
+ schemaNode = child;
+ break;
+ }
+ }
+ }
+ final DomToNormalizedNodeParserFactory parserFactory =
+ DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, iiContext.getSchemaContext());
+
+ if(schemaNode instanceof ContainerSchemaNode) {
+ return parserFactory.getContainerNodeParser().parse(Collections.singletonList(doc.getDocumentElement()), (ContainerSchemaNode) schemaNode);
+ } else if(schemaNode instanceof ListSchemaNode) {
+ final ListSchemaNode casted = (ListSchemaNode) schemaNode;
+ return parserFactory.getMapEntryNodeParser().parse(elements, casted);
+ } // FIXME : add another DataSchemaNode extensions e.g. LeafSchemaNode
+ return null;
+ }
+
private static Collection<File> loadFiles(final String resourceDirectory) throws FileNotFoundException {
final String path = TestRestconfUtils.class.getResource(resourceDirectory).getPath();
final File testDir = new File(path);
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-
import java.lang.reflect.Field;
import java.util.Collections;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Request;
import javax.ws.rs.core.UriInfo;
import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
import org.opendaylight.controller.sal.rest.api.RestconfConstants;
protected final static ControllerContext controllerContext = ControllerContext.getInstance();
protected final MediaType mediaType;
private static Field uriField;
+ private static Field requestField;
public AbstractBodyReaderTest () throws NoSuchFieldException, SecurityException {
uriField = AbstractIdentifierAwareJaxRsProvider.class.getDeclaredField("uriInfo");
uriField.setAccessible(true);
+ requestField = AbstractIdentifierAwareJaxRsProvider.class.getDeclaredField("request");
+ requestField.setAccessible(true);
mediaType = getMediaType();
}
}
protected static <T extends AbstractIdentifierAwareJaxRsProvider> void mockBodyReader(
- final String identifier, final T normalizedNodeProvider) throws NoSuchFieldException,
+ final String identifier, final T normalizedNodeProvider, final boolean isPost) throws NoSuchFieldException,
SecurityException, IllegalArgumentException, IllegalAccessException {
final UriInfo uriInfoMock = mock(UriInfo.class);
final MultivaluedMap<String, String> pathParm = new MultivaluedHashMap<>(1);
when(uriInfoMock.getPathParameters(false)).thenReturn(pathParm);
when(uriInfoMock.getPathParameters(true)).thenReturn(pathParm);
uriField.set(normalizedNodeProvider, uriInfoMock);
+ final Request request = mock(Request.class);
+ if (isPost) {
+ when(request.getMethod()).thenReturn("POST");
+ } else {
+ when(request.getMethod()).thenReturn("PUT");
+ }
+ requestField.set(normalizedNodeProvider, request);
}
protected static void checkMountPointNormalizedNodeContext(final NormalizedNodeContext nnContext) {
--- /dev/null
+/**
+ * Copyright (c) 2015 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.rest.impl.test.providers;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import com.google.common.base.Optional;
+import java.io.InputStream;
+import javax.ws.rs.core.MediaType;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.sal.rest.impl.JsonNormalizedNodeBodyReader;
+import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * sal-rest-connector
+ * org.opendaylight.controller.sal.rest.impl.test.providers
+ *
+ *
+ *
+ * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
+ *
+ * Created: Mar 11, 2015
+ */
+public class TestJsonBodyReader extends AbstractBodyReaderTest {
+
+ private final JsonNormalizedNodeBodyReader jsonBodyReader;
+ private static SchemaContext schemaContext;
+
+ public TestJsonBodyReader () throws NoSuchFieldException, SecurityException {
+ super();
+ jsonBodyReader = new JsonNormalizedNodeBodyReader();
+ }
+
+ @Override
+ MediaType getMediaType() {
+ return new MediaType(MediaType.APPLICATION_XML, null);
+ }
+
+ @BeforeClass
+ public static void initialization() throws NoSuchFieldException, SecurityException {
+ schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+ schemaContext = schemaContextLoader("/modules", schemaContext);
+ schemaContext = schemaContextLoader("/invoke-rpc", schemaContext);
+ controllerContext.setSchemas(schemaContext);
+ }
+
+ @Test
+ public void moduleDataTest() throws Exception {
+ final DataSchemaNode dataSchemaNode = schemaContext.getDataChildByName("cont");
+ final String uri = "instance-identifier-module:cont";
+ mockBodyReader(uri, jsonBodyReader, false);
+ final InputStream inputStream = TestJsonBodyReader.class
+ .getResourceAsStream("/instanceidentifier/json/jsondata.json");
+ final NormalizedNodeContext returnValue = jsonBodyReader
+ .readFrom(null, null, null, mediaType, null, inputStream);
+ checkNormalizedNodeContext(returnValue);
+ checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue);
+ }
+
+ @Test
+ public void moduleSubContainerDataPutTest() throws Exception {
+ final DataSchemaNode dataSchemaNode = schemaContext.getDataChildByName("cont");
+ final String uri = "instance-identifier-module:cont/cont1";
+ mockBodyReader(uri, jsonBodyReader, false);
+ final InputStream inputStream = TestJsonBodyReader.class
+ .getResourceAsStream("/instanceidentifier/json/json_sub_container.json");
+ final NormalizedNodeContext returnValue = jsonBodyReader
+ .readFrom(null, null, null, mediaType, null, inputStream);
+ checkNormalizedNodeContext(returnValue);
+ checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, "cont1");
+ }
+
+ @Test
+ public void moduleSubContainerDataPostTest() throws Exception {
+ final DataSchemaNode dataSchemaNode = schemaContext.getDataChildByName("cont");
+ final String uri = "instance-identifier-module:cont";
+ mockBodyReader(uri, jsonBodyReader, true);
+ final InputStream inputStream = TestJsonBodyReader.class
+ .getResourceAsStream("/instanceidentifier/json/json_sub_container.json");
+ final NormalizedNodeContext returnValue = jsonBodyReader
+ .readFrom(null, null, null, mediaType, null, inputStream);
+ checkNormalizedNodeContext(returnValue);
+ checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue);
+ }
+
+ @Test
+ public void rpcModuleInputTest() throws Exception {
+ final String uri = "invoke-rpc-module:rpc-test";
+ mockBodyReader(uri, jsonBodyReader, true);
+ final InputStream inputStream = TestJsonBodyReader.class
+ .getResourceAsStream("/invoke-rpc/json/rpc-input.json");
+ final NormalizedNodeContext returnValue = jsonBodyReader
+ .readFrom(null, null, null, mediaType, null, inputStream);
+ checkNormalizedNodeContext(returnValue);
+ final ContainerNode inputNode = (ContainerNode) returnValue.getData();
+ final YangInstanceIdentifier yangCont = YangInstanceIdentifier.of(QName.create(inputNode.getNodeType(), "cont"));
+ final Optional<DataContainerChild<? extends PathArgument, ?>> contDataNode = inputNode.getChild(yangCont.getLastPathArgument());
+ assertTrue(contDataNode.isPresent());
+ assertTrue(contDataNode.get() instanceof ContainerNode);
+ final YangInstanceIdentifier yangleaf = YangInstanceIdentifier.of(QName.create(inputNode.getNodeType(), "lf"));
+ final Optional<DataContainerChild<? extends PathArgument, ?>> leafDataNode = ((ContainerNode)contDataNode.get()).getChild(yangleaf.getLastPathArgument());
+ assertTrue(leafDataNode.isPresent());
+ assertTrue("lf-test".equalsIgnoreCase(leafDataNode.get().getValue().toString()));
+ }
+
+ private void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode,
+ final NormalizedNodeContext nnContext) {
+ checkExpectValueNormalizeNodeContext(dataSchemaNode, nnContext, null);
+ }
+
+ private void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode,
+ final NormalizedNodeContext nnContext, final String localQname) {
+ YangInstanceIdentifier dataNodeIdent = YangInstanceIdentifier.of(dataSchemaNode.getQName());
+
+ if (localQname != null && dataSchemaNode instanceof DataNodeContainer) {
+ final DataSchemaNode child = ((DataNodeContainer) dataSchemaNode).getDataChildByName(localQname);
+ dataNodeIdent = YangInstanceIdentifier.builder(dataNodeIdent).node(child.getQName()).build();
+ assertTrue(nnContext.getInstanceIdentifierContext().getSchemaNode().equals(child));
+ } else {
+ assertTrue(nnContext.getInstanceIdentifierContext().getSchemaNode().equals(dataSchemaNode));
+ }
+ assertTrue(nnContext.getInstanceIdentifierContext().getInstanceIdentifier().equals(dataNodeIdent));
+ assertNotNull(NormalizedNodes.findNode(nnContext.getData(), dataNodeIdent));
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2015 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.rest.impl.test.providers;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import com.google.common.base.Optional;
+import java.io.InputStream;
+import javax.ws.rs.core.MediaType;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.rest.impl.JsonNormalizedNodeBodyReader;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * sal-rest-connector
+ * org.opendaylight.controller.sal.rest.impl.test.providers
+ *
+ *
+ *
+ * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
+ *
+ * Created: Mar 11, 2015
+ */
+public class TestJsonBodyReaderMountPoint extends AbstractBodyReaderTest {
+
+ private final JsonNormalizedNodeBodyReader jsonBodyReader;
+ private static SchemaContext schemaContext;
+
+ public TestJsonBodyReaderMountPoint () throws NoSuchFieldException, SecurityException {
+ super();
+ jsonBodyReader = new JsonNormalizedNodeBodyReader();
+ }
+
+ @Override
+ MediaType getMediaType() {
+ return new MediaType(MediaType.APPLICATION_XML, null);
+ }
+
+ @BeforeClass
+ public static void initialization() throws NoSuchFieldException, SecurityException {
+ schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+ schemaContext = schemaContextLoader("/modules", schemaContext);
+ schemaContext = schemaContextLoader("/invoke-rpc", schemaContext);
+ final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
+ when(mountInstance.getSchemaContext()).thenReturn(schemaContext);
+ final DOMMountPointService mockMountService = mock(DOMMountPointService.class);
+ when(mockMountService.getMountPoint(any(YangInstanceIdentifier.class))).thenReturn(Optional.of(mountInstance));
+
+ ControllerContext.getInstance().setMountService(mockMountService);
+ controllerContext.setSchemas(schemaContext);
+ }
+
+ @Test
+ public void moduleDataTest() throws Exception {
+ final DataSchemaNode dataSchemaNode = schemaContext.getDataChildByName("cont");
+ final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont";
+ mockBodyReader(uri, jsonBodyReader, false);
+ final InputStream inputStream = TestJsonBodyReaderMountPoint.class
+ .getResourceAsStream("/instanceidentifier/json/jsondata.json");
+ final NormalizedNodeContext returnValue = jsonBodyReader
+ .readFrom(null, null, null, mediaType, null, inputStream);
+ checkMountPointNormalizedNodeContext(returnValue);
+ checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue);
+ }
+
+ @Test
+ public void moduleSubContainerDataPutTest() throws Exception {
+ final DataSchemaNode dataSchemaNode = schemaContext.getDataChildByName("cont");
+ final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont/cont1";
+ mockBodyReader(uri, jsonBodyReader, false);
+ final InputStream inputStream = TestJsonBodyReaderMountPoint.class
+ .getResourceAsStream("/instanceidentifier/json/json_sub_container.json");
+ final NormalizedNodeContext returnValue = jsonBodyReader
+ .readFrom(null, null, null, mediaType, null, inputStream);
+ checkMountPointNormalizedNodeContext(returnValue);
+ checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, "cont1");
+ }
+
+ @Test
+ public void moduleSubContainerDataPostTest() throws Exception {
+ final DataSchemaNode dataSchemaNode = schemaContext.getDataChildByName("cont");
+ final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont";
+ mockBodyReader(uri, jsonBodyReader, true);
+ final InputStream inputStream = TestJsonBodyReaderMountPoint.class
+ .getResourceAsStream("/instanceidentifier/json/json_sub_container.json");
+ final NormalizedNodeContext returnValue = jsonBodyReader
+ .readFrom(null, null, null, mediaType, null, inputStream);
+ checkMountPointNormalizedNodeContext(returnValue);
+ checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue);
+ }
+
+ @Test
+ public void rpcModuleInputTest() throws Exception {
+ final String uri = "instance-identifier-module:cont/yang-ext:mount/invoke-rpc-module:rpc-test";
+ mockBodyReader(uri, jsonBodyReader, true);
+ final InputStream inputStream = TestJsonBodyReaderMountPoint.class
+ .getResourceAsStream("/invoke-rpc/json/rpc-input.json");
+ final NormalizedNodeContext returnValue = jsonBodyReader
+ .readFrom(null, null, null, mediaType, null, inputStream);
+ checkNormalizedNodeContext(returnValue);
+ final ContainerNode inputNode = (ContainerNode) returnValue.getData();
+ final YangInstanceIdentifier yangCont = YangInstanceIdentifier.of(QName.create(inputNode.getNodeType(), "cont"));
+ final Optional<DataContainerChild<? extends PathArgument, ?>> contDataNode = inputNode.getChild(yangCont.getLastPathArgument());
+ assertTrue(contDataNode.isPresent());
+ assertTrue(contDataNode.get() instanceof ContainerNode);
+ final YangInstanceIdentifier yangleaf = YangInstanceIdentifier.of(QName.create(inputNode.getNodeType(), "lf"));
+ final Optional<DataContainerChild<? extends PathArgument, ?>> leafDataNode = ((ContainerNode)contDataNode.get()).getChild(yangleaf.getLastPathArgument());
+ assertTrue(leafDataNode.isPresent());
+ assertTrue("lf-test".equalsIgnoreCase(leafDataNode.get().getValue().toString()));
+ }
+
+ private void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode,
+ final NormalizedNodeContext nnContext) {
+ checkExpectValueNormalizeNodeContext(dataSchemaNode, nnContext, null);
+ }
+
+ protected void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode,
+ final NormalizedNodeContext nnContext, final String localQname) {
+ YangInstanceIdentifier dataNodeIdent = YangInstanceIdentifier.of(dataSchemaNode.getQName());
+ final DOMMountPoint mountPoint = nnContext.getInstanceIdentifierContext().getMountPoint();
+ final DataSchemaNode mountDataSchemaNode =
+ mountPoint.getSchemaContext().getDataChildByName(dataSchemaNode.getQName());
+ assertNotNull(mountDataSchemaNode);
+ if (localQname != null && dataSchemaNode instanceof DataNodeContainer) {
+ final DataSchemaNode child = ((DataNodeContainer) dataSchemaNode).getDataChildByName(localQname);
+ dataNodeIdent = YangInstanceIdentifier.builder(dataNodeIdent).node(child.getQName()).build();
+ assertTrue(nnContext.getInstanceIdentifierContext().getSchemaNode().equals(child));
+ } else {
+ assertTrue(mountDataSchemaNode.equals(dataSchemaNode));
+ }
+ assertNotNull(NormalizedNodes.findNode(nnContext.getData(), dataNodeIdent));
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2015 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.rest.impl.test.providers;
+
+import static org.junit.Assert.assertTrue;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import javax.ws.rs.core.MediaType;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.sal.rest.impl.JsonNormalizedNodeBodyReader;
+import org.opendaylight.controller.sal.rest.impl.NormalizedNodeJsonBodyWriter;
+import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * sal-rest-connector
+ * org.opendaylight.controller.sal.rest.impl.test.providers
+ *
+ *
+ *
+ * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
+ *
+ * Created: Mar 12, 2015
+ */
+public class TestJsonBodyWriter extends AbstractBodyReaderTest {
+
+ private final JsonNormalizedNodeBodyReader jsonBodyReader;
+ private final NormalizedNodeJsonBodyWriter jsonBodyWriter;
+ private static SchemaContext schemaContext;
+
+ public TestJsonBodyWriter () throws NoSuchFieldException, SecurityException {
+ super();
+ jsonBodyWriter = new NormalizedNodeJsonBodyWriter();
+ jsonBodyReader = new JsonNormalizedNodeBodyReader();
+ }
+
+ @Override
+ MediaType getMediaType() {
+ return new MediaType(MediaType.APPLICATION_XML, null);
+ }
+
+ @BeforeClass
+ public static void initialization() throws NoSuchFieldException, SecurityException {
+ schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+ schemaContext = schemaContextLoader("/modules", schemaContext);
+ schemaContext = schemaContextLoader("/invoke-rpc", schemaContext);
+ controllerContext.setSchemas(schemaContext);
+ }
+
+ @Test
+ public void rpcModuleInputTest() throws Exception {
+ final String uri = "invoke-rpc-module:rpc-test";
+ mockBodyReader(uri, jsonBodyReader, true);
+ final InputStream inputStream = TestJsonBodyWriter.class
+ .getResourceAsStream("/invoke-rpc/json/rpc-output.json");
+ final NormalizedNodeContext returnValue = jsonBodyReader
+ .readFrom(null, null, null, mediaType, null, inputStream);
+ final OutputStream output = new ByteArrayOutputStream();
+ jsonBodyWriter.writeTo(returnValue, null, null, null, mediaType, null, output);
+ assertTrue(output.toString().contains("lf-test"));
+ }
+}
public void moduleDataTest() throws Exception {
final DataSchemaNode dataSchemaNode = schemaContext.getDataChildByName("cont");
final String uri = "instance-identifier-module:cont";
- mockBodyReader(uri, xmlBodyReader);
+ mockBodyReader(uri, xmlBodyReader, false);
final InputStream inputStream = TestXmlBodyReader.class
.getResourceAsStream("/instanceidentifier/xml/xmldata.xml");
final NormalizedNodeContext returnValue = xmlBodyReader
public void moduleSubContainerDataPutTest() throws Exception {
final DataSchemaNode dataSchemaNode = schemaContext.getDataChildByName("cont");
final String uri = "instance-identifier-module:cont/cont1";
- mockBodyReader(uri, xmlBodyReader);
+ mockBodyReader(uri, xmlBodyReader, false);
final InputStream inputStream = TestXmlBodyReader.class
.getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml");
final NormalizedNodeContext returnValue = xmlBodyReader
public void moduleSubContainerDataPostTest() throws Exception {
final DataSchemaNode dataSchemaNode = schemaContext.getDataChildByName("cont");
final String uri = "instance-identifier-module:cont";
- mockBodyReader(uri, xmlBodyReader);
+ mockBodyReader(uri, xmlBodyReader, true);
final InputStream inputStream = TestXmlBodyReader.class
.getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml");
final NormalizedNodeContext returnValue = xmlBodyReader
@Test
public void rpcModuleInputTest() throws Exception {
final String uri = "invoke-rpc-module:rpc-test";
- mockBodyReader(uri, xmlBodyReader);
+ mockBodyReader(uri, xmlBodyReader, true);
final InputStream inputStream = TestXmlBodyReader.class
.getResourceAsStream("/invoke-rpc/xml/rpc-input.xml");
final NormalizedNodeContext returnValue = xmlBodyReader
public void moduleDataTest() throws Exception {
final DataSchemaNode dataSchemaNode = schemaContext.getDataChildByName("cont");
final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont";
- mockBodyReader(uri, xmlBodyReader);
- final InputStream inputStream = TestXmlBodyReader.class
+ mockBodyReader(uri, xmlBodyReader, false);
+ final InputStream inputStream = TestXmlBodyReaderMountPoint.class
.getResourceAsStream("/instanceidentifier/xml/xmldata.xml");
final NormalizedNodeContext returnValue = xmlBodyReader
.readFrom(null, null, null, mediaType, null, inputStream);
public void moduleSubContainerDataPutTest() throws Exception {
final DataSchemaNode dataSchemaNode = schemaContext.getDataChildByName("cont");
final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont/cont1";
- mockBodyReader(uri, xmlBodyReader);
- final InputStream inputStream = TestXmlBodyReader.class
+ mockBodyReader(uri, xmlBodyReader, false);
+ final InputStream inputStream = TestXmlBodyReaderMountPoint.class
.getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml");
final NormalizedNodeContext returnValue = xmlBodyReader
.readFrom(null, null, null, mediaType, null, inputStream);
public void moduleSubContainerDataPostTest() throws Exception {
final DataSchemaNode dataSchemaNode = schemaContext.getDataChildByName("cont");
final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont";
- mockBodyReader(uri, xmlBodyReader);
- final InputStream inputStream = TestXmlBodyReader.class
+ mockBodyReader(uri, xmlBodyReader, true);
+ final InputStream inputStream = TestXmlBodyReaderMountPoint.class
.getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml");
final NormalizedNodeContext returnValue = xmlBodyReader
.readFrom(null, null, null, mediaType, null, inputStream);
@Test
public void rpcModuleInputTest() throws Exception {
final String uri = "instance-identifier-module:cont/yang-ext:mount/invoke-rpc-module:rpc-test";
- mockBodyReader(uri, xmlBodyReader);
- final InputStream inputStream = TestXmlBodyReader.class
+ mockBodyReader(uri, xmlBodyReader, true);
+ final InputStream inputStream = TestXmlBodyReaderMountPoint.class
.getResourceAsStream("/invoke-rpc/xml/rpc-input.xml");
final NormalizedNodeContext returnValue = xmlBodyReader
.readFrom(null, null, null, mediaType, null, inputStream);
--- /dev/null
+/**
+ * Copyright (c) 2015 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.rest.impl.test.providers;
+
+import static org.junit.Assert.assertTrue;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import javax.ws.rs.core.MediaType;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.controller.sal.rest.impl.NormalizedNodeXmlBodyWriter;
+import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * sal-rest-connector
+ * org.opendaylight.controller.sal.rest.impl.test.providers
+ *
+ *
+ *
+ * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
+ *
+ * Created: Mar 12, 2015
+ */
+public class TestXmlBodyWriter extends AbstractBodyReaderTest {
+
+ private final NormalizedNodeXmlBodyWriter xmlBodyWriter;
+ private static SchemaContext schemaContext;
+
+ public TestXmlBodyWriter () throws NoSuchFieldException, SecurityException {
+ super();
+ xmlBodyWriter = new NormalizedNodeXmlBodyWriter();
+ }
+
+ @Override
+ MediaType getMediaType() {
+ return new MediaType(MediaType.APPLICATION_XML, null);
+ }
+
+ @BeforeClass
+ public static void initialization() throws NoSuchFieldException, SecurityException {
+ schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext);
+ schemaContext = schemaContextLoader("/modules", schemaContext);
+ schemaContext = schemaContextLoader("/invoke-rpc", schemaContext);
+ controllerContext.setSchemas(schemaContext);
+ }
+
+ @Test
+ public void rpcModuleInputTest() throws Exception {
+ final String uri = "invoke-rpc-module:rpc-test";
+ final String pathToInputFile = "/invoke-rpc/xml/rpc-output.xml";
+ final NormalizedNodeContext nnContext =
+ TestRestconfUtils.loadNormalizedContextFromXmlFile(pathToInputFile, uri);
+ final OutputStream output = new ByteArrayOutputStream();
+ xmlBodyWriter.writeTo(nnContext, null, null, null, mediaType, null, output);
+ assertTrue(output.toString().contains("lf-test"));
+ }
+}
\ No newline at end of file
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
import org.opendaylight.controller.sal.rest.api.Draft02;
restconfImpl.setControllerContext(context);
}
+// @Test
+// public void postRpcNoPayload() throws Exception {
+// setSchemaControllerContext(schemaContextTestModule);
+// final String uri = "/operations/test-module:no-payload-rpc-test";
+// final String mediaType = MediaType.APPLICATION_XML;
+// final Response response = target(uri).request(mediaType).post(Entity.entity("", mediaType));
+// assertNotNull(response);
+//
+// }
+
@Test
@Ignore //FIXME we don't wish to mock CompositeNode as result
public void postOperationsStatusCodes() throws IOException {
}
@Test
+ @Ignore //jenkins has problem with JerseyTest - we expecting problems with singletons ControllerContext as schemaContext holder
public void postConfigStatusCodes() throws UnsupportedEncodingException {
setSchemaControllerContext(schemaContextYangsIetf);
final String uri = "/config/ietf-interfaces:interfaces";
}
@Test
+ @Ignore //jenkins has problem with JerseyTest - we expecting problems with singletons ControllerContext as schemaContext holder
public void createConfigurationDataTest() throws UnsupportedEncodingException, ParseException {
initMocking();
final RpcResult<TransactionStatus> rpcResult = new DummyRpcResult.Builder<TransactionStatus>().result(
public void createConfigurationDataNullTest() throws UnsupportedEncodingException {
initMocking();
- when(brokerFacade.commitConfigurationDataPost(any(YangInstanceIdentifier.class), any(NormalizedNode.class)))
- .thenReturn(null);
+ when(brokerFacade.commitConfigurationDataPost(any(YangInstanceIdentifier.class),any(NormalizedNode.class)))
+ .thenReturn(Futures.<Void, TransactionCommitFailedException>immediateCheckedFuture(null));
//FIXME : find who is set schemaContext
// final String URI_1 = "/config";
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+@Ignore
public class RestPutOperationTest extends JerseyTest {
private static String xmlData;
}
@Test
+ @Ignore // jenkins has problem with JerseyTest - we expecting problems with singletons ControllerContext as schemaContext holder
public void testRpcResultCommitedToStatusCodesWithMountPoint() throws UnsupportedEncodingException,
FileNotFoundException, URISyntaxException {
}
@Test
+ @Ignore // jenkins has problem with JerseyTest - we expecting problems with singletons ControllerContext as schemaContext holder
public void putWithTransactionCommitFailedException() throws UnsupportedEncodingException {
final String uri = "/config/ietf-interfaces:interfaces/interface/eth0";
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.io.FileNotFoundException;
import java.util.Set;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
}
@Test
+ @Ignore //jenkins has problem with JerseyTest - we expecting problems with singletons ControllerContext as schemaContext holder
public void testToInstanceIdentifierChoice() throws FileNotFoundException {
final InstanceIdentifierContext instanceIdentifier = controllerContext
.toInstanceIdentifier("simple-nodes:food/nonalcoholic");
}
}
+ rpc no-payload-rpc-test {
+ output {
+ container cont-output {
+ }
+ }
+ }
+
rpc rpc-test {
input {
container cont {
--- /dev/null
+{
+ "instance-identifier-module:cont1": {
+ "augment-module-leaf-list:lf11" : "/instance-identifier-module:cont/instance-identifier-module:cont1/augment-module-leaf-list:lflst11[.=\"lflst11_1\"]"
+ }
+}
\ No newline at end of file
}
rpc rpc-test {
- input {
- container cont {
- leaf lf {
- type string;
- }
- }
- }
+ input {
+ container cont {
+ leaf lf {
+ type string;
+ }
+ }
+ }
+ output {
+ container cont-out {
+ leaf lf-out {
+ type string;
+ }
+ }
+ }
}
rpc rpc-noop {
--- /dev/null
+{
+ "invoke-rpc-module:input" : {
+ "cont" : {
+ "lf" : "lf-test"
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "invoke-rpc-module:output" : {
+ "cont-out" : {
+ "lf-out" : "lf-test"
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<output xmlns="invoke:rpc:module">
+ <cont-out>
+ <lf-out>lf-test</lf-out>
+ </cont-out>
+</output>
\ No newline at end of file
import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* This class gathers all yang defined {@link Module}s and generates Swagger compliant documentation.
*/
public class ApiDocGenerator extends BaseYangSwaggerGenerator {
- private static Logger _logger = LoggerFactory.getLogger(ApiDocGenerator.class);
-
private static final ApiDocGenerator INSTANCE = new ApiDocGenerator();
private SchemaService schemaService;
public class BaseYangSwaggerGenerator {
- private static Logger _logger = LoggerFactory.getLogger(BaseYangSwaggerGenerator.class);
+ private static final Logger LOG = LoggerFactory.getLogger(BaseYangSwaggerGenerator.class);
protected static final String API_VERSION = "1.0.0";
protected static final String SWAGGER_VERSION = "1.2";
List<Resource> resources = new ArrayList<>(modules.size());
- _logger.info("Modules found [{}]", modules.size());
+ LOG.info("Modules found [{}]", modules.size());
for (Module module : modules) {
String revisionString = SIMPLE_DATE_FORMAT.format(module.getRevision());
Resource resource = new Resource();
- _logger.debug("Working on [{},{}]...", module.getName(), revisionString);
+ LOG.debug("Working on [{},{}]...", module.getName(), revisionString);
ApiDeclaration doc = getApiDeclaration(module.getName(), revisionString, uriInfo, schemaContext, context);
if (doc != null) {
resource.setPath(generatePath(uriInfo, module.getName(), revisionString));
resources.add(resource);
} else {
- _logger.debug("Could not generate doc for {},{}", module.getName(), revisionString);
+ LOG.debug("Could not generate doc for {},{}", module.getName(), revisionString);
}
}
List<Api> apis = new ArrayList<Api>();
Collection<DataSchemaNode> dataSchemaNodes = m.getChildNodes();
- _logger.debug("child nodes size [{}]", dataSchemaNodes.size());
+ LOG.debug("child nodes size [{}]", dataSchemaNodes.size());
for (DataSchemaNode node : dataSchemaNodes) {
if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) {
- _logger.debug("Is Configuration node [{}] [{}]", node.isConfiguration(), node.getQName().getLocalName());
+ LOG.debug("Is Configuration node [{}] [{}]", node.isConfiguration(), node.getQName().getLocalName());
List<Parameter> pathParams = new ArrayList<Parameter>();
String resourcePath = getDataStorePath("/config/", context);
addRpcs(rpcDefinition, apis, resourcePath, schemaContext);
}
- _logger.debug("Number of APIs found [{}]", apis.size());
+ LOG.debug("Number of APIs found [{}]", apis.size());
if (!apis.isEmpty()) {
doc.setApis(apis);
try {
models = jsonConverter.convertToJsonSchema(m, schemaContext);
doc.setModels(models);
- if (_logger.isDebugEnabled()) {
- _logger.debug(mapper.writeValueAsString(doc));
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(mapper.writeValueAsString(doc));
}
} catch (IOException | JSONException e) {
e.printStackTrace();
List<Parameter> pathParams = new ArrayList<Parameter>(parentPathParams);
String resourcePath = parentPath + createPath(node, pathParams, schemaContext) + "/";
- _logger.debug("Adding path: [{}]", resourcePath);
+ LOG.debug("Adding path: [{}]", resourcePath);
api.setPath(resourcePath);
Iterable<DataSchemaNode> childSchemaNodes = Collections.<DataSchemaNode> emptySet();
import static org.opendaylight.controller.sal.rest.doc.impl.BaseYangSwaggerGenerator.MODULE_NAME_SUFFIX;
import static org.opendaylight.controller.sal.rest.doc.model.builder.OperationBuilder.Post.METHOD_NAME;
import static org.opendaylight.controller.sal.rest.doc.util.RestDocgenUtil.resolveNodesName;
-
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import javax.annotation.concurrent.NotThreadSafe;
import org.apache.commons.lang3.BooleanUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
/**
* Generates JSON Schema for data defined in Yang
*/
+@NotThreadSafe
public class ModelGenerator {
- private static Logger _logger = LoggerFactory.getLogger(ModelGenerator.class);
+ private static final Logger LOG = LoggerFactory.getLogger(ModelGenerator.class);
private static final String BASE_64 = "base64";
private static final String BINARY_ENCODING_KEY = "binaryEncoding";
private static final String ID_KEY = "id";
private static final String SUB_TYPES_KEY = "subTypes";
- private static final Map<Class<? extends TypeDefinition<?>>, String> YANG_TYPE_TO_JSON_TYPE_MAPPING;
+ private static final Map<Class<?>, String> YANG_TYPE_TO_JSON_TYPE_MAPPING;
static {
- Map<Class<? extends TypeDefinition<?>>, String> tempMap1 = new HashMap<Class<? extends TypeDefinition<?>>, String>(
- 10);
- tempMap1.put(StringType.class, STRING);
- tempMap1.put(BooleanType.class, BOOLEAN);
- tempMap1.put(Int8.class, INTEGER);
- tempMap1.put(Int16.class, INTEGER);
- tempMap1.put(Int32.class, INTEGER);
- tempMap1.put(Int64.class, INTEGER);
- tempMap1.put(Uint16.class, INTEGER);
- tempMap1.put(Uint32.class, INTEGER);
- tempMap1.put(Uint64.class, INTEGER);
- tempMap1.put(Uint8.class, INTEGER);
- tempMap1.put(Decimal64.class, NUMBER);
- tempMap1.put(EnumerationType.class, ENUM);
+ final Builder<Class<?>, String> b = ImmutableMap.builder();
+
+ b.put(StringType.class, STRING);
+ b.put(BooleanType.class, BOOLEAN);
+ b.put(Int8.class, INTEGER);
+ b.put(Int16.class, INTEGER);
+ b.put(Int32.class, INTEGER);
+ b.put(Int64.class, INTEGER);
+ b.put(Uint16.class, INTEGER);
+ b.put(Uint32.class, INTEGER);
+ b.put(Uint64.class, INTEGER);
+ b.put(Uint8.class, INTEGER);
+ b.put(Decimal64.class, NUMBER);
+ b.put(EnumerationType.class, ENUM);
// TODO: Binary type
- YANG_TYPE_TO_JSON_TYPE_MAPPING = Collections.unmodifiableMap(tempMap1);
+ YANG_TYPE_TO_JSON_TYPE_MAPPING = b.build();
}
private Module topLevelModule;
public ModelGenerator() {
}
- public JSONObject convertToJsonSchema(Module module, SchemaContext schemaContext) throws IOException, JSONException {
+ public JSONObject convertToJsonSchema(final Module module, final SchemaContext schemaContext) throws IOException, JSONException {
JSONObject models = new JSONObject();
topLevelModule = module;
processModules(module, models);
return models;
}
- private void processModules(Module module, JSONObject models) throws JSONException {
+ private void processModules(final Module module, final JSONObject models) throws JSONException {
createConcreteModelForPost(models, module.getName()+MODULE_NAME_SUFFIX, createPropertiesForPost(module));
}
- private void processContainersAndLists(Module module, JSONObject models, SchemaContext schemaContext)
+ private void processContainersAndLists(final Module module, final JSONObject models, final SchemaContext schemaContext)
throws IOException, JSONException {
String moduleName = module.getName();
* @throws JSONException
* @throws IOException
*/
- private void processRPCs(Module module, JSONObject models, SchemaContext schemaContext) throws JSONException,
+ private void processRPCs(final Module module, final JSONObject models, final SchemaContext schemaContext) throws JSONException,
IOException {
Set<RpcDefinition> rpcs = module.getRpcs();
* The JSONObject in which the parsed identity will be put as a 'model' obj
* @throws JSONException
*/
- private void processIdentities(Module module, JSONObject models) throws JSONException {
+ private void processIdentities(final Module module, final JSONObject models) throws JSONException {
String moduleName = module.getName();
Set<IdentitySchemaNode> idNodes = module.getIdentities();
- _logger.debug("Processing Identities for module {} . Found {} identity statements", moduleName, idNodes.size());
+ LOG.debug("Processing Identities for module {} . Found {} identity statements", moduleName, idNodes.size());
for (IdentitySchemaNode idNode : idNodes) {
JSONObject identityObj = new JSONObject();
String identityName = idNode.getQName().getLocalName();
- _logger.debug("Processing Identity: {}", identityName);
+ LOG.debug("Processing Identity: {}", identityName);
identityObj.put(ID_KEY, identityName);
identityObj.put(DESCRIPTION_KEY, idNode.getDescription());
* @throws JSONException
* @throws IOException
*/
- private JSONObject processDataNodeContainer(DataNodeContainer dataNode, String moduleName, JSONObject models,
- SchemaContext schemaContext) throws JSONException, IOException {
+ private JSONObject processDataNodeContainer(final DataNodeContainer dataNode, final String moduleName, final JSONObject models,
+ final SchemaContext schemaContext) throws JSONException, IOException {
return processDataNodeContainer(dataNode, moduleName, models, (Boolean) null, schemaContext);
}
- private JSONObject processDataNodeContainer(DataNodeContainer dataNode, String moduleName, JSONObject models,
- Boolean isConfig, SchemaContext schemaContext) throws JSONException, IOException {
+ private JSONObject processDataNodeContainer(final DataNodeContainer dataNode, final String moduleName, final JSONObject models,
+ final Boolean isConfig, final SchemaContext schemaContext) throws JSONException, IOException {
if (dataNode instanceof ListSchemaNode || dataNode instanceof ContainerSchemaNode) {
Preconditions.checkArgument(dataNode instanceof SchemaNode, "Data node should be also schema node");
Iterable<DataSchemaNode> containerChildren = dataNode.getChildNodes();
return properties;
}
- private JSONObject processChildren(Iterable<DataSchemaNode> nodes, QName parentQName, String moduleName,
- JSONObject models, SchemaContext schemaContext) throws JSONException, IOException {
+ private JSONObject processChildren(final Iterable<DataSchemaNode> nodes, final QName parentQName, final String moduleName,
+ final JSONObject models, final SchemaContext schemaContext) throws JSONException, IOException {
return processChildren(nodes, parentQName, moduleName, models, null, schemaContext);
}
* @throws JSONException
* @throws IOException
*/
- private JSONObject processChildren(Iterable<DataSchemaNode> nodes, QName parentQName, String moduleName,
- JSONObject models, Boolean isConfig, SchemaContext schemaContext) throws JSONException, IOException {
+ private JSONObject processChildren(final Iterable<DataSchemaNode> nodes, final QName parentQName, final String moduleName,
+ final JSONObject models, final Boolean isConfig, final SchemaContext schemaContext) throws JSONException, IOException {
JSONObject properties = new JSONObject();
} else if (node instanceof LeafListSchemaNode) {
property = processLeafListNode((LeafListSchemaNode) node);
- } else if (node instanceof ChoiceNode) {
- property = processChoiceNode((ChoiceNode) node, moduleName, models, schemaContext);
+ } else if (node instanceof ChoiceSchemaNode) {
+ property = processChoiceNode((ChoiceSchemaNode) node, moduleName, models, schemaContext);
} else if (node instanceof AnyXmlSchemaNode) {
property = processAnyXMLNode((AnyXmlSchemaNode) node);
* @param listNode
* @throws JSONException
*/
- private JSONObject processLeafListNode(LeafListSchemaNode listNode) throws JSONException {
+ private JSONObject processLeafListNode(final LeafListSchemaNode listNode) throws JSONException {
JSONObject props = new JSONObject();
props.put(TYPE_KEY, ARRAY_TYPE);
* @throws JSONException
* @throws IOException
*/
- private JSONObject processChoiceNode(ChoiceNode choiceNode, String moduleName, JSONObject models,
- SchemaContext schemaContext) throws JSONException, IOException {
+ private JSONObject processChoiceNode(final ChoiceSchemaNode choiceNode, final String moduleName, final JSONObject models,
+ final SchemaContext schemaContext) throws JSONException, IOException {
Set<ChoiceCaseNode> cases = choiceNode.getCases();
* @param props
* @throws JSONException
*/
- private void processConstraints(ConstraintDefinition constraints, JSONObject props) throws JSONException {
+ private void processConstraints(final ConstraintDefinition constraints, final JSONObject props) throws JSONException {
boolean isMandatory = constraints.isMandatory();
props.put(REQUIRED_KEY, isMandatory);
* @return
* @throws JSONException
*/
- private JSONObject processLeafNode(LeafSchemaNode leafNode) throws JSONException {
+ private JSONObject processLeafNode(final LeafSchemaNode leafNode) throws JSONException {
JSONObject property = new JSONObject();
String leafDescription = leafNode.getDescription();
* @return
* @throws JSONException
*/
- private JSONObject processAnyXMLNode(AnyXmlSchemaNode leafNode) throws JSONException {
+ private JSONObject processAnyXMLNode(final AnyXmlSchemaNode leafNode) throws JSONException {
JSONObject property = new JSONObject();
String leafDescription = leafNode.getDescription();
* @param property
* @throws JSONException
*/
- private void processTypeDef(TypeDefinition<?> leafTypeDef, JSONObject property) throws JSONException {
+ private void processTypeDef(final TypeDefinition<?> leafTypeDef, final JSONObject property) throws JSONException {
if (leafTypeDef instanceof ExtendedType) {
processExtendedType(leafTypeDef, property);
* @param property
* @throws JSONException
*/
- private void processExtendedType(TypeDefinition<?> leafTypeDef, JSONObject property) throws JSONException {
+ private void processExtendedType(final TypeDefinition<?> leafTypeDef, final JSONObject property) throws JSONException {
Object leafBaseType = leafTypeDef.getBaseType();
if (leafBaseType instanceof ExtendedType) {
// recursively process an extended type until we hit a base type
/*
*
*/
- private void processBinaryType(BinaryTypeDefinition binaryType, JSONObject property) throws JSONException {
+ private void processBinaryType(final BinaryTypeDefinition binaryType, final JSONObject property) throws JSONException {
property.put(TYPE_KEY, STRING);
JSONObject media = new JSONObject();
media.put(BINARY_ENCODING_KEY, BASE_64);
* @param property
* @throws JSONException
*/
- private void processEnumType(EnumerationType enumLeafType, JSONObject property) throws JSONException {
+ private void processEnumType(final EnumerationType enumLeafType, final JSONObject property) throws JSONException {
List<EnumPair> enumPairs = enumLeafType.getValues();
List<String> enumNames = new ArrayList<String>();
for (EnumPair enumPair : enumPairs) {
* @param property
* @throws JSONException
*/
- private void processBitsType(BitsTypeDefinition bitsType, JSONObject property) throws JSONException {
+ private void processBitsType(final BitsTypeDefinition bitsType, final JSONObject property) throws JSONException {
property.put(TYPE_KEY, ARRAY_TYPE);
property.put(MIN_ITEMS, 0);
property.put(UNIQUE_ITEMS_KEY, true);
* @param property
* @throws JSONException
*/
- private void processUnionType(UnionTypeDefinition unionType, JSONObject property) throws JSONException {
+ private void processUnionType(final UnionTypeDefinition unionType, final JSONObject property) throws JSONException {
StringBuilder type = new StringBuilder();
for (TypeDefinition<?> typeDef : unionType.getTypes()) {
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-/**
- *
- */
public final class OperationBuilder {
public static final String OPERATIONAL = "(operational)";
public static final String CONFIG = "(config)";
- /**
- *
- */
public static class Get {
protected Operation spec;
protected DataSchemaNode schemaNode;
- private final String METHOD_NAME = "GET";
+ private static final String METHOD_NAME = "GET";
public Get(DataSchemaNode node, boolean isConfig) {
this.schemaNode = node;
}
}
- /**
- *
- */
public static class Put {
protected Operation spec;
protected String nodeName;
- private final String METHOD_NAME = "PUT";
+ private static final String METHOD_NAME = "PUT";
public Put(String nodeName, final String description) {
this.nodeName = nodeName;
}
}
- /**
- *
- */
public static final class Post extends Put {
public static final String METHOD_NAME = "POST";
}
}
- /**
- *
- */
public static final class Delete extends Get {
- private final String METHOD_NAME = "DELETE";
+ private static final String METHOD_NAME = "DELETE";
public Delete(DataSchemaNode node) {
super(node, false);
package org.opendaylight.controller.netconf.confignetconfconnector.osgi;
-import com.google.common.base.Optional;
import java.lang.management.ManagementFactory;
-import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.management.MBeanServer;
import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
import org.opendaylight.controller.netconf.api.Capability;
import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
-import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.controller.netconf.util.capability.BasicCapability;
+import org.opendaylight.controller.netconf.util.capability.YangModuleCapability;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Set<Module> modules = yangStoreSnapshot.getModules();
for (Module module : modules) {
- capabilities.add(new YangStoreCapability(module, yangStoreSnapshot.getModuleSource(module)));
+ capabilities.add(new YangModuleCapability(module, yangStoreSnapshot.getModuleSource(module)));
}
return capabilities;
}
- private static class BasicCapability implements Capability {
-
- private final String capability;
-
- private BasicCapability(final String capability) {
- this.capability = capability;
- }
-
- @Override
- public String getCapabilityUri() {
- return capability;
- }
-
- @Override
- public Optional<String> getModuleNamespace() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getModuleName() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getRevision() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getCapabilitySchema() {
- return Optional.absent();
- }
-
- @Override
- public Collection<String> getLocation() {
- return Collections.emptyList();
- }
-
- @Override
- public String toString() {
- return capability;
- }
- }
-
- static final class YangStoreCapability extends BasicCapability {
-
- private final String content;
- private final String revision;
- private final String moduleName;
- private final String moduleNamespace;
-
- public YangStoreCapability(final Module module, final String moduleContent) {
- super(toCapabilityURI(module));
- this.content = moduleContent;
- this.moduleName = module.getName();
- this.moduleNamespace = module.getNamespace().toString();
- this.revision = Util.writeDate(module.getRevision());
- }
-
- @Override
- public Optional<String> getCapabilitySchema() {
- return Optional.of(content);
- }
-
- private static String toCapabilityURI(final Module module) {
- return String.valueOf(module.getNamespace()) + "?module="
- + module.getName() + "&revision=" + Util.writeDate(module.getRevision());
- }
-
- @Override
- public Optional<String> getModuleName() {
- return Optional.of(moduleName);
- }
-
- @Override
- public Optional<String> getModuleNamespace() {
- return Optional.of(moduleNamespace);
- }
-
- @Override
- public Optional<String> getRevision() {
- return Optional.of(revision);
- }
- }
}
import org.opendaylight.controller.netconf.notifications.BaseNetconfNotificationListener;
import org.opendaylight.controller.netconf.notifications.BaseNotificationPublisherRegistration;
import org.opendaylight.controller.netconf.notifications.NetconfNotificationCollector;
+import org.opendaylight.controller.netconf.util.capability.YangModuleCapability;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChangeBuilder;
private static final Function<Module, Capability> MODULE_TO_CAPABILITY = new Function<Module, Capability>() {
@Override
public Capability apply(final Module module) {
- return new NetconfOperationServiceFactoryImpl.YangStoreCapability(module, module.getSource());
+ return new YangModuleCapability(module, module.getSource());
}
};
private static final Function<Module, Uri> MODULE_TO_URI = new Function<Module, Uri>() {
@Override
public Uri apply(final Module input) {
- return new Uri(new NetconfOperationServiceFactoryImpl.YangStoreCapability(input, input.getSource()).getCapabilityUri());
+ return new Uri(new YangModuleCapability(input, input.getSource()).getCapabilityUri());
}
};
<type>test-jar</type>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <scope>test</scope>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-directory-xml-adapter</artifactId>
package org.opendaylight.controller.netconf.persist.impl;
import static org.junit.Assert.assertEquals;
-
+import com.google.common.base.Charsets;
import com.google.common.collect.Sets;
+import com.google.common.io.Resources;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
-import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Element;
}
private Set<String> readLines(String fileName) throws IOException {
- return new HashSet<>(IOUtils.readLines(getClass().getResourceAsStream(fileName)));
+ return new HashSet<>(Resources.readLines(getClass().getResource(fileName), Charsets.UTF_8));
}
private String readToString(String fileName) throws IOException {
- return IOUtils.toString(getClass().getResourceAsStream(fileName));
+ return Resources.toString(getClass().getResource(fileName), Charsets.UTF_8);
}
}
--- /dev/null
+/*
+ * Copyright (c) 2015 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.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema.Location;
+
+/**
+ * The purpose of generated class in src/main/java for Union types is to create new instances of unions from a string representation.
+ * In some cases it is very difficult to automate it since there can be unions such as (uint32 - uint16), or (string - uint32).
+ *
+ * The reason behind putting it under src/main/java is:
+ * This class is generated in form of a stub and needs to be finished by the user. This class is generated only once to prevent
+ * loss of user code.
+ */
+public class SchemaLocationBuilder {
+
+ public static Location getDefaultInstance(final String defaultValue) {
+ throw new java.lang.UnsupportedOperationException("Not yet implemented");
+ }
+
+}
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-core-api</artifactId>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-util</artifactId>
package org.opendaylight.controller.netconf.mdsal.connector;
-import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
-import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.netconf.api.Capability;
import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.controller.netconf.util.capability.BasicCapability;
+import org.opendaylight.controller.netconf.util.capability.YangModuleCapability;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
-import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.slf4j.Logger;
final Set<Module> modules = currentContext.getModules();
for (final Module module : modules) {
if(currentContext.getModuleSource(module).isPresent()) {
- capabilities.add(new YangStoreCapability(module, currentContext.getModuleSource(module).get()));
+ capabilities.add(new YangModuleCapability(module, currentContext.getModuleSource(module).get()));
} else {
LOG.warn("Missing source for module {}. This module will not be available from netconf server",
module);
return currentSchemaContext.registerCapabilityListener(listener);
}
- private static class BasicCapability implements Capability {
-
- private final String capability;
-
- private BasicCapability(final String capability) {
- this.capability = capability;
- }
-
- @Override
- public String getCapabilityUri() {
- return capability;
- }
-
- @Override
- public Optional<String> getModuleNamespace() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getModuleName() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getRevision() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getCapabilitySchema() {
- return Optional.absent();
- }
-
- @Override
- public Collection<String> getLocation() {
- return Collections.emptyList();
- }
-
- @Override
- public String toString() {
- return capability;
- }
- }
-
- private static final class YangStoreCapability extends BasicCapability {
-
- private final String content;
- private final String revision;
- private final String moduleName;
- private final String moduleNamespace;
-
- public YangStoreCapability(final Module module, final String moduleContent) {
- super(toCapabilityURI(module));
- this.content = moduleContent;
- this.moduleName = module.getName();
- this.moduleNamespace = module.getNamespace().toString();
- this.revision = SimpleDateFormatUtil.getRevisionFormat().format(module.getRevision());
- }
-
- @Override
- public Optional<String> getCapabilitySchema() {
- return Optional.of(content);
- }
-
- private static String toCapabilityURI(final Module module) {
- return String.valueOf(module.getNamespace()) + "?module="
- + module.getName() + "&revision=" + SimpleDateFormatUtil.getRevisionFormat().format(module.getRevision());
- }
-
- @Override
- public Optional<String> getModuleName() {
- return Optional.of(moduleName);
- }
-
- @Override
- public Optional<String> getModuleNamespace() {
- return Optional.of(moduleNamespace);
- }
-
- @Override
- public Optional<String> getRevision() {
- return Optional.of(revision);
- }
- }
}
import org.custommonkey.xmlunit.XMLUnit;
import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.opendaylight.controller.cluster.datastore.ConcurrentDOMDataBroker;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
}
- @Ignore("Xml is not similar")
@Test
public void testMoreComplexEditConfigs() throws Exception {
}
}
- @Ignore("Xml is not similar")
@Test
public void testEditWithCreate() throws Exception {
verifyResponse(edit("messages/mapping/editConfig_create.xml"), RPC_REPLY_OK);
- verifyResponse(getConfigCandidate(), XmlFileLoader.xmlFileToDocument("messages/mapping/editConfig_merge_n1_control.xml"));
+ verifyResponse(getConfigCandidate(), XmlFileLoader.xmlFileToDocument("messages/mapping/editConfig_create_n1_control.xml"));
+
try {
edit("messages/mapping/editConfig_create.xml");
assertEmptyDatastore(getConfigRunning());
}
- private void verifyResponse(Document response, Document template) {
+ private void verifyResponse(Document response, Document template){
DetailedDiff dd = new DetailedDiff(new Diff(response, template));
dd.overrideElementQualifier(new RecursiveElementNameAndTextQualifier());
assertTrue(dd.similar());
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc-reply a="64" id="a" message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlnx="a:b:c:d">
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <mapping-nodes xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="create" xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mapping-node>
+ <id>node1-put</id>
+ <content>put content</content>
+ </mapping-node>
+ </mapping-nodes>
+ </data>
+</rpc-reply>
\ No newline at end of file
<rpc-reply a="64" id="a" message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlnx="a:b:c:d">
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mapping-nodes xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="replace" xmlns="urn:opendaylight:mdsal:mapping:test">
<mapping-node>
<id>new-node7</id>
<content>new node content</content>
package org.opendaylight.controller.config.yang.netconf.mdsal.monitoring;
-import com.google.common.base.Optional;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import java.util.Set;
import org.opendaylight.controller.netconf.api.Capability;
import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.monitoring.GetSchema;
-import org.opendaylight.controller.netconf.monitoring.MonitoringConstants;
public class NetconfMdsalMonitoringMapperModule extends org.opendaylight.controller.config.yang.netconf.mdsal.monitoring.AbstractNetconfMdsalMonitoringMapperModule {
public NetconfMdsalMonitoringMapperModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
private final NetconfOperationService operationService;
- private static final Set<Capability> CAPABILITIES = Sets.<Capability>newHashSet(new Capability() {
-
- @Override
- public String getCapabilityUri() {
- return MonitoringConstants.URI;
- }
-
- @Override
- public Optional<String> getModuleNamespace() {
- return Optional.of(MonitoringConstants.NAMESPACE);
- }
-
- @Override
- public Optional<String> getModuleName() {
- return Optional.of(MonitoringConstants.MODULE_NAME);
- }
-
- @Override
- public Optional<String> getRevision() {
- return Optional.of(MonitoringConstants.MODULE_REVISION);
- }
-
- @Override
- public Optional<String> getCapabilitySchema() {
- return Optional.absent();
- }
-
- @Override
- public Collection<String> getLocation() {
- return Collections.emptyList();
- }
- });
-
private static final AutoCloseable AUTO_CLOSEABLE = new AutoCloseable() {
@Override
public void close() throws Exception {
}
};
- private final List<CapabilityListener> listeners = new ArrayList<>();
-
public MdSalMonitoringMapperFactory(final NetconfOperationService operationService) {
this.operationService = operationService;
}
@Override
public Set<Capability> getCapabilities() {
- return CAPABILITIES;
+ // TODO
+ // No capabilities exposed to prevent clashes with schemas from mdsal-netconf-connector (it exposes all the schemas)
+ // If the schemas exposed by mdsal-netconf-connector are filtered, this class would expose monitoring related models
+ return Collections.emptySet();
}
@Override
public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
- listener.onCapabilitiesAdded(getCapabilities());
- listeners.add(listener);
return AUTO_CLOSEABLE;
}
@Override
- public void close() {
- for (final CapabilityListener listener : listeners) {
- listener.onCapabilitiesRemoved(getCapabilities());
- }
- }
+ public void close() {}
}
package org.opendaylight.controller.netconf.monitoring;
-import com.google.common.base.Optional;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import java.util.Set;
import org.opendaylight.controller.netconf.api.Capability;
import org.opendaylight.controller.netconf.api.monitoring.CapabilityListener;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
-/**
-* Created by mmarsale on 18.2.2015.
-*/
public class NetconfMonitoringOperationServiceFactory implements NetconfOperationServiceFactory, AutoCloseable {
private final NetconfMonitoringOperationService operationService;
- private static final Set<Capability> CAPABILITIES = Sets.<Capability>newHashSet(new Capability() {
-
- @Override
- public String getCapabilityUri() {
- return MonitoringConstants.URI;
- }
-
- @Override
- public Optional<String> getModuleNamespace() {
- return Optional.of(MonitoringConstants.NAMESPACE);
- }
-
- @Override
- public Optional<String> getModuleName() {
- return Optional.of(MonitoringConstants.MODULE_NAME);
- }
-
- @Override
- public Optional<String> getRevision() {
- return Optional.of(MonitoringConstants.MODULE_REVISION);
- }
-
- @Override
- public Optional<String> getCapabilitySchema() {
- return Optional.absent();
- }
-
- @Override
- public Collection<String> getLocation() {
- return Collections.emptyList();
- }
- });
-
private static final AutoCloseable AUTO_CLOSEABLE = new AutoCloseable() {
@Override
public void close() throws Exception {
}
};
- private final List<CapabilityListener> listeners = new ArrayList<>();
-
public NetconfMonitoringOperationServiceFactory(final NetconfMonitoringOperationService operationService) {
this.operationService = operationService;
}
@Override
public Set<Capability> getCapabilities() {
- return CAPABILITIES;
+ // TODO
+ // No capabilities exposed to prevent clashes with schemas from config-netconf-connector (it exposes all the schemas)
+ // If the schemas exposed by config-netconf-connector are filtered, this class would expose monitoring related models
+ return Collections.emptySet();
}
@Override
public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
- listener.onCapabilitiesAdded(getCapabilities());
- listeners.add(listener);
return AUTO_CLOSEABLE;
}
@Override
- public void close() {
- for (final CapabilityListener listener : listeners) {
- listener.onCapabilitiesRemoved(getCapabilities());
- }
- }
+ public void close() {}
}
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-parser-impl</artifactId>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-netconf-connector</artifactId>
- </dependency>
</dependencies>
<build>
import org.opendaylight.controller.netconf.cli.io.ConsoleContext;
import org.opendaylight.controller.netconf.cli.io.ConsoleIO;
import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
String defaultValue = null;
if (schemaNode instanceof LeafSchemaNode) {
defaultValue = ((LeafSchemaNode) schemaNode).getDefault();
- } else if (schemaNode instanceof ChoiceNode) {
- defaultValue = ((ChoiceNode) schemaNode).getDefaultCase();
+ } else if (schemaNode instanceof ChoiceSchemaNode) {
+ defaultValue = ((ChoiceSchemaNode) schemaNode).getDefaultCase();
}
return Optional.fromNullable(defaultValue);
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
public class EditContentReader extends ChoiceReader {
}
@Override
- public List<Node<?>> readWithContext(final ChoiceNode choiceNode) throws IOException, ReadingException {
+ public List<Node<?>> readWithContext(final ChoiceSchemaNode choiceNode) throws IOException, ReadingException {
Preconditions.checkState(choiceNode.getQName().equals(EDIT_CONTENT_QNAME), "Unexpected choice %s, expected %s", choiceNode, EDIT_CONTENT_QNAME);
final ChoiceCaseNode selectedCase = choiceNode.getCaseNodeByName(CONFIG_QNAME);
Preconditions.checkNotNull(selectedCase, "Unexpected choice %s, expected %s that contains %s", choiceNode, EDIT_CONTENT_QNAME, CONFIG_QNAME);
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ChoiceReader extends AbstractReader<ChoiceNode> {
+public class ChoiceReader extends AbstractReader<ChoiceSchemaNode> {
private static final Logger LOG = LoggerFactory.getLogger(ChoiceReader.class);
}
@Override
- public List<Node<?>> readWithContext(final ChoiceNode choiceNode) throws IOException, ReadingException {
+ public List<Node<?>> readWithContext(final ChoiceSchemaNode choiceNode) throws IOException, ReadingException {
final Map<String, ChoiceCaseNode> availableCases = collectAllCases(choiceNode);
console.formatLn("Select case for choice %s from: %s", choiceNode.getQName().getLocalName(),
formatSet(availableCases.keySet()));
return false;
}
- private Map<String, ChoiceCaseNode> collectAllCases(final ChoiceNode schemaNode) {
+ private Map<String, ChoiceCaseNode> collectAllCases(final ChoiceSchemaNode schemaNode) {
return Maps.uniqueIndex(schemaNode.getCases(), new Function<ChoiceCaseNode, String>() {
@Override
public String apply(final ChoiceCaseNode input) {
}
@Override
- protected ConsoleContext getContext(final ChoiceNode schemaNode) {
- return new BaseConsoleContext<ChoiceNode>(schemaNode) {
+ protected ConsoleContext getContext(final ChoiceSchemaNode schemaNode) {
+ return new BaseConsoleContext<ChoiceSchemaNode>(schemaNode) {
@Override
public List<Completer> getAdditionalCompleters() {
return Collections
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
getSchemaContext(), getReadConfigNode());
return new GenericListReader<>(console, entryReader, getSchemaContext(), getReadConfigNode())
.read((LeafListSchemaNode) schemaNode);
- } else if (schemaNode instanceof ChoiceNode) {
+ } else if (schemaNode instanceof ChoiceSchemaNode) {
return new ChoiceReader(console, argumentHandlerRegistry, getSchemaContext(), getReadConfigNode())
- .read((ChoiceNode) schemaNode);
+ .read((ChoiceSchemaNode) schemaNode);
} else if (schemaNode instanceof AnyXmlSchemaNode) {
return new AnyXmlReader(console, getSchemaContext(), getReadConfigNode())
.read((AnyXmlSchemaNode) schemaNode);
import org.opendaylight.controller.netconf.cli.writer.OutFormatter;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.ChoiceNodeBaseSerializer;
import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
final class ChoiceNodeCliSerializer extends ChoiceNodeBaseSerializer<String> {
private final NodeSerializerDispatcher<String> dispatcher;
}
@Override
- public Iterable<String> serialize(final ChoiceNode schema, final org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode node) {
+ public Iterable<String> serialize(final ChoiceSchemaNode schema, final ChoiceNode node) {
final StringBuilder output = new StringBuilder();
out.increaseIndent();
out.addStringWithIndent(output, "choice ");
return Collections.singletonList(output.toString());
}
- private String detectCase(final ChoiceNode schema, final org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode node) {
+ private String detectCase(final ChoiceSchemaNode schema, final ChoiceNode node) {
for (final DataContainerChild<? extends PathArgument, ?> caseChild : node.getValue()) {
final QName presentChildQName = caseChild.getNodeType();
for (final ChoiceCaseNode choiceCaseNode : schema.getCases()) {
import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
}
@Override
- public FromNormalizedNodeSerializer<String, ChoiceNode, org.opendaylight.yangtools.yang.model.api.ChoiceNode> getChoiceNodeSerializer() {
+ public FromNormalizedNodeSerializer<String, ChoiceNode, ChoiceSchemaNode> getChoiceNodeSerializer() {
return choiceSerializer;
}
throw new UnsupportedOperationException();
}
-}
\ No newline at end of file
+}
import org.opendaylight.yangtools.yang.data.impl.schema.transform.FromNormalizedNodeSerializerFactory;
import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
private Iterable<String> onChoiceNode(final Object childSchema,
final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataContainerChild) {
- checkSchemaCompatibility(childSchema, org.opendaylight.yangtools.yang.model.api.ChoiceNode.class,
- dataContainerChild);
+ checkSchemaCompatibility(childSchema, ChoiceSchemaNode.class, dataContainerChild);
return factory.getChoiceNodeSerializer().serialize(
- (org.opendaylight.yangtools.yang.model.api.ChoiceNode) childSchema, (ChoiceNode) dataContainerChild);
+ (ChoiceSchemaNode) childSchema, (ChoiceNode) dataContainerChild);
}
private Iterable<String> onListNode(final Object childSchema,
import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer.NodeSerializerDispatcher;
import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
return factoryParsing.getLeafSetNodeParser().parse(dataNodes, (LeafListSchemaNode) dataSchemaNode);
} else if (dataSchemaNode instanceof ListSchemaNode) {
return factoryParsing.getMapNodeParser().parse(dataNodes, (ListSchemaNode) dataSchemaNode);
- } else if (dataSchemaNode instanceof ChoiceNode) {
- return factoryParsing.getChoiceNodeParser().parse(dataNodes, (ChoiceNode) dataSchemaNode);
+ } else if (dataSchemaNode instanceof ChoiceSchemaNode) {
+ return factoryParsing.getChoiceNodeParser().parse(dataNodes, (ChoiceSchemaNode) dataSchemaNode);
} else if (dataSchemaNode instanceof AugmentationSchema) {
return factoryParsing.getAugmentationNodeParser().parse(dataNodes, (AugmentationSchema) dataSchemaNode);
}
<provider>/modules/module[type='threadpool-scheduled'][name='global-netconf-ssh-scheduled-executor']</provider>
</instance>
</service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:scheduled-threadpool</type>
+ <instance>
+ <name>global-netconf-ssh-scheduled-executor</name>
+ <provider>/modules/module[type='threadpool-scheduled'][name='global-netconf-ssh-scheduled-executor']</provider>
+ </instance>
+ </service>
</services>
</data>
<type>test-jar</type>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <scope>test</scope>
- </dependency>
<dependency>
<groupId>xmlunit</groupId>
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import com.google.common.io.ByteStreams;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
-import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
}
LOG.info(sb.toString());
- outToServer.write(IOUtils.toByteArray(clientHello));
+ outToServer.write(ByteStreams.toByteArray(clientHello));
outToServer.write("]]>]]>".getBytes());
outToServer.flush();
// Thread.sleep(100);
- outToServer.write(IOUtils.toByteArray(getConfig));
+ outToServer.write(ByteStreams.toByteArray(getConfig));
outToServer.write("]]>]]>".getBytes());
outToServer.flush();
Thread.sleep(100);
<artifactId>config-api</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>object-cache-guava</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>mockito-configuration</artifactId>
- </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>object-cache-guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ </dependency>
- <dependency>
+ <dependency>
<groupId>${project.groupId}</groupId>
<artifactId>config-manager</artifactId>
<scope>test</scope>
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
-
+import com.google.common.io.ByteStreams;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
-import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
assertNotNull(inputStream);
final byte[] content;
try {
- content = IOUtils.toByteArray(inputStream);
+ content = ByteStreams.toByteArray(inputStream);
} catch (IOException e) {
throw new IllegalStateException("Cannot read " + inputStream, e);
}
package org.opendaylight.controller.netconf.notifications.impl.osgi;
-import com.google.common.base.Optional;
import com.google.common.collect.Sets;
-import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Hashtable;
import org.opendaylight.controller.netconf.notifications.impl.NetconfNotificationManager;
import org.opendaylight.controller.netconf.notifications.impl.ops.CreateSubscription;
import org.opendaylight.controller.netconf.notifications.impl.ops.Get;
+import org.opendaylight.controller.netconf.util.capability.BasicCapability;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
final NetconfOperationServiceFactory netconfOperationServiceFactory = new NetconfOperationServiceFactory() {
- private final Set<Capability> capabilities = Collections.<Capability>singleton(new NotificationsCapability());
+ private final Set<Capability> capabilities = Collections.<Capability>singleton(new BasicCapability(NetconfNotification.NOTIFICATION_NAMESPACE));
@Override
public Set<Capability> getCapabilities() {
operationaServiceRegistration = null;
}
}
-
- private class NotificationsCapability implements Capability {
- @Override
- public String getCapabilityUri() {
- return NetconfNotification.NOTIFICATION_NAMESPACE;
- }
-
- @Override
- public Optional<String> getModuleNamespace() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getModuleName() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getRevision() {
- return Optional.absent();
- }
-
- @Override
- public Optional<String> getCapabilitySchema() {
- return Optional.absent();
- }
-
- @Override
- public Collection<String> getLocation() {
- return Collections.emptyList();
- }
- }
}
package org.opendaylight.controller.netconf.notifications.impl.ops;
-import static org.junit.Assert.assertTrue;
-
import com.google.common.collect.Lists;
import java.io.IOException;
-import org.custommonkey.xmlunit.Diff;
-import org.custommonkey.xmlunit.XMLUnit;
import org.junit.Test;
-import org.opendaylight.controller.netconf.notifications.impl.ops.Get;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.StreamNameType;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.Streams;
final Document response = getBlankResponse();
Get.serializeStreamsSubtree(response, streams);
- final Diff diff = XMLUnit.compareXML(XmlUtil.toString(response),
+ NotificationsTransformUtilTest.compareXml(XmlUtil.toString(response),
"<rpc-reply message-id=\"101\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
- "<data>\n" +
- "<netconf xmlns=\"urn:ietf:params:xml:ns:netmod:notification\">\n" +
- "<streams>\n" +
- "<stream>\n" +
- "<name>base</name>\n" +
- "<description>description</description>\n" +
- "<replaySupport>false</replaySupport>\n" +
- "</stream>\n" +
- "</streams>\n" +
- "</netconf>\n" +
- "</data>\n" +
- "</rpc-reply>\n");
-
- assertTrue(diff.toString(), diff.identical());
+ "<data>\n" +
+ "<netconf xmlns=\"urn:ietf:params:xml:ns:netmod:notification\">\n" +
+ "<streams>\n" +
+ "<stream>\n" +
+ "<name>base</name>\n" +
+ "<description>description</description>\n" +
+ "<replaySupport>false</replaySupport>\n" +
+ "</stream>\n" +
+ "</streams>\n" +
+ "</netconf>\n" +
+ "</data>\n" +
+ "</rpc-reply>\n");
}
private Document getBlankResponse() throws IOException, SAXException {
import static org.junit.Assert.assertTrue;
import com.google.common.collect.Lists;
+import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
+import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
+import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier;
import org.junit.Test;
import org.opendaylight.controller.netconf.notifications.NetconfNotification;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChangeBuilder;
+import org.xml.sax.SAXException;
public class NotificationsTransformUtilTest {
private static final Date DATE = new Date();
private static final String innerNotification = "<netconf-capability-change xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-notifications\">" +
- "<deleted-capability>uri3</deleted-capability>" +
"<deleted-capability>uri4</deleted-capability>" +
+ "<deleted-capability>uri3</deleted-capability>" +
"<added-capability>uri1</added-capability>" +
"</netconf-capability-change>";
final NetconfCapabilityChangeBuilder netconfCapabilityChangeBuilder = new NetconfCapabilityChangeBuilder();
netconfCapabilityChangeBuilder.setAddedCapability(Lists.newArrayList(new Uri("uri1"), new Uri("uri1")));
- netconfCapabilityChangeBuilder.setDeletedCapability(Lists.newArrayList(new Uri("uri3"), new Uri("uri4")));
+ netconfCapabilityChangeBuilder.setDeletedCapability(Lists.newArrayList(new Uri("uri4"), new Uri("uri3")));
final NetconfCapabilityChange capabilityChange = netconfCapabilityChangeBuilder.build();
final NetconfNotification transform = NotificationsTransformUtil.transform(capabilityChange, DATE);
final String serialized = XmlUtil.toString(transform.getDocument());
+ compareXml(expectedNotification, serialized);
+ }
+
+ static void compareXml(final String expected, final String actual) throws SAXException, IOException {
XMLUnit.setIgnoreWhitespace(true);
- final Diff diff = XMLUnit.compareXML(expectedNotification, serialized);
- assertTrue(diff.toString(), diff.similar());
+ final Diff diff = new Diff(expected, actual);
+ final DetailedDiff detailedDiff = new DetailedDiff(diff);
+ detailedDiff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier());
+ assertTrue(detailedDiff.toString(), detailedDiff.similar());
}
@Test
final NetconfNotification netconfNotification = new NetconfNotification(XmlUtil.readXmlToDocument(innerNotification), DATE);
XMLUnit.setIgnoreWhitespace(true);
- final Diff diff = XMLUnit.compareXML(expectedNotification, netconfNotification.toString());
- assertTrue(diff.toString(), diff.similar());
+ compareXml(expectedNotification, netconfNotification.toString());
}
}
\ No newline at end of file
<groupId>${project.groupId}</groupId>
<artifactId>netconf-util</artifactId>
</dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
--- /dev/null
+/*
+ * Copyright (c) 2015 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.netconf.test.tool;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import java.util.Collections;
+import java.util.Set;
+import javax.annotation.Nullable;
+import org.opendaylight.controller.netconf.api.Capability;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Yang;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Capabilities;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.CapabilitiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SchemasBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SessionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema.Location;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema.Location.Enumeration;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.SchemaBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.SchemaKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+
+public class DummyMonitoringService implements NetconfMonitoringService {
+
+ private static final Sessions EMPTY_SESSIONS = new SessionsBuilder().setSession(Collections.<Session>emptyList()).build();
+ private static final Function<Capability, Uri> CAPABILITY_URI_FUNCTION = new Function<Capability, Uri>() {
+ @Nullable
+ @Override
+ public Uri apply(Capability capability) {
+ return new Uri(capability.getCapabilityUri());
+ }
+ };
+
+ private static final Function<Capability, Schema> CAPABILITY_SCHEMA_FUNCTION = new Function<Capability, Schema>() {
+ @Nullable
+ @Override
+ public Schema apply(@Nullable Capability capability) {
+ return new SchemaBuilder()
+ .setIdentifier(capability.getModuleName().get())
+ .setNamespace(new Uri(capability.getModuleNamespace().get()))
+ .setFormat(Yang.class)
+ .setVersion(capability.getRevision().get())
+ .setLocation(Collections.singletonList(new Location(Enumeration.NETCONF)))
+ .setKey(new SchemaKey(Yang.class, capability.getModuleName().get(), capability.getRevision().get())).build();
+ }
+ };
+
+ private final Capabilities capabilities;
+ private final ArrayListMultimap<String, Capability> capabilityMultiMap;
+ private final Schemas schemas;
+
+ public DummyMonitoringService(Set<Capability> capabilities) {
+
+ this.capabilities = new CapabilitiesBuilder().setCapability(
+ Lists.newArrayList(Collections2.transform(capabilities, CAPABILITY_URI_FUNCTION))).build();
+
+ this.capabilityMultiMap = ArrayListMultimap.create();
+ for (Capability cap : capabilities) {
+ capabilityMultiMap.put(cap.getModuleName().get(), cap);
+ }
+
+ this.schemas = new SchemasBuilder().setSchema(Lists.newArrayList(Collections2.transform(capabilities, CAPABILITY_SCHEMA_FUNCTION))).build();
+ }
+
+ @Override
+ public Sessions getSessions() {
+ return EMPTY_SESSIONS;
+ }
+
+ @Override
+ public Schemas getSchemas() {
+ return schemas;
+ }
+
+ @Override
+ public String getSchemaForCapability(String moduleName, Optional<String> revision) {
+
+ for (Capability capability : capabilityMultiMap.get(moduleName)) {
+ if (capability.getRevision().get().equals(revision.get())) {
+ return capability.getCapabilitySchema().get();
+ }
+ }
+ throw new IllegalArgumentException("Module with name: " + moduleName + " and revision: " + revision + " does not exist");
+ }
+
+ @Override
+ public Capabilities getCapabilities() {
+ return capabilities;
+ }
+
+ @Override
+ public AutoCloseable registerListener(MonitoringListener listener) {
+ return null;
+ }
+
+ @Override
+ public void onCapabilitiesAdded(Set<Capability> addedCaps) {
+
+ }
+
+ @Override
+ public void onCapabilitiesRemoved(Set<Capability> removedCaps) {
+
+ }
+
+ @Override
+ public void onSessionUp(NetconfManagementSession session) {
+
+ }
+
+ @Override
+ public void onSessionDown(NetconfManagementSession session) {
+
+ }
+}
package org.opendaylight.controller.netconf.test.tool;
import com.google.common.base.Optional;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import org.opendaylight.controller.netconf.api.Capability;
-import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
-import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
/**
* Can be passed instead of ModuleBuilderCapability when building capabilities
* in NetconfDeviceSimulator when testing various schema resolution related exceptions.
*/
-public class FakeModuleBuilderCapability implements Capability{
- private static final Date NO_REVISION = new Date(0);
- private final ModuleBuilder input;
- private final Optional<String> content;
+public class FakeModuleBuilderCapability extends ModuleBuilderCapability {
public FakeModuleBuilderCapability(final ModuleBuilder input, final String inputStream) {
- this.input = input;
- this.content = Optional.of(inputStream);
- }
-
- @Override
- public String getCapabilityUri() {
- // FIXME capabilities in Netconf-impl need to check for NO REVISION
- final String withoutRevision = getModuleNamespace().get() + "?module=" + getModuleName().get();
- return hasRevision() ? withoutRevision + "&revision=" + Util.writeDate(input.getRevision()) : withoutRevision;
- }
-
- @Override
- public Optional<String> getModuleNamespace() {
- return Optional.of(input.getNamespace().toString());
- }
-
- @Override
- public Optional<String> getModuleName() {
- return Optional.of(input.getName());
- }
-
- @Override
- public Optional<String> getRevision() {
- return Optional.of(hasRevision() ? QName.formattedRevision(input.getRevision()) : "");
- }
-
- private boolean hasRevision() {
- return !input.getRevision().equals(NO_REVISION);
+ super(input, inputStream);
}
/**
public Optional<String> getCapabilitySchema() {
return Optional.absent();
}
-
- @Override
- public List<String> getLocation() {
- return Collections.emptyList();
- }
}
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
-final class ModuleBuilderCapability implements Capability {
+class ModuleBuilderCapability implements Capability {
private static final Date NO_REVISION = new Date(0);
+ private static final List<String> NETCONF = Collections.singletonList("NETCONF");
private final ModuleBuilder input;
private final Optional<String> content;
@Override
public String getCapabilityUri() {
- // FIXME capabilities in Netconf-impl need to check for NO REVISION
final String withoutRevision = getModuleNamespace().get() + "?module=" + getModuleName().get();
return hasRevision() ? withoutRevision + "&revision=" + Util.writeDate(input.getRevision()) : withoutRevision;
}
@Override
public List<String> getLocation() {
- return Collections.emptyList();
+ return NETCONF;
}
}
import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
import org.opendaylight.controller.netconf.impl.SessionIdProvider;
import org.opendaylight.controller.netconf.impl.osgi.AggregatedNetconfOperationServiceFactory;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
final SessionIdProvider idProvider = new SessionIdProvider();
-
final AggregatedNetconfOperationServiceFactory aggregatedNetconfOperationServiceFactory = new AggregatedNetconfOperationServiceFactory();
final SimulatedOperationProvider simulatedOperationProvider = new SimulatedOperationProvider(idProvider, capabilities, notificationsFile);
- final NetconfMonitoringService monitoringService1 = new NetconfMonitoringServiceImpl(aggregatedNetconfOperationServiceFactory);
+ final NetconfMonitoringService monitoringService1 = new DummyMonitoringService(capabilities);
+
final NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory monitoringService =
- new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(new NetconfMonitoringOperationService(monitoringService1));
+ new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
+ new NetconfMonitoringOperationService(monitoringService1));
aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(simulatedOperationProvider);
aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(monitoringService);
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>mockito-configuration</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-api</artifactId>
+ </dependency>
</dependencies>
<build>
--- /dev/null
+/*
+ * Copyright (c) 2015 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.netconf.util.capability;
+
+import com.google.common.base.Optional;
+import java.util.Collection;
+import java.util.Collections;
+import org.opendaylight.controller.netconf.api.Capability;
+
+/**
+ * Capability representing a basic, one-line, string based capability
+ */
+public class BasicCapability implements Capability {
+
+ private final String capability;
+
+ public BasicCapability(final String capability) {
+ this.capability = capability;
+ }
+
+ @Override
+ public String getCapabilityUri() {
+ return capability;
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Collection<String> getLocation() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return capability;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 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.netconf.util.capability;
+
+import com.google.common.base.Optional;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+/**
+ * Yang model representing capability.
+ */
+public final class YangModuleCapability extends BasicCapability {
+
+ private final String content;
+ private final String revision;
+ private final String moduleName;
+ private final String moduleNamespace;
+
+ public YangModuleCapability(final Module module, final String moduleContent) {
+ super(toCapabilityURI(module));
+ this.content = moduleContent;
+ this.moduleName = module.getName();
+ this.moduleNamespace = module.getNamespace().toString();
+ this.revision = SimpleDateFormatUtil.getRevisionFormat().format(module.getRevision());
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.of(content);
+ }
+
+ private static String toCapabilityURI(final Module module) {
+ return String.valueOf(module.getNamespace()) + "?module="
+ + module.getName() + "&revision=" + SimpleDateFormatUtil.getRevisionFormat().format(module.getRevision());
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.of(moduleName);
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of(moduleNamespace);
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.of(revision);
+ }
+}