Publish cluster-wide AAA stats

Change-Id: Icbdacdae08b6235be022f85eb41ce6d0f8f35a35
diff --git a/api/src/main/java/org/opencord/aaa/AaaStatistics.java b/api/src/main/java/org/opencord/aaa/AaaStatistics.java
index c9953e3..436979e 100644
--- a/api/src/main/java/org/opencord/aaa/AaaStatistics.java
+++ b/api/src/main/java/org/opencord/aaa/AaaStatistics.java
@@ -16,10 +16,84 @@
 
 package org.opencord.aaa;
 
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableMap;
+
 import java.util.LinkedList;
 import java.util.concurrent.atomic.AtomicLong;
 
+/**
+ * Records metrics for the AAA application.
+ */
 public class AaaStatistics {
+    public static final String ACCEPT_RESPONSES_RX = "acceptResponsesRx";
+    public static final String REJECT_RESPONSES_RX = "rejectResponsesRx";
+    public static final String CHALLENGE_RESPONSES_RX = "challengeResponsesRx";
+    public static final String ACCESS_REQUESTS_TX = "accessRequestsTx";
+    public static final String PENDING_REQUESTS = "pendingRequests";
+    public static final String TIMED_OUT_PACKETS = "timedOutPackets";
+    public static final String UNKNOWN_TYPE_RX = "unknownTypeRx";
+    public static final String INVALID_VALIDATORS_RX = "invalidValidatorsRx";
+    public static final String DROPPED_RESPONSES_RX = "droppedResponsesRx";
+    public static final String MALFORMED_RESPONSES_RX = "malformedResponsesRx";
+    public static final String UNKNOWN_SERVER_RX = "unknownServerRx";
+    public static final String REQUEST_RTT_MILLIS = "requestRttMillis";
+    public static final String REQUEST_RE_TX = "requestReTx";
+    public static final String NUM_SESSIONS_EXPIRED = "numSessionsExpired";
+    public static final String EAPOL_LOGOFF_RX = "eapolLogoffRx";
+    public static final String EAPOL_AUTH_SUCCESS_TRANS = "eapolAuthSuccessTrans";
+    public static final String EAPOL_AUTH_FAILURE_TRANS = "eapolAuthFailureTrans";
+    public static final String EAPOL_START_REQ_TRANS = "eapolStartReqTrans";
+    public static final String EAPOL_MD5_RESP_CHALLENGE = "eapolMd5RespChallenge";
+    public static final String EAPOL_TLS_RESP_CHALLENGE = "eapolTlsRespChallenge";
+    public static final String EAPOL_TRANS_RESP_NOT_NAK = "eapolTransRespNotNak";
+    public static final String EAP_PKT_TX_AUTH_CHOOSE_EAP = "eapPktTxauthChooseEap";
+    public static final String RES_ID_EAP_FRAMES_RX = "resIdEapFramesRx";
+    public static final String EAPOL_RES_IDENTITY_MSG_TRANS = "eapolResIdentityMsgTrans";
+    public static final String EAPOL_FRAMES_TX = "eapolFramesTx";
+    public static final String AUTH_STATE_IDLE = "authStateIdle";
+    public static final String REQUEST_ID_FRAMES_TX = "requestIdFramesTx";
+    public static final String REQUEST_EAP_FRAMES_TX = "requestEapFramesTx";
+    public static final String INVALID_PKT_TYPE = "invalidPktType";
+    public static final String INVALID_BODY_LENGTH = "invalidBodyLength";
+    public static final String VALID_EAPOL_FRAMES_RX = "validEapolFramesRx";
+    public static final String PENDING_RES_SUPPLICANT = "pendingResSupplicant";
+
+    public static final String[] COUNTER_NAMES = new String[]{
+            ACCEPT_RESPONSES_RX,
+            REJECT_RESPONSES_RX,
+            CHALLENGE_RESPONSES_RX,
+            ACCESS_REQUESTS_TX,
+            PENDING_REQUESTS,
+            TIMED_OUT_PACKETS,
+            UNKNOWN_TYPE_RX,
+            INVALID_VALIDATORS_RX,
+            DROPPED_RESPONSES_RX,
+            MALFORMED_RESPONSES_RX,
+            UNKNOWN_SERVER_RX,
+            REQUEST_RTT_MILLIS,
+            REQUEST_RE_TX,
+            NUM_SESSIONS_EXPIRED,
+            EAPOL_LOGOFF_RX,
+            EAPOL_AUTH_SUCCESS_TRANS,
+            EAPOL_AUTH_FAILURE_TRANS,
+            EAPOL_START_REQ_TRANS,
+            EAPOL_MD5_RESP_CHALLENGE,
+            EAPOL_TLS_RESP_CHALLENGE,
+            EAPOL_TRANS_RESP_NOT_NAK,
+            EAP_PKT_TX_AUTH_CHOOSE_EAP,
+            RES_ID_EAP_FRAMES_RX,
+            EAPOL_RES_IDENTITY_MSG_TRANS,
+            EAPOL_FRAMES_TX,
+            AUTH_STATE_IDLE,
+            REQUEST_ID_FRAMES_TX,
+            REQUEST_EAP_FRAMES_TX,
+            INVALID_PKT_TYPE,
+            INVALID_BODY_LENGTH,
+            VALID_EAPOL_FRAMES_RX,
+            PENDING_RES_SUPPLICANT,
+    };
+
     // Number of access accept packets sent to the server
     private AtomicLong acceptResponsesRx = new AtomicLong();
     // Number of access reject packets sent to the server
@@ -386,8 +460,11 @@
         validEapolFramesRx.set(0);
         pendingResSupp.set(0);
         timedOutPackets.set(0);
-
+        eapolMd5RspChall.set(0);
+        eapolTlsRespChall.set(0);
+        eapolAttrIdentity.set(0);
     }
+
     public void countTransRespNotNak() {
         long eapolTransactionNotNak = eapolMd5RspChall.get();
         eapolTransactionNotNak += eapolTlsRespChall.get();
@@ -413,4 +490,123 @@
         timedOutPackets.incrementAndGet();
     }
 
+    /**
+     * Creates a snapshot of the current values of the counters.
+     *
+     * @return statistics snapshot
+     */
+    public AaaStatisticsSnapshot snapshot() {
+        ImmutableMap.Builder<String, Long> builder = ImmutableMap.builder();
+        builder.put(ACCEPT_RESPONSES_RX, acceptResponsesRx.get())
+                .put(REJECT_RESPONSES_RX, rejectResponsesRx.get())
+                .put(CHALLENGE_RESPONSES_RX, challengeResponsesRx.get())
+                .put(ACCESS_REQUESTS_TX, accessRequestsTx.get())
+                .put(PENDING_REQUESTS, pendingRequests.get())
+                .put(TIMED_OUT_PACKETS, timedOutPackets.get())
+                .put(UNKNOWN_TYPE_RX, unknownTypeRx.get())
+                .put(INVALID_VALIDATORS_RX, invalidValidatorsRx.get())
+                .put(DROPPED_RESPONSES_RX, droppedResponsesRx.get())
+                .put(MALFORMED_RESPONSES_RX, malformedResponsesRx.get())
+                .put(UNKNOWN_SERVER_RX, unknownServerRx.get())
+                .put(REQUEST_RTT_MILLIS, requestRttMilis.get())
+                .put(REQUEST_RE_TX, requestReTx.get())
+                .put(NUM_SESSIONS_EXPIRED, numberOfSessionsExpired.get())
+                .put(EAPOL_LOGOFF_RX, eapolLogoffRx.get())
+                .put(EAPOL_AUTH_SUCCESS_TRANS, eapolAuthSuccessTrans.get())
+                .put(EAPOL_AUTH_FAILURE_TRANS, eapolAuthFailureTrans.get())
+                .put(EAPOL_START_REQ_TRANS, eapolStartReqTrans.get())
+                .put(EAPOL_MD5_RESP_CHALLENGE, eapolMd5RspChall.get())
+                .put(EAPOL_TLS_RESP_CHALLENGE, eapolTlsRespChall.get())
+                .put(EAPOL_TRANS_RESP_NOT_NAK, eapolTransRespNotNak.get())
+                .put(EAP_PKT_TX_AUTH_CHOOSE_EAP, eapPktTxauthChooseEap.get())
+                .put(RES_ID_EAP_FRAMES_RX, eapolAttrIdentity.get())
+                .put(EAPOL_RES_IDENTITY_MSG_TRANS, eapolResIdentityMsgTrans.get())
+                .put(EAPOL_FRAMES_TX, eapolFramesTx.get())
+                .put(AUTH_STATE_IDLE, authStateIdle.get())
+                .put(REQUEST_ID_FRAMES_TX, requestIdFramesTx.get())
+                .put(REQUEST_EAP_FRAMES_TX, reqEapFramesTx.get())
+                .put(INVALID_PKT_TYPE, invalidPktType.get())
+                .put(INVALID_BODY_LENGTH, invalidBodyLength.get())
+                .put(VALID_EAPOL_FRAMES_RX, validEapolFramesRx.get())
+                .put(PENDING_RES_SUPPLICANT, pendingResSupp.get());
+
+        return new AaaStatisticsSnapshot(builder.build());
+    }
+
+    public static AaaStatistics fromSnapshot(AaaStatisticsSnapshot snapshot) {
+        AaaStatistics stats = new AaaStatistics();
+
+        stats.acceptResponsesRx.set(snapshot.get(ACCEPT_RESPONSES_RX));
+        stats.rejectResponsesRx.set(snapshot.get(REJECT_RESPONSES_RX));
+        stats.challengeResponsesRx.set(snapshot.get(CHALLENGE_RESPONSES_RX));
+        stats.accessRequestsTx.set(snapshot.get(ACCESS_REQUESTS_TX));
+        stats.pendingRequests.set(snapshot.get(PENDING_REQUESTS));
+        stats.timedOutPackets.set(snapshot.get(TIMED_OUT_PACKETS));
+        stats.unknownTypeRx.set(snapshot.get(UNKNOWN_TYPE_RX));
+        stats.invalidValidatorsRx.set(snapshot.get(INVALID_VALIDATORS_RX));
+        stats.droppedResponsesRx.set(snapshot.get(DROPPED_RESPONSES_RX));
+        stats.malformedResponsesRx.set(snapshot.get(MALFORMED_RESPONSES_RX));
+        stats.unknownServerRx.set(snapshot.get(UNKNOWN_SERVER_RX));
+        stats.requestRttMilis.set(snapshot.get(REQUEST_RTT_MILLIS));
+        stats.requestReTx.set(snapshot.get(REQUEST_RE_TX));
+        stats.numberOfSessionsExpired.set(snapshot.get(NUM_SESSIONS_EXPIRED));
+        stats.eapolLogoffRx.set(snapshot.get(EAPOL_LOGOFF_RX));
+        stats.eapolAuthSuccessTrans.set(snapshot.get(EAPOL_AUTH_SUCCESS_TRANS));
+        stats.eapolAuthFailureTrans.set(snapshot.get(EAPOL_AUTH_FAILURE_TRANS));
+        stats.eapolStartReqTrans.set(snapshot.get(EAPOL_START_REQ_TRANS));
+        stats.eapolMd5RspChall.set(snapshot.get(EAPOL_MD5_RESP_CHALLENGE));
+        stats.eapolTlsRespChall.set(snapshot.get(EAPOL_TLS_RESP_CHALLENGE));
+        stats.eapolTransRespNotNak.set(snapshot.get(EAPOL_TRANS_RESP_NOT_NAK));
+        stats.eapPktTxauthChooseEap.set(snapshot.get(EAP_PKT_TX_AUTH_CHOOSE_EAP));
+        stats.eapolAttrIdentity.set(snapshot.get(RES_ID_EAP_FRAMES_RX));
+        stats.eapolResIdentityMsgTrans.set(snapshot.get(EAPOL_RES_IDENTITY_MSG_TRANS));
+        stats.eapolFramesTx.set(snapshot.get(EAPOL_FRAMES_TX));
+        stats.authStateIdle.set(snapshot.get(AUTH_STATE_IDLE));
+        stats.requestIdFramesTx.set(snapshot.get(REQUEST_ID_FRAMES_TX));
+        stats.reqEapFramesTx.set(snapshot.get(REQUEST_EAP_FRAMES_TX));
+        stats.invalidPktType.set(snapshot.get(INVALID_PKT_TYPE));
+        stats.invalidBodyLength.set(snapshot.get(INVALID_BODY_LENGTH));
+        stats.validEapolFramesRx.set(snapshot.get(VALID_EAPOL_FRAMES_RX));
+        stats.pendingResSupp.set(snapshot.get(PENDING_RES_SUPPLICANT));
+
+        return stats;
+    }
+
+    public String toString() {
+        MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this.getClass());
+        helper.add(ACCEPT_RESPONSES_RX, acceptResponsesRx.get())
+                .add(REJECT_RESPONSES_RX, rejectResponsesRx.get())
+                .add(CHALLENGE_RESPONSES_RX, challengeResponsesRx.get())
+                .add(ACCESS_REQUESTS_TX, accessRequestsTx.get())
+                .add(PENDING_REQUESTS, pendingRequests.get())
+                .add(TIMED_OUT_PACKETS, timedOutPackets.get())
+                .add(UNKNOWN_TYPE_RX, unknownTypeRx.get())
+                .add(INVALID_VALIDATORS_RX, invalidValidatorsRx.get())
+                .add(DROPPED_RESPONSES_RX, droppedResponsesRx.get())
+                .add(MALFORMED_RESPONSES_RX, malformedResponsesRx.get())
+                .add(UNKNOWN_SERVER_RX, unknownServerRx.get())
+                .add(REQUEST_RTT_MILLIS, requestRttMilis.get())
+                .add(REQUEST_RE_TX, requestReTx.get())
+                .add(NUM_SESSIONS_EXPIRED, numberOfSessionsExpired.get())
+                .add(EAPOL_LOGOFF_RX, eapolLogoffRx.get())
+                .add(EAPOL_AUTH_SUCCESS_TRANS, eapolAuthSuccessTrans.get())
+                .add(EAPOL_AUTH_FAILURE_TRANS, eapolAuthFailureTrans.get())
+                .add(EAPOL_START_REQ_TRANS, eapolStartReqTrans.get())
+                .add(EAPOL_MD5_RESP_CHALLENGE, eapolMd5RspChall.get())
+                .add(EAPOL_TLS_RESP_CHALLENGE, eapolTlsRespChall.get())
+                .add(EAPOL_TRANS_RESP_NOT_NAK, eapolTransRespNotNak.get())
+                .add(EAP_PKT_TX_AUTH_CHOOSE_EAP, eapPktTxauthChooseEap.get())
+                .add(RES_ID_EAP_FRAMES_RX, eapolAttrIdentity.get())
+                .add(EAPOL_RES_IDENTITY_MSG_TRANS, eapolResIdentityMsgTrans.get())
+                .add(EAPOL_FRAMES_TX, eapolFramesTx.get())
+                .add(AUTH_STATE_IDLE, authStateIdle.get())
+                .add(REQUEST_ID_FRAMES_TX, requestIdFramesTx.get())
+                .add(REQUEST_EAP_FRAMES_TX, reqEapFramesTx.get())
+                .add(INVALID_PKT_TYPE, invalidPktType.get())
+                .add(INVALID_BODY_LENGTH, invalidBodyLength.get())
+                .add(VALID_EAPOL_FRAMES_RX, validEapolFramesRx.get())
+                .add(PENDING_RES_SUPPLICANT, pendingResSupp.get());
+        return helper.toString();
+    }
+
 }
diff --git a/api/src/main/java/org/opencord/aaa/AaaStatisticsSnapshot.java b/api/src/main/java/org/opencord/aaa/AaaStatisticsSnapshot.java
new file mode 100644
index 0000000..7d3cfdc
--- /dev/null
+++ b/api/src/main/java/org/opencord/aaa/AaaStatisticsSnapshot.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.opencord.aaa;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Immutable snapshot of AAA statistics.
+ */
+public class AaaStatisticsSnapshot {
+
+    private final ImmutableMap<String, Long> counters;
+
+    /**
+     * Gets the value of a counter.
+     *
+     * @param counterName name of the counter
+     * @return counter value, or 0 if it doesn't exist
+     */
+    public long get(String counterName) {
+        return counters.getOrDefault(counterName, 0L);
+    }
+
+    /**
+     * Creates a new empty snapshot with all counters initialized to 0.
+     */
+    public AaaStatisticsSnapshot() {
+        ImmutableMap.Builder<String, Long> builder = ImmutableMap.builder();
+
+        for (String name : AaaStatistics.COUNTER_NAMES) {
+            builder.put(name, 0L);
+        }
+
+        counters = builder.build();
+    }
+
+    /**
+     * Creates a new snapshot with the given counter values.
+     *
+     * @param counters counter values
+     */
+    public AaaStatisticsSnapshot(ImmutableMap<String, Long> counters) {
+        this.counters = counters;
+    }
+
+    /**
+     * Adds the given snapshot to this snapshot and returns a new snapshot with the aggregate values.
+     *
+     * @param other other snapshot to add to this one
+     * @return new aggregate snapshot
+     */
+    public AaaStatisticsSnapshot add(AaaStatisticsSnapshot other) {
+        ImmutableMap.Builder<String, Long> builder = ImmutableMap.builder();
+
+        counters.forEach((name, value) -> builder.put(name, value + other.counters.get(name)));
+
+        return new AaaStatisticsSnapshot(builder.build());
+    }
+
+    public String toString() {
+        MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this.getClass());
+        counters.forEach(helper::add);
+        return helper.toString();
+    }
+
+}
diff --git a/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsEvent.java b/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsEvent.java
index 11c00e3..5dbbdad 100644
--- a/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsEvent.java
+++ b/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsEvent.java
@@ -15,22 +15,26 @@
  */
 
 package org.opencord.aaa;
+
 import org.onosproject.event.AbstractEvent;
+
 /**
  * Event indicating the Accounting Data of AAA.
  */
 public class AuthenticationStatisticsEvent extends
         AbstractEvent<AuthenticationStatisticsEvent.Type, AaaStatistics> {
-/**
- * Accounting data.
- * AuthenticationMetrixEvent event type.
- */
+
+    /**
+     * Type of AuthenticationStatisticsEvent.
+     */
     public enum Type {
+
         /**
-         * signifies that the Authentication Metrix Event stats has been updated.
+         * Signifies that the authentication statistics have been updated.
          */
         STATS_UPDATE
     }
+
     public AuthenticationStatisticsEvent(Type type, AaaStatistics stats) {
         super(type, stats);
     }
diff --git a/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsService.java b/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsService.java
index 37a2205..9f09387 100644
--- a/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsService.java
+++ b/api/src/main/java/org/opencord/aaa/AuthenticationStatisticsService.java
@@ -19,46 +19,46 @@
 import org.onosproject.event.ListenerService;
 
 /**
- * Service for interacting with accounting module.
+ * Service for interacting with accounting statistics.
  */
-
 public interface AuthenticationStatisticsService extends
-ListenerService<AuthenticationStatisticsEvent, AuthenticationStatisticsEventListener> {
+        ListenerService<AuthenticationStatisticsEvent, AuthenticationStatisticsEventListener> {
 
     /**
-     * Returns AaaStatistics object.
+     * Returns AaaStatistics object containing only local (this instance) statistics.
      *
      * @return AaaStatistics
      */
-    public AaaStatistics getAaaStats();
+    AaaStatistics getAaaStats();
+
     /**
-     * Returns AuthenticationStatisticsDelegate object.
+     * Gets the cluster-wide statistics.
      *
-     * @return AuthenticationStatisticsDelegate
+     * @return snapshot containing cluster statistics
      */
-    public AuthenticationStatisticsDelegate getStatsDelegate();
+    AaaStatisticsSnapshot getClusterStatistics();
 
     /**
      * Handle the roundTrip time of Radius Packet.
      *
      * @param identifier identifier of incoming radius packet
      */
-    public void handleRoundtripTime(byte identifier);
+    void handleRoundtripTime(byte identifier);
 
     /**
      * Calculate average roundTrip time of multiple Packets.
      */
-    public void calculatePacketRoundtripTime();
+    void calculatePacketRoundtripTime();
 
     /**
      * Put the identifier value to map.
      *
      * @param identifier identifier of incoming radius packet
      */
-    public void putOutgoingIdentifierToMap(byte identifier);
+    void putOutgoingIdentifierToMap(byte identifier);
 
-   /**
-    * Reset all the values of aaa counters to 0.
-    */
-    public void resetAllCounters();
+    /**
+     * Reset all the values of aaa counters to 0.
+     */
+    void resetAllCounters();
 }