Add support for FRR in addition to Quagga

This commit is contained in:
Martin Winter 2017-01-15 00:45:57 +07:00 committed by Donald Sharp
parent 593335d02d
commit b36d3e1c85
2 changed files with 60 additions and 39 deletions

View File

@ -117,6 +117,13 @@ class QuaggaRouter(Node):
def config(self, **params): def config(self, **params):
super(QuaggaRouter, self).config(**params) super(QuaggaRouter, self).config(**params)
# Check if Quagga or FRR is installed
if os.path.isfile('/usr/lib/frr/zebra'):
self.routertype = 'frr'
elif os.path.isfile('/usr/lib/quagga/zebra'):
self.routertype = 'quagga'
else:
raise Exception('No FRR or Quagga found in ususal location')
# Enable forwarding on the router # Enable forwarding on the router
self.cmd('sysctl net.ipv4.ip_forward=1') self.cmd('sysctl net.ipv4.ip_forward=1')
self.cmd('sysctl net.ipv6.conf.all.forwarding=1') self.cmd('sysctl net.ipv6.conf.all.forwarding=1')
@ -126,7 +133,7 @@ class QuaggaRouter(Node):
self.cmd("sysctl kernel.core_pattern=/tmp/%s_%%e_core-sig_%%s-pid_%%p.dmp" % self.name) self.cmd("sysctl kernel.core_pattern=/tmp/%s_%%e_core-sig_%%s-pid_%%p.dmp" % self.name)
self.cmd('ulimit -c unlimited') self.cmd('ulimit -c unlimited')
# Set ownership of config files # Set ownership of config files
self.cmd('chown quagga:quaggavty /etc/quagga') self.cmd('chown %s:%svty /etc/%s' % (self.routertype, self.routertype, self.routertype))
self.daemons = {'zebra': 0, 'ripd': 0, 'ripngd': 0, 'ospfd': 0, self.daemons = {'zebra': 0, 'ripd': 0, 'ripngd': 0, 'ospfd': 0,
'ospf6d': 0, 'isisd': 0, 'bgpd': 0, 'pimd': 0} 'ospf6d': 0, 'isisd': 0, 'bgpd': 0, 'pimd': 0}
def terminate(self): def terminate(self):
@ -147,24 +154,24 @@ class QuaggaRouter(Node):
if daemon in self.daemons.keys(): if daemon in self.daemons.keys():
self.daemons[daemon] = 1 self.daemons[daemon] = 1
if source is None: if source is None:
self.cmd('touch /etc/quagga/%s.conf' % daemon) self.cmd('touch /etc/%s/%s.conf' % (self.routertype, daemon))
self.waitOutput() self.waitOutput()
else: else:
self.cmd('cp %s /etc/quagga/%s.conf' % (source, daemon)) self.cmd('cp %s /etc/%s/%s.conf' % (source, self.routertype, daemon))
self.waitOutput() self.waitOutput()
self.cmd('chmod 640 /etc/quagga/%s.conf' % daemon) self.cmd('chmod 640 /etc/%s/%s.conf' % (self.routertype, daemon))
self.waitOutput() self.waitOutput()
self.cmd('chown quagga:quagga /etc/quagga/%s.conf' % daemon) self.cmd('chown %s:%s /etc/%s/%s.conf' % (self.routertype, self.routertype, self.routertype, daemon))
self.waitOutput() self.waitOutput()
else: else:
print("No daemon %s known" % daemon) print("No daemon %s known" % daemon)
# print "Daemons after:", self.daemons # print "Daemons after:", self.daemons
def startQuagga(self): def startQuagga(self):
# Disable integrated-vtysh-config # Disable integrated-vtysh-config
self.cmd('echo "no service integrated-vtysh-config" > /etc/quagga/vtysh.conf') ### self.cmd('echo "no service integrated-vtysh-config" > /etc/%s/vtysh.conf' % self.routertype)
with open("/etc/quagga/vtysh.conf", "w") as vtyshfile: with open('/etc/%s/vtysh.conf' % self.routertype, "w") as vtyshfile:
vtyshfile.write('no service integrated-vtysh-config') vtyshfile.write('no service integrated-vtysh-config')
self.cmd('chown quagga:quaggavty /etc/quagga/vtysh.conf') self.cmd('chown %s:%svty /etc/%s/vtysh.conf' % (self.routertype, self.routertype, self.routertype))
# Try to find relevant old logfiles in /tmp and delete them # Try to find relevant old logfiles in /tmp and delete them
map(os.remove, glob.glob("/tmp/*%s*.log" % self.name)) map(os.remove, glob.glob("/tmp/*%s*.log" % self.name))
# Remove old core files # Remove old core files
@ -173,9 +180,9 @@ class QuaggaRouter(Node):
self.removeIPs() self.removeIPs()
# Start Zebra first # Start Zebra first
if self.daemons['zebra'] == 1: if self.daemons['zebra'] == 1:
self.cmd('/usr/lib/quagga/zebra -d') self.cmd('/usr/lib/%s/zebra -d' % self.routertype)
self.waitOutput() self.waitOutput()
print('%s: zebra started' % self) print('%s: %s zebra started' % (self, self.routertype))
sleep(1) sleep(1)
# Fix Link-Local Addresses # Fix Link-Local Addresses
# Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this # Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this
@ -183,9 +190,9 @@ class QuaggaRouter(Node):
# Now start all the other daemons # Now start all the other daemons
for daemon in self.daemons: for daemon in self.daemons:
if (self.daemons[daemon] == 1) and (daemon != 'zebra'): if (self.daemons[daemon] == 1) and (daemon != 'zebra'):
self.cmd('/usr/lib/quagga/%s -d' % daemon) self.cmd('/usr/lib/%s/%s -d' % (self.routertype, daemon))
self.waitOutput() self.waitOutput()
print('%s: %s started' % (self, daemon)) print('%s: %s %s started' % (self, self.routertype, daemon))
def checkQuaggaRunning(self): def checkQuaggaRunning(self):
global fatal_error global fatal_error
@ -197,14 +204,14 @@ class QuaggaRouter(Node):
# Look for core file # Look for core file
corefiles = glob.glob("/tmp/%s_%s_core*.dmp" % (self.name, daemon)) corefiles = glob.glob("/tmp/%s_%s_core*.dmp" % (self.name, daemon))
if (len(corefiles) > 0): if (len(corefiles) > 0):
backtrace = subprocess.check_output(["gdb /usr/lib/quagga/%s %s --batch -ex bt 2> /dev/null" % (daemon, corefiles[0])], shell=True) backtrace = subprocess.check_output(["gdb /usr/lib/%s/%s %s --batch -ex bt 2> /dev/null" % (self.routertype, daemon, corefiles[0])], shell=True)
sys.stderr.write("\n%s: %s crashed. Core file found - Backtrace follows:\n" % (self.name, daemon)) sys.stderr.write("\n%s: %s crashed. Core file found - Backtrace follows:\n" % (self.name, daemon))
sys.stderr.write("%s\n" % backtrace) sys.stderr.write("%s\n" % backtrace)
else: else:
# No core found - If we find matching logfile in /tmp, then print last 20 lines from it. # No core found - If we find matching logfile in /tmp, then print last 20 lines from it.
if os.path.isfile("/tmp/%s-%s.log" % (self.name, daemon)): if os.path.isfile("/tmp/%s-%s.log" % (self.name, daemon)):
log_tail = subprocess.check_output(["tail -n20 /tmp/%s-%s.log 2> /dev/null" % (self.name, daemon)], shell=True) log_tail = subprocess.check_output(["tail -n20 /tmp/%s-%s.log 2> /dev/null" % (self.name, daemon)], shell=True)
sys.stderr.write("\nFrom quagga %s %s log file:\n" % (self.name, daemon)) sys.stderr.write("\nFrom %s %s %s log file:\n" % (self.routertype, self.name, daemon))
sys.stderr.write("%s\n" % log_tail) sys.stderr.write("%s\n" % log_tail)
failed += [daemon] failed += [daemon]
return failed return failed
@ -229,9 +236,10 @@ class NetworkTopo(Topo):
def build(self, **_opts): def build(self, **_opts):
quaggaPrivateDirs = ['/etc/quagga', quaggaPrivateDirs = ['/etc/quagga',
'/etc/frr',
'/var/run/quagga', '/var/run/quagga',
'/var/log', '/var/run/frr',
'/var/run/ssh'] '/var/log']
exabgpPrivateDirs = ['/etc/exabgp', exabgpPrivateDirs = ['/etc/exabgp',
'/var/run/exabgp', '/var/run/exabgp',
'/var/log'] '/var/log']
@ -327,7 +335,7 @@ def test_quagga_running():
if (fatal_error != ""): if (fatal_error != ""):
pytest.skip(fatal_error) pytest.skip(fatal_error)
print("\n\n** Check if Quagga is running on each Router node") print("\n\n** Check if FRR/Quagga is running on each Router node")
print("******************************************\n") print("******************************************\n")
sleep(5) sleep(5)
@ -379,6 +387,10 @@ def test_bgp_converge():
bgpStatus = net['r%s' % i].cmd('show ip bgp view %s summary"') bgpStatus = net['r%s' % i].cmd('show ip bgp view %s summary"')
assert False, "BGP did not converge:\n%s" % bgpStatus assert False, "BGP did not converge:\n%s" % bgpStatus
# Wait for an extra 30s to announce all routes
print('Waiting 30s for routes to be announced');
sleep(30)
print("BGP converged.") print("BGP converged.")
# if timeout < 60: # if timeout < 60:

View File

@ -120,10 +120,17 @@ class LinuxRouter(Node):
super(LinuxRouter, self).terminate() super(LinuxRouter, self).terminate()
class QuaggaRouter(Node): class QuaggaRouter(Node):
"A Node with IPv4/IPv6 forwarding enabled and Quagga as Routing Engine" "A Node with IPv4/IPv6 forwarding enabled and FRR/Quagga as Routing Engine"
def config(self, **params): def config(self, **params):
super(QuaggaRouter, self).config(**params) super(QuaggaRouter, self).config(**params)
# Check if Quagga or FRR is installed
if os.path.isfile('/usr/lib/frr/zebra'):
self.routertype = 'frr'
elif os.path.isfile('/usr/lib/quagga/zebra'):
self.routertype = 'quagga'
else:
raise Exception('No FRR or Quagga found in ususal location')
# Enable forwarding on the router # Enable forwarding on the router
self.cmd('sysctl net.ipv4.ip_forward=1') self.cmd('sysctl net.ipv4.ip_forward=1')
self.cmd('sysctl net.ipv6.conf.all.forwarding=1') self.cmd('sysctl net.ipv6.conf.all.forwarding=1')
@ -133,12 +140,12 @@ class QuaggaRouter(Node):
self.cmd("sysctl kernel.core_pattern=/tmp/%s_%%e_core-sig_%%s-pid_%%p.dmp" % self.name) self.cmd("sysctl kernel.core_pattern=/tmp/%s_%%e_core-sig_%%s-pid_%%p.dmp" % self.name)
self.cmd('ulimit -c unlimited') self.cmd('ulimit -c unlimited')
# Set ownership of config files # Set ownership of config files
self.cmd('chown quagga:quaggavty /etc/quagga') self.cmd('chown %s:%svty /etc/%s' % (self.routertype, self.routertype, self.routertype))
self.daemons = {'zebra': 0, 'ripd': 0, 'ripngd': 0, 'ospfd': 0, self.daemons = {'zebra': 0, 'ripd': 0, 'ripngd': 0, 'ospfd': 0,
'ospf6d': 0, 'isisd': 0, 'bgpd': 0, 'pimd': 0} 'ospf6d': 0, 'isisd': 0, 'bgpd': 0, 'pimd': 0}
def terminate(self): def terminate(self):
# Delete Running Quagga Daemons # Delete Running Quagga Daemons
rundaemons = self.cmd('ls -1 /var/run/quagga/*.pid') rundaemons = self.cmd('ls -1 /var/run/%s/*.pid' % self.routertype)
for d in StringIO.StringIO(rundaemons): for d in StringIO.StringIO(rundaemons):
self.cmd('kill -7 `cat %s`' % d.rstrip()) self.cmd('kill -7 `cat %s`' % d.rstrip())
self.waitOutput() self.waitOutput()
@ -154,24 +161,24 @@ class QuaggaRouter(Node):
if daemon in self.daemons.keys(): if daemon in self.daemons.keys():
self.daemons[daemon] = 1 self.daemons[daemon] = 1
if source is None: if source is None:
self.cmd('touch /etc/quagga/%s.conf' % daemon) self.cmd('touch /etc/%s/%s.conf' % (self.routertype, daemon))
self.waitOutput() self.waitOutput()
else: else:
self.cmd('cp %s /etc/quagga/%s.conf' % (source, daemon)) self.cmd('cp %s /etc/%s/%s.conf' % (source, self.routertype, daemon))
self.waitOutput() self.waitOutput()
self.cmd('chmod 640 /etc/quagga/%s.conf' % daemon) self.cmd('chmod 640 /etc/%s/%s.conf' % (self.routertype, daemon))
self.waitOutput() self.waitOutput()
self.cmd('chown quagga:quagga /etc/quagga/%s.conf' % daemon) self.cmd('chown %s:%s /etc/%s/%s.conf' % (self.routertype, self.routertype, self.routertype, daemon))
self.waitOutput() self.waitOutput()
else: else:
print("No daemon %s known" % daemon) print("No daemon %s known" % daemon)
# print "Daemons after:", self.daemons # print "Daemons after:", self.daemons
def startQuagga(self): def startQuagga(self):
# Disable integrated-vtysh-config # Disable integrated-vtysh-config
self.cmd('echo "no service integrated-vtysh-config" > /etc/quagga/vtysh.conf') ### self.cmd('echo "no service integrated-vtysh-config" > /etc/%s/vtysh.conf' % self.routertype)
with open("/etc/quagga/vtysh.conf", "w") as vtyshfile: with open('/etc/%s/vtysh.conf' % self.routertype, "w") as vtyshfile:
vtyshfile.write('no service integrated-vtysh-config') vtyshfile.write('no service integrated-vtysh-config')
self.cmd('chown quagga:quaggavty /etc/quagga/vtysh.conf') self.cmd('chown %s:%svty /etc/%s/vtysh.conf' % (self.routertype, self.routertype, self.routertype))
# Try to find relevant old logfiles in /tmp and delete them # Try to find relevant old logfiles in /tmp and delete them
map(os.remove, glob.glob("/tmp/*%s*.log" % self.name)) map(os.remove, glob.glob("/tmp/*%s*.log" % self.name))
# Remove old core files # Remove old core files
@ -180,9 +187,9 @@ class QuaggaRouter(Node):
self.removeIPs() self.removeIPs()
# Start Zebra first # Start Zebra first
if self.daemons['zebra'] == 1: if self.daemons['zebra'] == 1:
self.cmd('/usr/lib/quagga/zebra -d') self.cmd('/usr/lib/%s/zebra -d' % self.routertype)
self.waitOutput() self.waitOutput()
print('%s: zebra started' % self) print('%s: %s zebra started' % (self, self.routertype))
sleep(1) sleep(1)
# Fix Link-Local Addresses # Fix Link-Local Addresses
# Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this # Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this
@ -190,9 +197,9 @@ class QuaggaRouter(Node):
# Now start all the other daemons # Now start all the other daemons
for daemon in self.daemons: for daemon in self.daemons:
if (self.daemons[daemon] == 1) and (daemon != 'zebra'): if (self.daemons[daemon] == 1) and (daemon != 'zebra'):
self.cmd('/usr/lib/quagga/%s -d' % daemon) self.cmd('/usr/lib/%s/%s -d' % (self.routertype, daemon))
self.waitOutput() self.waitOutput()
print('%s: %s started' % (self, daemon)) print('%s: %s %s started' % (self, self.routertype, daemon))
def checkQuaggaRunning(self): def checkQuaggaRunning(self):
global fatal_error global fatal_error
@ -204,14 +211,14 @@ class QuaggaRouter(Node):
# Look for core file # Look for core file
corefiles = glob.glob("/tmp/%s_%s_core*.dmp" % (self.name, daemon)) corefiles = glob.glob("/tmp/%s_%s_core*.dmp" % (self.name, daemon))
if (len(corefiles) > 0): if (len(corefiles) > 0):
backtrace = subprocess.check_output(["gdb /usr/lib/quagga/%s %s --batch -ex bt 2> /dev/null" % (daemon, corefiles[0])], shell=True) backtrace = subprocess.check_output(["gdb /usr/lib/%s/%s %s --batch -ex bt 2> /dev/null" % (self.routertype, daemon, corefiles[0])], shell=True)
sys.stderr.write("\n%s: %s crashed. Core file found - Backtrace follows:\n" % (self.name, daemon)) sys.stderr.write("\n%s: %s crashed. Core file found - Backtrace follows:\n" % (self.name, daemon))
sys.stderr.write("%s\n" % backtrace) sys.stderr.write("%s\n" % backtrace)
else: else:
# No core found - If we find matching logfile in /tmp, then print last 20 lines from it. # No core found - If we find matching logfile in /tmp, then print last 20 lines from it.
if os.path.isfile("/tmp/%s-%s.log" % (self.name, daemon)): if os.path.isfile("/tmp/%s-%s.log" % (self.name, daemon)):
log_tail = subprocess.check_output(["tail -n20 /tmp/%s-%s.log 2> /dev/null" % (self.name, daemon)], shell=True) log_tail = subprocess.check_output(["tail -n20 /tmp/%s-%s.log 2> /dev/null" % (self.name, daemon)], shell=True)
sys.stderr.write("\nFrom quagga %s %s log file:\n" % (self.name, daemon)) sys.stderr.write("\nFrom %s %s %s log file:\n" % (self.routertype, self.name, daemon))
sys.stderr.write("%s\n" % log_tail) sys.stderr.write("%s\n" % log_tail)
failed += [daemon] failed += [daemon]
return failed return failed
@ -260,7 +267,9 @@ class NetworkTopo(Topo):
def build(self, **_opts): def build(self, **_opts):
quaggaPrivateDirs = ['/etc/quagga', quaggaPrivateDirs = ['/etc/quagga',
'/etc/frr',
'/var/run/quagga', '/var/run/quagga',
'/var/run/frr',
'/var/log'] '/var/log']
# #
# Define Switches first # Define Switches first
@ -270,7 +279,7 @@ class NetworkTopo(Topo):
switch[i] = self.addSwitch('SW%s' % i, dpid=int2dpid(i), switch[i] = self.addSwitch('SW%s' % i, dpid=int2dpid(i),
cls=LegacySwitch) cls=LegacySwitch)
# #
# Define Quagga Routers # Define FRR/Quagga Routers
# #
router = {} router = {}
for i in range(1, 5): for i in range(1, 5):
@ -312,7 +321,7 @@ def setup_module(module):
net = Mininet(controller=None, topo=topo) net = Mininet(controller=None, topo=topo)
net.start() net.start()
# For debugging after starting net, but before starting Quagga, uncomment the next line # For debugging after starting net, but before starting FRR/Quagga, uncomment the next line
# CLI(net) # CLI(net)
# Starting Routers # Starting Routers
@ -321,7 +330,7 @@ def setup_module(module):
net['r%s' % i].loadConf('ospf6d', '%s/r%s/ospf6d.conf' % (thisDir, i)) net['r%s' % i].loadConf('ospf6d', '%s/r%s/ospf6d.conf' % (thisDir, i))
net['r%s' % i].startQuagga() net['r%s' % i].startQuagga()
# For debugging after starting Quagga daemons, uncomment the next line # For debugging after starting FRR/Quagga daemons, uncomment the next line
# CLI(net) # CLI(net)
@ -343,7 +352,7 @@ def test_quagga_running():
if (fatal_error != ""): if (fatal_error != ""):
pytest.skip(fatal_error) pytest.skip(fatal_error)
print("\n\n** Check if Quagga is running on each Router node") print("\n\n** Check if FRR/Quagga is running on each Router node")
print("******************************************\n") print("******************************************\n")
sleep(5) sleep(5)
@ -445,7 +454,7 @@ def test_ospfv3_routingTable():
assert failures == 0, "OSPFv3 (IPv6) Routing Table verification failed for router r%s:\n%s" % (i, diff) assert failures == 0, "OSPFv3 (IPv6) Routing Table verification failed for router r%s:\n%s" % (i, diff)
# For debugging after starting Quagga daemons, uncomment the next line # For debugging after starting FRR/Quagga daemons, uncomment the next line
# CLI(net) # CLI(net)
def test_linux_ipv6_kernel_routingTable(): def test_linux_ipv6_kernel_routingTable():
@ -506,7 +515,7 @@ def test_linux_ipv6_kernel_routingTable():
assert failures == 0, "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s" % (i, diff) assert failures == 0, "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s" % (i, diff)
# For debugging after starting Quagga daemons, uncomment the next line # For debugging after starting FRR/Quagga daemons, uncomment the next line
# CLI(net) # CLI(net)