VOL-252: Process flow rules and provision BAL for HSIA, DHCP, EAPOL flows

Change-Id: I3dc2629e442b44377d69f7025f70446634a4eac5
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
index 47946e0..6f02b80 100644
--- a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
@@ -23,21 +23,32 @@
 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
+import voltha.core.flow_decomposer as fd
 from voltha.protos.common_pb2 import OperStatus, ConnectStatus
 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, \
     OFPPF_1GB_FD, OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, \
-    OFPC_FLOW_STATS, ofp_switch_features, ofp_desc, ofp_port
+    OFPC_FLOW_STATS, ofp_switch_features, ofp_desc, ofp_port, \
+    OFPXMC_OPENFLOW_BASIC
 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 ChannelterminationConfig, \
-        VOntaniConfig
+from voltha.protos.bbf_fiber_base_pb2 import \
+    ChannelgroupConfig, ChannelpartitionConfig, ChannelpairConfig, \
+    ChannelterminationConfig, OntaniConfig, VOntaniConfig, VEnetConfig
+from voltha.protos.bbf_fiber_traffic_descriptor_profile_body_pb2 import \
+    TrafficDescriptorProfileData
+from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
+from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
+from voltha.protos.bbf_fiber_multicast_gemport_body_pb2 import \
+    MulticastGemportsConfigData
+from voltha.protos.bbf_fiber_multicast_distribution_set_body_pb2 import \
+    MulticastDistributionSetData
+import time
 
 ASFVOLT_NNI_PORT = 50
 # ASFVOLT_NNI_PORT needs to be other than pon port value.
@@ -48,14 +59,58 @@
 is_inband_frame = BpfProgramFilter('(ether[14:2] & 0xfff) = 0x{:03x}'.format(
     PACKET_IN_VLAN))
 
+ASFVOLT_EAPOL_ID = 1
+ASFVOLT_DHCP_UNTAGGED_ID = 2
+ASFVOLT_DHCP_TAGGED_ID = 3
+ASFVOLT_IGMP_UNTAGGED_ID = 4
+ASFVOLT_IGMP_TAGGED_ID = 5
+ASFVOLT_FIRMWARE_ID = 6
+ASFVOLT_ARP_ID = 7
+ASFVOLT_HSIA_ID = 8
+ASFVOLT_DNS_ID = 9
+
+ASFVOLT_DOWNLINK_EAPOL_ID = 10
+ASFVOLT_DOWNLINK_DHCP_UNTAGGED_ID = 11
+ASFVOLT_DOWNLINK_DHCP_TAGGED_ID = 12
+ASFVOLT_DOWNLINK_IGMP_UNTAGGED_ID = 13
+ASFVOLT_DOWNLINK_IGMP_TAGGED_ID = 14
+ASFVOLT_DOWNLINK_FIRMWARE_ID = 15
+ASFVOLT_DOWNLINK_ARP_ID = 16
+ASFVOLT_DOWNLINK_HSIA_ID = 17
+ASFVOLT_DOWNLINK_DNS_ID = 18
+
+
+class VEnetHandler(object):
+
+    def __init__(self):
+        self.v_enet = VEnetConfig()
+        self.gem_ports = dict()
+
+
+class VOntAniHandler(object):
+
+    def __init__(self):
+        self.v_ont_ani = VOntaniConfig()
+        self.tconts = dict()
+
 
 class Asfvolt16Handler(OltDeviceHandler):
+
     def __init__(self, adapter, device_id):
         super(Asfvolt16Handler, self).__init__(adapter, device_id)
         self.filter = is_inband_frame
         self.bal = Bal(self, self.log)
         self.host_and_port = None
         self.olt_id = 0
+        self.channel_groups = dict()
+        self.channel_partitions = dict()
+        self.channel_pairs = dict()
+        self.channel_terminations = dict()
+        self.v_ont_anis = dict()
+        self.ont_anis = dict()
+        self.v_enets = dict()
+        self.traffic_descriptors = dict()
+        self.uni_port_num = 20
 
     def __del__(self):
         super(Asfvolt16Handler, self).__del__()
@@ -63,6 +118,60 @@
     def __str__(self):
         return "Asfvolt16Handler: {}".format(self.host_and_port)
 
+    def _get_next_uni_port(self):
+        self.uni_port_num += 1
+        return self.uni_port_num
+
+    def get_venet(self, name):
+        for key, v_enet in self.v_enets.items():
+            if key == name:
+                return v_enet
+        return None
+
+    def get_v_ont_ani(self, name):
+        for key, v_ont_ani in self.v_ont_anis.items():
+            if key == name:
+                return v_ont_ani
+        return None
+
+    def get_gem_port_info(self, v_enet, **kwargs):
+        traffic_class = kwargs.pop('traffic_class', None)
+        name = kwargs.pop('name', None)
+        for key, gem_port in v_enet.gem_ports.items():
+            if traffic_class is not None:
+                if traffic_class == gem_port.traffic_class:
+                    return gem_port
+            if name is not None:
+                if name == gem_port.name:
+                    return gem_port
+        return None
+
+    def get_tcont_info(self, v_ont_ani, **kwargs):
+        alloc_id = kwargs.pop('alloc_id', None)
+        name = kwargs.pop('name', None)
+        for key, tcont in v_ont_ani.tconts.items():
+            if alloc_id is not None:
+                if alloc_id == tcont.alloc_id:
+                    return tcont
+            if name is not None:
+                if name == tcont.name:
+                    return tcont
+        return None
+
+    def get_traffic_profile(self, name):
+        for key, traffic_profile in self.traffic_descriptors():
+            if name is not None:
+                if name == traffic_profile.name:
+                    return traffic_profile
+        return None
+
+    def get_flow_id(self, onu_id, intf_id, id):
+        # Tp-Do Need to generate unique flow ID using
+        # OnuID, IntfId, id
+        # BAL accepts flow_id till 16384. So we are
+        # using only onu_id and id to generate flow ID.
+        return ((onu_id << 13) | id)
+
     def activate(self, device):
 
         self.log.info('activating-asfvolt16-olt', device=device)
@@ -87,7 +196,7 @@
                           label='NNI facing Ethernet port')
             self.logical_device_id = \
                 self.add_logical_device(device_id=device.id)
-            self.add_logical_port(port_no=1,
+            self.add_logical_port(port_no=ASFVOLT_NNI_PORT,
                                   port_type=Port.ETHERNET_NNI,
                                   device_id=device.id,
                                   logical_device_id=self.logical_device_id)
@@ -111,7 +220,7 @@
             # For now make the status as Active.
             oper_status = OperStatus.ACTIVE
         else:
-            self.log.erro('invalid-port-type', port_type=port_type)
+            self.log.error('invalid-port-type', port_type=port_type)
             return
 
         port = Port(
@@ -160,7 +269,7 @@
             curr_speed = OFPPF_1GB_FD
             max_speed = OFPPF_1GB_FD
         else:
-            self.log.erro('invalid-port-type', port_type=port_type)
+            self.log.error('invalid-port-type', port_type=port_type)
             return
 
         ofp = ofp_port(
@@ -188,7 +297,7 @@
     def handle_access_term_ind(self, ind_info):
         device = self.adapter_agent.get_device(self.device_id)
         if ind_info['activation_successful'] is True:
-            self.log.info('successful access terminal Indication',
+            self.log.info('successful-access-terminal-Indication',
                           olt_id=self.olt_id)
             device.connect_status = ConnectStatus.REACHABLE
             device.oper_status = OperStatus.ACTIVE
@@ -204,35 +313,35 @@
 
     def handle_not_started_onu(self, child_device, ind_info):
         if ind_info['_sub_group_type'] == 'onu_discovery':
-            self.log.info('Onu discovered', olt_id=self.olt_id,
+            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
-            self.log.info('Not Yet handled', olt_id=self.olt_id,
+            self.log.info('Not-Yet-handled', olt_id=self.olt_id,
                           pon_ni=ind_info['_pon_id'], onu_data=ind_info)
         else:
-            self.log.info('Invalid ONU event', olt_id=self.olt_id,
+            self.log.info('Invalid-ONU-event', olt_id=self.olt_id,
                           pon_ni=ind_info['_pon_id'], onu_data=ind_info)
 
     def handle_activating_onu(self, child_device, ind_info):
         pon_id = ind_info['_pon_id']
-        self.log.info('Not handled Yet', olt_id=self.olt_id,
+        self.log.info('Not-handled-Yet', olt_id=self.olt_id,
                       pon_ni=pon_id, onu_data=ind_info)
 
     def handle_activated_onu(self, child_device, ind_info):
         pon_id = ind_info['_pon_id']
-        self.log.info('Not handled Yet', olt_id=self.olt_id,
+        self.log.info('Not-handled-Yet', olt_id=self.olt_id,
                       pon_ni=pon_id, onu_data=ind_info)
 
     def handle_discovered_onu(self, child_device, ind_info):
         pon_id = ind_info['_pon_id']
         if ind_info['_sub_group_type'] == 'onu_discovery':
-            self.log.info('Activation is in progress', olt_id=self.olt_id,
+            self.log.info('Activation-is-in-progress', olt_id=self.olt_id,
                           pon_ni=pon_id, onu_data=ind_info,
                           onu_id=child_device.proxy_address.onu_id)
 
         elif ind_info['_sub_group_type'] == 'sub_term_indication':
-            self.log.info('ONU activation is completed', olt_id=self.olt_id,
+            self.log.info('ONU-activation-is-completed', olt_id=self.olt_id,
                           pon_ni=pon_id, onu_data=ind_info)
 
             msg = {'proxy_address': child_device.proxy_address,
@@ -241,8 +350,25 @@
             # Send the event message to the ONU adapter
             self.adapter_agent.publish_inter_adapter_message(child_device.id,
                                                              msg)
+            if ind_info['activation_successful'] is True:
+                for key, v_ont_ani in self.v_ont_anis.items():
+                    if v_ont_ani.v_ont_ani.data.onu_id == \
+                            child_device.proxy_address.onu_id:
+                        for tcont_key, tcont in v_ont_ani.tconts.items():
+                            owner_info = dict()
+                            # To-Do: Right Now use alloc_id as schduler ID. Need to
+                            # find way to generate uninqe number.
+                            id = tcont.alloc_id
+                            owner_info['type'] = 'agg_port'
+                            owner_info['intf_id'] = \
+                                child_device.proxy_address.channel_id
+                            owner_info['onu_id'] = \
+                                child_device.proxy_address.onu_id
+                            owner_info['alloc_id'] = tcont.alloc_id
+                            self.bal.create_scheduler(id, 'upstream',
+                                                      owner_info, 8)
         else:
-            self.log.info('Invalid ONU event', olt_id=self.olt_id,
+            self.log.info('Invalid-ONU-event', olt_id=self.olt_id,
                           pon_ni=ind_info['_pon_id'], onu_data=ind_info)
 
     onu_handlers = {
@@ -259,7 +385,7 @@
             serial_number=(ind_info['_vendor_id'] +
                            ind_info['_vendor_specific']))
         if child_device is None:
-            self.log.info('Onu is not configured', olt_id=self.olt_id,
+            self.log.info('Onu-is-not-configured', olt_id=self.olt_id,
                           pon_ni=ind_info['_pon_id'], onu_data=ind_info)
             return
 
@@ -288,53 +414,182 @@
                 child_device.proxy_address,
                 ind_info['packet'])
         except Exception as e:
-                self.log.exception('', exc=str(e))
+            self.log.exception('', exc=str(e))
         return
 
+    def handle_v_ont_ani_config(self, data):
+        serial_number = data.data.expected_serial_number
+        child_device = self.adapter_agent.get_child_device(
+            self.device_id,
+            serial_number=serial_number)
+        if child_device is None:
+            self.log.info('Failed-to-find-ONU-Info',
+                          serial_number=serial_number)
+        elif child_device.admin_state == AdminState.ENABLED:
+            self.log.info('Activating ONU',
+                          serial_number=serial_number,
+                          onu_id=child_device.proxy_address.onu_id,
+                          pon_id=child_device.parent_port_no)
+            onu_info = dict()
+            onu_info['pon_id'] = child_device.parent_port_no
+            onu_info['onu_id'] = child_device.proxy_address.onu_id
+            onu_info['vendor'] = child_device.vendor_id
+            onu_info['vendor_specific'] = serial_number[4:]
+            self.bal.activate_onu(onu_info)
+        else:
+            self.log.info('Invalid-ONU-state-to-activate',
+                          onu_id=child_device.proxy_address.onu_id,
+                          serial_number=serial_number)
+
     def create_interface(self, data):
         try:
+            if isinstance(data, ChannelgroupConfig):
+                if data.name in self.channel_groups:
+                    self.log('Channel-Group-already-present',
+                             channel_group=data)
+                else:
+                    channel_group_config = ChannelgroupConfig()
+                    channel_group_config.CopyFrom(data)
+                    self.channel_groups[data.name] = channel_group_config
+            if isinstance(data, ChannelpartitionConfig):
+                if data.name in self.channel_partitions:
+                    self.log('Channel-partition-already-present',
+                             channel_partition=data)
+                else:
+                    channel_partition_config = ChannelpartitionConfig()
+                    channel_partition_config.CopyFrom(data)
+                    self.channel_partitions[data.name] = \
+                        channel_partition_config
+            if isinstance(data, ChannelpairConfig):
+                if data.name in self.channel_pairs:
+                    self.log('Channel-pair-already-present',
+                             channel_pair=data)
+                else:
+                    channel_pair_config = ChannelpairConfig()
+                    channel_pair_config.CopyFrom(data)
+                    self.channel_pairs[data.name] = channel_pair_config
             if isinstance(data, ChannelterminationConfig):
-                self.log.info('Activating PON port at OLT',
+                self.log.info('Activating-PON-port-at-OLT',
                               pon_id=data.data.xgs_ponid)
                 self.add_port(port_no=data.data.xgs_ponid,
                               port_type=Port.PON_OLT,
                               label=data.name)
                 self.bal.activate_pon_port(self.olt_id, data.data.xgs_ponid)
-            if isinstance(data, VOntaniConfig):
-                serial_number = data.data.expected_serial_number
-                child_device = self.adapter_agent.get_child_device(
-                    self.device_id,
-                    serial_number=serial_number)
-                if child_device is None:
-                    self.log.info('Failed to find ONU Info',
-                                  serial_number=serial_number)
-                elif child_device.admin_state == AdminState.ENABLED:
-                    self.log.info('Activating ONU',
-                                  serial_number=serial_number,
-                                  onu_id=child_device.proxy_address.onu_id,
-                                  pon_id=child_device.parent_port_no)
-                    onu_info = dict()
-                    onu_info['pon_id'] = child_device.parent_port_no
-                    onu_info['onu_id'] = child_device.proxy_address.onu_id
-                    onu_info['vendor'] = child_device.vendor_id
-                    onu_info['vendor_specific'] = serial_number[4:]
-                    self.bal.activate_onu(onu_info)
+                if data.name in self.channel_terminations:
+                    self.log.info('Channel-termination-already-present',
+                                  channel_termination=data)
                 else:
-                    self.log.info('Invalid ONU state to activate',
-                                  onu_id=child_device.proxy_address.onu_id,
-                                  serial_number=serial_number)
+                    channel_termination_config = ChannelterminationConfig()
+                    channel_termination_config.CopyFrom(data)
+                    self.channel_terminations[data.name] = \
+                        channel_termination_config
+            if isinstance(data, VOntaniConfig):
+                if data.name in self.v_ont_anis:
+                    self.log.info('v_ont_ani-already-present',
+                                  v_ont_ani=data)
+                else:
+                    self.handle_v_ont_ani_config(data)
+                    v_ont_ani_config = VOntAniHandler()
+                    v_ont_ani_config.v_ont_ani.CopyFrom(data)
+                    self.v_ont_anis[data.name] = v_ont_ani_config
+            if isinstance(data, VEnetConfig):
+                if data.name in self.v_enets:
+                    self.log.info('v_enet-already-present',
+                                  v_enet=data)
+                else:
+                    v_enet_config = VEnetHandler()
+                    v_enet_config.v_enet.CopyFrom(data)
+                    self.log.info("creating-port-at-olt")
+                    self.adapter_agent.add_port(self.device_id, Port(
+                        port_no=self._get_next_uni_port(),
+                        label=data.interface.name,
+                        type=Port.ETHERNET_UNI,
+                        admin_state=AdminState.ENABLED,
+                        oper_status=OperStatus.ACTIVE
+                    ))
+                    self.v_enets[data.name] = v_enet_config
+            if isinstance(data, OntaniConfig):
+                if data.name in self.ont_anis:
+                    self.log.info('ont_ani-already-present',
+                                  v_enet=data)
+                else:
+                    ont_ani_config = OntaniConfig()
+                    ont_ani_config.CopyFrom(data)
+                    self.ont_anis[data.name] = ont_ani_config
         except Exception as e:
             self.log.exception('', exc=str(e))
         return
 
     def update_interface(self, data):
-        self.log.info('Not Implemented yet')
+        self.log.info('Not-Implemented-yet')
         return
 
     def remove_interface(self, data):
-        self.log.info('Not Implemented yet')
+        self.log.info('Not-Implemented-yet')
         return
 
+    def create_tcont(self, tcont_data, traffic_descriptor_data):
+        if traffic_descriptor_data.name in self.traffic_descriptors:
+            traffic_descriptor = TrafficDescriptorProfileData()
+            traffic_descriptor.CopyFrom(traffic_descriptor_data)
+            self.traffic_descriptors[traffic_descriptor_data.name] = \
+                traffic_descriptor
+        if tcont_data.interface_reference in self.v_ont_anis:
+            v_ont_ani = self.v_ont_anis[tcont_data.interface_reference]
+            onu_device = self.adapter_agent.get_child_device(
+                self.device_id,
+                onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+            if (onu_device is not None and
+                        onu_device.oper_status == OperStatus.ACTIVE):
+                owner_info = dict()
+                # To-Do: Right Now use alloc_id as schduler ID. Need to
+                # find way to generate uninqe number.
+                id = tcont_data.alloc_id
+                owner_info['type'] = 'agg_port'
+                owner_info['intf_id'] = onu_device.proxy_address.channel_id
+                owner_info['onu_id'] = onu_device.proxy_address.onu_id
+                owner_info['alloc_id'] = tcont_data.alloc_id
+                self.bal.create_scheduler(id, 'upstream', owner_info, 8)
+            else:
+                self.log.info('Onu-is-not-configured', olt_id=self.olt_id,
+                              intf_id=onu_device.proxy_address.channel_id,
+                              onu_data=onu_device.proxy_address.onu_id)
+            if tcont_data.name in v_ont_ani.tconts:
+                self.log.info('tcont-info-already-present',
+                              tcont_info=tcont_data)
+            else:
+                tcont = TcontsConfigData()
+                tcont.CopyFrom(tcont_data)
+                v_ont_ani.tconts[tcont_data.name] = tcont
+
+    def update_tcont(self, tcont_data, traffic_descriptor_data):
+        raise NotImplementedError()
+
+    def remove_tcont(self, tcont_data, traffic_descriptor_data):
+        raise NotImplementedError()
+
+    def create_gemport(self, data):
+        if data.itf_ref in self.v_enets:
+            v_enet = self.v_enets[data.itf_ref]
+            if data.name in v_enet.gem_ports:
+                self.log.info('Gem-port-info-is-already-present',
+                              VEnet=v_enet, gem_info=data)
+            if data.gemport_id > 9212:
+                raise Exception('supported range for '
+                                'gem-port is from 1024 to 4099')
+            gem_port = GemportsConfigData()
+            gem_port.CopyFrom(data)
+            v_enet.gem_ports[data.name] = gem_port
+        else:
+            self.log.info('VEnet-is-not-configured-yet.',
+                          gem_port_info=data)
+
+    def update_gemport(self, data):
+        raise NotImplementedError()
+
+    def remove_gemport(self, data):
+        raise NotImplementedError()
+
     def disable(self):
         super(Asfvolt16Handler, self).disable()
 
@@ -375,6 +630,746 @@
             Dot1Q(vlan=egress_port, type=pkt.type) /
             pkt.payload
         )
-
+        onu_id = 1
         # TODO: Need to retrieve the correct destination onu_id
-        self.bal.packet_out(1, egress_port, str(out_pkt))
+        self.bal.packet_out(onu_id, egress_port, str(out_pkt))
+
+    def update_flow_table(self, flows):
+        device = self.adapter_agent.get_device(self.device_id)
+        self.log.info('bulk-flow-update', device_id=self.device_id)
+
+        for flow in flows:
+            self.log.info('flow-details', device_id=self.device_id, flow=flow)
+            classifier_info = dict()
+            action_info = dict()
+            is_down_stream = None
+            _in_port = None
+            try:
+                _in_port = fd.get_in_port(flow)
+                assert _in_port is not None
+                # Right now there is only one NNI port. Get the NNI PORT and compare
+                # with IN_PUT port number. Need to find better way.
+                ports = self.adapter_agent.get_ports(device.id, Port.ETHERNET_NNI)
+
+                for port in ports:
+                    if (port.port_no == _in_port):
+                        self.log.info('downstream-flow')
+                        is_down_stream = True
+                        break
+                if is_down_stream is None:
+                    is_down_stream = False
+                    self.log.info('upstream-flow')
+
+                _out_port = fd.get_out_port(flow)  # may be None
+                self.log.info('out-port', out_port=_out_port)
+
+                for field in fd.get_ofb_fields(flow):
+
+                    if field.type == fd.ETH_TYPE:
+                        classifier_info['eth_type'] = field.eth_type
+                        self.log.info('field-type-eth-type',
+                                      eth_type=classifier_info['eth_type'])
+
+                    elif field.type == fd.IP_PROTO:
+                        classifier_info['ip_proto'] = field.ip_proto
+                        self.log.info('field-type-ip-proto',
+                                      ip_proto=classifier_info['ip_proto'])
+
+                    elif field.type == fd.IN_PORT:
+                        classifier_info['in_port'] = field.port
+                        self.log.info('field-type-in-port',
+                                      in_port=classifier_info['in_port'])
+
+                    elif field.type == fd.VLAN_VID:
+                        classifier_info['vlan_vid'] = field.vlan_vid & 0xfff
+                        self.log.info('field-type-vlan-vid',
+                                      vlan=classifier_info['vlan_vid'])
+
+                    elif field.type == fd.VLAN_PCP:
+                        classifier_info['vlan_pcp'] = field.vlan_pcp
+                        self.log.info('field-type-vlan-pcp',
+                                      pcp=classifier_info['vlan_pcp'])
+
+                    elif field.type == fd.UDP_DST:
+                        classifier_info['udp_dst'] = field.udp_dst
+                        self.log.info('field-type-udp-dst',
+                                      udp_dst=classifier_info['udp_dst'])
+
+                    elif field.type == fd.UDP_SRC:
+                        classifier_info['udp_src'] = field.udp_src
+                        self.log.info('field-type-udp-src',
+                                      udp_src=classifier_info['udp_src'])
+
+                    elif field.type == fd.IPV4_DST:
+                        classifier_info['ipv4_dst'] = field.ipv4_dst
+                        self.log.info('field-type-ipv4-dst',
+                                      ipv4_dst=classifier_info['ipv4_dst'])
+
+                    elif field.type == fd.IPV4_SRC:
+                        classifier_info['ipv4_src'] = field.ipv4_src
+                        self.log.info('field-type-ipv4-src',
+                                      ipv4_dst=classifier_info['ipv4_src'])
+
+                    elif field.type == fd.METADATA:
+                        classifier_info['metadata'] = field.table_metadata
+                        self.log.info('field-type-metadata',
+                                      metadata=classifier_info['metadata'])
+
+                    else:
+                        raise NotImplementedError('field.type={}'.format(
+                            field.type))
+
+                for action in fd.get_actions(flow):
+
+                    if action.type == fd.OUTPUT:
+                        action_info['output'] = action.output.port
+                        self.log.info('action-type-output',
+                                      output=action_info['output'],
+                                      in_port=classifier_info['in_port'])
+
+                    elif action.type == fd.POP_VLAN:
+                        action_info['pop_vlan'] = True
+                        self.log.info('action-type-pop-vlan',
+                                      in_port=_in_port)
+
+                    elif action.type == fd.PUSH_VLAN:
+                        action_info['push_vlan'] = True
+                        action_info['tpid'] = action.push.ethertype
+                        self.log.info('action-type-push-vlan',
+                                      push_tpid=action_info['tpid'],
+                                      in_port=_in_port)
+                        if action.push.ethertype != 0x8100:
+                            self.log.error('unhandled-tpid',
+                                           ethertype=action.push.ethertype)
+
+                    elif action.type == fd.SET_FIELD:
+                        # action_info['action_type'] = 'set_field'
+                        _field = action.set_field.field.ofb_field
+                        assert (action.set_field.field.oxm_class ==
+                                OFPXMC_OPENFLOW_BASIC)
+                        self.log.info('action-type-set-field',
+                                      field=_field, in_port=_in_port)
+                        if _field.type == fd.VLAN_VID:
+                            self.log.info('set-field-type-vlan-vid',
+                                          vlan_vid=_field.vlan_vid & 0xfff)
+                            action_info['vlan_vid'] = (_field.vlan_vid & 0xfff)
+                        else:
+                            self.log.error('unsupported-action-set-field-type',
+                                           field_type=_field.type)
+                    else:
+                        self.log.error('unsupported-action-type',
+                                       action_type=action.type, in_port=_in_port)
+
+                if is_down_stream is False:
+                    self.divide_and_add_flow(classifier_info, action_info)
+            except Exception as e:
+                self.log.exception('failed-to-install-flow', e=e, flow=flow)
+
+    # This function will divide the upstream flow into both
+    # upstreand and downstream flow, as broadcom devices
+    # expects down stream flows to be added to handle
+    # packet_out messge from controller.
+    def divide_and_add_flow(self, classifier, action):
+        found = False
+        ports = self.adapter_agent.get_ports(self.device_id, Port.ETHERNET_UNI)
+        for port in ports:
+            if port.port_no == classifier['in_port']:
+                found = True
+                break
+        if found is True:
+            v_enet = self.get_venet(port.label)
+        else:
+            self.log.error('Failed to get v_enet info',
+                           in_port=classifier['in_port'])
+            return
+        if 'ip_proto' in classifier:
+            if classifier['ip_proto'] == 17:
+                self.log.error('Addtion of DHCP flows are defferd')
+                '''
+                # DHCP flow from the ONOS doesn't have Ctag and Stags
+                # information. For now DHCP flow will be added as a
+                # part of data flows.
+                # self.add_dhcp_flow(classifier, action, v_enet,
+                #                   ASFVOLT_DHCP_UNTAGGED_ID)
+                '''
+            elif classifier['ip_proto'] == 2:
+                self.log.info('Addtion of IGMP flow are not handled yet')
+                '''
+                #self.add_igmp_flow(classifier, action, v_enet,
+                #                   ASFVOLT_IGMP_UNTAGGED_ID)
+                '''
+            else:
+                self.log.info("Invalid-Classifier-to-handle",
+                              classifier=classifier,
+                              action=action)
+        elif 'eth_type' in classifier:
+            if classifier['eth_type'] == 0x888e:
+                # self.log.error('Addtion of EAPOL flows are defferd')
+                self.add_eapol_flow(classifier, action,
+                                    v_enet, ASFVOLT_EAPOL_ID,
+                                    ASFVOLT_DOWNLINK_EAPOL_ID)
+        elif 'push_vlan' in action:
+            self.prepare_and_add_dhcp_flow(classifier, action, v_enet,
+                                           ASFVOLT_DHCP_TAGGED_ID,
+                                           ASFVOLT_DOWNLINK_DHCP_TAGGED_ID)
+            self.add_data_flow(classifier, action, v_enet)
+        else:
+            self.log.info('Invalid-flow-type-to-handle',
+                          classifier=classifier,
+                          action=action)
+
+    def add_eapol_flow(self, uplink_classifier, uplink_action,
+                       v_enet, uplink_eapol_id, downlink_eapol_id):
+        downlink_classifier = dict(uplink_classifier)
+        downlink_action = dict(uplink_action)
+        # To-Do For a time being hard code the traffic class value.
+        # Need to know how to get the traffic class info from flows.
+        gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
+        if gem_port is None:
+            self.log.info('Failed-to-get-gemport',)
+            return
+        v_ont_ani = self.get_v_ont_ani(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
+        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
+        onu_device = self.adapter_agent.get_child_device(
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+        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
+
+        flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                   onu_device.proxy_address.channel_id,
+                                   uplink_eapol_id)
+        # Add Upstream EAPOL Flow.
+        uplink_classifier['pkt_tag_type'] = 'untagged'
+        uplink_action.clear()
+        uplink_action['trap_to_host'] = True
+        try:
+            is_down_stream = False
+            self.log.info('Adding-Upstream-EAPOL-flow',
+                          classifier=uplink_classifier,
+                          action=uplink_action, gem_port=gem_port,
+                          flow_id=flow_id,
+                          sched_info=tcont.alloc_id)
+            self.bal.add_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              flow_id, gem_port.gemport_id,
+                              uplink_classifier, is_down_stream,
+                              action_info=uplink_action,
+                              sched_id=tcont.alloc_id)
+            # To-Do. While addition of one flow is in progress,
+            # we cannot add an another flow. Right now use sleep
+            # of 5 sec, assuming that addtion of flow is successful.
+            time.sleep(10)
+        except Exception as e:
+            self.log.exception('failed-to-install-Upstream-EAPOL-flow', e=e,
+                               classifier=uplink_classifier,
+                               action=uplink_action,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
+
+        # Add Downstream EAPOL Flow.
+        downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                            onu_device.proxy_address.channel_id,
+                                            downlink_eapol_id)
+        is_down_stream = True
+        downlink_classifier['pkt_tag_type'] = 'untagged'
+        try:
+            self.log.info('Adding-Downstream-EAPOL-flow',
+                          classifier=downlink_classifier,
+                          action=downlink_action,
+                          gem_port=gem_port,
+                          flow_id=downlink_flow_id,
+                          sched_info=tcont.alloc_id)
+            self.bal.add_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              downlink_flow_id, gem_port.gemport_id,
+                              downlink_classifier, is_down_stream)
+            # To-Do. While addition of one flow is in progress,
+            # we cannot add an another flow. Right now use sleep
+            # of 5 sec, assuming that addtion of flow is successful.
+            time.sleep(10)
+        except Exception as e:
+            self.log.exception('failed-to-install-downstream-EAPOL-flow', e=e,
+                               classifier=downlink_classifier,
+                               action=downlink_action,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
+
+    def prepare_and_add_dhcp_flow(self, data_classifier, data_action,
+                                  v_enet, dhcp_id, downlink_dhcp_id):
+        dhcp_classifier = dict()
+        dhcp_action = dict()
+        dhcp_classifier['ip_proto'] = 17
+        dhcp_classifier['udp_src'] = 68
+        dhcp_classifier['udp_dsct'] = 67
+        dhcp_classifier['pkt_tag_type'] = 'single_tag'
+        dhcp_classifier['vlan_vid'] = data_classifier['vlan_vid']
+        dhcp_action['vlan_push'] = True
+        dhcp_action['vlan_vid'] = data_action['vlan_vid']
+        self.add_dhcp_flow(dhcp_classifier, dhcp_action, v_enet,
+                           dhcp_id, downlink_dhcp_id)
+
+    def add_dhcp_flow(self, uplink_classifier, uplink_action,
+                      v_enet, dhcp_id, downlink_dhcp_id):
+        downlink_classifier = dict(uplink_classifier)
+        downlink_action = dict(uplink_action)
+        # Add Upstream DHCP Flow.
+        # To-Do For a time being hard code the traffic class value.
+        # Need to know how to get the traffic class info from flows.
+        gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
+        if gem_port is None:
+            self.log.error('Failed-to-get-gemport')
+            return
+        v_ont_ani = self.get_v_ont_ani(v_enet.v_enet.data.v_ontani_ref)
+        if v_ont_ani is None:
+            self.log.error('Failed-to-get-v_ont_ani',
+                           v_ont_ani=v_enet.v_enet.data.v_ontani_ref)
+            return
+        tcont = self.get_tcont_info(v_ont_ani, name=gem_port.tcont_ref)
+        if tcont is None:
+            self.log.error('Failed-to-get-tcont-info',
+                           tcont=gem_port.tcont_ref)
+            return
+        onu_device = self.adapter_agent.get_child_device(
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+        if onu_device is None:
+            self.log.error('Failed-to-get-onu-device',
+                           onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+            return
+
+        flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                   onu_device.proxy_address.channel_id,
+                                   dhcp_id)
+        uplink_action.clear()
+        uplink_action['trap_to_host'] = True
+        try:
+            is_down_stream = False
+            self.log.info('Adding-Upstream-DHCP-flow',
+                          classifier=uplink_classifier,
+                          action=uplink_action,
+                          gem_port=gem_port,
+                          flow_id=flow_id,
+                          sched_info=tcont.alloc_id)
+            self.bal.add_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              flow_id, gem_port.gemport_id,
+                              uplink_classifier, is_down_stream,
+                              action_info=uplink_action,
+                              sched_id=tcont.alloc_id)
+            # To-Do. While addition of one flow is in progress,
+            # we cannot add an another flow. Right now use sleep
+            # of 5 sec, assuming that addtion of flow is successful.
+            time.sleep(10)
+
+        except Exception as e:
+            self.log.exception('failed-to-install-dhcp-upstream-flow', e=e,
+                               classifier=uplink_classifier,
+                               action=uplink_action,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
+
+        is_down_stream = True
+        downlink_classifier['udp_src'] = 67
+        downlink_classifier['udp_dst'] = 68
+
+        if dhcp_id == ASFVOLT_DHCP_TAGGED_ID:
+            downlink_classifier['pkt_tag_type'] = 'double_tag'
+            downlink_classifier['vlan_vid'] = downlink_action['vlan_vid']
+            if 'push_vlan' in downlink_classifier:
+                downlink_action.pop('push_vlan')
+            downlink_action['pop_vlan'] = True
+
+        downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                            onu_device.proxy_address.channel_id,
+                                            downlink_dhcp_id)
+
+        try:
+            self.log.info('Adding-Downstream-DHCP-flow',
+                          classifier=downlink_classifier,
+                          action=downlink_action, gem_port=gem_port,
+                          flow_id=downlink_flow_id,
+                          sched_info=tcont.alloc_id)
+            self.bal.add_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              downlink_flow_id, gem_port.gemport_id,
+                              downlink_classifier, is_down_stream,
+                              action_info=downlink_action)
+            # To-Do. While addition of one flow is in progress,
+            # we cannot add an another flow. Right now use sleep
+            # of 5 sec, assuming that addtion of flow is successful.
+            time.sleep(10)
+        except Exception as e:
+            self.log.exception('failed-to-install-dhcp-downstream-flow', e=e,
+                               classifier=downlink_classifier,
+                               action=downlink_action,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
+
+    def add_igmp_flow(self, classifier, action, v_enet, igmp_id):
+        self.log.info('Not-Implemented-Yet')
+        return
+
+    def add_data_flow(self, uplink_classifier, uplink_action, v_enet):
+
+        downlink_classifier = dict(uplink_classifier)
+        downlink_action = dict(uplink_action)
+
+        uplink_classifier['pkt_tag_type'] = 'single_tag'
+
+        downlink_classifier['pkt_tag_type'] = 'double_tag'
+        downlink_classifier['vlan_vid'] = uplink_action['vlan_vid']
+        downlink_classifier['vlan_pcp'] = 0
+        del downlink_action['push_vlan']
+        downlink_action['pop_vlan'] = True
+
+        self.add_firmware_flow(uplink_classifier, uplink_action,
+                               downlink_classifier, downlink_action,
+                               v_enet, ASFVOLT_FIRMWARE_ID,
+                               ASFVOLT_DOWNLINK_FIRMWARE_ID)
+        self.add_arp_flow(uplink_classifier, uplink_action,
+                          downlink_classifier, downlink_action,
+                          v_enet, ASFVOLT_ARP_ID, ASFVOLT_DOWNLINK_ARP_ID)
+        self.add_dns_flow(uplink_classifier, uplink_action,
+                          downlink_classifier, downlink_action,
+                          v_enet, ASFVOLT_DNS_ID, ASFVOLT_DOWNLINK_DNS_ID)
+        self.add_hsia_flow(uplink_classifier, uplink_action,
+                           downlink_classifier, downlink_action,
+                           v_enet, ASFVOLT_HSIA_ID, ASFVOLT_DOWNLINK_HSIA_ID)
+
+    def add_firmware_flow(self, uplink_classifier, uplink_action,
+                          downlink_classifier, downlink_action,
+                          v_enet, firmware_id, downlink_firmware_id):
+        # Add Upstream Firmware Flow.
+        # To-Do For a time being hard code the traffic class value.
+        # Need to know how to get the traffic class info from flows.
+        gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
+        if gem_port is None:
+            self.log.info('Failed-to-get-gemport')
+            return
+        v_ont_ani = self.get_v_ont_ani(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
+        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
+        onu_device = self.adapter_agent.get_child_device(
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+        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
+
+        flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                   onu_device.proxy_address.channel_id,
+                                   firmware_id)
+        self.log.info('Adding-FirmWare-data-upstream-flow')
+        try:
+            is_down_stream = False
+            self.log.info('Adding-Firmware-data-upstream-flow',
+                          classifier=uplink_classifier,
+                          action=uplink_action,
+                          gem_port=gem_port,
+                          flow_id=flow_id,
+                          sched_info=tcont.alloc_id)
+            self.bal.add_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              flow_id, gem_port.gemport_id,
+                              uplink_classifier, is_down_stream,
+                              action_info=uplink_action,
+                              sched_id=tcont.alloc_id)
+            # To-Do. While addition of one flow is in progress,
+            # we cannot add an another flow. Right now use sleep
+            # of 5 sec, assuming that addtion of flow is successful.
+            time.sleep(10)
+        except Exception as e:
+            self.log.exception('failed-to-install-firmware-upstream-flow', e=e,
+                               classifier=uplink_classifier,
+                               action=uplink_action,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
+        is_down_stream = True
+        # To-Do: For Now hard code the p-bit values.
+        downlink_classifier['vlan_pcp'] = 1
+        downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                            onu_device.proxy_address.channel_id,
+                                            downlink_firmware_id)
+        try:
+            self.log.info('Adding-Firmware-data-downstream-flow',
+                          classifier=downlink_classifier,
+                          action=downlink_action,
+                          gem_port=gem_port,
+                          flow_id=downlink_flow_id)
+            self.bal.add_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              downlink_flow_id, gem_port.gemport_id,
+                              downlink_classifier, is_down_stream,
+                              action_info=downlink_action)
+            # To-Do. While addition of one flow is in progress,
+            # we cannot add an another flow. Right now use sleep
+            # of 5 sec, assuming that addtion of flow is successful.
+            time.sleep(10)
+        except Exception as e:
+            self.log.exception('failed-to-install-firmware-downstream-flow', e=e,
+                               classifier=downlink_classifier,
+                               action=downlink_action,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
+
+    def add_dns_flow(self, uplink_classifier, uplink_action,
+                     downlink_classifier, downlink_action,
+                     v_enet, dns_id, downlink_dns_id):
+        # Add Upstream Firmware Flow.
+        # To-Do For a time being hard code the traffic class value.
+        # Need to know how to get the traffic class info from flows.
+        gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
+        if gem_port is None:
+            self.log.info('Failed-to-get-gemport')
+            return
+        v_ont_ani = self.get_v_ont_ani(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
+        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
+        onu_device = self.adapter_agent.get_child_device(
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+        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
+
+        flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                   onu_device.proxy_address.channel_id,
+                                   dns_id)
+        try:
+            is_down_stream = False
+            self.log.info('Adding-DNS-upstream-flow',
+                          classifier=uplink_classifier,
+                          action=uplink_action,
+                          gem_port=gem_port,
+                          flow_id=flow_id,
+                          sched_info=tcont.alloc_id)
+            self.bal.add_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              flow_id, gem_port.gemport_id,
+                              uplink_classifier, is_down_stream,
+                              action_info=uplink_action,
+                              sched_id=tcont.alloc_id)
+            # To-Do. While addition of one flow is in progress,
+            # we cannot add an another flow. Right now use sleep
+            # of 5 sec, assuming that addtion of flow is successful.
+            time.sleep(10)
+        except Exception as e:
+            self.log.exception('failed-to-install-DNS-upstream-flow', e=e,
+                               classifier=uplink_classifier,
+                               action=uplink_action,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
+        is_down_stream = True
+        # To-Do: For Now hard code the p-bit values.
+        downlink_classifier['vlan_pcp'] = 3
+        downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                            onu_device.proxy_address.channel_id,
+                                            downlink_dns_id)
+        try:
+            self.log.info('Adding-DNS-downstream-flow',
+                          classifier=downlink_classifier,
+                          action=downlink_action,
+                          gem_port=gem_port,
+                          flow_id=downlink_flow_id)
+            self.bal.add_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              downlink_flow_id, gem_port.gemport_id,
+                              downlink_classifier, is_down_stream,
+                              action_info=downlink_action)
+            # To-Do. While addition of one flow is in progress,
+            # we cannot add an another flow. Right now use sleep
+            # of 5 sec, assuming that addtion of flow is successful.
+            time.sleep(10)
+        except Exception as e:
+            self.log.exception('failed-to-install-DNS-downstream-flow', e=e,
+                               classifier=downlink_classifier,
+                               action=downlink_action,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
+
+    def add_arp_flow(self, uplink_classifier, uplink_action,
+                     downlink_classifier, downlink_action,
+                     v_enet, arp_id, downlink_arp_id):
+        # Add Upstream Firmware Flow.
+        # To-Do For a time being hard code the traffic class value.
+        # Need to know how to get the traffic class info from flows.
+        gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
+        if gem_port is None:
+            self.log.info('Failed-to-get-gemport')
+            return
+        v_ont_ani = self.get_v_ont_ani(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
+        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
+        onu_device = self.adapter_agent.get_child_device(
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+        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
+
+        flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                   onu_device.proxy_address.channel_id,
+                                   arp_id)
+        try:
+            is_down_stream = False
+            self.log.info('Adding-ARP-upstream-flow',
+                          classifier=uplink_classifier,
+                          action=uplink_action,
+                          gem_port=gem_port,
+                          flow_id=flow_id,
+                          sched_info=tcont.alloc_id)
+            self.bal.add_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              flow_id, gem_port.gemport_id,
+                              uplink_classifier, is_down_stream,
+                              action_info=uplink_action,
+                              sched_id=tcont.alloc_id)
+            # To-Do. While addition of one flow is in progress,
+            # we cannot add an another flow. Right now use sleep
+            # of 5 sec, assuming that addtion of flow is successful.
+            time.sleep(10)
+        except Exception as e:
+            self.log.exception('failed-to-install-ARP-upstream-flow', e=e,
+                               classifier=uplink_classifier,
+                               action=uplink_action,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
+        is_down_stream = True
+        # To-Do: For Now hard code the p-bit values.
+        downlink_classifier['vlan_pcp'] = 7
+        downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                            onu_device.proxy_address.channel_id,
+                                            downlink_arp_id)
+        try:
+            self.log.info('Adding-ARP-downstream-flow',
+                          classifier=downlink_classifier,
+                          action=downlink_action,
+                          gem_port=gem_port,
+                          flow_id=downlink_flow_id)
+            self.bal.add_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              downlink_flow_id, gem_port.gemport_id,
+                              downlink_classifier, is_down_stream,
+                              action_info=downlink_action)
+            # To-Do. While addition of one flow is in progress,
+            # we cannot add an another flow. Right now use sleep
+            # of 5 sec, assuming that addtion of flow is successful.
+            time.sleep(10)
+        except Exception as e:
+            self.log.exception('failed-to-install-ARP-downstream-flow', e=e,
+                               classifier=downlink_classifier,
+                               action=downlink_action,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
+
+    def add_hsia_flow(self, uplink_classifier, uplink_action,
+                      downlink_classifier, downlink_action,
+                      v_enet, hsia_id, downlink_hsia_id):
+        # Add Upstream Firmware Flow.
+        # To-Do For a time being hard code the traffic class value.
+        # Need to know how to get the traffic class info from flows.
+        gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
+        if gem_port is None:
+            self.log.info('Failed-to-get-gemport')
+            return
+        v_ont_ani = self.get_v_ont_ani(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
+        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
+        onu_device = self.adapter_agent.get_child_device(
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+        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
+
+        flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                   onu_device.proxy_address.channel_id,
+                                   hsia_id)
+        try:
+            is_down_stream = False
+            self.log.info('Adding-HSIA-upstream-flow',
+                          classifier=uplink_classifier,
+                          action=uplink_action,
+                          gem_port=gem_port,
+                          flow_id=flow_id,
+                          sched_info=tcont.alloc_id)
+            self.bal.add_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              flow_id, gem_port.gemport_id,
+                              uplink_classifier, is_down_stream,
+                              action_info=uplink_action,
+                              sched_id=tcont.alloc_id)
+            # To-Do. While addition of one flow is in progress,
+            # we cannot add an another flow. Right now use sleep
+            # of 5 sec, assuming that addtion of flow is successful.
+            time.sleep(10)
+        except Exception as e:
+            self.log.exception('failed-to-install-HSIA-upstream-flow', e=e,
+                               classifier=uplink_classifier,
+                               action=uplink_action,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
+        is_down_stream = True
+        # To-Do: For Now hard code the p-bit values.
+        downlink_classifier['vlan_pcp'] = 0
+        downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                            onu_device.proxy_address.channel_id,
+                                            downlink_hsia_id)
+        try:
+            self.log.info('Adding-HSIA-downstream-flow',
+                          classifier=downlink_classifier,
+                          action=downlink_action,
+                          gem_port=gem_port,
+                          flow_id=downlink_flow_id)
+            self.bal.add_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              downlink_flow_id, gem_port.gemport_id,
+                              downlink_classifier, is_down_stream,
+                              action_info=downlink_action)
+            # To-Do. While addition of one flow is in progress,
+            # we cannot add an another flow. Right now use sleep
+            # of 5 sec, assuming that addtion of flow is successful.
+            time.sleep(10)
+        except Exception as e:
+            self.log.exception('failed-to-install-HSIA-downstream-flow', e=e,
+                               classifier=downlink_classifier,
+                               action=downlink_action,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_olt.py b/voltha/adapters/asfvolt16_olt/asfvolt16_olt.py
index 1aeaf7f..437a587 100644
--- a/voltha/adapters/asfvolt16_olt/asfvolt16_olt.py
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_olt.py
@@ -43,3 +43,45 @@
 
     def stop(self):
         self.rx_handler.stop()
+
+    def create_tcont(self, device, tcont_data, traffic_descriptor_data):
+        log.info('create-tcont', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.create_tcont(tcont_data, traffic_descriptor_data)
+
+    def update_tcont(self, device, tcont_data, traffic_descriptor_data):
+        log.info('update-tcont', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.update_tcont(tcont_data, traffic_descriptor_data)
+
+    def remove_tcont(self, device, tcont_data, traffic_descriptor_data):
+        log.info('remove-tcont', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.remove_tcont(tcont_data, traffic_descriptor_data)
+
+    def create_gemport(self, device, data):
+        log.info('create-gemport', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.create_gemport(data)
+
+    def update_gemport(self, device, data):
+        log.info('update-gemport', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.update_gemport(data)
+
+    def remove_gemport(self, device, data):
+        log.info('remove-gemport', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.remove_gemport(data)
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py b/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py
index 8017e2b..5f60e9b 100644
--- a/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py
@@ -26,6 +26,7 @@
 
 
 class Asfvolt16RxHandler(object):
+
     def __init__(self, adapter, port, log):
         self.adapter = adapter
         self.adapter_agent = adapter.adapter_agent
diff --git a/voltha/adapters/asfvolt16_olt/bal.py b/voltha/adapters/asfvolt16_olt/bal.py
index 878202f..b246db1 100644
--- a/voltha/adapters/asfvolt16_olt/bal.py
+++ b/voltha/adapters/asfvolt16_olt/bal.py
@@ -18,8 +18,7 @@
 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
-from common.utils.nethelpers import get_my_primary_interface, \
-    get_my_primary_local_ipv4
+from common.utils.nethelpers import get_my_primary_local_ipv4
 import os
 
 """
@@ -27,6 +26,7 @@
 """
 ADAPTER_PORT = 60001
 
+
 class Bal(object):
     def __init__(self, olt, log):
         self.log = log
@@ -37,7 +37,6 @@
 
     @inlineCallbacks
     def connect_olt(self, host_and_port, device_id):
-        self.log.info('connecting-olt', host_and_port=host_and_port)
         self.device_id = device_id
         self.grpc_client.connect(host_and_port)
         self.stub = bal_pb2.BalStub(self.grpc_client.channel)
@@ -60,6 +59,8 @@
         TODO: Need to determine out what information
         needs to be sent to the OLT at this stage.
         '''
+        self.log.info('connecting-olt', host_and_port=host_and_port,
+                      init_details=init)
         yield self.stub.BalApiInit(init)
 
     def activate_olt(self):
@@ -68,19 +69,18 @@
 
     @inlineCallbacks
     def set_access_terminal_admin_state(self, admin_state):
-        self.log.info('setting-admin-state',
-                      admin_state=admin_state, device_id=self.device_id)
         obj = bal_pb2.BalCfg()
         obj.device_id = self.device_id.encode('ascii', 'ignore')
         obj.hdr.obj_type = bal_model_ids_pb2.BAL_OBJ_ID_ACCESS_TERMINAL
         obj.cfg.key.access_term_id = 0
         obj.cfg.data.admin_state = admin_state
+        self.log.info('Activating-Access-Terminal-Device',
+                      admin_state=admin_state, device_id=self.device_id,
+                      access_terminal_details=obj)
         yield self.stub.BalCfgSet(obj)
 
     @inlineCallbacks
     def activate_pon_port(self, olt_no, pon_port):
-        self.log.info('activating-pon-port in olt',
-                      olt=olt_no, pon_port=pon_port)
         try:
             obj = bal_pb2.BalCfg()
             #            Fill Header details
@@ -92,6 +92,9 @@
             obj.interface.data.admin_state = bal_model_types_pb2.BAL_STATE_UP
             obj.interface.data.transceiver_type = \
                 bal_model_types_pb2.BAL_TRX_TYPE_XGPON_LTH_7226_PC
+            self.log.info('activating-pon-port-in-olt',
+                          olt=olt_no, pon_port=pon_port,
+                          pon_port_details=obj)
             yield self.stub.BalCfgSet(obj)
         except Exception as e:
             self.log.info('activating-pon-port in olt-exception', exc=str(e))
@@ -99,9 +102,6 @@
 
     @inlineCallbacks
     def send_omci_request_message(self, proxy_address, msg):
-        self.log.info('send_omci_request_message',
-                      proxy_address=proxy_address.channel_id,
-                      msg=msg)
         try:
             obj = bal_pb2.BalCfg()
             #            Fill Header details
@@ -115,6 +115,9 @@
             obj.packet.key.packet_send_dest.itu_omci_channel.intf_id = \
                 proxy_address.channel_id
             obj.packet.data.pkt = msg
+            self.log.info('send_omci_request_message',
+                          proxy_address=proxy_address.channel_id,
+                          omci_msg_details=obj)
             yield self.stub.BalCfgSet(obj)
         except Exception as e:
             self.log.info('send-proxied_message-exception', exc=str(e))
@@ -122,8 +125,6 @@
 
     @inlineCallbacks
     def activate_onu(self, onu_info):
-        self.log.info('activating-ONU in olt',
-                      olt=self.olt.olt_id, onu_id=onu_info['onu_id'])
         try:
             obj = bal_pb2.BalCfg()
             # Fill Header details
@@ -138,6 +139,8 @@
                 onu_info['vendor_specific']
             obj.terminal.data.registration_id = \
                 '202020202020202020202020202020202020202020202020202020202020202020202020'
+            self.log.info('activating-ONU-in-olt',
+                          onu_details=obj)
             yield self.stub.BalCfgSet(obj)
         except Exception as e:
             self.log.info('activating-ONU-exception',
@@ -146,10 +149,8 @@
 
     @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
@@ -157,9 +158,183 @@
         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
+        obj.packet.data.flow_type = bal_model_types_pb2.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
-
+        self.log.info('packet-out',
+                      packet_out_details=obj)
         yield self.stub.BalCfgSet(obj)
+
+    @inlineCallbacks
+    def add_flow(self, onu_id, intf_id, flow_id, gem_port,
+                 classifier_info, is_downstream,
+                 action_info=None, sched_id=None):
+        try:
+            obj = bal_pb2.BalCfg()
+            # Fill Header details
+            obj.device_id = self.device_id.encode('ascii', 'ignore')
+            obj.hdr.obj_type = bal_model_ids_pb2.BAL_OBJ_ID_FLOW
+            # 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
+                obj.flow.data.dba_tm_sched_id = sched_id
+            else:
+                obj.flow.key.flow_type = \
+                    bal_model_types_pb2.BAL_FLOW_TYPE_DOWNSTREAM
+
+            obj.flow.data.admin_state = bal_model_types_pb2.BAL_STATE_UP
+            obj.flow.data.access_int_id = intf_id
+            #obj.flow.data.network_int_id = intf_id
+            obj.flow.data.sub_term_id = onu_id
+            obj.flow.data.svc_port_id = gem_port
+            obj.flow.data.classifier.presence_mask = 0
+            if 'eth_type' in classifier_info:
+                obj.flow.data.classifier.ether_type = \
+                    classifier_info['eth_type']
+                obj.flow.data.classifier.presence_mask |= \
+                    bal_model_types_pb2.BAL_CLASSIFIER_ID_ETHER_TYPE
+            if 'ip_proto' in classifier_info:
+                obj.flow.data.classifier.ip_proto = \
+                    classifier_info['ip_proto']
+                obj.flow.data.classifier.presence_mask |= \
+                    bal_model_types_pb2.BAL_CLASSIFIER_ID_IP_PROTO
+            if 'vlan_vid' in classifier_info:
+                obj.flow.data.classifier.o_vid = \
+                    classifier_info['vlan_vid']
+                obj.flow.data.classifier.presence_mask |= \
+                    bal_model_types_pb2.BAL_CLASSIFIER_ID_O_VID
+            if 'vlan_pcp' in classifier_info:
+                obj.flow.data.classifier.o_pbits = \
+                    classifier_info['vlan_pcp']
+                obj.flow.data.classifier.presence_mask |= \
+                    bal_model_types_pb2.BAL_CLASSIFIER_ID_O_PBITS
+            if 'udp_src' in classifier_info:
+                obj.flow.data.classifier.src_port = \
+                    classifier_info['udp_src']
+                obj.flow.data.classifier.presence_mask |= \
+                    bal_model_types_pb2.BAL_CLASSIFIER_ID_SRC_PORT
+            if 'udp_dst' in classifier_info:
+                obj.flow.data.classifier.dst_port = \
+                    classifier_info['udp_dst']
+                obj.flow.data.classifier.presence_mask |= \
+                    bal_model_types_pb2.BAL_CLASSIFIER_ID_DST_PORT
+            if 'ipv4_dst' in classifier_info:
+                obj.flow.data.classifier.dst_ip = \
+                    classifier_info['ipv4_dst']
+                obj.flow.data.classifier.presence_mask |= \
+                    bal_model_types_pb2.BAL_CLASSIFIER_ID_DST_IP
+            if 'ipv4_src' in classifier_info:
+                obj.flow.data.classifier.src_ip = \
+                    classifier_info['ipv4_src']
+                obj.flow.data.classifier.presence_mask |= \
+                    bal_model_types_pb2.BAL_CLASSIFIER_ID_SRC_IP
+            if 'metadata' in classifier_info:
+                obj.flow.data.classifier.i_vid = \
+                    classifier_info['metadata']
+                obj.flow.data.classifier.presence_mask |= \
+                    bal_model_types_pb2.BAL_CLASSIFIER_ID_I_VID
+            if 'pkt_tag_type' in classifier_info:
+                if classifier_info['pkt_tag_type'] == 'single_tag':
+                    obj.flow.data.classifier.pkt_tag_type = \
+                        bal_model_types_pb2.BAL_PKT_TAG_TYPE_SINGLE_TAG
+                elif classifier_info['pkt_tag_type'] == 'double_tag':
+                    obj.flow.data.classifier.pkt_tag_type = \
+                        bal_model_types_pb2.BAL_PKT_TAG_TYPE_DOUBLE_TAG
+                elif classifier_info['pkt_tag_type'] == 'untagged':
+                    obj.flow.data.classifier.pkt_tag_type = \
+                        bal_model_types_pb2.BAL_PKT_TAG_TYPE_UNTAGGED
+                else:
+                    obj.flow.data.classifier.pkt_tag_type = \
+                        bal_model_types_pb2.BAL_PKT_TAG_TYPE_NONE
+                obj.flow.data.classifier.presence_mask |= \
+                    bal_model_types_pb2.BAL_CLASSIFIER_ID_PKT_TAG_TYPE
+
+            if action_info is not None:
+                obj.flow.data.action.presence_mask = 0
+                obj.flow.data.action.cmds_bitmask = 0
+                if 'pop_vlan' in action_info:
+                    obj.flow.data.action.o_vid = action_info['vlan_vid']
+                    obj.flow.data.action.cmds_bitmask |= \
+                        bal_model_types_pb2.BAL_ACTION_CMD_ID_REMOVE_OUTER_TAG
+                    obj.flow.data.action.presence_mask |= \
+                        bal_model_types_pb2.BAL_ACTION_ID_CMDS_BITMASK
+                    obj.flow.data.action.presence_mask |= \
+                        bal_model_types_pb2.BAL_ACTION_ID_O_VID
+                elif 'push_vlan' in action_info:
+                    obj.flow.data.action.o_vid = action_info['vlan_vid']
+                    obj.flow.data.action.cmds_bitmask |= \
+                        bal_model_types_pb2.BAL_ACTION_CMD_ID_ADD_OUTER_TAG
+                    obj.flow.data.action.presence_mask |= \
+                        bal_model_types_pb2.BAL_ACTION_ID_CMDS_BITMASK
+                    obj.flow.data.action.presence_mask |= \
+                        bal_model_types_pb2.BAL_ACTION_ID_O_VID
+                elif 'trap_to_host' in action_info:
+                    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
+                else:
+                    self.log.info('Invalid-action-field')
+                    return
+            self.log.info('adding-flow-to-OLT-Device',
+                          flow_details=obj)
+            yield self.stub.BalCfgSet(obj)
+        except Exception as e:
+            self.log.info('add_flow-exception',
+                          flow_id, onu_id, exc=str(e))
+        return
+
+    @inlineCallbacks
+    def create_scheduler(self, id, direction, owner_info, num_priority):
+        try:
+            obj = bal_pb2.BalCfg()
+            # Fill Header details
+            obj.device_id = self.device_id.encode('ascii', 'ignore')
+            obj.hdr.obj_type = bal_model_ids_pb2.BAL_OBJ_ID_TM_SCHED
+            # Fill Access Terminal Details
+            if direction == 'downstream':
+                obj.tm_sched_cfg.key.dir =\
+                    bal_model_types_pb2.BAL_TM_SCHED_DIR_DS
+            else:
+                obj.tm_sched_cfg.key.dir = \
+                    bal_model_types_pb2.BAL_TM_SCHED_DIR_US
+            obj.tm_sched_cfg.key.id = id
+
+            if owner_info['type'] == 'agg_port':
+                obj.tm_sched_cfg.data.owner.type = \
+                    bal_model_types_pb2.BAL_TM_SCHED_OWNER_TYPE_AGG_PORT
+                obj.tm_sched_cfg.data.owner.agg_port.presence_mask = 0
+                obj.tm_sched_cfg.data.owner.agg_port.intf_id =\
+                    owner_info['intf_id']
+                obj.tm_sched_cfg.data.owner.agg_port.presence_mask |= \
+                    bal_model_types_pb2.BAL_TM_SCHED_OWNER_AGG_PORT_ID_INTF_ID
+                obj.tm_sched_cfg.data.owner.agg_port.sub_term_id = \
+                    owner_info['onu_id']
+                obj.tm_sched_cfg.data.owner.agg_port.presence_mask |= \
+                    bal_model_types_pb2.BAL_TM_SCHED_OWNER_AGG_PORT_ID_SUB_TERM_ID
+                obj.tm_sched_cfg.data.owner.agg_port.agg_port_id = \
+                    owner_info['alloc_id']
+                obj.tm_sched_cfg.data.owner.agg_port.presence_mask |= \
+                    bal_model_types_pb2.BAL_TM_SCHED_OWNER_AGG_PORT_ID_AGG_PORT_ID
+            else:
+                self.log.error('Not supported scheduling type',
+                               sched_type=owner_info['type'])
+                return
+            obj.tm_sched_cfg.data.sched_type = \
+                bal_model_types_pb2.BAL_TM_SCHED_TYPE_SP_WFQ
+            obj.tm_sched_cfg.data.num_priorities = num_priority
+            self.log.info('Creating Scheduler',
+                          scheduler_details=obj)
+            yield self.stub.BalCfgSet(obj)
+        except Exception as e:
+            self.log.info('creat-scheduler-exception',
+                          olt=self.olt.olt_id,
+                          sched_id=id,
+                          direction=direction,
+                          owner=owner_info,
+                          exc=str(e))
+        return
diff --git a/voltha/adapters/broadcom_onu/broadcom_onu.py b/voltha/adapters/broadcom_onu/broadcom_onu.py
index bef5adf..44bbe48 100644
--- a/voltha/adapters/broadcom_onu/broadcom_onu.py
+++ b/voltha/adapters/broadcom_onu/broadcom_onu.py
@@ -37,6 +37,8 @@
 from voltha.protos.logical_device_pb2 import LogicalPort
 from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPF_1GB_FD
 from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC, ofp_port
+from voltha.protos.bbf_fiber_base_pb2 import VEnetConfig
+
 from common.frameio.frameio import hexify
 from voltha.extensions.omci.omci import *
 
@@ -204,7 +206,8 @@
         raise NotImplementedError()
 
     def create_tcont(self, device, tcont_data, traffic_descriptor_data):
-        raise NotImplementedError()
+        log.info('Not implemented Yet', device_id=device.id)
+        #raise NotImplementedError()
 
     def update_tcont(self, device, tcont_data, traffic_descriptor_data):
         raise NotImplementedError()
@@ -213,7 +216,8 @@
         raise NotImplementedError()
 
     def create_gemport(self, device, data):
-        raise NotImplementedError()
+        log.info('Not implemented Yet', device_id=device.id)
+        #raise NotImplementedError()
 
     def update_gemport(self, device, data):
         raise NotImplementedError()
@@ -1308,13 +1312,44 @@
         yield self.wait_for_response()
 
     def create_interface(self, data):
-        self.log.info('Not Implemented yet')
-        return;
+        if isinstance(data, VEnetConfig):
+            parent_port_num = None
+            onu_device = self.adapter_agent.get_device(self.device_id)
+            ports = self.adapter_agent.get_ports(onu_device.parent_id, Port.ETHERNET_UNI)
+            parent_port_num = None
+            for port in ports:
+                if port.label == data.interface.name:
+                    parent_port_num = port.port_no
+                    break
+
+            if not parent_port_num:
+                self.log.error("matching-parent-uni-port-num-not-found")
+                return
+
+            onu_ports = self.adapter_agent.get_ports(self.device_id, Port.PON_ONU)
+            if onu_ports:
+                # To-Do :
+                # Assumed only one PON port and UNI port per ONU.
+                pon_port = onu_ports[0]
+            else:
+                self.log.error("No-Pon-port-configured-yet")
+                return
+
+            self.adapter_agent.delete_port_reference_from_parent(self.device_id,
+                                                                 pon_port)
+
+            pon_port.peers[0].device_id = onu_device.parent_id
+            pon_port.peers[0].port_no = parent_port_num
+            self.adapter_agent.add_port_reference_to_parent(self.device_id,
+                                                            pon_port)
+        else:
+            self.log.info('Not handled Yet')
+        return
 
     def update_interface(self, data):
         self.log.info('Not Implemented yet')
-        return;
+        return
 
     def remove_interface(self, data):
         self.log.info('Not Implemented yet')
-        return;
+        return
diff --git a/voltha/core/adapter_agent.py b/voltha/core/adapter_agent.py
index 95fb4d6..2f73505 100644
--- a/voltha/core/adapter_agent.py
+++ b/voltha/core/adapter_agent.py
@@ -478,6 +478,10 @@
         self.log.info('delete-port-reference', device_id=device_id, port=port)
         self._del_peer_reference(device_id, port)
 
+        # update child port details
+        self._make_up_to_date('/devices/{}/ports'.format(device_id),
+                              port.port_no, port)
+
     def add_port_reference_to_parent(self, device_id, port):
         """
         Add the port reference to the parent device
@@ -488,6 +492,9 @@
         assert isinstance(port, Port)
         self.log.info('add-port-reference', device_id=device_id, port=port)
         self._add_peer_reference(device_id, port)
+        # update child port details
+        self._make_up_to_date('/devices/{}/ports'.format(device_id),
+                              port.port_no, port)
 
     def _find_first_available_id(self):
         logical_devices = self.root_proxy.get('/logical_devices')
diff --git a/voltha/core/xpon_handler.py b/voltha/core/xpon_handler.py
index e91a604..c102c1a 100644
--- a/voltha/core/xpon_handler.py
+++ b/voltha/core/xpon_handler.py
@@ -87,7 +87,7 @@
                 design evolution, for a better approach in future.
             '''
             self.cg_dict[cg.name] = {'alloc_id': IndexPool(16383, 1024)}
-            self.cg_dict[cg.name].update({'gemport_id': IndexPool(64500, 1021)})
+            self.cg_dict[cg.name].update({'gemport_id': IndexPool(64500, 1024)})
         self.cg_pool.pre_allocate(cg_tup)
 
     def reinitialize_tcont_and_gemport_ids(self):
@@ -128,7 +128,7 @@
             request.cg_index = _id
             self.root.add('/channel_groups', request)
             self.cg_dict[request.name] = {'alloc_id': IndexPool(16383, 1024)}
-            self.cg_dict[request.name].update({'gemport_id': IndexPool(64500, 1021)})
+            self.cg_dict[request.name].update({'gemport_id': IndexPool(64500, 1024)})
 
             return Empty()
         except AssertionError, e: