summaryrefslogtreecommitdiff
path: root/tests/topotests/lib/topotest.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/topotests/lib/topotest.py')
-rw-r--r--tests/topotests/lib/topotest.py835
1 files changed, 517 insertions, 318 deletions
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
index 9e1d344687..fab101cb25 100644
--- a/tests/topotests/lib/topotest.py
+++ b/tests/topotests/lib/topotest.py
@@ -50,6 +50,7 @@ from mininet.log import setLogLevel, info
from mininet.cli import CLI
from mininet.link import Intf
+
class json_cmp_result(object):
"json_cmp result class for better assertion messages"
@@ -66,7 +67,7 @@ class json_cmp_result(object):
return len(self.errors) > 0
def __str__(self):
- return '\n'.join(self.errors)
+ return "\n".join(self.errors)
def json_diff(d1, d2):
@@ -74,12 +75,12 @@ def json_diff(d1, d2):
Returns a string with the difference between JSON data.
"""
json_format_opts = {
- 'indent': 4,
- 'sort_keys': True,
+ "indent": 4,
+ "sort_keys": True,
}
dstr1 = json.dumps(d1, **json_format_opts)
dstr2 = json.dumps(d2, **json_format_opts)
- return difflines(dstr2, dstr1, title1='Expected value', title2='Current value', n=0)
+ return difflines(dstr2, dstr1, title1="Expected value", title2="Current value", n=0)
def _json_list_cmp(list1, list2, parent, result):
@@ -87,18 +88,21 @@ def _json_list_cmp(list1, list2, parent, result):
# Check second list2 type
if not isinstance(list1, type([])) or not isinstance(list2, type([])):
result.add_error(
- '{} has different type than expected '.format(parent) +
- '(have {}, expected {}):\n{}'.format(
- type(list1), type(list2), json_diff(list1, list2)))
+ "{} has different type than expected ".format(parent)
+ + "(have {}, expected {}):\n{}".format(
+ type(list1), type(list2), json_diff(list1, list2)
+ )
+ )
return
# Check list size
if len(list2) > len(list1):
result.add_error(
- '{} too few items '.format(parent) +
- '(have {}, expected {}:\n {})'.format(
- len(list1), len(list2),
- json_diff(list1, list2)))
+ "{} too few items ".format(parent)
+ + "(have {}, expected {}:\n {})".format(
+ len(list1), len(list2), json_diff(list1, list2)
+ )
+ )
return
# List all unmatched items errors
@@ -106,7 +110,7 @@ def _json_list_cmp(list1, list2, parent, result):
for expected in list2:
matched = False
for value in list1:
- if json_cmp({'json': value}, {'json': expected}) is None:
+ if json_cmp({"json": value}, {"json": expected}) is None:
matched = True
break
@@ -116,8 +120,8 @@ def _json_list_cmp(list1, list2, parent, result):
# If there are unmatched items, error out.
if unmatched:
result.add_error(
- '{} value is different (\n{})'.format(
- parent, json_diff(list1, list2)))
+ "{} value is different (\n{})".format(parent, json_diff(list1, list2))
+ )
def json_cmp(d1, d2):
@@ -131,7 +135,7 @@ def json_cmp(d1, d2):
Note: key absence can be tested by adding a key with value `None`.
"""
- squeue = [(d1, d2, 'json')]
+ squeue = [(d1, d2, "json")]
result = json_cmp_result()
for s in squeue:
@@ -150,23 +154,33 @@ def json_cmp(d1, d2):
s2_req = set([key for key in nd2 if nd2[key] is not None])
diff = s2_req - s1
if diff != set({}):
- result.add_error('expected key(s) {} in {} (have {}):\n{}'.format(
- str(list(diff)), parent, str(list(s1)), json_diff(nd1, nd2)))
+ result.add_error(
+ "expected key(s) {} in {} (have {}):\n{}".format(
+ str(list(diff)), parent, str(list(s1)), json_diff(nd1, nd2)
+ )
+ )
for key in s2.intersection(s1):
# Test for non existence of key in d2
if nd2[key] is None:
- result.add_error('"{}" should not exist in {} (have {}):\n{}'.format(
- key, parent, str(s1), json_diff(nd1[key], nd2[key])))
+ result.add_error(
+ '"{}" should not exist in {} (have {}):\n{}'.format(
+ key, parent, str(s1), json_diff(nd1[key], nd2[key])
+ )
+ )
continue
# If nd1 key is a dict, we have to recurse in it later.
if isinstance(nd2[key], type({})):
if not isinstance(nd1[key], type({})):
result.add_error(
- '{}["{}"] has different type than expected '.format(parent, key) +
- '(have {}, expected {}):\n{}'.format(
- type(nd1[key]), type(nd2[key]), json_diff(nd1[key], nd2[key])))
+ '{}["{}"] has different type than expected '.format(parent, key)
+ + "(have {}, expected {}):\n{}".format(
+ type(nd1[key]),
+ type(nd2[key]),
+ json_diff(nd1[key], nd2[key]),
+ )
+ )
continue
nparent = '{}["{}"]'.format(parent, key)
squeue.append((nd1[key], nd2[key], nparent))
@@ -181,7 +195,9 @@ def json_cmp(d1, d2):
if nd1[key] != nd2[key]:
result.add_error(
'{}["{}"] value is different (\n{})'.format(
- parent, key, json_diff(nd1[key], nd2[key])))
+ parent, key, json_diff(nd1[key], nd2[key])
+ )
+ )
continue
if result.has_errors():
@@ -194,10 +210,12 @@ def router_output_cmp(router, cmd, expected):
"""
Runs `cmd` in router and compares the output with `expected`.
"""
- return difflines(normalize_text(router.vtysh_cmd(cmd)),
- normalize_text(expected),
- title1="Current output",
- title2="Expected output")
+ return difflines(
+ normalize_text(router.vtysh_cmd(cmd)),
+ normalize_text(expected),
+ title1="Current output",
+ title2="Expected output",
+ )
def router_json_cmp(router, cmd, data):
@@ -232,7 +250,9 @@ def run_and_expect(func, what, count=20, wait=3):
logger.info(
"'{}' polling started (interval {} secs, maximum wait {} secs)".format(
- func_name, wait, int(wait * count)))
+ func_name, wait, int(wait * count)
+ )
+ )
while count > 0:
result = func()
@@ -242,13 +262,17 @@ def run_and_expect(func, what, count=20, wait=3):
continue
end_time = time.time()
- logger.info("'{}' succeeded after {:.2f} seconds".format(
- func_name, end_time - start_time))
+ logger.info(
+ "'{}' succeeded after {:.2f} seconds".format(
+ func_name, end_time - start_time
+ )
+ )
return (True, result)
end_time = time.time()
- logger.error("'{}' failed after {:.2f} seconds".format(
- func_name, end_time - start_time))
+ logger.error(
+ "'{}' failed after {:.2f} seconds".format(func_name, end_time - start_time)
+ )
return (False, result)
@@ -273,12 +297,16 @@ def run_and_expect_type(func, etype, count=20, wait=3, avalue=None):
logger.info(
"'{}' polling started (interval {} secs, maximum wait {} secs)".format(
- func_name, wait, int(wait * count)))
+ func_name, wait, int(wait * count)
+ )
+ )
while count > 0:
result = func()
if not isinstance(result, etype):
- logger.debug("Expected result type '{}' got '{}' instead".format(etype, type(result)))
+ logger.debug(
+ "Expected result type '{}' got '{}' instead".format(etype, type(result))
+ )
time.sleep(wait)
count -= 1
continue
@@ -290,13 +318,17 @@ def run_and_expect_type(func, etype, count=20, wait=3, avalue=None):
continue
end_time = time.time()
- logger.info("'{}' succeeded after {:.2f} seconds".format(
- func_name, end_time - start_time))
+ logger.info(
+ "'{}' succeeded after {:.2f} seconds".format(
+ func_name, end_time - start_time
+ )
+ )
return (True, result)
end_time = time.time()
- logger.error("'{}' failed after {:.2f} seconds".format(
- func_name, end_time - start_time))
+ logger.error(
+ "'{}' failed after {:.2f} seconds".format(func_name, end_time - start_time)
+ )
return (False, result)
@@ -305,12 +337,15 @@ def int2dpid(dpid):
try:
dpid = hex(dpid)[2:]
- dpid = '0'*(16-len(dpid))+dpid
+ dpid = "0" * (16 - len(dpid)) + dpid
return dpid
except IndexError:
- raise Exception('Unable to derive default datapath ID - '
- 'please either specify a dpid or use a '
- 'canonical switch name such as s23.')
+ raise Exception(
+ "Unable to derive default datapath ID - "
+ "please either specify a dpid or use a "
+ "canonical switch name such as s23."
+ )
+
def pid_exists(pid):
"Check whether pid exists in the current process table."
@@ -333,70 +368,78 @@ def pid_exists(pid):
else:
return True
+
def get_textdiff(text1, text2, title1="", title2="", **opts):
"Returns empty string if same or formatted diff"
- diff = '\n'.join(difflib.unified_diff(text1, text2,
- fromfile=title1, tofile=title2, **opts))
+ diff = "\n".join(
+ difflib.unified_diff(text1, text2, fromfile=title1, tofile=title2, **opts)
+ )
# Clean up line endings
diff = os.linesep.join([s for s in diff.splitlines() if s])
return diff
-def difflines(text1, text2, title1='', title2='', **opts):
+
+def difflines(text1, text2, title1="", title2="", **opts):
"Wrapper for get_textdiff to avoid string transformations."
- text1 = ('\n'.join(text1.rstrip().splitlines()) + '\n').splitlines(1)
- text2 = ('\n'.join(text2.rstrip().splitlines()) + '\n').splitlines(1)
+ text1 = ("\n".join(text1.rstrip().splitlines()) + "\n").splitlines(1)
+ text2 = ("\n".join(text2.rstrip().splitlines()) + "\n").splitlines(1)
return get_textdiff(text1, text2, title1, title2, **opts)
+
def get_file(content):
"""
Generates a temporary file in '/tmp' with `content` and returns the file name.
"""
- fde = tempfile.NamedTemporaryFile(mode='w', delete=False)
+ fde = tempfile.NamedTemporaryFile(mode="w", delete=False)
fname = fde.name
fde.write(content)
fde.close()
return fname
+
def normalize_text(text):
"""
Strips formating spaces/tabs, carriage returns and trailing whitespace.
"""
- text = re.sub(r'[ \t]+', ' ', text)
- text = re.sub(r'\r', '', text)
+ text = re.sub(r"[ \t]+", " ", text)
+ text = re.sub(r"\r", "", text)
# Remove whitespace in the middle of text.
- text = re.sub(r'[ \t]+\n', '\n', text)
+ text = re.sub(r"[ \t]+\n", "\n", text)
# Remove whitespace at the end of the text.
text = text.rstrip()
return text
+
def module_present_linux(module, load):
"""
Returns whether `module` is present.
If `load` is true, it will try to load it via modprobe.
"""
- with open('/proc/modules', 'r') as modules_file:
- if module.replace('-','_') in modules_file.read():
+ with open("/proc/modules", "r") as modules_file:
+ if module.replace("-", "_") in modules_file.read():
return True
- cmd = '/sbin/modprobe {}{}'.format('' if load else '-n ',
- module)
+ cmd = "/sbin/modprobe {}{}".format("" if load else "-n ", module)
if os.system(cmd) != 0:
return False
else:
return True
+
def module_present_freebsd(module, load):
return True
+
def module_present(module, load=True):
if sys.platform.startswith("linux"):
return module_present_linux(module, load)
elif sys.platform.startswith("freebsd"):
return module_present_freebsd(module, load)
+
def version_cmp(v1, v2):
"""
Compare two version strings and returns:
@@ -407,15 +450,15 @@ def version_cmp(v1, v2):
Raises `ValueError` if versions are not well formated.
"""
- vregex = r'(?P<whole>\d+(\.(\d+))*)'
+ vregex = r"(?P<whole>\d+(\.(\d+))*)"
v1m = re.match(vregex, v1)
v2m = re.match(vregex, v2)
if v1m is None or v2m is None:
raise ValueError("got a invalid version string")
# Split values
- v1g = v1m.group('whole').split('.')
- v2g = v2m.group('whole').split('.')
+ v1g = v1m.group("whole").split(".")
+ v2g = v2m.group("whole").split(".")
# Get the longest version string
vnum = len(v1g)
@@ -452,35 +495,42 @@ def version_cmp(v1, v2):
return -1
return 0
+
def interface_set_status(node, ifacename, ifaceaction=False, vrf_name=None):
if ifaceaction:
- str_ifaceaction = 'no shutdown'
+ str_ifaceaction = "no shutdown"
else:
- str_ifaceaction = 'shutdown'
+ str_ifaceaction = "shutdown"
if vrf_name == None:
- cmd = 'vtysh -c \"configure terminal\" -c \"interface {0}\" -c \"{1}\"'.format(ifacename, str_ifaceaction)
+ cmd = 'vtysh -c "configure terminal" -c "interface {0}" -c "{1}"'.format(
+ ifacename, str_ifaceaction
+ )
else:
- cmd = 'vtysh -c \"configure terminal\" -c \"interface {0} vrf {1}\" -c \"{2}\"'.format(ifacename, vrf_name, str_ifaceaction)
+ cmd = 'vtysh -c "configure terminal" -c "interface {0} vrf {1}" -c "{2}"'.format(
+ ifacename, vrf_name, str_ifaceaction
+ )
node.run(cmd)
+
def ip4_route_zebra(node, vrf_name=None):
"""
Gets an output of 'show ip route' command. It can be used
with comparing the output to a reference
"""
if vrf_name == None:
- tmp = node.vtysh_cmd('show ip route')
+ tmp = node.vtysh_cmd("show ip route")
else:
- tmp = node.vtysh_cmd('show ip route vrf {0}'.format(vrf_name))
+ tmp = node.vtysh_cmd("show ip route vrf {0}".format(vrf_name))
output = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", tmp)
lines = output.splitlines()
header_found = False
while lines and (not lines[0].strip() or not header_found):
- if '> - selected route' in lines[0]:
+ if "> - selected route" in lines[0]:
header_found = True
lines = lines[1:]
- return '\n'.join(lines)
+ return "\n".join(lines)
+
def ip6_route_zebra(node, vrf_name=None):
"""
@@ -489,40 +539,42 @@ def ip6_route_zebra(node, vrf_name=None):
"""
if vrf_name == None:
- tmp = node.vtysh_cmd('show ipv6 route')
+ tmp = node.vtysh_cmd("show ipv6 route")
else:
- tmp = node.vtysh_cmd('show ipv6 route vrf {0}'.format(vrf_name))
+ tmp = node.vtysh_cmd("show ipv6 route vrf {0}".format(vrf_name))
# Mask out timestamp
output = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", tmp)
# Mask out the link-local addresses
- output = re.sub(r'fe80::[^ ]+,', 'fe80::XXXX:XXXX:XXXX:XXXX,', output)
+ output = re.sub(r"fe80::[^ ]+,", "fe80::XXXX:XXXX:XXXX:XXXX,", output)
lines = output.splitlines()
header_found = False
while lines and (not lines[0].strip() or not header_found):
- if '> - selected route' in lines[0]:
+ if "> - selected route" in lines[0]:
header_found = True
lines = lines[1:]
- return '\n'.join(lines)
+ return "\n".join(lines)
def proto_name_to_number(protocol):
return {
- 'bgp': '186',
- 'isis': '187',
- 'ospf': '188',
- 'rip': '189',
- 'ripng': '190',
- 'nhrp': '191',
- 'eigrp': '192',
- 'ldp': '193',
- 'sharp': '194',
- 'pbr': '195',
- 'static': '196'
- }.get(protocol, protocol) # default return same as input
+ "bgp": "186",
+ "isis": "187",
+ "ospf": "188",
+ "rip": "189",
+ "ripng": "190",
+ "nhrp": "191",
+ "eigrp": "192",
+ "ldp": "193",
+ "sharp": "194",
+ "pbr": "195",
+ "static": "196",
+ }.get(
+ protocol, protocol
+ ) # default return same as input
def ip4_route(node):
@@ -543,28 +595,29 @@ def ip4_route(node):
}
}
"""
- output = normalize_text(node.run('ip route')).splitlines()
+ output = normalize_text(node.run("ip route")).splitlines()
result = {}
for line in output:
- columns = line.split(' ')
+ columns = line.split(" ")
route = result[columns[0]] = {}
prev = None
for column in columns:
- if prev == 'dev':
- route['dev'] = column
- if prev == 'via':
- route['via'] = column
- if prev == 'proto':
+ if prev == "dev":
+ route["dev"] = column
+ if prev == "via":
+ route["via"] = column
+ if prev == "proto":
# translate protocol names back to numbers
- route['proto'] = proto_name_to_number(column)
- if prev == 'metric':
- route['metric'] = column
- if prev == 'scope':
- route['scope'] = column
+ route["proto"] = proto_name_to_number(column)
+ if prev == "metric":
+ route["metric"] = column
+ if prev == "scope":
+ route["scope"] = column
prev = column
return result
+
def ip6_route(node):
"""
Gets a structured return of the command 'ip -6 route'. It can be used in
@@ -582,80 +635,103 @@ def ip6_route(node):
}
}
"""
- output = normalize_text(node.run('ip -6 route')).splitlines()
+ output = normalize_text(node.run("ip -6 route")).splitlines()
result = {}
for line in output:
- columns = line.split(' ')
+ columns = line.split(" ")
route = result[columns[0]] = {}
prev = None
for column in columns:
- if prev == 'dev':
- route['dev'] = column
- if prev == 'via':
- route['via'] = column
- if prev == 'proto':
+ if prev == "dev":
+ route["dev"] = column
+ if prev == "via":
+ route["via"] = column
+ if prev == "proto":
# translate protocol names back to numbers
- route['proto'] = proto_name_to_number(column)
- if prev == 'metric':
- route['metric'] = column
- if prev == 'pref':
- route['pref'] = column
+ route["proto"] = proto_name_to_number(column)
+ if prev == "metric":
+ route["metric"] = column
+ if prev == "pref":
+ route["pref"] = column
prev = column
return result
+
def sleep(amount, reason=None):
"""
Sleep wrapper that registers in the log the amount of sleep
"""
if reason is None:
- logger.info('Sleeping for {} seconds'.format(amount))
+ logger.info("Sleeping for {} seconds".format(amount))
else:
- logger.info(reason + ' ({} seconds)'.format(amount))
+ logger.info(reason + " ({} seconds)".format(amount))
time.sleep(amount)
+
def checkAddressSanitizerError(output, router, component):
"Checks for AddressSanitizer in output. If found, then logs it and returns true, false otherwise"
- addressSantizerError = re.search('(==[0-9]+==)ERROR: AddressSanitizer: ([^\s]*) ', output)
+ addressSantizerError = re.search(
+ "(==[0-9]+==)ERROR: AddressSanitizer: ([^\s]*) ", output
+ )
if addressSantizerError:
- sys.stderr.write("%s: %s triggered an exception by AddressSanitizer\n" % (router, component))
+ sys.stderr.write(
+ "%s: %s triggered an exception by AddressSanitizer\n" % (router, component)
+ )
# Sanitizer Error found in log
pidMark = addressSantizerError.group(1)
- addressSantizerLog = re.search('%s(.*)%s' % (pidMark, pidMark), output, re.DOTALL)
+ addressSantizerLog = re.search(
+ "%s(.*)%s" % (pidMark, pidMark), output, re.DOTALL
+ )
if addressSantizerLog:
- callingTest = os.path.basename(sys._current_frames().values()[0].f_back.f_back.f_globals['__file__'])
+ callingTest = os.path.basename(
+ sys._current_frames().values()[0].f_back.f_back.f_globals["__file__"]
+ )
callingProc = sys._getframe(2).f_code.co_name
with open("/tmp/AddressSanitzer.txt", "a") as addrSanFile:
- sys.stderr.write('\n'.join(addressSantizerLog.group(1).splitlines()) + '\n')
+ sys.stderr.write(
+ "\n".join(addressSantizerLog.group(1).splitlines()) + "\n"
+ )
addrSanFile.write("## Error: %s\n\n" % addressSantizerError.group(2))
- addrSanFile.write("### AddressSanitizer error in topotest `%s`, test `%s`, router `%s`\n\n" % (callingTest, callingProc, router))
- addrSanFile.write(' '+ '\n '.join(addressSantizerLog.group(1).splitlines()) + '\n')
+ addrSanFile.write(
+ "### AddressSanitizer error in topotest `%s`, test `%s`, router `%s`\n\n"
+ % (callingTest, callingProc, router)
+ )
+ addrSanFile.write(
+ " "
+ + "\n ".join(addressSantizerLog.group(1).splitlines())
+ + "\n"
+ )
addrSanFile.write("\n---------------\n")
return True
return False
+
def addRouter(topo, name):
"Adding a FRRouter (or Quagga) to Topology"
- MyPrivateDirs = ['/etc/frr',
- '/etc/quagga',
- '/var/run/frr',
- '/var/run/quagga',
- '/var/log']
+ MyPrivateDirs = [
+ "/etc/frr",
+ "/etc/quagga",
+ "/var/run/frr",
+ "/var/run/quagga",
+ "/var/log",
+ ]
if sys.platform.startswith("linux"):
return topo.addNode(name, cls=LinuxRouter, privateDirs=MyPrivateDirs)
elif sys.platform.startswith("freebsd"):
return topo.addNode(name, cls=FreeBSDRouter, privateDirs=MyPrivateDirs)
+
def set_sysctl(node, sysctl, value):
"Set a sysctl value and return None on success or an error string"
- valuestr = '{}'.format(value)
+ valuestr = "{}".format(value)
command = "sysctl {0}={1}".format(sysctl, valuestr)
cmdret = node.cmd(command)
- matches = re.search(r'([^ ]+) = ([^\s]+)', cmdret)
+ matches = re.search(r"([^ ]+) = ([^\s]+)", cmdret)
if matches is None:
return cmdret
if matches.group(1) != sysctl:
@@ -665,6 +741,7 @@ def set_sysctl(node, sysctl, value):
return None
+
def assert_sysctl(node, sysctl, value):
"Set and assert that the sysctl is set with the specified value."
assert set_sysctl(node, sysctl, value) is None
@@ -675,65 +752,81 @@ class Router(Node):
def __init__(self, name, **params):
super(Router, self).__init__(name, **params)
- self.logdir = params.get('logdir')
+ self.logdir = params.get("logdir")
# Backward compatibility:
# Load configuration defaults like topogen.
- self.config_defaults = configparser.ConfigParser({
- 'verbosity': 'info',
- 'frrdir': '/usr/lib/frr',
- 'quaggadir': '/usr/lib/quagga',
- 'routertype': 'frr',
- 'memleak_path': None,
- })
+ self.config_defaults = configparser.ConfigParser(
+ {
+ "verbosity": "info",
+ "frrdir": "/usr/lib/frr",
+ "quaggadir": "/usr/lib/quagga",
+ "routertype": "frr",
+ "memleak_path": None,
+ }
+ )
self.config_defaults.read(
- os.path.join(os.path.dirname(os.path.realpath(__file__)),
- '../pytest.ini')
+ os.path.join(os.path.dirname(os.path.realpath(__file__)), "../pytest.ini")
)
# If this topology is using old API and doesn't have logdir
# specified, then attempt to generate an unique logdir.
if self.logdir is None:
- cur_test = os.environ['PYTEST_CURRENT_TEST']
- self.logdir = ('/tmp/topotests/' +
- cur_test[0:cur_test.find(".py")].replace('/', '.'))
+ cur_test = os.environ["PYTEST_CURRENT_TEST"]
+ self.logdir = "/tmp/topotests/" + cur_test[
+ 0 : cur_test.find(".py")
+ ].replace("/", ".")
# If the logdir is not created, then create it and set the
# appropriated permissions.
if not os.path.isdir(self.logdir):
- os.system('mkdir -p ' + self.logdir + '/' + name)
- os.system('chmod -R go+rw /tmp/topotests')
+ os.system("mkdir -p " + self.logdir + "/" + name)
+ os.system("chmod -R go+rw /tmp/topotests")
self.daemondir = None
self.hasmpls = False
- self.routertype = 'frr'
- self.daemons = {'zebra': 0, 'ripd': 0, 'ripngd': 0, 'ospfd': 0,
- 'ospf6d': 0, 'isisd': 0, 'bgpd': 0, 'pimd': 0,
- 'ldpd': 0, 'eigrpd': 0, 'nhrpd': 0, 'staticd': 0,
- 'bfdd': 0, 'sharpd': 0}
- self.daemons_options = {'zebra': ''}
+ self.routertype = "frr"
+ self.daemons = {
+ "zebra": 0,
+ "ripd": 0,
+ "ripngd": 0,
+ "ospfd": 0,
+ "ospf6d": 0,
+ "isisd": 0,
+ "bgpd": 0,
+ "pimd": 0,
+ "ldpd": 0,
+ "eigrpd": 0,
+ "nhrpd": 0,
+ "staticd": 0,
+ "bfdd": 0,
+ "sharpd": 0,
+ }
+ self.daemons_options = {"zebra": ""}
self.reportCores = True
self.version = None
def _config_frr(self, **params):
"Configure FRR binaries"
- self.daemondir = params.get('frrdir')
+ self.daemondir = params.get("frrdir")
if self.daemondir is None:
- self.daemondir = self.config_defaults.get('topogen', 'frrdir')
+ self.daemondir = self.config_defaults.get("topogen", "frrdir")
- zebra_path = os.path.join(self.daemondir, 'zebra')
+ zebra_path = os.path.join(self.daemondir, "zebra")
if not os.path.isfile(zebra_path):
raise Exception("FRR zebra binary doesn't exist at {}".format(zebra_path))
def _config_quagga(self, **params):
"Configure Quagga binaries"
- self.daemondir = params.get('quaggadir')
+ self.daemondir = params.get("quaggadir")
if self.daemondir is None:
- self.daemondir = self.config_defaults.get('topogen', 'quaggadir')
+ self.daemondir = self.config_defaults.get("topogen", "quaggadir")
- zebra_path = os.path.join(self.daemondir, 'zebra')
+ zebra_path = os.path.join(self.daemondir, "zebra")
if not os.path.isfile(zebra_path):
- raise Exception("Quagga zebra binary doesn't exist at {}".format(zebra_path))
+ raise Exception(
+ "Quagga zebra binary doesn't exist at {}".format(zebra_path)
+ )
# pylint: disable=W0221
# Some params are only meaningful for the parent class.
@@ -741,28 +834,27 @@ class Router(Node):
super(Router, self).config(**params)
# User did not specify the daemons directory, try to autodetect it.
- self.daemondir = params.get('daemondir')
+ self.daemondir = params.get("daemondir")
if self.daemondir is None:
- self.routertype = params.get('routertype',
- self.config_defaults.get(
- 'topogen',
- 'routertype'))
- if self.routertype == 'quagga':
+ self.routertype = params.get(
+ "routertype", self.config_defaults.get("topogen", "routertype")
+ )
+ if self.routertype == "quagga":
self._config_quagga(**params)
else:
self._config_frr(**params)
else:
# Test the provided path
- zpath = os.path.join(self.daemondir, 'zebra')
+ zpath = os.path.join(self.daemondir, "zebra")
if not os.path.isfile(zpath):
- raise Exception('No zebra binary found in {}'.format(zpath))
+ raise Exception("No zebra binary found in {}".format(zpath))
# Allow user to specify routertype when the path was specified.
- if params.get('routertype') is not None:
- self.routertype = params.get('routertype')
+ if params.get("routertype") is not None:
+ self.routertype = params.get("routertype")
- self.cmd('ulimit -c unlimited')
+ self.cmd("ulimit -c unlimited")
# Set ownership of config files
- self.cmd('chown {0}:{0}vty /etc/{0}'.format(self.routertype))
+ self.cmd("chown {0}:{0}vty /etc/{0}".format(self.routertype))
def terminate(self):
# Delete Running Quagga or FRR Daemons
@@ -772,62 +864,66 @@ class Router(Node):
# self.cmd('kill -7 `cat %s`' % d.rstrip())
# self.waitOutput()
# Disable forwarding
- set_sysctl(self, 'net.ipv4.ip_forward', 0)
- set_sysctl(self, 'net.ipv6.conf.all.forwarding', 0)
+ set_sysctl(self, "net.ipv4.ip_forward", 0)
+ set_sysctl(self, "net.ipv6.conf.all.forwarding", 0)
super(Router, self).terminate()
- os.system('chmod -R go+rw /tmp/topotests')
+ os.system("chmod -R go+rw /tmp/topotests")
- def stopRouter(self, wait=True, assertOnError=True, minErrorVersion='5.1'):
+ def stopRouter(self, wait=True, assertOnError=True, minErrorVersion="5.1"):
# Stop Running Quagga or FRR Daemons
- rundaemons = self.cmd('ls -1 /var/run/%s/*.pid' % self.routertype)
+ rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype)
errors = ""
if re.search(r"No such file or directory", rundaemons):
return errors
if rundaemons is not None:
numRunning = 0
for d in StringIO.StringIO(rundaemons):
- daemonpid = self.cmd('cat %s' % d.rstrip()).rstrip()
- if (daemonpid.isdigit() and pid_exists(int(daemonpid))):
- logger.info('{}: stopping {}'.format(
- self.name,
- os.path.basename(d.rstrip().rsplit(".", 1)[0])
- ))
- self.cmd('kill -TERM %s' % daemonpid)
+ daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
+ if daemonpid.isdigit() and pid_exists(int(daemonpid)):
+ logger.info(
+ "{}: stopping {}".format(
+ self.name, os.path.basename(d.rstrip().rsplit(".", 1)[0])
+ )
+ )
+ self.cmd("kill -TERM %s" % daemonpid)
self.waitOutput()
if pid_exists(int(daemonpid)):
numRunning += 1
if wait and numRunning > 0:
- sleep(2, '{}: waiting for daemons stopping'.format(self.name))
+ sleep(2, "{}: waiting for daemons stopping".format(self.name))
# 2nd round of kill if daemons didn't exit
for d in StringIO.StringIO(rundaemons):
- daemonpid = self.cmd('cat %s' % d.rstrip()).rstrip()
- if (daemonpid.isdigit() and pid_exists(int(daemonpid))):
- logger.info('{}: killing {}'.format(
- self.name,
- os.path.basename(d.rstrip().rsplit(".", 1)[0])
- ))
- self.cmd('kill -7 %s' % daemonpid)
+ daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
+ if daemonpid.isdigit() and pid_exists(int(daemonpid)):
+ logger.info(
+ "{}: killing {}".format(
+ self.name,
+ os.path.basename(d.rstrip().rsplit(".", 1)[0]),
+ )
+ )
+ self.cmd("kill -7 %s" % daemonpid)
self.waitOutput()
- self.cmd('rm -- {}'.format(d.rstrip()))
+ self.cmd("rm -- {}".format(d.rstrip()))
if wait:
- errors = self.checkRouterCores(reportOnce=True)
- if self.checkRouterVersion('<', minErrorVersion):
- #ignore errors in old versions
- errors = ""
- if assertOnError and len(errors) > 0:
- assert "Errors found - details follow:" == 0, errors
+ errors = self.checkRouterCores(reportOnce=True)
+ if self.checkRouterVersion("<", minErrorVersion):
+ # ignore errors in old versions
+ errors = ""
+ if assertOnError and len(errors) > 0:
+ assert "Errors found - details follow:" == 0, errors
return errors
def removeIPs(self):
for interface in self.intfNames():
- self.cmd('ip address flush', interface)
+ self.cmd("ip address flush", interface)
def checkCapability(self, daemon, param):
if param is not None:
daemon_path = os.path.join(self.daemondir, daemon)
- daemon_search_option = param.replace('-','')
- output = self.cmd('{0} -h | grep {1}'.format(
- daemon_path, daemon_search_option))
+ daemon_search_option = param.replace("-", "")
+ output = self.cmd(
+ "{0} -h | grep {1}".format(daemon_path, daemon_search_option)
+ )
if daemon_search_option not in output:
return False
return True
@@ -839,74 +935,89 @@ class Router(Node):
if param is not None:
self.daemons_options[daemon] = param
if source is None:
- self.cmd('touch /etc/%s/%s.conf' % (self.routertype, daemon))
+ self.cmd("touch /etc/%s/%s.conf" % (self.routertype, daemon))
self.waitOutput()
else:
- self.cmd('cp %s /etc/%s/%s.conf' % (source, self.routertype, daemon))
+ self.cmd("cp %s /etc/%s/%s.conf" % (source, self.routertype, daemon))
self.waitOutput()
- self.cmd('chmod 640 /etc/%s/%s.conf' % (self.routertype, daemon))
+ self.cmd("chmod 640 /etc/%s/%s.conf" % (self.routertype, daemon))
self.waitOutput()
- self.cmd('chown %s:%s /etc/%s/%s.conf' % (self.routertype, self.routertype, self.routertype, daemon))
+ self.cmd(
+ "chown %s:%s /etc/%s/%s.conf"
+ % (self.routertype, self.routertype, self.routertype, daemon)
+ )
self.waitOutput()
- if (daemon == 'zebra') and (self.daemons['staticd'] == 0):
+ if (daemon == "zebra") and (self.daemons["staticd"] == 0):
# Add staticd with zebra - if it exists
- staticd_path = os.path.join(self.daemondir, 'staticd')
+ staticd_path = os.path.join(self.daemondir, "staticd")
if os.path.isfile(staticd_path):
- self.daemons['staticd'] = 1
- self.daemons_options['staticd'] = ''
+ self.daemons["staticd"] = 1
+ self.daemons_options["staticd"] = ""
# Auto-Started staticd has no config, so it will read from zebra config
else:
- logger.info('No daemon {} known'.format(daemon))
+ logger.info("No daemon {} known".format(daemon))
# print "Daemons after:", self.daemons
def startRouter(self, tgen=None):
# Disable integrated-vtysh-config
- self.cmd('echo "no service integrated-vtysh-config" >> /etc/%s/vtysh.conf' % self.routertype)
- self.cmd('chown %s:%svty /etc/%s/vtysh.conf' % (self.routertype, self.routertype, self.routertype))
+ self.cmd(
+ 'echo "no service integrated-vtysh-config" >> /etc/%s/vtysh.conf'
+ % self.routertype
+ )
+ self.cmd(
+ "chown %s:%svty /etc/%s/vtysh.conf"
+ % (self.routertype, self.routertype, self.routertype)
+ )
# TODO remove the following lines after all tests are migrated to Topogen.
# Try to find relevant old logfiles in /tmp and delete them
- map(os.remove, glob.glob('{}/{}/*.log'.format(self.logdir, self.name)))
+ map(os.remove, glob.glob("{}/{}/*.log".format(self.logdir, self.name)))
# Remove old core files
- map(os.remove, glob.glob('{}/{}/*.dmp'.format(self.logdir, self.name)))
+ map(os.remove, glob.glob("{}/{}/*.dmp".format(self.logdir, self.name)))
# Remove IP addresses from OS first - we have them in zebra.conf
self.removeIPs()
# If ldp is used, check for LDP to be compiled and Linux Kernel to be 4.5 or higher
# No error - but return message and skip all the tests
- if self.daemons['ldpd'] == 1:
- ldpd_path = os.path.join(self.daemondir, 'ldpd')
+ if self.daemons["ldpd"] == 1:
+ ldpd_path = os.path.join(self.daemondir, "ldpd")
if not os.path.isfile(ldpd_path):
logger.info("LDP Test, but no ldpd compiled or installed")
return "LDP Test, but no ldpd compiled or installed"
- if version_cmp(platform.release(), '4.5') < 0:
+ if version_cmp(platform.release(), "4.5") < 0:
logger.info("LDP Test need Linux Kernel 4.5 minimum")
return "LDP Test need Linux Kernel 4.5 minimum"
# Check if have mpls
if tgen != None:
self.hasmpls = tgen.hasmpls
if self.hasmpls != True:
- logger.info("LDP/MPLS Tests will be skipped, platform missing module(s)")
+ logger.info(
+ "LDP/MPLS Tests will be skipped, platform missing module(s)"
+ )
else:
# Test for MPLS Kernel modules available
self.hasmpls = False
- if not module_present('mpls-router'):
- logger.info('MPLS tests will not run (missing mpls-router kernel module)')
- elif not module_present('mpls-iptunnel'):
- logger.info('MPLS tests will not run (missing mpls-iptunnel kernel module)')
+ if not module_present("mpls-router"):
+ logger.info(
+ "MPLS tests will not run (missing mpls-router kernel module)"
+ )
+ elif not module_present("mpls-iptunnel"):
+ logger.info(
+ "MPLS tests will not run (missing mpls-iptunnel kernel module)"
+ )
else:
self.hasmpls = True
if self.hasmpls != True:
return "LDP/MPLS Tests need mpls kernel modules"
- self.cmd('echo 100000 > /proc/sys/net/mpls/platform_labels')
+ self.cmd("echo 100000 > /proc/sys/net/mpls/platform_labels")
- if self.daemons['eigrpd'] == 1:
- eigrpd_path = os.path.join(self.daemondir, 'eigrpd')
+ if self.daemons["eigrpd"] == 1:
+ eigrpd_path = os.path.join(self.daemondir, "eigrpd")
if not os.path.isfile(eigrpd_path):
logger.info("EIGRP Test, but no eigrpd compiled or installed")
return "EIGRP Test, but no eigrpd compiled or installed"
- if self.daemons['bfdd'] == 1:
- bfdd_path = os.path.join(self.daemondir, 'bfdd')
+ if self.daemons["bfdd"] == 1:
+ bfdd_path = os.path.join(self.daemondir, "bfdd")
if not os.path.isfile(bfdd_path):
logger.info("BFD Test, but no bfdd compiled or installed")
return "BFD Test, but no bfdd compiled or installed"
@@ -917,52 +1028,65 @@ class Router(Node):
def restartRouter(self):
# Starts actual daemons without init (ie restart)
# cd to per node directory
- self.cmd('cd {}/{}'.format(self.logdir, self.name))
- self.cmd('umask 000')
- #Re-enable to allow for report per run
+ self.cmd("cd {}/{}".format(self.logdir, self.name))
+ self.cmd("umask 000")
+ # Re-enable to allow for report per run
self.reportCores = True
if self.version == None:
- self.version = self.cmd(os.path.join(self.daemondir, 'bgpd')+' -v').split()[2]
- logger.info('{}: running version: {}'.format(self.name,self.version))
+ self.version = self.cmd(
+ os.path.join(self.daemondir, "bgpd") + " -v"
+ ).split()[2]
+ logger.info("{}: running version: {}".format(self.name, self.version))
# Start Zebra first
- if self.daemons['zebra'] == 1:
- zebra_path = os.path.join(self.daemondir, 'zebra')
- zebra_option = self.daemons_options['zebra']
- self.cmd('{0} {1} > zebra.out 2> zebra.err &'.format(
- zebra_path, zebra_option, self.logdir, self.name
- ))
+ if self.daemons["zebra"] == 1:
+ zebra_path = os.path.join(self.daemondir, "zebra")
+ zebra_option = self.daemons_options["zebra"]
+ self.cmd(
+ "{0} {1} > zebra.out 2> zebra.err &".format(
+ zebra_path, zebra_option, self.logdir, self.name
+ )
+ )
self.waitOutput()
- logger.debug('{}: {} zebra started'.format(self, self.routertype))
- sleep(1, '{}: waiting for zebra to start'.format(self.name))
+ logger.debug("{}: {} zebra started".format(self, self.routertype))
+ sleep(1, "{}: waiting for zebra to start".format(self.name))
# Start staticd next if required
- if self.daemons['staticd'] == 1:
- staticd_path = os.path.join(self.daemondir, 'staticd')
- staticd_option = self.daemons_options['staticd']
- self.cmd('{0} {1} > staticd.out 2> staticd.err &'.format(
- staticd_path, staticd_option, self.logdir, self.name
- ))
+ if self.daemons["staticd"] == 1:
+ staticd_path = os.path.join(self.daemondir, "staticd")
+ staticd_option = self.daemons_options["staticd"]
+ self.cmd(
+ "{0} {1} > staticd.out 2> staticd.err &".format(
+ staticd_path, staticd_option, self.logdir, self.name
+ )
+ )
self.waitOutput()
- logger.debug('{}: {} staticd started'.format(self, self.routertype))
- # Fix Link-Local Addresses
+ logger.debug("{}: {} staticd started".format(self, self.routertype))
+ # Fix Link-Local Addresses
# Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this
- self.cmd('for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; IFS=\':\'; set $mac; unset IFS; ip address add dev $i scope link fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64; done')
+ self.cmd(
+ "for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; IFS=':'; set $mac; unset IFS; ip address add dev $i scope link fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64; done"
+ )
# Now start all the other daemons
for daemon in self.daemons:
# Skip disabled daemons and zebra
- if self.daemons[daemon] == 0 or daemon == 'zebra' or daemon == 'staticd':
+ if self.daemons[daemon] == 0 or daemon == "zebra" or daemon == "staticd":
continue
daemon_path = os.path.join(self.daemondir, daemon)
- self.cmd('{0} {1} > {2}.out 2> {2}.err &'.format(
- daemon_path, self.daemons_options.get(daemon, ''), daemon
- ))
+ self.cmd(
+ "{0} {1} > {2}.out 2> {2}.err &".format(
+ daemon_path, self.daemons_options.get(daemon, ""), daemon
+ )
+ )
self.waitOutput()
- logger.debug('{}: {} {} started'.format(self, self.routertype, daemon))
+ logger.debug("{}: {} {} started".format(self, self.routertype, daemon))
+
def getStdErr(self, daemon):
- return self.getLog('err', daemon)
+ return self.getLog("err", daemon)
+
def getStdOut(self, daemon):
- return self.getLog('out', daemon)
+ return self.getLog("out", daemon)
+
def getLog(self, log, daemon):
- return self.cmd('cat {}/{}/{}.{}'.format(self.logdir, self.name, daemon, log))
+ return self.cmd("cat {}/{}/{}.{}".format(self.logdir, self.name, daemon, log))
def checkRouterCores(self, reportLeaks=True, reportOnce=False):
if reportOnce and not self.reportCores:
@@ -970,33 +1094,62 @@ class Router(Node):
reportMade = False
traces = ""
for daemon in self.daemons:
- if (self.daemons[daemon] == 1):
+ if self.daemons[daemon] == 1:
# Look for core file
- corefiles = glob.glob('{}/{}/{}_core*.dmp'.format(
- self.logdir, self.name, daemon))
- if (len(corefiles) > 0):
+ corefiles = glob.glob(
+ "{}/{}/{}_core*.dmp".format(self.logdir, self.name, daemon)
+ )
+ if len(corefiles) > 0:
daemon_path = os.path.join(self.daemondir, daemon)
- backtrace = subprocess.check_output([
- "gdb {} {} --batch -ex bt 2> /dev/null".format(daemon_path, corefiles[0])
- ], shell=True)
- sys.stderr.write("\n%s: %s crashed. Core file found - Backtrace follows:\n" % (self.name, daemon))
+ backtrace = subprocess.check_output(
+ [
+ "gdb {} {} --batch -ex bt 2> /dev/null".format(
+ daemon_path, corefiles[0]
+ )
+ ],
+ shell=True,
+ )
+ sys.stderr.write(
+ "\n%s: %s crashed. Core file found - Backtrace follows:\n"
+ % (self.name, daemon)
+ )
sys.stderr.write("%s" % backtrace)
- traces = traces + "\n%s: %s crashed. Core file found - Backtrace follows:\n%s" % (self.name, daemon, backtrace)
+ traces = (
+ traces
+ + "\n%s: %s crashed. Core file found - Backtrace follows:\n%s"
+ % (self.name, daemon, backtrace)
+ )
reportMade = True
elif reportLeaks:
log = self.getStdErr(daemon)
if "memstats" in log:
- sys.stderr.write("%s: %s has memory leaks:\n" % (self.name, daemon))
- traces = traces + "\n%s: %s has memory leaks:\n" % (self.name, daemon)
+ sys.stderr.write(
+ "%s: %s has memory leaks:\n" % (self.name, daemon)
+ )
+ traces = traces + "\n%s: %s has memory leaks:\n" % (
+ self.name,
+ daemon,
+ )
log = re.sub("core_handler: ", "", log)
- log = re.sub(r"(showing active allocations in memory group [a-zA-Z0-9]+)", r"\n ## \1", log)
+ log = re.sub(
+ r"(showing active allocations in memory group [a-zA-Z0-9]+)",
+ r"\n ## \1",
+ log,
+ )
log = re.sub("memstats: ", " ", log)
sys.stderr.write(log)
reportMade = True
# Look for AddressSanitizer Errors and append to /tmp/AddressSanitzer.txt if found
- if checkAddressSanitizerError(self.getStdErr(daemon), self.name, daemon):
- sys.stderr.write("%s: Daemon %s killed by AddressSanitizer" % (self.name, daemon))
- traces = traces + "\n%s: Daemon %s killed by AddressSanitizer" % (self.name, daemon)
+ if checkAddressSanitizerError(
+ self.getStdErr(daemon), self.name, daemon
+ ):
+ sys.stderr.write(
+ "%s: Daemon %s killed by AddressSanitizer" % (self.name, daemon)
+ )
+ traces = traces + "\n%s: Daemon %s killed by AddressSanitizer" % (
+ self.name,
+ daemon,
+ )
reportMade = True
if reportMade:
self.reportCores = False
@@ -1007,7 +1160,9 @@ class Router(Node):
global fatal_error
- daemonsRunning = self.cmd('vtysh -c "show logging" | grep "Logging configuration for"')
+ daemonsRunning = self.cmd(
+ 'vtysh -c "show logging" | grep "Logging configuration for"'
+ )
# Look for AddressSanitizer Errors in vtysh output and append to /tmp/AddressSanitzer.txt if found
if checkAddressSanitizerError(daemonsRunning, self.name, "vtysh"):
return "%s: vtysh killed by AddressSanitizer" % (self.name)
@@ -1016,32 +1171,59 @@ class Router(Node):
if (self.daemons[daemon] == 1) and not (daemon in daemonsRunning):
sys.stderr.write("%s: Daemon %s not running\n" % (self.name, daemon))
if daemon is "staticd":
- sys.stderr.write("You may have a copy of staticd installed but are attempting to test against\n")
- sys.stderr.write("a version of FRR that does not have staticd, please cleanup the install dir\n")
+ sys.stderr.write(
+ "You may have a copy of staticd installed but are attempting to test against\n"
+ )
+ sys.stderr.write(
+ "a version of FRR that does not have staticd, please cleanup the install dir\n"
+ )
# Look for core file
- corefiles = glob.glob('{}/{}/{}_core*.dmp'.format(
- self.logdir, self.name, daemon))
- if (len(corefiles) > 0):
+ corefiles = glob.glob(
+ "{}/{}/{}_core*.dmp".format(self.logdir, self.name, daemon)
+ )
+ if len(corefiles) > 0:
daemon_path = os.path.join(self.daemondir, daemon)
- backtrace = subprocess.check_output([
- "gdb {} {} --batch -ex bt 2> /dev/null".format(daemon_path, corefiles[0])
- ], shell=True)
- sys.stderr.write("\n%s: %s crashed. Core file found - Backtrace follows:\n" % (self.name, daemon))
+ backtrace = subprocess.check_output(
+ [
+ "gdb {} {} --batch -ex bt 2> /dev/null".format(
+ daemon_path, corefiles[0]
+ )
+ ],
+ shell=True,
+ )
+ sys.stderr.write(
+ "\n%s: %s crashed. Core file found - Backtrace follows:\n"
+ % (self.name, daemon)
+ )
sys.stderr.write("%s\n" % backtrace)
else:
# No core found - If we find matching logfile in /tmp, then print last 20 lines from it.
- if os.path.isfile('{}/{}/{}.log'.format(self.logdir, self.name, daemon)):
- log_tail = subprocess.check_output([
- "tail -n20 {}/{}/{}.log 2> /dev/null".format(
- self.logdir, self.name, daemon)
- ], shell=True)
- sys.stderr.write("\nFrom %s %s %s log file:\n" % (self.routertype, self.name, daemon))
+ if os.path.isfile(
+ "{}/{}/{}.log".format(self.logdir, self.name, daemon)
+ ):
+ log_tail = subprocess.check_output(
+ [
+ "tail -n20 {}/{}/{}.log 2> /dev/null".format(
+ self.logdir, self.name, daemon
+ )
+ ],
+ shell=True,
+ )
+ sys.stderr.write(
+ "\nFrom %s %s %s log file:\n"
+ % (self.routertype, self.name, daemon)
+ )
sys.stderr.write("%s\n" % log_tail)
# Look for AddressSanitizer Errors and append to /tmp/AddressSanitzer.txt if found
- if checkAddressSanitizerError(self.getStdErr(daemon), self.name, daemon):
- return "%s: Daemon %s not running - killed by AddressSanitizer" % (self.name, daemon)
+ if checkAddressSanitizerError(
+ self.getStdErr(daemon), self.name, daemon
+ ):
+ return "%s: Daemon %s not running - killed by AddressSanitizer" % (
+ self.name,
+ daemon,
+ )
return "%s: Daemon %s not running" % (self.name, daemon)
return ""
@@ -1061,25 +1243,27 @@ class Router(Node):
# Make sure we have version information first
if self.version == None:
- self.version = self.cmd(os.path.join(self.daemondir, 'bgpd')+' -v').split()[2]
- logger.info('{}: running version: {}'.format(self.name,self.version))
+ self.version = self.cmd(
+ os.path.join(self.daemondir, "bgpd") + " -v"
+ ).split()[2]
+ logger.info("{}: running version: {}".format(self.name, self.version))
rversion = self.version
if rversion is None:
return False
result = version_cmp(rversion, version)
- if cmpop == '>=':
+ if cmpop == ">=":
return result >= 0
- if cmpop == '>':
+ if cmpop == ">":
return result > 0
- if cmpop == '=':
+ if cmpop == "=":
return result == 0
- if cmpop == '<':
+ if cmpop == "<":
return result < 0
- if cmpop == '<':
+ if cmpop == "<":
return result < 0
- if cmpop == '<=':
+ if cmpop == "<=":
return result <= 0
def get_ipv6_linklocal(self):
@@ -1087,37 +1271,41 @@ class Router(Node):
linklocal = []
- ifaces = self.cmd('ip -6 address')
+ ifaces = self.cmd("ip -6 address")
# Fix newlines (make them all the same)
- ifaces = ('\n'.join(ifaces.splitlines()) + '\n').splitlines()
- interface=""
- ll_per_if_count=0
+ ifaces = ("\n".join(ifaces.splitlines()) + "\n").splitlines()
+ interface = ""
+ ll_per_if_count = 0
for line in ifaces:
- m = re.search('[0-9]+: ([^:@]+)[@if0-9:]+ <', line)
+ m = re.search("[0-9]+: ([^:@]+)[@if0-9:]+ <", line)
if m:
interface = m.group(1)
ll_per_if_count = 0
- m = re.search('inet6 (fe80::[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+)[/0-9]* scope link', line)
+ m = re.search(
+ "inet6 (fe80::[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+)[/0-9]* scope link",
+ line,
+ )
if m:
local = m.group(1)
ll_per_if_count += 1
- if (ll_per_if_count > 1):
+ if ll_per_if_count > 1:
linklocal += [["%s-%s" % (interface, ll_per_if_count), local]]
else:
linklocal += [[interface, local]]
return linklocal
+
def daemon_available(self, daemon):
"Check if specified daemon is installed (and for ldp if kernel supports MPLS)"
daemon_path = os.path.join(self.daemondir, daemon)
if not os.path.isfile(daemon_path):
return False
- if (daemon == 'ldpd'):
- if version_cmp(platform.release(), '4.5') < 0:
+ if daemon == "ldpd":
+ if version_cmp(platform.release(), "4.5") < 0:
return False
- if not module_present('mpls-router', load=False):
+ if not module_present("mpls-router", load=False):
return False
- if not module_present('mpls-iptunnel', load=False):
+ if not module_present("mpls-iptunnel", load=False):
return False
return True
@@ -1125,18 +1313,20 @@ class Router(Node):
"Return the type of Router (frr or quagga)"
return self.routertype
+
def report_memory_leaks(self, filename_prefix, testscript):
"Report Memory Leaks to file prefixed with given string"
leakfound = False
filename = filename_prefix + re.sub(r"\.py", "", testscript) + ".txt"
for daemon in self.daemons:
- if (self.daemons[daemon] == 1):
+ if self.daemons[daemon] == 1:
log = self.getStdErr(daemon)
if "memstats" in log:
# Found memory leak
- logger.info('\nRouter {} {} StdErr Log:\n{}'.format(
- self.name, daemon, log))
+ logger.info(
+ "\nRouter {} {} StdErr Log:\n{}".format(self.name, daemon, log)
+ )
if not leakfound:
leakfound = True
# Check if file already exists
@@ -1144,17 +1334,25 @@ class Router(Node):
leakfile = open(filename, "a")
if not fileexists:
# New file - add header
- leakfile.write("# Memory Leak Detection for topotest %s\n\n" % testscript)
+ leakfile.write(
+ "# Memory Leak Detection for topotest %s\n\n"
+ % testscript
+ )
leakfile.write("## Router %s\n" % self.name)
leakfile.write("### Process %s\n" % daemon)
log = re.sub("core_handler: ", "", log)
- log = re.sub(r"(showing active allocations in memory group [a-zA-Z0-9]+)", r"\n#### \1\n", log)
+ log = re.sub(
+ r"(showing active allocations in memory group [a-zA-Z0-9]+)",
+ r"\n#### \1\n",
+ log,
+ )
log = re.sub("memstats: ", " ", log)
leakfile.write(log)
leakfile.write("\n")
if leakfound:
leakfile.close()
+
class LinuxRouter(Router):
"A Linux Router Node with IPv4/IPv6 forwarding enabled."
@@ -1164,25 +1362,26 @@ class LinuxRouter(Router):
def config(self, **params):
Router.config(self, **params)
# Enable forwarding on the router
- assert_sysctl(self, 'net.ipv4.ip_forward', 1)
- assert_sysctl(self, 'net.ipv6.conf.all.forwarding', 1)
+ assert_sysctl(self, "net.ipv4.ip_forward", 1)
+ assert_sysctl(self, "net.ipv6.conf.all.forwarding", 1)
# Enable coredumps
- assert_sysctl(self, 'kernel.core_uses_pid', 1)
- assert_sysctl(self, 'fs.suid_dumpable', 1)
- #this applies to the kernel not the namespace...
- #original on ubuntu 17.x, but apport won't save as in namespace
+ assert_sysctl(self, "kernel.core_uses_pid", 1)
+ assert_sysctl(self, "fs.suid_dumpable", 1)
+ # this applies to the kernel not the namespace...
+ # original on ubuntu 17.x, but apport won't save as in namespace
# |/usr/share/apport/apport %p %s %c %d %P
- corefile = '%e_core-sig_%s-pid_%p.dmp'
- assert_sysctl(self, 'kernel.core_pattern', corefile)
+ corefile = "%e_core-sig_%s-pid_%p.dmp"
+ assert_sysctl(self, "kernel.core_pattern", corefile)
def terminate(self):
"""
Terminate generic LinuxRouter Mininet instance
"""
- set_sysctl(self, 'net.ipv4.ip_forward', 0)
- set_sysctl(self, 'net.ipv6.conf.all.forwarding', 0)
+ set_sysctl(self, "net.ipv4.ip_forward", 0)
+ set_sysctl(self, "net.ipv6.conf.all.forwarding", 0)
Router.terminate(self)
+
class FreeBSDRouter(Router):
"A FreeBSD Router Node with IPv4/IPv6 forwarding enabled."
@@ -1194,5 +1393,5 @@ class LegacySwitch(OVSSwitch):
"A Legacy Switch without OpenFlow"
def __init__(self, name, **params):
- OVSSwitch.__init__(self, name, failMode='standalone', **params)
+ OVSSwitch.__init__(self, name, failMode="standalone", **params)
self.switchIP = None