[SEBA-624] Implementation of radius server operational status

Cherry-picked from aaa-1.10

Change-Id: I3b70ddfc45c4b9e57c587df671088580c7426a89
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 55b6726..0e819d9
--- a/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
+++ b/app/src/main/java/org/opencord/aaa/impl/AaaManager.java
@@ -15,23 +15,8 @@
  */
 package org.opencord.aaa.impl;
 
-import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
-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 java.util.Arrays;
-import java.util.List;
-
+import com.google.common.base.Strings;
 import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.onlab.packet.DeserializationException;
 import org.onlab.packet.EAP;
 import org.onlab.packet.EAPOL;
@@ -71,31 +56,56 @@
 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.RadiusOperationalStatusService.RadiusOperationalStatusEvaluationMode;
 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.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.slf4j.Logger;
-import com.google.common.base.Strings;
 
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 
-import static org.opencord.aaa.impl.OsgiPropertyConstants.STATISTICS_GENERATION_EVENT;
-import static org.opencord.aaa.impl.OsgiPropertyConstants.STATISTICS_GENERATION_EVENT_DEFAULT;
+import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
+import static org.opencord.aaa.impl.OsgiPropertyConstants.OPERATIONAL_STATUS_SERVER_EVENT_GENERATION;
+import static org.opencord.aaa.impl.OsgiPropertyConstants.OPERATIONAL_STATUS_SERVER_EVENT_GENERATION_DEFAULT;
+import static org.opencord.aaa.impl.OsgiPropertyConstants.OPERATIONAL_STATUS_SERVER_TIMEOUT;
+import static org.opencord.aaa.impl.OsgiPropertyConstants.OPERATIONAL_STATUS_SERVER_TIMEOUT_DEFAULT;
+import static org.opencord.aaa.impl.OsgiPropertyConstants.STATISTICS_GENERATION_PERIOD;
+import static org.opencord.aaa.impl.OsgiPropertyConstants.STATISTICS_GENERATION_PERIOD_DEFAULT;
+import static org.opencord.aaa.impl.OsgiPropertyConstants.STATUS_SERVER_MODE;
+import static org.opencord.aaa.impl.OsgiPropertyConstants.STATUS_SERVER_MODE_DEFAULT;
+import static org.slf4j.LoggerFactory.getLogger;
 
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
 /**
  * AAA application for ONOS.
  */
 @Component(immediate = true, property = {
-        STATISTICS_GENERATION_EVENT + ":Integer=" + STATISTICS_GENERATION_EVENT_DEFAULT,
+        STATISTICS_GENERATION_PERIOD + ":Integer=" + STATISTICS_GENERATION_PERIOD_DEFAULT,
+        OPERATIONAL_STATUS_SERVER_EVENT_GENERATION + ":Integer=" + OPERATIONAL_STATUS_SERVER_EVENT_GENERATION_DEFAULT,
+        OPERATIONAL_STATUS_SERVER_TIMEOUT + ":Integer=" + OPERATIONAL_STATUS_SERVER_TIMEOUT_DEFAULT,
+        STATUS_SERVER_MODE + ":String=" + STATUS_SERVER_MODE_DEFAULT,
 })
 public class AaaManager
         extends AbstractListenerManager<AuthenticationEvent, AuthenticationEventListener>
@@ -129,12 +139,18 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected ComponentConfigService cfgService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected RadiusOperationalStatusService radiusOperationalStatusService;
+
     protected AuthenticationStatisticsEventPublisher authenticationStatisticsPublisher;
     protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
     private final DeviceListener deviceListener = new InternalDeviceListener();
 
-    /** Statistics generation interval. */
-    private int statisticsGenerationEvent = STATISTICS_GENERATION_EVENT_DEFAULT;
+    // Properties
+    private int statisticsGenerationPeriodInSeconds = STATISTICS_GENERATION_PERIOD_DEFAULT;
+    private int operationalStatusEventGenerationPeriodInSeconds = OPERATIONAL_STATUS_SERVER_EVENT_GENERATION_DEFAULT;
+    private int operationalStatusServerTimeoutInSeconds = OPERATIONAL_STATUS_SERVER_TIMEOUT_DEFAULT;
+    protected String operationalStatusEvaluationMode = STATUS_SERVER_MODE_DEFAULT;
 
     // NAS IP address
     protected InetAddress nasIpAddress;
@@ -181,7 +197,7 @@
     AaaConfig newCfg;
 
     ScheduledFuture<?> scheduledFuture;
-
+    ScheduledFuture<?> scheduledStatusServerChecker;
     ScheduledExecutorService executor;
     String configuredAaaServerAddress;
     HashSet<Byte> outPacketSet = new HashSet<Byte>();
@@ -261,11 +277,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,16 +302,40 @@
         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, STATISTICS_GENERATION_EVENT);
-        statisticsGenerationEvent = Strings.isNullOrEmpty(s)
-                ? STATISTICS_GENERATION_EVENT_DEFAULT : Integer.parseInt(s.trim());
+        Dictionary<String, Object> properties = context.getProperties();
+
+        String s = Tools.get(properties, "statisticsGenerationPeriodInSeconds");
+        statisticsGenerationPeriodInSeconds = Strings.isNullOrEmpty(s) ? STATISTICS_GENERATION_PERIOD_DEFAULT
+                : Integer.parseInt(s.trim());
+
+        s = Tools.get(properties, "operationalStatusEventGenerationPeriodInSeconds");
+        operationalStatusEventGenerationPeriodInSeconds = Strings.isNullOrEmpty(s)
+                ? OPERATIONAL_STATUS_SERVER_EVENT_GENERATION_DEFAULT
+                    : Integer.parseInt(s.trim());
+
+        s = Tools.get(properties, "operationalStatusServerTimeoutInSeconds");
+        operationalStatusServerTimeoutInSeconds = Strings.isNullOrEmpty(s) ? OPERATIONAL_STATUS_SERVER_TIMEOUT_DEFAULT
+                : Integer.parseInt(s.trim());
+
+        s = Tools.get(properties, "operationalStatusEvaluationMode");
+        String newEvaluationModeString = Strings.isNullOrEmpty(s) ? STATUS_SERVER_MODE_DEFAULT : 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() {
@@ -381,6 +425,7 @@
         outPacketSet.add(radiusPacket.getIdentifier());
         aaaStatisticsManager.getAaaStats().increaseOrDecreasePendingRequests(true);
         aaaStatisticsManager.getAaaStats().increaseAccessRequestsTx();
+        aaaStatisticsManager.putOutgoingIdentifierToMap(radiusPacket.getIdentifier());
         impl.sendRadiusPacket(radiusPacket, inPkt);
     }
 
@@ -411,6 +456,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 "
@@ -916,5 +965,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/OsgiPropertyConstants.java b/app/src/main/java/org/opencord/aaa/impl/OsgiPropertyConstants.java
index 38f1a32..1c59799 100644
--- a/app/src/main/java/org/opencord/aaa/impl/OsgiPropertyConstants.java
+++ b/app/src/main/java/org/opencord/aaa/impl/OsgiPropertyConstants.java
@@ -24,6 +24,16 @@
     private OsgiPropertyConstants() {
     }
 
-    public static final String STATISTICS_GENERATION_EVENT = "statisticsGenerationEvent";
-    public static final int STATISTICS_GENERATION_EVENT_DEFAULT = 20;
+    public static final String STATISTICS_GENERATION_PERIOD = "statisticsGenerationPeriodInSeconds";
+    public static final int STATISTICS_GENERATION_PERIOD_DEFAULT = 20;
+
+    public static final String OPERATIONAL_STATUS_SERVER_EVENT_GENERATION =
+            "operationalStatusEventGenerationPeriodInSeconds";
+    public static final int OPERATIONAL_STATUS_SERVER_EVENT_GENERATION_DEFAULT = 30;
+
+    public static final String OPERATIONAL_STATUS_SERVER_TIMEOUT = "operationalStatusServerTimeoutInSeconds";
+    public static final int OPERATIONAL_STATUS_SERVER_TIMEOUT_DEFAULT = 10;
+
+    public static final String STATUS_SERVER_MODE = "operationalStatusEvaluationMode";
+    public static final String STATUS_SERVER_MODE_DEFAULT = "AUTO";
 }
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..ad00b5f 100755
--- a/app/src/main/java/org/opencord/aaa/impl/PortBasedRadiusCommunicator.java
+++ b/app/src/main/java/org/opencord/aaa/impl/PortBasedRadiusCommunicator.java
@@ -15,17 +15,17 @@
  */
 package org.opencord.aaa.impl;
 
+import com.google.common.collect.Maps;
 import org.onlab.packet.ARP;
 import org.onlab.packet.DeserializationException;
 import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
-import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.RADIUS;
 import org.onlab.packet.TpPort;
 import org.onlab.packet.UDP;
-
 import org.onosproject.core.ApplicationId;
 import org.onosproject.mastership.MastershipEvent;
 import org.onosproject.mastership.MastershipListener;
@@ -43,24 +43,20 @@
 import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketService;
-
 import org.opencord.aaa.AaaConfig;
 import org.opencord.aaa.RadiusCommunicator;
 import org.opencord.sadis.BaseInformationService;
 import org.opencord.sadis.SubscriberAndDeviceInformation;
-
 import org.slf4j.Logger;
 
-import com.google.common.collect.Maps;
-
-import static org.onosproject.net.packet.PacketPriority.CONTROL;
-import static org.slf4j.LoggerFactory.getLogger;
-
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.util.Map;
 import java.util.Set;
 
+import static org.onosproject.net.packet.PacketPriority.CONTROL;
+import static org.slf4j.LoggerFactory.getLogger;
+
 /**
  * Handles communication with the RADIUS server through ports
  * of the SDN switches.
@@ -260,13 +256,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..2fe5755
--- /dev/null
+++ b/app/src/main/java/org/opencord/aaa/impl/RadiusOperationalStatusManager.java
@@ -0,0 +1,248 @@
+/*
+ * 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 org.onlab.packet.RADIUS;
+import org.onlab.packet.RADIUSAttribute;
+import org.onosproject.event.AbstractListenerManager;
+import org.opencord.aaa.RadiusCommunicator;
+import org.opencord.aaa.RadiusOperationalStatusEvent;
+import org.opencord.aaa.RadiusOperationalStatusEventDelegate;
+import org.opencord.aaa.RadiusOperationalStatusEventListener;
+import org.opencord.aaa.RadiusOperationalStatusService;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+@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..d3c49c2 100755
--- a/app/src/main/java/org/opencord/aaa/impl/SocketBasedRadiusCommunicator.java
+++ b/app/src/main/java/org/opencord/aaa/impl/SocketBasedRadiusCommunicator.java
@@ -15,18 +15,7 @@
  */
 package org.opencord.aaa.impl;
 
-import static org.onosproject.net.packet.PacketPriority.CONTROL;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import org.onlab.packet.DeserializationException;
 import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
@@ -41,7 +30,17 @@
 import org.opencord.aaa.RadiusCommunicator;
 import org.slf4j.Logger;
 
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.onosproject.net.packet.PacketPriority.CONTROL;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Handles Socket based communication with the RADIUS server.
@@ -148,10 +147,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 5e9e140..48e4ac0 100644
--- a/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
+++ b/app/src/main/java/org/opencord/aaa/impl/StateMachine.java
@@ -17,19 +17,18 @@
 
 package org.opencord.aaa.impl;
 
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
+import com.google.common.collect.Maps;
 import org.onlab.packet.MacAddress;
 import org.onosproject.net.ConnectPoint;
 import org.opencord.aaa.AuthenticationEvent;
 import org.opencord.aaa.StateMachineDelegate;
 import org.slf4j.Logger;
 
-import com.google.common.collect.Maps;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * AAA Finite State Machine.
@@ -56,7 +55,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 +128,7 @@
     public static void initializeMaps() {
         sessionIdMap = Maps.newConcurrentMap();
         identifierMap = Maps.newConcurrentMap();
-        identifier = -1;
+        identifier = 1;
     }
 
     public static void destroyMaps() {
@@ -207,7 +206,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 +418,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();