[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/apps/faultmanagement/fmweb/pom.xml b/apps/faultmanagement/fmweb/pom.xml
index a67ad56..daaa09b 100644
--- a/apps/faultmanagement/fmweb/pom.xml
+++ b/apps/faultmanagement/fmweb/pom.xml
@@ -124,6 +124,7 @@
                             com.fasterxml.jackson.core,
                             org.apache.karaf.shell.commands,
                             org.apache.commons.lang.math.*,
+                            org.apache.commons.lang.*,
                             com.google.common.*,
                             org.onlab.packet.*,
                             org.onlab.rest.*,
diff --git a/apps/faultmanagement/fmweb/src/main/java/org/onosproject/faultmanagement/web/AlarmCodec.java b/apps/faultmanagement/fmweb/src/main/java/org/onosproject/faultmanagement/web/AlarmCodec.java
index 60587bd..279e6b2 100644
--- a/apps/faultmanagement/fmweb/src/main/java/org/onosproject/faultmanagement/web/AlarmCodec.java
+++ b/apps/faultmanagement/fmweb/src/main/java/org/onosproject/faultmanagement/web/AlarmCodec.java
@@ -66,28 +66,29 @@
         }
 
         log.debug("id={}, full json={} ", json.get("id"), json);
-        final Long id = json.get("id").asLong();
+        Long id = json.get("id").asLong();
 
-        final DeviceId deviceId = DeviceId.deviceId(json.get("deviceId").asText());
-        final String description = json.get("description").asText();
-        final Long timeRaised = json.get("timeRaised").asLong();
-        final Long timeUpdated = json.get("timeUpdated").asLong();
+        DeviceId deviceId = DeviceId.deviceId(json.get("deviceId").asText());
+        String description = json.get("description").asText();
+        Long timeRaised = json.get("timeRaised").asLong();
+        Long timeUpdated = json.get("timeUpdated").asLong();
 
-        final JsonNode jsonTimeCleared = json.get("timeCleared");
-        final Long timeCleared = jsonTimeCleared == null || jsonTimeCleared.isNull() ? null : jsonTimeCleared.asLong();
+        JsonNode jsonTimeCleared = json.get("timeCleared");
+        Long timeCleared = jsonTimeCleared == null || jsonTimeCleared.isNull() ? null : jsonTimeCleared.asLong();
 
-        final Alarm.SeverityLevel severity = Alarm.SeverityLevel.valueOf(json.get("severity").asText().toUpperCase());
+        Alarm.SeverityLevel severity = Alarm.SeverityLevel.valueOf(json.get("severity").asText().toUpperCase());
 
-        final Boolean serviceAffecting = json.get("serviceAffecting").asBoolean();
-        final Boolean acknowledged = json.get("acknowledged").asBoolean();
-        final Boolean manuallyClearable = json.get("manuallyClearable").asBoolean();
+        Boolean serviceAffecting = json.get("serviceAffecting").asBoolean();
+        Boolean acknowledged = json.get("acknowledged").asBoolean();
+        Boolean manuallyClearable = json.get("manuallyClearable").asBoolean();
 
-        final JsonNode jsonAssignedUser = json.get("assignedUser");
-        final String assignedUser
+        JsonNode jsonAssignedUser = json.get("assignedUser");
+        String assignedUser
                 = jsonAssignedUser == null || jsonAssignedUser.isNull() ? null : jsonAssignedUser.asText();
 
         return new DefaultAlarm.Builder(
-                AlarmId.valueOf(id), deviceId, description, severity, timeRaised).forSource(AlarmEntityId.NONE).
+                deviceId, description, severity, timeRaised).forSource(AlarmEntityId.NONE).
+                withId(AlarmId.alarmId(id)).
                 withTimeUpdated(timeUpdated).
                 withTimeCleared(timeCleared).
                 withServiceAffecting(serviceAffecting).
diff --git a/apps/faultmanagement/fmweb/src/main/java/org/onosproject/faultmanagement/web/AlarmsWebResource.java b/apps/faultmanagement/fmweb/src/main/java/org/onosproject/faultmanagement/web/AlarmsWebResource.java
index d62fa76..118cd18 100644
--- a/apps/faultmanagement/fmweb/src/main/java/org/onosproject/faultmanagement/web/AlarmsWebResource.java
+++ b/apps/faultmanagement/fmweb/src/main/java/org/onosproject/faultmanagement/web/AlarmsWebResource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014-2015 Open Networking Laboratory
+ * Copyright 2015 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.
@@ -34,7 +34,9 @@
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
+import org.apache.commons.lang.StringUtils;
 import org.onosproject.incubator.net.faultmanagement.alarm.AlarmService;
+import org.onosproject.net.DeviceId;
 import org.slf4j.Logger;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -48,31 +50,32 @@
 
     private final Logger log = getLogger(getClass());
 
-    public AlarmsWebResource() {
-    }
-
     /**
-     * Get all alarms. Returns a list of all alarms across all devices.
+     * Get alarms. Returns a list of alarms
      *
-     * @param includeCleared include recently cleared alarms in response
+     * @param includeCleared (optional) include recently cleared alarms in response
+     * @param devId (optional) include only for specified device
      * @return JSON encoded set of alarms
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
-    public Response getAlarms(@DefaultValue("false") @QueryParam("includeCleared") boolean includeCleared
+    public Response getAlarms(@DefaultValue("false") @QueryParam("includeCleared") boolean includeCleared,
+            @DefaultValue("") @QueryParam("devId") String devId
     ) {
 
         log.info("Requesting all alarms, includeCleared={}", includeCleared);
-        final AlarmService service = get(AlarmService.class);
+        AlarmService service = get(AlarmService.class);
 
-        final Iterable<Alarm> alarms = includeCleared
-                ? service.getAlarms()
-                : service.getActiveAlarms();
-
-        final ObjectNode result = new ObjectMapper().createObjectNode();
-        result.set("alarms",
-                codec(Alarm.class).
-                encode(alarms, this));
+        Iterable<Alarm> alarms;
+        if (StringUtils.isBlank(devId)) {
+            alarms = includeCleared
+                    ? service.getAlarms()
+                    : service.getActiveAlarms();
+        } else {
+            alarms = service.getAlarms(DeviceId.deviceId(devId));
+        }
+        ObjectNode result = new ObjectMapper().createObjectNode();
+        result.set("alarms", new AlarmCodec().encode(alarms, this));
         return ok(result.toString()).build();
 
     }
@@ -89,11 +92,11 @@
     public Response getAlarm(@PathParam("id") String id) {
         log.info("HTTP GET alarm for id={}", id);
 
-        final AlarmId alarmId = toAlarmId(id);
-        final Alarm alarm = get(AlarmService.class).getAlarm(alarmId);
+        AlarmId alarmId = toAlarmId(id);
+        Alarm alarm = get(AlarmService.class).getAlarm(alarmId);
 
-        final ObjectNode result = mapper().createObjectNode();
-        result.set("alarm", codec(Alarm.class).encode(alarm, this));
+        ObjectNode result = new ObjectMapper().createObjectNode();
+        result.set("alarm", new AlarmCodec().encode(alarm, this));
         return ok(result.toString()).build();
     }
 
@@ -113,20 +116,22 @@
         log.info("PUT NEW ALARM at /{}", alarmIdPath);
 
         try {
-            final ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
+            ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
             log.info("jsonTree={}", jsonTree);
 
-            final Alarm alarm = codec(Alarm.class).decode(jsonTree, this);
+            Alarm alarm = new AlarmCodec().decode(jsonTree, this);
 
-            final AlarmService service = get(AlarmService.class);
+            AlarmService service = get(AlarmService.class);
 
             if (Long.parseLong(alarmIdPath) != alarm.id().fingerprint()) {
                 throw new IllegalArgumentException("id in path is " + Long.parseLong(alarmIdPath)
                         + " but payload uses id=" + alarm.id().fingerprint());
 
             }
-            final Alarm updated = service.update(alarm);
-            final ObjectNode encoded = new AlarmCodec().encode(updated, this);
+            Alarm updated = service.updateBookkeepingFields(
+                    alarm.id(), alarm.acknowledged(), alarm.assignedUser()
+            );
+            ObjectNode encoded = new AlarmCodec().encode(updated, this);
             return ok(encoded.toString()).build();
 
         } catch (IOException ioe) {
@@ -136,7 +141,7 @@
 
     private static AlarmId toAlarmId(String id) {
         try {
-            return AlarmId.valueOf(Long.parseLong(id));
+            return AlarmId.alarmId(Long.parseLong(id));
         } catch (NumberFormatException ex) {
             throw new IllegalArgumentException("Alarm id should be numeric", ex);
         }
diff --git a/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmCodecContext.java b/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmCodecContext.java
index 6b80b72..56da337 100644
--- a/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmCodecContext.java
+++ b/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmCodecContext.java
@@ -25,7 +25,7 @@
 /**
  * Mock codec context for use in codec unit tests.
  */
-public class AlarmCodecContext implements CodecContext {
+public final class AlarmCodecContext implements CodecContext {
 
     private final ObjectMapper mapper = new ObjectMapper();
     private final CodecManager codecManager = new CodecManager();
diff --git a/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmCodecTest.java b/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmCodecTest.java
index 012b1ff..6d06b58 100644
--- a/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmCodecTest.java
+++ b/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmCodecTest.java
@@ -36,37 +36,26 @@
 public class AlarmCodecTest {
 
     private final AlarmCodecContext context = new AlarmCodecContext();
+    private static final AlarmId ALARM_ID = AlarmId.alarmId(44);
 
     // Use this to check handling for miminal Alarm
     private final Alarm alarmMinimumFields = new DefaultAlarm.Builder(
-            new AlarmId(44),
-            DeviceId.deviceId("of:2222000000000000"),
-            "NE unreachable",
-            Alarm.SeverityLevel.CLEARED,
-            1).
-            build();
+            DeviceId.deviceId("of:2222000000000000"), "NE unreachable", Alarm.SeverityLevel.CLEARED, 1
+    ).withId(ALARM_ID).build();
 
     // Use this to check handling for fully populated Alarm
     private final Alarm alarmWithSource = new DefaultAlarm.Builder(
-            new AlarmId(44),
-            DeviceId.deviceId("of:2222000000000000"),
-            "NE unreachable",
-            Alarm.SeverityLevel.CLEARED, 1).
-            forSource(AlarmEntityId.alarmEntityId("port:1/2/3/4")).
-            withTimeUpdated(2).
-            withTimeCleared(3L).
-            withServiceAffecting(true).
-            withAcknowledged(true).
-            withManuallyClearable(true).
+            DeviceId.deviceId("of:2222000000000000"), "NE unreachable", Alarm.SeverityLevel.CLEARED, 1
+    ).withId(ALARM_ID).forSource(AlarmEntityId.alarmEntityId("port:1/2/3/4")).withTimeUpdated(2).withTimeCleared(3L).
+            withServiceAffecting(true).withAcknowledged(true).withManuallyClearable(true).
             withAssignedUser("the assigned user").build();
 
     @Test
     public void alarmCodecTestWithOptionalFieldMissing() {
-        //context.registerService(AlarmService.class, new AlarmServiceAdapter());
-        final JsonCodec<Alarm> codec = context.codec(Alarm.class);
+        JsonCodec<Alarm> codec = context.codec(Alarm.class);
         assertThat(codec, is(notNullValue()));
 
-        final ObjectNode alarmJson = codec.encode(alarmMinimumFields, context);
+        ObjectNode alarmJson = codec.encode(alarmMinimumFields, context);
         assertThat(alarmJson, notNullValue());
         assertThat(alarmJson, matchesAlarm(alarmMinimumFields));
 
@@ -74,10 +63,10 @@
 
     @Test
     public void alarmCodecTestWithOptionalField() {
-        final JsonCodec<Alarm> codec = context.codec(Alarm.class);
+        JsonCodec<Alarm> codec = context.codec(Alarm.class);
         assertThat(codec, is(notNullValue()));
 
-        final ObjectNode alarmJson = codec.encode(alarmWithSource, context);
+        ObjectNode alarmJson = codec.encode(alarmWithSource, context);
         assertThat(alarmJson, notNullValue());
         assertThat(alarmJson, matchesAlarm(alarmWithSource));
 
@@ -85,9 +74,9 @@
 
     @Test
     public void verifyMinimalAlarmIsEncoded() throws Exception {
-        final JsonCodec<Alarm> alarmCodec = context.codec(Alarm.class);
+        JsonCodec<Alarm> alarmCodec = context.codec(Alarm.class);
 
-        final Alarm alarm = getDecodedAlarm(alarmCodec, "alarm-minimal.json");
+        Alarm alarm = getDecodedAlarm(alarmCodec, "alarm-minimal.json");
         assertCommon(alarm);
 
         assertThat(alarm.timeCleared(), nullValue());
@@ -97,9 +86,9 @@
 
     @Test
     public void verifyFullyLoadedAlarmIsEncoded() throws Exception {
-        final JsonCodec<Alarm> alarmCodec = context.codec(Alarm.class);
+        JsonCodec<Alarm> alarmCodec = context.codec(Alarm.class);
 
-        final Alarm alarm = getDecodedAlarm(alarmCodec, "alarm-full.json");
+        Alarm alarm = getDecodedAlarm(alarmCodec, "alarm-full.json");
         assertCommon(alarm);
 
         assertThat(alarm.timeCleared(), is(2222L));
@@ -108,7 +97,7 @@
     }
 
     private void assertCommon(Alarm alarm) {
-        assertThat(alarm.id(), is(new AlarmId(10L)));
+        assertThat(alarm.id(), is(AlarmId.alarmId(10L)));
         assertThat(alarm.description(), is("NE is not reachable"));
         assertThat(alarm.source(), is(AlarmEntityId.NONE));
         assertThat(alarm.timeRaised(), is(999L));
@@ -127,14 +116,14 @@
      * @throws IOException if processing the resource fails to decode
      */
     private Alarm getDecodedAlarm(JsonCodec<Alarm> codec, String resourceName) throws IOException {
-        final InputStream jsonStream = AlarmCodecTest.class
-                .getResourceAsStream(resourceName);
-        final JsonNode json = context.mapper().readTree(jsonStream);
-        assertThat(json, notNullValue());
-        final Alarm result = codec.decode((ObjectNode) json, context);
-        assertThat(result, notNullValue());
-        return result;
+        try (InputStream jsonStream = AlarmCodecTest.class
+                .getResourceAsStream(resourceName)) {
+            JsonNode json = context.mapper().readTree(jsonStream);
+            assertThat(json, notNullValue());
+            Alarm result = codec.decode((ObjectNode) json, context);
+            assertThat(result, notNullValue());
+            return result;
+        }
     }
 
-
 }
diff --git a/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmJsonMatcher.java b/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmJsonMatcher.java
index 9a295c8..d32383c 100644
--- a/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmJsonMatcher.java
+++ b/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmJsonMatcher.java
@@ -34,48 +34,48 @@
 
     @Override
     public boolean matchesSafely(JsonNode jsonAlarm, Description description) {
-        final String jsonAlarmId = jsonAlarm.get("id").asText();
-        final String alarmId = Long.toString(alarm.id().fingerprint());
+        String jsonAlarmId = jsonAlarm.get("id").asText();
+        String alarmId = Long.toString(alarm.id().fingerprint());
         if (!jsonAlarmId.equals(alarmId)) {
             description.appendText("alarm id was " + jsonAlarmId);
             return false;
         }
 
-        final String jsonDeviceId = jsonAlarm.get("deviceId").asText();
-        final String alarmDeviceId = alarm.deviceId().toString();
+        String jsonDeviceId = jsonAlarm.get("deviceId").asText();
+        String alarmDeviceId = alarm.deviceId().toString();
         if (!jsonDeviceId.equals(alarmDeviceId)) {
             description.appendText("DeviceId was " + jsonDeviceId);
             return false;
         }
 
 
-        final String jsonDescription = jsonAlarm.get("description").asText();
-        final String alarmDesc = alarm.description();
+        String jsonDescription = jsonAlarm.get("description").asText();
+        String alarmDesc = alarm.description();
         if (!jsonDescription.equals(alarmDesc)) {
             description.appendText("description was " + jsonDescription);
             return false;
         }
 
-        final long jsonTimeRaised = jsonAlarm.get("timeRaised").asLong();
-        final long timeRaised = alarm.timeRaised();
+        long jsonTimeRaised = jsonAlarm.get("timeRaised").asLong();
+        long timeRaised = alarm.timeRaised();
         if (timeRaised != jsonTimeRaised) {
             description.appendText("timeRaised was " + jsonTimeRaised);
             return false;
         }
 
 
-        final long jsonTimeUpdated = jsonAlarm.get("timeUpdated").asLong();
-        final long timeUpdated = alarm.timeUpdated();
+        long jsonTimeUpdated = jsonAlarm.get("timeUpdated").asLong();
+        long timeUpdated = alarm.timeUpdated();
         if (timeUpdated != jsonTimeUpdated) {
             description.appendText("timeUpdated was " + jsonTimeUpdated);
             return false;
         }
 
-        final JsonNode jsonTimeClearedNode = jsonAlarm.get("timeCleared");
+        JsonNode jsonTimeClearedNode = jsonAlarm.get("timeCleared");
 
         if (alarm.timeCleared() != null) {
-            final Long jsonTimeCleared = jsonTimeClearedNode.longValue();
-            final Long timeCleared = alarm.timeCleared();
+            Long jsonTimeCleared = jsonTimeClearedNode.longValue();
+            Long timeCleared = alarm.timeCleared();
 
             if (!timeCleared.equals(jsonTimeCleared)) {
                 description.appendText("Time Cleared was " + jsonTimeCleared);
@@ -89,18 +89,18 @@
             }
         }
 
-        final String jsonSeverity = jsonAlarm.get("severity").asText();
-        final String severity = alarm.severity().toString();
+        String jsonSeverity = jsonAlarm.get("severity").asText();
+        String severity = alarm.severity().toString();
         if (!severity.equals(jsonSeverity)) {
             description.appendText("severity was " + jsonSeverity);
             return false;
         }
 
-        final JsonNode jsonAlarmNode = jsonAlarm.get("source");
+        JsonNode jsonAlarmNode = jsonAlarm.get("source");
 
         if (alarm.source() != null) {
-            final String jsonSource = jsonAlarmNode.textValue();
-            final String source = alarm.source().toString();
+            String jsonSource = jsonAlarmNode.textValue();
+            String source = alarm.source().toString();
 
             if (!source.equals(jsonSource)) {
                 description.appendText("source was " + jsonSource);
diff --git a/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmsWebResourceTest.java b/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmsWebResourceTest.java
index e07487c..027a548 100644
--- a/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmsWebResourceTest.java
+++ b/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmsWebResourceTest.java
@@ -38,10 +38,10 @@
     @Before
     public void setUp() {
 
-        final CodecManager codecService = new CodecManager();
+        CodecManager codecService = new CodecManager();
         codecService.activate();
 
-        final ServiceDirectory testDirectory = new TestServiceDirectory()
+        ServiceDirectory testDirectory = new TestServiceDirectory()
                 // Currently no alarms-service implemented
                 // .add(AlarmsService.class, alarmsService)
                 .add(CodecService.class, codecService);
@@ -51,8 +51,8 @@
     @Test
     @Ignore
     public void getAllAlarms() {
-        final WebResource rs = resource();
-        final String response = rs.path("/alarms").get(String.class);
+        WebResource rs = resource();
+        String response = rs.path("/alarms").get(String.class);
         // Ensure hard-coded alarms returned okay
         assertThat(response, containsString("\"NE is not reachable\","));
         assertThat(response, containsString("\"Equipment Missing\","));
@@ -61,8 +61,8 @@
     @Test
     @Ignore
     public void getAlarm() {
-        final WebResource rs = resource();
-        final String response = rs.path("/alarms/1").get(String.class);
+        WebResource rs = resource();
+        String response = rs.path("/alarms/1").get(String.class);
         // Ensure hard-coded alarms returned okay
         assertThat(response, containsString("\"NE is not reachable\","));
         assertThat(response, not(containsString("\"Equipment Missing\",")));