<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-monitoring</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-notifications-api</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-broker-impl</artifactId>
<groupId>org.opendaylight.yangtools.thirdparty</groupId>
<artifactId>antlr4-runtime-osgi-nohead</artifactId>
</dependency>
+
<!--Compile scopes for all testing dependencies are intentional-->
<!--This way, all testing dependencies can be transitively used by other integration test modules-->
<!--If the dependencies are test scoped, they are not visible to other maven modules depending on sal-binding-it-->
mavenBundle("org.eclipse.birt.runtime.3_7_1", "org.apache.xml.resolver", "1.2.0"),
mavenBundle(CONTROLLER, "config-netconf-connector").versionAsInProject(), //
+ mavenBundle(CONTROLLER, "netconf-notifications-api").versionAsInProject(), //
+ mavenBundle(CONTROLLER, "ietf-netconf").versionAsInProject(), //
+ mavenBundle(CONTROLLER, "ietf-netconf-notifications").versionAsInProject(), //
mavenBundle(CONTROLLER, "netconf-impl").versionAsInProject(), //
mavenBundle(CONTROLLER, "config-persister-file-xml-adapter").versionAsInProject().noStart(),
<groupId>org.opendaylight.controller</groupId>
<artifactId>ietf-netconf-monitoring</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>ietf-netconf-notifications</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-client</artifactId>
import static org.opendaylight.controller.config.api.JmxAttributeValidationException.checkCondition;
import static org.opendaylight.controller.config.api.JmxAttributeValidationException.checkNotNull;
+
import com.google.common.base.Optional;
import io.netty.util.concurrent.EventExecutor;
import java.math.BigDecimal;
}
userCapabilities = getUserCapabilities();
-
}
private boolean isHostAddressPresent(final Host address) {
new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory, new NetconfStateSchemas.NetconfStateSchemasResolverImpl());
final NetconfDevice device =
- new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, new NetconfMessageTransformer());
+ new NetconfDevice(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, new NetconfMessageTransformer(), true);
final NetconfDeviceCommunicator listener = userCapabilities.isPresent() ?
new NetconfDeviceCommunicator(id, device, userCapabilities.get()) : new NetconfDeviceCommunicator(id, device);
final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(listener);
-
final NetconfClientDispatcher dispatcher = getClientDispatcherDependency();
+
listener.initializeRemoteConnection(dispatcher, clientConfig);
- return new MyAutoCloseable(listener, salFacade);
+ return new SalConnectorCloseable(listener, salFacade);
}
private Optional<NetconfSessionPreferences> getUserCapabilities() {
final InetSocketAddress socketAddress = getSocketAddress();
final long clientConnectionTimeoutMillis = getConnectionTimeoutMillis();
- final ReconnectStrategyFactory sf = new MyReconnectStrategyFactory(
+ final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(
getEventExecutorDependency(), getMaxConnectionAttempts(), getBetweenAttemptsTimeoutMillis(), getSleepFactor());
final ReconnectStrategy strategy = sf.createReconnectStrategy();
.withAddress(socketAddress)
.withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
.withReconnectStrategy(strategy)
- .withSessionListener(listener)
.withAuthHandler(new LoginPassword(getUsername(),getPassword()))
.withProtocol(getTcpOnly() ?
NetconfClientConfiguration.NetconfClientProtocol.TCP :
NetconfClientConfiguration.NetconfClientProtocol.SSH)
.withConnectStrategyFactory(sf)
+ .withSessionListener(listener)
.build();
}
- private static final class MyAutoCloseable implements AutoCloseable {
+ private static final class SalConnectorCloseable implements AutoCloseable {
private final RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
private final NetconfDeviceCommunicator listener;
- public MyAutoCloseable(final NetconfDeviceCommunicator listener,
- final RemoteDeviceHandler<NetconfSessionPreferences> salFacade) {
+ public SalConnectorCloseable(final NetconfDeviceCommunicator listener,
+ final RemoteDeviceHandler<NetconfSessionPreferences> salFacade) {
this.listener = listener;
this.salFacade = salFacade;
}
}
}
- private static final class MyReconnectStrategyFactory implements ReconnectStrategyFactory {
+ private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
private final Long connectionAttempts;
private final EventExecutor executor;
private final double sleepFactor;
private final int minSleep;
- MyReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts, final int minSleep, final BigDecimal sleepFactor) {
+ TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts, final int minSleep, final BigDecimal sleepFactor) {
if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
connectionAttempts = maxConnectionAttempts;
} else {
/**
*
*/
-public interface RemoteDevice<PREF, M> {
+public interface RemoteDevice<PREF, M, LISTENER extends RemoteDeviceCommunicator<M>> {
- void onRemoteSessionUp(PREF remoteSessionCapabilities, RemoteDeviceCommunicator<M> listener);
+ void onRemoteSessionUp(PREF remoteSessionCapabilities, LISTENER listener);
void onRemoteSessionDown();
import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCapabilities;
+import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator;
import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc;
import org.opendaylight.controller.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.fields.unavailable.capabilities.UnavailableCapability.FailureReason;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.fields.unavailable.capabilities.UnavailableCapability;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
/**
* This is a mediator between NetconfDeviceCommunicator and NetconfDeviceSalFacade
*/
-public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferences, NetconfMessage> {
+public final class NetconfDevice implements RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> {
private static final Logger logger = LoggerFactory.getLogger(NetconfDevice.class);
};
private final RemoteDeviceId id;
+ private final boolean reconnectOnSchemasChange;
private final SchemaContextFactory schemaContextFactory;
private final RemoteDeviceHandler<NetconfSessionPreferences> salFacade;
public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
final ExecutorService globalProcessingExecutor, final MessageTransformer<NetconfMessage> messageTransformer) {
+ this(schemaResourcesDTO, id, salFacade, globalProcessingExecutor, messageTransformer, false);
+ }
+
+ // FIXME reduce parameters
+ public NetconfDevice(final SchemaResourcesDTO schemaResourcesDTO, final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
+ final ExecutorService globalProcessingExecutor, final MessageTransformer<NetconfMessage> messageTransformer, final boolean reconnectOnSchemasChange) {
this.id = id;
+ this.reconnectOnSchemasChange = reconnectOnSchemasChange;
this.schemaRegistry = schemaResourcesDTO.getSchemaRegistry();
this.messageTransformer = messageTransformer;
this.schemaContextFactory = schemaResourcesDTO.getSchemaContextFactory();
@Override
public void onRemoteSessionUp(final NetconfSessionPreferences remoteSessionCapabilities,
- final RemoteDeviceCommunicator<NetconfMessage> listener) {
+ final NetconfDeviceCommunicator listener) {
// SchemaContext setup has to be performed in a dedicated thread since
// we are in a netty thread in this method
// Yang models are being downloaded in this method and it would cause a
final DeviceSourcesResolver task = new DeviceSourcesResolver(deviceRpc, remoteSessionCapabilities, id, stateSchemasResolver);
final ListenableFuture<DeviceSources> sourceResolverFuture = processingExecutor.submit(task);
+ if(shouldListenOnSchemaChange(remoteSessionCapabilities)) {
+ registerToBaseNetconfStream(deviceRpc, listener);
+ }
+
final FutureCallback<DeviceSources> resolvedSourceCallback = new FutureCallback<DeviceSources>() {
@Override
public void onSuccess(final DeviceSources result) {
}
+ private void registerToBaseNetconfStream(final NetconfDeviceRpc deviceRpc, final NetconfDeviceCommunicator listener) {
+ final ListenableFuture<RpcResult<CompositeNode>> rpcResultListenableFuture =
+ deviceRpc.invokeRpc(NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_QNAME, NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_CONTENT);
+
+ final NotificationHandler.NotificationFilter filter = new NotificationHandler.NotificationFilter() {
+ @Override
+ public Optional<CompositeNode> filterNotification(final CompositeNode notification) {
+ if (isCapabilityChanged(notification)) {
+ logger.info("{}: Schemas change detected, reconnecting", id);
+ // Only disconnect is enough, the reconnecting nature of the connector will take care of reconnecting
+ listener.disconnect();
+ return Optional.absent();
+ }
+ return Optional.of(notification);
+ }
+
+ private boolean isCapabilityChanged(final CompositeNode notification) {
+ return notification.getNodeType().equals(NetconfCapabilityChange.QNAME);
+ }
+ };
+
+ Futures.addCallback(rpcResultListenableFuture, new FutureCallback<RpcResult<CompositeNode>>() {
+ @Override
+ public void onSuccess(final RpcResult<CompositeNode> result) {
+ notificationHandler.addNotificationFilter(filter);
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ logger.warn("Unable to subscribe to base notification stream. Schemas will not be reloaded on the fly", t);
+ }
+ });
+ }
+
+ private boolean shouldListenOnSchemaChange(final NetconfSessionPreferences remoteSessionCapabilities) {
+ return remoteSessionCapabilities.isNotificationsSupported() && reconnectOnSchemasChange;
+ }
+
private void handleSalInitializationSuccess(final SchemaContext result, final NetconfSessionPreferences remoteSessionCapabilities, final NetconfDeviceRpc deviceRpc) {
updateMessageTransformer(result);
salFacade.onDeviceConnected(result, remoteSessionCapabilities, deviceRpc);
notificationHandler.onRemoteSchemaUp();
- logger.debug("{}: Initialization in sal successful", id);
logger.info("{}: Netconf connector initialized successfully", id);
}
/**
* Update initial message transformer to use retrieved schema
- * @param currentSchemaContext
*/
private void updateMessageTransformer(final SchemaContext currentSchemaContext) {
messageTransformer.onGlobalContextUpdated(currentSchemaContext);
if (t instanceof MissingSchemaSourceException) {
final SourceIdentifier missingSource = ((MissingSchemaSourceException) t).getSourceId();
logger.warn("{}: Unable to build schema context, missing source {}, will reattempt without it", id, missingSource);
- capabilities.addUnresolvedCapabilities(getQNameFromSourceIdentifiers(Sets.newHashSet(missingSource)), FailureReason.MissingSource);
+ capabilities.addUnresolvedCapabilities(getQNameFromSourceIdentifiers(Sets.newHashSet(missingSource)), UnavailableCapability.FailureReason.MissingSource);
setUpSchema(stripMissingSource(requiredSources, missingSource));
// In case resolution error, try only with resolved sources
// TODO check for infinite loop
final SchemaResolutionException resolutionException = (SchemaResolutionException) t;
final Set<SourceIdentifier> unresolvedSources = resolutionException.getUnsatisfiedImports().keySet();
- capabilities.addUnresolvedCapabilities(getQNameFromSourceIdentifiers(unresolvedSources), FailureReason.UnableToResolve);
+ capabilities.addUnresolvedCapabilities(getQNameFromSourceIdentifiers(unresolvedSources), UnavailableCapability.FailureReason.UnableToResolve);
logger.warn("{}: Unable to build schema context, unsatisfied imports {}, will reattempt with resolved only", id, resolutionException.getUnsatisfiedImports());
setUpSchema(resolutionException.getResolvedSources());
// unknown error, fail
*/
package org.opendaylight.controller.sal.connect.netconf;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import java.util.LinkedList;
import java.util.List;
private final MessageTransformer<NetconfMessage> messageTransformer;
private final RemoteDeviceId id;
private boolean passNotifications = false;
+ private NotificationFilter filter;
NotificationHandler(final RemoteDeviceHandler<?> salFacade, final MessageTransformer<NetconfMessage> messageTransformer, final RemoteDeviceId id) {
this.salFacade = Preconditions.checkNotNull(salFacade);
queue.add(notification);
}
- private void passNotification(final CompositeNode parsedNotification) {
+ private synchronized void passNotification(final CompositeNode parsedNotification) {
logger.debug("{}: Forwarding notification {}", id, parsedNotification);
Preconditions.checkNotNull(parsedNotification);
- salFacade.onNotification(parsedNotification);
+
+ if(filter == null || filter.filterNotification(parsedNotification).isPresent()) {
+ salFacade.onNotification(parsedNotification);
+ }
+ }
+
+ synchronized void addNotificationFilter(final NotificationFilter filter) {
+ this.filter = filter;
+ }
+
+ static interface NotificationFilter {
+
+ Optional<CompositeNode> filterNotification(CompositeNode notification);
}
}
private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceCommunicator.class);
- private final RemoteDevice<NetconfSessionPreferences, NetconfMessage> remoteDevice;
+ private final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> remoteDevice;
private final Optional<NetconfSessionPreferences> overrideNetconfCapabilities;
private final RemoteDeviceId id;
private final Lock sessionLock = new ReentrantLock();
private NetconfClientSession session;
private Future<?> initFuture;
- public NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice<NetconfSessionPreferences, NetconfMessage> remoteDevice,
- final NetconfSessionPreferences netconfSessionPreferences) {
- this(id, remoteDevice, Optional.of(netconfSessionPreferences));
+ public NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> remoteDevice,
+ final NetconfSessionPreferences NetconfSessionPreferences) {
+ this(id, remoteDevice, Optional.of(NetconfSessionPreferences));
}
public NetconfDeviceCommunicator(final RemoteDeviceId id,
- final RemoteDevice<NetconfSessionPreferences, NetconfMessage> remoteDevice) {
+ final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> remoteDevice) {
this(id, remoteDevice, Optional.<NetconfSessionPreferences>absent());
}
- private NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice<NetconfSessionPreferences, NetconfMessage> remoteDevice,
+ private NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> remoteDevice,
final Optional<NetconfSessionPreferences> overrideNetconfCapabilities) {
this.id = id;
this.remoteDevice = remoteDevice;
}
}
- public void initializeRemoteConnection(final NetconfClientDispatcher dispatch,
- final NetconfClientConfiguration config) {
+ public void initializeRemoteConnection(final NetconfClientDispatcher dispatcher, final NetconfClientConfiguration config) {
+ // TODO 2313 extract listener from configuration
if(config instanceof NetconfReconnectingClientConfiguration) {
- initFuture = dispatch.createReconnectingClient((NetconfReconnectingClientConfiguration) config);
+ initFuture = dispatcher.createReconnectingClient((NetconfReconnectingClientConfiguration) config);
} else {
- initFuture = dispatch.createClient(config);
+ initFuture = dispatcher.createClient(config);
}
+
initFuture.addListener(new GenericFutureListener<Future<Object>>(){
@Override
}
}
});
+
+ }
+
+ public void disconnect() {
+ if(session != null) {
+ session.close();
+ }
}
private void tearDown( String reason ) {
}
}
- private RpcResult<NetconfMessage> createSessionDownRpcResult()
- {
+ private RpcResult<NetconfMessage> createSessionDownRpcResult() {
return createErrorRpcResult( RpcError.ErrorType.TRANSPORT,
String.format( "The netconf session to %1$s is disconnected", id.getName() ) );
}
- private RpcResult<NetconfMessage> createErrorRpcResult( RpcError.ErrorType errorType, String message )
- {
+ private RpcResult<NetconfMessage> createErrorRpcResult( RpcError.ErrorType errorType, String message ) {
return RpcResultBuilder.<NetconfMessage>failed()
- .withError( errorType, NetconfDocumentedException.ErrorTag.operation_failed.getTagValue(),
- message )
- .build();
+ .withError(errorType, NetconfDocumentedException.ErrorTag.operation_failed.getTagValue(), message).build();
}
@Override
if(session != null) {
session.close();
}
+
tearDown(id + ": Netconf session closed");
}
logger.debug("{}: Message received {}", id, message);
if(logger.isTraceEnabled()) {
- logger.trace( "{}: Matched request: {} to response: {}", id,
- msgToS( request.request ), msgToS( message ) );
+ logger.trace( "{}: Matched request: {} to response: {}", id, msgToS( request.request ), msgToS( message ) );
}
try {
NetconfMessageTransformUtil.checkValidReply( request.request, message );
- }
- catch (final NetconfDocumentedException e) {
+ } catch (final NetconfDocumentedException e) {
logger.warn( "{}: Invalid request-reply match, reply message contains different message-id, request: {}, response: {}",
id, msgToS( request.request ), msgToS( message ), e );
try {
NetconfMessageTransformUtil.checkSuccessReply(message);
- }
- catch(final NetconfDocumentedException e) {
+ } catch(final NetconfDocumentedException e) {
logger.warn( "{}: Error reply from remote device, request: {}, response: {}", id,
msgToS( request.request ), msgToS( message ), e );
}
@Override
- public ListenableFuture<RpcResult<NetconfMessage>> sendRequest(
- final NetconfMessage message, final QName rpc) {
+ public ListenableFuture<RpcResult<NetconfMessage>> sendRequest(final NetconfMessage message, final QName rpc) {
sessionLock.lock();
try {
return sendRequestWithLock( message, rpc );
- }
- finally {
+ } finally {
sessionLock.unlock();
}
}
return containsNonModuleCapability(NetconfMessageTransformUtil.NETCONF_RUNNING_WRITABLE_URI.toString());
}
+ public boolean isNotificationsSupported() {
+ return containsNonModuleCapability(NetconfMessageTransformUtil.NETCONF_NOTIFICATONS_URI.toString())
+ || containsModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_NOTIFICATIONS);
+ }
+
public boolean isMonitoringSupported() {
return containsModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)
|| containsNonModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.CreateSubscriptionInput;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
public class NetconfMessageTransformUtil {
public static final String MESSAGE_ID_ATTR = "message-id";
+ public static final QName CREATE_SUBSCRIPTION_RPC_QNAME = QName.cachedReference(QName.create(CreateSubscriptionInput.QNAME, "create-subscription"));
private NetconfMessageTransformUtil() {}
public static final QName IETF_NETCONF_MONITORING_SCHEMA_VERSION = QName.create(IETF_NETCONF_MONITORING, "version");
public static final QName IETF_NETCONF_MONITORING_SCHEMA_NAMESPACE = QName.create(IETF_NETCONF_MONITORING, "namespace");
+ public static final QName IETF_NETCONF_NOTIFICATIONS = QName.create(NetconfCapabilityChange.QNAME, "ietf-netconf-notifications");
+
public static URI NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
public static QName NETCONF_QNAME = QName.create(NETCONF_URI, null, "netconf");
public static QName NETCONF_DATA_QNAME = QName.create(NETCONF_QNAME, "data");
public static URI NETCONF_CANDIDATE_URI = URI
.create("urn:ietf:params:netconf:capability:candidate:1.0");
+ public static URI NETCONF_NOTIFICATONS_URI = URI
+ .create("urn:ietf:params:netconf:capability:notification:1.0");
+
public static URI NETCONF_RUNNING_WRITABLE_URI = URI
.create("urn:ietf:params:netconf:capability:writable-running:1.0");
public static final CompositeNode COMMIT_RPC_CONTENT =
NodeFactory.createImmutableCompositeNode(NETCONF_COMMIT_QNAME, null, Collections.<Node<?>>emptyList());
+ // Create-subscription changes message
+ public static final CompositeNode CREATE_SUBSCRIPTION_RPC_CONTENT =
+ NodeFactory.createImmutableCompositeNode(CREATE_SUBSCRIPTION_RPC_QNAME, null, Collections.<Node<?>>emptyList());
+
public static Node<?> toFilterStructure(final YangInstanceIdentifier identifier) {
Node<?> previous = null;
if (Iterables.isEmpty(identifier.getPathArguments())) {
}
}
+ leaf reconnect-on-changed-schema {
+ type boolean;
+ default false;
+ description "If true, the connector would auto disconnect/reconnect when schemas are changed in the remote device.
+ The connector subscribes (right after connect) to base netconf notifications and listens for netconf-capability-change notification";
+ }
+
container dom-registry {
uses config:service-ref {
refine type {
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.sal.connect.api.MessageTransformer;
-import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
import org.opendaylight.controller.sal.connect.api.SchemaSourceProviderFactory;
+import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator;
import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc;
import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
final ArrayList<String> capList = Lists.newArrayList(TEST_CAPABILITY);
final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();
- final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
+ final NetconfDeviceCommunicator listener = getListener();
final SchemaContextFactory schemaFactory = getSchemaFactory();
final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
= new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver);
- final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), getMessageTransformer());
+ final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), getMessageTransformer(), true);
// Monitoring not supported
final NetconfSessionPreferences sessionCaps = getSessionCaps(false, capList);
device.onRemoteSessionUp(sessionCaps, listener);
@Test
public void testNetconfDeviceMissingSource() throws Exception {
final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();
- final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
+ final NetconfDeviceCommunicator listener = getListener();
final SchemaContextFactory schemaFactory = getSchemaFactory();
final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
= new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver);
- final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), getMessageTransformer());
+ final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), getMessageTransformer(), true);
// Monitoring supported
final NetconfSessionPreferences sessionCaps = getSessionCaps(true, Lists.newArrayList(TEST_CAPABILITY, TEST_CAPABILITY2));
device.onRemoteSessionUp(sessionCaps, listener);
@Test
public void testNotificationBeforeSchema() throws Exception {
final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();
- final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
+ final NetconfDeviceCommunicator listener = getListener();
final MessageTransformer<NetconfMessage> messageTransformer = getMessageTransformer();
final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
= new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), getSchemaFactory(), stateSchemasResolver);
- final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), messageTransformer);
+ final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), messageTransformer, true);
device.onNotification(netconfMessage);
device.onNotification(netconfMessage);
@Test
public void testNetconfDeviceReconnect() throws Exception {
final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();
- final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
+ final NetconfDeviceCommunicator listener = getListener();
final SchemaContextFactory schemaContextProviderFactory = getSchemaFactory();
final MessageTransformer<NetconfMessage> messageTransformer = getMessageTransformer();
final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
= new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaContextProviderFactory, stateSchemasResolver);
- final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), messageTransformer);
+ final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), messageTransformer, true);
final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION));
device.onRemoteSessionUp(sessionCaps, listener);
capabilities);
}
- public RemoteDeviceCommunicator<NetconfMessage> getListener() throws Exception {
- final RemoteDeviceCommunicator<NetconfMessage> remoteDeviceCommunicator = mockCloseableClass(RemoteDeviceCommunicator.class);
+ public NetconfDeviceCommunicator getListener() throws Exception {
+ final NetconfDeviceCommunicator remoteDeviceCommunicator = mockCloseableClass(NetconfDeviceCommunicator.class);
doReturn(Futures.immediateFuture(rpcResult)).when(remoteDeviceCommunicator).sendRequest(any(NetconfMessage.class), any(QName.class));
return remoteDeviceCommunicator;
}
import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl;
import org.opendaylight.controller.netconf.client.NetconfClientSession;
import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
+import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration;
import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
import org.opendaylight.controller.sal.connect.api.RemoteDevice;
-import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
import org.opendaylight.protocol.framework.ReconnectStrategy;
NetconfClientSession mockSession;
@Mock
- RemoteDevice<NetconfSessionPreferences, NetconfMessage> mockDevice;
+ RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> mockDevice;
NetconfDeviceCommunicator communicator;
public void setUp() throws Exception {
MockitoAnnotations.initMocks( this );
- communicator = new NetconfDeviceCommunicator( new RemoteDeviceId( "test" ), mockDevice );
+ communicator = new NetconfDeviceCommunicator( new RemoteDeviceId( "test" ), mockDevice);
}
@SuppressWarnings("unchecked")
- void setupSession()
- {
- doReturn( Collections.<String>emptySet() ).when( mockSession ).getServerCapabilities();
- doNothing().when( mockDevice ).onRemoteSessionUp( any( NetconfSessionPreferences.class ),
- any( RemoteDeviceCommunicator.class ) );
- communicator.onSessionUp( mockSession );
+ void setupSession() {
+ doReturn(Collections.<String>emptySet()).when(mockSession).getServerCapabilities();
+ doNothing().when(mockDevice).onRemoteSessionUp(any(NetconfSessionPreferences.class),
+ any(NetconfDeviceCommunicator.class));
+ communicator.onSessionUp(mockSession);
}
private ListenableFuture<RpcResult<NetconfMessage>> sendRequest() throws Exception {
testCapability );
doReturn( serverCapabilities ).when( mockSession ).getServerCapabilities();
- ArgumentCaptor<NetconfSessionPreferences> netconfSessionCapabilities =
+ ArgumentCaptor<NetconfSessionPreferences> NetconfSessionPreferences =
ArgumentCaptor.forClass( NetconfSessionPreferences.class );
- doNothing().when( mockDevice ).onRemoteSessionUp( netconfSessionCapabilities.capture(), eq( communicator ) );
+ doNothing().when( mockDevice ).onRemoteSessionUp( NetconfSessionPreferences.capture(), eq( communicator ) );
communicator.onSessionUp( mockSession );
verify( mockSession ).getServerCapabilities();
- verify( mockDevice ).onRemoteSessionUp( netconfSessionCapabilities.capture(), eq( communicator ) );
+ verify( mockDevice ).onRemoteSessionUp( NetconfSessionPreferences.capture(), eq( communicator ) );
- NetconfSessionPreferences actualCapabilites = netconfSessionCapabilities.getValue();
+ NetconfSessionPreferences actualCapabilites = NetconfSessionPreferences.getValue();
assertEquals( "containsModuleCapability", true, actualCapabilites.containsNonModuleCapability(
NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString()) );
assertEquals( "containsModuleCapability", false, actualCapabilites.containsNonModuleCapability(testCapability) );
*/
@Test
public void testNetconfDeviceReconnectInCommunicator() throws Exception {
- final RemoteDevice<NetconfSessionPreferences, NetconfMessage> device = mock(RemoteDevice.class);
+ final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device = mock(RemoteDevice.class);
final TimedReconnectStrategy timedReconnectStrategy = new TimedReconnectStrategy(GlobalEventExecutor.INSTANCE, 10000, 0, 1.0, null, 100L, null);
final ReconnectStrategy reconnectStrategy = spy(new ReconnectStrategy() {
}
});
- final NetconfDeviceCommunicator listener = new NetconfDeviceCommunicator(new RemoteDeviceId("test"), device);
final EventLoopGroup group = new NioEventLoopGroup();
final Timer time = new HashedWheelTimer();
try {
- final NetconfClientConfiguration cfg = NetconfReconnectingClientConfigurationBuilder.create()
+ final NetconfDeviceCommunicator listener = new NetconfDeviceCommunicator(new RemoteDeviceId("test"), device);
+ final NetconfReconnectingClientConfiguration cfg = NetconfReconnectingClientConfigurationBuilder.create()
.withAddress(new InetSocketAddress("localhost", 65000))
.withReconnectStrategy(reconnectStrategy)
.withConnectStrategyFactory(new ReconnectStrategyFactory() {
.withSessionListener(listener)
.build();
-
listener.initializeRemoteConnection(new NetconfClientDispatcherImpl(group, group, time), cfg);
verify(reconnectStrategy, timeout((int) TimeUnit.MINUTES.toMillis(3)).times(101)).scheduleReconnect(any(Throwable.class));
<groupId>${project.groupId}</groupId>
<artifactId>netconf-mapping-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-notifications-api</artifactId>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-util</artifactId>
org.opendaylight.controller.netconf.confignetconfconnector.util,
org.opendaylight.controller.netconf.confignetconfconnector.osgi,
org.opendaylight.controller.netconf.confignetconfconnector.exception,</Private-Package>
- <Import-Package>*</Import-Package>
- <Export-Package></Export-Package>
</instructions>
</configuration>
</plugin>
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigXmlParser.EditConfigExecution;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreContext;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
private static final Logger LOG = LoggerFactory.getLogger(EditConfig.class);
- private final YangStoreSnapshot yangStoreSnapshot;
+ private final YangStoreContext yangStoreSnapshot;
private final TransactionProvider transactionProvider;
private EditConfigXmlParser editConfigXmlParser;
- public EditConfig(YangStoreSnapshot yangStoreSnapshot, TransactionProvider transactionProvider,
+ public EditConfig(YangStoreContext yangStoreSnapshot, TransactionProvider transactionProvider,
ConfigRegistryClient configRegistryClient, String netconfSessionIdForReporting) {
super(configRegistryClient, netconfSessionIdForReporting);
this.yangStoreSnapshot = yangStoreSnapshot;
}
}
- public static Config getConfigMapping(ConfigRegistryClient configRegistryClient, YangStoreSnapshot yangStoreSnapshot) {
+ public static Config getConfigMapping(ConfigRegistryClient configRegistryClient, YangStoreContext yangStoreSnapshot) {
Map<String, Map<String, ModuleConfig>> factories = transformMbeToModuleConfigs(configRegistryClient,
yangStoreSnapshot.getModuleMXBeanEntryMap());
Map<String, Map<Date, IdentityMapping>> identitiesMap = transformIdentities(yangStoreSnapshot.getModules());
import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.Datastore;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreContext;
import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException;
import org.opendaylight.controller.netconf.util.exception.UnexpectedElementException;
import org.opendaylight.controller.netconf.util.exception.UnexpectedNamespaceException;
public class Get extends AbstractConfigNetconfOperation {
- private final YangStoreSnapshot yangStoreSnapshot;
+ private final YangStoreContext yangStoreSnapshot;
private static final Logger LOG = LoggerFactory.getLogger(Get.class);
- public Get(YangStoreSnapshot yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
+ public Get(YangStoreContext yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
String netconfSessionIdForReporting) {
super(configRegistryClient, netconfSessionIdForReporting);
this.yangStoreSnapshot = yangStoreSnapshot;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.Datastore;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreContext;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException;
import org.opendaylight.controller.netconf.util.exception.UnexpectedElementException;
public static final String GET_CONFIG = "get-config";
- private final YangStoreSnapshot yangStoreSnapshot;
+ private final YangStoreContext yangStoreSnapshot;
private final Optional<String> maybeNamespace;
private final TransactionProvider transactionProvider;
private static final Logger LOG = LoggerFactory.getLogger(GetConfig.class);
- public GetConfig(YangStoreSnapshot yangStoreSnapshot, Optional<String> maybeNamespace,
+ public GetConfig(YangStoreContext yangStoreSnapshot, Optional<String> maybeNamespace,
TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient,
String netconfSessionIdForReporting) {
super(configRegistryClient, netconfSessionIdForReporting);
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.ModuleRpcs;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.Rpcs;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreContext;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
private static final Logger LOG = LoggerFactory.getLogger(RuntimeRpc.class);
public static final String CONTEXT_INSTANCE = "context-instance";
- private final YangStoreSnapshot yangStoreSnapshot;
+ private final YangStoreContext yangStoreSnapshot;
- public RuntimeRpc(final YangStoreSnapshot yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
+ public RuntimeRpc(final YangStoreContext yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
String netconfSessionIdForReporting) {
super(configRegistryClient, netconfSessionIdForReporting);
this.yangStoreSnapshot = yangStoreSnapshot;
SchemaContextProvider schemaContextProvider = reference.getBundle().getBundleContext().getService(reference);
- YangStoreServiceImpl yangStoreService = new YangStoreServiceImpl(schemaContextProvider);
+ YangStoreService yangStoreService = new YangStoreService(schemaContextProvider, context);
configRegistryLookup = new ConfigRegistryLookupThread(yangStoreService);
configRegistryLookup.start();
return configRegistryLookup;
}
private class ConfigRegistryLookupThread extends Thread {
- private final YangStoreServiceImpl yangStoreService;
+ private final YangStoreService yangStoreService;
- private ConfigRegistryLookupThread(YangStoreServiceImpl yangStoreService) {
+ private ConfigRegistryLookupThread(YangStoreService yangStoreService) {
super("config-registry-lookup");
this.yangStoreService = yangStoreService;
}
final class NetconfOperationProvider {
private final Set<NetconfOperation> operations;
- NetconfOperationProvider(YangStoreSnapshot yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
+ NetconfOperationProvider(YangStoreContext yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
TransactionProvider transactionProvider, String netconfSessionIdForReporting) {
operations = setUpOperations(yangStoreSnapshot, configRegistryClient, transactionProvider,
return operations;
}
- private static Set<NetconfOperation> setUpOperations(YangStoreSnapshot yangStoreSnapshot,
+ private static Set<NetconfOperation> setUpOperations(YangStoreContext yangStoreSnapshot,
ConfigRegistryClient configRegistryClient, TransactionProvider transactionProvider,
String netconfSessionIdForReporting) {
Set<NetconfOperation> ops = Sets.newHashSet();
@Override
public NetconfOperationServiceImpl createService(String netconfSessionIdForReporting) {
- try {
- return new NetconfOperationServiceImpl(yangStoreService, jmxClient, netconfSessionIdForReporting);
- } catch (YangStoreException e) {
- throw new IllegalStateException(e);
- }
+ return new NetconfOperationServiceImpl(yangStoreService, jmxClient, netconfSessionIdForReporting);
}
}
package org.opendaylight.controller.netconf.confignetconfconnector.osgi;
-import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Map;
import java.util.Set;
-import org.opendaylight.controller.config.api.LookupRegistry;
import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
-import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.yangtools.yang.model.api.Module;
/**
- * Manages life cycle of {@link YangStoreSnapshot}.
+ * Manages life cycle of {@link YangStoreContext}.
*/
public class NetconfOperationServiceImpl implements NetconfOperationService {
- private final YangStoreSnapshot yangStoreSnapshot;
private final NetconfOperationProvider operationProvider;
- private final Set<Capability> capabilities;
private final TransactionProvider transactionProvider;
+ private final YangStoreService yangStoreService;
public NetconfOperationServiceImpl(final YangStoreService yangStoreService, final ConfigRegistryJMXClient jmxClient,
- final String netconfSessionIdForReporting) throws YangStoreException {
+ final String netconfSessionIdForReporting) {
- yangStoreSnapshot = yangStoreService.getYangStoreSnapshot();
- checkConsistencyBetweenYangStoreAndConfig(jmxClient, yangStoreSnapshot);
+ this.yangStoreService = yangStoreService;
transactionProvider = new TransactionProvider(jmxClient, netconfSessionIdForReporting);
- operationProvider = new NetconfOperationProvider(yangStoreSnapshot, jmxClient, transactionProvider,
+ operationProvider = new NetconfOperationProvider(yangStoreService, jmxClient, transactionProvider,
netconfSessionIdForReporting);
- capabilities = setupCapabilities(yangStoreSnapshot);
- }
-
-
- @VisibleForTesting
- static void checkConsistencyBetweenYangStoreAndConfig(final LookupRegistry jmxClient, final YangStoreSnapshot yangStoreSnapshot) {
- Set<String> missingModulesFromConfig = Sets.newHashSet();
-
- Set<String> modulesSeenByConfig = jmxClient.getAvailableModuleFactoryQNames();
- Map<String, Map<String, ModuleMXBeanEntry>> moduleMXBeanEntryMap = yangStoreSnapshot.getModuleMXBeanEntryMap();
-
- for (Map<String, ModuleMXBeanEntry> moduleNameToMBE : moduleMXBeanEntryMap.values()) {
- for (ModuleMXBeanEntry moduleMXBeanEntry : moduleNameToMBE.values()) {
- String moduleSeenByYangStore = moduleMXBeanEntry.getYangModuleQName().toString();
- if(!modulesSeenByConfig.contains(moduleSeenByYangStore)){
- missingModulesFromConfig.add(moduleSeenByYangStore);
- }
- }
- }
-
- Preconditions
- .checkState(
- missingModulesFromConfig.isEmpty(),
- "There are inconsistencies between configuration subsystem and yangstore in terms of discovered yang modules, yang modules missing from config subsystem but present in yangstore: %s, %sAll modules present in config: %s",
- missingModulesFromConfig, System.lineSeparator(), modulesSeenByConfig);
-
}
@Override
public void close() {
- yangStoreSnapshot.close();
transactionProvider.close();
}
@Override
public Set<Capability> getCapabilities() {
- return capabilities;
+ return setupCapabilities(yangStoreService);
}
@Override
return operationProvider.getOperations();
}
- private static Set<Capability> setupCapabilities(final YangStoreSnapshot yangStoreSnapshot) {
+ private static Set<Capability> setupCapabilities(final YangStoreContext yangStoreSnapshot) {
Set<Capability> capabilities = new HashSet<>();
// [RFC6241] 8.3. Candidate Configuration Capability
capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
--- /dev/null
+/*
+ * Copyright (c) 2013 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.confignetconfconnector.osgi;
+
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
+
+public interface YangStoreContext {
+
+ /**
+ * @deprecated Use {@link #getQNamesToIdentitiesToModuleMXBeanEntries()} instead. This method return only one
+ * module representation even if multiple revisions are available.
+ */
+ @Deprecated
+ Map<String/* Namespace from yang file */,
+ Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> getModuleMXBeanEntryMap();
+
+
+ Map<QName, Map<String /* identity local name */, ModuleMXBeanEntry>> getQNamesToIdentitiesToModuleMXBeanEntries();
+
+ /**
+ * Get all modules discovered when this snapshot was created.
+ * @return all modules discovered. If one module exists with two different revisions, return both.
+ */
+ Set<Module> getModules();
+
+ String getModuleSource(ModuleIdentifier moduleIdentifier);
+
+}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.confignetconfconnector.osgi;
-
-public class YangStoreException extends Exception {
-
- private static final long serialVersionUID = 2841238836278528836L;
-
- public YangStoreException(String message, Throwable cause) {
- super(message, cause);
- }
-
-}
* 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.confignetconfconnector.osgi;
-/**
- * Yang store OSGi service
- */
-public interface YangStoreService {
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import java.lang.ref.SoftReference;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicReference;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.netconf.notifications.BaseNetconfNotificationListener;
+import org.opendaylight.controller.netconf.notifications.BaseNotificationPublisherRegistration;
+import org.opendaylight.controller.netconf.notifications.NetconfNotificationCollector;
+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.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.changed.by.parms.ChangedByBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.changed.by.parms.changed.by.server.or.user.ServerBuilder;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class YangStoreService implements YangStoreContext {
+
+ private static final Logger LOG = LoggerFactory.getLogger(YangStoreService.class);
/**
- * Module entry objects mapped to module names and namespaces.
+ * This is a rather interesting locking model. We need to guard against both the
+ * cache expiring from GC and being invalidated by schema context change. The
+ * context can change while we are doing processing, so we do not want to block
+ * it, so no synchronization can happen on the methods.
+ *
+ * So what we are doing is the following:
*
- * @return actual view of what is available in OSGi service registry.
+ * We synchronize with GC as usual, using a SoftReference.
+ *
+ * The atomic reference is used to synchronize with {@link #refresh()}, e.g. when
+ * refresh happens, it will push a SoftReference(null), e.g. simulate the GC. Now
+ * that may happen while the getter is already busy acting on the old schema context,
+ * so it needs to understand that a refresh has happened and retry. To do that, it
+ * attempts a CAS operation -- if it fails, in knows that the SoftReference has
+ * been replaced and thus it needs to retry.
+ *
+ * Note that {@link #getYangStoreSnapshot()} will still use synchronize() internally
+ * to stop multiple threads doing the same work.
*/
- YangStoreSnapshot getYangStoreSnapshot() throws YangStoreException;
+ private final AtomicReference<SoftReference<YangStoreSnapshot>> ref =
+ new AtomicReference<>(new SoftReference<YangStoreSnapshot>(null));
+
+ private final SchemaContextProvider schemaContextProvider;
+ private final BaseNetconfNotificationListener notificationPublisher;
+
+ private final ExecutorService notificationExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(final Runnable r) {
+ return new Thread(r, "config-netconf-connector-capability-notifications");
+ }
+ });
+
+ public YangStoreService(final SchemaContextProvider schemaContextProvider, final BundleContext context) {
+ this(schemaContextProvider, new NotificationCollectorTracker(context));
+ }
+
+ public YangStoreService(final SchemaContextProvider schemaContextProvider, final BaseNetconfNotificationListener notificationHandler) {
+ this.schemaContextProvider = schemaContextProvider;
+ this.notificationPublisher = notificationHandler;
+ }
+
+ private synchronized YangStoreContext getYangStoreSnapshot() {
+ SoftReference<YangStoreSnapshot> r = ref.get();
+ YangStoreSnapshot ret = r.get();
+
+ while (ret == null) {
+ // We need to be compute a new value
+ ret = new YangStoreSnapshot(schemaContextProvider.getSchemaContext());
+
+ if (!ref.compareAndSet(r, new SoftReference<>(ret))) {
+ LOG.debug("Concurrent refresh detected, recomputing snapshot");
+ r = ref.get();
+ ret = null;
+ }
+ }
+
+ return ret;
+ }
+
+ @Override
+ public Map<String, Map<String, ModuleMXBeanEntry>> getModuleMXBeanEntryMap() {
+ return getYangStoreSnapshot().getModuleMXBeanEntryMap();
+ }
+
+ @Override
+ public Map<QName, Map<String, ModuleMXBeanEntry>> getQNamesToIdentitiesToModuleMXBeanEntries() {
+ return getYangStoreSnapshot().getQNamesToIdentitiesToModuleMXBeanEntries();
+ }
+
+ @Override
+ public Set<Module> getModules() {
+ return getYangStoreSnapshot().getModules();
+ }
+
+ @Override
+ public String getModuleSource(final ModuleIdentifier moduleIdentifier) {
+ return getYangStoreSnapshot().getModuleSource(moduleIdentifier);
+ }
+
+ public void refresh() {
+ final YangStoreSnapshot previous = ref.get().get();
+ ref.set(new SoftReference<YangStoreSnapshot>(null));
+ notificationExecutor.submit(new CapabilityChangeNotifier(previous));
+ }
+
+ private final class CapabilityChangeNotifier implements Runnable {
+ private final YangStoreSnapshot previous;
+
+ public CapabilityChangeNotifier(final YangStoreSnapshot previous) {
+ this.previous = previous;
+ }
+
+ @Override
+ public void run() {
+ final YangStoreContext current = getYangStoreSnapshot();
+
+ if(current.equals(previous) == false) {
+ notificationPublisher.onCapabilityChanged(computeDiff(previous, current));
+ }
+ }
+ }
+
+ private static final Function<Module, Uri> MODULE_TO_URI = new Function<Module, Uri>() {
+ @Override
+ public Uri apply(final Module input) {
+ final QName qName = QName.cachedReference(QName.create(input.getQNameModule(), input.getName()));
+ return new Uri(qName.toString());
+ }
+ };
+
+ static NetconfCapabilityChange computeDiff(final YangStoreContext previous, final YangStoreContext current) {
+ final Sets.SetView<Module> removed = Sets.difference(previous.getModules(), current.getModules());
+ final Sets.SetView<Module> added = Sets.difference(current.getModules(), previous.getModules());
+
+ final NetconfCapabilityChangeBuilder netconfCapabilityChangeBuilder = new NetconfCapabilityChangeBuilder();
+ netconfCapabilityChangeBuilder.setChangedBy(new ChangedByBuilder().setServerOrUser(new ServerBuilder().setServer(true).build()).build());
+ netconfCapabilityChangeBuilder.setDeletedCapability(Lists.newArrayList(Collections2.transform(removed, MODULE_TO_URI)));
+ netconfCapabilityChangeBuilder.setAddedCapability(Lists.newArrayList(Collections2.transform(added, MODULE_TO_URI)));
+ // TODO modified should be computed ... but why ?
+ netconfCapabilityChangeBuilder.setModifiedCapability(Collections.<Uri>emptyList());
+ return netconfCapabilityChangeBuilder.build();
+ }
+
+
+ /**
+ * Looks for NetconfNotificationCollector service and publishes base netconf notifications if possible
+ */
+ private static class NotificationCollectorTracker implements ServiceTrackerCustomizer<NetconfNotificationCollector, NetconfNotificationCollector>, BaseNetconfNotificationListener, AutoCloseable {
+
+ private final BundleContext context;
+ private final ServiceTracker<NetconfNotificationCollector, NetconfNotificationCollector> listenerTracker;
+ private BaseNotificationPublisherRegistration publisherReg;
+
+ public NotificationCollectorTracker(final BundleContext context) {
+ this.context = context;
+ listenerTracker = new ServiceTracker<>(context, NetconfNotificationCollector.class, this);
+ listenerTracker.open();
+ }
+
+ @Override
+ public synchronized NetconfNotificationCollector addingService(final ServiceReference<NetconfNotificationCollector> reference) {
+ closePublisherRegistration();
+ publisherReg = context.getService(reference).registerBaseNotificationPublisher();
+ return null;
+ }
+
+ @Override
+ public synchronized void modifiedService(final ServiceReference<NetconfNotificationCollector> reference, final NetconfNotificationCollector service) {
+ closePublisherRegistration();
+ publisherReg = context.getService(reference).registerBaseNotificationPublisher();
+ }
+
+ @Override
+ public synchronized void removedService(final ServiceReference<NetconfNotificationCollector> reference, final NetconfNotificationCollector service) {
+ closePublisherRegistration();
+ publisherReg = null;
+ }
+
+ private void closePublisherRegistration() {
+ if(publisherReg != null) {
+ publisherReg.close();
+ }
+ }
+
+ @Override
+ public synchronized void close() {
+ closePublisherRegistration();
+ listenerTracker.close();
+ }
+
+ @Override
+ public void onCapabilityChanged(final NetconfCapabilityChange capabilityChange) {
+ if(publisherReg == null) {
+ LOG.warn("Omitting notification due to missing notification service: {}", capabilityChange);
+ return;
+ }
+ publisherReg.onCapabilityChanged(capabilityChange);
+ }
+ }
}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.confignetconfconnector.osgi;
-
-import java.lang.ref.SoftReference;
-import java.util.concurrent.atomic.AtomicReference;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class YangStoreServiceImpl implements YangStoreService {
- private static final Logger LOG = LoggerFactory.getLogger(YangStoreServiceImpl.class);
-
- /**
- * This is a rather interesting locking model. We need to guard against both the
- * cache expiring from GC and being invalidated by schema context change. The
- * context can change while we are doing processing, so we do not want to block
- * it, so no synchronization can happen on the methods.
- *
- * So what we are doing is the following:
- *
- * We synchronize with GC as usual, using a SoftReference.
- *
- * The atomic reference is used to synchronize with {@link #refresh()}, e.g. when
- * refresh happens, it will push a SoftReference(null), e.g. simulate the GC. Now
- * that may happen while the getter is already busy acting on the old schema context,
- * so it needs to understand that a refresh has happened and retry. To do that, it
- * attempts a CAS operation -- if it fails, in knows that the SoftReference has
- * been replaced and thus it needs to retry.
- *
- * Note that {@link #getYangStoreSnapshot()} will still use synchronize() internally
- * to stop multiple threads doing the same work.
- */
- private final AtomicReference<SoftReference<YangStoreSnapshotImpl>> ref = new AtomicReference<>(new SoftReference<YangStoreSnapshotImpl>(null));
- private final SchemaContextProvider service;
-
- public YangStoreServiceImpl(final SchemaContextProvider service) {
- this.service = service;
- }
-
- @Override
- public synchronized YangStoreSnapshotImpl getYangStoreSnapshot() throws YangStoreException {
- SoftReference<YangStoreSnapshotImpl> r = ref.get();
- YangStoreSnapshotImpl ret = r.get();
-
- while (ret == null) {
- // We need to be compute a new value
- ret = new YangStoreSnapshotImpl(service.getSchemaContext());
-
- if (!ref.compareAndSet(r, new SoftReference<>(ret))) {
- LOG.debug("Concurrent refresh detected, recomputing snapshot");
- r = ref.get();
- ret = null;
- }
- }
-
- return ret;
- }
-
- /**
- * Called when schema context changes, invalidates cache.
- */
- public void refresh() {
- ref.set(new SoftReference<YangStoreSnapshotImpl>(null));
- }
-}
* 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.confignetconfconnector.osgi;
+import com.google.common.collect.Maps;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.PackageTranslator;
+import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper;
+import org.opendaylight.yangtools.sal.binding.yang.types.TypeProviderImpl;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class YangStoreSnapshot implements YangStoreContext {
+ private static final Logger LOG = LoggerFactory.getLogger(YangStoreSnapshot.class);
+
+
+ private final Map<String /* Namespace from yang file */,
+ Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> moduleMXBeanEntryMap;
+
+
+ private final Map<QName, Map<String, ModuleMXBeanEntry>> qNamesToIdentitiesToModuleMXBeanEntries;
+
+ private final SchemaContext schemaContext;
+
+ public YangStoreSnapshot(final SchemaContext resolveSchemaContext) {
+ LOG.trace("Resolved modules:{}", resolveSchemaContext.getModules());
+ this.schemaContext = resolveSchemaContext;
+ // JMX generator
+
+ Map<String, String> namespaceToPackageMapping = Maps.newHashMap();
+ PackageTranslator packageTranslator = new PackageTranslator(namespaceToPackageMapping);
+ Map<QName, ServiceInterfaceEntry> qNamesToSIEs = new HashMap<>();
+ Map<IdentitySchemaNode, ServiceInterfaceEntry> knownSEITracker = new HashMap<>();
+ // create SIE structure qNamesToSIEs
+ for (Module module : resolveSchemaContext.getModules()) {
+ String packageName = packageTranslator.getPackageName(module);
+ Map<QName, ServiceInterfaceEntry> namesToSIEntries = ServiceInterfaceEntry
+ .create(module, packageName, knownSEITracker);
+ for (Entry<QName, ServiceInterfaceEntry> sieEntry : namesToSIEntries.entrySet()) {
+ // merge value into qNamesToSIEs
+ if (qNamesToSIEs.containsKey(sieEntry.getKey()) == false) {
+ qNamesToSIEs.put(sieEntry.getKey(), sieEntry.getValue());
+ } else {
+ throw new IllegalStateException("Cannot add two SIE with same qname "
+ + sieEntry.getValue());
+ }
+ }
+ }
+
+ Map<String, Map<String, ModuleMXBeanEntry>> moduleMXBeanEntryMap = Maps.newHashMap();
+
+ Map<QName, Map<String /* identity local name */, ModuleMXBeanEntry>> qNamesToIdentitiesToModuleMXBeanEntries = new HashMap<>();
+
-public interface YangStoreSnapshot extends AutoCloseable {
+ for (Module module : schemaContext.getModules()) {
+ String packageName = packageTranslator.getPackageName(module);
+ TypeProviderWrapper typeProviderWrapper = new TypeProviderWrapper(
+ new TypeProviderImpl(resolveSchemaContext));
- /**
- * @deprecated Use {@link #getQNamesToIdentitiesToModuleMXBeanEntries()} instead. This method return only one
- * module representation even if multiple revisions are available.
- */
- @Deprecated
- Map<String/* Namespace from yang file */,
- Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> getModuleMXBeanEntryMap();
+ QName qName = QName.create(module.getNamespace(), module.getRevision(), module.getName());
+ Map<String /* MB identity local name */, ModuleMXBeanEntry> namesToMBEs =
+ Collections.unmodifiableMap(ModuleMXBeanEntry.create(module, qNamesToSIEs, resolveSchemaContext,
+ typeProviderWrapper, packageName));
+ moduleMXBeanEntryMap.put(module.getNamespace().toString(), namesToMBEs);
+
+ qNamesToIdentitiesToModuleMXBeanEntries.put(qName, namesToMBEs);
+ }
+ this.moduleMXBeanEntryMap = Collections.unmodifiableMap(moduleMXBeanEntryMap);
+ this.qNamesToIdentitiesToModuleMXBeanEntries = Collections.unmodifiableMap(qNamesToIdentitiesToModuleMXBeanEntries);
+
+ }
+
+ @Override
+ public Map<String, Map<String, ModuleMXBeanEntry>> getModuleMXBeanEntryMap() {
+ return moduleMXBeanEntryMap;
+ }
+
+ @Override
+ public Map<QName, Map<String, ModuleMXBeanEntry>> getQNamesToIdentitiesToModuleMXBeanEntries() {
+ return qNamesToIdentitiesToModuleMXBeanEntries;
+ }
+
+ @Override
+ public Set<Module> getModules() {
+ return schemaContext.getModules();
+ }
+
+ @Override
+ public String getModuleSource(final org.opendaylight.yangtools.yang.model.api.ModuleIdentifier moduleIdentifier) {
+ return schemaContext.getModuleSource(moduleIdentifier).get();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
- Map<QName, Map<String /* identity local name */, ModuleMXBeanEntry>> getQNamesToIdentitiesToModuleMXBeanEntries();
+ final YangStoreSnapshot that = (YangStoreSnapshot) o;
- /**
- * Get all modules discovered when this snapshot was created.
- * @return all modules discovered. If one module exists with two different revisions, return both.
- */
- Set<Module> getModules();
+ if (schemaContext != null ? !schemaContext.equals(that.schemaContext) : that.schemaContext != null)
+ return false;
- String getModuleSource(ModuleIdentifier moduleIdentifier);
+ return true;
+ }
@Override
- void close();
+ public int hashCode() {
+ return schemaContext != null ? schemaContext.hashCode() : 0;
+ }
}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.confignetconfconnector.osgi;
-
-import com.google.common.collect.Maps;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
-import org.opendaylight.controller.config.yangjmxgenerator.PackageTranslator;
-import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
-import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper;
-import org.opendaylight.yangtools.sal.binding.yang.types.TypeProviderImpl;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class YangStoreSnapshotImpl implements YangStoreSnapshot {
- private static final Logger LOG = LoggerFactory.getLogger(YangStoreSnapshotImpl.class);
-
-
- private final Map<String /* Namespace from yang file */,
- Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> moduleMXBeanEntryMap;
-
-
- private final Map<QName, Map<String, ModuleMXBeanEntry>> qNamesToIdentitiesToModuleMXBeanEntries;
-
- private final SchemaContext schemaContext;
-
-
- public YangStoreSnapshotImpl(final SchemaContext resolveSchemaContext) {
- LOG.trace("Resolved modules:{}", resolveSchemaContext.getModules());
- this.schemaContext = resolveSchemaContext;
- // JMX generator
-
- Map<String, String> namespaceToPackageMapping = Maps.newHashMap();
- PackageTranslator packageTranslator = new PackageTranslator(namespaceToPackageMapping);
- Map<QName, ServiceInterfaceEntry> qNamesToSIEs = new HashMap<>();
- Map<IdentitySchemaNode, ServiceInterfaceEntry> knownSEITracker = new HashMap<>();
- // create SIE structure qNamesToSIEs
- for (Module module : resolveSchemaContext.getModules()) {
- String packageName = packageTranslator.getPackageName(module);
- Map<QName, ServiceInterfaceEntry> namesToSIEntries = ServiceInterfaceEntry
- .create(module, packageName, knownSEITracker);
- for (Entry<QName, ServiceInterfaceEntry> sieEntry : namesToSIEntries.entrySet()) {
- // merge value into qNamesToSIEs
- if (qNamesToSIEs.containsKey(sieEntry.getKey()) == false) {
- qNamesToSIEs.put(sieEntry.getKey(), sieEntry.getValue());
- } else {
- throw new IllegalStateException("Cannot add two SIE with same qname "
- + sieEntry.getValue());
- }
- }
- }
-
- Map<String, Map<String, ModuleMXBeanEntry>> moduleMXBeanEntryMap = Maps.newHashMap();
-
- Map<QName, Map<String /* identity local name */, ModuleMXBeanEntry>> qNamesToIdentitiesToModuleMXBeanEntries = new HashMap<>();
-
-
- for (Module module : schemaContext.getModules()) {
- String packageName = packageTranslator.getPackageName(module);
- TypeProviderWrapper typeProviderWrapper = new TypeProviderWrapper(
- new TypeProviderImpl(resolveSchemaContext));
-
- QName qName = QName.create(module.getNamespace(), module.getRevision(), module.getName());
-
- Map<String /* MB identity local name */, ModuleMXBeanEntry> namesToMBEs =
- Collections.unmodifiableMap(ModuleMXBeanEntry.create(module, qNamesToSIEs, resolveSchemaContext,
- typeProviderWrapper, packageName));
- moduleMXBeanEntryMap.put(module.getNamespace().toString(), namesToMBEs);
-
- qNamesToIdentitiesToModuleMXBeanEntries.put(qName, namesToMBEs);
- }
- this.moduleMXBeanEntryMap = Collections.unmodifiableMap(moduleMXBeanEntryMap);
- this.qNamesToIdentitiesToModuleMXBeanEntries = Collections.unmodifiableMap(qNamesToIdentitiesToModuleMXBeanEntries);
-
- }
-
- @Override
- public Map<String, Map<String, ModuleMXBeanEntry>> getModuleMXBeanEntryMap() {
- return moduleMXBeanEntryMap;
- }
-
- @Override
- public Map<QName, Map<String, ModuleMXBeanEntry>> getQNamesToIdentitiesToModuleMXBeanEntries() {
- return qNamesToIdentitiesToModuleMXBeanEntries;
- }
-
- @Override
- public Set<Module> getModules() {
- return schemaContext.getModules();
- }
-
- @Override
- public String getModuleSource(final org.opendaylight.yangtools.yang.model.api.ModuleIdentifier moduleIdentifier) {
- return schemaContext.getModuleSource(moduleIdentifier).get();
- }
-
- @Override
- public void close() {
-
- }
-}
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.get.Get;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.GetConfig;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc.RuntimeRpc;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreServiceImpl;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreContext;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreService;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
private TestImplModuleFactory factory4;
@Mock
- YangStoreSnapshot yangStoreSnapshot;
+ YangStoreContext yangStoreSnapshot;
@Mock
NetconfOperationRouter netconfOperationRouter;
@Mock
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+
+
+ final Filter filter = mock(Filter.class);
+ doReturn(filter).when(mockedContext).createFilter(anyString());
+ doNothing().when(mockedContext).addServiceListener(any(ServiceListener.class), anyString());
+ doReturn(new ServiceReference<?>[]{}).when(mockedContext).getServiceReferences(anyString(), anyString());
+
doReturn(getMbes()).when(this.yangStoreSnapshot).getModuleMXBeanEntryMap();
doReturn(getModules()).when(this.yangStoreSnapshot).getModules();
doNothing().when(netconfOperationServiceSnapshot).close();
this.factory2 = new DepTestImplModuleFactory();
this.factory3 = new IdentityTestModuleFactory();
factory4 = new TestImplModuleFactory();
+
+
super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext, this.factory, this.factory2,
this.factory3, factory4));
YangParserImpl yangParser = new YangParserImpl();
final SchemaContext schemaContext = yangParser.resolveSchemaContext(new HashSet<>(yangParser.parseYangModelsFromStreamsMapped(yangDependencies).values()));
- YangStoreServiceImpl yangStoreService = new YangStoreServiceImpl(new SchemaContextProvider() {
+ YangStoreService yangStoreService = new YangStoreService(new SchemaContextProvider() {
@Override
public SchemaContext getSchemaContext() {
return schemaContext ;
}
- });
- mBeanEntries.putAll(yangStoreService.getYangStoreSnapshot().getModuleMXBeanEntryMap());
+ }, mockedContext);
+ mBeanEntries.putAll(yangStoreService.getModuleMXBeanEntryMap());
return mBeanEntries;
}
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.ValidateTest;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigXmlParser.EditConfigExecution;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreContext;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
public class EditConfigTest {
@Mock
- private YangStoreSnapshot yangStoreSnapshot;
+ private YangStoreContext yangStoreSnapshot;
@Mock
private TransactionProvider provider;
@Mock
+++ /dev/null
-/*
- * Copyright (c) 2013 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.confignetconfconnector.osgi;
-
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import java.net.URI;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Map;
-import java.util.Set;
-import org.hamcrest.CoreMatchers;
-import org.junit.Assert;
-import org.junit.Test;
-import org.opendaylight.controller.config.api.LookupRegistry;
-import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
-import org.opendaylight.yangtools.yang.common.QName;
-
-public class NetconfOperationServiceImplTest {
-
- private static final Date date1970_01_01;
-
- static {
- try {
- date1970_01_01 = new SimpleDateFormat("yyyy-MM-dd").parse("1970-01-01");
- } catch (ParseException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Test
- public void testCheckConsistencyBetweenYangStoreAndConfig_ok() throws Exception {
- NetconfOperationServiceImpl.checkConsistencyBetweenYangStoreAndConfig(
- mockJmxClient("qname1", "qname2"),
- mockYangStoreSnapshot("qname2", "qname1"));
- }
-
- @Test
- public void testCheckConsistencyBetweenYangStoreAndConfig_ok2() throws Exception {
- NetconfOperationServiceImpl.checkConsistencyBetweenYangStoreAndConfig(
- mockJmxClient("qname1", "qname2", "qname4", "qname5"),
- mockYangStoreSnapshot("qname2", "qname1"));
- }
-
- @Test
- public void testCheckConsistencyBetweenYangStoreAndConfig_ok3() throws Exception {
- NetconfOperationServiceImpl.checkConsistencyBetweenYangStoreAndConfig(
- mockJmxClient(),
- mockYangStoreSnapshot());
- }
-
- @Test
- public void testCheckConsistencyBetweenYangStoreAndConfig_yangStoreMore() throws Exception {
- try {
- NetconfOperationServiceImpl.checkConsistencyBetweenYangStoreAndConfig(mockJmxClient("qname1"),
- mockYangStoreSnapshot("qname2", "qname1"));
- fail("An exception of type " + IllegalStateException.class + " was expected");
- } catch (IllegalStateException e) {
- String message = e.getMessage();
- Assert.assertThat(
- message,
- CoreMatchers
- .containsString("missing from config subsystem but present in yangstore: [(namespace?revision=1970-01-01)qname2]"));
- Assert.assertThat(
- message,
- CoreMatchers
- .containsString("All modules present in config: [(namespace?revision=1970-01-01)qname1]"));
- }
- }
-
- private YangStoreSnapshot mockYangStoreSnapshot(final String... qnames) {
- YangStoreSnapshot mock = mock(YangStoreSnapshot.class);
-
- Map<String, Map<String, ModuleMXBeanEntry>> map = Maps.newHashMap();
-
- Map<String, ModuleMXBeanEntry> innerMap = Maps.newHashMap();
-
- int i = 1;
- for (String qname : qnames) {
- innerMap.put(Integer.toString(i++), mockMBeanEntry(qname));
- }
-
- map.put("1", innerMap);
-
- doReturn(map).when(mock).getModuleMXBeanEntryMap();
-
- return mock;
- }
-
- private ModuleMXBeanEntry mockMBeanEntry(final String qname) {
- ModuleMXBeanEntry mock = mock(ModuleMXBeanEntry.class);
- QName q = getQName(qname);
- doReturn(q).when(mock).getYangModuleQName();
- return mock;
- }
-
- private QName getQName(final String qname) {
- return QName.create(URI.create("namespace"), date1970_01_01, qname);
- }
-
- private LookupRegistry mockJmxClient(final String... visibleQNames) {
- LookupRegistry mock = mock(LookupRegistry.class);
- Set<String> qnames = Sets.newHashSet();
- for (String visibleQName : visibleQNames) {
- QName q = getQName(visibleQName);
- qnames.add(q.toString());
- }
- doReturn(qnames).when(mock).getAvailableModuleFactoryQNames();
- return mock;
- }
-}
<username xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">admin</username>
<password xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">admin</password>
<tcp-only xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">false</tcp-only>
+ <reconnect-on-changed-schema>true</reconnect-on-changed-schema>
<event-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>
<name>global-event-executor</name>
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
+import org.opendaylight.controller.netconf.mapping.api.SessionAwareNetconfOperation;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
if (netconfOperation instanceof DefaultNetconfOperation) {
((DefaultNetconfOperation) netconfOperation).setNetconfSession(session);
}
+ if(netconfOperation instanceof SessionAwareNetconfOperation) {
+ ((SessionAwareNetconfOperation) netconfOperation).setSession(session);
+ }
if (!handlingPriority.equals(HandlingPriority.CANNOT_HANDLE)) {
Preconditions.checkState(!sortedPriority.containsKey(handlingPriority),
import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder;
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException;
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreService;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreServiceImpl;
-import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.controller.netconf.notifications.BaseNetconfNotificationListener;
import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
import org.opendaylight.protocol.framework.NeverReconnectStrategy;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
return clientDispatcher;
}
- private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException {
+ private HardcodedYangStoreService getYangStore() throws IOException {
final Collection<InputStream> yangDependencies = getBasicYangs();
return new HardcodedYangStoreService(yangDependencies);
}
return b.build();
}
- public static final class HardcodedYangStoreService implements YangStoreService {
-
- private final List<InputStream> byteArrayInputStreams;
+ public static final class HardcodedYangStoreService extends YangStoreService {
+ public HardcodedYangStoreService(final Collection<? extends InputStream> inputStreams) throws IOException {
+ super(new SchemaContextProvider() {
+ @Override
+ public SchemaContext getSchemaContext() {
+ return getSchema(inputStreams);
+ }
+ }, new BaseNetconfNotificationListener() {
+ @Override
+ public void onCapabilityChanged(final NetconfCapabilityChange capabilityChange) {
+ // NOOP
+ }
+ });
+ }
- public HardcodedYangStoreService(final Collection<? extends InputStream> inputStreams) throws YangStoreException, IOException {
- byteArrayInputStreams = new ArrayList<>();
+ private static SchemaContext getSchema(final Collection<? extends InputStream> inputStreams) {
+ final ArrayList<InputStream> byteArrayInputStreams = new ArrayList<>();
for (final InputStream inputStream : inputStreams) {
assertNotNull(inputStream);
- final byte[] content = IOUtils.toByteArray(inputStream);
+ final byte[] content;
+ try {
+ content = IOUtils.toByteArray(inputStream);
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot read " + inputStream, e);
+ }
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(content);
byteArrayInputStreams.add(byteArrayInputStream);
}
- }
- @Override
- public YangStoreSnapshot getYangStoreSnapshot() throws YangStoreException {
for (final InputStream inputStream : byteArrayInputStreams) {
try {
inputStream.reset();
}
final YangParserImpl yangParser = new YangParserImpl();
- final SchemaContext schemaContext = yangParser.resolveSchemaContext(new HashSet<>(yangParser.parseYangModelsFromStreamsMapped(byteArrayInputStreams).values()));
- final YangStoreServiceImpl yangStoreService = new YangStoreServiceImpl(new SchemaContextProvider() {
- @Override
- public SchemaContext getSchemaContext() {
- return schemaContext ;
- }
- });
- return yangStoreService.getYangStoreSnapshot();
+ return yangParser.resolveSchemaContext(new HashSet<>(yangParser.parseYangModelsFromStreamsMapped(byteArrayInputStreams).values()));
}
}
}
import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.opendaylight.controller.sal.connect.api.RemoteDevice;
-import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator;
import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
}
static NetconfDeviceCommunicator getSessionListener() {
- RemoteDevice<NetconfSessionPreferences, NetconfMessage> mockedRemoteDevice = mock(RemoteDevice.class);
- doNothing().when(mockedRemoteDevice).onRemoteSessionUp(any(NetconfSessionPreferences.class), any(RemoteDeviceCommunicator.class));
+ RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> mockedRemoteDevice = mock(RemoteDevice.class);
+ doNothing().when(mockedRemoteDevice).onRemoteSessionUp(any(NetconfSessionPreferences.class), any(NetconfDeviceCommunicator.class));
doNothing().when(mockedRemoteDevice).onRemoteSessionDown();
return new NetconfDeviceCommunicator(new RemoteDeviceId("secure-test"), mockedRemoteDevice);
}
classes.add(NeutronLoadBalancerPoolNorthbound.class);
classes.add(NeutronLoadBalancerHealthMonitorNorthbound.class);
classes.add(NeutronLoadBalancerPoolMembersNorthbound.class);
+ classes.add(MOXyJsonProvider.class);
return classes;
}
moxyJsonProvider.setMarshalEmptyCollections(true);
moxyJsonProvider.setValueWrapper("$");
- Map<String, String> namespacePrefixMapper = new HashMap<String, String>(1);
+ Map<String, String> namespacePrefixMapper = new HashMap<String, String>(3);
namespacePrefixMapper.put("router", "router"); // FIXME: fill in with XSD
namespacePrefixMapper.put("provider", "provider"); // FIXME: fill in with XSD
+ namespacePrefixMapper.put("binding", "binding");
moxyJsonProvider.setNamespacePrefixMapper(namespacePrefixMapper);
moxyJsonProvider.setNamespaceSeparator(':');
package org.opendaylight.controller.networkconfig.neutron;
+
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
@XmlElement (name="security_groups")
List<NeutronSecurityGroup> securityGroups;
+ @XmlElement (namespace= "binding", name="host_id")
+ String bindinghostID;
+
+ @XmlElement (namespace= "binding", name="vnic_type")
+ String bindingvnicType;
+
+ @XmlElement (namespace= "binding", name="vif_type")
+ String bindingvifType;
+
+
/* this attribute stores the floating IP address assigned to
* each fixed IP address
*/
this.securityGroups = securityGroups;
}
+ public String getBindinghostID() {
+ return bindinghostID;
+ }
+
+ public void setBindinghostID(String bindinghostID) {
+ this.bindinghostID = bindinghostID;
+ }
+
+ public String getBindingvnicType() {
+ return bindingvnicType;
+ }
+
+ public void setBindingvnicType(String bindingvnicType) {
+ this.bindingvnicType = bindingvnicType;
+ }
+
+ public String getBindingvifType() {
+ return bindingvifType;
+ }
+
+ public void setBindingvifType(String bindingvifType) {
+ this.bindingvifType = bindingvifType;
+ }
+
public NeutronFloatingIP getFloatingIP(String key) {
if (!floatingIPMap.containsKey(key)) {
return null;
return "NeutronPort [portUUID=" + portUUID + ", networkUUID=" + networkUUID + ", name=" + name
+ ", adminStateUp=" + adminStateUp + ", status=" + status + ", macAddress=" + macAddress
+ ", fixedIPs=" + fixedIPs + ", deviceID=" + deviceID + ", deviceOwner=" + deviceOwner + ", tenantID="
- + tenantID + ", floatingIPMap=" + floatingIPMap + ", securityGroups=" + securityGroups + "]";
+ + tenantID + ", floatingIPMap=" + floatingIPMap + ", securityGroups=" + securityGroups
+ + ", bindinghostID=" + bindinghostID + ", bindingvnicType=" + bindingvnicType
+ + ", bindingvnicType=" + bindingvnicType + "]";
}
}