Introduce the openolt_data_model module.

A voltha adapter maintains its data model in KV store.
In most cases the interaction with the KV store's
data model is not direct but via the adapter_agent.
There is a fair amount of boiler-plate code in the
adapter related to the interaction with adapter_agent.
Most of this is going to change from voltha 1.x to 2.0.
This, and subsequent related commits, aim to abstract
out the adapter_agent interface in the openolt_data_model.
The resulting de-cluttered logic of the adapter will
be more amenable to re-use in the porting to 2.0.

Change-Id: Ic0d7223db2a6713bae7a0c953d11b1977759fab6
diff --git a/voltha/adapters/openolt/openolt.py b/voltha/adapters/openolt/openolt.py
index 995be78..814c26a 100644
--- a/voltha/adapters/openolt/openolt.py
+++ b/voltha/adapters/openolt/openolt.py
@@ -35,6 +35,7 @@
 from voltha.adapters.openolt.openolt_bw import OpenOltBW
 from voltha.adapters.openolt.openolt_platform import OpenOltPlatform
 from voltha.adapters.openolt.openolt_resource_manager import OpenOltResourceMgr
+from voltha.adapters.openolt.openolt_data_model import OpenOltDataModel
 
 _ = third_party
 log = structlog.get_logger()
@@ -79,6 +80,7 @@
         self.interface = registry('main').get_args().interface
         self.logical_device_id_to_root_device_id = dict()
         self.num_devices = 0
+        self.data_model = OpenOltDataModel(adapter_agent)
 
     def start(self):
         log.info('started', interface=self.interface)
@@ -109,6 +111,7 @@
 
         kwargs = {
             'support_classes': OpenOltDefaults['support_classes'],
+            'data_model': self.data_model,
             'adapter_agent': self.adapter_agent,
             'device': device,
             'device_num': self.num_devices + 1
diff --git a/voltha/adapters/openolt/openolt_data_model.py b/voltha/adapters/openolt/openolt_data_model.py
new file mode 100644
index 0000000..14903ae
--- /dev/null
+++ b/voltha/adapters/openolt/openolt_data_model.py
@@ -0,0 +1,163 @@
+#
+# Copyright 2019 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import structlog
+import socket
+from voltha.adapters.openolt.openolt_utils import OpenoltUtils
+from voltha.protos.device_pb2 import Port
+from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
+    OFPPS_LINK_DOWN, OFPPF_1GB_FD, OFPC_PORT_STATS, OFPC_TABLE_STATS, \
+    OFPC_FLOW_STATS, OFPC_GROUP_STATS, ofp_port, ofp_port_stats, ofp_desc, \
+    ofp_switch_features
+from voltha.core.logical_device_agent import mac_str_to_tuple
+from voltha.protos.logical_device_pb2 import LogicalPort
+from voltha.protos.common_pb2 import OperStatus, ConnectStatus
+from voltha.protos.logical_device_pb2 import LogicalDevice
+from voltha.registry import registry
+
+
+class OpenOltDataModel(object):
+    log = structlog.get_logger()
+
+    def __init__(self, adapter_agent):
+        self.adapter_agent = adapter_agent
+        self.log = structlog.get_logger()
+
+    def create_logical_device(self, device_id, device_info):
+        dpid = device_info.device_id
+        serial_number = device_info.device_serial_number
+
+        device = self.adapter_agent.get_device(device_id)
+        host_and_port = device.host_and_port
+
+        if dpid is None or dpid == '':
+            uri = host_and_port.split(":")[0]
+            try:
+                socket.inet_pton(socket.AF_INET, uri)
+                dpid = '00:00:' + OpenoltUtils.ip_hex(uri)
+            except socket.error:
+                # this is not an IP
+                dpid = OpenoltUtils.str_to_mac(uri)
+
+        self.log.info('creating-openolt-logical-device', dp_id=dpid,
+                      serial_number=serial_number)
+
+        hw_desc = device_info.model
+        if device_info.hardware_version:
+            hw_desc += '-' + device_info.hardware_version
+
+        # Create logical OF device
+        ld = LogicalDevice(
+            root_device_id=device_id,
+            switch_features=ofp_switch_features(
+                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
+                )
+            ),
+            desc=ofp_desc(
+                serial_num=serial_number
+            )
+        )
+        ld_init = self.adapter_agent.create_logical_device(ld,
+                                                           dpid=dpid)
+
+        device = self.adapter_agent.get_device(device_id)
+        device.serial_number = serial_number
+        self.adapter_agent.update_device(device)
+
+        self.log.info('created-openolt-logical-device',
+                      logical_device_id=ld_init.id)
+
+        return ld_init.id
+
+    def disable_logical_device(self, device_id):
+
+        oper_state = OperStatus.UNKNOWN
+        connect_state = ConnectStatus.UNREACHABLE
+
+        device = self.adapter_agent.get_device(device_id)
+
+        logical_device_id = device.parent_id
+
+        child_devices = self.adapter_agent.get_child_devices(device_id)
+        for onu_device in child_devices:
+            onu_adapter_agent = \
+                registry('adapter_loader').get_agent(onu_device.adapter)
+            onu_adapter_agent.update_interface(onu_device,
+                                               {'oper_state': 'down'})
+            self.onu_ports_down(onu_device, oper_state)
+
+        # Children devices
+        self.adapter_agent.update_child_devices_state(
+            device_id, oper_status=oper_state,
+            connect_status=connect_state)
+        # Device Ports
+        device_ports = self.adapter_agent.get_ports(device_id,
+                                                    Port.ETHERNET_NNI)
+        logical_ports_ids = [port.label for port in device_ports]
+        device_ports += self.adapter_agent.get_ports(device_id,
+                                                     Port.PON_OLT)
+
+        for port in device_ports:
+            port.oper_status = oper_state
+            self.adapter_agent.add_port(device_id, port)
+
+        # Device logical port
+        for logical_port_id in logical_ports_ids:
+            logical_port = self.adapter_agent.get_logical_port(
+                logical_device_id, logical_port_id)
+            logical_port.ofp_port.state = OFPPS_LINK_DOWN
+            self.adapter_agent.update_logical_port(self.logical_device_id,
+                                                   logical_port)
+        device.oper_status = oper_state
+        device.connect_status = connect_state
+        self.adapter_agent.update_device(device)
+
+    def add_logical_port(self, logical_device_id, device_id, port_no, intf_id,
+                         oper_state):
+        self.log.info('adding-logical-port', port_no=port_no)
+
+        label = OpenoltUtils.port_name(port_no, Port.ETHERNET_NNI)
+
+        cap = OFPPF_1GB_FD | OFPPF_FIBER
+        curr_speed = OFPPF_1GB_FD
+        max_speed = OFPPF_1GB_FD
+
+        if oper_state == OperStatus.ACTIVE:
+            of_oper_state = OFPPS_LIVE
+        else:
+            of_oper_state = OFPPS_LINK_DOWN
+
+        ofp = ofp_port(
+            port_no=port_no,
+            hw_addr=mac_str_to_tuple(
+                OpenoltUtils.make_mac_from_port_no(port_no)),
+            name=label, config=0, state=of_oper_state, curr=cap,
+            advertised=cap, peer=cap, curr_speed=curr_speed,
+            max_speed=max_speed)
+
+        ofp_stats = ofp_port_stats(port_no=port_no)
+
+        logical_port = LogicalPort(
+            id=label, ofp_port=ofp, device_id=device_id,
+            device_port_no=port_no, root_port=True,
+            ofp_port_stats=ofp_stats)
+
+        self.adapter_agent.add_logical_port(logical_device_id, logical_port)
diff --git a/voltha/adapters/openolt/openolt_device.py b/voltha/adapters/openolt/openolt_device.py
index 318d9e1..c7fc9d9 100644
--- a/voltha/adapters/openolt/openolt_device.py
+++ b/voltha/adapters/openolt/openolt_device.py
@@ -16,7 +16,6 @@
 import threading
 import binascii
 import grpc
-import socket
 import structlog
 import time
 from twisted.internet import reactor
@@ -25,17 +24,10 @@
 
 from voltha.protos.device_pb2 import Port, Device
 from voltha.protos.common_pb2 import OperStatus, AdminState, ConnectStatus
-from voltha.protos.logical_device_pb2 import LogicalDevice
-from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
-    OFPPS_LINK_DOWN, OFPPF_1GB_FD, OFPC_GROUP_STATS, OFPC_PORT_STATS, \
-    OFPC_TABLE_STATS, OFPC_FLOW_STATS, ofp_switch_features, ofp_port, \
-    ofp_port_stats, ofp_desc
-from voltha.protos.logical_device_pb2 import LogicalPort
-from voltha.core.logical_device_agent import mac_str_to_tuple
+from voltha.protos.openflow_13_pb2 import OFPPS_LINK_DOWN
 from voltha.registry import registry
 from voltha.adapters.openolt.protos import openolt_pb2_grpc, openolt_pb2
 from voltha.adapters.openolt.openolt_utils import OpenoltUtils
-
 from voltha.extensions.alarms.onu.onu_discovery_alarm import OnuDiscoveryAlarm
 
 
@@ -84,6 +76,7 @@
 
         self.admin_state = "up"
 
+        self.data_model = kwargs['data_model']
         self.adapter_agent = kwargs['adapter_agent']
         self.device_num = kwargs['device_num']
         device = kwargs['device']
@@ -135,55 +128,6 @@
 
         self.go_state_init()
 
-    def create_logical_device(self, device_info):
-        dpid = device_info.device_id
-        serial_number = device_info.device_serial_number
-
-        if dpid is None or dpid == '':
-            uri = self.host_and_port.split(":")[0]
-            try:
-                socket.inet_pton(socket.AF_INET, uri)
-                dpid = '00:00:' + OpenoltUtils.ip_hex(uri)
-            except socket.error:
-                # this is not an IP
-                dpid = OpenoltUtils.str_to_mac(uri)
-
-        self.log.info('creating-openolt-logical-device', dp_id=dpid,
-                      serial_number=serial_number)
-
-        hw_desc = device_info.model
-        if device_info.hardware_version:
-            hw_desc += '-' + device_info.hardware_version
-
-        # Create logical OF device
-        ld = LogicalDevice(
-            root_device_id=self.device_id,
-            switch_features=ofp_switch_features(
-                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
-                )
-            ),
-            desc=ofp_desc(
-                serial_num=serial_number
-            )
-        )
-        ld_init = self.adapter_agent.create_logical_device(ld,
-                                                           dpid=dpid)
-
-        device = self.adapter_agent.get_device(self.device_id)
-        device.serial_number = serial_number
-        self.adapter_agent.update_device(device)
-
-        self.log.info('created-openolt-logical-device',
-                      logical_device_id=ld_init.id)
-
-        return ld_init.id
-
     def do_state_init(self, event):
         # Initialize gRPC
         self.channel = grpc.insecure_channel(self.host_and_port)
@@ -222,8 +166,8 @@
 
         if self.logical_device_id is None:
             # first time connect to olt
-            self.logical_device_id = self.create_logical_device(
-                self.device_info)
+            self.logical_device_id = self.data_model.create_logical_device(
+                self.device_id, self.device_info)
         else:
             # reconnect to olt (e.g. olt reboot)
             # TODO - Update logical device with new device_info
@@ -270,49 +214,8 @@
 
     def do_state_down(self, event):
         self.log.debug("do_state_down")
-        oper_state = OperStatus.UNKNOWN
-        connect_state = ConnectStatus.UNREACHABLE
 
-        # Propagating to the children
-
-        # Children ports
-        child_devices = self.adapter_agent.get_child_devices(self.device_id)
-        for onu_device in child_devices:
-            onu_adapter_agent = \
-                registry('adapter_loader').get_agent(onu_device.adapter)
-            onu_adapter_agent.update_interface(onu_device,
-                                               {'oper_state': 'down'})
-            self.onu_ports_down(onu_device, oper_state)
-
-        # Children devices
-        self.adapter_agent.update_child_devices_state(
-            self.device_id, oper_status=oper_state,
-            connect_status=connect_state)
-        # Device Ports
-        device_ports = self.adapter_agent.get_ports(self.device_id,
-                                                    Port.ETHERNET_NNI)
-        logical_ports_ids = [port.label for port in device_ports]
-        device_ports += self.adapter_agent.get_ports(self.device_id,
-                                                     Port.PON_OLT)
-
-        for port in device_ports:
-            port.oper_status = oper_state
-            self.adapter_agent.add_port(self.device_id, port)
-
-        # Device logical port
-        for logical_port_id in logical_ports_ids:
-            logical_port = self.adapter_agent.get_logical_port(
-                self.logical_device_id, logical_port_id)
-            logical_port.ofp_port.state = OFPPS_LINK_DOWN
-            self.adapter_agent.update_logical_port(self.logical_device_id,
-                                                   logical_port)
-
-        # Device
-        device = self.adapter_agent.get_device(self.device_id)
-        device.oper_status = oper_state
-        device.connect_status = connect_state
-
-        reactor.callLater(2, self.adapter_agent.update_device, device)
+        self.data_model.disable_logical_device(self.device_id)
 
     def post_down(self, event):
         self.log.debug('post_down')
@@ -406,13 +309,6 @@
                     self.log.warn('unknown indication type')
 
     def olt_indication(self, olt_indication):
-        '''
-        if self.admin_state is "up":
-            if olt_indication.oper_state == "up":
-                self.go_state_up()
-            elif olt_indication.oper_state == "down":
-                self.go_state_down()
-        '''
         if olt_indication.oper_state == "up":
             self.go_state_up()
         elif olt_indication.oper_state == "down":
@@ -447,8 +343,10 @@
             port_no, label = self.add_port(intf_oper_indication.intf_id,
                                            Port.ETHERNET_NNI, oper_state)
             self.log.debug("int_oper_indication", port_no=port_no, label=label)
-            self.add_logical_port(port_no, intf_oper_indication.intf_id,
-                                  oper_state)
+            self.data_model.add_logical_port(self.logical_device_id,
+                                             self.device_id, port_no,
+                                             intf_oper_indication.intf_id,
+                                             oper_state)
 
         elif intf_oper_indication.type == "pon":
             # FIXME - handle PON oper state change
@@ -831,38 +729,6 @@
             admin_state=AdminState.ENABLED
         )
 
-    def add_logical_port(self, port_no, intf_id, oper_state):
-        self.log.info('adding-logical-port', port_no=port_no)
-
-        label = OpenoltUtils.port_name(port_no, Port.ETHERNET_NNI)
-
-        cap = OFPPF_1GB_FD | OFPPF_FIBER
-        curr_speed = OFPPF_1GB_FD
-        max_speed = OFPPF_1GB_FD
-
-        if oper_state == OperStatus.ACTIVE:
-            of_oper_state = OFPPS_LIVE
-        else:
-            of_oper_state = OFPPS_LINK_DOWN
-
-        ofp = ofp_port(
-            port_no=port_no,
-            hw_addr=mac_str_to_tuple(
-                OpenoltUtils.make_mac_from_port_no(port_no)),
-            name=label, config=0, state=of_oper_state, curr=cap,
-            advertised=cap, peer=cap, curr_speed=curr_speed,
-            max_speed=max_speed)
-
-        ofp_stats = ofp_port_stats(port_no=port_no)
-
-        logical_port = LogicalPort(
-            id=label, ofp_port=ofp, device_id=self.device_id,
-            device_port_no=port_no, root_port=True,
-            ofp_port_stats=ofp_stats)
-
-        self.adapter_agent.add_logical_port(self.logical_device_id,
-                                            logical_port)
-
     def add_port(self, intf_id, port_type, oper_status):
         port_no = self.platform.intf_id_to_port_no(intf_id, port_type)