Way enhanced -- loads more debug output, does deletes
ONLY LIGHTLY TESTED
diff --git a/tests/flow_query.py b/tests/flow_query.py
index 5b8cb3b..a09b886 100644
--- a/tests/flow_query.py
+++ b/tests/flow_query.py
@@ -3,6 +3,7 @@
Attempts to fill switch to capacity with randomized flows, and ensure that they all are read back correctly.
"""
+import math
import logging
@@ -63,44 +64,161 @@
return list
-def rand_dl_addr():
- return [random.randint(0, 255) & ~1, random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)]
+def rand_pick(list):
+ return list[random.randint(0, len(list) - 1)]
+def rand_dl_addr():
+ return [random.randint(0, 255) & ~1,
+ random.randint(0, 255),
+ random.randint(0, 255),
+ random.randint(0, 255),
+ random.randint(0, 255),
+ random.randint(0, 255)
+ ]
def rand_nw_addr():
return random.randint(0, (1 << 32) - 1)
+class flow_info:
+ # Members:
+ # priorities - list of flow priorities
+ # dl_addrs - list of MAC addresses
+ # vlans - list of VLAN ids
+ # ethertypes - list of Ethertypes
+ # ip_addrs - list of IP addresses
+ # ip_tos - list of IP TOS values
+ # ip_protos - list of IP protocols
+ # l4_ports - list of L4 ports
+
+ def __init__(self):
+ priorities = []
+ dl_addrs = []
+ vlans = []
+ ethertypes = []
+ ip_addrs = []
+ ip_tos = []
+ ip_protos = []
+ l4_ports = []
+
+ def rand(self, n):
+ self.priorities = []
+ i = 0
+ while i < n:
+ self.priorities.append(random.randint(1, 65534))
+ i = i + 1
+
+ self.dl_addrs = []
+ i = 0
+ while i < n:
+ self.dl_addrs.append(rand_dl_addr())
+ i = i + 1
+
+ self.vlans = []
+ i = 0
+ while i < n:
+ self.vlans.append(random.randint(1, 4094))
+ i = i + 1
+
+ self.ethertypes = []
+ i = 0
+ while i < n:
+ self.ethertypes.append(random.randint(0, (1 << 16) - 1))
+ i = i + 1
+
+ self.ip_addrs = []
+ i = 0
+ while i < n:
+ self.ip_addrs.append(rand_nw_addr())
+ i = i + 1
+
+ self.ip_tos = []
+ i = 0
+ while i < n:
+ self.ip_tos.append(random.randint(0, (1 << 8) - 1) & ~3)
+ i = i + 1
+
+ self.ip_protos = []
+ i = 0
+ while i < n:
+ self.ip_protos.append(random.randint(0, (1 << 8) - 1))
+ i = i + 1
+
+ self.l4_ports = []
+ i = 0
+ while i < n:
+ self.l4_ports.append(random.randint(0, (1 << 16) - 1))
+ i = i + 1
+
+ def rand_priority(self):
+ return rand_pick(self.priorities)
+
+ def rand_dl_addr(self):
+ return rand_pick(self.dl_addrs)
+
+ def rand_vlan(self):
+ return rand_pick(self.vlans)
+
+ def rand_ethertype(self):
+ return rand_pick(self.ethertypes)
+
+ def rand_ip_addr(self):
+ return rand_pick(self.ip_addrs)
+
+ def rand_ip_tos(self):
+ return rand_pick(self.ip_tos)
+
+ def rand_ip_proto(self):
+ return rand_pick(self.ip_protos)
+
+ def rand_l4_port(self):
+ return rand_pick(self.l4_ports)
+
+
# TBD - These don't belong here
-all_wildcards = [ofp.OFPFW_IN_PORT, \
- ofp.OFPFW_DL_VLAN, \
- ofp.OFPFW_DL_SRC, \
- ofp.OFPFW_DL_DST, \
- ofp.OFPFW_DL_TYPE, \
- ofp.OFPFW_NW_PROTO, \
- ofp.OFPFW_TP_SRC, \
- ofp.OFPFW_TP_DST, \
- ofp.OFPFW_NW_SRC_ALL, \
- ofp.OFPFW_NW_DST_ALL, \
- ofp.OFPFW_DL_VLAN_PCP, \
- ofp.OFPFW_NW_TOS \
- ]
+all_wildcards_list = [ofp.OFPFW_IN_PORT,
+ ofp.OFPFW_DL_VLAN,
+ ofp.OFPFW_DL_SRC,
+ ofp.OFPFW_DL_DST,
+ ofp.OFPFW_DL_TYPE,
+ ofp.OFPFW_NW_PROTO,
+ ofp.OFPFW_TP_SRC,
+ ofp.OFPFW_TP_DST,
+ ofp.OFPFW_NW_SRC_MASK,
+ ofp.OFPFW_NW_DST_MASK,
+ ofp.OFPFW_DL_VLAN_PCP,
+ ofp.OFPFW_NW_TOS
+ ]
-all_actions = [ofp.OFPAT_OUTPUT,
- ofp.OFPAT_SET_VLAN_VID,
- ofp.OFPAT_SET_VLAN_PCP,
- ofp.OFPAT_STRIP_VLAN,
- ofp.OFPAT_SET_DL_SRC,
- ofp.OFPAT_SET_DL_DST,
- ofp.OFPAT_SET_NW_SRC,
- ofp.OFPAT_SET_NW_DST,
- ofp.OFPAT_SET_NW_TOS,
- ofp.OFPAT_SET_TP_SRC,
- ofp.OFPAT_SET_TP_DST,
- ofp.OFPAT_ENQUEUE
- ]
+all_actions_list = [ofp.OFPAT_OUTPUT,
+ ofp.OFPAT_SET_VLAN_VID,
+ ofp.OFPAT_SET_VLAN_PCP,
+ ofp.OFPAT_STRIP_VLAN,
+ ofp.OFPAT_SET_DL_SRC,
+ ofp.OFPAT_SET_DL_DST,
+ ofp.OFPAT_SET_NW_SRC,
+ ofp.OFPAT_SET_NW_DST,
+ ofp.OFPAT_SET_NW_TOS,
+ ofp.OFPAT_SET_TP_SRC,
+ ofp.OFPAT_SET_TP_DST,
+ ofp.OFPAT_ENQUEUE
+ ]
+
+def dl_addr_to_str(a):
+ return "%x:%x:%x:%x:%x:%x" % tuple(a)
+
+def ip_addr_to_str(a, n):
+ result = "%d.%d.%d.%d" % (a >> 24, \
+ (a >> 16) & 0xff, \
+ (a >> 8) & 0xff, \
+ a & 0xff \
+ )
+ if n is not None:
+ result = result + ("/%d" % (n))
+ return result
+
class flow_cfg:
# Members:
@@ -111,133 +229,314 @@
# - action_list
def __init__(self):
+ self.priority = 0
self.match = parse.ofp_match()
self.match.wildcards = ofp.OFPFW_ALL
self.idle_timeout = 0
self.hard_timeout = 0
- self.priority = 0
self.actions = action_list.action_list()
def __eq__(self, x):
- if self.match != x.match:
+ if self.priority != x.priority:
+ return False
+ # TBD - Should this logic be moved to ofp_match.__eq__()?
+ if self.match.wildcards != x.match.wildcards:
+ return False
+ if (self.match.wildcards & ofp.OFPFW_IN_PORT) == 0 \
+ and self.match.in_port != x.match.in_port:
+ return False
+ if (self.match.wildcards & ofp.OFPFW_DL_SRC) == 0 \
+ and self.match.dl_src != x.match.dl_src:
+ return False
+ if (self.match.wildcards & ofp.OFPFW_DL_DST) == 0 \
+ and self.match.dl_dst != x.match.dl_dst:
+ return False
+ if (self.match.wildcards & ofp.OFPFW_DL_VLAN) == 0 \
+ and self.match.dl_vlan != x.match.dl_vlan:
+ return False
+ if (self.match.wildcards & ofp.OFPFW_DL_VLAN_PCP) == 0 \
+ and self.match.dl_vlan_pcp != x.match.dl_vlan_pcp:
+ return False
+ if (self.match.wildcards & ofp.OFPFW_DL_TYPE) == 0 \
+ and self.match.dl_type != x.match.dl_type:
+ return False
+ if (self.match.wildcards & ofp.OFPFW_NW_TOS) == 0 \
+ and self.match.nw_tos != x.match.nw_tos:
+ return False
+ if (self.match.wildcards & ofp.OFPFW_NW_PROTO) == 0 \
+ and self.match.nw_proto != x.match.nw_proto:
+ return False
+ if (self.match.wildcards & ofp.OFPFW_NW_SRC_MASK) \
+ < ofp.OFPFW_NW_SRC_ALL:
+ m = ~((1 << ((self.match.wildcards & ofp.OFPFW_NW_SRC_MASK) \
+ >> ofp.OFPFW_NW_SRC_SHIFT)) - 1)
+ if (self.match.nw_src & m) != (x.match.nw_src & m):
+ return False
+ if (self.match.wildcards & ofp.OFPFW_NW_DST_MASK) \
+ < ofp.OFPFW_NW_DST_ALL:
+ m = ~((1 << ((self.match.wildcards & ofp.OFPFW_NW_DST_MASK) \
+ >> ofp.OFPFW_NW_DST_SHIFT)) - 1)
+ if (self.match.nw_dst & m) != (x.match.nw_dst & m):
+ return False
+ if (self.match.wildcards & ofp.OFPFW_TP_SRC) == 0 \
+ and self.match.tp_src != x.match.tp_src:
+ return False
+ if (self.match.wildcards & ofp.OFPFW_TP_DST) == 0 \
+ and self.match.tp_dst != x.match.tp_dst:
return False
if self.idle_timeout != x.idle_timeout:
return False
if self.hard_timeout != x.hard_timeout:
return False
- if self.priority != x.priority:
- return False
return self.actions == x.actions # N.B. Action lists are ordered
- def rand(self, valid_wildcards, valid_actions, valid_ports):
- # TBD - Are IP addr wildcard specs all or nothing, valid wildcard reported as all 1s or all 0s?
- self.match.wildcards = valid_wildcards
- if (self.match.wildcards & ofp.OFPFW_NW_SRC_MASK) == ofp.OFPFW_NW_SRC_MASK:
- self.match.wildcards = (self.match.wildcards & ~ofp.OFPFW_NW_SRC_MASK) | ofp.OFPFW_NW_SRC_ALL
- if (self.match.wildcards & ofp.OFPFW_NW_DST_MASK) == ofp.OFPFW_NW_DST_MASK:
- self.match.wildcards = (self.match.wildcards & ~ofp.OFPFW_NW_DST_MASK) | ofp.OFPFW_NW_DST_ALL
+ def __str__(self):
+ result = "priority=%d" % self.priority
+ # TBD - Would be nice if ofp_match.show() was better behaved
+ # (no newlines), and more intuitive (things in hex where approprate), etc.
+ result = result + ", wildcards={"
+ sep = ""
+ for w in ofp.ofp_flow_wildcards_map:
+ if w == ofp.OFPFW_NW_SRC_SHIFT \
+ or w == ofp.OFPFW_NW_SRC_BITS \
+ or w == ofp.OFPFW_NW_SRC_ALL \
+ or w == ofp.OFPFW_NW_DST_SHIFT \
+ or w == ofp.OFPFW_NW_DST_BITS \
+ or w == ofp.OFPFW_NW_DST_ALL \
+ or w == ofp.OFPFW_ALL \
+ or self.match.wildcards & w == 0:
+ continue
+ if w == ofp.OFPFW_NW_SRC_MASK:
+ result = result + sep + "OFPFW_NW_SRC"
+ elif w == ofp.OFPFW_NW_DST_MASK:
+ result = result + sep + "OFPFW_NW_DST"
+ else:
+ result = result + sep + ofp.ofp_flow_wildcards_map[w]
+ sep = ", "
+ result = result +"}"
+ if (self.match.wildcards & ofp.OFPFW_IN_PORT) == 0:
+ result = result + (", in_port=%d" % (self.match.in_port))
+ if (self.match.wildcards & ofp.OFPFW_DL_SRC) == 0:
+ result = result + (", dl_src=%s" % (dl_addr_to_str(self.match.dl_src)))
+ if (self.match.wildcards & ofp.OFPFW_DL_DST) == 0:
+ result = result + (", dl_dst=%s" % (dl_addr_to_str(self.match.dl_dst)))
+ if (self.match.wildcards & ofp.OFPFW_DL_VLAN) == 0:
+ result = result + (", dl_vlan=%d" % (self.match.dl_vlan))
+ if (self.match.wildcards & ofp.OFPFW_DL_VLAN_PCP) == 0:
+ result = result + (", dl_vlan_pcp=%d" % (self.match.dl_vlan_pcp))
+ if (self.match.wildcards & ofp.OFPFW_DL_TYPE) == 0:
+ result = result + (", dl_type=0x%x" % (self.match.dl_type))
+ if (self.match.wildcards & ofp.OFPFW_NW_TOS) == 0:
+ result = result + (", nw_tos=0x%x" % (self.match.nw_tos))
+ if (self.match.wildcards & ofp.OFPFW_NW_PROTO) == 0:
+ result = result + (", nw_proto=%d" % (self.match.nw_proto))
+ n = (self.match.wildcards & ofp.OFPFW_NW_SRC_MASK) >> ofp.OFPFW_NW_SRC_SHIFT
+ if n < 32:
+ result = result + (", nw_src=%s" % (ip_addr_to_str(self.match.nw_src, n)))
+ n = (self.match.wildcards & ofp.OFPFW_NW_DST_MASK) >> ofp.OFPFW_NW_DST_SHIFT
+ if n < 32:
+ result = result + (", nw_dst=%s" % (ip_addr_to_str(self.match.nw_dst, n)))
+ if (self.match.wildcards & ofp.OFPFW_TP_SRC) == 0:
+ result = result + (", tp_src=%d" % self.match.tp_src)
+ if (self.match.wildcards & ofp.OFPFW_TP_DST) == 0:
+ result = result + (", tp_dst=%d" % self.match.tp_dst)
+ result = result + (", idle_timeout=%d" % self.idle_timeout)
+ result = result + (", hard_timeout=%d" % self.hard_timeout)
+ result = result + (", hard_timeout=%d" % self.hard_timeout)
+ for a in self.actions.actions:
+ result = result + (", action=%s" % ofp.ofp_action_type_map[a.type])
+ if a.type == ofp.OFPAT_OUTPUT:
+ result = result + ("(%d)" % (a.port))
+ elif a.type == ofp.OFPAT_SET_VLAN_VID:
+ result = result + ("(%d)" % (a.vlan_vid))
+ elif a.type == ofp.OFPAT_SET_VLAN_PCP:
+ result = result + ("(%d)" % (a.vlan_pcp))
+ elif a.type == ofp.OFPAT_SET_DL_SRC or a.type == ofp.OFPAT_SET_DL_DST:
+ result = result + ("(%s)" % (dl_addr_to_str(a.dl_addr)))
+ elif a.type == ofp.OFPAT_SET_NW_SRC or a.type == ofp.OFPAT_SET_NW_DST:
+ result = result + ("(%s)" % (ip_addr_to_str(a.nw_addr, None)))
+ elif a.type == ofp.OFPAT_SET_NW_TOS:
+ result = result + ("(0x%x)" % (a.nw_tos))
+ elif a.type == ofp.OFPAT_SET_TP_SRC or a.type == ofp.OFPAT_SET_TP_DST:
+ result = result + ("(%d)" % (a.tp_port))
+ elif a.type == ofp.OFPAT_ENQUEUE:
+ result = result + ("(port=%d,queue=%d)" % (a.port, a.queue_id))
+ return result
+ def rand(self, fi, valid_wildcards, valid_actions, valid_ports):
+ # Start with no wildcards, i.e. everything specified
+ self.match.wildcards = 0
+
+ # Make approx. 1% of flows exact
exact = True if random.randint(1, 100) == 1 else False
- for w in all_wildcards:
- if not exact and (w & valid_wildcards) != 0:
- if random.randint(1, 100) <= 50:
- continue
+ # For each qualifier Q,
+ # if (wildcarding is not supported for Q,
+ # or an exact flow is specified
+ # or a coin toss comes up heads),
+ # specify Q
+ # else
+ # wildcard Q
- if w == ofp.OFPFW_IN_PORT:
- self.match.in_port = valid_ports[random.randint(0, len(valid_ports) - 1)].port_no
- self.match.wildcards = self.match.wildcards & ~w
- elif w == ofp.OFPFW_DL_VLAN:
- self.match.vl_vlan = random.randint(1, 4094)
- self.match.wildcards = self.match.wildcards & ~w
- elif w == ofp.OFPFW_DL_SRC:
- self.match.dl_src = rand_dl_addr()
- self.match.wildcards = self.match.wildcards & ~w
- elif w == ofp.OFPFW_DL_DST:
- self.match.dl_dst = rand_dl_addr()
- self.match.wildcards = self.match.wildcards & ~w
- elif w == ofp.OFPFW_DL_TYPE:
- if (self.match.wildcards & w) != 0:
- self.match.dl_type = random.randint(0, (1 << 16) - 1)
- self.match.wildcards = self.match.wildcards & ~w
- elif w == ofp.OFPFW_NW_PROTO:
- if (self.match.wildcards & w) != 0:
- self.match.nw_proto = random.randint(0, (1 << 8) - 1)
- self.match.wildcards = self.match.wildcards & ~w
- self.match.dl_type = 0x0800
- self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
- elif w == ofp.OFPFW_TP_SRC:
- self.match.tp_src = random.randint(0, (1 << 16) - 1)
- self.match.wildcards = self.match.wildcards & ~w
- self.match.nw_proto = [1, 6, 17][random.randint(0, 2)]
- self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_NW_PROTO
- self.match.dl_type = 0x0800
- self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
- elif w == ofp.OFPFW_TP_DST:
- self.match.tp_dst = random.randint(0, (1 << 16) - 1)
- self.match.wildcards = self.match.wildcards & ~w
- self.match.nw_proto = [1, 6, 17][random.randint(0, 2)]
- self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_NW_PROTO
- self.match.dl_type = 0x0800
- self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
- elif w == ofp.OFPFW_NW_SRC_MASK:
- n = 0 if exact else random.randint(0, 31)
- self.match.nw_src = rand_nw_addr() & ~((1 << n) - 1)
- self.match.wildcards = (self.match.wildcards & ~w) | (n << ofp.OFPFW_NW_SRC_SHIFT)
- self.match.dl_type = [0x0800, 0x0806][random.randint(0, 1)]
- self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
- elif w == ofp.OFPFW_NW_DST_MASK:
- n = 0 if exact else random.randint(0, 31)
- self.match.nw_dst = rand_nw_addr() & ~((1 << n) - 1)
- self.match.wildcards = (self.match.wildcards & ~w) | (n << ofp.OFPFW_NW_DST_SHIFT)
- self.match.dl_type = [0x0800, 0x0806][random.randint(0, 1)]
- self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
- elif w == ofp.OFPFW_DL_VLAN_PCP:
- self.match.dl_vlan_pcp = random.randint(0, (1 << 3) - 1)
- self.match.wildcards = self.match.wildcards & ~w
- elif w == ofp.OFPFW_NW_TOS:
- while True:
- self.match.nw_tos = random.randint(0, (1 << 8) - 1)
- if (self.match.nw_tos & 3) == 0:
- break
- self.match.wildcards = self.match.wildcards & ~w
- self.match.dl_type = 0x0800
- self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
+ if (ofp.OFPFW_IN_PORT & valid_wildcards) == 0 \
+ or exact \
+ or random.randint(1, 100) <= 50:
+ self.match.in_port = rand_pick(valid_ports)
+ else:
+ self.match.wildcards = self.match.wildcards | ofp.OFPFW_IN_PORT
+
+ if (ofp.OFPFW_DL_DST & valid_wildcards) == 0 \
+ or exact \
+ or random.randint(1, 100) <= 50:
+ self.match.dl_dst = fi.rand_dl_addr()
+ else:
+ self.match.wildcards = self.match.wildcards | ofp.OFPFW_DL_DST
- # N.B. Don't make the timeout too short, else the flow might disappear before we
- # get a chance to check for it.
+ if (ofp.OFPFW_DL_SRC & valid_wildcards) == 0 \
+ or exact \
+ or random.randint(1, 100) <= 50:
+ self.match.dl_src = fi.rand_dl_addr()
+ else:
+ self.match.wildcards = self.match.wildcards | ofp.OFPFW_DL_SRC
+
+ if (ofp.OFPFW_DL_VLAN_PCP & valid_wildcards) == 0 \
+ or exact \
+ or random.randint(1, 100) <= 50:
+ self.match.dl_vlan_pcp = random.randint(0, (1 << 3) - 1)
+ else:
+ self.match.wildcards = self.match.wildcards | ofp.OFPFW_DL_VLAN_PCP
+
+ if (ofp.OFPFW_DL_VLAN & valid_wildcards) == 0 \
+ or exact \
+ or random.randint(1, 100) <= 50:
+ self.match.dl_vlan = fi.rand_vlan()
+ else:
+ self.match.wildcards = self.match.wildcards | ofp.OFPFW_DL_VLAN
+
+ if (ofp.OFPFW_DL_TYPE & valid_wildcards) == 0 \
+ or exact \
+ or random.randint(1, 100) <= 50:
+ self.match.dl_type = fi.rand_ethertype()
+ else:
+ self.match.wildcards = self.match.wildcards | ofp.OFPFW_DL_TYPE
+
+ if exact:
+ n = 0
+ else:
+ n = (valid_wildcards & ofp.OFPFW_NW_SRC_MASK) \
+ >> ofp.OFPFW_NW_SRC_SHIFT
+ if n > 32:
+ n = 32
+ n = random.randint(0, n)
+ self.match.wildcards = self.match.wildcards \
+ | (n << ofp.OFPFW_NW_SRC_SHIFT)
+ if n < 32:
+ self.match.nw_src = fi.rand_ip_addr() & ~((1 << n) - 1)
+ # Specifying any IP address match other than all bits
+ # don't care requires that Ethertype is one of {IP, ARP}
+ self.match.dl_type = rand_pick([0x0800, 0x0806])
+ self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
+
+ if exact:
+ n = 0
+ else:
+ n = (valid_wildcards & ofp.OFPFW_NW_DST_MASK) \
+ >> ofp.OFPFW_NW_DST_SHIFT
+ if n > 32:
+ n = 32
+ n = random.randint(0, n)
+ self.match.wildcards = self.match.wildcards \
+ | (n << ofp.OFPFW_NW_DST_SHIFT)
+ if n < 32:
+ self.match.nw_dst = fi.rand_ip_addr() & ~((1 << n) - 1)
+ # Specifying any IP address match other than all bits
+ # don't care requires that Ethertype is one of {IP, ARP}
+ self.match.dl_type = rand_pick([0x0800, 0x0806])
+ self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
+
+ if (ofp.OFPFW_NW_TOS & valid_wildcards) == 0 \
+ or exact \
+ or random.randint(1, 100) <= 50:
+ self.match.nw_tos = fi.rand_ip_tos()
+ # Specifying a TOS value requires that Ethertype is IP
+ self.match.dl_type = 0x0800
+ self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
+ else:
+ self.match.wildcards = self.match.wildcards | ofp.OFPFW_NW_TOS
+
+ if (ofp.OFPFW_NW_PROTO & valid_wildcards) == 0 \
+ or exact \
+ or random.randint(1, 100) <= 50:
+ self.match.nw_proto = fi.rand_ip_proto()
+ # Specifying an IP protocol requires that Ethertype is IP
+ self.match.dl_type = 0x0800
+ self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
+ else:
+ self.match.wildcards = self.match.wildcards | ofp.OFPFW_NW_PROTO
+
+ if (ofp.OFPFW_TP_SRC & valid_wildcards) == 0 \
+ or exact\
+ or random.randint(1, 100) <= 50:
+ self.match.tp_src = fi.rand_l4_port()
+ # Specifying a L4 port requires that IP protcol is
+ # one of {ICMP, TCP, UDP}
+ self.match.nw_proto = rand_pick([1, 6, 17])
+ self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_NW_PROTO
+ # Specifying a L4 port requirues that Ethertype is IP
+ self.match.dl_type = 0x0800
+ self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
+ else:
+ self.match.wildcards = self.match.wildcards | ofp.OFPFW_TP_SRC
+
+ if (ofp.OFPFW_TP_DST & valid_wildcards) == 0 \
+ or exact \
+ or random.randint(1, 100) <= 50:
+ self.match.tp_dst = fi.rand_l4_port()
+ # Specifying a L4 port requires that IP protcol is
+ # one of {ICMP, TCP, UDP}
+ self.match.nw_proto = rand_pick([1, 6, 17])
+ self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_NW_PROTO
+ # Specifying a L4 port requirues that Ethertype is IP
+ self.match.dl_type = 0x0800
+ self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
+ else:
+ self.match.wildcards = self.match.wildcards | ofp.OFPFW_TP_DST
+
+ # N.B. Don't make the timeout too short, else the flow might
+ # disappear before we get a chance to check for it.
t = random.randint(0, 65535)
self.idle_timeout = 0 if t < 60 else t
t = random.randint(0, 65535)
self.hard_timeout = 0 if t < 60 else t
- self.priority = 65535 if exact else random.randint(1, 65535)
- # N.B. Action lists are ordered
- supported_action_idxs = []
- ai = 0
- while ai < len(all_actions):
- if ((1 << all_actions[ai]) & valid_actions) != 0:
- supported_action_idxs.append(ai)
- ai = ai + 1
+ # If nothing is wildcarded, it is an exact flow spec -- some switches
+ # (Open vSwitch, for one) *require* that exact flow specs have priority 65535.
+ self.priority = 65535 if self.match.wildcards == 0 else fi.rand_priority()
- supported_action_idxs = shuffle(supported_action_idxs)
- supported_action_idxs = supported_action_idxs[0 : random.randint(1, len(supported_action_idxs) - 1)]
+ # Action lists are ordered, so pick an ordered random subset of
+ # supported actions
+ supported_actions = []
+ for a in all_actions_list:
+ if ((1 << a) & valid_actions) != 0:
+ supported_actions.append(a)
+
+ supported_actions = shuffle(supported_actions)
+ supported_actions \
+ = supported_actions[0 : random.randint(1, len(supported_actions))]
self.actions = action_list.action_list()
- for ai in supported_action_idxs:
- a = all_actions[ai]
-
+ for a in supported_actions:
if a == ofp.OFPAT_OUTPUT:
# TBD - Output actions are clustered in list, spread them out?
port_idxs = shuffle(range(len(valid_ports)))
- port_idxs = port_idxs[0 : random.randint(1, len(valid_ports) - 1)]
+ port_idxs = port_idxs[0 : random.randint(1, len(valid_ports))]
for pi in port_idxs:
act = action.action_output()
- act.port = valid_ports[pi].port_no
+ act.port = valid_ports[pi]
self.actions.add(act)
elif a == ofp.OFPAT_SET_VLAN_VID:
act = action.action_set_vlan_vid()
- act.vlan_vid = random.randint(1, 4094)
+ act.vlan_vid = fi.rand_vlan()
self.actions.add(act)
elif a == ofp.OFPAT_SET_VLAN_PCP:
# TBD - Temporaily removed, broken in Indigo
@@ -249,81 +548,135 @@
self.actions.add(act)
elif a == ofp.OFPAT_SET_DL_SRC:
act = action.action_set_dl_src()
- act.dl_addr = rand_dl_addr()
+ act.dl_addr = fi.rand_dl_addr()
self.actions.add(act)
elif a == ofp.OFPAT_SET_DL_DST:
act = action.action_set_dl_dst()
- act.dl_addr = rand_dl_addr()
+ act.dl_addr = fi.rand_dl_addr()
self.actions.add(act)
elif a == ofp.OFPAT_SET_NW_SRC:
act = action.action_set_nw_src()
- act.nw_addr = rand_nw_addr()
+ act.nw_addr = fi.rand_ip_addr()
self.actions.add(act)
elif a == ofp.OFPAT_SET_NW_DST:
act = action.action_set_nw_dst()
- act.nw_addr = rand_nw_addr()
+ act.nw_addr = fi.rand_ip_addr()
self.actions.add(act)
elif a == ofp.OFPAT_SET_NW_TOS:
act = action.action_set_nw_tos()
- act.nw_tos = random.randint(0, (1 << 8) - 1)
+ act.nw_tos = fi.rand_ip_tos()
self.actions.add(act)
elif a == ofp.OFPAT_SET_TP_SRC:
act = action.action_set_tp_src()
- act.tp_port = random.randint(0, (1 << 16) - 1)
+ act.tp_port = fi.rand_l4_port()
self.actions.add(act)
elif a == ofp.OFPAT_SET_TP_DST:
act = action.action_set_tp_dst()
- act.tp_port = random.randint(0, (1 << 16) - 1)
+ act.tp_port = fi.rand_l4_port()
self.actions.add(act)
elif a == ofp.OFPAT_ENQUEUE:
# TBD - Enqueue actions are clustered in list, spread them out?
port_idxs = shuffle(range(len(valid_ports)))
- port_idxs = port_idxs[0 : random.randint(1, len(valid_ports) - 1)]
+ port_idxs = port_idxs[0 : random.randint(1, len(valid_ports))]
for pi in port_idxs:
act = action.action_enqueue()
- act.port = valid_ports[pi].port_no
+ act.port = valid_ports[pi]
# TBD - Limits for queue number?
act.queue_id = random.randint(0, 7)
self.actions.add(act)
return self
- def overlap(self, x):
+ # Overlap check
+ # delf == True <=> Check for delete overlap, else add overlap
+ # "Add overlap" is defined as there exists a packet that could match both the
+ # receiver and argument flowspecs
+ # "Delete overlap" is defined as the specificity of the argument flowspec
+ # is greater than or equal to the specificity of the receiver flowspec
+ def overlaps(self, x, delf):
if self.priority != x.priority:
return False
- if (self.match.wildcards & ofp.OFPFW_IN_PORT) == 0 and (x.match.wildcards & ofp.OFPFW_IN_PORT) == 0 and self.match.in_port != x.match.in_port:
- return False
- if (self.match.wildcards & ofp.OFPFW_DL_VLAN) == 0 and (x.match.wildcards & ofp.OFPFW_DL_VLAN) == 0 and self.match.dl_vlan != x.match.dl_vlan:
- return False
- if (self.match.wildcards & ofp.OFPFW_DL_SRC) == 0 and (x.match.wildcards & ofp.OFPFW_DL_SRC) == 0 and self.match.dl_src != x.match.dl_src:
- return False
- if (self.match.wildcards & ofp.OFPFW_DL_DST) == 0 and (x.match.wildcards & ofp.OFPFW_DL_DST) == 0 and self.match.dl_dst != x.match.dl_dst:
- return False
- if (self.match.wildcards & ofp.OFPFW_DL_TYPE) == 0 and (x.match.wildcards & ofp.OFPFW_DL_TYPE) == 0 and self.match.dl_type != x.match.dl_type:
- return False
- if (self.match.wildcards & ofp.OFPFW_NW_PROTO) == 0 and (x.match.wildcards & ofp.OFPFW_NW_PROTO) == 0 and self.match.nw_proto != x.match.nw_proto:
- return False
- if (self.match.wildcards & ofp.OFPFW_TP_SRC) == 0 and (x.match.wildcards & ofp.OFPFW_TP_SRC) == 0 and self.match.tp_src != x.match.tp_src:
- return False
- if (self.match.wildcards & ofp.OFPFW_TP_DST) == 0 and (x.match.wildcards & ofp.OFPFW_TP_DST) == 0 and self.match.tp_dst != x.match.tp_dst:
- return False
- na = (self.match.wildcards & ofp.OFPFW_NW_SRC_MASK) >> ofp.OFPFW_NW_SRC_SHIFT
- nb = (x.match.wildcards & ofp.OFPFW_NW_SRC_MASK) >> ofp.OFPFW_NW_SRC_SHIFT
+ if (self.match.wildcards & ofp.OFPFW_IN_PORT) == 0:
+ if (x.match.wildcards & ofp.OFPFW_IN_PORT) == 0:
+ if self.match.in_port != x.match.in_port:
+ return False # Both specified, and not equal
+ elif delf:
+ return False # Receiver more specific
+ if (self.match.wildcards & ofp.OFPFW_DL_VLAN) == 0:
+ if (x.match.wildcards & ofp.OFPFW_DL_VLAN) == 0:
+ if self.match.dl_vlan != x.match.dl_vlan:
+ return False # Both specified, and not equal
+ elif delf:
+ return False # Receiver more specific
+ if (self.match.wildcards & ofp.OFPFW_DL_SRC) == 0:
+ if (x.match.wildcards & ofp.OFPFW_DL_SRC) == 0:
+ if self.match.dl_src != x.match.dl_src:
+ return False # Both specified, and not equal
+ elif delf:
+ return False # Receiver more specific
+ if (self.match.wildcards & ofp.OFPFW_DL_DST) == 0:
+ if (x.match.wildcards & ofp.OFPFW_DL_DST) == 0:
+ if self.match.dl_dst != x.match.dl_dst:
+ return False # Both specified, and not equal
+ elif delf:
+ return False # Receiver more specific
+ if (self.match.wildcards & ofp.OFPFW_DL_TYPE) == 0:
+ if (x.match.wildcards & ofp.OFPFW_DL_TYPE) == 0:
+ if self.match.dl_type != x.match.dl_type:
+ return False # Both specified, and not equal
+ elif delf:
+ return False # Recevier more specific
+ if (self.match.wildcards & ofp.OFPFW_NW_PROTO) == 0:
+ if (x.match.wildcards & ofp.OFPFW_NW_PROTO) == 0:
+ if self.match.nw_proto != x.match.nw_proto:
+ return False # Both specified, and not equal
+ elif delf:
+ return False # Receiver more specific
+ if (self.match.wildcards & ofp.OFPFW_TP_SRC) == 0:
+ if (x.match.wildcards & ofp.OFPFW_TP_SRC) == 0:
+ if self.match.tp_src != x.match.tp_src:
+ return False # Both specified, and not equal
+ elif delf:
+ return False # Receiver more specific
+ if (self.match.wildcards & ofp.OFPFW_TP_DST) == 0:
+ if (x.match.wildcards & ofp.OFPFW_TP_DST) == 0:
+ if self.match.tp_dst != x.match.tp_dst:
+ return False # Both specified, and not equal
+ elif delf:
+ return False # Receiver more specific
+ na = (self.match.wildcards & ofp.OFPFW_NW_SRC_MASK) \
+ >> ofp.OFPFW_NW_SRC_SHIFT
+ nb = (x.match.wildcards & ofp.OFPFW_NW_SRC_MASK) \
+ >> ofp.OFPFW_NW_SRC_SHIFT
+ if delf and na < nb:
+ return False # Receiver more specific
if (na < 32 and nb < 32):
m = ~((1 << na) - 1) & ~((1 << nb) - 1)
if (self.match.nw_src & m) != (x.match.nw_src & m):
- return False
- na = (self.match.wildcards & ofp.OFPFW_NW_DST_MASK) >> ofp.OFPFW_NW_DST_SHIFT
- nb = (x.match.wildcards & ofp.OFPFW_NW_DST_MASK) >> ofp.OFPFW_NW_DST_SHIFT
+ return False # Overlapping bits not equal
+ na = (self.match.wildcards & ofp.OFPFW_NW_DST_MASK) \
+ >> ofp.OFPFW_NW_DST_SHIFT
+ nb = (x.match.wildcards & ofp.OFPFW_NW_DST_MASK) \
+ >> ofp.OFPFW_NW_DST_SHIFT
+ if delf and na < nb:
+ return False # Receiver more specific
if (na < 32 and nb < 32):
m = ~((1 << na) - 1) & ~((1 << nb) - 1)
if (self.match.nw_dst & m) != (x.match.nw_dst & m):
- return False
- if (self.match.wildcards & ofp.OFPFW_DL_VLAN_PCP) == 0 and (x.match.wildcards & ofp.OFPFW_DL_VLAN_PCP) == 0 and self.match.dl_vlan_pcp != x.match.dl_vlan_pcp:
- return False
- if (self.match.wildcards & ofp.OFPFW_NW_TOS) == 0 and (x.match.wildcards & ofp.OFPFW_NW_TOS) == 0 and self.match.nw_tos != x.match.nw_tos:
- return False
- return True
+ return False # Overlapping bit not equal
+ if (self.match.wildcards & ofp.OFPFW_DL_VLAN_PCP) == 0:
+ if (x.match.wildcards & ofp.OFPFW_DL_VLAN_PCP) == 0:
+ if self.match.dl_vlan_pcp != x.match.dl_vlan_pcp:
+ return False # Both specified, and not equal
+ elif delf:
+ return False # Receiver more specific
+ if (self.match.wildcards & ofp.OFPFW_NW_TOS) == 0:
+ if (x.match.wildcards & ofp.OFPFW_NW_TOS) == 0:
+ if self.match.nw_tos != x.match.nw_tos:
+ return False # Both specified, and not equal
+ elif delf:
+ return False # Receiver more specific
+ return True # Flows overlap
def to_flow_mod_msg(self, msg):
msg.match = self.match
@@ -345,7 +698,211 @@
"""
"""
- def test1(self, overlapf):
+ def do_barrier(self):
+ barrier = message.barrier_request()
+ (resp, pkt) = self.controller.transact(barrier, 5)
+ self.assertTrue(resp is not None,
+ "Did not receive response to barrier request"
+ )
+
+
+ def verify_flows(self,
+ sw_features,
+ tbl_flows,
+ num_flows,
+ overlapf,
+ num_overlaps
+ ):
+ result = True
+
+ # Check number of flows reported in table stats
+
+ self.logger.debug("Verifying table stats reports correct number of")
+ self.logger.debug(" active flows")
+ request = message.table_stats_request()
+ (tbl_stats_after, pkt) = self.controller.transact(request, timeout=2)
+ self.assertTrue(tbl_stats_after is not None,
+ "No reply to table_stats_request"
+ )
+
+ num_flows_reported = 0
+ for ts in tbl_stats_after.stats:
+ num_flows_reported = num_flows_reported + ts.active_count
+
+ num_flows_expected = num_flows
+ if overlapf:
+ num_flows_expected = num_flows_expected - num_overlaps
+
+ self.logger.debug("Number of flows reported = "
+ + str(num_flows_reported)
+ )
+ self.logger.debug("Numer of flows expected = "
+ + str(num_flows_expected)
+ )
+ if num_flows_reported != num_flows_expected:
+ self.logger.error("Incorrect number of flows returned by table stats")
+ result = False
+
+ # Retrieve all flows from switch
+
+ self.logger.debug("Retrieving all flows from switch")
+ stat_req = message.flow_stats_request()
+ query_match = ofp.ofp_match()
+ query_match.wildcards = ofp.OFPFW_ALL
+ stat_req.match = query_match
+ stat_req.table_id = 0xff
+ stat_req.out_port = ofp.OFPP_NONE;
+ flow_stats, pkt = self.controller.transact(stat_req, timeout=2)
+ self.assertTrue(flow_stats is not None, "Get all flow stats failed")
+
+ # Verify retrieved flows
+
+ self.logger.debug("Verifying retrieved flows")
+
+ self.assertEqual(flow_stats.type,
+ ofp.OFPST_FLOW,
+ "Unexpected type of response message"
+ )
+
+ num_flows_reported = len(flow_stats.stats)
+
+ self.logger.debug("Number of flows reported = "
+ + str(num_flows_reported)
+ )
+ self.logger.debug("Numer of flows expected = "
+ + str(num_flows_expected)
+ )
+ if num_flows_reported != num_flows_expected:
+ self.logger.error("Incorrect number of flows returned by table stats")
+ result = False
+
+ for f in tbl_flows:
+ f.resp_matched = False
+
+ num_resp_flows_matched = 0
+ for flow_stat in flow_stats.stats:
+ flow_in = flow_cfg()
+ flow_in.from_flow_stat(flow_stat)
+
+ matched = False
+ for f in tbl_flows:
+ if f.deleted:
+ continue
+ if not f.resp_matched \
+ and (not overlapf or not f.overlap) \
+ and f == flow_in:
+ f.resp_matched = True
+ num_resp_flows_matched = num_resp_flows_matched + 1
+ matched = True
+ break
+ if not matched:
+ self.logger.error("Response flow")
+ self.logger.error(str(flow_in))
+ self.logger.error("does not match any configured flow")
+ result = False
+
+ self.logger.debug("Number of flows matched in response = "
+ + str(num_resp_flows_matched)
+ )
+ self.logger.debug("Number of flows expected = "
+ + str(num_flows_expected)
+ )
+ if num_resp_flows_matched != num_flows_expected:
+ for f in tbl_flows:
+ if not f.resp_matched:
+ self.logger.error("Configured flow")
+ self.logger.error("tbl_idx=%d, flow_idx=%d, %s" % (f.tbl_idx, f.flow_idx, str(f)))
+ self.logger.error("missing in flow response")
+ result = False
+
+ self.assertTrue(result, "Flow verification failed")
+
+ def flow_add(self, flow, overlapf):
+ flow_mod_msg = message.flow_mod()
+ flow_mod_msg.command = ofp.OFPFC_ADD
+ flow_mod_msg.buffer_id = 0xffffffff
+ flow_mod_msg.cookie = random.randint(0, (1 << 53) - 1)
+ flow.to_flow_mod_msg(flow_mod_msg)
+ if overlapf:
+ flow_mod_msg.flags = flow_mod_msg.flags | ofp.OFPFF_CHECK_OVERLAP
+ self.logger.debug("Sending flow_mod(add) request to switch")
+ rv = self.controller.message_send(flow_mod_msg)
+ self.assertTrue(rv != -1, "Error installing flow mod")
+
+ # TBD - Don't poll for each error message
+ if flow.overlap:
+ self.logger.debug("Flow overlaps with tbl_idx=%d flow_idx=%d"
+ % (flow.overlaps_with[0], flow.overlaps_with[1])
+ )
+ else:
+ self.logger.debug("Flow does not overlap")
+ self.logger.debug("Checking for error response from switch")
+ (errmsg, pkt) = self.controller.poll(ofp.OFPT_ERROR, 1)
+ if errmsg is not None:
+ # Got ERROR message
+ self.logger.debug("Got ERROR message, type = "
+ + str(errmsg.type)
+ + ", code = "
+ + str(errmsg.code)
+ )
+
+ if errmsg.type == ofp.OFPET_FLOW_MOD_FAILED \
+ and errmsg.code == ofp.OFPFMFC_OVERLAP:
+ # Got "overlap" ERROR message
+ self.logger.debug("ERROR is overlap")
+
+ self.assertTrue(overlapf and flow.overlap,
+ "Overlap not expected"
+ )
+ else:
+ self.logger.debug("ERROR is not overlap")
+ self.assertTrue(False,
+ "Unexpected error message")
+
+ else:
+ # Did not get ERROR message
+ self.logger.debug("No ERROR message received")
+ self.assertTrue(not (overlapf and flow.overlap),
+ "Did not get expected OVERLAP"
+ )
+
+
+ def flow_del(self, flow, strictf):
+ flow_mod_msg = message.flow_mod()
+ flow_mod_msg.command = ofp.OFPFC_DELETE_STRICT \
+ if strictf else ofp.OFPFC_DELETE
+ flow_mod_msg.buffer_id = 0xffffffff
+ flow_mod_msg.cookie = random.randint(0, (1 << 53) - 1)
+ # TBD - Needs to be a test variable
+ flow_mod_msg.out_port = ofp.OFPP_NONE
+ flow.to_flow_mod_msg(flow_mod_msg)
+ rv = self.controller.message_send(flow_mod_msg)
+ self.assertTrue(rv != -1, "Error installing flow mod")
+ # TBD - Don't poll for each error message
+ (errmsg, pkt) = self.controller.poll(ofp.OFPT_ERROR, 1)
+ if errmsg is not None:
+ # Got ERROR message
+ self.logger.debug("Got ERROR message, type = "
+ + str(errmsg.type)
+ + ", code = "
+ + str(errmsg.code)
+ )
+ self.assertTrue(False,
+ "Unexpected error message"
+ )
+
+
+ # Add flows to capacity, make sure they can be read back, and delete them
+
+ def test1(self,
+ overlapf, # True <=> When sending flow adds to
+ # switch, include the "check for
+ # overlap" flag, and verify that an
+ # error message is received
+ # if an overlapping flow is defined
+ strictf # True <=> When deleting flows, delete
+ # them strictly
+ ):
"""
"""
@@ -365,26 +922,59 @@
self.logger.debug("Switch features -")
self.logger.debug("Number of tables: " + str(sw_features.n_tables))
self.logger.debug("Supported actions: " + hex(sw_features.actions))
- self.logger.debug("Ports: " + str(map(lambda x: x.port_no, sw_features.ports)))
+ self.logger.debug("Ports: "
+ + str(map(lambda x: x.port_no, sw_features.ports))
+ )
# For each table, get wildcards supported maximum number of flows
self.logger.debug("Retrieving table stats from switch")
request = message.table_stats_request()
(tbl_stats, pkt) = self.controller.transact(request, timeout=2)
- self.assertTrue(tbl_stats is not None, "No reply to table_stats_request")
+ self.assertTrue(tbl_stats is not None,
+ "No reply to table_stats_request"
+ )
active_count = 0
+ max_entries = 0
tbl_idx = 0
while tbl_idx < sw_features.n_tables:
self.logger.debug("Table " + str(tbl_idx) + " - ")
- self.logger.debug("Supported wildcards: " + hex(tbl_stats.stats[tbl_idx].wildcards))
- self.logger.debug("Max entries: " + str(tbl_stats.stats[tbl_idx].max_entries))
- self.logger.debug("Active count: " + str(tbl_stats.stats[tbl_idx].active_count))
+ self.logger.debug("Supported wildcards: "
+ + hex(tbl_stats.stats[tbl_idx].wildcards)
+ )
+ self.logger.debug("Max entries: "
+ + str(tbl_stats.stats[tbl_idx].max_entries)
+ )
+ self.logger.debug("Active count: "
+ + str(tbl_stats.stats[tbl_idx].active_count)
+ )
+ max_entries = max_entries + tbl_stats.stats[tbl_idx].max_entries
active_count = active_count + tbl_stats.stats[tbl_idx].active_count
tbl_idx = tbl_idx + 1
- self.assertEqual(active_count, 0, "Total number of active entries not 0 -- delete all flow failed?")
+ self.logger.debug("Total active entries = "
+ + str(active_count)
+ )
+ self.assertEqual(active_count,
+ 0,
+ "Delete all flows failed"
+ )
+ # TBD - For testing only, since Open vSWitch reports
+ # ridiculously large capacity; remove
+ sw_features.n_tables = 1
+ tbl_stats.stats[0].max_entries = 10
+ max_entries = tbl_stats.stats[0].max_entries
+
+ # Dream up some flow information, i.e. space to chose from for
+ # random flow parameter generation
+ fi = flow_info()
+ n = int(math.log(max_entries))
+ if not overlapf:
+ # Generated space smaller when testing overlaps,
+ # to increase likelihood of some
+ n = 2 * n
+ fi.rand(n)
# For each table, think up flows to fill it
@@ -393,134 +983,219 @@
num_overlaps = 0
tbl_flows = []
tbl_idx = 0
+
while tbl_idx < sw_features.n_tables:
- flow_cfgs = []
flow_idx = 0
while flow_idx < tbl_stats.stats[tbl_idx].max_entries:
- flow_out = flow_cfg().rand(tbl_stats.stats[tbl_idx].wildcards, sw_features.actions, sw_features.ports)
- j = 0
- while j < len(flow_cfgs):
- if flow_out.overlap(flow_cfgs[j]):
- break
- j = j + 1
- if j < len(flow_cfgs):
- num_overlaps = num_overlaps + 1
+ flow_out = flow_cfg()
+ if overlapf and num_flows == 1:
+ # Make 2nd flow a copy of the first,
+ # to guarantee at least 1 overlap
+ flow_out = copy.deepcopy(tbl_flows[0])
flow_out.overlap = True
+ flow_out.overlaps_with = [0, 0]
+ num_overlaps = num_overlaps + 1
else:
- flow_out.overlap = False
- flow_cfgs.append(flow_out)
+ flow_out.rand(fi,
+ tbl_stats.stats[tbl_idx].wildcards,
+ sw_features.actions,
+ map(lambda x: x.port_no, sw_features.ports)
+ )
+ flow_out.overlap = False
+
+ for f in tbl_flows:
+ if (not overlapf or not f.overlap) \
+ and flow_out.overlaps(f, False):
+ flow_out.overlap = True
+ flow_out.overlaps_with = [f.tbl_idx, f.flow_idx]
+ num_overlaps = num_overlaps + 1
+
+ flow_out.tbl_idx = tbl_idx
+ flow_out.flow_idx = flow_idx
+
+ self.logger.debug("tbl_idx=%d, flow_idx=%d, %s" % (tbl_idx, flow_idx, str(flow_out)))
+
+ tbl_flows.append(flow_out)
+
num_flows = num_flows + 1
flow_idx = flow_idx + 1
- tbl_flows.append(flow_cfgs)
tbl_idx = tbl_idx + 1
- self.logger.debug("Created " + str(num_flows) + " flows, with " + str(num_overlaps) + " overlaps")
+ self.logger.debug("Created " + str(num_flows)
+ + " flows, with " + str(num_overlaps)
+ + " overlaps"
+ )
# Send all flows to switch
self.logger.debug("Sending flows to switch")
- flow_num = 1
- tbl_idx = 0
- while tbl_idx < sw_features.n_tables:
- flow_idx = 0
- while flow_idx < len(tbl_flows[tbl_idx]):
- self.logger.debug("Sending flow number " + str(flow_num))
- flow_mod_msg = message.flow_mod()
- flow_mod_msg.buffer_id = 0xffffffff
- flow_mod_msg.cookie = random.randint(0, (1 << 53) - 1)
- tbl_flows[tbl_idx][flow_idx].to_flow_mod_msg(flow_mod_msg)
- if overlapf:
- flow_mod_msg.flags = flow_mod_msg.flags | ofp.OFPFF_CHECK_OVERLAP
- rv = self.controller.message_send(flow_mod_msg)
- self.assertTrue(rv != -1, "Error installing flow mod")
- (errmsg, pkt) = self.controller.poll(ofp.OFPT_ERROR, 1) # TBD - Tune timeout for error message
- if errmsg is not None:
- # Got ERROR message
- if errmsg.type == ofp.OFPET_FLOW_MOD_FAILED and errmsg.code == ofp.OFPFMFC_OVERLAP:
- # Got "overlap" ERROR message
- self.assertTrue(overlapf and tbl_flows[tbl_idx][flow_idx].overlap, "Got unexpected OVERLAP error message")
- else:
- self.assertTrue(False, "Got unexpected error message, type = " + str(errmsg.type) + ", code = " + str(errmsg.code))
- else:
- # Did not get ERROR message
- self.assertTrue(not (overlapf and tbl_flows[tbl_idx][flow_idx].overlap), "Did not get expected OVERLAP message")
-
- flow_idx = flow_idx + 1
- flow_num = flow_num + 1
- tbl_idx = tbl_idx + 1
+ for f in tbl_flows:
+ self.flow_add(f, overlapf)
+ f.deleted = False
# Send barrier, to make sure all flows are in
+ self.do_barrier()
- barrier = message.barrier_request()
- (resp, pkt) = self.controller.transact(barrier, 5)
- self.assertTrue(resp is not None, "Did not receive response to barrier request")
+ # Red back all flows from switch, and verify
- # Check number of flows reported in table stats
+ self.verify_flows(sw_features,
+ tbl_flows,
+ num_flows,
+ overlapf,
+ num_overlaps
+ )
- self.logger.debug("Verifying that table stats reports the correct number of active flows")
- request = message.table_stats_request()
- (tbl_stats_after, pkt) = self.controller.transact(request, timeout=2)
- self.assertTrue(tbl_stats_after is not None, "No reply to table_stats_request")
+ # Delete a flows from switch
- num_flows_reported = 0
- for ts in tbl_stats_after.stats:
- num_flows_reported = num_flows_reported + ts.active_count
+ if strictf:
+ # Strict delete
+
+ # Delete a few flows, in random order, individually (i.e. strictly)
+
+ del_flow_idxs = shuffle(range(len(tbl_flows)))
+ # TBD - Limited, for testing only; remove
+ del_flow_idxs = del_flow_idxs[0 : random.randint(3, 3)]
+ for di in del_flow_idxs:
+ f = tbl_flows[di]
+ tbl_idx = f.tbl_idx
+ flow_idx = f.flow_idx
+ if (overlapf and f.overlap):
+ self.logger.debug("Flow tbl_idx = " + str(tbl_idx)
+ + ", flow_idx = " + str(flow_idx)
+ + " was an overlap, skipping delete"
+ )
+ else:
+ self.logger.debug("Deleting flow, tbl_idx = "
+ + str(tbl_idx) + ", flow_idx = "
+ + str(flow_idx)
+ )
+ self.flow_del(f, True)
+ f.deleted = True
+ num_flows = num_flows - 1
- num_flows_expected = num_flows
- if overlapf:
- num_flows_expected = num_flows_expected - num_overlaps
+ # Send barrier, to make sure all flows are deleted
+ self.do_barrier();
- self.assertEqual(num_flows_reported, num_flows_expected, "Incorrect number of flows returned by table stats, reported = " + str(num_flows_reported) + ", expected = " + str(num_flows_expected))
+ # Red back all flows from switch, and verify
- # Retrieve all flows from switch
+ self.verify_flows(sw_features,
+ tbl_flows,
+ num_flows,
+ overlapf,
+ num_overlaps
+ )
- self.logger.debug("Retrieving all flows from switch")
- stat_req = message.flow_stats_request()
- query_match = ofp.ofp_match()
- query_match.wildcards = ofp.OFPFW_ALL
- stat_req.match = query_match
- stat_req.table_id = 0xff
- stat_req.out_port = ofp.OFPP_NONE;
- flow_stats, pkt = self.controller.transact(stat_req, timeout=2)
- self.assertTrue(flow_stats is not None, "Get all flow stats failed")
+ # Delete all remaining flows, in random order,
+ # individually (i.e. strictly)
+
+ del_flow_idxs = shuffle(range(len(tbl_flows)))
+ for di in del_flow_idxs:
+ f = tbl_flows[di]
+ if f.deleted:
+ continue
+ tbl_idx = f.tbl_idx
+ flow_idx = f.flow_idx
+ if (overlapf and f.overlap):
+ self.logger.debug("Flow tbl_idx = "
+ + str(tbl_idx)
+ + ", flow_idx = "
+ + str(flow_idx)
+ + " was an overlap, skipping delete"
+ )
+ else:
+ self.logger.debug("Deleting flow, tbl_idx = "
+ + str(tbl_idx)
+ + ", flow_idx = "
+ + str(flow_idx)
+ )
+ self.flow_del(f, True)
+ f.deleted = True
+ num_flows = num_flows - 1
- # Verify retrieved flows
+ # Send barrier, to make sure all flows are deleted
+ self.do_barrier()
+
+ # Red back all flows from switch (i.e. none), and verify
+
+ self.verify_flows(sw_features,
+ tbl_flows,
+ num_flows,
+ overlapf,
+ num_overlaps
+ )
- self.logger.debug("Verifying retrieved flows")
+ else:
+ # Non-strict delete
- self.assertEqual(flow_stats.type, ofp.OFPST_FLOW, "Unexpected type of response message")
+ # Pick a flow at random that had at least 1 qualifier specified,
+ # wildcard a qualifier that was specified,
+ # and do a non-strict delete
+ # Keep wildcarding specified qualifiers, one by one, and deleteing,
+ # until everything is wildcarded,
+ # and hence all flows should be deleted
- num_flows_reported = len(flow_stats.stats)
+ while True:
+ f = tbl_flows[random.randint(0, len(tbl_flows) - 1)]
+ if f.match.wildcards != tbl_stats.stats[f.tbl_idx].wildcards:
+ self.logger.debug("Choosing flow for basis of non-strict delete")
+ self.logger.debug(" tbl_idx=%d flow_idx=%d" % (f.tbl_idx, f.flow_idx))
+ self.logger.debug(" " + str(f))
+ break
- self.assertEqual(num_flows_reported, num_flows_expected, "Incorrect number of flows returned by table stats, reported = " + str(num_flows_reported) + ", expected = " + str(num_flows_expected))
+ # For each qualifier, in random order, if it was specified,
+ # wildcard it, do a delete, and check the results
+
+ wildcard_idxs = shuffle(range(len(all_wildcards_list)))
+ for wi in wildcard_idxs:
+ w = all_wildcards_list[wi]
+ if (f.match.wildcards & w) != 0:
+ continue
- tbl_idx = 0
- while tbl_idx < sw_features.n_tables:
- flow_idx = 0
- while flow_idx < len(tbl_flows[tbl_idx]):
- tbl_flows[tbl_idx][flow_idx].resp_matched = False
- flow_idx = flow_idx + 1
- tbl_idx = tbl_idx + 1
+ if w == ofp.OFPFW_NW_SRC_MASK:
+ f.match.wildcards = (f.match.wildcards
+ & ~ofp.OFPFW_NW_SRC_MASK
+ ) \
+ | ofp.OFPFW_NW_SRC_ALL
+ wn = "OFPFW_NW_SRC"
+ elif w == ofp.OFPFW_NW_DST_MASK:
+ f.match.wildcards = (f.match.wildcards
+ & ~ofp.OFPFW_NW_DST_MASK
+ ) \
+ | ofp.OFPFW_NW_DST_ALL
+ wn = "OFPFW_NW_DST"
+ else:
+ f.match.wildcards = f.match.wildcards | w
+ wn = ofp.ofp_flow_wildcards_map[w]
- num_resp_flows_matched = 0
- for flow_stat in flow_stats.stats:
- flow_in = flow_cfg()
- flow_in.from_flow_stat(flow_stat)
+ self.logger.debug("Adding wildcard %s" % (wn))
+ self.logger.debug(str(f))
- tbl_idx = 0
- while tbl_idx < sw_features.n_tables:
- flow_idx = 0
- while flow_idx < len(tbl_flows[tbl_idx]):
- f = tbl_flows[tbl_idx][flow_idx]
- if not f.resp_matched and (not overlapf or not f.overlap) and f == flow_in:
- f.resp_matched = True
- num_resp_flows_matched = num_resp_flows_matched + 1
- break
- flow_idx = flow_idx + 1
- self.assertTrue(flow_idx < len(tbl_flows[tbl_idx]), "Reponse flow does not match any configured flow")
- tbl_idx = tbl_idx + 1
+ # Mark all flows which would be deleted by this
+ # non-strict delete
+
+ for ff in tbl_flows:
+ if not ff.deleted and f.overlaps(ff, True):
+ self.logger.debug("Deleting flow, tbl_idx = "
+ + str(ff.tbl_idx) + ", flow_idx = "
+ + str(ff.flow_idx)
+ )
+ ff.deleted = True
+ num_flows = num_flows - 1
+
+ self.flow_del(f, False)
+
+ # Send barrier, to make sure all flows are deleted
+ self.do_barrier()
+
+ # Red back all flows from switch, and verify
+
+ self.verify_flows(sw_features,
+ tbl_flows,
+ num_flows,
+ overlapf,
+ num_overlaps
+ )
- self.assertEqual(num_resp_flows_matched, num_flows_expected, "Configured flow(s) missing in response, num_resp_flows_matched = " + str(num_resp_flows_matched) + ", num_flows_expected = " + str(num_flows_expected))
def runTest(self):
@@ -528,6 +1203,8 @@
Run all tests
"""
- self.test1(False) # Test with no overlaps
- self.test1(True) # Test with overlaps
+ self.test1(False, True) # Test with no overlaps, strict delete
+ self.test1(True, True) # Test with overlaps, strict delete
+# self.test1(False, False) # Test with no overlaps, non-strict delete
+# self.test1(True, False) # Test with overlaps, non-strict delete