]>
git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/ldp-topo1/test_ldp_topo1.py
4 # test_bgp_multiview_topo1.py
5 # Part of 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
26 test_ldp_topo1.py: Simple FRR LDP Test
45 r2-eth2 .2 | | .2 r2-eth1
48 ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
50 ~~ 10.0.3.0/24 ~~ ~~ 10.0.2.0/24 ~~
51 ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
55 r3-eth1 .3 | | .3 r3-eth0 | .4 r4-eth0
56 +----+--+---+ +----+----+
58 | 3.3.3.3 | | 4.4.4.4 |
59 +-----------+ +---------+
66 from time
import sleep
68 from mininet
.topo
import Topo
69 from mininet
.net
import Mininet
70 from mininet
.node
import Node
, OVSSwitch
, Host
71 from mininet
.log
import setLogLevel
, info
72 from mininet
.cli
import CLI
73 from mininet
.link
import Intf
75 sys
.path
.append(os
.path
.dirname(os
.path
.dirname(os
.path
.abspath(__file__
))))
76 from lib
import topotest
80 pytestmark
= [pytest
.mark
.ldpd
]
82 #####################################################
84 ## Network Topology Definition
86 #####################################################
89 class NetworkTopo(Topo
):
92 def build(self
, **_opts
):
97 router
[i
] = topotest
.addRouter(self
, "r%s" % i
)
99 # Setup Switches, add Interfaces and Connections
102 switch
[0] = self
.addSwitch("sw0", cls
=topotest
.LegacySwitch
)
107 addr1
="80:AA:00:00:00:00",
108 addr2
="00:11:00:01:00:00",
114 addr1
="80:AA:00:00:00:01",
115 addr2
="00:11:00:02:00:00",
118 switch
[1] = self
.addSwitch("sw1", cls
=topotest
.LegacySwitch
)
123 addr1
="80:AA:00:01:00:00",
124 addr2
="00:11:00:02:00:01",
130 addr1
="80:AA:00:01:00:01",
131 addr2
="00:11:00:03:00:00",
137 addr1
="80:AA:00:01:00:02",
138 addr2
="00:11:00:04:00:00",
141 switch
[2] = self
.addSwitch("sw2", cls
=topotest
.LegacySwitch
)
146 addr1
="80:AA:00:02:00:00",
147 addr2
="00:11:00:02:00:02",
153 addr1
="80:AA:00:02:00:01",
154 addr2
="00:11:00:03:00:01",
158 #####################################################
162 #####################################################
165 def setup_module(module
):
169 print("\n\n** %s: Setup Topology" % module
.__name
__)
170 print("******************************************\n")
172 print("Cleanup old Mininet runs")
173 os
.system("sudo mn -c > /dev/null 2>&1")
175 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
178 net
= Mininet(controller
=None, topo
=topo
)
182 for i
in range(1, 5):
183 net
["r%s" % i
].loadConf("zebra", "%s/r%s/zebra.conf" % (thisDir
, i
))
184 net
["r%s" % i
].loadConf("ospfd", "%s/r%s/ospfd.conf" % (thisDir
, i
))
185 net
["r%s" % i
].loadConf("ldpd", "%s/r%s/ldpd.conf" % (thisDir
, i
))
186 fatal_error
= net
["r%s" % i
].startRouter()
188 if fatal_error
!= "":
191 # For debugging after starting FRR daemons, uncomment the next line
195 def teardown_module(module
):
198 print("\n\n** %s: Shutdown Topology" % module
.__name
__)
199 print("******************************************\n")
201 # End - Shutdown network
205 def test_router_running():
209 # Skip if previous fatal error condition is raised
210 if fatal_error
!= "":
211 pytest
.skip(fatal_error
)
213 print("\n\n** Check if FRR is running on each Router node")
214 print("******************************************\n")
218 for i
in range(1, 5):
219 fatal_error
= net
["r%s" % i
].checkRouterRunning()
220 assert fatal_error
== "", fatal_error
222 # For debugging after starting FRR daemons, uncomment the next line
226 def test_mpls_interfaces():
230 # Skip if previous fatal error condition is raised
231 if fatal_error
!= "":
232 pytest
.skip(fatal_error
)
234 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
236 # Verify MPLS Interfaces
237 print("\n\n** Verifying MPLS Interfaces")
238 print("******************************************\n")
240 for i
in range(1, 5):
241 refTableFile
= "%s/r%s/show_mpls_ldp_interface.ref"
242 if os
.path
.isfile(refTableFile
):
243 # Read expected result from file
244 expected
= open(refTableFile
).read().rstrip()
245 # Fix newlines (make them all the same)
246 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
248 # Actual output from router
251 .cmd('vtysh -c "show mpls ldp interface" 2> /dev/null')
254 # Mask out Timer in Uptime
255 actual
= re
.sub(r
" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ", " xx:xx:xx ", actual
)
256 # Fix newlines (make them all the same)
257 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
260 diff
= topotest
.get_textdiff(
263 title1
="actual MPLS LDP interface status",
264 title2
="expected MPLS LDP interface status",
267 # Empty string if it matches, otherwise diff contains unified diff
270 "r%s failed MPLS LDP Interface status Check:\n%s\n" % (i
, diff
)
277 fatal_error
= "MPLS LDP Interface status failed"
281 ), "MPLS LDP Interface status failed for router r%s:\n%s" % (i
, diff
)
283 # Make sure that all daemons are running
284 for i
in range(1, 5):
285 fatal_error
= net
["r%s" % i
].checkRouterRunning()
286 assert fatal_error
== "", fatal_error
288 # For debugging after starting FRR daemons, uncomment the next line
292 def test_mpls_ldp_neighbor_establish():
296 # Skip if previous fatal error condition is raised
297 if fatal_error
!= "":
298 pytest
.skip(fatal_error
)
300 # Wait for MPLS LDP neighbors to establish.
301 print("\n\n** Verify MPLS LDP neighbors to establish")
302 print("******************************************\n")
305 print("Timeout in %s: " % timeout
),
307 # Look for any node not yet converged
308 for i
in range(1, 5):
311 .cmd('vtysh -c "show mpls ldp neighbor" 2> /dev/null')
315 # On current version, we need to make sure they all turn to OPERATIONAL on all lines
317 lines
= ("\n".join(established
.splitlines()) + "\n").splitlines(1)
318 # Check all lines to be either table header (starting with ^AF or show OPERATIONAL)
320 operational
= r
"^ip.*OPERATIONAL.*"
321 found_operational
= 0
322 for j
in range(1, len(lines
)):
323 if (not re
.search(header
, lines
[j
])) and (
324 not re
.search(operational
, lines
[j
])
326 established
= "" # Empty string shows NOT established
327 if re
.search(operational
, lines
[j
]):
328 found_operational
+= 1
329 if found_operational
< 1:
330 # Need at least one operational neighbor
331 established
= "" # Empty string shows NOT established
333 print("Waiting for r%s" % i
)
343 # Bail out with error if a router fails to converge
344 fatal_error
= "MPLS LDP neighbors did not establish"
345 assert False, "MPLS LDP neighbors did not establish" % ospfStatus
347 print("MPLS LDP neighbors established.")
350 # Only wait if we actually went through a convergence
351 print("\nwaiting 15s for LDP sessions to establish")
354 # Make sure that all daemons are running
355 for i
in range(1, 5):
356 fatal_error
= net
["r%s" % i
].checkRouterRunning()
357 assert fatal_error
== "", fatal_error
360 def test_mpls_ldp_discovery():
364 # Skip if previous fatal error condition is raised
365 if fatal_error
!= "":
366 pytest
.skip(fatal_error
)
368 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
370 # Verify MPLS LDP discovery
371 print("\n\n** Verifying MPLS LDP discovery")
372 print("******************************************\n")
374 for i
in range(1, 5):
375 refTableFile
= "%s/r%s/show_mpls_ldp_discovery.ref" % (thisDir
, i
)
376 if os
.path
.isfile(refTableFile
):
377 # Actual output from router
380 .cmd('vtysh -c "show mpls ldp discovery" 2> /dev/null')
384 # Read expected result from file
385 expected
= open(refTableFile
).read().rstrip()
386 # Fix newlines (make them all the same)
387 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
389 # Actual output from router
392 .cmd('vtysh -c "show mpls ldp discovery" 2> /dev/null')
396 # Fix newlines (make them all the same)
397 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
400 diff
= topotest
.get_textdiff(
403 title1
="actual MPLS LDP discovery output",
404 title2
="expected MPLS LDP discovery output",
407 # Empty string if it matches, otherwise diff contains unified diff
410 "r%s failed MPLS LDP discovery output Check:\n%s\n" % (i
, diff
)
418 ), "MPLS LDP Interface discovery output for router r%s:\n%s" % (i
, diff
)
420 # Make sure that all daemons are running
421 for i
in range(1, 5):
422 fatal_error
= net
["r%s" % i
].checkRouterRunning()
423 assert fatal_error
== "", fatal_error
425 # For debugging after starting FRR daemons, uncomment the next line
429 def test_mpls_ldp_neighbor():
433 # Skip if previous fatal error condition is raised
434 if fatal_error
!= "":
435 pytest
.skip(fatal_error
)
437 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
439 # Verify MPLS LDP neighbor
440 print("\n\n** Verifying MPLS LDP neighbor")
441 print("******************************************\n")
443 for i
in range(1, 5):
444 refTableFile
= "%s/r%s/show_mpls_ldp_neighbor.ref" % (thisDir
, i
)
445 if os
.path
.isfile(refTableFile
):
446 # Read expected result from file
447 expected
= open(refTableFile
).read().rstrip()
448 # Fix newlines (make them all the same)
449 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
451 # Actual output from router
454 .cmd('vtysh -c "show mpls ldp neighbor" 2> /dev/null')
458 # Mask out changing parts in output
459 # Mask out Timer in Uptime
461 r
"(ipv4 [0-9\.]+ +OPERATIONAL [0-9\.]+ +)[0-9][0-9]:[0-9][0-9]:[0-9][0-9]",
466 # Fix newlines (make them all the same)
467 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
470 diff
= topotest
.get_textdiff(
473 title1
="actual MPLS LDP neighbor output",
474 title2
="expected MPLS LDP neighbor output",
477 # Empty string if it matches, otherwise diff contains unified diff
480 "r%s failed MPLS LDP neighbor output Check:\n%s\n" % (i
, diff
)
488 ), "MPLS LDP Interface neighbor output for router r%s:\n%s" % (i
, diff
)
490 # Make sure that all daemons are running
491 for i
in range(1, 5):
492 fatal_error
= net
["r%s" % i
].checkRouterRunning()
493 assert fatal_error
== "", fatal_error
495 # For debugging after starting FRR daemons, uncomment the next line
499 def test_mpls_ldp_binding():
503 # Skip this test for now until proper sorting of the output
505 # pytest.skip("Skipping test_mpls_ldp_binding")
507 # Skip if previous fatal error condition is raised
508 if fatal_error
!= "":
509 pytest
.skip(fatal_error
)
511 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
513 # Verify MPLS LDP binding
514 print("\n\n** Verifying MPLS LDP binding")
515 print("******************************************\n")
517 for i
in range(1, 5):
518 refTableFile
= "%s/r%s/show_mpls_ldp_binding.ref" % (thisDir
, i
)
519 if os
.path
.isfile(refTableFile
):
520 # Read expected result from file
521 expected
= open(refTableFile
).read().rstrip()
522 # Fix newlines (make them all the same)
523 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
525 # Actual output from router
528 .cmd('vtysh -c "show mpls ldp binding" 2> /dev/null')
532 # Mask out changing parts in output
535 r
"(ipv4 [0-9\./]+ +[0-9\.]+ +)[0-9][0-9] (.*)", r
"\1xxx\2", actual
538 r
"(ipv4 [0-9\./]+ +[0-9\.]+ +[a-z\-]+ +)[0-9][0-9] (.*)",
543 # Fix newlines (make them all the same)
544 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
546 # Sort lines which start with "xx via inet "
547 pattern
= r
"^\s+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\s+"
551 for j
in range(1, len(actual
)):
552 if re
.search(pattern
, actual
[j
]) and re
.search(
553 pattern
, actual
[j
- 1]
555 if actual
[j
- 1] > actual
[j
]:
557 actual
[j
- 1] = actual
[j
]
562 diff
= topotest
.get_textdiff(
565 title1
="actual MPLS LDP binding output",
566 title2
="expected MPLS LDP binding output",
569 # Empty string if it matches, otherwise diff contains unified diff
572 "r%s failed MPLS LDP binding output Check:\n%s\n" % (i
, diff
)
580 ), "MPLS LDP Interface binding output for router r%s:\n%s" % (i
, diff
)
582 # Make sure that all daemons are running
583 for i
in range(1, 5):
584 fatal_error
= net
["r%s" % i
].checkRouterRunning()
585 assert fatal_error
== "", fatal_error
587 # For debugging after starting FRR daemons, uncomment the next line
591 def test_zebra_ipv4_routingTable():
595 # Skip if previous fatal error condition is raised
596 if fatal_error
!= "":
597 pytest
.skip(fatal_error
)
599 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
601 # Verify Zebra IPv4 Routing Table
602 print("\n\n** Verifying Zebra IPv4 Routing Table")
603 print("******************************************\n")
605 for i
in range(1, 5):
606 refTableFile
= "%s/r%s/show_ipv4_route.ref" % (thisDir
, i
)
607 if os
.path
.isfile(refTableFile
):
608 # Read expected result from file
609 expected
= open(refTableFile
).read().rstrip()
611 # Actual output from router
614 .cmd('vtysh -c "show ip route" 2> /dev/null | grep "^O"')
617 # Drop timers on end of line
618 actual
= re
.sub(r
", [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", "", actual
)
620 # Mask out label - all LDP labels should be >= 10 (2-digit)
621 # leaving the implicit labels unmasked
622 actual
= re
.sub(r
" label [0-9][0-9]+", " label xxx", actual
)
623 # and translating remaining implicit (single-digit) labels to label implicit-null
624 actual
= re
.sub(r
" label [0-9]+", " label implicit-null", actual
)
625 # Check if we have implicit labels - if not, then remove them from reference
626 if not re
.search(r
" label implicit-null", actual
):
627 expected
= re
.sub(r
", label implicit-null", "", expected
)
629 # now fix newlines of expected (make them all the same)
630 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
632 # Fix newlines (make them all the same)
633 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
636 diff
= topotest
.get_textdiff(
639 title1
="actual IPv4 zebra routing table",
640 title2
="expected IPv4 zebra routing table",
643 # Empty string if it matches, otherwise diff contains unified diff
646 "r%s failed IPv4 Zebra Routing Table Check:\n%s\n" % (i
, diff
)
654 ), "IPv4 Zebra Routing Table verification failed for router r%s:\n%s" % (
659 # Make sure that all daemons are running
660 for i
in range(1, 5):
661 fatal_error
= net
["r%s" % i
].checkRouterRunning()
662 assert fatal_error
== "", fatal_error
664 # For debugging after starting FRR daemons, uncomment the next line
668 def test_mpls_table():
672 # Skip if previous fatal error condition is raised
673 if fatal_error
!= "":
674 pytest
.skip(fatal_error
)
676 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
679 print("\n\n** Verifying MPLS table")
680 print("******************************************\n")
683 for i
in range(1, 5):
684 refTableFile
= "%s/r%s/show_mpls_table.ref" % (thisDir
, i
)
685 if os
.path
.isfile(refTableFile
):
686 # Read expected result from file
687 expected
= open(refTableFile
).read()
688 # Fix newlines (make them all the same)
689 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
691 # Actual output from router
692 actual
= net
["r%s" % i
].cmd('vtysh -c "show mpls table" 2> /dev/null')
694 # Fix inconsistent Label numbers at beginning of line
695 actual
= re
.sub(r
"(\s+)[0-9]+(\s+LDP)", r
"\1XX\2", actual
)
696 # Fix inconsistent Label numbers at end of line
698 r
"(\s+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\s+)[0-9][0-9]", r
"\1XX", actual
701 # Fix newlines (make them all the same)
702 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
704 # Sort lines which start with " XX LDP"
705 pattern
= r
"^\s+[0-9X]+\s+LDP"
709 for j
in range(1, len(actual
)):
710 if re
.search(pattern
, actual
[j
]) and re
.search(
711 pattern
, actual
[j
- 1]
713 if actual
[j
- 1] > actual
[j
]:
715 actual
[j
- 1] = actual
[j
]
720 diff
= topotest
.get_textdiff(
723 title1
="actual MPLS table output",
724 title2
="expected MPLS table output",
727 # Empty string if it matches, otherwise diff contains unified diff
730 "r%s failed MPLS table output Check:\n%s\n" % (i
, diff
)
736 assert failures
== 0, "MPLS table output for router r%s:\n%s" % (i
, diff
)
738 # Make sure that all daemons are running
739 for i
in range(1, 5):
740 fatal_error
= net
["r%s" % i
].checkRouterRunning()
741 assert fatal_error
== "", fatal_error
743 # For debugging after starting FRR daemons, uncomment the next line
747 def test_linux_mpls_routes():
751 # Skip if previous fatal error condition is raised
752 if fatal_error
!= "":
753 pytest
.skip(fatal_error
)
755 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
757 # Verify Linux Kernel MPLS routes
758 print("\n\n** Verifying Linux Kernel MPLS routes")
759 print("******************************************\n")
761 for i
in range(1, 5):
762 refTableFile
= "%s/r%s/ip_mpls_route.ref" % (thisDir
, i
)
763 if os
.path
.isfile(refTableFile
):
764 # Read expected result from file
765 expected
= open(refTableFile
).read().rstrip()
766 # Fix newlines (make them all the same)
767 expected
= ("\n".join(expected
.splitlines()) + "\n").splitlines(1)
769 # Actual output from router
771 net
["r%s" % i
].cmd("ip -o -family mpls route 2> /dev/null").rstrip()
774 # Mask out label and protocol
775 actual
= re
.sub(r
"[0-9][0-9] via inet ", "xx via inet ", actual
)
776 actual
= re
.sub(r
"[0-9][0-9] +proto", "xx proto", actual
)
777 actual
= re
.sub(r
"[0-9][0-9] as to ", "xx as to ", actual
)
778 actual
= re
.sub(r
"[ ]+proto \w+", " proto xx", actual
)
782 for line
in actual
.splitlines():
783 tokens
= re
.split(r
"\\\t", line
.strip())
784 nexthop_sorted
.append(
787 " ".join([token
.strip() for token
in sorted(tokens
[1:])]),
791 # Sort lines and fixup differences between old and new iproute
792 actual
= "\n".join(sorted(nexthop_sorted
))
793 actual
= re
.sub(r
"nexthop via", "nexthopvia", actual
)
794 actual
= re
.sub(r
" nexthop as to xx via inet ", " nexthopvia inet ", actual
)
795 actual
= re
.sub(r
" weight 1", "", actual
)
796 actual
= re
.sub(r
" [ ]+", " ", actual
)
798 # put \n back at line ends
799 actual
= ("\n".join(actual
.splitlines()) + "\n").splitlines(1)
802 diff
= topotest
.get_textdiff(
805 title1
="actual Linux Kernel MPLS route",
806 title2
="expected Linux Kernel MPLS route",
809 # Empty string if it matches, otherwise diff contains unified diff
812 "r%s failed Linux Kernel MPLS route output Check:\n%s\n" % (i
, diff
)
820 ), "Linux Kernel MPLS route output for router r%s:\n%s" % (i
, diff
)
822 # Make sure that all daemons are running
823 for i
in range(1, 5):
824 fatal_error
= net
["r%s" % i
].checkRouterRunning()
825 assert fatal_error
== "", fatal_error
827 # For debugging after starting FRR daemons, uncomment the next line
831 def test_shutdown_check_stderr():
835 # Skip if previous fatal error condition is raised
836 if fatal_error
!= "":
837 pytest
.skip(fatal_error
)
839 if os
.environ
.get("TOPOTESTS_CHECK_STDERR") is None:
841 "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
843 pytest
.skip("Skipping test for Stderr output")
845 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
847 print("\n\n** Verifying unexpected STDERR output from daemons")
848 print("******************************************\n")
850 for i
in range(1, 5):
851 net
["r%s" % i
].stopRouter()
852 log
= net
["r%s" % i
].getStdErr("ldpd")
854 print("\nRouter r%s LDPd StdErr Log:\n%s" % (i
, log
))
855 log
= net
["r%s" % i
].getStdErr("ospfd")
857 print("\nRouter r%s OSPFd StdErr Log:\n%s" % (i
, log
))
858 log
= net
["r%s" % i
].getStdErr("zebra")
860 print("\nRouter r%s Zebra StdErr Log:\n%s" % (i
, log
))
863 def test_shutdown_check_memleak():
867 # Skip if previous fatal error condition is raised
868 if fatal_error
!= "":
869 pytest
.skip(fatal_error
)
871 if os
.environ
.get("TOPOTESTS_CHECK_MEMLEAK") is None:
873 "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n"
875 pytest
.skip("Skipping test for memory leaks")
877 thisDir
= os
.path
.dirname(os
.path
.realpath(__file__
))
879 for i
in range(1, 5):
880 net
["r%s" % i
].stopRouter()
881 net
["r%s" % i
].report_memory_leaks(
882 os
.environ
.get("TOPOTESTS_CHECK_MEMLEAK"), os
.path
.basename(__file__
)
886 if __name__
== "__main__":
889 # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli
890 # retval = pytest.main(["-s", "--tb=no"])
891 retval
= pytest
.main(["-s"])