1)flow table is improved for OpenOlt device adding more information e.g.
flow_id, flow_category, flow_type, gemport_id, alloc_id, o_pbits, intf_onu_id
2)exit command added

Change-Id: Ia7c8e2ad67455a78d99b1c439965c6c58df3b59e
diff --git a/cli/device.py b/cli/device.py
index 44407e2..4456a4c 100644
--- a/cli/device.py
+++ b/cli/device.py
@@ -30,16 +30,25 @@
 from voltha.protos import voltha_pb2, common_pb2
 import sys
 import json
+import unicodedata
+import ast
 from google.protobuf.json_format import MessageToDict
 
 # Since proto3 won't send fields that are set to 0/false/"" any object that
 # might have those values set in them needs to be replicated here such that the
 # fields can be adequately
 
+FLOW_ID_INFO_PATH = '{}/{}/flow_id_info/{}'
+FLOW_IDS_PATH = '{}/{}/flow_ids'
+technology = 'xgspon'
+PATH_PREFIX0 = 'service/voltha/resource_manager/{}'
+PATH_PREFIX = PATH_PREFIX0.format(technology)
+PATH = '{}/{}'
+
 
 class DeviceCli(Cmd):
 
-    def __init__(self, device_id, get_stub):
+    def __init__(self, device_id, get_stub, etcd):
         Cmd.__init__(self)
         self.get_stub = get_stub
         self.device_id = device_id
@@ -47,6 +56,7 @@
             self.colorize('device {}'.format(device_id), 'red'), 'bold') + ') '
         self.pm_config_last = None
         self.pm_config_dirty = False
+        self.etcd = etcd
 
     def cmdloop(self):
         self._cmdloop()
@@ -347,15 +357,99 @@
                                    omit_fields, self.poutput, dividers=100,
                                    show_nulls=True)
 
+    def get_flow_id(self, id_str):
+        # original representation of the flow id
+        # there is a mask for upstream flow ids of openolt adapter as 0x1 << 15 | flow_id
+        # ponsim and other flow does not need any modification
+        if int(id_str) >= 0x1 << 15:
+            flow_id = int(id_str) ^ 0x1 << 15
+        else:
+            flow_id = int(id_str)
+
+        return flow_id
+
+    def flow_exist(self, pon_intf_onu_id, flow_id):
+        # checks whether the flow still exists in ETCD or not
+        flow_ids_path = FLOW_IDS_PATH.format(self.device_id, pon_intf_onu_id)
+        path_to_flow_ids = PATH.format(PATH_PREFIX, flow_ids_path)
+        (flow_ids, _) = self.etcd.get(path_to_flow_ids)
+        if flow_ids is None:
+            return False
+        else:
+            if flow_id in eval(flow_ids):
+                return True
+            else:
+                return False
+
+    def update_repeated_ids_dict(self,flow_id, repeated_ids):
+        # updates how many times an id is seen
+        if str(flow_id) in repeated_ids:
+            repeated_ids[str(flow_id)] += 1
+        else:
+            repeated_ids[str(flow_id)] = 1
+        return repeated_ids
+
+    def get_flow_index(self,flow, flow_info_all):
+        if 'flow_store_cookie' in flow:
+            for i, flow_info in enumerate(flow_info_all):
+                if unicodedata.normalize('NFKD', flow['flow_store_cookie']).encode('ascii', 'ignore') == flow_info['flow_store_cookie']:
+                    return i
+            return None
+        else: #only one flow or those flows that are not added to device
+            return 0
+
     def do_flows(self, line):
         """Show flow table for device"""
         device = pb2dict(self.get_device(-1))
+        flows_info = list()
+        flows = device['flows']['items']
+        for i, flow in enumerate(flows):
+            flow_info = dict()
+            if unicodedata.normalize('NFKD', device['type']).encode('ascii', 'ignore') == 'openolt':
+                flow_id = self.get_flow_id(flow['id'])
+            else:
+                flow_id = int(flow['id'])
+
+            flow_info.update({'flow_id' : str(flow_id)})
+
+            if 'intf_tuple' in flow and len(flow['intf_tuple']) > 0:    # we have extra flow info in ETCD!!!
+                pon_intf_onu_id = unicodedata.normalize('NFKD', flow['intf_tuple'][0]).encode('ascii', 'ignore')
+                flow_info.update({'pon_intf_onu_id': pon_intf_onu_id})
+
+                # check if the flow info still exists in ETCD
+                if self.flow_exist(pon_intf_onu_id, flow_id):
+                    flow_id_info_path = FLOW_ID_INFO_PATH.format(self.device_id, pon_intf_onu_id, flow_id)
+                    path_to_flow_info = PATH.format(PATH_PREFIX, flow_id_info_path)
+                    (flow_info_all, _) = self.etcd.get(path_to_flow_info)
+                    flow_info_all = ast.literal_eval(flow_info_all)
+                    flow_info_index = self.get_flow_index(flow, flow_info_all)
+
+                    if flow_info_index is not None:
+                        flow_info_all = flow_info_all[flow_info_index]
+                        if 'gemport_id' in flow_info_all:
+                            flow_info.update({'gemport_id': flow_info_all['gemport_id']})
+
+                        if 'alloc_id' in flow_info_all:
+                            flow_info.update({'alloc_id': flow_info_all['alloc_id']})
+
+                        if 'flow_type' in flow_info_all:
+                            flow_info.update({'flow_type': flow_info_all['flow_type']})
+
+                        if 'o_pbits' in flow_info_all['classifier']:
+                            flow_info.update({'o_pbits': flow_info_all['classifier']['o_pbits']})
+
+                        if 'flow_category' in flow_info_all:
+                            flow_info.update({'flow_category': flow_info_all['flow_category']})
+
+            flows_info.append(flow_info)
         print_flows(
             'Device',
             self.device_id,
             type=device['type'],
             flows=device['flows']['items'],
-            groups=device['flow_groups']['items']
+            groups=device['flow_groups']['items'],
+            flows_info=flows_info,
+            fields_to_omit = ['table_id', 'goto-table']
         )
 
     def do_images(self, line):