From 0578156b721fa01c8c645b8f9625ecebdb6449e4 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Wed, 27 Jul 2016 11:05:51 +0200 Subject: HONEYCOMB-130: Separate v3po plugin from HC infra Creating folders: - common/ - infra/ - v3po/ - vpp-common/ Change-Id: I2c39e1b17e39e7c0f0628f44aa5fe08563fa06e4 Signed-off-by: Maros Marsalek --- infra/notification/api/pom.xml | 40 ++++++ .../notification/ManagedNotificationProducer.java | 38 +++++ .../v3po/notification/NotificationCollector.java | 35 +++++ .../v3po/notification/NotificationProducer.java | 37 +++++ .../api/src/main/yang/notification-api.yang | 31 ++++ infra/notification/impl/pom.xml | 92 ++++++++++++ .../impl/src/main/config/default-config.xml | 71 ++++++++++ .../main/config/notification-to-netconf-config.xml | 58 ++++++++ .../impl/HoneycombNotificationCollector.java | 66 +++++++++ .../impl/NotificationProducerRegistry.java | 112 +++++++++++++++ .../impl/NotificationProducerTracker.java | 109 ++++++++++++++ .../HoneycombDomNotificationServiceModule.java | 27 ++++ ...neycombDomNotificationServiceModuleFactory.java | 13 ++ .../HoneycombNotificationManagerModule.java | 93 ++++++++++++ .../HoneycombNotificationManagerModuleFactory.java | 13 ++ ...ycombNotificationToNetconfTranslatorModule.java | 157 +++++++++++++++++++++ ...tificationToNetconfTranslatorModuleFactory.java | 13 ++ .../impl/src/main/yang/notification-impl.yang | 141 ++++++++++++++++++ .../impl/HoneycombNotificationCollectorTest.java | 63 +++++++++ .../impl/NotificationProducerRegistryTest.java | 98 +++++++++++++ .../impl/NotificationProducerTrackerTest.java | 67 +++++++++ .../NoetificationToNetconfModuleTest.java | 81 +++++++++++ infra/notification/pom.xml | 56 ++++++++ 23 files changed, 1511 insertions(+) create mode 100644 infra/notification/api/pom.xml create mode 100644 infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java create mode 100644 infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java create mode 100644 infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java create mode 100644 infra/notification/api/src/main/yang/notification-api.yang create mode 100644 infra/notification/impl/pom.xml create mode 100644 infra/notification/impl/src/main/config/default-config.xml create mode 100644 infra/notification/impl/src/main/config/notification-to-netconf-config.xml create mode 100644 infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java create mode 100644 infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java create mode 100644 infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java create mode 100644 infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModule.java create mode 100644 infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModuleFactory.java create mode 100644 infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java create mode 100644 infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModuleFactory.java create mode 100644 infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java create mode 100644 infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModuleFactory.java create mode 100644 infra/notification/impl/src/main/yang/notification-impl.yang create mode 100644 infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java create mode 100644 infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java create mode 100644 infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java create mode 100644 infra/notification/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/NoetificationToNetconfModuleTest.java create mode 100644 infra/notification/pom.xml (limited to 'infra/notification') diff --git a/infra/notification/api/pom.xml b/infra/notification/api/pom.xml new file mode 100644 index 000000000..2291025f9 --- /dev/null +++ b/infra/notification/api/pom.xml @@ -0,0 +1,40 @@ + + + + + io.fd.honeycomb.common + impl-parent + 1.0.0-SNAPSHOT + ../../../common/impl-parent + + + 4.0.0 + io.fd.honeycomb + notification-api + 1.0.0-SNAPSHOT + bundle + + + + org.opendaylight.mdsal + mdsal-dom-api + 2.0.2-Beryllium-SR2 + + + + + diff --git a/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java b/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java new file mode 100644 index 000000000..0f5e28cde --- /dev/null +++ b/infra/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/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java b/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java new file mode 100644 index 000000000..406ab03d2 --- /dev/null +++ b/infra/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/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java b/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java new file mode 100644 index 000000000..dab773c45 --- /dev/null +++ b/infra/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> getNotificationTypes(); +} diff --git a/infra/notification/api/src/main/yang/notification-api.yang b/infra/notification/api/src/main/yang/notification-api.yang new file mode 100644 index 000000000..4e6eb98ae --- /dev/null +++ b/infra/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/infra/notification/impl/pom.xml b/infra/notification/impl/pom.xml new file mode 100644 index 000000000..9fa542422 --- /dev/null +++ b/infra/notification/impl/pom.xml @@ -0,0 +1,92 @@ + + + + + io.fd.honeycomb.common + impl-parent + 1.0.0-SNAPSHOT + ../../../common/impl-parent + + + 4.0.0 + io.fd.honeycomb + notification-impl + 1.0.0-SNAPSHOT + bundle + + + + + ${project.groupId} + notification-api + ${project.version} + + + org.opendaylight.controller + sal-binding-config + 1.3.2-Beryllium-SR2 + + + + org.opendaylight.netconf + netconf-notifications-api + 1.0.2-Beryllium-SR2 + + + junit + junit + test + + + org.mockito + mockito-all + test + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + + attach-artifact + + package + + + + ${config.file} + xml + config + + + src/main/config/notification-to-netconf-config.xml + xml + notification2netconf + + + + + + + + + diff --git a/infra/notification/impl/src/main/config/default-config.xml b/infra/notification/impl/src/main/config/default-config.xml new file mode 100644 index 000000000..2b91de47f --- /dev/null +++ b/infra/notification/impl/src/main/config/default-config.xml @@ -0,0 +1,71 @@ + + + + + + urn:honeycomb:params:xml:ns:yang:notification:impl?module=notification-impl&revision=2016-06-01 + + + + + + + + prefix:honeycomb-dom-notification-service + honeycomb-dom-notification-service + 1024 + + + + + prefix:honeycomb-notification-manager + honeycomb-notification-manager + + binding-impl:binding-dom-mapping-service + runtime-mapping-singleton + + + prefix:dom-notification-service + honeycomb-dom-notification-service + + + + + + + + + + + prefix:dom-notification-service + + honeycomb-dom-notification-service + /modules/module[type='honeycomb-dom-notification-service'][name='honeycomb-dom-notification-service'] + + + + prefix:honeycomb-notification-collector + + honeycomb-notification-manager + /modules/module[type='honeycomb-notification-manager'][name='honeycomb-notification-manager'] + + + + + + + + diff --git a/infra/notification/impl/src/main/config/notification-to-netconf-config.xml b/infra/notification/impl/src/main/config/notification-to-netconf-config.xml new file mode 100644 index 000000000..d2aac0932 --- /dev/null +++ b/infra/notification/impl/src/main/config/notification-to-netconf-config.xml @@ -0,0 +1,58 @@ + + + + + + urn:honeycomb:params:xml:ns:yang:notification:impl?module=notification-impl&revision=2016-06-01 + + + + + + + prefix:honeycomb-notification-to-netconf-translator + honeycomb-notification-to-netconf-translator + + + prefix:netconf-notification-registry + + vpp-netconf-notification-manager + + + + prefix:netconf-notification-collector + + vpp-netconf-notification-manager + + + dom:schema-service + yang-schema-service + + + prefix:honeycomb-notification-collector + honeycomb-notification-manager + + + prefix:dom-notification-service + honeycomb-dom-notification-service + + honeycomb + All notifications received by honeycomb's plugins + + + + + diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java new file mode 100644 index 000000000..e7d54e318 --- /dev/null +++ b/infra/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> getNotificationTypes() { + return notificationProducerRegistry.getNotificationTypes(); + } +} diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java new file mode 100644 index 000000000..8fba700bd --- /dev/null +++ b/infra/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> notificationTypes; + private final Map notificationQNameToProducer; + private final Multimap notificationProducerQNames; + + public NotificationProducerRegistry(final List notificationProducersDependency) { + this.notificationTypes = toTypes(notificationProducersDependency); + this.notificationQNameToProducer = toQNameMap(notificationProducersDependency); + this.notificationProducerQNames = toQNameMapReversed(notificationProducersDependency); + } + + private static Multimap toQNameMapReversed(final List notificationProducers) { + final Multimap multimap = HashMultimap.create(); + + for (ManagedNotificationProducer producer : notificationProducers) { + for (Class aClass : producer.getNotificationTypes()) { + multimap.put(producer, getQName(aClass)); + } + } + return multimap; + } + + private static Set> toTypes(final List notificationProducersDependency) { + // Get all notification types registered from HC notification producers + return notificationProducersDependency + .stream() + .flatMap(producer -> producer.getNotificationTypes().stream()) + .collect(Collectors.toSet()); + } + + + private static Map toQNameMap(final List producerDependencies) { + // Only a single notification producer per notification type is allowed + final Map 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 typesToQNames(final Collection> notificationTypes) { + return notificationTypes + .stream() + .map(NotificationProducerRegistry::getQName) + .collect(Collectors.toSet()); + } + + + public static QName getQName(final Class 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> getNotificationTypes() { + return notificationTypes; + } + + Map getNotificationQNameToProducer() { + return notificationQNameToProducer; + } + + Multimap getNotificationProducerQNames() { + return notificationProducerQNames; + } +} diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java new file mode 100644 index 000000000..cefb50ac9 --- /dev/null +++ b/infra/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 subscriptionListener; + private final NotificationProducerRegistry registry; + private final NotificationCollector collector; + + private final Set 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 set) { + LOG.debug("Subscriptions changed. Current subscriptions: {}", set); + final Set currentSubscriptions = set.stream().map(SchemaPath::getLastComponent).collect(Collectors.toSet()); + final Set startedQNames = getStartedQNames(alreadyStartedProducers); + final Sets.SetView newSubscriptions = Sets.difference(currentSubscriptions, startedQNames); + LOG.debug("Subscriptions changed. New subscriptions: {}", newSubscriptions); + final Sets.SetView 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 getStartedQNames(final Set 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/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModule.java b/infra/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/infra/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/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModuleFactory.java b/infra/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/infra/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/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java b/infra/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/infra/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> getNotificationTypes() { + return honeycombNotificationCollector.getNotificationTypes(); + } + } +} diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModuleFactory.java b/infra/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/infra/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/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java b/infra/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/infra/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 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 notificationQNames = + final ListenerRegistration 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/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModuleFactory.java b/infra/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/infra/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/infra/notification/impl/src/main/yang/notification-impl.yang b/infra/notification/impl/src/main/yang/notification-impl.yang new file mode 100644 index 000000000..84899751c --- /dev/null +++ b/infra/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/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java b/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java new file mode 100644 index 000000000..f55d3abdf --- /dev/null +++ b/infra/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/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java b/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java new file mode 100644 index 000000000..5fdf502b9 --- /dev/null +++ b/infra/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 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> 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 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 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/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java b/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java new file mode 100644 index 000000000..b62bf0709 --- /dev/null +++ b/infra/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 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/infra/notification/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/NoetificationToNetconfModuleTest.java b/infra/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/infra/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("user")); + 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/infra/notification/pom.xml b/infra/notification/pom.xml new file mode 100644 index 000000000..81b8aae40 --- /dev/null +++ b/infra/notification/pom.xml @@ -0,0 +1,56 @@ + + + + + + org.opendaylight.odlparent + odlparent + 1.6.2-Beryllium-SR2 + + + + notification-aggregator + 1.0.0-SNAPSHOT + notification + pom + 4.0.0 + + 3.1.1 + + + api + impl + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-install-plugin + + true + + + + + -- cgit 1.2.3-korg