From 7f994a34d213f3e250e475eefa47778f1b0a355b Mon Sep 17 00:00:00 2001 From: Adrian Moreno Date: Thu, 29 Jul 2021 14:57:03 +0200 Subject: [PATCH] fix unit_tests Signed-off-by: Adrian Moreno --- ovs_dbg/decoders.py | 3 - ovs_dbg/{ofparse => }/filter.py | 117 ++++++++++++++------------------ ovs_dbg/flow.py | 2 +- ovs_dbg/ofparse/main.py | 2 +- tests/test_filter.py | 2 +- tests/test_odp.py | 8 +-- tests/test_ofp.py | 4 +- 7 files changed, 59 insertions(+), 79 deletions(-) rename ovs_dbg/{ofparse => }/filter.py (51%) diff --git a/ovs_dbg/decoders.py b/ovs_dbg/decoders.py index 623f8f1..0ec83c2 100644 --- a/ovs_dbg/decoders.py +++ b/ovs_dbg/decoders.py @@ -17,9 +17,6 @@ def decode_default(value): It tries to convert into an integer value and, if it fails, just returns the string. """ - if not value: - return True - try: ival = int(value, 0) return ival diff --git a/ovs_dbg/ofparse/filter.py b/ovs_dbg/filter.py similarity index 51% rename from ovs_dbg/ofparse/filter.py rename to ovs_dbg/filter.py index 24c5a3e..854f776 100644 --- a/ovs_dbg/ofparse/filter.py +++ b/ovs_dbg/filter.py @@ -1,94 +1,77 @@ -#!/bin/python3 +""" Defines a Flow Filtering syntax +""" import pyparsing as pp import netaddr -from ovs_dbg.decoders import decode_default, decode_int, decode_mac, decode_ip +from ovs_dbg.decoders import decode_default from ovs_dbg.fields import field_decoders class ClauseExpression: - type_decoders = { - "int": decode_int, - "IPMask": decode_ip, - "IPAddress": decode_ip, - "EthMask": decode_mac, - "EUI": decode_mac, - "bool": bool, + operators = {} + decoders = { + "nw_src": netaddr.IPNetwork, + **field_decoders, } def __init__(self, tokens): self.field = tokens[0] - self.value = "" - self.operator = "" - - if len(tokens) > 1: + if len(tokens) <= 1: + self.operator = "=" + self.value_raw = True + self.value = True + else: self.operator = tokens[1] - self.value = tokens[2] + self.value_raw = tokens[2] + self.value = ( + self.decoders.get(self.field)(self.value_raw) + if self.decoders.get(self.field) + else decode_default(self.value_raw) + ) + if isinstance(self.value, str) and self.value == "true": + self.value = True + elif isinstance(self.value, str) and self.value == "false": + self.value = False def __repr__(self): return "{}(field: {}, operator: {}, value: {})".format( - self.__class__.__name__, self.field, self.operator, self.value + self.__class__.__name__, self.field, self.operator, self.value_raw ) - def _find_data_in_kv(self, kv_list): - """Find a value for evaluation in a list of KeyValue - - Args: - kv_list (list[KeyValue]): list of KeyValue to look into - """ - key_parts = self.field.split(".") - field = key_parts[0] - kvs = [kv for kv in kv_list if kv.key == field] - if not kvs: - return None - - for kv in kvs: - if kv.key == self.field: - # exact match - return kv.value - elif len(key_parts) > 1: - data = kv.value - for subkey in key_parts[1:]: - try: - data = data.get(subkey) - except Exception: - data = None - break - if not data: - break - if data: - return data - - def _find_data(self, flow): - """Finds the key-value to use for evaluation""" - for section in flow.sections: - data = self._find_data_in_kv(section.data) - if data: - return data - return None - def evaluate(self, flow): - data = self._find_data(flow) - if not data: - return False + data = flow.info.get(self.field) or flow.match.get(self.field) - if not self.value and not self.operator: - # just asserting the existance of the key - return True - - # Decode the value based on the type of data - data_type = data.__class__.__name__ - decoder = self.type_decoders.get(data_type) or decode_default - decoded_value = decoder(self.value) + if not data: + # search in actions + act_parts = self.field.split(".") + act_name = act_parts[0] + actions = [act for act in flow.actions_kv if act.key == act_name] + if not actions: + return False + + # Look into arguments + for action in actions: + if action.key == self.field: + # exact match + data = action.value + break + elif len(act_parts) > 1: + data = action.value + for key in act_parts[1:]: + data = data.get(key) + if not data: + break + if not data: + return False if self.operator == "=": - return decoded_value == data + return self.value == data elif self.operator == "<": - return data < decoded_value + return data < self.value elif self.operator == ">": - return data > decoded_value + return data > self.value elif self.operator == "~=": - return decoded_value in data + return self.value in data class BoolNot: diff --git a/ovs_dbg/flow.py b/ovs_dbg/flow.py index 06552a3..40b4604 100644 --- a/ovs_dbg/flow.py +++ b/ovs_dbg/flow.py @@ -61,7 +61,7 @@ def __init__(self, sections, orig=""): def section(self, name): """Return the section by name""" - return next((sect for sect in self._sections if sect.name == name)) + return next((sect for sect in self._sections if sect.name == name), None) @property def sections(self): diff --git a/ovs_dbg/ofparse/main.py b/ovs_dbg/ofparse/main.py index f01a15d..75a3ae2 100644 --- a/ovs_dbg/ofparse/main.py +++ b/ovs_dbg/ofparse/main.py @@ -1,7 +1,7 @@ import click import sys -from ovs_dbg.ofparse.filter import OFFilter +from ovs_dbg.filter import OFFilter class Options(dict): diff --git a/tests/test_filter.py b/tests/test_filter.py index 8c04d4f..c936d4b 100644 --- a/tests/test_filter.py +++ b/tests/test_filter.py @@ -1,6 +1,6 @@ import pytest -from ovs_dbg.ofparse.filter import OFFilter +from ovs_dbg.filter import OFFilter from ovs_dbg.ofp import OFPFlow diff --git a/tests/test_odp.py b/tests/test_odp.py index ce05ff6..dea128a 100644 --- a/tests/test_odp.py +++ b/tests/test_odp.py @@ -143,8 +143,8 @@ def test_odp_fields(input_string, expected): assert expected[i].value == match[i].value # Assert positions relative to action string are OK - mpos = odp.meta.mpos - mstring = odp.meta.mstring + mpos = odp.section("match").pos + mstring = odp.section("match").string kpos = match[i].meta.kpos kstr = match[i].meta.kstring @@ -526,8 +526,8 @@ def test_odp_actions(input_string, expected): assert expected[i].value == actions[i].value # Assert positions relative to action string are OK - apos = odp.meta.apos - astring = odp.meta.astring + apos = odp.section("actions").pos + astring= odp.section("actions").string kpos = actions[i].meta.kpos kstr = actions[i].meta.kstring diff --git a/tests/test_ofp.py b/tests/test_ofp.py index bd58d3a..14db7d0 100644 --- a/tests/test_ofp.py +++ b/tests/test_ofp.py @@ -360,8 +360,8 @@ def test_act(input_string, expected): assert expected[i].value == actions[i].value # Assert positions relative to action string are OK - apos = ofp.meta.apos - astring = ofp.meta.astring + apos = ofp.section("actions").pos + astring = ofp.section("actions").string kpos = actions[i].meta.kpos kstr = actions[i].meta.kstring