mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-10-24 22:01:02 +02:00
Update bmv2.py to run stratum_bmv2 plus various improvements
Also added alias to quickly run mininet with stratum in cell machines and p4vm Change-Id: Id10bf8f3de4fe14d77b5efe47b6129a8a28b5a89
This commit is contained in:
parent
79705aab23
commit
499f320249
@ -343,4 +343,5 @@ alias atttopo='onos-netcfg $OCI $ONOS_ROOT/tools/test/topos/attmpls-cfg.json'
|
||||
alias uktopo='onos-netcfg $OCI $ONOS_ROOT/tools/test/topos/uk-cfg.json'
|
||||
|
||||
# Mininet command that uses BMv2 instead of OVS
|
||||
alias mn-p4='sudo -E mn --custom $BMV2_MN_PY --switch onosbmv2 --controller remote,ip=$OC1'
|
||||
alias mn-bmv2='sudo -E mn --custom $BMV2_MN_PY --switch onosbmv2 --controller remote,ip=$OC1'
|
||||
alias mn-stratum='sudo -E mn --custom $BMV2_MN_PY --switch stratum --controller remote,ip=$OC1'
|
||||
|
||||
@ -1,3 +1,19 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
Copyright 2019-present Open Networking Foundation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
import multiprocessing
|
||||
import os
|
||||
|
||||
@ -5,11 +21,12 @@ import json
|
||||
import random
|
||||
import re
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import urllib2
|
||||
from contextlib import closing
|
||||
from mininet.log import info, warn
|
||||
from mininet.log import info, warn, debug
|
||||
from mininet.node import Switch, Host
|
||||
|
||||
SIMPLE_SWITCH_GRPC = 'simple_switch_grpc'
|
||||
@ -19,6 +36,17 @@ SWITCH_START_TIMEOUT = 5 # seconds
|
||||
BMV2_LOG_LINES = 5
|
||||
BMV2_DEFAULT_DEVICE_ID = 1
|
||||
|
||||
# Stratum paths relative to stratum repo root
|
||||
STRATUM_BMV2 = 'stratum_bmv2'
|
||||
STRATUM_BINARY = '/bazel-bin/stratum/hal/bin/bmv2/' + STRATUM_BMV2
|
||||
STRATUM_INIT_PIPELINE = '/stratum/hal/bin/bmv2/dummy.json'
|
||||
|
||||
|
||||
def getStratumRoot():
|
||||
if 'STRATUM_ROOT' not in os.environ:
|
||||
raise Exception("Env variable STRATUM_ROOT not set")
|
||||
return os.environ['STRATUM_ROOT']
|
||||
|
||||
|
||||
def parseBoolean(value):
|
||||
if value in ['1', 1, 'true', 'True']:
|
||||
@ -41,20 +69,27 @@ def writeToFile(path, value):
|
||||
|
||||
|
||||
def watchDog(sw):
|
||||
while True:
|
||||
if ONOSBmv2Switch.mininet_exception == 1:
|
||||
sw.killBmv2(log=False)
|
||||
return
|
||||
if sw.stopped:
|
||||
return
|
||||
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
|
||||
if s.connect_ex(('127.0.0.1', sw.grpcPort)) == 0:
|
||||
time.sleep(1)
|
||||
else:
|
||||
warn("\n*** WARN: BMv2 instance %s died!\n" % sw.name)
|
||||
sw.printBmv2Log()
|
||||
print ("-" * 80) + "\n"
|
||||
try:
|
||||
writeToFile(sw.keepaliveFile,
|
||||
"Remove this file to terminate %s" % sw.name)
|
||||
while True:
|
||||
if ONOSBmv2Switch.mininet_exception == 1 \
|
||||
or not os.path.isfile(sw.keepaliveFile):
|
||||
sw.killBmv2(log=False)
|
||||
return
|
||||
if sw.stopped:
|
||||
return
|
||||
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
|
||||
if s.connect_ex(('127.0.0.1', sw.grpcPort)) == 0:
|
||||
time.sleep(1)
|
||||
else:
|
||||
warn("\n*** WARN: switch %s died ☠️ \n" % sw.name)
|
||||
sw.printBmv2Log()
|
||||
print ("-" * 80) + "\n"
|
||||
return
|
||||
except Exception as e:
|
||||
warn("*** ERROR: " + e.message)
|
||||
sw.killBmv2(log=True)
|
||||
|
||||
|
||||
class ONOSHost(Host):
|
||||
@ -86,12 +121,13 @@ class ONOSBmv2Switch(Switch):
|
||||
elogger=False, grpcport=None, cpuport=255, notifications=False,
|
||||
thriftport=None, netcfg=True, dryrun=False, pipeconf="",
|
||||
pktdump=False, valgrind=False, gnmi=False,
|
||||
portcfg=True, onosdevid=None, **kwargs):
|
||||
portcfg=True, onosdevid=None, stratum=False, **kwargs):
|
||||
Switch.__init__(self, name, **kwargs)
|
||||
self.grpcPort = grpcport
|
||||
self.thriftPort = thriftport
|
||||
self.cpuPort = cpuport
|
||||
self.json = json
|
||||
self.useStratum = parseBoolean(stratum)
|
||||
self.debugger = parseBoolean(debugger)
|
||||
self.notifications = parseBoolean(notifications)
|
||||
self.loglevel = loglevel
|
||||
@ -116,7 +152,11 @@ class ONOSBmv2Switch(Switch):
|
||||
self.onosDeviceId = "device:bmv2:%s" % self.name
|
||||
self.logfd = None
|
||||
self.bmv2popen = None
|
||||
self.stopped = False
|
||||
self.stopped = True
|
||||
# In case of exceptions, mininet removes *.out files from /tmp. We use
|
||||
# this as a signal to terminate the switch instance (if active).
|
||||
self.keepaliveFile = '/tmp/bmv2-%s-watchdog.out' % self.name
|
||||
self.targetName = STRATUM_BMV2 if self.useStratum else SIMPLE_SWITCH_GRPC
|
||||
|
||||
# Remove files from previous executions
|
||||
self.cleanupTmpFiles()
|
||||
@ -226,29 +266,43 @@ class ONOSBmv2Switch(Switch):
|
||||
warn("*** WARN: unable to push config to ONOS (%s)\n" % e.reason)
|
||||
|
||||
def start(self, controllers):
|
||||
bmv2Args = [SIMPLE_SWITCH_GRPC] + self.grpcTargetArgs()
|
||||
if self.valgrind:
|
||||
bmv2Args = VALGRIND_PREFIX.split() + bmv2Args
|
||||
|
||||
cmdString = " ".join(bmv2Args)
|
||||
if not self.stopped:
|
||||
warn("*** %s is already running!\n" % self.name)
|
||||
return
|
||||
|
||||
# Remove files from previous executions (if we are restarting)
|
||||
self.cleanupTmpFiles()
|
||||
|
||||
if self.grpcPort is None:
|
||||
self.grpcPort = pickUnusedPort()
|
||||
writeToFile("/tmp/bmv2-%s-grpc-port" % self.name, self.grpcPort)
|
||||
|
||||
if self.useStratum:
|
||||
config_dir = "/tmp/bmv2-%s-stratum" % self.name
|
||||
os.mkdir(config_dir)
|
||||
cmdString = self.getStratumCmdString(config_dir)
|
||||
else:
|
||||
if self.thriftPort is None:
|
||||
self.thriftPort = pickUnusedPort()
|
||||
writeToFile("/tmp/bmv2-%s-thrift-port" % self.name, self.thriftPort)
|
||||
cmdString = self.getBmv2CmdString()
|
||||
|
||||
if self.dryrun:
|
||||
info("\n*** DRY RUN (not executing bmv2)")
|
||||
info("\n*** DRY RUN (not executing %s)\n" % self.targetName)
|
||||
|
||||
info("\nStarting BMv2 target: %s\n" % cmdString)
|
||||
|
||||
writeToFile("/tmp/bmv2-%s-grpc-port" % self.name, self.grpcPort)
|
||||
writeToFile("/tmp/bmv2-%s-thrift-port" % self.name, self.thriftPort)
|
||||
debug("\n%s\n" % cmdString)
|
||||
|
||||
try:
|
||||
if not self.dryrun:
|
||||
# Start the switch
|
||||
self.stopped = False
|
||||
self.logfd = open(self.logfile, "w")
|
||||
self.bmv2popen = self.popen(cmdString,
|
||||
stdout=self.logfd,
|
||||
stderr=self.logfd)
|
||||
self.waitBmv2Start()
|
||||
# We want to be notified if BMv2 dies...
|
||||
# We want to be notified if BMv2/Stratum dies...
|
||||
threading.Thread(target=watchDog, args=[self]).start()
|
||||
|
||||
self.doOnosNetcfg(self.controllerIp(controllers))
|
||||
@ -258,11 +312,29 @@ class ONOSBmv2Switch(Switch):
|
||||
self.printBmv2Log()
|
||||
raise
|
||||
|
||||
def grpcTargetArgs(self):
|
||||
if self.grpcPort is None:
|
||||
self.grpcPort = pickUnusedPort()
|
||||
if self.thriftPort is None:
|
||||
self.thriftPort = pickUnusedPort()
|
||||
def getBmv2CmdString(self):
|
||||
bmv2Args = [SIMPLE_SWITCH_GRPC] + self.bmv2Args()
|
||||
if self.valgrind:
|
||||
bmv2Args = VALGRIND_PREFIX.split() + bmv2Args
|
||||
return " ".join(bmv2Args)
|
||||
|
||||
def getStratumCmdString(self, config_dir):
|
||||
stratumRoot = getStratumRoot()
|
||||
args = [
|
||||
stratumRoot + STRATUM_BINARY,
|
||||
'-device_id=%d' % BMV2_DEFAULT_DEVICE_ID,
|
||||
'-forwarding_pipeline_configs_file=%s/config.txt' % config_dir,
|
||||
'-persistent_config_dir=' + config_dir,
|
||||
'-initial_pipeline=' + stratumRoot + STRATUM_INIT_PIPELINE,
|
||||
'-cpu_port=%s' % self.cpuPort,
|
||||
'-external_hercules_urls=0.0.0.0:%d' % self.grpcPort
|
||||
]
|
||||
for port, intf in self.intfs.items():
|
||||
if not intf.IP():
|
||||
args.append('%d@%s' % (port, intf.name))
|
||||
return " ".join(args)
|
||||
|
||||
def bmv2Args(self):
|
||||
args = ['--device-id %s' % str(BMV2_DEFAULT_DEVICE_ID)]
|
||||
for port, intf in self.intfs.items():
|
||||
if not intf.IP():
|
||||
@ -299,12 +371,17 @@ class ONOSBmv2Switch(Switch):
|
||||
while True:
|
||||
result = sock.connect_ex(('127.0.0.1', self.grpcPort))
|
||||
if result == 0:
|
||||
# No new line
|
||||
sys.stdout.write("⚡️ %s @ %d" % (self.targetName, self.bmv2popen.pid))
|
||||
sys.stdout.flush()
|
||||
# The port is open. Let's go! (Close socket first)
|
||||
sock.close()
|
||||
break
|
||||
# Port is not open yet. If there is time, we wait a bit.
|
||||
if endtime > time.time():
|
||||
time.sleep(0.1)
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
time.sleep(0.05)
|
||||
else:
|
||||
# Time's up.
|
||||
raise Exception("Switch did not start before timeout")
|
||||
@ -331,23 +408,35 @@ class ONOSBmv2Switch(Switch):
|
||||
return random.choice(clist).IP()
|
||||
|
||||
def killBmv2(self, log=False):
|
||||
self.stopped = True
|
||||
if self.bmv2popen is not None:
|
||||
self.bmv2popen.kill()
|
||||
self.bmv2popen.terminate()
|
||||
self.bmv2popen.wait()
|
||||
self.bmv2popen = None
|
||||
if self.logfd is not None:
|
||||
if log:
|
||||
self.logfd.write("*** PROCESS TERMINATED BY MININET ***\n")
|
||||
self.logfd.close()
|
||||
self.logfd = None
|
||||
|
||||
def cleanupTmpFiles(self):
|
||||
self.cmd("rm -f /tmp/bmv2-%s-*" % self.name)
|
||||
self.cmd("rm -rf /tmp/bmv2-%s-*" % self.name)
|
||||
|
||||
def stop(self, deleteIntfs=True):
|
||||
"""Terminate switch."""
|
||||
self.stopped = True
|
||||
self.killBmv2(log=True)
|
||||
Switch.stop(self, deleteIntfs)
|
||||
|
||||
|
||||
class ONOSStratumSwitch(ONOSBmv2Switch):
|
||||
def __init__(self, name, **kwargs):
|
||||
kwargs["stratum"] = True
|
||||
super(ONOSStratumSwitch, self).__init__(name, **kwargs)
|
||||
|
||||
|
||||
# Exports for bin/mn
|
||||
switches = {'onosbmv2': ONOSBmv2Switch}
|
||||
switches = {
|
||||
'onosbmv2': ONOSBmv2Switch,
|
||||
'stratum': ONOSStratumSwitch,
|
||||
}
|
||||
hosts = {'onoshost': ONOSHost}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user