[ONOS-3775] Building alarms from NETCONF notifications

Change-Id: I80d960193ce957fa640fde0d1e7b4422d60c7fe4
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmTranslator.java b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmTranslator.java
new file mode 100644
index 0000000..9fbcc35
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmTranslator.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.onosproject.incubator.net.faultmanagement.alarm;
+
+import org.onosproject.net.DeviceId;
+
+import java.io.InputStream;
+import java.util.Collection;
+
+/**
+ * Abstraction of ability to translate device messages into alarms.
+ */
+public interface AlarmTranslator {
+
+    /**
+     * Translates message from device into an alarm with appropriate
+     * information.
+     *
+     * @param message message from device to translate to alarm
+     * @return Alarm with information determined by given message
+     */
+    Collection<Alarm> translateToAlarm(DeviceId deviceId, InputStream message);
+}
diff --git a/providers/netconf/alarm/src/main/java/org/onosproject/provider/netconf/alarm/NetconfAlarmProvider.java b/providers/netconf/alarm/src/main/java/org/onosproject/provider/netconf/alarm/NetconfAlarmProvider.java
index 5a86dd0..ec74eb6 100644
--- a/providers/netconf/alarm/src/main/java/org/onosproject/provider/netconf/alarm/NetconfAlarmProvider.java
+++ b/providers/netconf/alarm/src/main/java/org/onosproject/provider/netconf/alarm/NetconfAlarmProvider.java
@@ -25,9 +25,7 @@
 import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
 import org.onosproject.incubator.net.faultmanagement.alarm.AlarmProvider;
 import org.onosproject.incubator.net.faultmanagement.alarm.AlarmProviderService;
-import org.onosproject.incubator.net.faultmanagement.alarm.AlarmService;
 import org.onosproject.incubator.net.faultmanagement.alarm.AlarmProviderRegistry;
-import org.onosproject.incubator.net.faultmanagement.alarm.DefaultAlarm;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
@@ -41,8 +39,10 @@
 import org.onosproject.netconf.ctl.NetconfDeviceOutputEventListenerImpl;
 import org.slf4j.Logger;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Map;
 
 import static org.slf4j.LoggerFactory.getLogger;
@@ -62,9 +62,6 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NetconfController controller;
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected AlarmService alarmService;
-
     protected AlarmProviderService providerService;
 
     private Map<DeviceId, InternalNotificationListener> idNotificationListenerMap = Maps.newHashMap();
@@ -94,8 +91,8 @@
         providerRegistry.unregister(this);
         idNotificationListenerMap.forEach((id, listener) -> {
             controller.getNetconfDevice(id)
-                      .getSession()
-                      .removeDeviceOutputListener(listener);
+                    .getSession()
+                    .removeDeviceOutputListener(listener);
         });
         controller.removeDeviceListener(deviceListener);
         providerService = null;
@@ -123,10 +120,11 @@
         public void event(NetconfDeviceOutputEvent event) {
             if (event.type() == NetconfDeviceOutputEvent.Type.DEVICE_NOTIFICATION) {
                 DeviceId deviceId = event.getDeviceInfo().getDeviceId();
-                Alarm newAlarm = new DefaultAlarm.Builder(deviceId, event.getMessagePayload(),
-                                                          Alarm.SeverityLevel.WARNING, 0).build();
-                Collection<Alarm> alarms = Collections.singleton(newAlarm);
-                triggerProbe(deviceId, alarms);
+                NetconfAlarmTranslator translator = new NetconfAlarmTranslator();
+                String message = event.getMessagePayload();
+                InputStream in = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8));
+                Collection<Alarm> newAlarms = translator.translateToAlarm(deviceId, in);
+                triggerProbe(deviceId, newAlarms);
             }
         }
     }
diff --git a/providers/netconf/alarm/src/main/java/org/onosproject/provider/netconf/alarm/NetconfAlarmTranslator.java b/providers/netconf/alarm/src/main/java/org/onosproject/provider/netconf/alarm/NetconfAlarmTranslator.java
new file mode 100644
index 0000000..bbf97b7
--- /dev/null
+++ b/providers/netconf/alarm/src/main/java/org/onosproject/provider/netconf/alarm/NetconfAlarmTranslator.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.onosproject.provider.netconf.alarm;
+
+import com.google.common.collect.ImmutableSet;
+import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
+import org.onosproject.incubator.net.faultmanagement.alarm.AlarmTranslator;
+import org.onosproject.incubator.net.faultmanagement.alarm.DefaultAlarm;
+import org.onosproject.net.DeviceId;
+import org.slf4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.joda.time.format.ISODateTimeFormat;
+import org.xml.sax.SAXException;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Translates NETCONF notification messages to actions on alarms.
+ */
+public class NetconfAlarmTranslator implements AlarmTranslator {
+
+    private final Logger log = getLogger(getClass());
+    private static final String EVENTTIME_TAGNAME = "eventTime";
+
+    @Override
+    public Collection<Alarm> translateToAlarm(DeviceId deviceId, InputStream message) {
+        try {
+            Collection<Alarm> alarms = new ArrayList<>();
+            Document doc = createDocFromMessage(message);
+
+            // parse date element value into long
+            Node eventTime = doc.getElementsByTagName(EVENTTIME_TAGNAME).item(0);
+            String date = eventTime.getTextContent();
+            long timeStamp = parseDate(date);
+
+            // event-specific tag names as alarm descriptions
+            Node descriptionNode = eventTime.getNextSibling();
+            while (descriptionNode != null) {
+                if (descriptionNode.getNodeType() == Node.ELEMENT_NODE) {
+                    String description = nodeToString(descriptionNode);
+                    alarms.add(new DefaultAlarm.Builder(deviceId, description,
+                                                        Alarm.SeverityLevel.WARNING,
+                                                        timeStamp).build());
+                    descriptionNode = null;
+                } else {
+                    descriptionNode = descriptionNode.getNextSibling();
+                }
+            }
+            return alarms;
+        } catch (SAXException | IOException | ParserConfigurationException |
+                UnsupportedOperationException | IllegalArgumentException |
+                TransformerException e) {
+            log.error("Exception thrown translating {} from {}.", message, deviceId);
+            return ImmutableSet.of();
+        }
+    }
+
+    private Document createDocFromMessage(InputStream message)
+            throws SAXException, IOException, ParserConfigurationException {
+        DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder builder = dbfactory.newDocumentBuilder();
+        return builder.parse(new InputSource(message));
+    }
+
+    private long parseDate(String timeStr)
+            throws UnsupportedOperationException, IllegalArgumentException {
+        return ISODateTimeFormat.dateTimeNoMillis().parseMillis(timeStr);
+    }
+
+    private static String nodeToString(Node rootNode) throws TransformerException {
+        TransformerFactory tf = TransformerFactory.newInstance();
+        Transformer transformer = tf.newTransformer();
+        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+        StringWriter writer = new StringWriter();
+        DOMSource source = new DOMSource(rootNode);
+        transformer.transform(source, new StreamResult(writer));
+        return writer.getBuffer().toString();
+    }
+}