VOL-484: delete and addition of flows are not working on ASFvOLT16

Change-Id: I70c8ad6b2402cc3d468ac76cba6cb47f1ea2de13
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
index 653d978..043ce4c 100644
--- a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
@@ -1860,7 +1860,9 @@
                                     ASFVOLT_DOWNLINK_EAPOL_ID,
                                     ASFVOLT16_DEFAULT_VLAN)
         elif 'push_vlan' in action:
-            #self.del_flow(v_enet, ASFVOLT_EAPOL_ID, ASFVOLT_DOWNLINK_EAPOL_ID)
+            yield self.del_eapol_flow(v_enet, ASFVOLT_EAPOL_ID,
+                                ASFVOLT_DOWNLINK_EAPOL_ID,
+                                ASFVOLT16_DEFAULT_VLAN)
             yield self.prepare_and_add_eapol_flow(classifier, action, v_enet,
                                            ASFVOLT_EAPOL_ID_DATA_VLAN,
                                            ASFVOLT_DOWNLINK_EAPOL_ID_DATA_VLAN)
@@ -2231,12 +2233,112 @@
 
     @inlineCallbacks
     def del_all_flow(self, v_enet):
+        # Currently this got called whenever we delete a gemport, but
+        # del_flow will not work properly as suffcient parameters are not
+        # present for deleting a flow.
+        # Deleting a flow is of two steps process.
+        # 1. Deactivate the flow, making ADMIN_STATE Down
+        # 2. Deleting the flow, making BalCfgClear
+        # Need to implement deactivate flow, before deleting a flow
+        # for HSIA, DHCP and other flow types.
+        # For EAPOL it's implemented, check del_eapol_flow method
         yield self.del_flow(v_enet, ASFVOLT_HSIA_ID, ASFVOLT_HSIA_ID)
         yield self.del_flow(v_enet, ASFVOLT_DHCP_TAGGED_ID,
                       ASFVOLT_DOWNLINK_DHCP_TAGGED_ID)
         yield self.del_flow(v_enet, ASFVOLT_EAPOL_ID_DATA_VLAN,
                       ASFVOLT_DOWNLINK_EAPOL_ID_DATA_VLAN)
-        yield self.del_flow(v_enet, ASFVOLT_EAPOL_ID, ASFVOLT_DOWNLINK_EAPOL_ID)
+
+    @inlineCallbacks
+    def del_eapol_flow(self, v_enet, uplink_id, downlink_id, vlan_id):
+        # To-Do For a time being hard code the traffic class value.
+        # Need to know how to get the traffic class info from flows.
+        v_ont_ani = self.get_v_ont_ani(name=v_enet.v_enet.data.v_ontani_ref)
+        if v_ont_ani is None:
+            self.log.info('Failed-to-get-v_ont_ani',
+                          v_ont_ani=v_enet.v_enet.data.v_ontani_ref)
+            return
+        gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
+        if gem_port is None:
+            self.log.info('Failed-to-get-gemport',)
+            # To-Do: If Gemport not found, then flow failure indication
+            # should be sent to controller. For now, not sure how to
+            # send that to controller. so store the flows in v_enet
+            # and add it when gem port is created
+            #self.store_flows(uplink_classifier, uplink_action,
+            #                 v_enet, traffic_class=2)
+            return
+        pon_port = self._get_pon_port_from_pref_chanpair_ref(
+            v_ont_ani.v_ont_ani.data.preferred_chanpair)
+        onu_device = self.adapter_agent.get_child_device(
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id,
+            parent_port_no=pon_port)
+        if onu_device is None:
+            self.log.info('Failed-to-get-onu-device',
+                          onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+            return
+        downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                            onu_device.proxy_address.channel_id,
+                                            downlink_id)
+        is_down_stream = True
+        try:
+            self.log.info('Deleting-Downstream-flow',
+                          flow_id=downlink_flow_id)
+
+            yield self.bal.deactivate_eapol_flow(downlink_flow_id, is_down_stream,
+                                                 onu_id=onu_device.proxy_address.onu_id,
+                                                 intf_id=onu_device.proxy_address.channel_id,
+                                                 network_int_id=self.nni_intf_id,
+                                                 gemport_id=gem_port.gemport_id,
+                                                 stag=vlan_id)
+            # While deletion of one flow is in progress,
+            # we cannot delete an another flow. Right now use sleep
+            # of 0.1 sec, assuming that deletion of flow is successful.
+            yield asleep(0.1)
+
+            self.log.info('deleting-Downstream-eapol-flow',
+                          flow_id=downlink_flow_id)
+            yield self.bal.delete_flow(downlink_flow_id, is_down_stream)
+            yield asleep(0.1)
+        except Exception as e:
+            self.log.exception('failed-to-delete-downstream-flow', e=e,
+                               flow_id=downlink_flow_id,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
+
+        uplink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                          onu_device.proxy_address.channel_id,
+                                          uplink_id)
+
+        tcont = self.get_tcont_info(v_ont_ani, name=gem_port.tcont_ref)
+        if tcont is None:
+            self.log.info('Failed-to-get-tcont-info',
+                          tcont=gem_port.tcont_ref)
+            return
+        try:
+            is_down_stream = False
+            self.log.info('deactivating-Upstream-flow',
+                          flow_id=uplink_flow_id)
+            yield self.bal.deactivate_eapol_flow(uplink_flow_id, is_down_stream,
+                                                 onu_id=onu_device.proxy_address.onu_id,
+                                                 intf_id=onu_device.proxy_address.channel_id,
+                                                 network_int_id=self.nni_intf_id,
+                                                 gemport_id=gem_port.gemport_id,
+                                                 stag=vlan_id,
+                                                 sched_id=tcont.alloc_id)
+            # While deletion of one flow is in progress,
+            # we cannot delete an another flow. Right now use sleep
+            # of 0.1 sec, assuming that deletion of flow is successful.
+            yield asleep(0.1)
+
+            self.log.info('deleting-Upstream-eapol-flow',
+                          flow_id=uplink_flow_id)
+            yield self.bal.delete_flow(uplink_flow_id, is_down_stream)
+            yield asleep(0.1)
+        except Exception as e:
+            self.log.exception('failed-to-delete-Upstream-flow', e=e,
+                               flow_id=uplink_flow_id,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
 
     @inlineCallbacks
     def del_flow(self, v_enet, uplink_id, downlink_id):
@@ -2264,10 +2366,9 @@
         try:
             self.log.info('Deleting-Downstream-flow',
                           flow_id=downlink_flow_id)
-
-            yield self.bal.delete_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
-                              downlink_flow_id, is_down_stream)
+            # Need to implement deactivate HSIA flow ,currently only
+            # Delete is called, so hsia flow deletion will not work properly
+            yield self.bal.delete_flow(downlink_flow_id, is_down_stream)
             # While deletion of one flow is in progress,
             # we cannot delete an another flow. Right now use sleep
             # of 0.1 sec, assuming that deletion of flow is successful.
@@ -2285,9 +2386,10 @@
             is_down_stream = False
             self.log.info('deleting-Upstream-flow',
                           flow_id=uplink_flow_id)
-            yield self.bal.delete_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
-                              uplink_flow_id, is_down_stream)
+            # Need to implement deactivate HSIA flow ,currently only
+            # Delete is called, so hsia flow deletion will not work properly
+
+            yield self.bal.delete_flow(uplink_flow_id, is_down_stream)
             # While deletion of one flow is in progress,
             # we cannot delete an another flow. Right now use sleep
             # of 0.1 sec, assuming that deletion of flow is successful.
diff --git a/voltha/adapters/asfvolt16_olt/bal.py b/voltha/adapters/asfvolt16_olt/bal.py
index 43cdef1..ae8dcf0 100644
--- a/voltha/adapters/asfvolt16_olt/bal.py
+++ b/voltha/adapters/asfvolt16_olt/bal.py
@@ -348,7 +348,7 @@
                     obj.flow.data.action.presence_mask |= \
                         bal_model_types_pb2.BAL_ACTION_ID_CMDS_BITMASK
                 else:
-                    self.log.info('invalid-action',action_info=action_info)
+                    self.log.info('invalid-action', action_info=action_info)
                     return
 
             self.log.info('adding-flow-to-OLT-Device',
@@ -359,8 +359,16 @@
                           flow_id, onu_id, exc=str(e))
         return
 
+    # Note: Bal_2.4 version expects expects all the classifier and action
+    # in deactivate flow that was used during flow activation.
     @inlineCallbacks
-    def delete_flow(self, onu_id, intf_id, flow_id, is_downstream):
+    def deactivate_eapol_flow(self, flow_id, is_downstream,
+                              onu_id=None,
+                              intf_id=None,
+                              network_int_id=None,
+                              gemport_id=None,
+                              stag=None,
+                              sched_id=None):
         try:
             obj = bal_pb2.BalCfg()
             # Fill Header details
@@ -369,23 +377,65 @@
             # Fill Access Terminal Details
             # To-DO flow ID need to be retrieved from flow details
             obj.flow.key.flow_id = flow_id
-            if is_downstream is False:
-                obj.flow.key.flow_type = \
-                    bal_model_types_pb2.BAL_FLOW_TYPE_UPSTREAM
-            else:
+            obj.flow.data.admin_state = bal_model_types_pb2.BAL_STATE_DOWN
+            if intf_id is not None:
+                obj.flow.data.access_int_id = intf_id
+            if network_int_id is not None:
+                obj.flow.data.network_int_id = network_int_id
+            if onu_id is not None:
+                obj.flow.data.sub_term_id = onu_id
+            if gemport_id is not None:
+                obj.flow.data.svc_port_id = gemport_id
+
+            if is_downstream is True:
                 obj.flow.key.flow_type = \
                     bal_model_types_pb2.BAL_FLOW_TYPE_DOWNSTREAM
+            else:
+                obj.flow.key.flow_type = \
+                    bal_model_types_pb2.BAL_FLOW_TYPE_UPSTREAM
 
-            obj.flow.data.admin_state = bal_model_types_pb2.BAL_STATE_DOWN
-            obj.flow.data.access_int_id = intf_id
-            # obj.flow.data.network_int_id = intf_id
-            obj.flow.data.sub_term_id = onu_id
-            self.log.info('deleting-flows-from-OLT-Device',
+            obj.flow.data.classifier.pkt_tag_type = \
+                bal_model_types_pb2.BAL_PKT_TAG_TYPE_SINGLE_TAG
+            obj.flow.data.classifier.presence_mask |= \
+                bal_model_types_pb2.BAL_CLASSIFIER_ID_PKT_TAG_TYPE
+            obj.flow.data.classifier.o_vid = stag
+            obj.flow.data.classifier.presence_mask |= \
+                bal_model_types_pb2.BAL_CLASSIFIER_ID_O_VID
+            obj.flow.data.action.cmds_bitmask |= \
+                bal_model_types_pb2.BAL_ACTION_CMD_ID_TRAP_TO_HOST
+            obj.flow.data.action.presence_mask |= \
+                bal_model_types_pb2.BAL_ACTION_ID_CMDS_BITMASK
+            if sched_id is not None:
+                obj.flow.data.dba_tm_sched_id = sched_id
+
+            self.log.info('deactivating-eapol-flows-from-OLT-Device',
                           flow_details=obj)
             yield self.stub.BalCfgSet(obj, timeout=GRPC_TIMEOUT)
         except Exception as e:
-            self.log.info('delete_flow-exception',
-                          flow_id, onu_id, exc=str(e))
+            self.log.exception('deactivate-eapol-flow-exception',
+                               flow_id, onu_id, exc=str(e))
+        return
+
+    @inlineCallbacks
+    def delete_flow(self, flow_id, is_downstream):
+        try:
+            obj = bal_pb2.BalKey()
+            # Fill Header details
+            obj.hdr.obj_type = bal_model_ids_pb2.BAL_OBJ_ID_FLOW
+            obj.flow_key.flow_id = flow_id
+            if is_downstream is False:
+                obj.flow_key.flow_type = \
+                    bal_model_types_pb2.BAL_FLOW_TYPE_UPSTREAM
+            else:
+                obj.flow_key.flow_type = \
+                    bal_model_types_pb2.BAL_FLOW_TYPE_DOWNSTREAM
+
+            self.log.info('deleting-flows-from-OLT-Device',
+                          flow_details=obj)
+            resp = yield self.stub.BalCfgClear(obj, timeout=GRPC_TIMEOUT)
+        except Exception as e:
+            self.log.exception('delete_flow-exception',
+                          flow_id, e=e)
         return
 
     @inlineCallbacks