[SEBA-624] Implementation of radius server operational status

Change-Id: I2881ffc80e224589ae042db1e0b7471e72c64ec3
diff --git a/api/src/main/java/org/opencord/aaa/RadiusOperationalStatusEvent.java b/api/src/main/java/org/opencord/aaa/RadiusOperationalStatusEvent.java
new file mode 100644
index 0000000..7ba9534
--- /dev/null
+++ b/api/src/main/java/org/opencord/aaa/RadiusOperationalStatusEvent.java
@@ -0,0 +1,32 @@
+/*
+ * 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 org.onosproject.event.AbstractEvent;
+
+public class RadiusOperationalStatusEvent extends
+              AbstractEvent<RadiusOperationalStatusEvent.Type, String> {
+
+    public RadiusOperationalStatusEvent(Type type, String subject) {
+         super(type, subject);
+    }
+
+    public enum Type {
+         RADIUS_OPERATIONAL_STATUS
+    }
+
+}
diff --git a/api/src/main/java/org/opencord/aaa/RadiusOperationalStatusEventDelegate.java b/api/src/main/java/org/opencord/aaa/RadiusOperationalStatusEventDelegate.java
new file mode 100644
index 0000000..f4d5d5d
--- /dev/null
+++ b/api/src/main/java/org/opencord/aaa/RadiusOperationalStatusEventDelegate.java
@@ -0,0 +1,24 @@
+/*
+ * 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 org.onosproject.store.StoreDelegate;
+
+public interface RadiusOperationalStatusEventDelegate
+              extends StoreDelegate<RadiusOperationalStatusEvent> {
+
+}
diff --git a/api/src/main/java/org/opencord/aaa/RadiusOperationalStatusEventListener.java b/api/src/main/java/org/opencord/aaa/RadiusOperationalStatusEventListener.java
new file mode 100644
index 0000000..defffcc
--- /dev/null
+++ b/api/src/main/java/org/opencord/aaa/RadiusOperationalStatusEventListener.java
@@ -0,0 +1,24 @@
+/*
+ * 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 org.onosproject.event.EventListener;
+
+public interface RadiusOperationalStatusEventListener extends
+                  EventListener<RadiusOperationalStatusEvent>  {
+
+}
diff --git a/api/src/main/java/org/opencord/aaa/RadiusOperationalStatusService.java b/api/src/main/java/org/opencord/aaa/RadiusOperationalStatusService.java
new file mode 100644
index 0000000..10c5d8b
--- /dev/null
+++ b/api/src/main/java/org/opencord/aaa/RadiusOperationalStatusService.java
@@ -0,0 +1,121 @@
+/*
+ * 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 org.onlab.packet.RADIUS;
+import org.onosproject.event.ListenerService;
+
+/**
+ * Service for interacting with operational status module.
+ */
+
+public interface RadiusOperationalStatusService extends
+             ListenerService<RadiusOperationalStatusEvent, RadiusOperationalStatusEventListener> {
+   /**
+     * Return RadiusOperationalStatusEventDelegate object.
+     *
+     * @return RadiusOperationalStatusEventDelegate
+    */
+    RadiusOperationalStatusEventDelegate getRadiusOprStDelegate();
+
+    /**
+     * Return String object.
+     *
+     * @return String
+    */
+    String getRadiusServerOperationalStatus();
+
+    /**
+     * Set the value of statusServerReqSent flag.
+     *
+     * @param statusServerReqSent statusServerReqSent flag
+    */
+    void setStatusServerReqSent(boolean statusServerReqSent);
+
+    /**
+     * Set the value of radiusOperationalStatus Evaluation Mode.
+     *
+     * @param radiusOperationalStatusEvaluationMode radiusOperationalStatusEvaluationMode value
+    */
+    void setRadiusOperationalStatusEvaluationMode(
+            RadiusOperationalStatusEvaluationMode radiusOperationalStatusEvaluationMode);
+
+    /**
+     * Set the value of Operational Status Server Timeout In Milliseconds.
+     *
+     * @param operationalStatusServerTimeoutInMillis operationalStatusServerTimeoutInMillis
+    */
+    void setOperationalStatusServerTimeoutInMillis(long operationalStatusServerTimeoutInMillis);
+
+    /**
+     * Determine the operational status of server.
+    */
+    void checkServerOperationalStatus();
+
+    /**
+     * Check if radius response is for operational status.
+     *
+     * @param identifier identifier value of radius packet
+     * @return boolean
+    */
+    boolean isRadiusResponseForOperationalStatus(byte identifier);
+
+    /**
+     * handle incoming radius packet for operational status.
+     *
+     * @param radiusPacket radiusPacket of incoming operational status
+    */
+    void handleRadiusPacketForOperationalStatus(RADIUS radiusPacket);
+
+    /**
+     * initialize radiusOperationalStatusService.
+     *
+     * @param address address of radius server
+     * @param secret secret key for radius server
+     * @param impl impl of RadiusCommunicator
+    */
+    void initialize(byte[] address, String secret, RadiusCommunicator impl);
+
+    /**
+     * set packet outgoing time in milliseconds.
+     *
+     * @param identifier identifier of outgoing packet
+    */
+    void setOutTimeInMillis(byte identifier);
+
+    enum OperationalStatus {
+        UNAVAILABLE,
+        UNKNOWN,
+        IN_USE,
+    }
+
+    enum RadiusOperationalStatusEvaluationMode {
+
+        STATUS_REQUEST, ACCESS_REQUEST, AUTO;
+
+        public static RadiusOperationalStatusEvaluationMode getValue(String value) {
+
+            for (RadiusOperationalStatusEvaluationMode mode: RadiusOperationalStatusEvaluationMode.values()) {
+                if (mode.toString().equalsIgnoreCase(value)) {
+                   return mode;
+                }
+            }
+            return null;
+        }
+    }
+
+}
diff --git a/app/src/main/java/org/opencord/aaa/impl/AaaManager.java b/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
old mode 100755
new mode 100644
index ff3c812..8e1f5c1
--- a/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
+++ b/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
@@ -73,7 +73,10 @@
 import org.opencord.aaa.AuthenticationStatisticsEvent;
 import org.opencord.aaa.AuthenticationStatisticsService;
 import org.opencord.aaa.RadiusCommunicator;
+import org.opencord.aaa.RadiusOperationalStatusEvent;
+import org.opencord.aaa.RadiusOperationalStatusService;
 import org.opencord.aaa.StateMachineDelegate;
+import org.opencord.aaa.RadiusOperationalStatusService.RadiusOperationalStatusEvaluationMode;
 import org.opencord.sadis.BaseInformationService;
 import org.opencord.sadis.SadisService;
 import org.opencord.sadis.SubscriberAndDeviceInformation;
@@ -127,14 +130,35 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ComponentConfigService cfgService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected RadiusOperationalStatusService radiusOperationalStatusService;
+
     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;
+    @Property(name = "statisticsGenerationPeriodInSeconds", intValue = DEFAULT_REPEAT_DELAY,
+              label = "AAA Statistics generation frequency in seconds")
+    private int statisticsGenerationPeriodInSeconds = DEFAULT_REPEAT_DELAY;
+
+    private static final int DEFAULT_OPERATIONAL_STATUS_SERVER_EVENT_GENERATION = 30;
+    @Property(name = "operationalStatusEventGenerationPeriodInSeconds",
+            intValue = DEFAULT_OPERATIONAL_STATUS_SERVER_EVENT_GENERATION, label = "AAA Radius Server Operational "
+                    + "Status Frequency in seconds")
+    private int operationalStatusEventGenerationPeriodInSeconds = DEFAULT_OPERATIONAL_STATUS_SERVER_EVENT_GENERATION;
+
+    private static final int DEFAULT_OPERATIONAL_STATUS_SERVER_TIMEOUT = 10;
+    @Property(name = "operationalStatusServerTimeoutInSeconds",
+            intValue = DEFAULT_OPERATIONAL_STATUS_SERVER_TIMEOUT, label = "Maximum period(in Seconds) to "
+                    + "wait for Status response from AAA Server ")
+    private int operationalStatusServerTimeoutInSeconds = DEFAULT_OPERATIONAL_STATUS_SERVER_TIMEOUT;
+
+    private static final String DEFAULT_STATUS_SERVER_MODE = "AUTO";
+    @Property(name = "operationalStatusEvaluationMode", value = DEFAULT_STATUS_SERVER_MODE,
+            label = "Evaluation mode for determining the Operational Status of Radius Server. Valid values are AUTO "
+                    + "(default), STATUS_REQUEST and ACCESS_REQUEST")
+    protected String operationalStatusEvaluationMode = DEFAULT_STATUS_SERVER_MODE;
 
     // NAS IP address
     protected InetAddress nasIpAddress;
@@ -181,7 +205,7 @@
     AaaConfig newCfg;
 
     ScheduledFuture<?> scheduledFuture;
-
+    ScheduledFuture<?> scheduledStatusServerChecker;
     ScheduledExecutorService executor;
     String configuredAaaServerAddress;
     HashSet<Byte> outPacketSet = new HashSet<Byte>();
@@ -261,11 +285,15 @@
         impl.requestIntercepts();
         deviceService.addListener(deviceListener);
         getConfiguredAaaServerAddress();
+        radiusOperationalStatusService.initialize(nasIpAddress.getAddress(), radiusSecret, impl);
         authenticationStatisticsPublisher =
                 new AuthenticationStatisticsEventPublisher();
-        executor = Executors.newScheduledThreadPool(1);
+        executor = Executors.newScheduledThreadPool(3);
+
         scheduledFuture = executor.scheduleAtFixedRate(authenticationStatisticsPublisher,
-                0, statisticsGenerationEvent, TimeUnit.SECONDS);
+            0, statisticsGenerationPeriodInSeconds, TimeUnit.SECONDS);
+        scheduledStatusServerChecker = executor.scheduleAtFixedRate(new ServerStatusChecker(), 0,
+            operationalStatusEventGenerationPeriodInSeconds, TimeUnit.SECONDS);
 
         log.info("Started");
     }
@@ -282,15 +310,39 @@
         deviceService.removeListener(deviceListener);
         eventDispatcher.removeSink(AuthenticationEvent.class);
         scheduledFuture.cancel(true);
+        scheduledStatusServerChecker.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());
+        Dictionary<String, Object> properties = context.getProperties();
+        String s = Tools.get(properties, "statisticsGenerationPeriodInSeconds");
+        statisticsGenerationPeriodInSeconds = Strings.isNullOrEmpty(s) ? DEFAULT_REPEAT_DELAY
+                : Integer.parseInt(s.trim());
+
+        s = Tools.get(properties, "operationalStatusEventGenerationPeriodInSeconds");
+        operationalStatusEventGenerationPeriodInSeconds = Strings.isNullOrEmpty(s)
+                ? DEFAULT_OPERATIONAL_STATUS_SERVER_EVENT_GENERATION
+                    : Integer.parseInt(s.trim());
+
+        s = Tools.get(properties, "operationalStatusServerTimeoutInSeconds");
+        operationalStatusServerTimeoutInSeconds = Strings.isNullOrEmpty(s) ? DEFAULT_OPERATIONAL_STATUS_SERVER_TIMEOUT
+                : Integer.parseInt(s.trim());
+
+        s = Tools.get(properties, "operationalStatusEvaluationMode");
+        String newEvaluationModeString = Strings.isNullOrEmpty(s) ? DEFAULT_STATUS_SERVER_MODE : s.trim();
+
+        radiusOperationalStatusService
+            .setOperationalStatusServerTimeoutInMillis(operationalStatusServerTimeoutInSeconds * 1000);
+        RadiusOperationalStatusEvaluationMode newEvaluationMode =
+                RadiusOperationalStatusEvaluationMode.getValue(newEvaluationModeString);
+        if (newEvaluationMode != null) {
+            radiusOperationalStatusService.setRadiusOperationalStatusEvaluationMode(newEvaluationMode);
+            operationalStatusEvaluationMode = newEvaluationModeString;
+        } else {
+            properties.put("operationalStatusEvaluationMode", operationalStatusEvaluationMode);
+        }
     }
 
     protected void configureRadiusCommunication() {
@@ -380,6 +432,7 @@
         outPacketSet.add(radiusPacket.getIdentifier());
         aaaStatisticsManager.getAaaStats().increaseOrDecreasePendingRequests(true);
         aaaStatisticsManager.getAaaStats().increaseAccessRequestsTx();
+        aaaStatisticsManager.putOutgoingIdentifierToMap(radiusPacket.getIdentifier());
         impl.sendRadiusPacket(radiusPacket, inPkt);
     }
 
@@ -410,6 +463,10 @@
         if (log.isTraceEnabled()) {
             log.trace("Received RADIUS packet {}", radiusPacket);
         }
+        if (radiusOperationalStatusService.isRadiusResponseForOperationalStatus(radiusPacket.getIdentifier())) {
+            radiusOperationalStatusService.handleRadiusPacketForOperationalStatus(radiusPacket);
+            return;
+        }
         StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
         if (stateMachine == null) {
             log.error("Invalid packet identifier {}, could not find corresponding "
@@ -915,5 +972,20 @@
                 notify(new AuthenticationStatisticsEvent(AuthenticationStatisticsEvent.Type.STATS_UPDATE,
                     aaaStatisticsManager.getAaaStats()));
         }
+    }
+
+    private class ServerStatusChecker implements Runnable {
+        @Override
+        public void run() {
+            log.info("Notifying RadiusOperationalStatusEvent");
+            radiusOperationalStatusService.checkServerOperationalStatus();
+            log.info("--POSTING--" + radiusOperationalStatusService.getRadiusServerOperationalStatus());
+            radiusOperationalStatusService.getRadiusOprStDelegate()
+                .notify(new RadiusOperationalStatusEvent(
+                        RadiusOperationalStatusEvent.Type.RADIUS_OPERATIONAL_STATUS,
+                        radiusOperationalStatusService.
+                        getRadiusServerOperationalStatus()));
         }
-}
+
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/opencord/aaa/impl/PortBasedRadiusCommunicator.java b/app/src/main/java/org/opencord/aaa/impl/PortBasedRadiusCommunicator.java
index 2967a14..92fea87 100755
--- a/app/src/main/java/org/opencord/aaa/impl/PortBasedRadiusCommunicator.java
+++ b/app/src/main/java/org/opencord/aaa/impl/PortBasedRadiusCommunicator.java
@@ -260,13 +260,20 @@
 
         if (deviceInfo == null) {
             log.warn("No Device found with SN {}", serialNo);
+            aaaManager.radiusOperationalStatusService.setStatusServerReqSent(false);
             return;
         }
         ipToSnMap.put(deviceInfo.ipAddress(), serialNo);
-        aaaManager.aaaStatisticsManager.putOutgoingIdentifierToMap(radiusPacket.getIdentifier());
+        if (radiusPacket.getIdentifier() == RadiusOperationalStatusManager.AAA_REQUEST_ID_STATUS_REQUEST ||
+                radiusPacket.getIdentifier() == RadiusOperationalStatusManager.AAA_REQUEST_ID_FAKE_ACCESS_REQUEST) {
+            aaaManager.radiusOperationalStatusService.setOutTimeInMillis(radiusPacket.getIdentifier());
+        } else {
+            aaaManager.aaaStatisticsManager.putOutgoingIdentifierToMap(radiusPacket.getIdentifier());
+        }
         // send the message out
         sendFromRadiusServerPort(pktCustomizer.
                 customizeEthernetIPHeaders(ethReply, inPkt));
+        aaaManager.radiusOperationalStatusService.setStatusServerReqSent(true);
     }
 
     /**
diff --git a/app/src/main/java/org/opencord/aaa/impl/RadiusOperationalStatusManager.java b/app/src/main/java/org/opencord/aaa/impl/RadiusOperationalStatusManager.java
new file mode 100644
index 0000000..448cbce
--- /dev/null
+++ b/app/src/main/java/org/opencord/aaa/impl/RadiusOperationalStatusManager.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2017-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.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.RADIUS;
+import org.onlab.packet.RADIUSAttribute;
+import org.onosproject.event.AbstractListenerManager;
+import org.opencord.aaa.RadiusOperationalStatusService;
+import org.opencord.aaa.RadiusCommunicator;
+import org.opencord.aaa.RadiusOperationalStatusEvent;
+import org.opencord.aaa.RadiusOperationalStatusEventDelegate;
+import org.opencord.aaa.RadiusOperationalStatusEventListener;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.slf4j.Logger;
+
+@Service
+@Component(immediate = true)
+public class RadiusOperationalStatusManager
+        extends AbstractListenerManager<RadiusOperationalStatusEvent, RadiusOperationalStatusEventListener>
+        implements RadiusOperationalStatusService {
+
+    private byte[] address;
+    private String secret;
+    private RadiusCommunicator impl;
+    private RadiusOperationalStatusEventDelegate radiusOprStDelegate;
+
+    private long operationalStatusServerTimeoutInMillis;
+    private boolean statusServerReqSent;
+    private final Logger log = getLogger(getClass());
+
+    private Boolean fakeAccessRequestPacketRecieved = false;
+    private long fakeAccessRequestOutTimeInMillis;
+
+    private Boolean serverStatusPacketRecieved = false;
+    private long serverStatusOutTimeInMillis;
+
+    private OperationalStatus radiusServerOperationalStatus;
+    public static final byte AAA_REQUEST_ID_STATUS_REQUEST = 0;
+    public static final byte AAA_REQUEST_ID_FAKE_ACCESS_REQUEST = 1;
+
+    private RadiusOperationalStatusEvaluationMode radiusOperationalStatusEvaluationMode;
+
+    private static final String DUMMY_USER = new String("dummy-user");
+    private static final byte RADIUS_CODE_STATUS_REQUEST = (byte) 12;
+    private long lastRadiusPacketInTimeInMillis;
+
+    public void setOperationalStatusServerTimeoutInMillis(long operationalStatusServerTimeoutInMillis) {
+        this.operationalStatusServerTimeoutInMillis = operationalStatusServerTimeoutInMillis;
+    }
+
+    public void setRadiusOperationalStatusEvaluationMode(
+        RadiusOperationalStatusEvaluationMode radiusOperationalStatusEvaluationMode) {
+        this.radiusOperationalStatusEvaluationMode = radiusOperationalStatusEvaluationMode;
+    }
+
+    public RadiusOperationalStatusEventDelegate getRadiusOprStDelegate() {
+        return radiusOprStDelegate;
+    }
+
+    @Override
+    public void setOutTimeInMillis(byte identifier) {
+        if (identifier == AAA_REQUEST_ID_STATUS_REQUEST) {
+            serverStatusOutTimeInMillis = System.currentTimeMillis();
+        } else {
+            fakeAccessRequestOutTimeInMillis = System.currentTimeMillis();
+        }
+    }
+
+    @Override
+    public String getRadiusServerOperationalStatus() {
+        return radiusServerOperationalStatus.toString();
+    }
+
+    @Activate
+    public void activate() {
+        radiusOprStDelegate = new InternalRadiusOperationalStatusDelegate();
+        eventDispatcher.addSink(RadiusOperationalStatusEvent.class, listenerRegistry);
+        radiusServerOperationalStatus = OperationalStatus.UNKNOWN;
+    }
+
+    public void setStatusServerReqSent(boolean statusServerReqSent) {
+        this.statusServerReqSent = statusServerReqSent;
+    }
+
+    @Deactivate
+    public void deactivate() {
+        eventDispatcher.removeSink(RadiusOperationalStatusEvent.class);
+    }
+
+    public void initialize(byte[] address, String secret, RadiusCommunicator impl) {
+        this.address = address;
+        this.secret = secret;
+        this.impl = impl;
+    }
+
+    public boolean isRadiusResponseForOperationalStatus(byte identifier) {
+        if (identifier == AAA_REQUEST_ID_STATUS_REQUEST || identifier == AAA_REQUEST_ID_FAKE_ACCESS_REQUEST) {
+            return true;
+        } else {
+            lastRadiusPacketInTimeInMillis = System.currentTimeMillis();
+            return false;
+        }
+    }
+
+    public void handleRadiusPacketForOperationalStatus(RADIUS radiusPacket) {
+        byte radiusPktIdentifier = radiusPacket.getIdentifier();
+
+        if (radiusPktIdentifier == AAA_REQUEST_ID_STATUS_REQUEST) {
+            long serverStatusRttInMillis = System.currentTimeMillis() - serverStatusOutTimeInMillis;
+            if (serverStatusRttInMillis < operationalStatusServerTimeoutInMillis) {
+                serverStatusPacketRecieved = true;
+            }
+        } else {
+            long fakeAccessRttInMillis = System.currentTimeMillis() - fakeAccessRequestOutTimeInMillis;
+            if (fakeAccessRttInMillis < operationalStatusServerTimeoutInMillis) {
+                fakeAccessRequestPacketRecieved = true;
+            }
+        }
+
+        switch (radiusPacket.getCode()) {
+            case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
+                synchronized (serverStatusPacketRecieved) {
+                    serverStatusPacketRecieved.notify();
+                }
+                break;
+            case RADIUS.RADIUS_CODE_ACCESS_REJECT:
+                synchronized (fakeAccessRequestPacketRecieved) {
+                    fakeAccessRequestPacketRecieved.notify();
+                }
+                break;
+            default:
+                log.warn("Unexpected Radius message for operational status recieved "
+                        + "with code: {}", radiusPacket.getCode());
+        }
+    }
+
+    public void checkServerStatusUsingStatusServerRequest() throws InterruptedException {
+        RADIUS radiusStatusServerRequest;
+        // identifier = 0 for status server
+        radiusStatusServerRequest = new RADIUS(RADIUS_CODE_STATUS_REQUEST, AAA_REQUEST_ID_STATUS_REQUEST);
+
+        radiusStatusServerRequest.setIdentifier(AAA_REQUEST_ID_STATUS_REQUEST);
+        radiusStatusServerRequest.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME, DUMMY_USER.getBytes());
+
+        radiusStatusServerRequest.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP, address);
+        radiusStatusServerRequest.addMessageAuthenticator(secret);
+        setOutTimeInMillis(radiusStatusServerRequest.getIdentifier());
+        impl.sendRadiusPacket(radiusStatusServerRequest, null);
+        synchronized (serverStatusPacketRecieved) {
+            serverStatusPacketRecieved.wait(operationalStatusServerTimeoutInMillis);
+        }
+    }
+
+    public void checkServerStatusUsingFakeAccessRequest() throws InterruptedException {
+        RADIUS radiusDummyAccessRequest;
+        // identifier = 1 for fake accessRequest
+        radiusDummyAccessRequest = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST, AAA_REQUEST_ID_FAKE_ACCESS_REQUEST);
+
+        radiusDummyAccessRequest.setIdentifier(AAA_REQUEST_ID_FAKE_ACCESS_REQUEST);
+        radiusDummyAccessRequest.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME, DUMMY_USER.getBytes());
+
+        radiusDummyAccessRequest.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP, address);
+        radiusDummyAccessRequest.addMessageAuthenticator(secret);
+        setOutTimeInMillis(radiusDummyAccessRequest.getIdentifier());
+        impl.sendRadiusPacket(radiusDummyAccessRequest, null);
+        synchronized (fakeAccessRequestPacketRecieved) {
+            fakeAccessRequestPacketRecieved.wait(operationalStatusServerTimeoutInMillis);
+        }
+    }
+
+    public void checkStatusServerForAccessRequestMode() throws InterruptedException {
+        long radiusResponseRecievedTimeDifference = System.currentTimeMillis() - lastRadiusPacketInTimeInMillis;
+        if (radiusResponseRecievedTimeDifference > operationalStatusServerTimeoutInMillis) {
+            checkServerStatusUsingFakeAccessRequest();
+            if (statusServerReqSent && fakeAccessRequestPacketRecieved) {
+                radiusServerOperationalStatus = OperationalStatus.IN_USE;
+            } else if (statusServerReqSent && !fakeAccessRequestPacketRecieved) {
+                radiusServerOperationalStatus = OperationalStatus.UNAVAILABLE;
+            } else {
+                radiusServerOperationalStatus = OperationalStatus.UNKNOWN;
+            }
+        } else {
+            radiusServerOperationalStatus = OperationalStatus.IN_USE;
+        }
+    }
+
+    public void checkServerOperationalStatus() {
+
+        try {
+            if (radiusOperationalStatusEvaluationMode == RadiusOperationalStatusEvaluationMode.STATUS_REQUEST) {
+                // determine operational status by statusServerRequest
+                checkServerStatusUsingStatusServerRequest();
+                if (statusServerReqSent && serverStatusPacketRecieved) {
+                    // if req sent and response recieved
+                    radiusServerOperationalStatus = OperationalStatus.IN_USE;
+                } else if (statusServerReqSent && !serverStatusPacketRecieved) {
+                    radiusServerOperationalStatus = OperationalStatus.UNAVAILABLE;
+                } else {
+                radiusServerOperationalStatus = OperationalStatus.UNKNOWN;
+                }
+            } else {
+                if (radiusOperationalStatusEvaluationMode == RadiusOperationalStatusEvaluationMode.AUTO) {
+                    checkServerStatusUsingStatusServerRequest();
+                    if (statusServerReqSent && serverStatusPacketRecieved) {
+                        radiusServerOperationalStatus = OperationalStatus.IN_USE;
+                    } else {
+                        checkStatusServerForAccessRequestMode();
+                    }
+                } else {
+                    checkStatusServerForAccessRequestMode();
+                }
+            }
+            fakeAccessRequestPacketRecieved = false;
+            serverStatusPacketRecieved = false;
+        } catch (Exception e) {
+            log.error("Caught exception while checking radius server status::" + e);
+        }
+    }
+
+    /**
+     * Delegate allowing the RadiusOperationalStatus to notify us of events.
+     */
+     private class InternalRadiusOperationalStatusDelegate implements RadiusOperationalStatusEventDelegate {
+        @Override
+        public void notify(RadiusOperationalStatusEvent radiusOperationalStatusEvent) {
+            log.debug("Radius Operational Status event {} for {}", radiusOperationalStatusEvent.type(),
+                radiusOperationalStatusEvent.subject());
+            post(radiusOperationalStatusEvent);
+        }
+     }
+
+}
diff --git a/app/src/main/java/org/opencord/aaa/impl/SocketBasedRadiusCommunicator.java b/app/src/main/java/org/opencord/aaa/impl/SocketBasedRadiusCommunicator.java
index 0324083..32d4f21 100755
--- a/app/src/main/java/org/opencord/aaa/impl/SocketBasedRadiusCommunicator.java
+++ b/app/src/main/java/org/opencord/aaa/impl/SocketBasedRadiusCommunicator.java
@@ -148,10 +148,11 @@
                     log.trace("Sending packet {} to Radius Server {}:{} using socket",
                               radiusPacket, address, radiusServerPort);
                 }
-                aaaManager.aaaStatisticsManager.putOutgoingIdentifierToMap(radiusPacket.getIdentifier());
                 socket.send(packet);
+                aaaManager.radiusOperationalStatusService.setStatusServerReqSent(true);
             } catch (UnknownHostException uhe) {
                 log.warn("Unable to resolve host {}", radiusHost);
+                aaaManager.radiusOperationalStatusService.setStatusServerReqSent(false);
             }
         } catch (IOException e) {
             log.info("Cannot send packet to RADIUS server", e);
diff --git a/app/src/main/java/org/opencord/aaa/impl/StateMachine.java b/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
index 5994a28..7635d86 100644
--- a/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
+++ b/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
@@ -56,7 +56,7 @@
     static final int TRANSITION_DENY_ACCESS = 3;
     static final int TRANSITION_LOGOFF = 4;
 
-    private static int identifier = -1;
+    private static int identifier = 1;
     private byte challengeIdentifier;
     private byte[] challengeState;
     private byte[] username;
@@ -129,7 +129,7 @@
     public static void initializeMaps() {
         sessionIdMap = Maps.newConcurrentMap();
         identifierMap = Maps.newConcurrentMap();
-        identifier = -1;
+        identifier = 1;
     }
 
     public static void destroyMaps() {
@@ -207,7 +207,7 @@
             if (e.getValue() != null && e.getValue().supplicantAddress != null
                     && e.getValue().supplicantAddress.equals(mac)) {
                 sessionIdMap.remove(e.getValue().sessionId);
-                if (e.getValue().identifier != -1) {
+                if (e.getValue().identifier != 1) {
                     deleteStateMachineMapping(e.getValue());
                 }
                 break;
@@ -419,9 +419,11 @@
      * @return The state machine identifier.
      */
     public synchronized byte identifier() {
-        identifier = (identifier + 1) % 255;
-        identifierMap.put(identifier, this);
-        return (byte) identifier;
+        //identifier 0 is for statusServerrequest
+        //identifier 1 is for fake accessRequest
+        identifier = (identifier + 1) % 253;
+        identifierMap.put((identifier + 2), this);
+        return (byte) (identifier + 2);
     }
 
     /**
diff --git a/app/src/test/java/org/opencord/aaa/impl/AaaManagerTest.java b/app/src/test/java/org/opencord/aaa/impl/AaaManagerTest.java
index 7303257..1b4257d 100644
--- a/app/src/test/java/org/opencord/aaa/impl/AaaManagerTest.java
+++ b/app/src/test/java/org/opencord/aaa/impl/AaaManagerTest.java
@@ -122,13 +122,13 @@
 
         String challenge = "12345678901234567";
 
-        EAP eap = new EAP(challengeType, (byte) 1, challengeType,
+        EAP eap = new EAP(challengeType, (byte) 4, challengeType,
                           challenge.getBytes(Charsets.US_ASCII));
-        eap.setIdentifier((byte) 1);
+        eap.setIdentifier((byte) 4);
 
         RADIUS radius = new RADIUS();
         radius.setCode(challengeCode);
-
+        radius.setIdentifier((byte) 4);
         radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
                             challenge.getBytes(Charsets.US_ASCII));
 
@@ -167,6 +167,7 @@
         aaaManager.sadisService = new MockSadisService();
         aaaManager.cfgService = new MockCfgService();
         aaaStatisticsManager = new AaaStatisticsManager();
+        aaaManager.radiusOperationalStatusService = new RadiusOperationalStatusManager();
         TestUtils.setField(aaaStatisticsManager, "eventDispatcher", new TestEventDispatcher());
         aaaStatisticsManager.activate();
         aaaManager.aaaStatisticsManager = this.aaaStatisticsManager;
@@ -228,7 +229,7 @@
 
         //  (2) Supplicant identify
 
-        Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
+        Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 3, null);
         sendPacket(identifyPacket);
 
         RADIUS radiusIdentifyPacket = (RADIUS) fetchPacket(1);
@@ -273,7 +274,7 @@
         RADIUS responseMd5RadiusPacket = (RADIUS) fetchPacket(3);
 
         checkRadiusPacketFromSupplicant(responseMd5RadiusPacket);
-        assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 3));
+        assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 9));
         assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
 
         //  State machine should be in pending state
diff --git a/app/src/test/java/org/opencord/aaa/impl/AaaStatisticsTest.java b/app/src/test/java/org/opencord/aaa/impl/AaaStatisticsTest.java
index e4718b7..8627d3c 100644
--- a/app/src/test/java/org/opencord/aaa/impl/AaaStatisticsTest.java
+++ b/app/src/test/java/org/opencord/aaa/impl/AaaStatisticsTest.java
@@ -138,20 +138,24 @@
      */
    private RADIUS constructRadiusCodeAccessChallengePacket(byte challengeCode, byte challengeType) {
 
-     String challenge = "12345678901234567";
-     EAP eap = new EAP(challengeType, (byte) 1, challengeType, challenge.getBytes(Charsets.US_ASCII));
-     eap.setIdentifier((byte) 1);
+       String challenge = "12345678901234567";
 
-     RADIUS radius = new RADIUS();
-     radius.setCode(challengeCode);
+       EAP eap = new EAP(challengeType, (byte) 4, challengeType,
+                         challenge.getBytes(Charsets.US_ASCII));
+       eap.setIdentifier((byte) 4);
 
-     radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, challenge.getBytes(Charsets.US_ASCII));
+       RADIUS radius = new RADIUS();
+       radius.setCode(challengeCode);
+       radius.setIdentifier((byte) 4);
+       radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
+                           challenge.getBytes(Charsets.US_ASCII));
 
-     radius.setPayload(eap);
-     radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE, eap.serialize());
-     radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH, aaaManager.radiusSecret.getBytes());
-     return radius;
-
+       radius.setPayload(eap);
+       radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
+                           eap.serialize());
+       radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_MESSAGE_AUTH,
+               aaaManager.radiusSecret.getBytes());
+       return radius;
    }
 
     public static void injectEventDispatcher(Object manager, EventDeliveryService svc) {
@@ -174,6 +178,7 @@
     @Before
     public void setUp() {
         aaaManager = new AaaManagerWithoutRadiusServer();
+        aaaManager.radiusOperationalStatusService = new RadiusOperationalStatusManager();
         aaaManager.netCfgService = new TestNetworkConfigRegistry();
         aaaManager.coreService = new CoreServiceAdapter();
         aaaManager.packetService = new MockPacketService();
@@ -278,7 +283,7 @@
         RADIUS responseMd5RadiusPacket = (RADIUS) fetchPacket(3);
 
         checkRadiusPacketFromSupplicant(responseMd5RadiusPacket);
-        assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 3));
+        assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 9));
         assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
 
         // State machine should be in pending state
diff --git a/app/src/test/java/org/opencord/aaa/impl/AaaTestBase.java b/app/src/test/java/org/opencord/aaa/impl/AaaTestBase.java
index 4912e4e..8f48ce4 100644
--- a/app/src/test/java/org/opencord/aaa/impl/AaaTestBase.java
+++ b/app/src/test/java/org/opencord/aaa/impl/AaaTestBase.java
@@ -411,7 +411,7 @@
         eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
         eth.setVlanID((short) 2);
 
-        EAP eap = new EAP(EAPOL.EAPOL_START, (byte) 2, EAPOL.EAPOL_START, null);
+        EAP eap = new EAP(EAPOL.EAPOL_START, (byte) 3, EAPOL.EAPOL_START, null);
 
         // eapol header
         EAPOL eapol = new EAPOL();