From 28413645ed85637af0cc74f3c12db1e3e5f20b8b Mon Sep 17 00:00:00 2001 From: bluhm Date: Thu, 22 Jun 2017 20:06:14 +0000 Subject: [PATCH] Get the framework from relayd regress to start switchd on demand. This allows to run the test without manual setup. --- regress/usr.sbin/switchd/Proc.pm | 194 ++++++++++++++++++++++++++++ regress/usr.sbin/switchd/Switchd.pm | 75 +++++++++++ regress/usr.sbin/switchd/run.pl | 38 ++++-- 3 files changed, 293 insertions(+), 14 deletions(-) create mode 100644 regress/usr.sbin/switchd/Proc.pm create mode 100644 regress/usr.sbin/switchd/Switchd.pm diff --git a/regress/usr.sbin/switchd/Proc.pm b/regress/usr.sbin/switchd/Proc.pm new file mode 100644 index 00000000000..074ad24b40f --- /dev/null +++ b/regress/usr.sbin/switchd/Proc.pm @@ -0,0 +1,194 @@ +# $OpenBSD: Proc.pm,v 1.1 2017/06/22 20:06:14 bluhm Exp $ + +# Copyright (c) 2010-2017 Alexander Bluhm +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +use strict; +use warnings; + +package Proc; +use Carp; +use Errno; +use IO::File; +use POSIX; +use Time::HiRes qw(time alarm sleep); + +my %CHILDREN; + +sub kill_children { + my @pids = @_ ? @_ : keys %CHILDREN + or return; + my @perms; + foreach my $pid (@pids) { + if (kill(TERM => $pid) != 1 and $!{EPERM}) { + push @perms, $pid; + } + } + if (my $sudo = $ENV{SUDO} and @perms) { + local $?; # do not modify during END block + my @cmd = ($sudo, '/bin/kill', '-TERM', @perms); + system(@cmd); + } + delete @CHILDREN{@pids}; +} + +BEGIN { + $SIG{TERM} = $SIG{INT} = sub { + my $sig = shift; + kill_children(); + $SIG{TERM} = $SIG{INT} = 'DEFAULT'; + POSIX::raise($sig); + }; +} + +END { + kill_children(); + $SIG{TERM} = $SIG{INT} = 'DEFAULT'; +} + +sub new { + my $class = shift; + my $self = { @_ }; + $self->{down} ||= "Shutdown"; + $self->{func} && ref($self->{func}) eq 'CODE' + or croak "$class func not given"; + $self->{logfile} + or croak "$class log file not given"; + open(my $fh, '>', $self->{logfile}) + or die "$class log file $self->{logfile} create failed: $!"; + $fh->autoflush; + $self->{log} = $fh; + $self->{ppid} = $$; + return bless $self, $class; +} + +sub run { + my $self = shift; + + pipe(my $reader, my $writer) + or die ref($self), " pipe to child failed: $!"; + defined(my $pid = fork()) + or die ref($self), " fork child failed: $!"; + if ($pid) { + $CHILDREN{$pid} = 1; + $self->{pid} = $pid; + close($reader); + $self->{pipe} = $writer; + return $self; + } + %CHILDREN = (); + $SIG{TERM} = $SIG{INT} = 'DEFAULT'; + $SIG{__DIE__} = sub { + die @_ if $^S; + warn @_; + IO::Handle::flush(\*STDERR); + POSIX::_exit(255); + }; + open(STDERR, '>&', $self->{log}) + or die ref($self), " dup STDERR failed: $!"; + close($writer); + open(STDIN, '<&', $reader) + or die ref($self), " dup STDIN failed: $!"; + close($reader); + + do { + $self->child(); + print STDERR $self->{up}, "\n"; + $self->{func}->($self); + } while ($self->{redo}); + print STDERR "Shutdown", "\n"; + + IO::Handle::flush(\*STDOUT); + IO::Handle::flush(\*STDERR); + POSIX::_exit(0); +} + +sub wait { + my $self = shift; + my $flags = shift; + + # if we a not the parent process, assume the child is still running + return 0 unless $self->{ppid} == $$; + + my $pid = $self->{pid} + or croak ref($self), " no child pid"; + my $kid = waitpid($pid, $flags); + if ($kid > 0) { + my $status = $?; + my $code; + $code = "exit: ". WEXITSTATUS($?) if WIFEXITED($?); + $code = "signal: ". WTERMSIG($?) if WIFSIGNALED($?); + $code = "stop: ". WSTOPSIG($?) if WIFSTOPPED($?); + delete $CHILDREN{$pid} if WIFEXITED($?) || WIFSIGNALED($?); + return wantarray ? ($kid, $status, $code) : $kid; + } + return $kid; +} + +sub loggrep { + my $self = shift; + my($regex, $timeout) = @_; + + my $end; + $end = time() + $timeout if $timeout; + + do { + my($kid, $status, $code) = $self->wait(WNOHANG); + if ($kid > 0 && $status != 0 && !$self->{dryrun}) { + # child terminated with failure + die ref($self), " child status: $status $code"; + } + open(my $fh, '<', $self->{logfile}) + or die ref($self), " log file open failed: $!"; + my @match = grep { /$regex/ } <$fh>; + return wantarray ? @match : $match[0] if @match; + close($fh); + # pattern not found + if ($kid == 0) { + # child still running, wait for log data + sleep .1; + } else { + # child terminated, no new log data possible + return; + } + } while ($timeout and time() < $end); + + return; +} + +sub up { + my $self = shift; + my $timeout = shift || 10; + $self->loggrep(qr/$self->{up}/, $timeout) + or croak ref($self), " no '$self->{up}' in $self->{logfile} ". + "after $timeout seconds"; + return $self; +} + +sub down { + my $self = shift; + my $timeout = shift || 30; + $self->loggrep(qr/$self->{down}/, $timeout) + or croak ref($self), " no '$self->{down}' in $self->{logfile} ". + "after $timeout seconds"; + return $self; +} + +sub kill_child { + my $self = shift; + kill_children($self->{pid}); + return $self; +} + +1; diff --git a/regress/usr.sbin/switchd/Switchd.pm b/regress/usr.sbin/switchd/Switchd.pm new file mode 100644 index 00000000000..d70d9beecf7 --- /dev/null +++ b/regress/usr.sbin/switchd/Switchd.pm @@ -0,0 +1,75 @@ +# $OpenBSD: Switchd.pm,v 1.1 2017/06/22 20:06:14 bluhm Exp $ + +# Copyright (c) 2010-2017 Alexander Bluhm +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +use strict; +use warnings; + +package Switchd; +use parent 'Proc'; +use Carp; +use Cwd; +use Sys::Hostname; +use File::Basename; + +sub new { + my $class = shift; + my %args = @_; + $args{logfile} ||= "switchd.log"; + $args{up} ||= $args{dryrun} || "listen on "; + $args{down} ||= $args{dryrun} ? "switchd.conf:" : "parent terminating"; + $args{func} = sub { Carp::confess "$class func may not be called" }; + $args{conffile} ||= "switchd.conf"; + my $self = Proc::new($class, %args); + $self->{listenaddr} + or croak "$class listen addr not given"; + $self->{listenport} + or croak "$class listen port not given"; + + # substitute variables in config file + my $curdir = dirname($0) || "."; + my $objdir = getcwd(); + my $hostname = hostname(); + (my $host = $hostname) =~ s/\..*//; + my $listenaddr = $self->{listenaddr}; + my $listenport = $self->{listenport}; + + my $test = basename($self->{testfile} || ""); + open(my $fh, '>', $self->{conffile}) + or die ref($self), " conf file $self->{conffile} create failed: $!"; + + my $config = "# regress $test\n"; + $config .= "listen on $self->{listenaddr} port $self->{listenport}\n" + unless $self->{conf} && $self->{conf} =~ /^listen /; + $config .= $self->{conf} if $self->{conf}; + $config =~ s/(\$[a-z]+)/$1/eeg; + print $fh $config; + close $fh; + + return $self; +} + +sub child { + my $self = shift; + my @sudo = $ENV{SUDO} ? $ENV{SUDO} : (); + my @ktrace = $ENV{KTRACE} ? ($ENV{KTRACE}, "-i") : (); + my $switchd = $ENV{SWITCHD} ? $ENV{SWITCHD} : "switchd"; + my @cmd = (@sudo, @ktrace, $switchd, "-dvv", "-f", $self->{conffile}); + print STDERR "execute: @cmd\n"; + exec @cmd; + die ref($self), " exec '@cmd' failed: $!"; +} + +1; diff --git a/regress/usr.sbin/switchd/run.pl b/regress/usr.sbin/switchd/run.pl index 9b0a4d309cd..665650422ef 100644 --- a/regress/usr.sbin/switchd/run.pl +++ b/regress/usr.sbin/switchd/run.pl @@ -1,6 +1,7 @@ -#!/usr/bin/perl -w -# $OpenBSD: run.pl,v 1.9 2016/12/22 15:40:07 rzalamena Exp $ +#!/usr/bin/perl +# $OpenBSD: run.pl,v 1.10 2017/06/22 20:06:14 bluhm Exp $ +# Copyright (c) 2017 Alexander Bluhm # Copyright (c) 2016 Reyk Floeter # # Permission to use, copy, modify, and distribute this software for any @@ -17,22 +18,22 @@ use strict; use warnings; - -BEGIN { - require OFP; - require 'ofp.ph'; - require 'ofp10.ph'; - require IO::Socket::INET; -} - use File::Basename; +use IO::Socket::INET; use Net::Pcap; use NetPacket::Ethernet; use NetPacket::IP; use NetPacket::UDP; -use Data::Dumper; use Crypt::Random; +use Switchd; + +BEGIN { + require OFP; + require 'ofp.ph'; + require 'ofp10.ph'; +} + sub fatal { my $class = shift; my $err = shift; @@ -325,10 +326,17 @@ for (@ARGV) { push(@test_files, glob($_)); } +my $sd = Switchd->new( + listenaddr => "127.0.0.1", + listenport => 6633, + testfile => $test, +); +$sd->run->up; + # Open connection to the controller -my $sock = new IO::Socket::INET( - PeerHost => '127.0.0.1', - PeerPort => '6633', +my $sock = IO::Socket::INET->new( + PeerHost => "127.0.0.1", + PeerPort => 6633, Proto => 'tcp', ) or fatal("main", "ERROR in Socket Creation : $!\n"); @@ -339,4 +347,6 @@ for my $test_file (@test_files) { $sock->close(); +$sd->kill_child->down; + 1; -- 2.20.1