[SEBA-37] Added 11 Statistics counters for Accounting Server operations

Change-Id: I7dda193129e5dffe62907775aaf8dfdc5b3f1bf1
diff --git a/app/src/main/java/org/opencord/aaa/impl/AaaManager.java b/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
index 3e75f17..bcd4cfa 100755
--- a/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
+++ b/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
@@ -19,12 +19,16 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
 import java.util.Map;
+import java.util.Dictionary;
+import java.util.HashSet;
 
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
@@ -36,6 +40,8 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.RADIUS;
 import org.onlab.packet.RADIUSAttribute;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.event.AbstractListenerManager;
@@ -62,14 +68,23 @@
 import org.opencord.aaa.AuthenticationEvent;
 import org.opencord.aaa.AuthenticationEventListener;
 import org.opencord.aaa.AuthenticationService;
+import org.opencord.aaa.AuthenticationStatisticsEvent;
+import org.opencord.aaa.AuthenticationStatisticsService;
 import org.opencord.aaa.RadiusCommunicator;
 import org.opencord.aaa.StateMachineDelegate;
 import org.opencord.sadis.BaseInformationService;
 import org.opencord.sadis.SadisService;
 import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Modified;
 import org.osgi.service.component.annotations.Activate;
 import org.slf4j.Logger;
+import com.google.common.base.Strings;
 
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
 /**
  * AAA application for ONOS.
  */
@@ -101,10 +116,21 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected MastershipService mastershipService;
 
-    protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected AuthenticationStatisticsService aaaStatisticsManager;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService cfgService;
+
+    protected AuthenticationStatisticsEventPublisher authenticationStatisticsPublisher;
+    protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
     private final DeviceListener deviceListener = new InternalDeviceListener();
 
+    private static final int DEFAULT_REPEAT_DELAY = 20;
+    @Property(name = "statisticsGenerationEvent", intValue = DEFAULT_REPEAT_DELAY,
+              label = "statisticsGenerationEvent")
+    private int statisticsGenerationEvent = DEFAULT_REPEAT_DELAY;
+
     // NAS IP address
     protected InetAddress nasIpAddress;
 
@@ -146,6 +172,11 @@
     // latest configuration
     AaaConfig newCfg;
 
+    ScheduledFuture<?> scheduledFuture;
+
+    ScheduledExecutorService executor;
+    String configuredAaaServerAddress;
+    HashSet<Byte> outPacketSet = new HashSet<Byte>();
     // Configuration properties factory
     private final ConfigFactory factory =
             new ConfigFactory<ApplicationId, AaaConfig>(APP_SUBJECT_FACTORY,
@@ -197,48 +228,58 @@
     }
 
     @Activate
-    public void activate() {
+    public void activate(ComponentContext context) {
         appId = coreService.registerApplication(APP_NAME);
         eventDispatcher.addSink(AuthenticationEvent.class, listenerRegistry);
         netCfgService.addListener(cfgListener);
         netCfgService.registerConfigFactory(factory);
+        cfgService.registerProperties(getClass());
+        modified(context);
         subsService = sadisService.getSubscriberInfoService();
         customInfo = new CustomizationInfo(subsService, deviceService);
         cfgListener.reconfigureNetwork(netCfgService.getConfig(appId, AaaConfig.class));
         log.info("Starting with config {} {}", this, newCfg);
-
         configureRadiusCommunication();
-
         // register our event handler
         packetService.addProcessor(processor, PacketProcessor.director(2));
-
-
         StateMachine.initializeMaps();
         StateMachine.setDelegate(delegate);
-
         impl.initializeLocalState(newCfg);
-
         impl.requestIntercepts();
-
         deviceService.addListener(deviceListener);
+        getConfiguredAaaServerAddress();
+        authenticationStatisticsPublisher =
+                new AuthenticationStatisticsEventPublisher();
+        executor = Executors.newScheduledThreadPool(1);
+        scheduledFuture = executor.scheduleAtFixedRate(authenticationStatisticsPublisher,
+                0, statisticsGenerationEvent, TimeUnit.SECONDS);
 
         log.info("Started");
     }
 
-
     @Deactivate
-    public void deactivate() {
+    public void deactivate(ComponentContext context) {
         impl.withdrawIntercepts();
         packetService.removeProcessor(processor);
         netCfgService.removeListener(cfgListener);
+        cfgService.unregisterProperties(getClass(), false);
         StateMachine.unsetDelegate(delegate);
         StateMachine.destroyMaps();
         impl.deactivate();
         deviceService.removeListener(deviceListener);
         eventDispatcher.removeSink(AuthenticationEvent.class);
+        scheduledFuture.cancel(true);
+        executor.shutdown();
         log.info("Stopped");
     }
 
+    @Modified
+    public void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+       String s = Tools.get(properties, "statisticsGenerationEvent");
+    statisticsGenerationEvent = Strings.isNullOrEmpty(s) ? DEFAULT_REPEAT_DELAY : Integer.parseInt(s.trim());
+    }
+
     private void configureRadiusCommunication() {
         if (radiusConnectionType.toLowerCase().equals("socket")) {
             impl = new SocketBasedRadiusCommunicator(appId, packetService, this);
@@ -265,6 +306,32 @@
         }
     }
 
+    private void getConfiguredAaaServerAddress() {
+        try {
+            InetAddress address;
+            if (newCfg.radiusHostName() != null) {
+                address = InetAddress.getByName(newCfg.radiusHostName());
+            } else {
+                 address = newCfg.radiusIp();
+            }
+
+            configuredAaaServerAddress = address.getHostAddress();
+        } catch (UnknownHostException uhe) {
+            log.warn("Unable to resolve host {}", newCfg.radiusHostName());
+        }
+    }
+
+    private void checkReceivedPacketForValidValidator(RADIUS radiusPacket) {
+        if (!radiusPacket.checkMessageAuthenticator(radiusSecret)) {
+            aaaStatisticsManager.getAaaStats().increaseInvalidValidatorsRx();
+        }
+    }
+    public void checkForPacketFromUnknownServer(String hostAddress) {
+            if (!hostAddress.equals(configuredAaaServerAddress)) {
+                 aaaStatisticsManager.getAaaStats().incrementUnknownServerRx();
+            }
+    }
+
     /**
      * Send RADIUS packet to the RADIUS server.
      *
@@ -272,6 +339,9 @@
      * @param inPkt        Incoming EAPOL packet
      */
     protected void sendRadiusPacket(RADIUS radiusPacket, InboundPacket inPkt) {
+        outPacketSet.add(radiusPacket.getIdentifier());
+        aaaStatisticsManager.getAaaStats().increaseOrDecreasePendingRequests(true);
+        aaaStatisticsManager.getAaaStats().increaseAccessRequestsTx();
         impl.sendRadiusPacket(radiusPacket, inPkt);
     }
 
@@ -291,12 +361,17 @@
         if (stateMachine == null) {
             log.error("Invalid packet identifier {}, could not find corresponding "
                     + "state machine ... exiting", radiusPacket.getIdentifier());
+            aaaStatisticsManager.getAaaStats().incrementNumberOfSessionsExpired();
+            aaaStatisticsManager.getAaaStats().countDroppedResponsesRx();
             return;
         }
-
         EAP eapPayload;
         Ethernet eth;
-
+        checkReceivedPacketForValidValidator(radiusPacket);
+        if (outPacketSet.contains(radiusPacket.getIdentifier())) {
+            aaaStatisticsManager.getAaaStats().increaseOrDecreasePendingRequests(false);
+            outPacketSet.remove(new Byte(radiusPacket.getIdentifier()));
+        }
         switch (radiusPacket.getCode()) {
             case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
                 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_CHALLENGE");
@@ -314,6 +389,7 @@
                         eapPayload, stateMachine.priorityCode());
                 log.debug("Send EAP challenge response to supplicant {}", stateMachine.supplicantAddress().toString());
                 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
+                aaaStatisticsManager.getAaaStats().increaseChallengeResponsesRx();
                 break;
             case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
                 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_ACCEPT");
@@ -331,7 +407,7 @@
                 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
 
                 stateMachine.authorizeAccess();
-
+                aaaStatisticsManager.getAaaStats().increaseAcceptResponsesRx();
                 break;
             case RADIUS.RADIUS_CODE_ACCESS_REJECT:
                 log.debug("RADIUS packet: RADIUS_CODE_ACCESS_REJECT");
@@ -356,11 +432,13 @@
                 log.warn("Send EAP failure message to supplicant {}", stateMachine.supplicantAddress().toString());
                 sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
                 stateMachine.denyAccess();
-
+                aaaStatisticsManager.getAaaStats().increaseRejectResponsesRx();
                 break;
             default:
                 log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode());
+                aaaStatisticsManager.getAaaStats().increaseUnknownTypeRx();
         }
+        aaaStatisticsManager.getAaaStats().countDroppedResponsesRx();
     }
 
     /**
@@ -473,7 +551,6 @@
                 log.debug("Using existing state-machine for sessionId: {}", sessionId);
             }
 
-
             switch (eapol.getEapolType()) {
                 case EAPOL.EAPOL_START:
                     log.debug("EAP packet: EAPOL_START");
@@ -523,6 +600,9 @@
                             sendRadiusPacket(radiusPayload, inPacket);
 
                             // change the state to "PENDING"
+                            if (stateMachine.state() == StateMachine.STATE_PENDING) {
+                                aaaStatisticsManager.getAaaStats().increaseRequestReTx();
+                            }
                             stateMachine.requestAccess();
                             break;
                         case EAP.ATTR_MD5:
@@ -686,4 +766,26 @@
             }
         }
     }
+    private class AuthenticationStatisticsEventPublisher implements Runnable {
+        private final Logger log = getLogger(getClass());
+        public void run() {
+            log.info("Notifying AuthenticationStatisticsEvent");
+            aaaStatisticsManager.calculatePacketRoundtripTime();
+            log.debug("AcceptResponsesRx---" + aaaStatisticsManager.getAaaStats().getAcceptResponsesRx());
+            log.debug("AccessRequestsTx---" + aaaStatisticsManager.getAaaStats().getAccessRequestsTx());
+            log.debug("ChallengeResponsesRx---" + aaaStatisticsManager.getAaaStats().getChallengeResponsesRx());
+            log.debug("DroppedResponsesRx---" + aaaStatisticsManager.getAaaStats().getDroppedResponsesRx());
+            log.debug("InvalidValidatorsRx---" + aaaStatisticsManager.getAaaStats().getInvalidValidatorsRx());
+            log.debug("MalformedResponsesRx---" + aaaStatisticsManager.getAaaStats().getMalformedResponsesRx());
+            log.debug("PendingRequests---" + aaaStatisticsManager.getAaaStats().getPendingRequests());
+            log.debug("RejectResponsesRx---" + aaaStatisticsManager.getAaaStats().getRejectResponsesRx());
+            log.debug("RequestReTx---" + aaaStatisticsManager.getAaaStats().getRequestReTx());
+            log.debug("RequestRttMilis---" + aaaStatisticsManager.getAaaStats().getRequestRttMilis());
+            log.debug("UnknownServerRx---" + aaaStatisticsManager.getAaaStats().getUnknownServerRx());
+            log.debug("UnknownTypeRx---" + aaaStatisticsManager.getAaaStats().getUnknownTypeRx());
+            aaaStatisticsManager.getStatsDelegate().
+                notify(new AuthenticationStatisticsEvent(AuthenticationStatisticsEvent.Type.STATS_UPDATE,
+                    aaaStatisticsManager.getAaaStats()));
+        }
+        }
 }