4d85d64c5707f74876dffe13cbd566c4cdac4082
[hc2vpp.git] /
1 package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601;
2
3 import com.google.common.annotations.VisibleForTesting;
4 import io.fd.honeycomb.v3po.notification.NotificationCollector;
5 import io.fd.honeycomb.v3po.notification.impl.NotificationProducerRegistry;
6 import java.io.IOException;
7 import java.util.Set;
8 import java.util.stream.Collectors;
9 import javax.xml.stream.XMLOutputFactory;
10 import javax.xml.stream.XMLStreamException;
11 import javax.xml.stream.XMLStreamWriter;
12 import javax.xml.transform.dom.DOMResult;
13 import org.opendaylight.controller.config.api.JmxAttributeValidationException;
14 import org.opendaylight.controller.config.util.xml.XmlUtil;
15 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
16 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
17 import org.opendaylight.controller.sal.core.api.model.SchemaService;
18 import org.opendaylight.netconf.notifications.NetconfNotification;
19 import org.opendaylight.netconf.notifications.NotificationPublisherRegistration;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.StreamNameType;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.streams.StreamBuilder;
22 import org.opendaylight.yangtools.concepts.ListenerRegistration;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
25 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
26 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
27 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
28 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
29 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32 import org.w3c.dom.Document;
33 import org.w3c.dom.Element;
34
35 public class HoneycombNotificationToNetconfTranslatorModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombNotificationToNetconfTranslatorModule {
36
37     private static final Logger LOG = LoggerFactory.getLogger(HoneycombNotificationToNetconfTranslatorModule.class);
38
39     public HoneycombNotificationToNetconfTranslatorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
40         super(identifier, dependencyResolver);
41     }
42
43     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) {
44         super(identifier, dependencyResolver, oldModule, oldInstance);
45     }
46
47     @Override
48     public void customValidation() {
49         JmxAttributeValidationException.checkCondition(!getNetconfStreamName().isEmpty(),
50             "Stream name cannot be empty", netconfStreamNameJmxAttribute);
51         JmxAttributeValidationException.checkCondition(!getNetconfStreamDescription().isEmpty(),
52             "Stream description cannot be empty", netconfStreamDescriptionJmxAttribute);
53     }
54
55     @Override
56     public java.lang.AutoCloseable createInstance() {
57         final SchemaService schemaService = getSchemaServiceDependency();
58         final StreamNameType streamType = new StreamNameType(getNetconfStreamName());
59         final NotificationCollector hcNotificationCollector = getHoneycombNotificationCollectorDependency();
60
61         // Register as NETCONF notification publisher under configured name
62         final NotificationPublisherRegistration netconfNotificationProducerReg =
63             getNetconfNotificationCollectorDependency().registerNotificationPublisher(new StreamBuilder()
64                 .setName(streamType)
65                 .setReplaySupport(false)
66                 .setDescription(getNetconfStreamDescription()).build());
67
68         // Notification Translator, get notification from HC producers and put into NETCONF notification collector
69         final DOMNotificationListener domNotificationListener =
70             notification -> {
71                 LOG.debug("Propagating notification: {} into NETCONF", notification.getType());
72                 netconfNotificationProducerReg.onNotification(streamType, notificationToXml(notification, schemaService.getGlobalContext()));
73             };
74
75         // NotificationManager is used to provide list of available notifications (which are all of the notifications registered)
76         // TODO make available notifications configurable here so that any number of notification streams for NETCONF
77         // can be configured on top of a single notification manager
78         LOG.debug("Current notifications to be exposed over NETCONF: {}", hcNotificationCollector.getNotificationTypes());
79         final Set<SchemaPath> currentNotificationSchemaPaths = hcNotificationCollector.getNotificationTypes()
80             .stream()
81             .map(NotificationProducerRegistry::getQName)
82             .map(qName -> SchemaPath.create(true, qName))
83             .collect(Collectors.toSet());
84
85         // Register as listener to HC's DOM notification service
86         // TODO This should only be triggered when NETCONF notifications are activated
87         // Because this way we actually start all notification producers
88         // final Collection<QName> notificationQNames =
89         final ListenerRegistration<DOMNotificationListener> domNotificationListenerReg = getDomNotificationServiceDependency()
90                 .registerNotificationListener(domNotificationListener, currentNotificationSchemaPaths);
91
92         LOG.info("Exposing NETCONF notification stream: {}", streamType.getValue());
93         return () -> {
94             domNotificationListenerReg.close();
95             netconfNotificationProducerReg.close();
96         };
97     }
98
99     @VisibleForTesting
100     static NetconfNotification notificationToXml(final DOMNotification domNotification, final SchemaContext ctx) {
101         LOG.trace("Transforming notification: {} into XML", domNotification.getType());
102
103         final SchemaPath type = domNotification.getType();
104         final QName notificationQName = type.getLastComponent();
105         final DOMResult result = prepareDomResultForRpcRequest(notificationQName);
106
107         try {
108             writeNormalizedRpc(domNotification, result, type, ctx);
109         } catch (final XMLStreamException | IOException | IllegalStateException e) {
110             LOG.warn("Unable to transform notification: {} into XML", domNotification.getType(), e);
111             throw new IllegalArgumentException("Unable to serialize " + type, e);
112         }
113
114         final Document node = result.getNode().getOwnerDocument();
115         return new NetconfNotification(node);
116     }
117
118     private static DOMResult prepareDomResultForRpcRequest(final QName notificationQName) {
119         final Document document = XmlUtil.newDocument();
120         final Element notificationElement =
121             document.createElementNS(notificationQName.getNamespace().toString(), notificationQName.getLocalName());
122         document.appendChild(notificationElement);
123         return new DOMResult(notificationElement);
124     }
125
126     private static final XMLOutputFactory XML_FACTORY;
127
128     static {
129         XML_FACTORY = XMLOutputFactory.newFactory();
130         XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false);
131     }
132
133     private static void writeNormalizedRpc(final DOMNotification normalized, final DOMResult result,
134                                            final SchemaPath schemaPath, final SchemaContext baseNetconfCtx)
135         throws IOException, XMLStreamException {
136         final XMLStreamWriter writer = XML_FACTORY.createXMLStreamWriter(result);
137         try {
138             try (final NormalizedNodeStreamWriter normalizedNodeStreamWriter =
139                      XMLStreamNormalizedNodeStreamWriter.create(writer, baseNetconfCtx, schemaPath)) {
140                 try (final NormalizedNodeWriter normalizedNodeWriter =
141                          NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter)) {
142                     for (DataContainerChild<?, ?> dataContainerChild : normalized.getBody().getValue()) {
143                         normalizedNodeWriter.write(dataContainerChild);
144                     }
145                     normalizedNodeWriter.flush();
146                 }
147             }
148         } finally {
149             try {
150                 writer.close();
151             } catch (final Exception e) {
152                 LOG.warn("Unable to close resource properly. Ignoring", e);
153             }
154         }
155     }
156
157 }