VOL-253: Initial commit for pkt-in/pkt-out for Edgecore adapter

- Add protobuf msg for pkt_send
- Implement pkt-in/pkt-out in adapter (untested)
- Misc changes to conform with pep8

Change-Id: I991b8e03a39b82524e0d9a9437765b3fe0c18529
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
index 7233ac7..2a26193 100644
--- a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
@@ -18,13 +18,16 @@
 Asfvolt16 OLT adapter
 """
 
+
+from scapy.layers.l2 import Ether, Dot1Q
 from uuid import uuid4
 from common.frameio.frameio import BpfProgramFilter
 from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, DeferredQueue
+from common.frameio.frameio import hexify
 from scapy.packet import Packet
 from voltha.protos.common_pb2 import OperStatus, ConnectStatus
-from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Port, Device, \
-    PmConfigs, PmConfig, PmGroupConfig
+from voltha.protos.device_pb2 import Port
 from voltha.protos.common_pb2 import AdminState
 from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
 from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
@@ -33,9 +36,8 @@
 from voltha.core.logical_device_agent import mac_str_to_tuple
 from voltha.adapters.asfvolt16_olt.bal import Bal
 from voltha.adapters.device_handler import OltDeviceHandler
-from voltha.protos.bbf_fiber_base_pb2 import \
-    ChannelgroupConfig, ChannelpartitionConfig, ChannelpairConfig, \
-    ChannelterminationConfig, OntaniConfig, VOntaniConfig, VEnetConfig
+from voltha.protos.bbf_fiber_base_pb2 import ChannelterminationConfig, \
+        VOntaniConfig
 
 ASFVOLT_NNI_PORT = 50
 # ASFVOLT_NNI_PORT needs to be other than pon port value.
@@ -80,7 +82,8 @@
             device.serial_number = device.host_and_port
             self.adapter_agent.update_device(device)
 
-            self.add_port(port_no=ASFVOLT_NNI_PORT, port_type=Port.ETHERNET_NNI,
+            self.add_port(port_no=ASFVOLT_NNI_PORT,
+                          port_type=Port.ETHERNET_NNI,
                           label='NNI facing Ethernet port')
             self.logical_device_id = \
                 self.add_logical_device(device_id=device.id)
@@ -99,10 +102,6 @@
         device.oper_status = OperStatus.ACTIVATING
         self.adapter_agent.update_device(device)
 
-        # Open the frameio port to receive in-band packet_in messages
-
-    #        self.activate_io_port()
-
     def add_port(self, port_no, port_type, label):
         self.log.info('adding-port', port_no=port_no, port_type=port_type)
         if port_type is Port.ETHERNET_NNI:
@@ -140,10 +139,10 @@
                 n_buffers=256,  # TODO fake for now
                 n_tables=2,  # TODO ditto
                 capabilities=(  # TODO and ditto
-                    OFPC_FLOW_STATS
-                    | OFPC_TABLE_STATS
-                    | OFPC_PORT_STATS
-                    | OFPC_GROUP_STATS
+                    OFPC_FLOW_STATS |
+                    OFPC_TABLE_STATS |
+                    OFPC_PORT_STATS |
+                    OFPC_GROUP_STATS
                 )
             ),
             root_device_id=device_id
@@ -194,7 +193,7 @@
             device.connect_status = ConnectStatus.REACHABLE
             device.oper_status = OperStatus.ACTIVE
             device.reason = 'OLT activated successfully'
-            status = self.adapter_agent.update_device(device)
+            self.adapter_agent.update_device(device)
             self.log.info('OLT activation complete')
         else:
             device.oper_status = OperStatus.FAILED
@@ -207,8 +206,8 @@
         if ind_info['_sub_group_type'] == 'onu_discovery':
             self.log.info('Onu discovered', olt_id=self.olt_id,
                           pon_ni=ind_info['_pon_id'], onu_data=ind_info)
-            #To-Do: Need to handle the ONUs, where the admin state is
-            #ENABLED and operation state is in Failed or Unkown
+            # To-Do: Need to handle the ONUs, where the admin state is
+            # ENABLED and operation state is in Failed or Unkown
             self.log.info('Not Yet handled', olt_id=self.olt_id,
                           pon_ni=ind_info['_pon_id'], onu_data=ind_info)
         else:
@@ -278,14 +277,16 @@
         return
 
     def handle_omci_ind(self, ind_info):
-        child_device = self.adapter_agent.get_child_device(self.device_id,
-                                                           onu_id=ind_info['onu_id'])
+        child_device = self.adapter_agent.get_child_device(
+            self.device_id,
+            onu_id=ind_info['onu_id'])
         if child_device is None:
-            self.log.info('Onu is not configured',onu_id=ind_info['onu_id'])
+            self.log.info('Onu is not configured', onu_id=ind_info['onu_id'])
             return
         try:
-            self.adapter_agent.receive_proxied_message(child_device.proxy_address,
-                                                       ind_info['packet'])
+            self.adapter_agent.receive_proxied_message(
+                child_device.proxy_address,
+                ind_info['packet'])
         except Exception as e:
                 self.log.exception('', exc=str(e))
         return
@@ -339,3 +340,41 @@
 
     def delete(self):
         super(Asfvolt16Handler, self).delete()
+
+    def handle_packet_in(self, ind_info):
+        self.log.info('Received Packet-In', ind_info=ind_info)
+
+        pkt = Ether(ind_info['packet'])
+        if pkt.haslayer(Dot1Q):
+            outer_shim = pkt.getlayer(Dot1Q)
+            if isinstance(outer_shim.payload, Dot1Q):
+                inner_shim = outer_shim.payload
+                cvid = inner_shim.vlan
+                logical_port = cvid
+                popped_frame = (
+                    Ether(src=pkt.src, dst=pkt.dst, type=inner_shim.type) /
+                    inner_shim.payload
+                )
+                kw = dict(
+                    logical_device_id=self.logical_device_id,
+                    logical_port_no=logical_port,
+                )
+                self.log.info('sending-packet-in', **kw)
+                self.adapter_agent.send_packet_in(
+                    packet=str(popped_frame), **kw)
+
+        reactor.callLater(1, self.process_packet_in)
+
+    def packet_out(self, egress_port, msg):
+        self.log.info('sending-packet-out', egress_port=egress_port,
+                      msg=hexify(msg))
+        pkt = Ether(msg)
+        out_pkt = (
+            Ether(src=pkt.src, dst=pkt.dst) /
+            Dot1Q(vlan=PACKET_IN_VLAN) /
+            Dot1Q(vlan=egress_port, type=pkt.type) /
+            pkt.payload
+        )
+
+        # TODO: Need to retrieve the correct destination onu_id
+        self.bal.packet_out(onu_id=1, egress_port, str(out_pkt))
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py b/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py
index 81130fa..8017e2b 100644
--- a/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py
@@ -20,11 +20,9 @@
 from twisted.internet import reactor
 from common.utils.grpc_utils import twisted_async
 from voltha.adapters.asfvolt16_olt.protos import bal_indications_pb2
-from voltha.adapters.asfvolt16_olt.protos import bal_msg_type_pb2, \
-    bal_osmsg_pb2, bal_model_ids_pb2, bal_obj_pb2, bal_model_types_pb2, \
+from voltha.adapters.asfvolt16_olt.protos import bal_model_types_pb2, \
     bal_errno_pb2, bal_pb2
 from voltha.adapters.asfvolt16_olt.grpc_server import GrpcServer
-from voltha.protos.device_pb2 import Device
 
 
 class Asfvolt16RxHandler(object):
@@ -72,8 +70,9 @@
         else:
             ind_info['activation_successful'] = False
 
+        device_handler = self.adapter.devices_handlers[device_id]
         reactor.callLater(0,
-                          self.adapter.devices_handlers[device_id].handle_access_term_ind,
+                          device_handler.handle_access_term_ind,
                           ind_info)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
@@ -199,8 +198,9 @@
         ind_info['_vendor_id'] = onu_data.data.serial_number.vendor_id
         ind_info['_vendor_specific'] = \
             onu_data.data.serial_number.vendor_specific
+        device_handler = self.adapter.devices_handlers[device_id]
         reactor.callLater(0,
-                          self.adapter.devices_handlers[device_id].handle_sub_term_ind,
+                          device_handler.handle_sub_term_ind,
                           ind_info)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
@@ -266,8 +266,9 @@
         elif (bal_model_types_pb2.BAL_STATE_UP == onu_data.data.admin_state):
             ind_info['activation_successful'] = True
 
+        device_handler = self.adapter.devices_handlers[device_id]
         reactor.callLater(0,
-                          self.adapter.devices_handlers[device_id].handle_sub_term_ind,
+                          device_handler.handle_sub_term_ind,
                           ind_info)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
@@ -300,11 +301,20 @@
     @twisted_async
     def BalPktBearerChannelRxInd(self, request, context):
         device_id = request.device_id.decode('unicode-escape')
-        self.log.info('Not implemented yet',
+        self.log.info('Received Packet-In',
                       device_id=device_id, obj_type=request.objType)
         ind_info = dict()
-        ind_info['_object_type'] = 'packet_in_indication'
-        ind_info['_sub_group_type'] = 'bearer_message'
+        ind_info['flow_id'] = request.pktData.data.flow_id
+        ind_info['flow_type'] = request.pktData.data.flow_type
+        ind_info['intf_id'] = request.pktData.data.intf_id
+        ind_info['intf_type'] = request.pktData.data.intf_type
+        ind_info['svc_port'] = request.pktData.data.svc_port
+        ind_info['flow_cookie'] = request.pktData.data.flow_cookie
+        ind_info['pkt'] = request.pktData.data.pkt
+        device_handler = self.adapter.devices_handlers[device_id]
+        reactor.callLater(0,
+                          device_handler.handle_packet_in,
+                          ind_info)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -322,11 +332,12 @@
         ind_info['onu_id'] = packet_data.itu_omci_channel.sub_term_id
         ind_info['packet'] = request.balOmciResp.data.pkt
         self.log.info('ONU Id is',
-                     onu_id=packet_data.itu_omci_channel.sub_term_id)
+                      onu_id=packet_data.itu_omci_channel.sub_term_id)
 
+        device_handler = self.adapter.devices_handlers[device_id]
         reactor.callLater(0,
-                         self.adapter.devices_handlers[device_id].handle_omci_ind,
-                         ind_info)
+                          device_handler.handle_omci_ind,
+                          ind_info)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
diff --git a/voltha/adapters/asfvolt16_olt/bal.py b/voltha/adapters/asfvolt16_olt/bal.py
index e078eb5..0ddc3e8 100644
--- a/voltha/adapters/asfvolt16_olt/bal.py
+++ b/voltha/adapters/asfvolt16_olt/bal.py
@@ -15,7 +15,7 @@
 #
 
 from twisted.internet.defer import inlineCallbacks
-from voltha.adapters.asfvolt16_olt.protos import bal_pb2, bal_obj_pb2, \
+from voltha.adapters.asfvolt16_olt.protos import bal_pb2, \
     bal_model_types_pb2, bal_model_ids_pb2
 from voltha.adapters.asfvolt16_olt.grpc_client import GrpcClient
 
@@ -122,3 +122,23 @@
             self.log.info('activating-ONU-exception',
                           onu_info['onu_id'], exc=str(e))
         return
+
+    @inlineCallbacks
+    def packet_out(self, onu_id, egress_port, pkt):
+        self.log.info('packet-out', onu_id=onu_id, egress_port=egress_port)
+
+        obj = bal_pb2.BalCfg()
+
+        # Set the destination ONU info
+        obj.packet.key.dest.packet_send_dest.sub_term.sub_term_id = onu_id
+        # TODO: Need to provide correct values for sub_term_uni and int_id
+        obj.packet.key.dest.packet_send_dest.sub_term.sub_term_uni = egress_port
+        obj.packet.key.dest.packet_send_dest.sub_term.int_id = egress_port
+
+        # Set the Packet-out info
+        obj.packet.data.flow_type = BAL_FLOW_TYPE_DOWNSTREAM
+        # TODO: Need to provide correct value for intf_id
+        obj.packet.data.intf_id = egress_port
+        obj.packet.data.pkt = pkt
+
+        yield self.stub.BalCfgSet(obj)
diff --git a/voltha/adapters/device_handler.py b/voltha/adapters/device_handler.py
index 15f11eb..a952aa2 100644
--- a/voltha/adapters/device_handler.py
+++ b/voltha/adapters/device_handler.py
@@ -18,6 +18,7 @@
 from voltha.registry import registry
 from voltha.protos.common_pb2 import OperStatus, ConnectStatus, AdminState
 
+
 class DeviceHandler(object):
     def __init__(self, adapter, device_id):
         self.adapter = adapter
@@ -40,6 +41,7 @@
         device.connect_status = ConnectStatus.UNREACHABLE
         self.adapter_agent.update_device(device)
 
+
 class OltDeviceHandler(DeviceHandler):
     def __init__(self, adapter, device_id):
         super(OltDeviceHandler, self).__init__(adapter, device_id)
@@ -130,5 +132,9 @@
                 self.alarms.send_alarm(self, raw_data)
             '''
 
+    def packet_out(self, egress_port, msg):
+        raise NotImplementedError()
+
+
 class OnuDeviceHandler(DeviceHandler):
     pass