groups-1.3: group table management tests
These tests were originally written by Rodrigo Denicol <rdenicol@cpqd.com.br>.
I updated them to the current OFTest API and made some other improvements.
These tests only cover add/modify/delete of groups, and the stats requests. No
packets are sent through the dataplane.
diff --git a/tests-1.3/groups.py b/tests-1.3/groups.py
new file mode 100644
index 0000000..a15bc50
--- /dev/null
+++ b/tests-1.3/groups.py
@@ -0,0 +1,735 @@
+# Distributed under the OpenFlow Software License (see LICENSE)
+# Copyright (c) 2010 The Board of Trustees of The Leland Stanford Junior University
+# Copyright (c) 2012, 2013 Big Switch Networks, Inc.
+# Copyright (c) 2012, 2013 CPqD
+# Copyright (c) 2012, 2013 Ericsson
+"""
+Group table test cases.
+"""
+
+import logging
+
+from oftest import config
+import oftest
+import oftest.base_tests as base_tests
+import ofp
+
+from oftest.testutils import *
+
+class GroupTest(base_tests.SimpleDataPlane):
+ def setUp(self):
+ base_tests.SimpleDataPlane.setUp(self)
+ delete_all_flows(self.controller)
+ delete_all_groups(self.controller)
+
+
+class GroupAdd(GroupTest):
+ """
+ A regular group should be added successfully
+ """
+
+ def runTest(self):
+ port1, = openflow_ports(1)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port1)])])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ stats = get_stats(self, ofp.message.group_desc_stats_request())
+ self.assertEquals(stats, [
+ ofp.group_desc_stats_entry(
+ type=msg.group_type,
+ group_id=msg.group_id,
+ buckets=msg.buckets)])
+
+
+class GroupAddInvalidAction(GroupTest):
+ """
+ If any action in the buckets is invalid, OFPET_BAD_ACTION/<code> should be returned
+ """
+
+ def runTest(self):
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(ofp.OFPP_ANY)])])
+
+ response, _ = self.controller.transact(msg)
+ self.assertIsInstance(response, ofp.message.error_msg)
+ self.assertEquals(response.err_type, ofp.OFPET_BAD_ACTION)
+ self.assertEquals(response.code, ofp.OFPBAC_BAD_OUT_PORT)
+
+
+class GroupAddExisting(GroupTest):
+ """
+ An addition with existing group id should result in OFPET_GROUP_MOD_FAILED/OFPGMFC_GROUP_EXISTS
+ """
+
+ def runTest(self):
+ port1, port2, = openflow_ports(2)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port1)])])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port2)])])
+
+ response, _ = self.controller.transact(msg)
+ self.assertIsInstance(response, ofp.message.error_msg)
+ self.assertEquals(response.err_type, ofp.OFPET_GROUP_MOD_FAILED)
+ self.assertEquals(response.code, ofp.OFPGMFC_GROUP_EXISTS)
+
+
+class GroupAddInvalidID(GroupTest):
+ """
+ An addition with invalid group id (reserved) should result in OFPET_GROUP_MOD_FAILED/OFPGMFC_INVALID_GROUP
+ """
+
+ def runTest(self):
+ port1, = openflow_ports(1)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=ofp.OFPG_ALL,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port1)])])
+
+ response, _ = self.controller.transact(msg)
+ self.assertIsInstance(response, ofp.message.error_msg)
+ self.assertEquals(response.err_type, ofp.OFPET_GROUP_MOD_FAILED)
+ self.assertEquals(response.code, ofp.OFPGMFC_INVALID_GROUP)
+
+
+class GroupModify(GroupTest):
+ """
+ A regular group modification should be successful
+ """
+
+ def runTest(self):
+ port1, port2, = openflow_ports(2)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port1)])])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_MODIFY,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port2)])])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ stats = get_stats(self, ofp.message.group_desc_stats_request())
+ self.assertEquals(stats, [
+ ofp.group_desc_stats_entry(
+ type=msg.group_type,
+ group_id=msg.group_id,
+ buckets=msg.buckets)])
+
+
+class GroupModifyNonexisting(GroupTest):
+ """
+ A modification for a non-existing group should result in OFPET_GROUP_MOD_FAILED/OFPGMFC_UNKNOWN_GROUP
+ """
+
+ def runTest(self):
+ port1, = openflow_ports(1)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_MODIFY,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port1)])])
+
+ response, _ = self.controller.transact(msg)
+ self.assertIsInstance(response, ofp.message.error_msg)
+ self.assertEquals(response.err_type, ofp.OFPET_GROUP_MOD_FAILED)
+ self.assertEquals(response.code, ofp.OFPGMFC_UNKNOWN_GROUP)
+
+
+class GroupModifyLoop(GroupTest):
+ """
+ A modification causing loop should result in OFPET_GROUP_MOD_FAILED/OFPGMFC_LOOP
+ """
+
+ def runTest(self):
+ port1, = openflow_ports(1)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port1)])])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=1,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.group(0)])])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=2,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.group(1)])])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_MODIFY,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.group(2)])])
+
+ response, _ = self.controller.transact(msg)
+ self.assertIsInstance(response, ofp.message.error_msg)
+ self.assertEquals(response.err_type, ofp.OFPET_GROUP_MOD_FAILED)
+ self.assertEquals(response.code, ofp.OFPGMFC_LOOP)
+
+
+class GroupModifyInvalidID(GroupTest):
+ """
+ A modification for a reserved group should result in OFPET_GROUP_MOD_FAILED/OFPGMFC_INVALID_GROUP
+ """
+
+ def runTest(self):
+ port1, = openflow_ports(1)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=ofp.OFPG_ALL,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port1)])])
+
+ response, _ = self.controller.transact(msg)
+ self.assertIsInstance(response, ofp.message.error_msg)
+ self.assertEquals(response.err_type, ofp.OFPET_GROUP_MOD_FAILED)
+ self.assertEquals(response.code, ofp.OFPGMFC_INVALID_GROUP)
+
+
+class GroupModifyEmpty(GroupTest):
+ """
+ A modification for an existing group with no buckets should be accepted
+ """
+
+ def runTest(self):
+ port1, = openflow_ports(1)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port1)])])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_MODIFY,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ stats = get_stats(self, ofp.message.group_desc_stats_request())
+ self.assertEquals(stats, [
+ ofp.group_desc_stats_entry(
+ type=msg.group_type,
+ group_id=msg.group_id,
+ buckets=msg.buckets)])
+
+
+class GroupDeleteExisting(GroupTest):
+ """
+ A deletion for an existing group should remove the group
+ """
+
+ def runTest(self):
+ port1, = openflow_ports(1)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port1)])])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_DELETE,
+ group_id=0)
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ stats = get_stats(self, ofp.message.group_desc_stats_request())
+ self.assertEquals(stats, [])
+
+
+class GroupDeleteNonexisting(GroupTest):
+ """
+ A deletion for nonexisting group should result in no error
+ """
+
+ def runTest(self):
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_DELETE,
+ group_id=0)
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+ verify_no_errors(self.controller)
+
+
+class GroupDeleteAll(GroupTest):
+ """
+ A deletion for OFPG_ALL should remove all groups
+ """
+
+ def runTest(self):
+ port1, = openflow_ports(1)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port1)])])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=1,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port1)])])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_DELETE,
+ group_id=ofp.OFPG_ALL)
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ stats = get_stats(self, ofp.message.group_desc_stats_request())
+ self.assertEquals(stats, [])
+
+
+class GroupAddAllWeight(GroupTest):
+ """
+ An ALL group with weights for buckets should result in OFPET_GROUP_MOD_FAILED, OFPGMFC_INVALID_GROUP
+ """
+
+ def runTest(self):
+ port1, port2, = openflow_ports(2)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(weight=1, actions=[ofp.action.output(port1)]),
+ ofp.bucket(weight=2, actions=[ofp.action.output(port2)])])
+
+ response, _ = self.controller.transact(msg)
+ self.assertIsInstance(response, ofp.message.error_msg)
+ self.assertEquals(response.err_type, ofp.OFPET_GROUP_MOD_FAILED)
+ self.assertEquals(response.code, ofp.OFPGMFC_INVALID_GROUP)
+
+
+class GroupAddIndirectWeight(GroupTest):
+ """
+ An INDIRECT group with weights for buckets should result in OFPET_GROUP_MOD_FAILED, OFPGMFC_INVALID_GROUP
+ """
+
+ def runTest(self):
+ port1, = openflow_ports(1)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_INDIRECT,
+ group_id=0,
+ buckets=[
+ ofp.bucket(weight=1, actions=[ofp.action.output(port1)])])
+
+ response, _ = self.controller.transact(msg)
+ self.assertIsInstance(response, ofp.message.error_msg)
+ self.assertEquals(response.err_type, ofp.OFPET_GROUP_MOD_FAILED)
+ self.assertEquals(response.code, ofp.OFPGMFC_INVALID_GROUP)
+
+
+class GroupAddIndirectBuckets(GroupTest):
+ """
+ An INDIRECT group with <>1 bucket should result in OFPET_GROUP_MOD_FAILED, OFPGMFC_INVALID_GROUP
+ """
+
+ def runTest(self):
+ port1, port2, = openflow_ports(2)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_INDIRECT,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port1)]),
+ ofp.bucket(actions=[ofp.action.output(port2)])])
+
+ response, _ = self.controller.transact(msg)
+ self.assertIsInstance(response, ofp.message.error_msg)
+ self.assertEquals(response.err_type, ofp.OFPET_GROUP_MOD_FAILED)
+ self.assertEquals(response.code, ofp.OFPGMFC_INVALID_GROUP)
+
+
+class GroupAddSelectNoWeight(GroupTest):
+ """
+ A SELECT group with ==0 weights should result in OFPET_GROUP_MOD_FAILED, OFPGMFC_INVALID_GROUP
+ """
+
+ def runTest(self):
+ port1, port2, = openflow_ports(2)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_SELECT,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[ofp.action.output(port1)]),
+ ofp.bucket(actions=[ofp.action.output(port2)])])
+
+ response, _ = self.controller.transact(msg)
+ self.assertIsInstance(response, ofp.message.error_msg)
+ self.assertEquals(response.err_type, ofp.OFPET_GROUP_MOD_FAILED)
+ self.assertEquals(response.code, ofp.OFPGMFC_INVALID_GROUP)
+
+
+class GroupStats(GroupTest):
+ """
+ A group stats request should return an entry for the specified group
+ """
+
+ def runTest(self):
+ port1, port2, = openflow_ports(2)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=10,
+ buckets=[
+ ofp.bucket(actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(2000)),
+ ofp.action.output(port1)]),
+ ofp.bucket(actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(3000)),
+ ofp.action.output(port2)])])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ request = ofp.message.group_stats_request(group_id=10)
+ stats = get_stats(self, request)
+
+ self.assertEquals(len(stats), 1)
+ self.assertEquals(stats[0].group_id, 10)
+ self.assertEquals(stats[0].ref_count, 0)
+ self.assertEquals(stats[0].packet_count, 0)
+ self.assertEquals(stats[0].byte_count, 0)
+ self.assertEquals(len(stats[0].bucket_stats), 2)
+
+
+class GroupStatsNonexistent(GroupTest):
+ """
+ A group stats request for a nonexistent group should return an empty list
+ """
+
+ def runTest(self):
+ request = ofp.message.group_stats_request(group_id=10)
+ stats = get_stats(self, request)
+ self.assertEquals(len(stats), 0)
+
+
+class GroupStatsAll(GroupTest):
+ """
+ A group stats request with OFPG_ALL should return an entry for each group
+ """
+
+ def runTest(self):
+ port1, port2, = openflow_ports(2)
+
+ msg0 = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(2000)),
+ ofp.action.output(port1)]),
+ ofp.bucket(actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(3000)),
+ ofp.action.output(port2)])])
+
+ self.controller.message_send(msg0)
+ do_barrier(self.controller)
+
+ msg1 = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=1,
+ buckets=[
+ ofp.bucket(actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(2001)),
+ ofp.action.output(port1)]),
+ ofp.bucket(actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(3001)),
+ ofp.action.output(port2)])])
+
+ self.controller.message_send(msg1)
+ do_barrier(self.controller)
+
+ request = ofp.message.group_stats_request(group_id=ofp.OFPG_ALL)
+ stats = sorted(get_stats(self, request), key=lambda x: x.group_id)
+
+ self.assertEquals(len(stats), 2)
+
+ self.assertEquals(stats[0].group_id, 0)
+ self.assertEquals(stats[0].ref_count, 0)
+ self.assertEquals(stats[0].packet_count, 0)
+ self.assertEquals(stats[0].byte_count, 0)
+ self.assertEquals(len(stats[0].bucket_stats), 2)
+
+ self.assertEquals(stats[1].group_id, 1)
+ self.assertEquals(stats[1].ref_count, 0)
+ self.assertEquals(stats[1].packet_count, 0)
+ self.assertEquals(stats[1].byte_count, 0)
+ self.assertEquals(len(stats[1].bucket_stats), 2)
+
+
+class GroupDescStats(GroupTest):
+ """
+ A group desc stats request should return the type, id, and buckets for each group
+ """
+
+ def runTest(self):
+ port1, port2, port3, = openflow_ports(3)
+
+ msg0 = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=0,
+ buckets=[
+ ofp.bucket(actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(2000)),
+ ofp.action.output(port1)]),
+ ofp.bucket(actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(3000)),
+ ofp.action.output(port2)]),
+ ofp.bucket(actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(4000)),
+ ofp.action.output(port3)])])
+
+ self.controller.message_send(msg0)
+ do_barrier(self.controller)
+
+ msg1 = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_SELECT,
+ group_id=1,
+ buckets=[
+ ofp.bucket(
+ weight=1,
+ actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(2001)),
+ ofp.action.output(port1)]),
+ ofp.bucket(
+ weight=2,
+ actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(3001)),
+ ofp.action.output(port2)]),
+ ofp.bucket(
+ weight=3,
+ actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(4001)),
+ ofp.action.output(port3)])])
+
+ self.controller.message_send(msg1)
+ do_barrier(self.controller)
+
+ msg2 = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_FF,
+ group_id=2,
+ buckets=[
+ ofp.bucket(
+ watch_port=port1,
+ actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(2002)),
+ ofp.action.output(port1)]),
+ ofp.bucket(
+ watch_port=port2,
+ actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(3002)),
+ ofp.action.output(port2,)]),
+ ofp.bucket(
+ watch_port=port3,
+ actions=[
+ ofp.action.set_field(ofp.oxm.tcp_src(4002)),
+ ofp.action.output(port3,)])])
+
+ self.controller.message_send(msg2)
+ do_barrier(self.controller)
+
+ request = ofp.message.group_desc_stats_request()
+ stats = sorted(get_stats(self, request), key=lambda x: x.group_id)
+
+ self.assertEquals(len(stats), 3)
+
+ self.assertEquals(stats[0].group_id, msg0.group_id)
+ self.assertEquals(stats[0].type, msg0.group_type)
+ self.assertEquals(stats[0].buckets, msg0.buckets)
+
+ self.assertEquals(stats[1].group_id, msg1.group_id)
+ self.assertEquals(stats[1].type, msg1.group_type)
+ self.assertEquals(stats[1].buckets, msg1.buckets)
+
+ self.assertEquals(stats[2].group_id, msg2.group_id)
+ self.assertEquals(stats[2].type, msg2.group_type)
+ self.assertEquals(stats[2].buckets, msg2.buckets)
+
+
+class GroupFlowSelect(GroupTest):
+ """
+ A flow stats request qualified on group id should select the correct flows
+ """
+
+ def runTest(self):
+ port1, = openflow_ports(1)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=1,
+ buckets=[])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ msg = ofp.message.group_mod(
+ command=ofp.OFPGC_ADD,
+ group_type=ofp.OFPGT_ALL,
+ group_id=2,
+ buckets=[])
+
+ self.controller.message_send(msg)
+ do_barrier(self.controller)
+
+ packet_in1 = simple_tcp_packet(tcp_sport=1000)
+
+ flow_add_msg1 = flow_msg_create(
+ self,
+ packet_in1,
+ ing_port=1,
+ action_list=[
+ ofp.action.group(1),
+ ofp.action.output(port1)])
+
+ self.controller.message_send(flow_add_msg1)
+ do_barrier(self.controller)
+
+ packet_in2 = simple_tcp_packet(tcp_sport=2000)
+
+ flow_add_msg2 = flow_msg_create(
+ self,
+ packet_in2,
+ ing_port=1,
+ action_list=[
+ ofp.action.group(2),
+ ofp.action.output(port1)])
+
+ self.controller.message_send(flow_add_msg2)
+ do_barrier(self.controller)
+
+ packet_in3 = simple_tcp_packet(tcp_sport=3000)
+
+ flow_add_msg3 = flow_msg_create(
+ self,
+ packet_in3,
+ ing_port=1,
+ action_list=[
+ ofp.action.group(2),
+ ofp.action.output(port1)])
+
+ self.controller.message_send(flow_add_msg3)
+ do_barrier(self.controller)
+
+ packet_in4 = simple_tcp_packet(tcp_sport=4000)
+
+ flow_add_msg4 = flow_msg_create(
+ self,
+ packet_in4,
+ ing_port=1,
+ action_list=[
+ ofp.action.output(port1)])
+
+ self.controller.message_send(flow_add_msg4)
+ do_barrier(self.controller)
+
+ request = ofp.message.aggregate_stats_request(
+ table_id=ofp.OFPTT_ALL,
+ out_port=ofp.OFPP_ANY,
+ out_group=2)
+
+ response, _ = self.controller.transact(request)
+
+ self.assertEqual(response.flow_count, 2,
+ 'Did not match expected flow count')