]>
git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/lib/topotest.py
5 # Library of helper functions for NetDEF Topology Tests
7 # Copyright (c) 2016 by
8 # Network Device Education Foundation, Inc. ("NetDEF")
10 # Permission to use, copy, modify, and/or distribute this software
11 # for any purpose with or without fee is hereby granted, provided
12 # that the above copyright notice and this permission notice appear
15 # THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
16 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
18 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
19 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
21 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
33 from mininet
. topo
import Topo
34 from mininet
. net
import Mininet
35 from mininet
. node
import Node
, OVSSwitch
, Host
36 from mininet
. log
import setLogLevel
, info
37 from mininet
. cli
import CLI
38 from mininet
. link
import Intf
40 from time
import sleep
43 "Converting Integer to DPID"
47 dpid
= '0' *( 16 - len ( dpid
))+ dpid
50 raise Exception ( 'Unable to derive default datapath ID - '
51 'please either specify a dpid or use a '
52 'canonical switch name such as s23.' )
54 def addRouter ( topo
, name
):
55 "Adding a FreeRangeRouter (or Quagga) to Topology"
57 MyPrivateDirs
= [ '/etc/frr' ,
62 return topo
. addNode ( name
, cls
= Router
, privateDirs
= MyPrivateDirs
)
64 class LinuxRouter ( Node
):
65 "A Node with IPv4/IPv6 forwarding enabled."
67 def config ( self
, ** params
):
68 super ( LinuxRouter
, self
). config (** params
)
69 # Enable forwarding on the router
70 self
. cmd ( 'sysctl net.ipv4.ip_forward=1' )
71 self
. cmd ( 'sysctl net.ipv6.conf.all.forwarding=1' )
74 Terminate generic LinuxRouter Mininet instance
76 self
. cmd ( 'sysctl net.ipv4.ip_forward=0' )
77 self
. cmd ( 'sysctl net.ipv6.conf.all.forwarding=0' )
78 super ( LinuxRouter
, self
). terminate ()
81 "A Node with IPv4/IPv6 forwarding enabled and Quagga as Routing Engine"
83 def config ( self
, ** params
):
84 super ( Router
, self
). config (** params
)
86 # Check if Quagga or FRR is installed
87 if os
. path
. isfile ( '/usr/lib/frr/zebra' ):
88 self
. routertype
= 'frr'
89 elif os
. path
. isfile ( '/usr/lib/quagga/zebra' ):
90 self
. routertype
= 'quagga'
92 raise Exception ( 'No FRR or Quagga found in ususal location' )
93 # Enable forwarding on the router
94 self
. cmd ( 'sysctl net.ipv4.ip_forward=1' )
95 self
. cmd ( 'sysctl net.ipv6.conf.all.forwarding=1' )
97 self
. cmd ( 'sysctl kernel.core_uses_pid=1' )
98 self
. cmd ( 'sysctl fs.suid_dumpable=2' )
99 self
. cmd ( "sysctl kernel.core_pattern=/tmp/ %s _ %%e _core-sig_ %%s- pid_ %% p.dmp" % self
. name
)
100 self
. cmd ( 'ulimit -c unlimited' )
101 # Set ownership of config files
102 self
. cmd ( 'chown %s : %s vty /etc/ %s ' % ( self
. routertype
, self
. routertype
, self
. routertype
))
103 self
. daemons
= { 'zebra' : 0 , 'ripd' : 0 , 'ripngd' : 0 , 'ospfd' : 0 ,
104 'ospf6d' : 0 , 'isisd' : 0 , 'bgpd' : 0 , 'pimd' : 0 ,
107 # Delete Running Quagga or FRR Daemons
109 # rundaemons = self.cmd('ls -1 /var/run/%s/*.pid' % self.routertype)
110 # for d in StringIO.StringIO(rundaemons):
111 # self.cmd('kill -7 `cat %s`' % d.rstrip())
114 self
. cmd ( 'sysctl net.ipv4.ip_forward=0' )
115 self
. cmd ( 'sysctl net.ipv6.conf.all.forwarding=0' )
116 super ( Router
, self
). terminate ()
117 def stopRouter ( self
):
118 # Stop Running Quagga or FRR Daemons
119 rundaemons
= self
. cmd ( 'ls -1 /var/run/ %s /*.pid' % self
. routertype
)
120 if rundaemons
is not None :
121 for d
in StringIO
. StringIO ( rundaemons
):
122 self
. cmd ( 'kill -7 `cat %s `' % d
. rstrip ())
125 for interface
in self
. intfNames ():
126 self
. cmd ( 'ip address flush' , interface
)
127 def loadConf ( self
, daemon
, source
= None ):
128 # print "Daemons before:", self.daemons
129 if daemon
in self
. daemons
. keys ():
130 self
. daemons
[ daemon
] = 1
132 self
. cmd ( 'touch /etc/ %s / %s .conf' % ( self
. routertype
, daemon
))
135 self
. cmd ( 'cp %s /etc/ %s / %s .conf' % ( source
, self
. routertype
, daemon
))
137 self
. cmd ( 'chmod 640 /etc/ %s / %s .conf' % ( self
. routertype
, daemon
))
139 self
. cmd ( 'chown %s : %s /etc/ %s / %s .conf' % ( self
. routertype
, self
. routertype
, self
. routertype
, daemon
))
142 print ( "No daemon %s known" % daemon
)
143 # print "Daemons after:", self.daemons
144 def startRouter ( self
):
145 # Disable integrated-vtysh-config
146 with
open ( '/etc/ %s /vtysh.conf' % self
. routertype
, "w" ) as vtyshfile
:
147 vtyshfile
. write ( 'no service integrated-vtysh-config' )
148 self
. cmd ( 'chown %s : %s vty /etc/ %s /vtysh.conf' % ( self
. routertype
, self
. routertype
, self
. routertype
))
149 # Try to find relevant old logfiles in /tmp and delete them
150 map ( os
. remove
, glob
. glob ( "/tmp/* %s *.log" % self
. name
))
151 # Remove old core files
152 map ( os
. remove
, glob
. glob ( "/tmp/ %s *.dmp" % self
. name
))
153 # Remove IP addresses from OS first - we have them in zebra.conf
155 # If ldp is used, check for LDP to be compiled and Linux Kernel to be 4.5 or higher
156 # No error - but return message and skip all the tests
157 if self
. daemons
[ 'ldpd' ] == 1 :
158 if not os
. path
. isfile ( '/usr/lib/ %s /ldpd' % self
. routertype
):
159 print ( "LDP Test, but no ldpd compiled or installed" )
160 return "LDP Test, but no ldpd compiled or installed"
161 kernel_version
= re
. search ( r
'([0-9]+\.[0-9]+).*' , platform
. release ())
163 if float ( kernel_version
. group ( 1 )) < 4.5 :
164 print ( "LDP Test need Linux Kernel 4.5 minimum" )
165 return "LDP Test need Linux Kernel 4.5 minimum"
166 # Add mpls modules to kernel if we use LDP
167 if self
. daemons
[ 'ldpd' ] == 1 :
168 self
. cmd ( '/sbin/modprobe mpls-router' )
169 self
. cmd ( '/sbin/modprobe mpls-iptunnel' )
170 self
. cmd ( 'echo 100000 > /proc/sys/net/mpls/platform_labels' )
171 # Init done - now restarting daemons
174 def restartRouter ( self
):
175 # Starts actuall daemons without init (ie restart)
177 if self
. daemons
[ 'zebra' ] == 1 :
178 # self.cmd('/usr/lib/%s/zebra -d' % self.routertype)
179 self
. cmd ( '/usr/lib/ %s /zebra > /tmp/ %s- zebra.out 2> /tmp/ %s- zebra.err &' % ( self
. routertype
, self
. name
, self
. name
))
181 print ( ' %s : %s zebra started' % ( self
, self
. routertype
))
183 # Fix Link-Local Addresses
184 # Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this
185 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 %0 2x $((0x$1 ^ 2)))$2:$ {3} ff:fe$4:$5$6/64; done' )
186 # Now start all the other daemons
187 for daemon
in self
. daemons
:
188 if ( self
. daemons
[ daemon
] == 1 ) and ( daemon
!= 'zebra' ):
189 # self.cmd('/usr/lib/%s/%s -d' % (self.routertype, daemon))
190 self
. cmd ( '/usr/lib/ %s / %s > /tmp/ %s-%s .out 2> /tmp/ %s-%s .err &' % ( self
. routertype
, daemon
, self
. name
, daemon
, self
. name
, daemon
))
192 print ( ' %s : %s %s started' % ( self
, self
. routertype
, daemon
))
193 def getStdErr ( self
, daemon
):
194 return self
. getLog ( 'err' , daemon
)
195 def getStdOut ( self
, daemon
):
196 return self
. getLog ( 'out' , daemon
)
197 def getLog ( self
, log
, daemon
):
198 return self
. cmd ( 'cat /tmp/ %s-%s . %s ' % ( self
. name
, daemon
, log
) )
199 def checkRouterRunning ( self
):
202 daemonsRunning
= self
. cmd ( 'vtysh -c "show log" | grep "Logging configuration for"' )
203 for daemon
in self
. daemons
:
204 if ( self
. daemons
[ daemon
] == 1 ) and not ( daemon
in daemonsRunning
):
205 sys
. stderr
. write ( " %s : Daemon %s not running \n " % ( self
. name
, daemon
))
207 corefiles
= glob
. glob ( "/tmp/ %s _ %s _core*.dmp" % ( self
. name
, daemon
))
208 if ( len ( corefiles
) > 0 ):
209 backtrace
= subprocess
. check_output ([ "gdb /usr/lib/ %s / %s %s --batch -ex bt 2> /dev/null" % ( self
. routertype
, daemon
, corefiles
[ 0 ])], shell
= True )
210 sys
. stderr
. write ( " \n %s : %s crashed. Core file found - Backtrace follows: \n " % ( self
. name
, daemon
))
211 sys
. stderr
. write ( " %s \n " % backtrace
)
213 # No core found - If we find matching logfile in /tmp, then print last 20 lines from it.
214 if os
. path
. isfile ( "/tmp/ %s-%s .log" % ( self
. name
, daemon
)):
215 log_tail
= subprocess
. check_output ([ "tail -n20 /tmp/ %s-%s .log 2> /dev/null" % ( self
. name
, daemon
)], shell
= True )
216 sys
. stderr
. write ( " \n From %s %s %s log file: \n " % ( self
. routertype
, self
. name
, daemon
))
217 sys
. stderr
. write ( " %s \n " % log_tail
)
219 return " %s : Daemon %s not running" % ( self
. name
, daemon
)
221 def get_ipv6_linklocal ( self
):
222 "Get LinkLocal Addresses from interfaces"
226 ifaces
= self
. cmd ( 'ip -6 address' )
227 # Fix newlines (make them all the same)
228 ifaces
= ( ' \n ' . join ( ifaces
. splitlines ()) + ' \n ' ). splitlines ()
232 m
= re
. search ( '[0-9]+: ([^:@]+)[@if0-9:]+ <' , line
)
234 interface
= m
. group ( 1 )
236 m
= re
. search ( 'inet6 (fe80::[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+)[/0-9]* scope link' , line
)
240 if ( ll_per_if_count
> 1 ):
241 linklocal
+= [[ " %s-%s " % ( interface
, ll_per_if_count
), local
]]
243 linklocal
+= [[ interface
, local
]]
246 class LegacySwitch ( OVSSwitch
):
247 "A Legacy Switch without OpenFlow"
249 def __init__ ( self
, name
, ** params
):
250 OVSSwitch
.__ init
__ ( self
, name
, failMode
= 'standalone' , ** params
)