]>
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 FRRouter (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 self
. cmd ( 'echo "no service integrated-vtysh-config" >> /etc/ %s /vtysh.conf' % self
. routertype
)
147 self
. cmd ( 'chown %s : %s vty /etc/ %s /vtysh.conf' % ( self
. routertype
, self
. routertype
, self
. routertype
))
148 # Try to find relevant old logfiles in /tmp and delete them
149 map ( os
. remove
, glob
. glob ( "/tmp/* %s *.log" % self
. name
))
150 # Remove old core files
151 map ( os
. remove
, glob
. glob ( "/tmp/ %s *.dmp" % self
. name
))
152 # Remove IP addresses from OS first - we have them in zebra.conf
154 # If ldp is used, check for LDP to be compiled and Linux Kernel to be 4.5 or higher
155 # No error - but return message and skip all the tests
156 if self
. daemons
[ 'ldpd' ] == 1 :
157 if not os
. path
. isfile ( '/usr/lib/ %s /ldpd' % self
. routertype
):
158 print ( "LDP Test, but no ldpd compiled or installed" )
159 return "LDP Test, but no ldpd compiled or installed"
160 kernel_version
= re
. search ( r
'([0-9]+\.[0-9]+).*' , platform
. release ())
162 if float ( kernel_version
. group ( 1 )) < 4.5 :
163 print ( "LDP Test need Linux Kernel 4.5 minimum" )
164 return "LDP Test need Linux Kernel 4.5 minimum"
165 # Add mpls modules to kernel if we use LDP
166 if self
. daemons
[ 'ldpd' ] == 1 :
167 self
. cmd ( '/sbin/modprobe mpls-router' )
168 self
. cmd ( '/sbin/modprobe mpls-iptunnel' )
169 self
. cmd ( 'echo 100000 > /proc/sys/net/mpls/platform_labels' )
170 # Init done - now restarting daemons
173 def restartRouter ( self
):
174 # Starts actuall daemons without init (ie restart)
176 if self
. daemons
[ 'zebra' ] == 1 :
177 # self.cmd('/usr/lib/%s/zebra -d' % self.routertype)
178 self
. cmd ( '/usr/lib/ %s /zebra > /tmp/ %s- zebra.out 2> /tmp/ %s- zebra.err &' % ( self
. routertype
, self
. name
, self
. name
))
180 print ( ' %s : %s zebra started' % ( self
, self
. routertype
))
182 # Fix Link-Local Addresses
183 # Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this
184 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' )
185 # Now start all the other daemons
186 for daemon
in self
. daemons
:
187 if ( self
. daemons
[ daemon
] == 1 ) and ( daemon
!= 'zebra' ):
188 # self.cmd('/usr/lib/%s/%s -d' % (self.routertype, daemon))
189 self
. cmd ( '/usr/lib/ %s / %s > /tmp/ %s-%s .out 2> /tmp/ %s-%s .err &' % ( self
. routertype
, daemon
, self
. name
, daemon
, self
. name
, daemon
))
191 print ( ' %s : %s %s started' % ( self
, self
. routertype
, daemon
))
192 def getStdErr ( self
, daemon
):
193 return self
. getLog ( 'err' , daemon
)
194 def getStdOut ( self
, daemon
):
195 return self
. getLog ( 'out' , daemon
)
196 def getLog ( self
, log
, daemon
):
197 return self
. cmd ( 'cat /tmp/ %s-%s . %s ' % ( self
. name
, daemon
, log
) )
198 def checkRouterRunning ( self
):
201 daemonsRunning
= self
. cmd ( 'vtysh -c "show log" | grep "Logging configuration for"' )
202 for daemon
in self
. daemons
:
203 if ( self
. daemons
[ daemon
] == 1 ) and not ( daemon
in daemonsRunning
):
204 sys
. stderr
. write ( " %s : Daemon %s not running \n " % ( self
. name
, daemon
))
206 corefiles
= glob
. glob ( "/tmp/ %s _ %s _core*.dmp" % ( self
. name
, daemon
))
207 if ( len ( corefiles
) > 0 ):
208 backtrace
= subprocess
. check_output ([ "gdb /usr/lib/ %s / %s %s --batch -ex bt 2> /dev/null" % ( self
. routertype
, daemon
, corefiles
[ 0 ])], shell
= True )
209 sys
. stderr
. write ( " \n %s : %s crashed. Core file found - Backtrace follows: \n " % ( self
. name
, daemon
))
210 sys
. stderr
. write ( " %s \n " % backtrace
)
212 # No core found - If we find matching logfile in /tmp, then print last 20 lines from it.
213 if os
. path
. isfile ( "/tmp/ %s-%s .log" % ( self
. name
, daemon
)):
214 log_tail
= subprocess
. check_output ([ "tail -n20 /tmp/ %s-%s .log 2> /dev/null" % ( self
. name
, daemon
)], shell
= True )
215 sys
. stderr
. write ( " \n From %s %s %s log file: \n " % ( self
. routertype
, self
. name
, daemon
))
216 sys
. stderr
. write ( " %s \n " % log_tail
)
218 return " %s : Daemon %s not running" % ( self
. name
, daemon
)
220 def get_ipv6_linklocal ( self
):
221 "Get LinkLocal Addresses from interfaces"
225 ifaces
= self
. cmd ( 'ip -6 address' )
226 # Fix newlines (make them all the same)
227 ifaces
= ( ' \n ' . join ( ifaces
. splitlines ()) + ' \n ' ). splitlines ()
231 m
= re
. search ( '[0-9]+: ([^:@]+)[@if0-9:]+ <' , line
)
233 interface
= m
. group ( 1 )
235 m
= re
. search ( 'inet6 (fe80::[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+)[/0-9]* scope link' , line
)
239 if ( ll_per_if_count
> 1 ):
240 linklocal
+= [[ " %s-%s " % ( interface
, ll_per_if_count
), local
]]
242 linklocal
+= [[ interface
, local
]]
244 def daemon_available ( self
, daemon
):
245 "Check if specified daemon is installed (and for ldp if kernel supports MPLS)"
247 if not os
. path
. isfile ( '/usr/lib/ %s / %s ' % ( self
. routertype
, daemon
)):
249 if ( daemon
== 'ldpd' ):
250 kernel_version
= re
. search ( r
'([0-9]+\.[0-9]+).*' , platform
. release ())
252 if float ( kernel_version
. group ( 1 )) < 4.5 :
257 def get_routertype ( self
):
258 "Return the type of Router (frr or quagga)"
260 return self
. routertype
263 class LegacySwitch ( OVSSwitch
):
264 "A Legacy Switch without OpenFlow"
266 def __init__ ( self
, name
, ** params
):
267 OVSSwitch
.__ init
__ ( self
, name
, failMode
= 'standalone' , ** params
)