| """ |
| |
| A set of inter-test utility functions for dealing with OF-DPA |
| |
| |
| """ |
| |
| import logging, subprocess, time |
| import ofp |
| |
| from oftest import config |
| from oftest.testutils import * |
| |
| def forceOfdpaRestart( user ): |
| output = 1; |
| credential = user; |
| test = subprocess.Popen(["ssh", credential, "service ofdpa restart &> /dev/null"]); |
| time.sleep(1); |
| while output < 10: |
| output = int(subprocess.check_output(["ssh", credential, "client_cfg_purge | wc -l"])); |
| time.sleep(1); |
| subprocess.Popen(["ssh", credential, "brcm-indigo-ofdpa-ofagent -t 10.128.0.220 &> /dev/null"], stdout=subprocess.PIPE); |
| |
| def forceOfdpaStop( user ): |
| subprocess.Popen(["ssh", user, "ps ax | grep 'brcm-indigo-ofdpa-ofagent' | awk '{print $1}' | xargs sudo kill"], stdout=subprocess.PIPE); |
| |
| |
| class table(object): |
| """ Metadata on each OFDPA table """ |
| def __init__(self, table_id, table_name): |
| self.table_id = table_id |
| self.table_name = table_name |
| # TODO consider adding type checking verification here |
| |
| INGRESS_TABLE = table(0, "Ingress") |
| VLAN_TABLE = table(10, "VLAN") |
| MACTERM_TABLE = table(20, "MacTerm") |
| UNICAST_ROUTING_TABLE = table(30, "Unicast Routing") |
| MULTICAST_ROUTING_TABLE = table(40, "Multicast Routing") |
| BRIDGING_TABLE = table(50, "Bridging Table") |
| ACL_TABLE = table(60, "ACL Policy Table") |
| #.... FIXME add all tables |
| |
| DEFAULT_VLAN = 1 |
| |
| def enableVlanOnPort(controller, vlan, port=ofp.OFPP_ALL, priority=0): |
| if port == ofp.OFPP_ALL: |
| ports = sorted(config["port_map"].keys()) |
| else: |
| ports = [port] |
| for port in ports: |
| tagged_match = ofp.match([ |
| ofp.oxm.in_port(port), |
| ofp.oxm.vlan_vid(vlan | ofp.OFPVID_PRESENT) |
| ]) |
| |
| request = ofp.message.flow_add( |
| table_id = VLAN_TABLE.table_id, |
| cookie = 0xdead, |
| match = tagged_match, |
| instructions = [ |
| ofp.instruction.goto_table(MACTERM_TABLE.table_id), |
| # ofp.instruction.apply_actions( |
| # actions=[ |
| # ofp.action.push_vlan(ethertype=0x8100), # DO NOT PUT THIS FOR OF-DPA 2.0 EA1 - seems to not matter for EA2 |
| # ofp.action.set_field(ofp.oxm.vlan_vid( ofp.OFPVID_PRESENT | vlan)) |
| # ]), |
| ], |
| buffer_id = ofp.OFP_NO_BUFFER, |
| priority = priority) |
| |
| logging.info("Inserting vlan rule allowing tagged vlan %d on port %d" % (vlan, port)) |
| controller.message_send(request) |
| do_barrier(controller) |
| verify_no_errors(controller) |
| |
| |
| def installDefaultVlan(controller, vlan=DEFAULT_VLAN, port=ofp.OFPP_ALL, priority=0): |
| """ Insert a rule that maps all untagged traffic to vlan $vlan |
| |
| In OFDPA, table 10 (the vlan table) requires that all traffic be |
| mapped to an internal vlan else the packets be dropped. This function |
| sets up a default vlan mapping all untagged traffic to an internal VLAN. |
| |
| With OF-DPA, before you can insert a 'untagged to X' rule on a |
| port, you must first insert a 'X --> X' rule for the same port. |
| |
| Further, the 'X --> X' rule must set ofp.OFPVID_PRESENT even |
| though 'X' is non-zero. |
| |
| The 'controller' variable is self.controller from a test |
| """ |
| # OFDPA seems to be dumb and wants each port set individually |
| # Can't set all ports by using OFPP_ALL |
| if port == ofp.OFPP_ALL: |
| ports = sorted(config["port_map"].keys()) |
| else: |
| ports = [port] |
| |
| for port in ports: |
| # enable this vlan on this port before we can map untagged packets to the vlan |
| enableVlanOnPort(controller, vlan, port) |
| |
| untagged_match = ofp.match([ |
| ofp.oxm.in_port(port), |
| # OFDPA 2.0 says untagged is vlan_id=0, mask=ofp.OFPVID_PRESENT |
| ofp.oxm.vlan_vid_masked(0,ofp.OFPVID_PRESENT) # WTF OFDPA 2.0EA2 -- really!? |
| ]) |
| |
| request = ofp.message.flow_add( |
| table_id = VLAN_TABLE.table_id, |
| cookie = 0xbeef, |
| match = untagged_match, |
| instructions = [ |
| ofp.instruction.apply_actions( |
| actions=[ |
| #ofp.action.push_vlan(ethertype=0x8100), |
| ofp.action.set_field(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | vlan)) |
| ]), |
| ofp.instruction.goto_table(MACTERM_TABLE.table_id) |
| ], |
| buffer_id = ofp.OFP_NO_BUFFER, |
| priority = priority) |
| |
| logging.info("Inserting default vlan sending all untagged traffic to vlan %d on port %d" % (vlan, port)) |
| controller.message_send(request) |
| do_barrier(controller) |
| verify_no_errors(controller) |
| |
| |
| _group_types = { |
| "L2 Interface": 0, |
| "L2 Rewrite" : 1, |
| "L3 Unicast" : 2, |
| "L2 Multicast" : 3, |
| "L2 Flood" : 4, |
| "L3 Interface" : 5, |
| "L3 Multicast": 6, |
| "L3 ECMP": 7, |
| "L2 Data Center Overlay": 8, |
| "MPLS Label" : 9, |
| "MPLS Forwarding" :10, |
| "L2 Unfiltered Interface": 11, |
| "L2 Loopback": 12, |
| } |
| |
| |
| def makeGroupID(groupType, local_id): |
| """ Group IDs in OF-DPA have rich meaning |
| |
| @param groupType is a key in _group_types |
| @param local_id is an integer 0<= local_id < 2**27, |
| but it may have more semantic meaning depending on the |
| groupType |
| |
| |
| Read Section 4.3 of the OF-DPA manual on groups for |
| details |
| """ |
| if groupType not in _group_types: |
| raise KeyError("%s not a valid OF-DPA group type" % groupType) |
| if local_id < 0 or local_id >=134217728: |
| raise ValueError("local_id %d must be 0<= local_id < 2**27" % local_id) |
| return (_group_types[groupType] << 28) + local_id |
| |
| |
| def delete_all_recursive_groups(controller): |
| pass |