VOL-1623-meter support and handling techprofile and fix for flow delete , now migrated to onosproject/onos:1.13.9-rc4
Change in flowupdate API towards adapters
Remove meter_get API from adapter to core
Added dependent vendor library files downloaded by "dep-ensure -update"
Added techprofile changes in the single commit
Review comments are addressed
submiting patch for integration tests for meter changes and modifications in unit test for updated flow decomposer logic
- submitting on behalf of "Salman.Siddiqui@radisys.com"
Load test for meter updated and other flow management test cases with meter
- Performed load test for 1K meters serially and parallely and added more TC in flow management
Rebased
Load test for meter updated and other flow management test cases with meter
- Performed load test for 1K meters serially and parallely and added more TC in flow management
- submitting on behalf of "Salman.Siddiqui@radisys.com"
pulled latest protos
verified EAPOL/DHCP/HSIA data with Edgecore OLT & TW ONT kit for one subcriber
verified delete/re-add is working end to end for the same subscriber
Change-Id: Idb232b7a0f05dc0c7e68266ac885740a3adff317
diff --git a/python/cli/logical_device.py b/python/cli/logical_device.py
index 187dd88..a49f6f8 100644
--- a/python/cli/logical_device.py
+++ b/python/cli/logical_device.py
@@ -96,6 +96,16 @@
groups=logical_device['flow_groups']['items']
)
+ def do_meters(self, _):
+ """Show flow meter table for logical device"""
+ logical_device = pb2dict(self.get_logical_device(-1))
+ print_meters(
+ 'Logical Device',
+ self.logical_device_id,
+ type='n/a',
+ meters=logical_device['meters']['items']
+ )
+
def do_devices(self, line):
"""List devices that belong to this logical device"""
logical_device = self.get_logical_device()
diff --git a/python/cli/utils.py b/python/cli/utils.py
index 1f72be3..7668b7c 100644
--- a/python/cli/utils.py
+++ b/python/cli/utils.py
@@ -140,15 +140,21 @@
for instruction in flow['instructions']:
itype = instruction['type']
- if itype == 4:
+ if itype == 4 or itype == 3:
for action in instruction['actions']['actions']:
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'])
+ elif itype == 2:
+ table.add_cell(i, 10001, 'write-metadata',
+ instruction['write_metadata']['metadata'])
elif itype == 5:
- table.add_cell(i, 10000, 'clear-actions', [])
+ table.add_cell(i, 10002, 'clear-actions', [])
+ elif itype == 6:
+ table.add_cell(i, 10003, 'meter',
+ instruction['meter']['meter_id'])
else:
raise NotImplementedError(
'not handling instruction type {}'.format(itype))
@@ -177,6 +183,26 @@
table.print_table(header, printfn)
+def print_meters(what, id, type, meters, printfn=_printfn):
+ header = ''.join([
+ '{} '.format(what),
+ colored(id, color='green', attrs=['bold']),
+ ' (type: ',
+ colored(type, color='blue'),
+ ')'
+ ]) + '\nMeters ({}):'.format(len(meters))
+
+ table = TablePrinter()
+ for i, meter in enumerate(meters):
+ bands = []
+ for meter_band in meter['config']['bands']:
+ bands.append(meter_band)
+ table.add_cell(i, 0, 'meter_id', value=str(meter['config']['meter_id']))
+ table.add_cell(i, 1, 'meter_bands', value=str(dict(bands=bands)))
+
+ table.print_table(header, printfn)
+
+
def dict2line(d):
assert isinstance(d, dict)
return ', '.join('{}: {}'.format(k, v) for k, v in sorted(d.items()))
diff --git a/python/ofagent/converter.py b/python/ofagent/converter.py
index 185fb59..6280331 100755
--- a/python/ofagent/converter.py
+++ b/python/ofagent/converter.py
@@ -65,6 +65,24 @@
kw = pb2dict(pb)
return of13.port_stats_entry(**kw)
+
+def ofp_meter_stats_to_loxi_meter_stats(pb):
+ return of13.meter_stats(
+ meter_id=pb.meter_id,
+ flow_count=pb.flow_count,
+ packet_in_count=pb.packet_in_count,
+ byte_in_count=pb.byte_in_count,
+ duration_sec=pb.duration_sec,
+ duration_nsec=pb.duration_nsec,
+ band_stats=[to_loxi(band_stat) for band_stat in pb.band_stats])
+
+
+def ofp_meter_band_stats_to_loxi_meter_stats(pb):
+ return of13.meter_band_stats(
+ packet_band_count=pb.packet_band_count,
+ byte_band_count=pb.byte_band_count
+ )
+
def make_loxi_field(oxm_field):
assert oxm_field['oxm_class'] == pb2.OFPXMC_OPENFLOW_BASIC
ofb_field = oxm_field['ofb_field']
@@ -179,6 +197,7 @@
elif type == pb2.OFPIT_METER:
return of13.instruction.meter(
meter_id=inst['meter']['meter_id'])
+
else:
raise NotImplementedError('Instruction type %d' % type)
@@ -244,7 +263,9 @@
'ofp_bucket_counter': ofp_bucket_counter_to_loxy_bucket_counter,
'ofp_bucket': ofp_bucket_to_loxi_bucket,
'ofp_action': make_loxi_action,
- 'ofp_port_stats': ofp_port_stats_to_loxi_port_stats
+ 'ofp_port_stats': ofp_port_stats_to_loxi_port_stats,
+ 'ofp_meter_stats': ofp_meter_stats_to_loxi_meter_stats,
+ 'ofp_meter_band_stats': ofp_meter_band_stats_to_loxi_meter_stats
}
@@ -264,6 +285,97 @@
match=to_grpc(lo.match),
instructions=[to_grpc(i) for i in lo.instructions])
+def loxi_meter_mod_to_ofp_meter_mod(lo):
+ return pb2.ofp_meter_mod(
+ command=lo.command,
+ flags=lo.flags,
+ meter_id=lo.meter_id,
+ bands=[to_grpc(i) for i in lo.meters])
+
+
+def loxi_meter_band_drop_to_ofp_meter_band_drop(lo):
+ return pb2.ofp_meter_band_header(
+ type=lo.type,
+ rate=lo.rate,
+ burst_size=lo.burst_size)
+
+
+def loxi_meter_band_dscp_remark_to_ofp_meter_band_dscp_remark(lo):
+ return pb2.ofp_meter_band_header(
+ type=lo.type,
+ rate=lo.rate,
+ burst_size=lo.burst_size,
+ dscp_remark=pb2.ofp_meter_band_dscp_remark(prec_level=lo.prec_level))
+
+
+def loxi_meter_band_experimenter_to_ofp_meter_band_experimenter(lo):
+ return pb2.ofp_meter_band_header(
+ type=lo.type,
+ rate=lo.rate,
+ burst_size=lo.burst_size,
+ experimenter=pb2.ofp_meter_band_experimenter(experimenter=lo.experimenter))
+
+
+def loxi_meter_multipart_request_to_ofp_meter_multipart_request(lo):
+ return pb2.ofp_meter_multipart_request(
+ meter_id=lo.meter_id)
+
+
+def loxi_meter_stats_to_ofp_meter_stats(lo):
+ return pb2.ofp_meter_stats(
+ meter_id=lo.meter_id,
+ flow_count=lo.flow_count,
+ packet_in_count =lo.packet_in_count,
+ byte_in_count=lo.byte_in_count,
+ duration_sec=lo.duration_sec,
+ duration_nsec=lo.duration_nsec,
+ band_stats=lo.band_stats)
+
+
+def loxi_meter_mod_to_ofp_meter_mod(lo):
+ return pb2.ofp_meter_mod(
+ command=lo.command,
+ flags=lo.flags,
+ meter_id=lo.meter_id,
+ bands=[to_grpc(i) for i in lo.meters])
+
+
+def loxi_meter_band_drop_to_ofp_meter_band_drop(lo):
+ return pb2.ofp_meter_band_header(
+ type=lo.type,
+ rate=lo.rate,
+ burst_size=lo.burst_size)
+
+
+def loxi_meter_band_dscp_remark_to_ofp_meter_band_dscp_remark(lo):
+ return pb2.ofp_meter_band_header(
+ type=lo.type,
+ rate=lo.rate,
+ burst_size=lo.burst_size,
+ dscp_remark=pb2.ofp_meter_band_dscp_remark(prec_level=lo.prec_level))
+
+
+def loxi_meter_band_experimenter_to_ofp_meter_band_experimenter(lo):
+ return pb2.ofp_meter_band_header(
+ type=lo.type,
+ rate=lo.rate,
+ burst_size=lo.burst_size,
+ experimenter=pb2.ofp_meter_band_experimenter(experimenter=lo.experimenter))
+
+
+def loxi_meter_multipart_request_to_ofp_meter_multipart_request(lo):
+ return pb2.ofp_meter_multipart_request(
+ meter_id=lo.meter_id)
+
+def loxi_meter_stats_to_ofp_meter_stats(lo):
+ return pb2.ofp_meter_stats(
+ meter_id=lo.meter_id,
+ flow_count=lo.flow_count,
+ packet_in_count =lo.packet_in_count,
+ byte_in_count=lo.byte_in_count,
+ duration_sec=lo.duration_sec,
+ duration_nsec=lo.duration_nsec,
+ band_stats=lo.band_stats)
def loxi_group_mod_to_ofp_group_mod(lo):
return pb2.ofp_group_mod(
@@ -384,12 +496,39 @@
goto_table=pb2.ofp_instruction_goto_table(table_id=lo.table_id))
+def loxi_write_metadata_to_ofp_instruction(lo):
+ return pb2.ofp_instruction(
+ type=pb2.OFPIT_WRITE_METADATA,
+ write_metadata=pb2.ofp_instruction_write_metadata(
+ metadata=lo.metadata,
+ metadata_mask=lo.metadata_mask))
+
+
+def loxi_meter_to_ofp_instruction(lo):
+ return pb2.ofp_instruction(
+ type=pb2.OFPIT_METER,
+ meter=pb2.ofp_instruction_meter(meter_id=lo.meter_id))
+
+
def loxi_output_action_to_ofp_action(lo):
return pb2.ofp_action(
type=pb2.OFPAT_OUTPUT,
output=pb2.ofp_action_output(port=lo.port, max_len=lo.max_len))
+def loxi_write_actions_to_ofp_instruction(lo):
+ return pb2.ofp_instruction(
+ type=pb2.OFPIT_WRITE_ACTIONS,
+ actions=pb2.ofp_instruction_actions(
+ actions=[to_grpc(a) for a in lo.actions]))
+
+
+def loxi_meter_to_ofp_instruction(lo):
+ return pb2.ofp_instruction(
+ type=pb2.OFPIT_METER,
+ meter=pb2.ofp_instruction_meter(meter_id=lo.meter_id))
+
+
def loxi_group_action_to_ofp_action(lo):
return pb2.ofp_action(
type=pb2.OFPAT_GROUP,
@@ -419,12 +558,18 @@
of13.message.flow_delete_strict: loxi_flow_mod_to_ofp_flow_mod,
of13.message.flow_modify: loxi_flow_mod_to_ofp_flow_mod,
of13.message.flow_modify_strict: loxi_flow_mod_to_ofp_flow_mod,
+ of13.message.meter_mod: loxi_meter_mod_to_ofp_meter_mod,
+ of13.message.meter_stats_request: loxi_meter_stats_to_ofp_meter_stats,
of13.message.group_add: loxi_group_mod_to_ofp_group_mod,
of13.message.group_delete: loxi_group_mod_to_ofp_group_mod,
of13.message.group_modify: loxi_group_mod_to_ofp_group_mod,
of13.message.packet_out: loxi_packet_out_to_ofp_packet_out,
+ of13.meter_band.drop: loxi_meter_band_drop_to_ofp_meter_band_drop,
+ of13.meter_band.dscp_remark: loxi_meter_band_dscp_remark_to_ofp_meter_band_dscp_remark,
+ of13.meter_band.experimenter: loxi_meter_band_experimenter_to_ofp_meter_band_experimenter,
+
of13.common.match_v3: loxi_match_v3_to_ofp_match,
of13.common.bucket: loxi_bucket_to_ofp_bucket,
@@ -440,7 +585,10 @@
of13.instruction.apply_actions: loxi_apply_actions_to_ofp_instruction,
of13.instruction.clear_actions: loxi_clear_actions_to_ofp_instruction,
+ of13.instruction.write_actions: loxi_write_actions_to_ofp_instruction,
of13.instruction.goto_table: loxi_goto_table_to_ofp_instruction,
+ of13.instruction.write_metadata: loxi_write_metadata_to_ofp_instruction,
+ of13.instruction.meter: loxi_meter_to_ofp_instruction,
of13.action.output: loxi_output_action_to_ofp_action,
of13.action.group: loxi_group_action_to_ofp_action,
diff --git a/python/ofagent/grpc_client.py b/python/ofagent/grpc_client.py
index 42e8510..e4b260e 100755
--- a/python/ofagent/grpc_client.py
+++ b/python/ofagent/grpc_client.py
@@ -29,7 +29,7 @@
from twisted.internet.defer import inlineCallbacks, returnValue, DeferredQueue
from voltha_protos.voltha_pb2_grpc import VolthaServiceStub
-from voltha_protos.voltha_pb2 import ID, FlowTableUpdate, \
+from voltha_protos.voltha_pb2 import ID, FlowTableUpdate, MeterModUpdate, \
FlowGroupTableUpdate, PacketOut
from voltha_protos.logical_device_pb2 import LogicalPortId
from google.protobuf import empty_pb2
@@ -246,6 +246,20 @@
returnValue(res)
@inlineCallbacks
+ def update_meter_mod_table(self, device_id, meter_mod):
+ log.debug('In update_meter_mod_table grpc')
+ req = MeterModUpdate(
+ id=device_id,
+ meter_mod=meter_mod
+ )
+ res = yield threads.deferToThread(
+ self.grpc_stub.UpdateLogicalDeviceMeterTable, req, timeout=self.grpc_timeout,
+ metadata=((self.core_group_id_key, self.core_group_id),
+ self.get_core_transaction_metadata(),))
+ log.debug('update_meter_mod_table grpc done')
+ returnValue(res)
+
+ @inlineCallbacks
def update_group_table(self, device_id, group_mod):
req = FlowGroupTableUpdate(
id=device_id,
@@ -310,3 +324,14 @@
self.core_group_id = pair[1]
log.debug('core-binding', core_group=self.core_group_id)
returnValue(res)
+
+ @inlineCallbacks
+ def list_meters(self, device_id):
+ log.debug('list_meters')
+ req = ID(id=device_id)
+ res = yield threads.deferToThread(
+ self.grpc_stub.ListLogicalDeviceMeters, req, timeout=self.grpc_timeout,
+ metadata=((self.core_group_id_key, self.core_group_id),
+ self.get_core_transaction_metadata(),))
+ log.debug('done stat query', resp=res)
+ returnValue(res.items)
diff --git a/python/ofagent/of_protocol_handler.py b/python/ofagent/of_protocol_handler.py
index 604ce3c..e90ca5b 100755
--- a/python/ofagent/of_protocol_handler.py
+++ b/python/ofagent/of_protocol_handler.py
@@ -29,6 +29,10 @@
ofp_version = [4] # OFAgent supported versions
+ MAX_METER_IDS = 4294967295
+ MAX_METER_BANDS = 255
+ MAX_METER_COLORS = 255
+
def __init__(self, datapath_id, device_id, agent, cxn, rpc):
"""
The upper half of the OpenFlow protocol, focusing on message
@@ -128,6 +132,30 @@
elif self.role == ofp.OFPCR_ROLE_SLAVE:
self.cxn.send(ofp.message.bad_request_error_msg(code=ofp.OFPBRC_IS_SLAVE))
+
+ def handle_meter_mod_request(self, req):
+ log.info('Received handle_meter_mod_request', request=req)
+ if self.role == ofp.OFPCR_ROLE_MASTER or self.role == ofp.OFPCR_ROLE_EQUAL:
+ try:
+ grpc_req = to_grpc(req)
+ except Exception, e:
+ log.exception('failed-to-convert-meter-mod-request', e=e)
+ else:
+ return self.rpc.update_meter_mod_table(self.device_id, grpc_req)
+
+ elif self.role == ofp.OFPCR_ROLE_SLAVE:
+ self.cxn.send(ofp.message.bad_request_error_msg(code=ofp.OFPBRC_IS_SLAVE))
+
+ @inlineCallbacks
+ def handle_meter_stats_request(self, req):
+ log.info('Received handle_meter_stats_request', request=req)
+ try:
+ meters = yield self.rpc.list_meters(self.device_id)
+ self.cxn.send(ofp.message.meter_stats_reply(
+ xid=req.xid, entries=[to_loxi(m.stats) for m in meters]))
+ except Exception, e:
+ log.exception("failed-meter-stats-request", req=req, e=e)
+
def handle_get_async_request(self, req):
raise NotImplementedError()
@@ -144,10 +172,6 @@
elif self.role == ofp.OFPCR_ROLE_SLAVE:
self.cxn.send(ofp.message.bad_request_error_msg(code=ofp.OFPBRC_IS_SLAVE))
-
- def handle_meter_mod_request(self, req):
- raise NotImplementedError()
-
def handle_role_request(self, req):
if req.role == ofp.OFPCR_ROLE_MASTER or req.role == ofp.OFPCR_ROLE_SLAVE:
if self.agent.generation_is_defined and (
@@ -238,16 +262,17 @@
def handle_group_features_request(self, req):
raise NotImplementedError()
- def handle_meter_stats_request(self, req):
- meter_stats = [] # see https://jira.opencord.org/browse/CORD-825
- self.cxn.send(ofp.message.meter_stats_reply(
- xid=req.xid, entries=meter_stats))
-
def handle_meter_config_request(self, req):
raise NotImplementedError()
def handle_meter_features_request(self, req):
- self.cxn.send(ofp.message.bad_request_error_msg())
+ feature = ofp.meter_features(max_meter=OpenFlowProtocolHandler.MAX_METER_IDS,
+ band_types=ofp.OFPMBT_DROP,
+ capabilities=ofp.OFPMF_KBPS,
+ max_bands=OpenFlowProtocolHandler.MAX_METER_BANDS,
+ max_color=OpenFlowProtocolHandler.MAX_METER_COLORS)
+ self.cxn.send(ofp.message.meter_features_stats_reply(xid=req.xid, flags=None,
+ features=feature))
@inlineCallbacks
def handle_port_stats_request(self, req):
diff --git a/python/requirements.txt b/python/requirements.txt
index 556e5e3..b82f310 100644
--- a/python/requirements.txt
+++ b/python/requirements.txt
@@ -59,5 +59,5 @@
pexpect==4.6.0
python-consul==0.6.2
afkak==3.0.0.dev20181106
-voltha-protos>=0.1.4
+voltha-protos==1.0.0
pyvoltha==0.2.2