Updating check_switch_started in p4runtime_switch.py (#85)

The previous implementation had a race conditiona between the
socket check and the BMv2 startup. Sometimes, if the check happen
just before (or as) BMv2 was trying to bind the port, it would
not be able to bind it. This solution uses psutil's equivalent
to 'netstat' to determine whether is BMv2 has bound the port yet.

Also, some minor README and comment improvements.
This commit is contained in:
Brian O'Connor 2017-11-08 11:48:15 -08:00 committed by Robert Soule
parent b2161b8a27
commit b41473fcc0
4 changed files with 40 additions and 20 deletions

View File

@ -5,7 +5,7 @@
In this exercise, we will be using P4 Runtime to send flow entries to the In this exercise, we will be using P4 Runtime to send flow entries to the
switch instead of using the switch's CLI. We will be building on the same P4 switch instead of using the switch's CLI. We will be building on the same P4
program that you used in the [basic_tunnel](../basic_tunnel) exercise. The program that you used in the [basic_tunnel](../basic_tunnel) exercise. The
P4 program has be renamed to `advanced_tunnel.py` and has been augmented P4 program has been renamed to `advanced_tunnel.py` and has been augmented
with two counters (`ingressTunnelCounter`, `egressTunnelCounter`) and with two counters (`ingressTunnelCounter`, `egressTunnelCounter`) and
two new actions (`myTunnel_ingress`, `myTunnel_egress`). two new actions (`myTunnel_ingress`, `myTunnel_egress`).
@ -20,7 +20,7 @@ necessary to tunnel traffic between host 1 and 2.
## Step 1: Run the (incomplete) starter code ## Step 1: Run the (incomplete) starter code
The starter code for this assignment is in a file called `mycontroller.py`, The starter code for this assignment is in a file called `mycontroller.py`,
and it will install only some of the rules that you need tunnel traffic between and it will install only some of the rules that you need to tunnel traffic between
two hosts. two hosts.
Let's first compile the new P4 program, start the network, use `mycontroller.py` Let's first compile the new P4 program, start the network, use `mycontroller.py`

View File

@ -43,13 +43,19 @@ def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id,
# 2) Tunnel Transit Rule # 2) Tunnel Transit Rule
# The rule will need to be added to the myTunnel_exact table and match on # The rule will need to be added to the myTunnel_exact table and match on
# the tunnel ID (hdr.myTunnel.dst_id). For our simple topology, transit # the tunnel ID (hdr.myTunnel.dst_id). Traffic will need to be forwarded
# traffic will need to be forwarded on the using the myTunnel_forward action # using the myTunnel_forward action on the port connected to the next switch.
# on the SWITCH_TO_SWITCH_PORT (port 2).
# #
# We will only need on transit rule on the ingress switch because we are # For our simple topology, switch 1 and switch 2 are connected using a
# link attached to port 2 on both switches. We have defined a variable at
# the top of the file, SWITCH_TO_SWITCH_PORT, that you can use as the output
# port for this action.
#
# We will only need a transit rule on the ingress switch because we are
# using a simple topology. In general, you'll need on transit rule for # using a simple topology. In general, you'll need on transit rule for
# each switch in the path (except the last one). # each switch in the path (except the last switch, which has the egress rule),
# and you will need to select the port dynamically for each switch based on
# your topology.
# TODO build the transit rule # TODO build the transit rule
# TODO install the transit rule on the ingress switch # TODO install the transit rule on the ingress switch

View File

@ -48,13 +48,20 @@ def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id,
# 2) Tunnel Transit Rule # 2) Tunnel Transit Rule
# The rule will need to be added to the myTunnel_exact table and match on # The rule will need to be added to the myTunnel_exact table and match on
# the tunnel ID (hdr.myTunnel.dst_id). For our simple topology, transit # the tunnel ID (hdr.myTunnel.dst_id). Traffic will need to be forwarded
# traffic will need to be forwarded on the using the myTunnel_forward action # using the myTunnel_forward action on the port connected to the next switch.
# on the SWITCH_TO_SWITCH_PORT (port 2).
# #
# We will only need on transit rule on the ingress switch because we are # For our simple topology, switch 1 and switch 2 are connected using a
# link attached to port 2 on both switches. We have defined a variable at
# the top of the file, SWITCH_TO_SWITCH_PORT, that you can use as the output
# port for this action.
#
# We will only need a transit rule on the ingress switch because we are
# using a simple topology. In general, you'll need on transit rule for # using a simple topology. In general, you'll need on transit rule for
# each switch in the path (except the last one). # each switch in the path (except the last switch, which has the egress rule),
# and you will need to select the port dynamically for each switch based on
# your topology.
table_entry = p4info_helper.buildTableEntry( table_entry = p4info_helper.buildTableEntry(
table_name="myTunnel_exact", table_name="myTunnel_exact",
match_fields={ match_fields={

View File

@ -25,6 +25,14 @@ from mininet.log import info, error, debug
sys.path.append('/home/vagrant/behavioral-model/mininet') sys.path.append('/home/vagrant/behavioral-model/mininet')
from p4_mininet import P4Switch from p4_mininet import P4Switch
SWITCH_START_TIMEOUT = 10 # seconds
import psutil
def check_listening_on_port(port):
for c in psutil.net_connections(kind='inet'):
if c.status == 'LISTEN' and c.laddr[1] == port:
return True
return False
class P4RuntimeSwitch(P4Switch): class P4RuntimeSwitch(P4Switch):
"BMv2 switch with gRPC support" "BMv2 switch with gRPC support"
@ -59,6 +67,10 @@ class P4RuntimeSwitch(P4Switch):
self.grpc_port = P4RuntimeSwitch.next_grpc_port self.grpc_port = P4RuntimeSwitch.next_grpc_port
P4RuntimeSwitch.next_grpc_port += 1 P4RuntimeSwitch.next_grpc_port += 1
if check_listening_on_port(self.grpc_port):
error('%s cannot bind port %d because it is bound by another process\n' % (self.name, self.grpc_port))
exit(1)
self.verbose = verbose self.verbose = verbose
logfile = "/tmp/p4s.{}.log".format(self.name) logfile = "/tmp/p4s.{}.log".format(self.name)
self.output = open(logfile, 'w') self.output = open(logfile, 'w')
@ -75,17 +87,12 @@ class P4RuntimeSwitch(P4Switch):
def check_switch_started(self, pid): def check_switch_started(self, pid):
while True: for _ in range(SWITCH_START_TIMEOUT * 2):
if not os.path.exists(os.path.join("/proc", str(pid))): if not os.path.exists(os.path.join("/proc", str(pid))):
return False return False
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if check_listening_on_port(self.grpc_port):
sock.settimeout(0.5)
result = sock.connect_ex(("localhost", self.grpc_port))
if result == 0:
# TODO It seems like sometimes BMv2 can hang if multiple instances start up too quickly;
# this sleep might also do nothing...
sleep(0.5)
return True return True
sleep(0.5)
def start(self, controllers): def start(self, controllers):
info("Starting P4 switch {}.\n".format(self.name)) info("Starting P4 switch {}.\n".format(self.name))