Add support for netconf-capability-change notification.
Change-Id: Ib05f0d57c744275dbf569e3b26821274671f6787
Signed-off-by: JMorvay <jmorvay@cisco.com>
<feature version='${project.version}'>odl-netconf-ssh</feature>
<feature version='${project.version}'>odl-netconf-client</feature>
<feature version='${controller.mdsal.version}'>odl-mdsal-broker</feature>
+ <bundle>mvn:org.opendaylight.netconf/mdsal-netconf-notification/${project.version}</bundle>
<bundle>mvn:org.opendaylight.netconf/mdsal-netconf-connector/${project.version}</bundle>
<bundle>mvn:org.opendaylight.netconf/mdsal-netconf-monitoring/${project.version}</bundle>
<configfile finalname='${config.configfile.directory}/${config.netconf.mdsal.configfile}'>mvn:org.opendaylight.netconf/netconf-mdsal-config/${project.version}/xml/config</configfile>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>netconf-subsystem</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>mdsal-netconf-notification</artifactId>
+ <packaging>bundle</packaging>
+ <name>${project.artifactId}</name>
+
+ <dependencies>
+ <!-- compile dependencies -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-mapping-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-notifications-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-notifications-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-config</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ </dependency>
+ </dependencies>
+
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Import-Package>*</Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ 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.config.yang.netconf.mdsal.notification;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.netconf.notifications.BaseNotificationPublisherRegistration;
+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.NetconfState;
+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.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.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+
+/**
+ * Listens on capabilities changes in data store and publishes them to base
+ * netconf notification stream listener.
+ */
+final class BaseCapabilityChangeNotificationPublisher implements DataChangeListener, AutoCloseable {
+
+ private final BaseNotificationPublisherRegistration baseNotificationPublisherRegistration;
+
+ public BaseCapabilityChangeNotificationPublisher(BaseNotificationPublisherRegistration baseNotificationPublisherRegistration) {
+ this.baseNotificationPublisherRegistration = baseNotificationPublisherRegistration;
+ }
+
+ @Override
+ public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ baseNotificationPublisherRegistration.onCapabilityChanged(computeCapabilitycChange(change));
+ }
+
+ private NetconfCapabilityChange computeCapabilitycChange(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ final NetconfCapabilityChangeBuilder netconfCapabilityChangeBuilder = new NetconfCapabilityChangeBuilder();
+ netconfCapabilityChangeBuilder.setChangedBy(new ChangedByBuilder().setServerOrUser(new ServerBuilder().setServer(true).build()).build());
+
+ if (!change.getCreatedData().isEmpty()) {
+ final InstanceIdentifier capabilitiesIdentifier = InstanceIdentifier.create(NetconfState.class).child(Capabilities.class).builder().build();
+ Preconditions.checkArgument(change.getCreatedData().get(capabilitiesIdentifier) instanceof Capabilities);
+ netconfCapabilityChangeBuilder.setAddedCapability(((Capabilities) change.getCreatedData().get(capabilitiesIdentifier)).getCapability());
+ netconfCapabilityChangeBuilder.setDeletedCapability(Collections.<Uri>emptyList());
+ } else {
+ Preconditions.checkArgument(change.getUpdatedSubtree() instanceof Capabilities);
+ final Set<Uri> currentState = Sets.newHashSet(((Capabilities) change.getUpdatedSubtree()).getCapability());
+ final Set<Uri> previousState = Sets.newHashSet(((Capabilities) change.getOriginalSubtree()).getCapability());
+
+ netconfCapabilityChangeBuilder.setAddedCapability(Lists.newArrayList(Sets.difference(currentState, previousState)));
+ netconfCapabilityChangeBuilder.setDeletedCapability(Lists.newArrayList(Sets.difference(previousState, currentState)));
+ }
+
+ // TODO modified should be computed ... but why ?
+ netconfCapabilityChangeBuilder.setModifiedCapability(Collections.<Uri>emptyList());
+ return netconfCapabilityChangeBuilder.build();
+ }
+
+ @Override
+ public void close() throws Exception {
+
+ }
+}
--- /dev/null
+package org.opendaylight.controller.config.yang.netconf.mdsal.notification;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.netconf.mdsal.notification.NetconfNotificationOperationServiceFactory;
+import org.opendaylight.netconf.notifications.NetconfNotificationCollector;
+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.monitoring.rev101004.netconf.state.Capabilities;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class NetconfMdsalNotificationMapperModule extends org.opendaylight.controller.config.yang.netconf.mdsal.notification.AbstractNetconfMdsalNotificationMapperModule {
+ public NetconfMdsalNotificationMapperModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NetconfMdsalNotificationMapperModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.netconf.mdsal.notification.NetconfMdsalNotificationMapperModule oldModule, java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ final NetconfNotificationCollector notificationCollector = getNotificationCollectorDependency();
+
+ final NotificationToMdsalWriter notificationToMdsalWriter = new NotificationToMdsalWriter(notificationCollector);
+ getBindingAwareBrokerDependency().registerProvider(notificationToMdsalWriter);
+
+ InstanceIdentifier capabilitiesIdentifier = InstanceIdentifier.create(NetconfState.class).child(Capabilities.class).builder().build();
+
+ final ListenerRegistration capabilityChangeListenerRegistration = getDataBrokerDependency().registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, capabilitiesIdentifier,
+ new BaseCapabilityChangeNotificationPublisher(notificationCollector.registerBaseNotificationPublisher()), AsyncDataBroker.DataChangeScope.SUBTREE);
+
+ final NetconfNotificationOperationServiceFactory netconfNotificationOperationServiceFactory =
+ new NetconfNotificationOperationServiceFactory(getNotificationRegistryDependency()) {
+ @Override
+ public void close() {
+ super.close();
+ notificationToMdsalWriter.close();
+ capabilityChangeListenerRegistration.close();
+ getAggregatorDependency().onRemoveNetconfOperationServiceFactory(this);
+ }
+ };
+
+ getAggregatorDependency().onAddNetconfOperationServiceFactory(netconfNotificationOperationServiceFactory);
+
+ return netconfNotificationOperationServiceFactory;
+ }
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: netconf-mdsal-notification yang module local name: netconf-mdsal-notification-mapper
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Aug 05 10:21:08 CEST 2015
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.netconf.mdsal.notification;
+
+public class NetconfMdsalNotificationMapperModuleFactory extends org.opendaylight.controller.config.yang.netconf.mdsal.notification.AbstractNetconfMdsalNotificationMapperModuleFactory {
+
+}
--- /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.config.yang.netconf.mdsal.notification;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+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.TransactionCommitFailedException;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.netconf.notifications.NetconfNotificationCollector;
+import org.opendaylight.netconf.notifications.NotificationRegistration;
+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;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.Streams;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.streams.Stream;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.streams.StreamKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Listens on changes in netconf notification stream availability and writes
+ * changes to the data store.
+ */
+final class NotificationToMdsalWriter implements AutoCloseable, NetconfNotificationCollector.NetconfNotificationStreamListener, BindingAwareProvider {
+
+ private static final Logger LOG = LoggerFactory.getLogger(NotificationToMdsalWriter.class);
+
+ private final NetconfNotificationCollector netconfNotificationCollector;
+ private NotificationRegistration notificationRegistration;
+ private DataBroker dataBroker;
+
+ public NotificationToMdsalWriter(NetconfNotificationCollector netconfNotificationCollector) {
+ this.netconfNotificationCollector = netconfNotificationCollector;
+ }
+
+ @Override
+ public void close() {
+ final WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
+ tx.delete(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(Netconf.class));
+ final CheckedFuture<Void, TransactionCommitFailedException> submit = tx.submit();
+
+ Futures.addCallback(submit, new FutureCallback<Void>() {
+ @Override
+ public void onSuccess(Void aVoid) {
+ LOG.debug("Streams cleared successfully");
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ LOG.warn("Unable to clear streams", throwable);
+ }
+ });
+
+ notificationRegistration.close();
+ }
+
+ @Override
+ public void onSessionInitiated(BindingAwareBroker.ProviderContext session) {
+ dataBroker = session.getSALService(DataBroker.class);
+ notificationRegistration = netconfNotificationCollector.registerStreamListener(this);
+ }
+
+ @Override
+ public void onStreamRegistered(Stream stream) {
+ final WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
+
+ final InstanceIdentifier streamIdentifier = InstanceIdentifier.create(Netconf.class).child(Streams.class).
+ builder().child(Stream.class, stream.getKey()).build();
+ tx.merge(LogicalDatastoreType.OPERATIONAL, streamIdentifier, stream, true);
+
+ try {
+ tx.submit().checkedGet();
+ LOG.debug("Stream %s registered successfully.", stream.getName());
+ } catch (TransactionCommitFailedException e) {
+ LOG.warn("Unable to register stream.", e);
+ }
+ }
+
+ @Override
+ public void onStreamUnregistered(StreamNameType stream) {
+ final WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
+
+ final StreamKey streamKey = new StreamKey(stream);
+ final InstanceIdentifier streamIdentifier = InstanceIdentifier.create(Netconf.class).child(Streams.class).
+ builder().child(Stream.class, streamKey).build();
+
+ tx.delete(LogicalDatastoreType.OPERATIONAL, streamIdentifier);
+
+ try {
+ tx.submit().checkedGet();
+ LOG.debug("Stream %s unregistered successfully.", stream);
+ } catch (TransactionCommitFailedException e) {
+ LOG.warn("Unable to unregister stream", e);
+ }
+ }
+}
--- /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.netconf.mdsal.notification;
+
+import java.util.Collections;
+import java.util.Set;
+import org.opendaylight.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.netconf.notifications.NetconfNotificationRegistry;
+import org.opendaylight.netconf.notifications.impl.ops.CreateSubscription;
+
+public class NetconfNotificationOperationService implements NetconfOperationService {
+ private final Set<NetconfOperation> netconfOperations;
+
+ public NetconfNotificationOperationService(String netconfSessionIdForReporting, NetconfNotificationRegistry netconfNotificationRegistry) {
+ this.netconfOperations = Collections.<NetconfOperation>singleton(new CreateSubscription(netconfSessionIdForReporting, netconfNotificationRegistry));
+ }
+
+
+ @Override
+ public Set<NetconfOperation> getNetconfOperations() {
+ return netconfOperations;
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
--- /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.netconf.mdsal.notification;
+
+import java.util.Collections;
+import java.util.Set;
+import org.opendaylight.controller.config.util.capability.Capability;
+import org.opendaylight.netconf.api.monitoring.CapabilityListener;
+import org.opendaylight.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.netconf.notifications.NetconfNotificationRegistry;
+
+public class NetconfNotificationOperationServiceFactory implements NetconfOperationServiceFactory, AutoCloseable {
+
+ private final NetconfNotificationRegistry netconfNotificationRegistry;
+
+ private static final AutoCloseable AUTO_CLOSEABLE = new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ // NOOP
+ }
+ };
+
+ public NetconfNotificationOperationServiceFactory(NetconfNotificationRegistry netconfNotificationRegistry) {
+ this.netconfNotificationRegistry = netconfNotificationRegistry;
+ }
+
+ @Override
+ public Set<Capability> getCapabilities() {
+ // 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 NetconfOperationService createService(String netconfSessionIdForReporting) {
+ return new NetconfNotificationOperationService(netconfSessionIdForReporting, netconfNotificationRegistry);
+ }
+
+ @Override
+ public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+ return AUTO_CLOSEABLE;
+ }
+
+ @Override
+ public void close() {
+ }
+}
--- /dev/null
+module netconf-mdsal-notification {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:notification";
+ prefix "nnotification";
+
+ import netconf-northbound-mapper { prefix nnm; revision-date 2015-01-14; }
+ import opendaylight-md-sal-dom { prefix md-sal-dom; revision-date 2013-10-28; }
+ import opendaylight-md-sal-binding {prefix md-sal-binding; revision-date 2013-10-28;}
+ import netconf-northbound-notification {prefix nnn; revision-date 2015-08-06;}
+ import config { prefix config; revision-date 2013-04-05; }
+
+
+ organization "Cisco Systems, Inc.";
+
+ description
+ "This module contains the base YANG definitions for
+ an SAL notification mapper implementation";
+
+ revision "2015-08-03" {
+ description
+ "Initial revision.";
+ }
+
+ identity netconf-mdsal-notification-mapper {
+ base config:module-type;
+ config:provided-service nnm:netconf-northbound-mapper;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case netconf-mdsal-notification-mapper {
+ when "/config:modules/config:module/config:type = 'netconf-mdsal-notification-mapper'";
+
+ container aggregator {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity nnm:netconf-mapper-registry;
+ }
+ }
+ }
+
+ container binding-aware-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity md-sal-binding:binding-broker-osgi-registry;
+ }
+ }
+ }
+
+ container data-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity md-sal-binding:binding-async-data-broker;
+ }
+ }
+ }
+
+ container notification-collector {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity nnn:netconf-notification-collector;
+ }
+ }
+ }
+
+ container notification-registry {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity nnn:netconf-notification-registry;
+ }
+ }
+ }
+ }
+ }
+}
\ 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.config.yang.netconf.mdsal.notification;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.netconf.notifications.BaseNotificationPublisherRegistration;
+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.NetconfState;
+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.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.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class BaseCapabilityChangeNotificationPublisherTest {
+
+ @Mock
+ private BaseNotificationPublisherRegistration baseNotificationPublisherRegistration;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doNothing().when(baseNotificationPublisherRegistration).onCapabilityChanged(any(NetconfCapabilityChange.class));
+ }
+
+ @Test
+ public void testOnDataChanged() {
+ final BaseCapabilityChangeNotificationPublisher baseCapabilityChangeNotificationPublisher =
+ new BaseCapabilityChangeNotificationPublisher(baseNotificationPublisherRegistration);
+ final InstanceIdentifier capabilitiesIdentifier = InstanceIdentifier.create(NetconfState.class).child(Capabilities.class).builder().build();
+ final List<Uri> newCapabilitiesList = Lists.newArrayList(new Uri("newCapability"), new Uri("createdCapability"));
+
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> capabilitiesChange = mock(AsyncDataChangeEvent.class);
+ Capabilities newCapabilities = new CapabilitiesBuilder().setCapability(newCapabilitiesList).build();
+ Map<InstanceIdentifier<?>, DataObject> createdData = Maps.newHashMap();
+ createdData.put(capabilitiesIdentifier, newCapabilities);
+ doReturn(createdData).when(capabilitiesChange).getCreatedData();
+ baseCapabilityChangeNotificationPublisher.onDataChanged(capabilitiesChange);
+
+ verify(baseNotificationPublisherRegistration).onCapabilityChanged(changedCapabilitesFrom(newCapabilitiesList, Collections.<Uri>emptyList()));
+
+ final List<Uri> originalCapabilitiesList = Lists.newArrayList(new Uri("originalCapability"), new Uri("anotherOriginalCapability"));
+ final List<Uri> updatedCapabilitiesList = Lists.newArrayList(new Uri("originalCapability"), new Uri("newCapability"));
+
+ Capabilities originalCapabilities = new CapabilitiesBuilder().setCapability(originalCapabilitiesList).build();
+ Capabilities updatedCapabilities = new CapabilitiesBuilder().setCapability(updatedCapabilitiesList).build();
+
+ doReturn(Collections.emptyMap()).when(capabilitiesChange).getCreatedData();
+ doReturn(originalCapabilities).when(capabilitiesChange).getOriginalSubtree();
+ doReturn(updatedCapabilities).when(capabilitiesChange).getUpdatedSubtree();
+ baseCapabilityChangeNotificationPublisher.onDataChanged(capabilitiesChange);
+
+ verify(baseNotificationPublisherRegistration).onCapabilityChanged(changedCapabilitesFrom(
+ Lists.newArrayList(new Uri("newCapability")), Lists.newArrayList(new Uri("anotherOriginalCapability"))));
+ }
+
+ private NetconfCapabilityChange changedCapabilitesFrom(List<Uri> added, List<Uri> deleted) {
+ NetconfCapabilityChangeBuilder netconfCapabilityChangeBuilder = new NetconfCapabilityChangeBuilder();
+ netconfCapabilityChangeBuilder.setChangedBy(new ChangedByBuilder().setServerOrUser(
+ new ServerBuilder().setServer(true).build()).build());
+
+ netconfCapabilityChangeBuilder.setModifiedCapability(Collections.<Uri>emptyList());
+ netconfCapabilityChangeBuilder.setAddedCapability(added);
+ netconfCapabilityChangeBuilder.setDeletedCapability(deleted);
+
+ return netconfCapabilityChangeBuilder.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.config.yang.netconf.mdsal.notification;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.util.concurrent.Futures;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+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.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.netconf.notifications.NetconfNotificationCollector;
+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;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.Streams;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.streams.Stream;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.streams.StreamBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class NotificationToMdsalWriterTest {
+
+ @Mock
+ private DataBroker dataBroker;
+
+ private NotificationToMdsalWriter writer;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ final NetconfNotificationCollector notificationCollector = mock(NetconfNotificationCollector.class);
+ final BindingAwareBroker.ProviderContext session = mock(BindingAwareBroker.ProviderContext.class);
+ doReturn(null).when(notificationCollector).registerStreamListener(any(
+ NetconfNotificationCollector.NetconfNotificationStreamListener.class));
+ doReturn(dataBroker).when(session).getSALService(DataBroker.class);
+
+ WriteTransaction tx = mock(WriteTransaction.class);
+ doNothing().when(tx).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class)
+ , any(DataObject.class), anyBoolean());
+ doNothing().when(tx).delete(any(LogicalDatastoreType.class), any(InstanceIdentifier.class));
+ doReturn(Futures.immediateCheckedFuture(null)).when(tx).submit();
+ doReturn(tx).when(dataBroker).newWriteOnlyTransaction();
+
+ writer = new NotificationToMdsalWriter(notificationCollector);
+ writer.onSessionInitiated(session);
+ }
+
+ @Test
+ public void testStreamRegisteration() {
+ final StreamNameType testStreamName = new StreamNameType("TESTSTREAM");
+ final Stream testStream = new StreamBuilder().setName(testStreamName).build();
+ final InstanceIdentifier streamIdentifier = InstanceIdentifier.create(Netconf.class).child(Streams.class)
+ .builder().child(Stream.class, testStream.getKey()).build();
+
+ writer.onStreamRegistered(testStream);
+
+ verify(dataBroker.newWriteOnlyTransaction()).merge(LogicalDatastoreType.OPERATIONAL, streamIdentifier, testStream, true);
+
+ writer.onStreamUnregistered(testStreamName);
+
+ verify(dataBroker.newWriteOnlyTransaction()).delete(LogicalDatastoreType.OPERATIONAL, streamIdentifier);
+ }
+}
</auth-provider>
</module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification:impl">prefix:netconf-notification-manager</type>
+ <name>netconf-notification-manager</name>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:notification">prefix:netconf-mdsal-notification-mapper</type>
+ <name>netconf-mdsal-notification-mapper</name>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:notification">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+ <name>binding-data-broker</name>
+ </data-broker>
+ <binding-aware-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:notification">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-broker-osgi-registry</type>
+ <name>binding-osgi-broker</name>
+ </binding-aware-broker>
+ <aggregator xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:notification">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">prefix:netconf-mapper-registry</type>
+ <name>mapper-aggregator-registry</name>
+ </aggregator>
+ <notification-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:notification">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification">prefix:netconf-notification-registry</type>
+ <name>netconf-notification-manager</name>
+ </notification-registry>
+ <notification-collector>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification">prefix:netconf-notification-collector</type>
+ <name>netconf-notification-manager</name>
+ </notification-collector>
+ </module>
+
+
<!--TCP endpoint for MD-SAL netconf server -->
<!--<module>-->
<provider>/modules/module[type='netconf-server-dispatcher-impl'][name='netconf-mdsal-server-dispatcher']</provider>
</instance>
</service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">prefix:netconf-northbound-mapper</type>
+ <instance>
+ <name>netconf-mdsal-notification-mapper</name>
+ <provider>/modules/module[type='netconf-mdsal-notification-mapper'][name='netconf-mdsal-notification-mapper']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification">prefix:netconf-notification-collector</type>
+ <instance>
+ <name>netconf-notification-manager</name>
+ <provider>/modules/module[type='netconf-notification-manager'][name='netconf-notification-manager']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification">prefix:netconf-notification-registry</type>
+ <instance>
+ <name>netconf-notification-manager</name>
+ <provider>/modules/module[type='netconf-notification-manager'][name='netconf-notification-manager']</provider>
+ </instance>
+ </service>
</services>
</data>
<capability>urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:tcp?module=netconf-northbound-tcp&revision=2015-04-23</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl?module=netconf-northbound-impl&revision=2015-01-12</capability>
<capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled?module=threadpool-impl-scheduled&revision=2013-12-01</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:notification?module=netconf-mdsal-notification&revision=2015-08-03</capability>
</required-capabilities>
</snapshot>
--- /dev/null
+module netconf-northbound-notification {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification";
+ prefix "nnn";
+
+ import config { prefix config; revision-date 2013-04-05; }
+
+ description "TODO";
+
+ revision "2015-08-06" {
+ description "Initial revision.";
+ }
+
+ identity netconf-notification-collector {
+ base "config:service-type";
+ config:java-class "org.opendaylight.netconf.notifications.NetconfNotificationCollector";
+ }
+
+ identity netconf-notification-registry {
+ base "config:service-type";
+ config:java-class "org.opendaylight.netconf.notifications.NetconfNotificationRegistry";
+ }
+}
\ No newline at end of file
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>netconf-subsystem</artifactId>
<groupId>org.opendaylight.netconf</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
- <modelVersion>4.0.0</modelVersion>
<packaging>bundle</packaging>
<artifactId>netconf-notifications-impl</artifactId>
<groupId>org.opendaylight.mdsal</groupId>
<artifactId>mdsal-binding-generator-impl</artifactId>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.mdsal</groupId>
<artifactId>mdsal-binding-dom-codec</artifactId>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ietf-netconf-notifications</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
--- /dev/null
+package org.opendaylight.controller.config.yang.netconf.northbound.notification.impl;
+
+import org.opendaylight.netconf.notifications.impl.NetconfNotificationManager;
+
+public class NetconfNotificationManagerModule extends org.opendaylight.controller.config.yang.netconf.northbound.notification.impl.AbstractNetconfNotificationManagerModule {
+ public NetconfNotificationManagerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NetconfNotificationManagerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.netconf.northbound.notification.impl.NetconfNotificationManagerModule oldModule, java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ return new NetconfNotificationManager();
+ }
+
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: netconf-northbound-notification-impl yang module local name: netconf-notification-manager
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Fri Aug 07 17:09:20 CEST 2015
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.netconf.northbound.notification.impl;
+public class NetconfNotificationManagerModuleFactory extends org.opendaylight.controller.config.yang.netconf.northbound.notification.impl.AbstractNetconfNotificationManagerModuleFactory {
+
+}
import org.opendaylight.controller.config.util.xml.DocumentedException;
import org.opendaylight.controller.config.util.xml.XmlElement;
import org.opendaylight.controller.config.util.xml.XmlUtil;
-import org.opendaylight.netconf.util.mapping.AbstractLastNetconfOperation;
import org.opendaylight.netconf.api.NetconfSession;
import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.netconf.mapping.api.SessionAwareNetconfOperation;
import org.opendaylight.netconf.notifications.NetconfNotificationRegistry;
import org.opendaylight.netconf.notifications.NotificationListenerRegistration;
import org.opendaylight.netconf.notifications.impl.NetconfNotificationManager;
+import org.opendaylight.netconf.util.mapping.AbstractSingletonNetconfOperation;
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.netconf.notification._1._0.rev080714.StreamNameType;
import org.slf4j.Logger;
* Create subscription listens for create subscription requests and registers notification listeners into notification registry.
* Received notifications are sent to the client right away
*/
-public class CreateSubscription extends AbstractLastNetconfOperation implements SessionAwareNetconfOperation, AutoCloseable {
+public class CreateSubscription extends AbstractSingletonNetconfOperation implements SessionAwareNetconfOperation, AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(CreateSubscription.class);
--- /dev/null
+module netconf-northbound-notification-impl {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification:impl";
+ prefix "nnnimpl";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import netconf-northbound-notification { prefix nnn; revision-date 2015-08-06; }
+
+ description "TODO";
+
+ revision "2015-08-07"{
+ description "Initial revision.";
+ }
+
+ identity netconf-notification-manager {
+ base config:module-type;
+ config:provided-service nnn:netconf-notification-collector;
+ config:provided-service nnn:netconf-notification-registry;
+ config:java-name-prefix NetconfNotificationManager;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case netconf-notification-manager {
+ when "/config:modules/config:module/config:type = 'netconf-notification-manager'";
+ }
+ }
+}
\ No newline at end of file
<module>config-netconf-connector</module>
<module>mdsal-netconf-connector</module>
<module>mdsal-netconf-monitoring</module>
+ <module>mdsal-netconf-notification</module>
<module>netconf-util</module>
<module>netconf-netty-util</module>
<module>netconf-mapping-api</module>