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):
diff --git a/cli/main.py b/cli/main.py
index 3a7dfed..3a30a0d 100755
--- a/cli/main.py
+++ b/cli/main.py
@@ -21,6 +21,7 @@
from optparse import make_option
from time import sleep, time
+import etcd3
import grpc
import requests
from cmd2 import Cmd, options
@@ -43,6 +44,7 @@
defs = dict(
# config=os.environ.get('CONFIG', './cli.yml'),
+ etcd=os.environ.get('ETCD', 'etcd-cluster.default.svc.cluster.local:2379'),
consul=os.environ.get('CONSUL', 'localhost:8500'),
voltha_grpc_endpoint=os.environ.get('VOLTHA_GRPC_ENDPOINT',
'localhost:50055'),
@@ -56,7 +58,7 @@
__ _____| | |_| |_ __ _ / __| | |_ _|
\ V / _ \ | _| ' \/ _` | | (__| |__ | |
\_/\___/_|\__|_||_\__,_| \___|____|___|
-(to exit type quit or hit Ctrl-D)
+(to exit type quit or exit or hit Ctrl-D)
"""
@@ -88,10 +90,11 @@
del Cmd.do_load
del Cmd.do__relative_load
- def __init__(self, voltha_grpc, voltha_sim_rest, global_request=False):
+ def __init__(self, voltha_grpc, voltha_sim_rest, etcd, global_request=False):
VolthaCli.voltha_grpc = voltha_grpc
VolthaCli.voltha_sim_rest = voltha_sim_rest
VolthaCli.global_request = global_request
+ VolthaCli.etcd = etcd
Cmd.__init__(self)
self.prompt = '(' + self.colorize(
self.colorize(self.prompt, 'blue'), 'bold') + ') '
@@ -154,6 +157,10 @@
while self.history:
self.history.pop()
+ def do_exit(self,line):
+ """exit from CLI"""
+ quit()
+
def do_launch(self, line):
"""If Voltha is not running yet, launch it"""
raise NotImplementedError('not implemented yet')
@@ -220,7 +227,7 @@
self.poutput( self.colorize('Error: ', 'red') +
'There is no such device')
raise Exception('<device-id> is not a valid one')
- sub = DeviceCli(device_id, self.get_stub)
+ sub = DeviceCli(device_id, self.get_stub, self.etcd)
sub.cmdloop()
def do_logical_device(self, line):
@@ -868,6 +875,10 @@
parser.add_argument(
'-C', '--consul', action='store', default=defs['consul'], help=_help)
+ _help = '<hostname>:<port> to etcd container (default: %s)' % defs['etcd']
+ parser.add_argument(
+ '-E', '--etcd', action='store', default=defs['etcd'], help=_help)
+
_help = 'Lookup Voltha endpoints based on service entries in Consul'
parser.add_argument(
'-L', '--lookup', action='store_true', help=_help)
@@ -908,7 +919,10 @@
args.sim_rest_endpoint = '{}:{}'.format(services[0]['ServiceAddress'],
services[0]['ServicePort'])
- c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint,
+ host = args.etcd.split(':')[0].strip()
+ port = int(args.etcd.split(':')[1].strip())
+ etcd = etcd3.client(host=host, port=port)
+ c = VolthaCli(args.grpc_endpoint, args.sim_rest_endpoint, etcd,
args.global_request)
c.poutput(banner)
c.load_history()
diff --git a/cli/utils.py b/cli/utils.py
index 82d6a12..ea190a1 100644
--- a/cli/utils.py
+++ b/cli/utils.py
@@ -111,7 +111,7 @@
}
-def print_flows(what, id, type, flows, groups, printfn=_printfn):
+def print_flows(what, id, type, flows, groups, printfn=_printfn, flows_info=[], fields_to_omit=[]):
header = ''.join([
'{} '.format(what),
@@ -124,9 +124,31 @@
table = TablePrinter()
for i, flow in enumerate(flows):
- table.add_cell(i, 0, 'table_id', value=str(flow['table_id']))
- table.add_cell(i, 1, 'priority', value=str(flow['priority']))
- table.add_cell(i, 2, 'cookie', p_cookie(flow['cookie']))
+ if flows_info:
+ flow_info = flows_info[i]
+ else:
+ flow_info = dict()
+
+ if 'table_id' not in fields_to_omit:
+ table.add_cell(i, 0, 'table_id', value=str(flow['table_id']))
+ if 'flow_id' not in fields_to_omit and 'flow_id' in flow_info:
+ table.add_cell(i, 1, 'flow_id', value=str(flow_info['flow_id']))
+ if 'flow_category' not in fields_to_omit and 'flow_category' in flow_info:
+ table.add_cell(i, 2, 'flow_category', value=str(flow_info['flow_category']))
+ if 'flow_type' not in fields_to_omit and 'flow_type' in flow_info:
+ table.add_cell(i, 3, 'flow_type', value=str(flow_info['flow_type']))
+ if 'priority' not in fields_to_omit:
+ table.add_cell(i, 4, 'priority', value=str(flow['priority']))
+ if 'gemport_id' not in fields_to_omit and 'gemport_id' in flow_info:
+ table.add_cell(i, 5, 'gemport_id', value=str(flow_info['gemport_id']))
+ if 'alloc_id' not in fields_to_omit and 'alloc_id' in flow_info:
+ table.add_cell(i, 6, 'alloc_id', value=str(flow_info['alloc_id']))
+ if 'o_pbits' not in fields_to_omit and 'o_pbits' in flow_info:
+ table.add_cell(i, 7, 'o_pbits', value=str(flow_info['o_pbits']))
+ if 'pon_intf_onu_id' not in fields_to_omit and 'pon_intf_onu_id' in flow_info:
+ table.add_cell(i, 8, 'intf_onu_id', value=str(flow_info['pon_intf_onu_id']))
+ if 'cookie' not in fields_to_omit:
+ table.add_cell(i, 9, 'cookie', p_cookie(flow['cookie']))
assert flow['match']['type'] == 'OFPMT_OXM'
for field in flow['match']['oxm_fields']:
@@ -142,16 +164,20 @@
atype = action['type'][len('OFPAT_'):]
table.add_cell(i, *action_printers[atype](action))
elif itype == 1:
- table.add_cell(i, 10000, 'goto-table',
- instruction['goto_table']['table_id'])
+ if 'goto-table' not in fields_to_omit:
+ table.add_cell(i, 10000, 'goto-table',
+ instruction['goto_table']['table_id'])
elif itype == 2:
- table.add_cell(i, 10001, 'write-metadata',
- instruction['write_metadata']['metadata'])
+ if 'write-metadata' not in fields_to_omit:
+ table.add_cell(i, 10001, 'write-metadata',
+ instruction['write_metadata']['metadata'])
elif itype == 5:
- table.add_cell(i, 10002, 'clear-actions', [])
+ if 'clear-actions' not in fields_to_omit:
+ table.add_cell(i, 10002, 'clear-actions', [])
elif itype == 6:
- table.add_cell(i, 10003, 'meter',
- instruction['meter']['meter_id'])
+ if 'meter' not in fields_to_omit:
+ table.add_cell(i, 10003, 'meter',
+ instruction['meter']['meter_id'])
else:
raise NotImplementedError(
'not handling instruction type {}'.format(itype))