diff options
author | Maros Marsalek <mmarsale@cisco.com> | 2016-04-09 16:22:06 +0200 |
---|---|---|
committer | Maros Marsalek <mmarsale@cisco.com> | 2016-06-10 11:04:21 +0200 |
commit | 0e517a26e69ff3d1b04ba19c4539cb8dee60b097 (patch) | |
tree | 29819b80acba85d45ea8d4b90a9b1ed7e0926e12 /v3po/notification | |
parent | 5440823dbe280d3f6c6c94074e86db0c11596686 (diff) |
HONEYCOMB-33: Add notification service to Hc
Implement VPP interface notification translator as part
of v3po2vpp plugin
Change-Id: I69cfad9668ae9e4d79ed30bb8d54d294faa4c54a
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Diffstat (limited to 'v3po/notification')
23 files changed, 1511 insertions, 0 deletions
diff --git a/v3po/notification/api/pom.xml b/v3po/notification/api/pom.xml new file mode 100644 index 000000000..042149871 --- /dev/null +++ b/v3po/notification/api/pom.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2015 Cisco and/or its affiliates. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<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"> + <parent> + <groupId>io.fd.honeycomb.common</groupId> + <artifactId>impl-parent</artifactId> + <version>1.0.0-SNAPSHOT</version> + <relativePath>../../../common/impl-parent</relativePath> + </parent> + + <modelVersion>4.0.0</modelVersion> + <groupId>io.fd.honeycomb.v3po</groupId> + <artifactId>notification-api</artifactId> + <version>1.0.0-SNAPSHOT</version> + <packaging>bundle</packaging> + + <dependencies> + <dependency> + <groupId>org.opendaylight.mdsal</groupId> + <artifactId>mdsal-dom-api</artifactId> + <version>2.0.2-Beryllium-SR2</version> + </dependency> + </dependencies> + + +</project> diff --git a/v3po/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java b/v3po/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java new file mode 100644 index 000000000..0f5e28cde --- /dev/null +++ b/v3po/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.v3po.notification; + +import com.google.common.annotations.Beta; +import javax.annotation.Nonnull; + +/** + * Special notification producer that is capable of starting and stopping the notification stream + */ +@Beta +public interface ManagedNotificationProducer extends NotificationProducer { + + /** + * Start notification stream managed by this producer. + * + * @param collector Notification collector expected to collect produced notifications + */ + void start(@Nonnull NotificationCollector collector); + + /** + * Stop notification stream managed by this producer. + */ + void stop(); +} diff --git a/v3po/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java b/v3po/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java new file mode 100644 index 000000000..406ab03d2 --- /dev/null +++ b/v3po/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.v3po.notification; + +import com.google.common.annotations.Beta; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.Notification; + +/** + * Notification collector. Collects all the notifications, which are further + * propagated to all wired northbound interfaces. + */ +@Beta +public interface NotificationCollector extends AutoCloseable, NotificationProducer { + + /** + * Publish a single notification. + * + * @param notification notification to be published + */ + void onNotification(@Nonnull Notification notification); +} diff --git a/v3po/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java b/v3po/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java new file mode 100644 index 000000000..dab773c45 --- /dev/null +++ b/v3po/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.v3po.notification; + +import com.google.common.annotations.Beta; +import java.util.Collection; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.Notification; + +/** + * Produces notification of exposed notification types + */ +@Beta +public interface NotificationProducer extends AutoCloseable { + + /** + * Return collection of notification types that will be emitted by this producer. + * Other types of notifications should not be emitted, since they can be rejected. + * + * @return collection of all notification types emitted by this producer + */ + @Nonnull + Collection<Class<? extends Notification>> getNotificationTypes(); +} diff --git a/v3po/notification/api/src/main/yang/notification-api.yang b/v3po/notification/api/src/main/yang/notification-api.yang new file mode 100644 index 000000000..4e6eb98ae --- /dev/null +++ b/v3po/notification/api/src/main/yang/notification-api.yang @@ -0,0 +1,31 @@ +module notification-api { + yang-version 1; + namespace "urn:honeycomb:params:xml:ns:yang:notification:api"; + prefix "hc-notif-a"; + + import config { prefix config; revision-date 2013-04-05; } + + description + "Module definition for honeycomb notification service APIs"; + + revision "2016-06-01" { + description + "Initial revision"; + } + + identity honeycomb-notification-collector { + base "config:service-type"; + config:java-class io.fd.honeycomb.v3po.notification.NotificationCollector; + } + + identity dom-notification-service { + base "config:service-type"; + config:java-class org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter; + } + + identity honeycomb-notification-producer { + base "config:service-type"; + config:java-class io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; + } + +} diff --git a/v3po/notification/impl/pom.xml b/v3po/notification/impl/pom.xml new file mode 100644 index 000000000..5c1b2034b --- /dev/null +++ b/v3po/notification/impl/pom.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2015 Cisco and/or its affiliates. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<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"> + <parent> + <groupId>io.fd.honeycomb.common</groupId> + <artifactId>impl-parent</artifactId> + <version>1.0.0-SNAPSHOT</version> + <relativePath>../../../common/impl-parent</relativePath> + </parent> + + <modelVersion>4.0.0</modelVersion> + <groupId>io.fd.honeycomb.v3po</groupId> + <artifactId>notification-impl</artifactId> + <version>1.0.0-SNAPSHOT</version> + <packaging>bundle</packaging> + + <dependencies> + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>notification-api</artifactId> + <version>1.0.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.opendaylight.controller</groupId> + <artifactId>sal-binding-config</artifactId> + <version>1.3.2-Beryllium-SR2</version> + </dependency> + + <dependency> + <groupId>org.opendaylight.netconf</groupId> + <artifactId>netconf-notifications-api</artifactId> + <version>1.0.2-Beryllium-SR2</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>attach-artifacts</id> + <goals> + <goal>attach-artifact</goal> + </goals> + <phase>package</phase> + <configuration> + <artifacts> + <artifact> + <file>${config.file}</file> + <type>xml</type> + <classifier>config</classifier> + </artifact> + <artifact> + <file>src/main/config/notification-to-netconf-config.xml</file> + <type>xml</type> + <classifier>notification2netconf</classifier> + </artifact> + </artifacts> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/v3po/notification/impl/src/main/config/default-config.xml b/v3po/notification/impl/src/main/config/default-config.xml new file mode 100644 index 000000000..2b91de47f --- /dev/null +++ b/v3po/notification/impl/src/main/config/default-config.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- vi: set et smarttab sw=4 tabstop=4: --> +<!-- + Copyright (c) 2015 Cisco and/or its affiliates. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<snapshot> + <required-capabilities> + <capability>urn:honeycomb:params:xml:ns:yang:notification:impl?module=notification-impl&revision=2016-06-01</capability> + </required-capabilities> + <configuration> + + <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> + <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config"> + <!-- Underlying dom notification service(router)--> + <module> + <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:impl">prefix:honeycomb-dom-notification-service</type> + <name>honeycomb-dom-notification-service</name> + <queue-depth>1024</queue-depth> + </module> + + <!-- Honeycomb notification collector built on top of dom notification service--> + <module> + <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:impl">prefix:honeycomb-notification-manager</type> + <name>honeycomb-notification-manager</name> + <runtime-mapping-codec> + <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type> + <name>runtime-mapping-singleton</name> + </runtime-mapping-codec> + <dom-notification-service> + <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:api">prefix:dom-notification-service</type> + <name>honeycomb-dom-notification-service</name> + </dom-notification-service> + <!-- Here goes list of producers. They will register themselves from plugins --> + <!--<notification-producers>--> + <!--</notification-producers>--> + </module> + + </modules> + + <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config"> + <service> + <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:api">prefix:dom-notification-service</type> + <instance> + <name>honeycomb-dom-notification-service</name> + <provider>/modules/module[type='honeycomb-dom-notification-service'][name='honeycomb-dom-notification-service']</provider> + </instance> + </service> + <service> + <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:api">prefix:honeycomb-notification-collector</type> + <instance> + <name>honeycomb-notification-manager</name> + <provider>/modules/module[type='honeycomb-notification-manager'][name='honeycomb-notification-manager']</provider> + </instance> + </service> + + </services> + </data> + </configuration> +</snapshot> + diff --git a/v3po/notification/impl/src/main/config/notification-to-netconf-config.xml b/v3po/notification/impl/src/main/config/notification-to-netconf-config.xml new file mode 100644 index 000000000..d2aac0932 --- /dev/null +++ b/v3po/notification/impl/src/main/config/notification-to-netconf-config.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- vi: set et smarttab sw=4 tabstop=4: --> +<!-- + Copyright (c) 2015 Cisco and/or its affiliates. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<snapshot> +<required-capabilities> + <capability>urn:honeycomb:params:xml:ns:yang:notification:impl?module=notification-impl&revision=2016-06-01</capability> +</required-capabilities> +<configuration> + + <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> + <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config"> + <module> + <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:impl">prefix:honeycomb-notification-to-netconf-translator</type> + <name>honeycomb-notification-to-netconf-translator</name> + <netconf-notification-registry> + <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification"> + prefix:netconf-notification-registry + </type> + <name>vpp-netconf-notification-manager</name> + </netconf-notification-registry> + <netconf-notification-collector> + <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification"> + prefix:netconf-notification-collector + </type> + <name>vpp-netconf-notification-manager</name> + </netconf-notification-collector> + <schema-service> + <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type> + <name>yang-schema-service</name> + </schema-service> + <honeycomb-notification-collector> + <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:api">prefix:honeycomb-notification-collector</type> + <name>honeycomb-notification-manager</name> + </honeycomb-notification-collector> + <dom-notification-service> + <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:api">prefix:dom-notification-service</type> + <name>honeycomb-dom-notification-service</name> + </dom-notification-service> + <netconf-stream-name>honeycomb</netconf-stream-name> + <netconf-stream-description>All notifications received by honeycomb's plugins</netconf-stream-description> + </module> + </modules> + </data> +</configuration> +</snapshot> diff --git a/v3po/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java b/v3po/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java new file mode 100644 index 000000000..e7d54e318 --- /dev/null +++ b/v3po/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.v3po.notification.impl; + +import io.fd.honeycomb.v3po.notification.NotificationCollector; +import java.util.Collection; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; +import org.opendaylight.yangtools.yang.binding.Notification; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Notification collector based on MD-SAL's {@link NotificationPublishService}. + */ +public final class HoneycombNotificationCollector implements NotificationCollector, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(HoneycombNotificationCollector.class); + + private final NotificationPublishService bindingDOMNotificationPublishServiceAdapter; + private final NotificationProducerRegistry notificationProducerRegistry; + + public HoneycombNotificationCollector( + @Nonnull final NotificationPublishService bindingDOMNotificationPublishServiceAdapter, + @Nonnull final NotificationProducerRegistry notificationProducerRegistry) { + this.bindingDOMNotificationPublishServiceAdapter = bindingDOMNotificationPublishServiceAdapter; + this.notificationProducerRegistry = notificationProducerRegistry; + } + + @Override + public void close() throws Exception { + LOG.trace("Closing"); + } + + @Override + public void onNotification(@Nonnull final Notification notification) { + LOG.debug("Notification: {} pushed into collector", notification.getClass().getSimpleName()); + LOG.trace("Notification: {} pushed into collector", notification); + try { + bindingDOMNotificationPublishServiceAdapter.putNotification(notification); + } catch (InterruptedException e) { + LOG.warn("Interrupted", e); + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + + @Override + @Nonnull + public Collection<Class<? extends Notification>> getNotificationTypes() { + return notificationProducerRegistry.getNotificationTypes(); + } +} diff --git a/v3po/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java b/v3po/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java new file mode 100644 index 000000000..8fba700bd --- /dev/null +++ b/v3po/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.v3po.notification.impl; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; +import io.fd.honeycomb.v3po.notification.NotificationProducer; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.yangtools.yang.binding.Notification; +import org.opendaylight.yangtools.yang.common.QName; + +/** + * Holds the collection of registered notification producers. + * Provides additional information about the types of notifications produced per producer and overall. + */ +@ThreadSafe +public final class NotificationProducerRegistry { + + private final Set<Class<? extends Notification>> notificationTypes; + private final Map<QName, ManagedNotificationProducer> notificationQNameToProducer; + private final Multimap<ManagedNotificationProducer, QName> notificationProducerQNames; + + public NotificationProducerRegistry(final List<ManagedNotificationProducer> notificationProducersDependency) { + this.notificationTypes = toTypes(notificationProducersDependency); + this.notificationQNameToProducer = toQNameMap(notificationProducersDependency); + this.notificationProducerQNames = toQNameMapReversed(notificationProducersDependency); + } + + private static Multimap<ManagedNotificationProducer, QName> toQNameMapReversed(final List<ManagedNotificationProducer> notificationProducers) { + final Multimap<ManagedNotificationProducer, QName> multimap = HashMultimap.create(); + + for (ManagedNotificationProducer producer : notificationProducers) { + for (Class<? extends Notification> aClass : producer.getNotificationTypes()) { + multimap.put(producer, getQName(aClass)); + } + } + return multimap; + } + + private static Set<Class<? extends Notification>> toTypes(final List<ManagedNotificationProducer> notificationProducersDependency) { + // Get all notification types registered from HC notification producers + return notificationProducersDependency + .stream() + .flatMap(producer -> producer.getNotificationTypes().stream()) + .collect(Collectors.toSet()); + } + + + private static Map<QName, ManagedNotificationProducer> toQNameMap(final List<ManagedNotificationProducer> producerDependencies) { + // Only a single notification producer per notification type is allowed + final Map<QName, ManagedNotificationProducer> qNamesToProducers = Maps.newHashMap(); + for (ManagedNotificationProducer notificationProducer : producerDependencies) { + for (QName qName : typesToQNames(notificationProducer.getNotificationTypes())) { + final NotificationProducer previousProducer = qNamesToProducers.put(qName, notificationProducer); + checkArgument(previousProducer == null, "2 producers of the same notification type: %s. " + + "Producer 1: {} Producer 2: {}" , qName, previousProducer, notificationProducer); + } + } + return qNamesToProducers; + } + + + private static Set<QName> typesToQNames(final Collection<Class<? extends Notification>> notificationTypes) { + return notificationTypes + .stream() + .map(NotificationProducerRegistry::getQName) + .collect(Collectors.toSet()); + } + + + public static QName getQName(final Class<? extends Notification> aClass) { + try { + return (QName) aClass.getField("QNAME").get(null); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new IllegalArgumentException("Unable to retrieve QName for notification of type: " + aClass, e); + } + } + + Set<Class<? extends Notification>> getNotificationTypes() { + return notificationTypes; + } + + Map<QName, ManagedNotificationProducer> getNotificationQNameToProducer() { + return notificationQNameToProducer; + } + + Multimap<ManagedNotificationProducer, QName> getNotificationProducerQNames() { + return notificationProducerQNames; + } +} diff --git a/v3po/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java b/v3po/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java new file mode 100644 index 000000000..cefb50ac9 --- /dev/null +++ b/v3po/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.v3po.notification.impl; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.collect.Sets; +import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; +import io.fd.honeycomb.v3po.notification.NotificationCollector; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListener; +import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListenerRegistry; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Starts & stops notification producer dependencies on demand. + * Uses {@link DOMNotificationSubscriptionListenerRegistry} to receive subscription change notifications. + */ +@ThreadSafe +public final class NotificationProducerTracker + implements DOMNotificationSubscriptionListener, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(NotificationProducerTracker.class); + + private final ListenerRegistration<NotificationProducerTracker> subscriptionListener; + private final NotificationProducerRegistry registry; + private final NotificationCollector collector; + + private final Set<ManagedNotificationProducer> alreadyStartedProducers = new HashSet<>(); + + public NotificationProducerTracker(@Nonnull final NotificationProducerRegistry registry, + @Nonnull final NotificationCollector collector, + @Nonnull final DOMNotificationSubscriptionListenerRegistry notificationRouter) { + this.registry = registry; + this.collector = collector; + this.subscriptionListener = notificationRouter.registerSubscriptionListener(this); + } + + @Override + public synchronized void onSubscriptionChanged(final Set<SchemaPath> set) { + LOG.debug("Subscriptions changed. Current subscriptions: {}", set); + final Set<QName> currentSubscriptions = set.stream().map(SchemaPath::getLastComponent).collect(Collectors.toSet()); + final Set<QName> startedQNames = getStartedQNames(alreadyStartedProducers); + final Sets.SetView<QName> newSubscriptions = Sets.difference(currentSubscriptions, startedQNames); + LOG.debug("Subscriptions changed. New subscriptions: {}", newSubscriptions); + final Sets.SetView<QName> deletedSubscriptions = Sets.difference(startedQNames, currentSubscriptions); + LOG.debug("Subscriptions changed. Deleted subscriptions: {}", deletedSubscriptions); + + newSubscriptions.stream().forEach(newSub -> { + if(!registry.getNotificationQNameToProducer().containsKey(newSub)) { + return; + } + final ManagedNotificationProducer producer = registry.getNotificationQNameToProducer().get(newSub); + if(alreadyStartedProducers.contains(producer)) { + return; + } + LOG.debug("Starting notification producer: {}", producer); + producer.start(collector); + alreadyStartedProducers.add(producer); + }); + + deletedSubscriptions.stream().forEach(newSub -> { + checkState(registry.getNotificationQNameToProducer().containsKey(newSub)); + final ManagedNotificationProducer producer = registry.getNotificationQNameToProducer().get(newSub); + checkState(alreadyStartedProducers.contains(producer)); + LOG.debug("Stopping notification producer: {}", producer); + producer.stop(); + alreadyStartedProducers.remove(producer); + }); + + } + + private Set<QName> getStartedQNames(final Set<ManagedNotificationProducer> alreadyStartedProducers) { + return alreadyStartedProducers.stream() + .flatMap(p -> registry.getNotificationProducerQNames().get(p).stream()) + .collect(Collectors.toSet()); + } + + @Override + public synchronized void close() throws Exception { + LOG.trace("Closing"); + subscriptionListener.close(); + // Stop all producers + LOG.debug("Stopping all producers: {}", alreadyStartedProducers); + alreadyStartedProducers.forEach(ManagedNotificationProducer::stop); + alreadyStartedProducers.clear(); + } +} diff --git a/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModule.java b/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModule.java new file mode 100644 index 000000000..9a9c7def0 --- /dev/null +++ b/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModule.java @@ -0,0 +1,27 @@ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; + +import org.opendaylight.controller.config.api.JmxAttributeValidationException; +import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter; + +public class HoneycombDomNotificationServiceModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombDomNotificationServiceModule { + public HoneycombDomNotificationServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public HoneycombDomNotificationServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.HoneycombDomNotificationServiceModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + JmxAttributeValidationException.checkCondition(getQueueDepth() > 0, "Queue depth must be > 0", queueDepthJmxAttribute); + } + + @Override + public java.lang.AutoCloseable createInstance() { + // Create DOMNotificationRouter to do the heavy lifting for HoneycombNotificationCollector + // It creates executor internally + return DOMNotificationRouter.create(getQueueDepth()); + } + +} diff --git a/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModuleFactory.java b/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModuleFactory.java new file mode 100644 index 000000000..d3603acb3 --- /dev/null +++ b/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: notification-impl yang module local name: honeycomb-dom-notification-service +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Wed Jun 08 09:49:08 CEST 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; +public class HoneycombDomNotificationServiceModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombDomNotificationServiceModuleFactory { + +} diff --git a/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java b/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java new file mode 100644 index 000000000..4a9440cbc --- /dev/null +++ b/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java @@ -0,0 +1,93 @@ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; + +import io.fd.honeycomb.v3po.notification.NotificationCollector; +import io.fd.honeycomb.v3po.notification.NotificationProducer; +import io.fd.honeycomb.v3po.notification.impl.HoneycombNotificationCollector; +import io.fd.honeycomb.v3po.notification.impl.NotificationProducerRegistry; +import io.fd.honeycomb.v3po.notification.impl.NotificationProducerTracker; +import java.util.Collection; +import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationPublishServiceAdapter; +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; +import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter; +import org.opendaylight.yangtools.yang.binding.Notification; + +public class HoneycombNotificationManagerModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombNotificationManagerModule { + + public HoneycombNotificationManagerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public HoneycombNotificationManagerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.HoneycombNotificationManagerModule 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 DOMNotificationRouter notificationRouter = getDomNotificationServiceDependency(); + + // Create the registry to keep track of what's registered + final NotificationProducerRegistry notificationProducerRegistry = + new NotificationProducerRegistry(getNotificationProducersDependency()); + + // Create BA version of notification service (implementation is free from ODL) + final BindingToNormalizedNodeCodec codec = getRuntimeMappingCodecDependency(); + final BindingDOMNotificationPublishServiceAdapter bindingDOMNotificationPublishServiceAdapter = + new BindingDOMNotificationPublishServiceAdapter(codec, notificationRouter); + + // Create Collector on top of BA notification service and registry + final HoneycombNotificationCollector honeycombNotificationCollector = + new HoneycombNotificationCollector(bindingDOMNotificationPublishServiceAdapter, notificationProducerRegistry); + + // Create tracker, responsible for starting and stopping registered notification producers whenever necessary + final NotificationProducerTracker notificationProducerTracker = + new NotificationProducerTracker(notificationProducerRegistry, honeycombNotificationCollector, + notificationRouter); + + // TODO wire with restconf + // DOMNotificationService is already provided by DOMBroker injected into RESTCONF, however RESTCONF + // only supports data-change notification, nothing else. So currently its impossible. + + return new CloseableCollector(honeycombNotificationCollector, () -> { + // Close all resources in order opposite to instantiation + notificationProducerTracker.close(); + honeycombNotificationCollector.close(); + bindingDOMNotificationPublishServiceAdapter.close(); + // notificationProducerRegistry; no close, it's just a collection + }); + } + + /** + * NotificationCollector wrapper in which close method execution can be injected + */ + private class CloseableCollector implements AutoCloseable, NotificationCollector, NotificationProducer { + + private final HoneycombNotificationCollector honeycombNotificationCollector; + private final AutoCloseable resources; + + CloseableCollector(final HoneycombNotificationCollector honeycombNotificationCollector, + final AutoCloseable resources) { + this.honeycombNotificationCollector = honeycombNotificationCollector; + this.resources = resources; + } + + @Override + public void close() throws Exception { + resources.close(); + } + + @Override + public void onNotification(final Notification notification) { + honeycombNotificationCollector.onNotification(notification); + } + + @Override + public Collection<Class<? extends Notification>> getNotificationTypes() { + return honeycombNotificationCollector.getNotificationTypes(); + } + } +} diff --git a/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModuleFactory.java b/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModuleFactory.java new file mode 100644 index 000000000..b12b70017 --- /dev/null +++ b/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: notification-impl yang module local name: honeycomb-notification-manager +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Wed Jun 01 16:08:01 CEST 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; +public class HoneycombNotificationManagerModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombNotificationManagerModuleFactory { + +} diff --git a/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java b/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java new file mode 100644 index 000000000..4d85d64c5 --- /dev/null +++ b/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java @@ -0,0 +1,157 @@ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; + +import com.google.common.annotations.VisibleForTesting; +import io.fd.honeycomb.v3po.notification.NotificationCollector; +import io.fd.honeycomb.v3po.notification.impl.NotificationProducerRegistry; +import java.io.IOException; +import java.util.Set; +import java.util.stream.Collectors; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.dom.DOMResult; +import org.opendaylight.controller.config.api.JmxAttributeValidationException; +import org.opendaylight.controller.config.util.xml.XmlUtil; +import org.opendaylight.controller.md.sal.dom.api.DOMNotification; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.netconf.notifications.NetconfNotification; +import org.opendaylight.netconf.notifications.NotificationPublisherRegistration; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.StreamNameType; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.streams.StreamBuilder; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class HoneycombNotificationToNetconfTranslatorModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombNotificationToNetconfTranslatorModule { + + private static final Logger LOG = LoggerFactory.getLogger(HoneycombNotificationToNetconfTranslatorModule.class); + + public HoneycombNotificationToNetconfTranslatorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public HoneycombNotificationToNetconfTranslatorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.HoneycombNotificationToNetconfTranslatorModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + JmxAttributeValidationException.checkCondition(!getNetconfStreamName().isEmpty(), + "Stream name cannot be empty", netconfStreamNameJmxAttribute); + JmxAttributeValidationException.checkCondition(!getNetconfStreamDescription().isEmpty(), + "Stream description cannot be empty", netconfStreamDescriptionJmxAttribute); + } + + @Override + public java.lang.AutoCloseable createInstance() { + final SchemaService schemaService = getSchemaServiceDependency(); + final StreamNameType streamType = new StreamNameType(getNetconfStreamName()); + final NotificationCollector hcNotificationCollector = getHoneycombNotificationCollectorDependency(); + + // Register as NETCONF notification publisher under configured name + final NotificationPublisherRegistration netconfNotificationProducerReg = + getNetconfNotificationCollectorDependency().registerNotificationPublisher(new StreamBuilder() + .setName(streamType) + .setReplaySupport(false) + .setDescription(getNetconfStreamDescription()).build()); + + // Notification Translator, get notification from HC producers and put into NETCONF notification collector + final DOMNotificationListener domNotificationListener = + notification -> { + LOG.debug("Propagating notification: {} into NETCONF", notification.getType()); + netconfNotificationProducerReg.onNotification(streamType, notificationToXml(notification, schemaService.getGlobalContext())); + }; + + // NotificationManager is used to provide list of available notifications (which are all of the notifications registered) + // TODO make available notifications configurable here so that any number of notification streams for NETCONF + // can be configured on top of a single notification manager + LOG.debug("Current notifications to be exposed over NETCONF: {}", hcNotificationCollector.getNotificationTypes()); + final Set<SchemaPath> currentNotificationSchemaPaths = hcNotificationCollector.getNotificationTypes() + .stream() + .map(NotificationProducerRegistry::getQName) + .map(qName -> SchemaPath.create(true, qName)) + .collect(Collectors.toSet()); + + // Register as listener to HC's DOM notification service + // TODO This should only be triggered when NETCONF notifications are activated + // Because this way we actually start all notification producers + // final Collection<QName> notificationQNames = + final ListenerRegistration<DOMNotificationListener> domNotificationListenerReg = getDomNotificationServiceDependency() + .registerNotificationListener(domNotificationListener, currentNotificationSchemaPaths); + + LOG.info("Exposing NETCONF notification stream: {}", streamType.getValue()); + return () -> { + domNotificationListenerReg.close(); + netconfNotificationProducerReg.close(); + }; + } + + @VisibleForTesting + static NetconfNotification notificationToXml(final DOMNotification domNotification, final SchemaContext ctx) { + LOG.trace("Transforming notification: {} into XML", domNotification.getType()); + + final SchemaPath type = domNotification.getType(); + final QName notificationQName = type.getLastComponent(); + final DOMResult result = prepareDomResultForRpcRequest(notificationQName); + + try { + writeNormalizedRpc(domNotification, result, type, ctx); + } catch (final XMLStreamException | IOException | IllegalStateException e) { + LOG.warn("Unable to transform notification: {} into XML", domNotification.getType(), e); + throw new IllegalArgumentException("Unable to serialize " + type, e); + } + + final Document node = result.getNode().getOwnerDocument(); + return new NetconfNotification(node); + } + + private static DOMResult prepareDomResultForRpcRequest(final QName notificationQName) { + final Document document = XmlUtil.newDocument(); + final Element notificationElement = + document.createElementNS(notificationQName.getNamespace().toString(), notificationQName.getLocalName()); + document.appendChild(notificationElement); + return new DOMResult(notificationElement); + } + + private static final XMLOutputFactory XML_FACTORY; + + static { + XML_FACTORY = XMLOutputFactory.newFactory(); + XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false); + } + + private static void writeNormalizedRpc(final DOMNotification normalized, final DOMResult result, + final SchemaPath schemaPath, final SchemaContext baseNetconfCtx) + throws IOException, XMLStreamException { + final XMLStreamWriter writer = XML_FACTORY.createXMLStreamWriter(result); + try { + try (final NormalizedNodeStreamWriter normalizedNodeStreamWriter = + XMLStreamNormalizedNodeStreamWriter.create(writer, baseNetconfCtx, schemaPath)) { + try (final NormalizedNodeWriter normalizedNodeWriter = + NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter)) { + for (DataContainerChild<?, ?> dataContainerChild : normalized.getBody().getValue()) { + normalizedNodeWriter.write(dataContainerChild); + } + normalizedNodeWriter.flush(); + } + } + } finally { + try { + writer.close(); + } catch (final Exception e) { + LOG.warn("Unable to close resource properly. Ignoring", e); + } + } + } + +} diff --git a/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModuleFactory.java b/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModuleFactory.java new file mode 100644 index 000000000..336223040 --- /dev/null +++ b/v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: notification-impl yang module local name: honeycomb-notification-to-netconf-translator +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Thu Jun 02 14:39:23 CEST 2016 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; +public class HoneycombNotificationToNetconfTranslatorModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombNotificationToNetconfTranslatorModuleFactory { + +} diff --git a/v3po/notification/impl/src/main/yang/notification-impl.yang b/v3po/notification/impl/src/main/yang/notification-impl.yang new file mode 100644 index 000000000..84899751c --- /dev/null +++ b/v3po/notification/impl/src/main/yang/notification-impl.yang @@ -0,0 +1,141 @@ +module notification-impl { + yang-version 1; + namespace "urn:honeycomb:params:xml:ns:yang:notification:impl"; + prefix "hc-notif-i"; + + import config { prefix config; revision-date 2013-04-05; } + import netconf-northbound-notification { prefix notify-api; revision-date 2015-08-06; } + import notification-api { prefix hc-notif-a; revision-date 2016-06-01; } + import opendaylight-md-sal-dom { prefix dom; revision-date 2013-10-28;} + import opendaylight-sal-binding-broker-impl { prefix binding-impl; revision-date 2013-10-28;} + + description + "Module definition for honeycomb notification implementations"; + + revision "2016-06-01" { + description + "Initial revision"; + } + + identity honeycomb-notification-manager { + base config:module-type; + config:java-name-prefix HoneycombNotificationManager; + config:provided-service hc-notif-a:honeycomb-notification-collector; + } + + augment "/config:modules/config:module/config:configuration" { + case honeycomb-notification-manager { + when "/config:modules/config:module/config:type = 'honeycomb-notification-manager'"; + + container runtime-mapping-codec { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity binding-impl:binding-dom-mapping-service; + } + } + } + + container dom-notification-service { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity hc-notif-a:dom-notification-service; + } + } + } + + list notification-producers { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity hc-notif-a:honeycomb-notification-producer; + } + } + } + + } + } + + identity honeycomb-dom-notification-service { + base config:module-type; + config:provided-service hc-notif-a:dom-notification-service; + } + + augment "/config:modules/config:module/config:configuration" { + case honeycomb-dom-notification-service { + when "/config:modules/config:module/config:type = 'honeycomb-dom-notification-service'"; + + leaf queue-depth { + type uint16; + description "Size of the queue for outgoing notifications"; + } + } + } + + identity honeycomb-notification-to-netconf-translator { + base config:module-type; + config:java-name-prefix HoneycombNotificationToNetconfTranslator; + } + + augment "/config:modules/config:module/config:configuration" { + case honeycomb-notification-to-netconf-translator { + when "/config:modules/config:module/config:type = 'honeycomb-notification-to-netconf-translator'"; + + container netconf-notification-collector { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity notify-api:netconf-notification-collector; + } + } + } + container honeycomb-notification-collector { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity hc-notif-a:honeycomb-notification-collector; + } + } + } + container netconf-notification-registry { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity notify-api:netconf-notification-registry; + } + } + } + container schema-service { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity dom:schema-service; + } + } + } + container dom-notification-service { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity hc-notif-a:dom-notification-service; + } + } + } + + leaf netconf-stream-name { + type string; + mandatory true; + description "Name of the stream under which all the registered notifications should be emitted"; + } + + leaf netconf-stream-description { + type string; + mandatory true; + description "Description for the stream under which all the registered notifications should be emitted"; + } + + } + } + +} diff --git a/v3po/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java b/v3po/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java new file mode 100644 index 000000000..f55d3abdf --- /dev/null +++ b/v3po/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.v3po.notification.impl; + +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.verify; + +import com.google.common.collect.Lists; +import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStart; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStartBuilder; + +public class HoneycombNotificationCollectorTest { + + private NotificationProducerRegistry notificationRegistry; + @Mock + private NotificationPublishService notificationService; + @Mock + private ManagedNotificationProducer producer; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + notificationRegistry = new NotificationProducerRegistry(Lists.newArrayList(producer)); + } + + @Test + public void testNotificationTypes() throws Exception { + final HoneycombNotificationCollector honeycombNotificationCollector = + new HoneycombNotificationCollector(notificationService, notificationRegistry); + + honeycombNotificationCollector.getNotificationTypes(); + verify(producer, atLeast(1)).getNotificationTypes(); + } + + @Test + public void testCollect() throws Exception { + final HoneycombNotificationCollector honeycombNotificationCollector = + new HoneycombNotificationCollector(notificationService, notificationRegistry); + + final NetconfSessionStart notif = new NetconfSessionStartBuilder().build(); + honeycombNotificationCollector.onNotification(notif); + verify(notificationService).putNotification(notif); + } +}
\ No newline at end of file diff --git a/v3po/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java b/v3po/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java new file mode 100644 index 000000000..5fdf502b9 --- /dev/null +++ b/v3po/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.v3po.notification.impl; + +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.mockito.Mockito.doReturn; + +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +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.NetconfSessionEnd; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStart; +import org.opendaylight.yangtools.yang.binding.Notification; +import org.opendaylight.yangtools.yang.common.QName; + +public class NotificationProducerRegistryTest { + + @Mock + private ManagedNotificationProducer producer; + @Mock + private ManagedNotificationProducer producer2; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + doReturn(Collections.singleton(NetconfCapabilityChange.class)) + .when(producer).getNotificationTypes(); + final ArrayList<Object> producer2Notifications = Lists.newArrayList(); + producer2Notifications.add(NetconfSessionStart.class); + producer2Notifications.add(NetconfSessionEnd.class); + doReturn(producer2Notifications).when(producer2).getNotificationTypes(); + } + + @Test + public void testNotificationTypes() throws Exception { + final NotificationProducerRegistry notificationRegistry = + new NotificationProducerRegistry(Lists.newArrayList(producer, producer2)); + + final Set<Class<? extends Notification>> notificationTypes = + notificationRegistry.getNotificationTypes(); + + Assert.assertThat(notificationTypes, hasItem(NetconfSessionEnd.class)); + Assert.assertThat(notificationTypes, hasItem(NetconfSessionStart.class)); + Assert.assertThat(notificationTypes, hasItem(NetconfCapabilityChange.class)); + } + + @Test + public void testNotificationTypesMapped() throws Exception { + final NotificationProducerRegistry notificationRegistry = + new NotificationProducerRegistry(Lists.newArrayList(producer, producer2)); + + final Multimap<ManagedNotificationProducer, QName> notificationTypes = + notificationRegistry.getNotificationProducerQNames(); + + Assert.assertThat(notificationTypes.keySet(), hasItem(producer)); + Assert.assertThat(notificationTypes.get(producer), hasItem(NetconfCapabilityChange.QNAME)); + Assert.assertThat(notificationTypes.keySet(), hasItem(producer2)); + Assert.assertThat(notificationTypes.get(producer2), hasItem(NetconfSessionStart.QNAME)); + Assert.assertThat(notificationTypes.get(producer2), hasItem(NetconfSessionEnd.QNAME)); + + final Map<QName, ManagedNotificationProducer> notificationQNameToProducer = + notificationRegistry.getNotificationQNameToProducer(); + + Assert.assertThat(notificationQNameToProducer.keySet(), hasItem(NetconfCapabilityChange.QNAME)); + Assert.assertThat(notificationQNameToProducer.get(NetconfCapabilityChange.QNAME), is(producer)); + + Assert.assertThat(notificationQNameToProducer.keySet(), hasItem(NetconfSessionStart.QNAME)); + Assert.assertThat(notificationQNameToProducer.keySet(), hasItem(NetconfSessionEnd.QNAME)); + Assert.assertThat(notificationQNameToProducer.get(NetconfSessionStart.QNAME), is(producer2)); + Assert.assertThat(notificationQNameToProducer.get(NetconfSessionEnd.QNAME), is(producer2)); + + + } +}
\ No newline at end of file diff --git a/v3po/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java b/v3po/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java new file mode 100644 index 000000000..b62bf0709 --- /dev/null +++ b/v3po/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fd.honeycomb.v3po.notification.impl; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; +import io.fd.honeycomb.v3po.notification.NotificationCollector; +import java.util.Collections; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListenerRegistry; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStart; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +public class NotificationProducerTrackerTest { + + private NotificationProducerRegistry registry; + @Mock + private DOMNotificationSubscriptionListenerRegistry subscriptionRegistry; + @Mock + private NotificationCollector collector; + @Mock + private ManagedNotificationProducer producer; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + doReturn(Collections.singleton(NetconfSessionStart.class)).when(producer).getNotificationTypes(); + registry = new NotificationProducerRegistry(Lists.newArrayList(producer)); + } + + @Test + public void name() throws Exception { + final NotificationProducerTracker notificationProducerTracker = + new NotificationProducerTracker(registry, collector, subscriptionRegistry); + verify(subscriptionRegistry).registerSubscriptionListener(notificationProducerTracker); + + final Set<SchemaPath> subscriptions = Sets.newHashSet(); + subscriptions.add(SchemaPath.create(true, NetconfSessionStart.QNAME)); + notificationProducerTracker.onSubscriptionChanged(subscriptions); + + verify(producer).start(collector); + + notificationProducerTracker.onSubscriptionChanged(Sets.newHashSet()); + verify(producer).stop(); + } +}
\ No newline at end of file diff --git a/v3po/notification/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/NoetificationToNetconfModuleTest.java b/v3po/notification/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/NoetificationToNetconfModuleTest.java new file mode 100644 index 000000000..ccfb4bb9d --- /dev/null +++ b/v3po/notification/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/NoetificationToNetconfModuleTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601; + +import javax.annotation.Nonnull; +import org.hamcrest.CoreMatchers; +import org.junit.Assert; +import org.junit.Test; +import org.opendaylight.controller.md.sal.dom.api.DOMNotification; +import org.opendaylight.netconf.notifications.NetconfNotification; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.*; +import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; +import org.opendaylight.yangtools.yang.binding.YangModuleInfo; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +public class NoetificationToNetconfModuleTest { + + private final DOMNotification notification = new DOMNotification() { + + private QName qname = NetconfSessionStart.QNAME; + private YangInstanceIdentifier.NodeIdentifier nodeIdentifier = + new YangInstanceIdentifier.NodeIdentifier(NetconfSessionStart.QNAME); + + @Nonnull + @Override + public SchemaPath getType() { + return SchemaPath.create(true, qname); + } + + @Nonnull + @Override + public ContainerNode getBody() { + return Builders.containerBuilder() + .withNodeIdentifier(nodeIdentifier) + .withChild(ImmutableNodes.leafNode(QName.create(qname, "username"), "user")) + .withChild(ImmutableNodes.leafNode(QName.create(qname, "session-id"), 1)) + .withChild(ImmutableNodes.leafNode(QName.create(qname, "source-host"), "127.0.0.1")) + .build(); + } + }; + + @Test + public void notificationToXml() throws Exception { + final ModuleInfoBackedContext moduleInfoBackedContext = getModuleInfoBackedCOntext(); + + final NetconfNotification netconfNotification = HoneycombNotificationToNetconfTranslatorModule + .notificationToXml(notification, moduleInfoBackedContext.getSchemaContext()); + + final String notificationString = netconfNotification.toString(); + Assert.assertThat(notificationString, CoreMatchers.containsString("<netconf-session-start")); + Assert.assertThat(notificationString, CoreMatchers.containsString("<username>user</username>")); + Assert.assertThat(notificationString, CoreMatchers.containsString("eventTime")); + } + + private static ModuleInfoBackedContext getModuleInfoBackedCOntext() { + final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create(); + final YangModuleInfo ietfNetconfNotifModuleInfo = + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.$YangModuleInfoImpl + .getInstance(); + moduleInfoBackedContext.registerModuleInfo(ietfNetconfNotifModuleInfo); + return moduleInfoBackedContext; + } +}
\ No newline at end of file diff --git a/v3po/notification/pom.xml b/v3po/notification/pom.xml new file mode 100644 index 000000000..07f09cd55 --- /dev/null +++ b/v3po/notification/pom.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2015 Cisco and/or its affiliates. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<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"> + + <parent> + <groupId>io.fd.honeycomb.v3po</groupId> + <artifactId>v3po-aggregator</artifactId> + <version>1.0.0-SNAPSHOT</version> + <relativePath/> + </parent> + + <artifactId>notification-aggregator</artifactId> + <version>1.0.0-SNAPSHOT</version> + <name>notification</name> + <packaging>pom</packaging> + <modelVersion>4.0.0</modelVersion> + <prerequisites> + <maven>3.1.1</maven> + </prerequisites> + <modules> + <module>api</module> + <module>impl</module> + </modules> + <!-- DO NOT install or deploy the repo root pom as it's only needed to initiate a build --> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-install-plugin</artifactId> + <configuration> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </build> +</project> |