[ONOS-3203] End-to-end demo of Fault Management via SNMP.

This adds SNMP device-discovery, and a Fault Management app which makes alarms available to users via REST/GUI/CLI interfaces.
There is still code cleanup that could be done, but aim of this commit is an end-to-end proof of concept.

To demonstrate :

1) /opt/onos/bin/onos-service
onos> app activate org.onosproject.snmp
onos> app activate org.onosproject.faultmanagement

2) SNMP devices are seeded via config file. The default seed file contains connection details for devices (SNMP agents) available via internet  e.g. demo.snmplabs.com
cp /opt/onos/apache-karaf-3.0.3/etc/samples/org.onosproject.provider.snmp.device.impl.SnmpDeviceProvider.cfg   /opt/onos/apache-karaf-3.0.3/etc/

3) ONOS will poll these SNMP devices and store their alarms.

4) You can now manipulate the alarms via REST  e.g. http://<onos>:8181/onos/v1/fm/alarms , via CLI  via various "alarm-*” commands or in UI with an Alarms Overlay.

More info at https://wiki.onosproject.org/display/ONOS/Fault+Management

15/Dec/15: Updated regarding review comments from Thomas Vachuska.
17/Dec/15: Updated coreService.registerApplication(name) as per https://gerrit.onosproject.org/#/c/6878/

Change-Id: I886f8511f178dc4600ab96e5ff10cc90329cabec
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmEvent.java b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmEvent.java
index bbbd993..4041a7a 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmEvent.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmEvent.java
@@ -15,51 +15,64 @@
  */
 package org.onosproject.incubator.net.faultmanagement.alarm;
 
+import java.util.Set;
 import org.onosproject.event.AbstractEvent;
+import org.onosproject.net.DeviceId;
 
 /**
- * Entity that represents Alarm events.
+ * Entity that represents Alarm events. Note: although the event will itself have a time, consumers may be more
+ * interested in the times embedded in the alarms themselves.
+ *
  */
-public class AlarmEvent extends AbstractEvent<AlarmEvent.Type, Alarm> {
+public class AlarmEvent extends AbstractEvent<AlarmEvent.Type, Set<Alarm>> {
 
+    private final DeviceId deviceRefreshed;
 
     /**
-     * Creates an event of a given type and for the specified alarm and the
-     * current time.
+     * Creates an event due to one or more notification.
      *
-     * @param type  topology event type
-     * @param alarm the alarm
+     * @param alarms the set one or more of alarms.
      */
-    public AlarmEvent(Type type, Alarm alarm) {
-        super(type, alarm);
+    public AlarmEvent(Set<Alarm> alarms) {
+        super(Type.NOTIFICATION, alarms);
+        deviceRefreshed = null;
     }
 
     /**
-     * Creates an event of a given type and for the specified alarm and time.
+     * Creates an event due to alarm discovery for a device.
      *
-     * @param type  link event type
-     * @param alarm the alarm
-     * @param time  occurrence time
+     * @param alarms the set of alarms.
+     * @param deviceRefreshed if of refreshed device, populated after a de-discovery
      */
-    public AlarmEvent(Type type, Alarm alarm,
-                      long time) {
-        super(type, alarm, time);
+    public AlarmEvent(Set<Alarm> alarms,
+            DeviceId deviceRefreshed) {
+        super(Type.DEVICE_DISCOVERY, alarms);
+        this.deviceRefreshed = deviceRefreshed;
+
     }
 
     /**
-     * Type of alarm events.
+     * Gets which device was refreshed.
+     *
+     * @return the refreshed device, or null if event related to a asynchronous notification(s)
+     */
+    public DeviceId getDeviceRefreshed() {
+        return deviceRefreshed;
+    }
+
+    /**
+     * Type of alarm event.
      */
     public enum Type {
-        /**
-         * A Raised Alarm.
-         */
-        RAISE,
 
         /**
-         * A Cleared Alarm.
+         * Individual alarm(s) updated.
          */
-        CLEAR
+        NOTIFICATION,
+        /**
+         * Alarm set updated for a given device.
+         */
+        DEVICE_DISCOVERY,
     }
 
-
 }
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmId.java b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmId.java
index e0107f8..4e65009 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmId.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmId.java
@@ -16,11 +16,9 @@
 package org.onosproject.incubator.net.faultmanagement.alarm;
 
 import com.google.common.annotations.Beta;
-
 import java.util.Objects;
-
 import static com.google.common.base.MoreObjects.toStringHelper;
-
+import static com.google.common.base.Preconditions.checkArgument;
 /**
  * Alarm identifier suitable as an external key.
  * <p>
@@ -30,23 +28,29 @@
 public final class AlarmId {
 
     private final long id;
+    public static final AlarmId NONE = new AlarmId();
 
     /**
      * Instantiates a new Alarm id.
      *
      * @param id the id
      */
-    public AlarmId(final long id) {
+    private AlarmId(long id) {
+        checkArgument(id != 0L, "id must be non-zero");
         this.id = id;
     }
 
+    private AlarmId() {
+        this.id = 0L;
+    }
+
     /**
      * Creates an alarm identifier from the specified long representation.
      *
      * @param value long value
      * @return intent identifier
      */
-    public static AlarmId valueOf(final long value) {
+    public static AlarmId alarmId(long value) {
         return new AlarmId(value);
     }
 
@@ -65,12 +69,12 @@
     }
 
     @Override
-    public boolean equals(final Object obj) {
+    public boolean equals(Object obj) {
         if (this == obj) {
             return true;
         }
         if (obj instanceof AlarmId) {
-            final AlarmId other = (AlarmId) obj;
+            AlarmId other = (AlarmId) obj;
             return Objects.equals(this.id, other.id);
         }
         return false;
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmProvider.java b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmProvider.java
index 82bcda2..2b82744 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmProvider.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmProvider.java
@@ -24,14 +24,25 @@
 public interface AlarmProvider extends Provider {
 
     /**
-     * Triggers an asynchronous discovery of the alarms on the specified device,
-     * intended to refresh internal alarm model for the device. An indirect
-     * result of this should be invocation of
-     * {@link org.onosproject.incubator.net.faultmanagement.alarm.AlarmProviderService#updateAlarmList} )}
-     * at some later point in time.
+     * Triggers an asynchronous discovery of the alarms on the specified device, intended to refresh internal alarm
+     * model for the device. An indirect result of this should be a event sent later with discovery result ie a set of
+     * alarms.
      *
      * @param deviceId ID of device to be probed
      */
     void triggerProbe(DeviceId deviceId);
 
+    /**
+     * Register a listener for alarms.
+     *
+     * @param listener the listener to notify
+     */
+    void addAlarmListener(AlarmListener listener);
+
+    /**
+     * Unregister a listener.
+     *
+     * @param listener the listener to unregister
+     */
+    void removeAlarmListener(AlarmListener listener);
 }
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmProviderService.java b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmProviderService.java
index 727aa28..3e8519a 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmProviderService.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmProviderService.java
@@ -16,6 +16,7 @@
 package org.onosproject.incubator.net.faultmanagement.alarm;
 
 
+import com.google.common.annotations.Beta;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.provider.ProviderService;
 
@@ -24,6 +25,7 @@
 /**
  * The interface Alarm provider service.
  */
+@Beta
 public interface AlarmProviderService extends ProviderService<AlarmProvider> {
 
     /**
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmService.java b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmService.java
index 03c0c7b..d31b3f4 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmService.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/AlarmService.java
@@ -16,60 +16,65 @@
 package org.onosproject.incubator.net.faultmanagement.alarm;
 
 import com.google.common.annotations.Beta;
-//import org.onosproject.event.ListenerService;
+import java.util.Map;
 
 import java.util.Set;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 
 /**
- * Service for interacting with the alarm handling of devices. Unless stated
- * otherwise method return active AND recently-cleared alarms.
+ * Service for interacting with the alarm handling of devices. Unless stated otherwise, getter methods
+ * return active AND recently-cleared alarms.
  */
 @Beta
 public interface AlarmService {
-// extends ListenerService<AlarmEvent, AlarmListener> {
 
     /**
-     * Alarm should be updated in ONOS's internal representation; only
-     * administration/book-keeping fields may be updated. Attempting to update
-     * fields which are mapped directly from device is prohibited.
+     * Update book-keeping (ie administrative) fields for the alarm matching the specified identifier.
      *
-     * @param replacement alarm with updated book-keeping fields
-     * @return updated alarm (including any recent device derived changes)
-
-     * @throws java.lang.IllegalStateException if attempt to update not allowed
-     * fields.
+     * @param id alarm identifier
+     * @param isAcknowledged new acknowledged state
+     * @param assignedUser new assigned user, null clear
+     * @return updated alarm (including any recent device-derived changes)
+     *
      */
-    Alarm update(Alarm replacement);
+    Alarm updateBookkeepingFields(AlarmId id, boolean isAcknowledged, String assignedUser);
 
     /**
-     * Returns the number of ACTIVE alarms on a device.
+     * Returns summary of alarms on a given device.
      *
      * @param deviceId the device
-     * @return number of alarms
+     * @return map of severity (if applicable) vs alarm counts; empty map if either the device has no alarms or
+     * identified device is not managed.
      */
-    int getActiveAlarmCount(DeviceId deviceId);
+    Map<Alarm.SeverityLevel, Long> getAlarmCounts(DeviceId deviceId);
+
+    /**
+     * Returns summary of alarms on all devices.
+     *
+     * @return map of severity (if applicable) vs alarm counts; empty map if no alarms.
+     */
+    Map<Alarm.SeverityLevel, Long> getAlarmCounts();
 
     /**
      * Returns the alarm with the specified identifier.
      *
      * @param alarmId alarm identifier
-     * @return alarm or null if one with the given identifier is not known
+     * @return alarm matching id; null if no alarm matches the identifier.
      */
     Alarm getAlarm(AlarmId alarmId);
 
     /**
      * Returns all of the alarms.
      *
-     * @return the alarms
+     * @return set of alarms; empty set if no alarms
      */
     Set<Alarm> getAlarms();
 
     /**
      * Returns all of the ACTIVE alarms. Recently cleared alarms excluded.
      *
-     * @return the alarms
+     * @return set of alarms; empty set if no alarms
      */
     Set<Alarm> getActiveAlarms();
 
@@ -77,16 +82,15 @@
      * Returns the alarms with the specified severity.
      *
      * @param severity the alarm severity
-     * @return the active alarms with a particular severity
+     * @return set of alarms with a particular severity; empty set if no alarms
      */
     Set<Alarm> getAlarms(Alarm.SeverityLevel severity);
 
     /**
-     * Returns the alarm for a given device, regardless of source within that
-     * device.
+     * Returns the alarm matching a given device, regardless of source within that device.
      *
-     * @param deviceId the device
-     * @return the alarms
+     * @param deviceId the device to use when searching alarms.
+     * @return set of alarms; empty set if no alarms
      */
     Set<Alarm> getAlarms(DeviceId deviceId);
 
@@ -95,7 +99,7 @@
      *
      * @param deviceId the device
      * @param source the source within the device
-     * @return the alarms
+     * @return set of alarms; empty set if no alarms
      */
     Set<Alarm> getAlarms(DeviceId deviceId, AlarmEntityId source);
 
@@ -104,7 +108,7 @@
      *
      * @param src one end of the link
      * @param dst one end of the link
-     * @return the alarms
+     * @return set of alarms; empty set if no alarms
      */
     Set<Alarm> getAlarmsForLink(ConnectPoint src, ConnectPoint dst);
 
@@ -113,9 +117,9 @@
      *
      * @param deviceId the device
      * @param flowId the flow
-     * @return the alarms
+     * @return set of alarms; empty set if no alarms
      */
     Set<Alarm> getAlarmsForFlow(DeviceId deviceId, long flowId);
 
-// Support retrieving alarms affecting other ONOS entity types may be added in future release
+    // TODO Support retrieving alarms affecting other entity types may be added in future release
 }
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/DefaultAlarm.java b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/DefaultAlarm.java
index afa366a..013eec1 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/DefaultAlarm.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/DefaultAlarm.java
@@ -145,14 +145,16 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(id, deviceId, description,
-                source, timeRaised, timeUpdated, timeCleared, severity,
+        // id or timeRaised or timeUpdated may differ
+        return Objects.hash(deviceId, description,
+                source, timeCleared, severity,
                 isServiceAffecting, isAcknowledged,
                 isManuallyClearable, assignedUser);
     }
 
     @Override
     public boolean equals(final Object obj) {
+        // Make sure equals() is tune with hashCode() so works ok in a hashSet !
         if (obj == null) {
             return false;
         }
@@ -160,9 +162,8 @@
             return false;
         }
         final DefaultAlarm other = (DefaultAlarm) obj;
-        if (!Objects.equals(this.id, other.id)) {
-            return false;
-        }
+
+        // id or timeRaised or timeUpdated may differ
         if (!Objects.equals(this.deviceId, other.deviceId)) {
             return false;
         }
@@ -172,12 +173,7 @@
         if (!Objects.equals(this.source, other.source)) {
             return false;
         }
-        if (this.timeRaised != other.timeRaised) {
-            return false;
-        }
-        if (this.timeUpdated != other.timeUpdated) {
-            return false;
-        }
+
         if (!Objects.equals(this.timeCleared, other.timeCleared)) {
             return false;
         }
@@ -219,11 +215,11 @@
 
     public static class Builder {
 
-        // Manadatory fields ..
-        private final AlarmId id;
+        // Manadatory fields when constructing alarm ...
+        private AlarmId id;
         private final DeviceId deviceId;
         private final String description;
-        private final SeverityLevel severity;
+        private SeverityLevel severity;
         private final long timeRaised;
 
         // Optional fields ..
@@ -236,8 +232,8 @@
         private String assignedUser = null;
 
         public Builder(final Alarm alarm) {
-            this(alarm.id(), alarm.deviceId(), alarm.description(), alarm.severity(), alarm.timeRaised());
-            this.source = AlarmEntityId.NONE;
+            this(alarm.deviceId(), alarm.description(), alarm.severity(), alarm.timeRaised());
+            this.source = alarm.source();
             this.timeUpdated = alarm.timeUpdated();
             this.timeCleared = alarm.timeCleared();
             this.isServiceAffecting = alarm.serviceAffecting();
@@ -247,10 +243,10 @@
 
         }
 
-        public Builder(final AlarmId id, final DeviceId deviceId,
+        public Builder(final DeviceId deviceId,
                 final String description, final SeverityLevel severity, final long timeRaised) {
             super();
-            this.id = id;
+            this.id = AlarmId.NONE;
             this.deviceId = deviceId;
             this.description = description;
             this.severity = severity;
@@ -274,6 +270,17 @@
             return this;
         }
 
+        public Builder withId(final AlarmId id) {
+            this.id = id;
+            return this;
+        }
+
+        public Builder clear() {
+            this.severity = SeverityLevel.CLEARED;
+            final long now = System.currentTimeMillis();
+            return withTimeCleared(now).withTimeUpdated(now);
+        }
+
         public Builder withServiceAffecting(final boolean isServiceAffecting) {
             this.isServiceAffecting = isServiceAffecting;
             return this;