Many CLI cleanups and flow preintegration

Changes:
- auto-completion for device and logical device IDs
- a set of test CLI commands to push down various flows
  to Voltha (aids test and integration)
- sample code in simulated_olt and onu to show how
  to process incoming bulk flow table
- extended Tibit OLT and ONU code with remaining flow
  directives they need to handle in the PON use-case

Change-Id: Id101e087cc79f4493805e3b4a051a10a4619bf53
diff --git a/cli/logical_device.py b/cli/logical_device.py
index 286faf0..3e90f41 100644
--- a/cli/logical_device.py
+++ b/cli/logical_device.py
@@ -21,9 +21,11 @@
 from cmd2 import Cmd
 from simplejson import dumps
 
+from cli.table import print_pb_as_table, print_pb_list_as_table
 from cli.utils import pb2dict
 from cli.utils import print_flows
 from voltha.protos import third_party
+from google.protobuf.empty_pb2 import Empty
 
 _ = third_party
 from voltha.protos import voltha_pb2
@@ -45,14 +47,34 @@
                                     metadata=(('get-depth', str(depth)), ))
         return res
 
+    def get_device(self, id):
+        stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
+        return stub.GetDevice(voltha_pb2.ID(id=id))
+
+    def get_devices(self):
+        stub = voltha_pb2.VolthaLocalServiceStub(self.get_channel())
+        res = stub.ListDevices(Empty())
+        return res.items
+
     do_exit = Cmd.do_quit
 
-    def do_show(self, arg):
+    def do_show(self, _):
         """Show detailed logical device information"""
-        self.poutput(dumps(pb2dict(self.get_logical_device(depth=-1)),
-                     indent=4, sort_keys=True))
+        print_pb_as_table('Logical device {}'.format(self.logical_device_id),
+                          self.get_logical_device(depth=-1))
 
-    def do_flows(self, arg):
+    def do_ports(self, _):
+        """Show ports of logical device"""
+        device = self.get_logical_device(depth=-1)
+        omit_fields = {
+            'ofp_port.advertised',
+            'ofp_port.peer',
+            'ofp_port.max_speed',
+        }
+        print_pb_list_as_table('Logical device ports:', device.ports,
+                               omit_fields, self.poutput)
+
+    def do_flows(self, _):
         """Show flow table for logical device"""
         logical_device = pb2dict(self.get_logical_device(-1))
         print_flows(
@@ -63,3 +85,22 @@
             groups=logical_device['flow_groups']['items']
         )
 
+    def do_devices(self, line):
+        """List devices that belong to this logical device"""
+        logical_device = self.get_logical_device()
+        root_device_id = logical_device.root_device_id
+        devices = [self.get_device(root_device_id)]
+        for d in self.get_devices():
+            if d.parent_id == root_device_id:
+                devices.append(d)
+        omit_fields = {
+            'adapter',
+            'vendor',
+            'model',
+            'hardware_version',
+            'software_version',
+            'firmware_version',
+            'serial_number'
+        }
+        print_pb_list_as_table('Devices:', devices, omit_fields, self.poutput)
+