ONOS-4374 Enable installing flows with hard_timeout

Change-Id: I4e60e93aad44c7e8f8913fa4dd3ed5a2565d7034
diff --git a/core/api/src/main/java/org/onosproject/net/flow/DefaultFlowRule.java b/core/api/src/main/java/org/onosproject/net/flow/DefaultFlowRule.java
index d38d044..d258a74 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/DefaultFlowRule.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/DefaultFlowRule.java
@@ -46,6 +46,8 @@
 
     private final int timeout;
     private final boolean permanent;
+    private final int hardTimeout;
+    private final FlowRemoveReason reason;
     private final GroupId groupId;
 
     private final Integer tableId;
@@ -60,6 +62,8 @@
         this.groupId = rule.groupId();
         this.id = rule.id();
         this.timeout = rule.timeout();
+        this.hardTimeout = rule.hardTimeout();
+        this.reason = rule.reason();
         this.permanent = rule.isPermanent();
         this.created = System.currentTimeMillis();
         this.tableId = rule.tableId();
@@ -68,8 +72,8 @@
 
     private DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
                             TrafficTreatment treatment, Integer priority,
-                            FlowId flowId, Boolean permanent, Integer timeout,
-                            Integer tableId) {
+                            FlowId flowId, Boolean permanent, Integer timeout, Integer hardTimeout,
+                            FlowRemoveReason reason, Integer tableId) {
 
         this.deviceId = deviceId;
         this.selector = selector;
@@ -79,6 +83,8 @@
         this.id = flowId;
         this.permanent = permanent;
         this.timeout = timeout;
+        this.hardTimeout = hardTimeout;
+        this.reason = reason;
         this.tableId = tableId;
         this.created = System.currentTimeMillis();
 
@@ -88,6 +94,8 @@
         this.payLoad = null;
     }
 
+
+
     /**
      * Support for the third party flow rule. Creates a flow rule of flow table.
      *
@@ -105,6 +113,28 @@
                            TrafficTreatment treatment, int priority,
                            ApplicationId appId, int timeout, boolean permanent,
                            FlowRuleExtPayLoad payLoad) {
+        this(deviceId, selector, treatment, priority, appId, timeout, 0, permanent, payLoad);
+    }
+
+
+    /**
+     * Support for the third party flow rule. Creates a flow rule of flow table.
+     *
+     * @param deviceId the identity of the device where this rule applies
+     * @param selector the traffic selector that identifies what traffic this
+     *            rule
+     * @param treatment the traffic treatment that applies to selected traffic
+     * @param priority the flow rule priority given in natural order
+     * @param appId the application id of this flow
+     * @param timeout the timeout for this flow requested by an application
+     * @param hardTimeout the hard timeout located switch's flow table for this flow requested by an application
+     * @param permanent whether the flow is permanent i.e. does not time out
+     * @param payLoad 3rd-party origin private flow
+     */
+    public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
+                           TrafficTreatment treatment, int priority,
+                           ApplicationId appId, int timeout, int hardTimeout, boolean permanent,
+                           FlowRuleExtPayLoad payLoad) {
 
         checkArgument(priority >= MIN_PRIORITY, "Priority cannot be less than " +
                 MIN_PRIORITY);
@@ -118,6 +148,8 @@
         this.appId = appId.id();
         this.groupId = new DefaultGroupId(0);
         this.timeout = timeout;
+        this.reason = FlowRemoveReason.NO_REASON;
+        this.hardTimeout = hardTimeout;
         this.permanent = permanent;
         this.tableId = 0;
         this.created = System.currentTimeMillis();
@@ -152,6 +184,30 @@
                            TrafficTreatment treatment, int priority,
                            ApplicationId appId, GroupId groupId, int timeout,
                            boolean permanent, FlowRuleExtPayLoad payLoad) {
+        this(deviceId, selector, treatment, priority, appId, groupId, timeout, 0, permanent, payLoad);
+    }
+
+    /**
+     * Support for the third party flow rule. Creates a flow rule of group
+     * table.
+     *
+     * @param deviceId the identity of the device where this rule applies
+     * @param selector the traffic selector that identifies what traffic this
+     *            rule
+     * @param treatment the traffic treatment that applies to selected traffic
+     * @param priority the flow rule priority given in natural order
+     * @param appId the application id of this flow
+     * @param groupId the group id of this flow
+     * @param timeout the timeout for this flow requested by an application
+     * @param hardTimeout the hard timeout located switch's flow table for this flow requested by an application
+     * @param permanent whether the flow is permanent i.e. does not time out
+     * @param payLoad 3rd-party origin private flow
+     *
+     */
+    public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
+                           TrafficTreatment treatment, int priority,
+                           ApplicationId appId, GroupId groupId, int timeout, int hardTimeout,
+                           boolean permanent, FlowRuleExtPayLoad payLoad) {
 
         checkArgument(priority >= MIN_PRIORITY, "Priority cannot be less than " +
                 MIN_PRIORITY);
@@ -165,6 +221,8 @@
         this.appId = appId.id();
         this.groupId = groupId;
         this.timeout = timeout;
+        this.reason = FlowRemoveReason.NO_REASON;
+        this.hardTimeout = hardTimeout;
         this.permanent = permanent;
         this.created = System.currentTimeMillis();
         this.tableId = 0;
@@ -280,6 +338,18 @@
         return timeout;
     }
 
+
+
+    @Override
+    public int hardTimeout() {
+        return hardTimeout;
+    }
+
+    @Override
+    public FlowRemoveReason reason() {
+        return reason;
+    }
+
     @Override
     public boolean isPermanent() {
         return permanent;
@@ -310,6 +380,8 @@
         private TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
         private Integer timeout;
         private Boolean permanent;
+        private Integer hardTimeout = 0;
+        private FlowRemoveReason reason = FlowRemoveReason.NO_REASON;
 
         @Override
         public FlowRule.Builder withCookie(long cookie) {
@@ -368,6 +440,20 @@
         }
 
         @Override
+        public FlowRule.Builder withHardTimeout(int timeout) {
+            this.permanent = false;
+            this.hardTimeout = timeout;
+            this.timeout = timeout;
+            return this;
+        }
+
+        @Override
+        public FlowRule.Builder withReason(FlowRemoveReason reason) {
+            this.reason = reason;
+            return this;
+        }
+
+        @Override
         public FlowRule build() {
             FlowId localFlowId;
             checkArgument((flowId != null) ^ (appId != null), "Either an application" +
@@ -390,7 +476,7 @@
             }
 
             return new DefaultFlowRule(deviceId, selector, treatment, priority,
-                                       localFlowId, permanent, timeout, tableId);
+                                       localFlowId, permanent, timeout, hardTimeout, reason, tableId);
         }
 
         private FlowId computeFlowId(ApplicationId appId) {
diff --git a/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java b/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java
index ecec89c..35804b8 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java
@@ -30,6 +30,28 @@
     int MAX_PRIORITY = 65535;
 
     /**
+     * Reason for flow parameter received from switches.
+     * Used to check reason parameter in flows.
+     */
+    enum FlowRemoveReason {
+        NO_REASON,
+        IDLE_TIMEOUT,
+        HARD_TIMEOUT;
+        public static FlowRemoveReason parseShort(short reason) {
+            switch (reason) {
+                case -1 :
+                    return NO_REASON;
+                case 0:
+                    return IDLE_TIMEOUT;
+                case 1:
+                    return HARD_TIMEOUT;
+                default :
+                    return NO_REASON;
+            }
+        }
+    }
+
+    /**
      * Returns the ID of this flow.
      *
      * @return the flow ID
@@ -88,6 +110,21 @@
     int timeout();
 
     /**
+     * Returns the hard timeout for this flow requested by an application.
+     * This paremeter configure switch's flow hard timeout.
+     * In case of controller-switch connection lost, this variable can be useful.
+     * @return integer value of the hard Timeout
+     */
+    int hardTimeout();
+
+    /**
+     * Returns the reason for the flow received from switches.
+     *
+     * @return FlowRemoveReason value of reason
+     */
+    FlowRemoveReason reason();
+
+    /**
      * Returns whether the flow is permanent i.e. does not time out.
      *
      * @return true if the flow is permanent, otherwise false
@@ -212,6 +249,20 @@
         Builder makeTemporary(int timeout);
 
         /**
+         * Sets hard timeout parameter in flow table.
+         * @param timeout an integer
+         * @return this
+         */
+        Builder withHardTimeout(int timeout);
+
+        /**
+         * Sets reason parameter received from switches .
+         * @param reason a short
+         * @return this
+         */
+        Builder withReason(FlowRemoveReason reason);
+
+        /**
          * Builds a flow rule object.
          *
          * @return a flow rule.
diff --git a/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java b/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
index 471546e..c090fae 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
@@ -26,6 +26,7 @@
 import org.onosproject.net.NetTestTools;
 import org.onosproject.net.NetworkResource;
 import org.onosproject.net.Path;
+import org.onosproject.net.flow.FlowRule.FlowRemoveReason;
 import org.onosproject.net.flow.FlowId;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.FlowRuleExtPayLoad;
@@ -330,6 +331,16 @@
         }
 
         @Override
+        public int hardTimeout() {
+            return 0;
+        }
+
+        @Override
+        public FlowRemoveReason reason() {
+            return FlowRemoveReason.NO_REASON;
+        }
+
+        @Override
         public boolean isPermanent() {
             return false;
         }
diff --git a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
index a61b150..74524ac 100644
--- a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
@@ -313,8 +313,10 @@
             extends AbstractProviderService<FlowRuleProvider>
             implements FlowRuleProviderService {
 
+        final Map<FlowEntry, Long> firstSeen = Maps.newConcurrentMap();
         final Map<FlowEntry, Long> lastSeen = Maps.newConcurrentMap();
 
+
         protected InternalFlowRuleProviderService(FlowRuleProvider provider) {
             super(provider);
         }
@@ -324,11 +326,15 @@
             checkNotNull(flowEntry, FLOW_RULE_NULL);
             checkValidity();
             lastSeen.remove(flowEntry);
+            firstSeen.remove(flowEntry);
             FlowEntry stored = store.getFlowEntry(flowEntry);
             if (stored == null) {
                 log.debug("Rule already evicted from store: {}", flowEntry);
                 return;
             }
+            if (flowEntry.reason() == FlowEntry.FlowRemoveReason.HARD_TIMEOUT) {
+                ((DefaultFlowEntry) stored).setState(FlowEntry.FlowEntryState.REMOVED);
+            }
             Device device = deviceService.getDevice(flowEntry.deviceId());
             FlowRuleProvider frp = getProvider(device.providerId());
             FlowRuleEvent event = null;
@@ -422,6 +428,21 @@
 
             final long timeout = storedRule.timeout() * 1000;
             final long currentTime = System.currentTimeMillis();
+
+            // Checking flow with hardTimeout
+            if (storedRule.hardTimeout() != 0) {
+                if (!firstSeen.containsKey(storedRule)) {
+                    // First time rule adding
+                    firstSeen.put(storedRule, currentTime);
+                } else {
+                    Long first = firstSeen.get(storedRule);
+                    final long hardTimeout = storedRule.hardTimeout() * 1000;
+                    if ((currentTime - first) > hardTimeout) {
+                        return false;
+                    }
+                }
+            }
+
             if (storedRule.packets() != swRule.packets()) {
                 lastSeen.put(storedRule, currentTime);
                 return true;
diff --git a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
index 74ad5d8..60030ee 100644
--- a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
+++ b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
@@ -101,6 +101,7 @@
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowId;
+import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.FlowRuleBatchEntry;
 import org.onosproject.net.flow.FlowRuleBatchEvent;
 import org.onosproject.net.flow.FlowRuleBatchOperation;
@@ -349,6 +350,7 @@
                     DefaultFlowEntry.class,
                     StoredFlowEntry.class,
                     DefaultFlowRule.class,
+                    FlowRule.FlowRemoveReason.class,
                     DefaultPacketRequest.class,
                     PacketPriority.class,
                     FlowEntry.FlowEntryState.class,
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
index b41f6ce..c02674f 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
@@ -95,6 +95,7 @@
                 .setMatch(match)
                 .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
                 .setPriority(flowRule().priority())
+                .setHardTimeout(flowRule().hardTimeout())
                 .build();
 
         return fm;
@@ -115,6 +116,7 @@
                 .setMatch(match)
                 .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
                 .setPriority(flowRule().priority())
+                .setHardTimeout(flowRule().hardTimeout())
                 .build();
 
         return fm;
@@ -133,6 +135,7 @@
                 .setMatch(match)
                 .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
                 .setPriority(flowRule().priority())
+                .setHardTimeout(flowRule().hardTimeout())
                 .build();
 
         return fm;
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
index cd96472..44b839b 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
@@ -158,6 +158,7 @@
                 .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
                 .setPriority(flowRule().priority())
                 .setTableId(TableId.of(flowRule().tableId()))
+                .setHardTimeout(flowRule().hardTimeout())
                 .build();
 
         return fm;
@@ -201,6 +202,7 @@
                 .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
                 .setPriority(flowRule().priority())
                 .setTableId(TableId.of(flowRule().tableId()))
+                .setHardTimeout(flowRule().hardTimeout())
                 .build();
 
         return fm;
@@ -220,6 +222,7 @@
                 .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
                 .setPriority(flowRule().priority())
                 .setTableId(TableId.of(flowRule().tableId()))
+                .setHardTimeout(flowRule().hardTimeout())
                 .build();
 
         return fm;
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java
index 54f704f..e40990e 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java
@@ -182,7 +182,9 @@
                             .withSelector(buildSelector())
                             .withPriority(removed.getPriority())
                             .makeTemporary(removed.getIdleTimeout())
-                            .withCookie(removed.getCookie().getValue());
+                            .withCookie(removed.getCookie().getValue())
+                            .withReason(FlowRule.FlowRemoveReason.parseShort(removed.getReason()));
+
                     if (removed.getVersion() != OFVersion.OF_10) {
                         builder.forTable(removed.getTableId().getValue());
                     }
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/FlowsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/FlowsResourceTest.java
index 68dc393..2ecd373 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/FlowsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/FlowsResourceTest.java
@@ -46,6 +46,7 @@
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowId;
+import org.onosproject.net.flow.FlowRule.FlowRemoveReason;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.FlowRuleExtPayLoad;
 import org.onosproject.net.flow.FlowRuleService;
@@ -218,6 +219,16 @@
         }
 
         @Override
+        public int hardTimeout() {
+            return 0;
+        }
+
+        @Override
+        public FlowRemoveReason reason() {
+            return  FlowRemoveReason.NO_REASON;
+        }
+
+        @Override
         public boolean isPermanent() {
             return false;
         }
@@ -295,6 +306,16 @@
         }
 
         @Override
+        public int hardTimeout() {
+            return 0;
+        }
+
+        @Override
+        public FlowRemoveReason reason() {
+            return FlowRemoveReason.NO_REASON;
+        }
+
+        @Override
         public boolean isPermanent() {
             return false;
         }