From 0e517a26e69ff3d1b04ba19c4539cb8dee60b097 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Sat, 9 Apr 2016 16:22:06 +0200 Subject: 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 --- .../rev150105/InterfaceNameOrIndexBuilder.java | 19 ++ v3po/api/src/main/yang/v3po.yang | 40 ++++ v3po/artifacts/pom.xml | 15 ++ v3po/features/pom.xml | 24 +++ v3po/features/src/main/features/features.xml | 4 + v3po/impl/pom.xml | 5 + .../src/main/config/context-datatree-config.xml | 17 ++ v3po/impl/src/main/config/default-config.xml | 4 + .../impl/NetconfFacadeHoneycombBindingBroker.java | 200 -------------------- .../impl/NorthboundFacadeHoneycombDOMBroker.java | 206 +++++++++++++++++++++ .../ns/yang/v3po/impl/rev141210/V3poModule.java | 5 +- v3po/impl/src/main/yang/v3po-impl.yang | 10 + v3po/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 ++++ v3po/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 ++++++++ v3po/notification/pom.xml | 56 ++++++ v3po/pom.xml | 1 + .../translate-api/src/main/yang/translate-api.yang | 5 + .../rev160406/RealtimeMappingContextModule.java | 84 +++++++++ .../RealtimeMappingContextModuleFactory.java | 13 ++ .../src/main/yang/translate-utils.yang | 21 +++ v3po/v3po2vpp/pom.xml | 5 + v3po/v3po2vpp/src/main/config/default-config.xml | 37 ++++ .../InterfaceChangeNotificationProducer.java | 149 +++++++++++++++ .../VppInterfaceNotificationProducerModule.java | 28 +++ ...InterfaceNotificationProducerModuleFactory.java | 13 ++ v3po/v3po2vpp/src/main/yang/v3po2vpp.yang | 39 ++++ .../InterfaceChangeNotificationProducerTest.java | 130 +++++++++++++ 47 files changed, 2383 insertions(+), 202 deletions(-) create mode 100644 v3po/api/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/rev150105/InterfaceNameOrIndexBuilder.java delete mode 100644 v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/NetconfFacadeHoneycombBindingBroker.java create mode 100644 v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/NorthboundFacadeHoneycombDOMBroker.java create mode 100644 v3po/notification/api/pom.xml create mode 100644 v3po/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java create mode 100644 v3po/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java create mode 100644 v3po/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java create mode 100644 v3po/notification/api/src/main/yang/notification-api.yang create mode 100644 v3po/notification/impl/pom.xml create mode 100644 v3po/notification/impl/src/main/config/default-config.xml create mode 100644 v3po/notification/impl/src/main/config/notification-to-netconf-config.xml create mode 100644 v3po/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java create mode 100644 v3po/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java create mode 100644 v3po/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java create mode 100644 v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModule.java create mode 100644 v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModuleFactory.java create mode 100644 v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java create mode 100644 v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModuleFactory.java create mode 100644 v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java create mode 100644 v3po/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModuleFactory.java create mode 100644 v3po/notification/impl/src/main/yang/notification-impl.yang create mode 100644 v3po/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java create mode 100644 v3po/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java create mode 100644 v3po/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java create mode 100644 v3po/notification/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/NoetificationToNetconfModuleTest.java create mode 100644 v3po/notification/pom.xml create mode 100644 v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModule.java create mode 100644 v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModuleFactory.java create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/notification/InterfaceChangeNotificationProducer.java create mode 100644 v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppInterfaceNotificationProducerModule.java create mode 100644 v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppInterfaceNotificationProducerModuleFactory.java create mode 100644 v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/notification/InterfaceChangeNotificationProducerTest.java (limited to 'v3po') diff --git a/v3po/api/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/rev150105/InterfaceNameOrIndexBuilder.java b/v3po/api/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/rev150105/InterfaceNameOrIndexBuilder.java new file mode 100644 index 000000000..5e9974a5a --- /dev/null +++ b/v3po/api/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/rev150105/InterfaceNameOrIndexBuilder.java @@ -0,0 +1,19 @@ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105; + + +/** + * The purpose of generated class in src/main/java for Union types is to create new instances of unions from a string representation. + * In some cases it is very difficult to automate it since there can be unions such as (uint32 - uint16), or (string - uint32). + * + * The reason behind putting it under src/main/java is: + * This class is generated in form of a stub and needs to be finished by the user. This class is generated only once to prevent + * loss of user code. + * + */ +public class InterfaceNameOrIndexBuilder { + + public static InterfaceNameOrIndex getDefaultInstance(java.lang.String defaultValue) { + throw new java.lang.UnsupportedOperationException("Not yet implemented"); + } + +} diff --git a/v3po/api/src/main/yang/v3po.yang b/v3po/api/src/main/yang/v3po.yang index f35ccdcdf..b1f2e2c56 100644 --- a/v3po/api/src/main/yang/v3po.yang +++ b/v3po/api/src/main/yang/v3po.yang @@ -505,4 +505,44 @@ module v3po { "vlib version info"; } } + + // VPP Notifications + + typedef interface-status { + type enumeration { + enum up { + value 1; + } + enum down { + value 0; + } + } + } + + typedef interface-name-or-index { + type union { + type string; + type uint32; + } + } + + notification interface-state-change { + leaf name { + type interface-name-or-index; + } + + leaf admin-status { + type interface-status; + } + + leaf oper-status { + type interface-status; + } + } + + notification interface-deleted { + leaf name { + type interface-name-or-index; + } + } } diff --git a/v3po/artifacts/pom.xml b/v3po/artifacts/pom.xml index e3ee2da39..95009b682 100644 --- a/v3po/artifacts/pom.xml +++ b/v3po/artifacts/pom.xml @@ -34,6 +34,21 @@ data-api ${project.version} + + ${project.groupId} + notification-api + ${project.version} + + + ${project.groupId} + notification-impl + ${project.version} + + + ${project.groupId} + notification-spi + ${project.version} + ${project.groupId} data-utils diff --git a/v3po/features/pom.xml b/v3po/features/pom.xml index b511e8f11..43b5e83bc 100644 --- a/v3po/features/pom.xml +++ b/v3po/features/pom.xml @@ -164,6 +164,30 @@ data-impl ${project.version} + + ${project.groupId} + notification-api + ${project.version} + + + ${project.groupId} + notification-impl + ${project.version} + + + ${project.groupId} + notification-impl + ${project.version} + xml + config + + + ${project.groupId} + notification-impl + ${project.version} + xml + notification2netconf + ${project.groupId} vpp-jvpp-cfg diff --git a/v3po/features/src/main/features/features.xml b/v3po/features/src/main/features/features.xml index 49208e7fa..89c64726c 100644 --- a/v3po/features/src/main/features/features.xml +++ b/v3po/features/src/main/features/features.xml @@ -42,6 +42,8 @@ mvn:io.fd.honeycomb.v3po/vpp-translate-utils/${project.version} mvn:io.fd.honeycomb.v3po/data-api/${project.version} mvn:io.fd.honeycomb.v3po/data-impl/${project.version} + mvn:io.fd.honeycomb.v3po/notification-api/${project.version} + mvn:io.fd.honeycomb.v3po/notification-impl/${project.version} mvn:io.fd.honeycomb.v3po/translate-impl/${project.version} wrap:mvn:io.fd.vpp/jvpp/16.09-SNAPSHOT mvn:io.fd.honeycomb.v3po/vpp-jvpp-cfg/${project.version} @@ -49,6 +51,7 @@ mvn:io.fd.honeycomb.v3po/vpp-cfg-init/${project.version} mvn:io.fd.honeycomb.v3po/vpp-jvpp-cfg/${project.version}/xml/config mvn:io.fd.honeycomb.v3po/v3po-impl/${project.version}/xml/context + mvn:io.fd.honeycomb.v3po/notification-impl/${project.version}/xml/config mvn:io.fd.honeycomb.v3po/v3po-impl/${project.version}/xml/config mvn:io.fd.honeycomb.v3po/v3po-impl/${project.version}/xml/init mvn:io.fd.honeycomb.v3po/v3po2vpp/${project.version}/xml/config @@ -59,6 +62,7 @@ odl-restconf mvn:io.fd.honeycomb.v3po/v3po-impl/${project.version}/xml/netconf + mvn:io.fd.honeycomb.v3po/notification-impl/${project.version}/xml/notification2netconf mvn:io.fd.honeycomb.v3po/v3po-impl/${project.version}/xml/restconf diff --git a/v3po/impl/pom.xml b/v3po/impl/pom.xml index dc41c5b75..8632f74fe 100644 --- a/v3po/impl/pom.xml +++ b/v3po/impl/pom.xml @@ -34,6 +34,11 @@ v3po-api ${project.version} + + ${project.groupId} + notification-api + ${project.version} + ${project.groupId} translate-impl diff --git a/v3po/impl/src/main/config/context-datatree-config.xml b/v3po/impl/src/main/config/context-datatree-config.xml index a77813daf..02d2f61d4 100644 --- a/v3po/impl/src/main/config/context-datatree-config.xml +++ b/v3po/impl/src/main/config/context-datatree-config.xml @@ -109,6 +109,15 @@ + + + prefix:realtime-mapping-context + realtime-mapping-context + + binding:binding-async-data-broker + honeycomb-context-binding-data-broker + + @@ -148,6 +157,14 @@ /modules/module[type='binding-forwarded-data-broker'][name='honeycomb-context-binding-data-broker'] + + + prefix:honeycomb-mapping-context + + realtime-mapping-context + /modules/module[type='realtime-mapping-context'][name='realtime-mapping-context'] + + diff --git a/v3po/impl/src/main/config/default-config.xml b/v3po/impl/src/main/config/default-config.xml index 2fe385631..00c586d12 100644 --- a/v3po/impl/src/main/config/default-config.xml +++ b/v3po/impl/src/main/config/default-config.xml @@ -161,6 +161,10 @@ prefix:dom-async-data-broker honeycomb-dom-data-broker + + prefix:dom-notification-service + honeycomb-dom-notification-service + diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/NetconfFacadeHoneycombBindingBroker.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/NetconfFacadeHoneycombBindingBroker.java deleted file mode 100644 index 9d70113e4..000000000 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/NetconfFacadeHoneycombBindingBroker.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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. - */ - -package io.fd.honeycomb.v3po.impl; - -import com.google.common.base.Optional; -import com.google.common.collect.Maps; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.Futures; -import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.concurrent.NotThreadSafe; -import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; -import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; -import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; -import org.opendaylight.controller.sal.core.api.Broker; -import org.opendaylight.controller.sal.core.api.BrokerService; -import org.opendaylight.controller.sal.core.api.Consumer; -import org.opendaylight.controller.sal.core.api.Provider; -import org.opendaylight.controller.sal.core.api.model.SchemaService; -import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener; -import org.opendaylight.yangtools.concepts.ListenerRegistration; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.model.api.SchemaPath; -import org.osgi.framework.BundleContext; - -/** - * Implementation of dom broker to facade VPP pipeline for netconf northbound server - */ -public class NetconfFacadeHoneycombBindingBroker implements AutoCloseable, Broker { - - private static final BrokerService EMPTY_DOM_RPC_SERVICE = new EmptyDomRpcService(); - private static final BrokerService EMPTY_DOM_MOUNT_SERVICE = new EmptyDomMountService(); - - private Map, BrokerService> services; - - public NetconfFacadeHoneycombBindingBroker(@Nonnull final DOMDataBroker domDataBrokerDependency, - @Nonnull final SchemaService schemaBiService) { - services = Maps.newHashMap(); - services.put(DOMDataBroker.class, domDataBrokerDependency); - // All services below are required to be present by Restconf northbound - services.put(SchemaService.class, schemaBiService); - services.put(DOMRpcService.class, EMPTY_DOM_RPC_SERVICE); - services.put(DOMMountPointService.class, EMPTY_DOM_MOUNT_SERVICE); - } - - @Override - public void close() throws Exception { - // NOOP - } - - @Override - public ConsumerSession registerConsumer(final Consumer consumer) { - final SimpleConsumerSession session = new SimpleConsumerSession(services); - consumer.onSessionInitiated(session); - return session; - } - - @Deprecated - @Override - public ConsumerSession registerConsumer(final Consumer consumer, final BundleContext bundleContext) { - throw new UnsupportedOperationException(); - } - - @Override - public ProviderSession registerProvider(final Provider provider) { - final SimpleProviderSession session = new SimpleProviderSession(services); - provider.onSessionInitiated(session); - return session; - } - - @Override - public ProviderSession registerProvider(final Provider provider, final BundleContext bundleContext) { - throw new UnsupportedOperationException(); - } - - @NotThreadSafe - private static class SimpleConsumerSession implements ConsumerSession { - private boolean closed; - private final Map, BrokerService> services; - - private SimpleConsumerSession(final Map, BrokerService> services) { - this.services = services; - } - - @Override - public boolean isClosed() { - return closed; - } - - @Override - public T getService(final Class aClass) { - return (T)services.get(aClass); - } - - @Override - public void close() { - closed = true; - } - } - - @NotThreadSafe - private static class SimpleProviderSession implements ProviderSession { - private boolean closed; - private final Map, BrokerService> services; - - private SimpleProviderSession(final Map, BrokerService> services) { - this.services = services; - } - - @Override - public boolean isClosed() { - return closed; - } - - @Override - public T getService(final Class aClass) { - return (T)services.get(aClass); - } - - @Override - public void close() { - closed = true; - } - } - - private static class EmptyDomRpcService implements DOMRpcService { - @Nonnull - @Override - public CheckedFuture invokeRpc(@Nonnull final SchemaPath schemaPath, - @Nullable final NormalizedNode normalizedNode) { - return Futures.immediateFailedCheckedFuture( - new DOMRpcImplementationNotAvailableException("RPCs not supported")); - } - - @Nonnull - @Override - public ListenerRegistration registerRpcListener(@Nonnull final T t) { - return new ListenerRegistration() { - @Override - public void close() { - // Noop - } - - @Override - public T getInstance() { - return t; - } - }; - } - } - - private static class EmptyDomMountService implements DOMMountPointService { - @Override - public Optional getMountPoint(final YangInstanceIdentifier yangInstanceIdentifier) { - return Optional.absent(); - } - - @Override - public DOMMountPointBuilder createMountPoint(final YangInstanceIdentifier yangInstanceIdentifier) { - throw new UnsupportedOperationException("No mountpoint support"); - } - - @Override - public ListenerRegistration registerProvisionListener( - final MountProvisionListener mountProvisionListener) { - return new ListenerRegistration() { - @Override - public void close() { - // Noop - } - - @Override - public MountProvisionListener getInstance() { - return mountProvisionListener; - } - }; - } - } -} diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/NorthboundFacadeHoneycombDOMBroker.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/NorthboundFacadeHoneycombDOMBroker.java new file mode 100644 index 000000000..c2d70c38c --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/NorthboundFacadeHoneycombDOMBroker.java @@ -0,0 +1,206 @@ +/* + * 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. + */ + +package io.fd.honeycomb.v3po.impl; + +import com.google.common.base.Optional; +import com.google.common.collect.Maps; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; +import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationPublishService; +import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.controller.sal.core.api.BrokerService; +import org.opendaylight.controller.sal.core.api.Consumer; +import org.opendaylight.controller.sal.core.api.Provider; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.osgi.framework.BundleContext; + +/** + * Implementation of dom broker to facade VPP pipeline for northbound APIs + */ +public class NorthboundFacadeHoneycombDOMBroker implements AutoCloseable, Broker { + + private static final BrokerService EMPTY_DOM_RPC_SERVICE = new EmptyDomRpcService(); + private static final BrokerService EMPTY_DOM_MOUNT_SERVICE = new EmptyDomMountService(); + + private Map, BrokerService> services; + + public NorthboundFacadeHoneycombDOMBroker(@Nonnull final DOMDataBroker domDataBrokerDependency, + @Nonnull final SchemaService schemaBiService, + @Nonnull final DOMNotificationService domNotificatioNService) { + services = Maps.newHashMap(); + services.put(DOMDataBroker.class, domDataBrokerDependency); + // All services below are required to be present by Restconf northbound + services.put(SchemaService.class, schemaBiService); + services.put(DOMRpcService.class, EMPTY_DOM_RPC_SERVICE); + services.put(DOMMountPointService.class, EMPTY_DOM_MOUNT_SERVICE); + services.put(DOMNotificationService.class, domNotificatioNService); + // TODO do both notification service types have to be registered ? + services.put(DOMNotificationPublishService.class, domNotificatioNService); + } + + @Override + public void close() throws Exception { + // NOOP + } + + @Override + public ConsumerSession registerConsumer(final Consumer consumer) { + final SimpleConsumerSession session = new SimpleConsumerSession(services); + consumer.onSessionInitiated(session); + return session; + } + + @Deprecated + @Override + public ConsumerSession registerConsumer(final Consumer consumer, final BundleContext bundleContext) { + throw new UnsupportedOperationException(); + } + + @Override + public ProviderSession registerProvider(final Provider provider) { + final SimpleProviderSession session = new SimpleProviderSession(services); + provider.onSessionInitiated(session); + return session; + } + + @Override + public ProviderSession registerProvider(final Provider provider, final BundleContext bundleContext) { + throw new UnsupportedOperationException(); + } + + @NotThreadSafe + private static class SimpleConsumerSession implements ConsumerSession { + private boolean closed; + private final Map, BrokerService> services; + + private SimpleConsumerSession(final Map, BrokerService> services) { + this.services = services; + } + + @Override + public boolean isClosed() { + return closed; + } + + @Override + public T getService(final Class aClass) { + return (T)services.get(aClass); + } + + @Override + public void close() { + closed = true; + } + } + + @NotThreadSafe + private static class SimpleProviderSession implements ProviderSession { + private boolean closed; + private final Map, BrokerService> services; + + private SimpleProviderSession(final Map, BrokerService> services) { + this.services = services; + } + + @Override + public boolean isClosed() { + return closed; + } + + @Override + public T getService(final Class aClass) { + return (T)services.get(aClass); + } + + @Override + public void close() { + closed = true; + } + } + + private static class EmptyDomRpcService implements DOMRpcService { + @Nonnull + @Override + public CheckedFuture invokeRpc(@Nonnull final SchemaPath schemaPath, + @Nullable final NormalizedNode normalizedNode) { + return Futures.immediateFailedCheckedFuture( + new DOMRpcImplementationNotAvailableException("RPCs not supported")); + } + + @Nonnull + @Override + public ListenerRegistration registerRpcListener(@Nonnull final T t) { + return new ListenerRegistration() { + @Override + public void close() { + // Noop + } + + @Override + public T getInstance() { + return t; + } + }; + } + } + + private static class EmptyDomMountService implements DOMMountPointService { + @Override + public Optional getMountPoint(final YangInstanceIdentifier yangInstanceIdentifier) { + return Optional.absent(); + } + + @Override + public DOMMountPointBuilder createMountPoint(final YangInstanceIdentifier yangInstanceIdentifier) { + throw new UnsupportedOperationException("No mountpoint support"); + } + + @Override + public ListenerRegistration registerProvisionListener( + final MountProvisionListener mountProvisionListener) { + return new ListenerRegistration() { + @Override + public void close() { + // Noop + } + + @Override + public MountProvisionListener getInstance() { + return mountProvisionListener; + } + }; + } + } +} diff --git a/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java index b22ece04f..caa792da4 100644 --- a/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java +++ b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java @@ -16,7 +16,7 @@ package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; -import io.fd.honeycomb.v3po.impl.NetconfFacadeHoneycombBindingBroker; +import io.fd.honeycomb.v3po.impl.NorthboundFacadeHoneycombDOMBroker; import org.opendaylight.controller.sal.core.api.AbstractProvider; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.controller.sal.core.api.model.SchemaService; @@ -53,7 +53,8 @@ public class V3poModule extends }); final SchemaService schemaBiService = providerSession.getService(SchemaService.class); - return new NetconfFacadeHoneycombBindingBroker(getHoneycombDomDataBrokerDependency(), schemaBiService); + return new NorthboundFacadeHoneycombDOMBroker(getHoneycombDomDataBrokerDependency(), schemaBiService, + getHoneycombDomNotificationServiceDependency()); } } diff --git a/v3po/impl/src/main/yang/v3po-impl.yang b/v3po/impl/src/main/yang/v3po-impl.yang index 3ab69f4e9..40b00ec96 100644 --- a/v3po/impl/src/main/yang/v3po-impl.yang +++ b/v3po/impl/src/main/yang/v3po-impl.yang @@ -9,6 +9,7 @@ module v3po-impl { import vpp-jvpp-cfg { prefix vjvppc; revision-date 2016-04-06; } import translate-api { prefix tapi; revision-date 2016-04-06; } import data-api { prefix dapi; revision-date 2016-04-11; } + import notification-api { prefix hc-notif-a; revision-date 2016-06-01; } description "Service definition for v3po project"; @@ -45,6 +46,15 @@ module v3po-impl { } } + container honeycomb-dom-notification-service { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity hc-notif-a:dom-notification-service; + } + } + } + } } 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 @@ + + + + + io.fd.honeycomb.common + impl-parent + 1.0.0-SNAPSHOT + ../../../common/impl-parent + + + 4.0.0 + io.fd.honeycomb.v3po + notification-api + 1.0.0-SNAPSHOT + bundle + + + + org.opendaylight.mdsal + mdsal-dom-api + 2.0.2-Beryllium-SR2 + + + + + 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> 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 @@ + + + + + io.fd.honeycomb.common + impl-parent + 1.0.0-SNAPSHOT + ../../../common/impl-parent + + + 4.0.0 + io.fd.honeycomb.v3po + notification-impl + 1.0.0-SNAPSHOT + bundle + + + + + ${project.groupId} + notification-api + 1.0.0-SNAPSHOT + + + 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/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 @@ + + + + + + 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/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 @@ + + + + + + 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/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> 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> 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/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 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/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> 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 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/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 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/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 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("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/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 @@ + + + + + + io.fd.honeycomb.v3po + v3po-aggregator + 1.0.0-SNAPSHOT + + + + 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 + + + + + diff --git a/v3po/pom.xml b/v3po/pom.xml index 7676a299a..908acbdb6 100644 --- a/v3po/pom.xml +++ b/v3po/pom.xml @@ -39,6 +39,7 @@ translate-api translate-impl translate-utils + notification vpp-translate-utils vpp-jvpp-cfg v3po2vpp diff --git a/v3po/translate-api/src/main/yang/translate-api.yang b/v3po/translate-api/src/main/yang/translate-api.yang index 28d969378..4a8093ab8 100644 --- a/v3po/translate-api/src/main/yang/translate-api.yang +++ b/v3po/translate-api/src/main/yang/translate-api.yang @@ -34,4 +34,9 @@ module translate-api { config:java-class io.fd.honeycomb.v3po.translate.write.WriterRegistry; } + identity honeycomb-mapping-context { + base "config:service-type"; + config:java-class io.fd.honeycomb.v3po.translate.MappingContext; + } + } \ No newline at end of file diff --git a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModule.java b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModule.java new file mode 100644 index 000000000..99cf4b4e1 --- /dev/null +++ b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModule.java @@ -0,0 +1,84 @@ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406; + +import com.google.common.base.Optional; +import io.fd.honeycomb.v3po.translate.MappingContext; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class RealtimeMappingContextModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractRealtimeMappingContextModule { + public RealtimeMappingContextModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public RealtimeMappingContextModule(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.translate.utils.rev160406.RealtimeMappingContextModule 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() { + // Provides real time CRUD on top of Context data broker + return new MappingContext() { + + @Override + public Optional read(@Nonnull final InstanceIdentifier currentId) { + try(ReadOnlyTransaction tx = getContextBindingBrokerDependency().newReadOnlyTransaction()) { + try { + return tx.read(LogicalDatastoreType.OPERATIONAL, currentId).checkedGet(); + } catch (ReadFailedException e) { + throw new IllegalStateException("Unable to perform read of " + currentId, e); + } + } + } + + @Override + public void delete(final InstanceIdentifier path) { + final WriteTransaction writeTx = getContextBindingBrokerDependency().newWriteOnlyTransaction(); + writeTx.delete(LogicalDatastoreType.OPERATIONAL, path); + try { + writeTx.submit().checkedGet(); + } catch (TransactionCommitFailedException e) { + throw new IllegalStateException("Unable to perform delete of " + path, e); + } + } + + @Override + public void merge(final InstanceIdentifier path, final T data) { + final WriteTransaction writeTx = getContextBindingBrokerDependency().newWriteOnlyTransaction(); + writeTx.merge(LogicalDatastoreType.OPERATIONAL, path, data); + try { + writeTx.submit().checkedGet(); + } catch (TransactionCommitFailedException e) { + throw new IllegalStateException("Unable to perform merge of " + path, e); + } + } + + @Override + public void put(final InstanceIdentifier path, final T data) { + final WriteTransaction writeTx = getContextBindingBrokerDependency().newWriteOnlyTransaction(); + writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data); + try { + writeTx.submit().checkedGet(); + } catch (TransactionCommitFailedException e) { + throw new IllegalStateException("Unable to perform put of " + path, e); + } + } + + @Override + public void close() { + // Noop + } + }; + } + +} diff --git a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModuleFactory.java b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModuleFactory.java new file mode 100644 index 000000000..76647eace --- /dev/null +++ b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: translate-utils yang module local name: realtime-mapping-context +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Fri Jun 03 16:04:29 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.translate.utils.rev160406; +public class RealtimeMappingContextModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractRealtimeMappingContextModuleFactory { + +} diff --git a/v3po/translate-utils/src/main/yang/translate-utils.yang b/v3po/translate-utils/src/main/yang/translate-utils.yang index 238528d56..1219648c3 100644 --- a/v3po/translate-utils/src/main/yang/translate-utils.yang +++ b/v3po/translate-utils/src/main/yang/translate-utils.yang @@ -5,6 +5,7 @@ module translate-utils { import config { prefix config; revision-date 2013-04-05; } import translate-api { prefix tapi; revision-date 2016-04-06; } + import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;} description "This module contains translation layer utilities"; @@ -69,4 +70,24 @@ module translate-utils { when "/config:modules/config:module/config:type = 'noop-writer-registry'"; } } + + identity realtime-mapping-context { + base config:module-type; + config:provided-service tapi:honeycomb-mapping-context; + } + + augment "/config:modules/config:module/config:configuration" { + case realtime-mapping-context { + when "/config:modules/config:module/config:type = 'realtime-mapping-context'"; + + container context-binding-broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity md-sal-binding:binding-async-data-broker; + } + } + } + } + } } \ No newline at end of file diff --git a/v3po/v3po2vpp/pom.xml b/v3po/v3po2vpp/pom.xml index d02807c9d..edff40b3d 100644 --- a/v3po/v3po2vpp/pom.xml +++ b/v3po/v3po2vpp/pom.xml @@ -34,6 +34,11 @@ translate-spi ${project.version} + + ${project.groupId} + notification-api + ${project.version} + ${project.groupId} translate-utils diff --git a/v3po/v3po2vpp/src/main/config/default-config.xml b/v3po/v3po2vpp/src/main/config/default-config.xml index 19226d4fc..1ee177553 100644 --- a/v3po/v3po2vpp/src/main/config/default-config.xml +++ b/v3po/v3po2vpp/src/main/config/default-config.xml @@ -26,6 +26,7 @@ urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled?module=threadpool-impl-scheduled&revision=2013-12-01 + urn:honeycomb:params:xml:ns:yang:notification:impl?module=notification-impl&revision=2016-06-01 @@ -65,6 +66,35 @@ global-netconf-ssh-scheduled-executor + + + + prefix:vpp-interface-notification-producer + vpp-interface-notification-producer + + prefix:vpp-jvpp + vpp-jvpp + + + prefix:naming-context + interface-context + + + prefix:honeycomb-mapping-context + realtime-mapping-context + + + + + prefix:honeycomb-notification-manager + honeycomb-notification-manager + + prefix:honeycomb-notification-producer + vpp-interface-notification-producer + + + + prefix:interfaces-state-honeycomb-reader interfaces-state-honeycomb-reader @@ -156,6 +186,13 @@ /modules/module[type='interfaces-state-honeycomb-reader'][name='interfaces-state-honeycomb-reader'] + + prefix:honeycomb-notification-producer + + vpp-interface-notification-producer + /modules/module[type='vpp-interface-notification-producer'][name='vpp-interface-notification-producer'] + + prefix:naming-context diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/notification/InterfaceChangeNotificationProducer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/notification/InterfaceChangeNotificationProducer.java new file mode 100644 index 000000000..f3689d7e2 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/notification/InterfaceChangeNotificationProducer.java @@ -0,0 +1,149 @@ +/* + * 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.translate.v3po.notification; + +import com.google.common.collect.Lists; +import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer; +import io.fd.honeycomb.v3po.notification.NotificationCollector; +import io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; +import io.fd.honeycomb.v3po.translate.v3po.util.TranslateUtils; +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.CompletionStage; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.InterfaceDeleted; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.InterfaceDeletedBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.InterfaceNameOrIndex; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.InterfaceStateChange; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.InterfaceStateChangeBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.InterfaceStatus; +import org.opendaylight.yangtools.yang.binding.Notification; +import org.openvpp.jvpp.VppBaseCallException; +import org.openvpp.jvpp.VppInvocationException; +import org.openvpp.jvpp.dto.SwInterfaceSetFlagsNotification; +import org.openvpp.jvpp.dto.WantInterfaceEvents; +import org.openvpp.jvpp.dto.WantInterfaceEventsReply; +import org.openvpp.jvpp.future.FutureJVpp; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Notification producer for interface events. It starts interface notification stream and for every + * received notification, it transforms it into its BA equivalent and pushes into HC's notification collector. + */ +@NotThreadSafe +public final class InterfaceChangeNotificationProducer implements ManagedNotificationProducer { + + private static final Logger LOG = LoggerFactory.getLogger(InterfaceChangeNotificationProducer.class); + + private final FutureJVpp jvpp; + private final NamingContext interfaceContext; + private final MappingContext mappingContext; + @Nullable + private AutoCloseable notificationListenerReg; + + public InterfaceChangeNotificationProducer(@Nonnull final FutureJVpp jvpp, + @Nonnull final NamingContext interfaceContext, + @Nonnull final MappingContext mappingContext) { + this.jvpp = jvpp; + this.interfaceContext = interfaceContext; + this.mappingContext = mappingContext; + } + + @Override + public void start(final NotificationCollector collector) { + LOG.trace("Starting interface notifications"); + enableDisableIfcNotifications(1); + LOG.debug("Interface notifications started successfully"); + notificationListenerReg = jvpp.getNotificationRegistry().registerSwInterfaceSetFlagsNotificationCallback( + swInterfaceSetFlagsNotification -> { + LOG.trace("Interface notification received: {}", swInterfaceSetFlagsNotification); + collector.onNotification(transformNotification(swInterfaceSetFlagsNotification)); + } + ); + } + + private Notification transformNotification(final SwInterfaceSetFlagsNotification swInterfaceSetFlagsNotification) { + if(swInterfaceSetFlagsNotification.deleted == 1) { + return new InterfaceDeletedBuilder().setName(getIfcName(swInterfaceSetFlagsNotification)).build(); + } else { + return new InterfaceStateChangeBuilder() + .setName(getIfcName(swInterfaceSetFlagsNotification)) + .setAdminStatus(swInterfaceSetFlagsNotification.adminUpDown == 1 ? InterfaceStatus.Up : InterfaceStatus.Down) + .setOperStatus(swInterfaceSetFlagsNotification.linkUpDown == 1 ? InterfaceStatus.Up : InterfaceStatus.Down) + .build(); + } + } + + /** + * Get mapped name for the interface. Best effort only! The mapping might not yet be stored in context + * data tree (write transaction is still in progress and context changes have not been committed yet, or + * VPP sends the notification before it returns create request(that would store mapping)). + * + * In case mapping is not available, index is used as name. TODO inconsistent behavior, maybe just use indices ? + */ + private InterfaceNameOrIndex getIfcName(final SwInterfaceSetFlagsNotification swInterfaceSetFlagsNotification) { + return interfaceContext.containsName(swInterfaceSetFlagsNotification.swIfIndex, mappingContext) + ? new InterfaceNameOrIndex(interfaceContext.getName(swInterfaceSetFlagsNotification.swIfIndex, mappingContext)) + : new InterfaceNameOrIndex((long) swInterfaceSetFlagsNotification.swIfIndex); + } + + @Override + public void stop() { + LOG.trace("Stopping interface notifications"); + enableDisableIfcNotifications(0); + LOG.debug("Interface notifications stopped successfully"); + try { + if (notificationListenerReg != null) { + notificationListenerReg.close(); + } + } catch (Exception e) { + LOG.warn("Unable to properly close notification registration: {}", notificationListenerReg, e); + } + } + + private void enableDisableIfcNotifications(int enableDisable) { + final WantInterfaceEvents wantInterfaceEvents = new WantInterfaceEvents(); + wantInterfaceEvents.pid = 1; + wantInterfaceEvents.enableDisable = enableDisable; + final CompletionStage wantInterfaceEventsReplyCompletionStage; + try { + wantInterfaceEventsReplyCompletionStage = jvpp.wantInterfaceEvents(wantInterfaceEvents); + TranslateUtils.getReply(wantInterfaceEventsReplyCompletionStage.toCompletableFuture()); + } catch (VppBaseCallException e) { + LOG.warn("Unable to {} interface notifications", enableDisable == 1 ? "enable" : "disable", e); + throw new IllegalStateException("Unable to control interface notifications", e); + } + + } + + @Override + public Collection> getNotificationTypes() { + final ArrayList> classes = Lists.newArrayList(); + classes.add(InterfaceStateChange.class); + classes.add(InterfaceDeleted.class); + return classes; + } + + @Override + public void close() throws Exception { + LOG.trace("Closing interface notifications producer"); + stop(); + } +} diff --git a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppInterfaceNotificationProducerModule.java b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppInterfaceNotificationProducerModule.java new file mode 100644 index 000000000..4251fdab2 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppInterfaceNotificationProducerModule.java @@ -0,0 +1,28 @@ +package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406; + +import io.fd.honeycomb.v3po.translate.v3po.notification.InterfaceChangeNotificationProducer; + +public class VppInterfaceNotificationProducerModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406.AbstractVppInterfaceNotificationProducerModule { + + public VppInterfaceNotificationProducerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public VppInterfaceNotificationProducerModule(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.v3po2vpp.rev160406.VppInterfaceNotificationProducerModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + // add custom validation form module attributes here. + } + + @Override + public java.lang.AutoCloseable createInstance() { + return new InterfaceChangeNotificationProducer( + getVppJvppDependency(), + getInterfaceContextDependency(), + getRealtimeMappingContextDependency()); + } + +} diff --git a/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppInterfaceNotificationProducerModuleFactory.java b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppInterfaceNotificationProducerModuleFactory.java new file mode 100644 index 000000000..c80a0d364 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/v3po2vpp/rev160406/VppInterfaceNotificationProducerModuleFactory.java @@ -0,0 +1,13 @@ +/* +* Generated file +* +* Generated from: yang module name: v3po2vpp yang module local name: vpp-interface-notification-producer +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Wed Jun 01 17:12:36 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.v3po2vpp.rev160406; +public class VppInterfaceNotificationProducerModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.v3po2vpp.rev160406.AbstractVppInterfaceNotificationProducerModuleFactory { + +} diff --git a/v3po/v3po2vpp/src/main/yang/v3po2vpp.yang b/v3po/v3po2vpp/src/main/yang/v3po2vpp.yang index 5ccac8e94..cf89cbd51 100644 --- a/v3po/v3po2vpp/src/main/yang/v3po2vpp.yang +++ b/v3po/v3po2vpp/src/main/yang/v3po2vpp.yang @@ -10,6 +10,7 @@ module v3po2vpp { import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;} import vpp-util { prefix vpp-u; revision-date 2016-04-06; } import threadpool {prefix th;} + import notification-api { prefix hc-notif-a; revision-date 2016-06-01; } description "This module contains reads and writers for v3po yang model"; @@ -68,6 +69,44 @@ module v3po2vpp { } } + identity vpp-interface-notification-producer { + base config:module-type; + config:provided-service hc-notif-a:honeycomb-notification-producer; + } + + augment "/config:modules/config:module/config:configuration" { + case vpp-interface-notification-producer { + when "/config:modules/config:module/config:type = 'vpp-interface-notification-producer'"; + + container vpp-jvpp { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity vjvppc:vpp-jvpp; + } + } + } + + container interface-context { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity vpp-u:naming-context; + } + } + } + + container realtime-mapping-context { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity tapi:honeycomb-mapping-context; + } + } + } + } + } + identity interfaces-state-honeycomb-reader { base config:module-type; config:provided-service tapi:honeycomb-reader; diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/notification/InterfaceChangeNotificationProducerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/notification/InterfaceChangeNotificationProducerTest.java new file mode 100644 index 000000000..dd5a22d61 --- /dev/null +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/v3po/translate/v3po/notification/InterfaceChangeNotificationProducerTest.java @@ -0,0 +1,130 @@ +/* + * 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.translate.v3po.notification; + +import static io.fd.honeycomb.v3po.translate.v3po.test.ContextTestUtils.getMapping; +import static io.fd.honeycomb.v3po.translate.v3po.test.ContextTestUtils.getMappingIid; +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import io.fd.honeycomb.v3po.notification.NotificationCollector; +import io.fd.honeycomb.v3po.translate.MappingContext; +import io.fd.honeycomb.v3po.translate.v3po.util.NamingContext; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.Mappings; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.MappingsBuilder; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.Mapping; +import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.naming.context.mappings.MappingKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.InterfaceStateChange; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.InterfaceStatus; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; +import org.openvpp.jvpp.callback.SwInterfaceSetFlagsNotificationCallback; +import org.openvpp.jvpp.dto.SwInterfaceSetFlagsNotification; +import org.openvpp.jvpp.dto.WantInterfaceEvents; +import org.openvpp.jvpp.dto.WantInterfaceEventsReply; +import org.openvpp.jvpp.future.FutureJVpp; +import org.openvpp.jvpp.notification.NotificationRegistry; + +public class InterfaceChangeNotificationProducerTest { + + @Mock + private FutureJVpp jVpp; + private NamingContext namingContext = new NamingContext("test", "test-instance"); + @Mock + private MappingContext mappingContext; + @Mock + private NotificationCollector collector; + @Mock + private NotificationRegistry notificationRegistry; + @Mock + private AutoCloseable notificationListenerReg; + + private ArgumentCaptor callbackArgumentCaptor; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + doReturn(notificationRegistry).when(jVpp).getNotificationRegistry(); + callbackArgumentCaptor = ArgumentCaptor.forClass(SwInterfaceSetFlagsNotificationCallback.class); + doReturn(notificationListenerReg).when(notificationRegistry).registerSwInterfaceSetFlagsNotificationCallback( + callbackArgumentCaptor.capture()); + + final KeyedInstanceIdentifier eth0Id = getMappingIid("eth0", "test-instance"); + final Optional eth0 = getMapping("eth0", 0); + + final List allMappings = Lists.newArrayList(getMapping("eth0", 0).get()); + final Mappings allMappingsBaObject = new MappingsBuilder().setMapping(allMappings).build(); + doReturn(Optional.of(allMappingsBaObject)).when(mappingContext).read(eth0Id.firstIdentifierOf(Mappings.class)); + + doReturn(eth0).when(mappingContext).read(eth0Id); + } + + @Test + public void testStart() throws Exception { + final CompletableFuture response = new CompletableFuture<>(); + response.complete(new WantInterfaceEventsReply()); + doReturn(response).when(jVpp).wantInterfaceEvents(any(WantInterfaceEvents.class)); + final InterfaceChangeNotificationProducer interfaceChangeNotificationProducer = + new InterfaceChangeNotificationProducer(jVpp, namingContext, mappingContext); + + interfaceChangeNotificationProducer.start(collector); + verify(jVpp).wantInterfaceEvents(any(WantInterfaceEvents.class)); + verify(jVpp).getNotificationRegistry(); + verify(notificationRegistry).registerSwInterfaceSetFlagsNotificationCallback(any( + SwInterfaceSetFlagsNotificationCallback.class)); + + interfaceChangeNotificationProducer.stop(); + verify(jVpp, times(2)).wantInterfaceEvents(any(WantInterfaceEvents.class)); + verify(notificationListenerReg).close(); + } + + @Test + public void testNotification() throws Exception { + final CompletableFuture response = new CompletableFuture<>(); + response.complete(new WantInterfaceEventsReply()); + doReturn(response).when(jVpp).wantInterfaceEvents(any(WantInterfaceEvents.class)); + final InterfaceChangeNotificationProducer interfaceChangeNotificationProducer = + new InterfaceChangeNotificationProducer(jVpp, namingContext, mappingContext); + + interfaceChangeNotificationProducer.start(collector); + + final SwInterfaceSetFlagsNotification swInterfaceSetFlagsNotification = new SwInterfaceSetFlagsNotification(); + swInterfaceSetFlagsNotification.deleted = 0; + swInterfaceSetFlagsNotification.swIfIndex = 0; + swInterfaceSetFlagsNotification.adminUpDown = 1; + swInterfaceSetFlagsNotification.linkUpDown = 1; + + callbackArgumentCaptor.getValue().onSwInterfaceSetFlagsNotification(swInterfaceSetFlagsNotification); + final ArgumentCaptor notificationCaptor = + ArgumentCaptor.forClass(InterfaceStateChange.class); + verify(collector).onNotification(notificationCaptor.capture()); + + assertEquals("eth0", notificationCaptor.getValue().getName().getString()); + assertEquals(InterfaceStatus.Up, notificationCaptor.getValue().getAdminStatus()); + assertEquals(InterfaceStatus.Up, notificationCaptor.getValue().getOperStatus()); + } +} \ No newline at end of file -- cgit 1.2.3-korg