Get the framework from relayd regress to start switchd on demand.
authorbluhm <bluhm@openbsd.org>
Thu, 22 Jun 2017 20:06:14 +0000 (20:06 +0000)
committerbluhm <bluhm@openbsd.org>
Thu, 22 Jun 2017 20:06:14 +0000 (20:06 +0000)
This allows to run the test without manual setup.

regress/usr.sbin/switchd/Proc.pm [new file with mode: 0644]
regress/usr.sbin/switchd/Switchd.pm [new file with mode: 0644]
regress/usr.sbin/switchd/run.pl

diff --git a/regress/usr.sbin/switchd/Proc.pm b/regress/usr.sbin/switchd/Proc.pm
new file mode 100644 (file)
index 0000000..074ad24
--- /dev/null
@@ -0,0 +1,194 @@
+#      $OpenBSD: Proc.pm,v 1.1 2017/06/22 20:06:14 bluhm Exp $
+
+# Copyright (c) 2010-2017 Alexander Bluhm <bluhm@openbsd.org>
+#
+# 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 (file)
index 0000000..d70d9be
--- /dev/null
@@ -0,0 +1,75 @@
+#      $OpenBSD: Switchd.pm,v 1.1 2017/06/22 20:06:14 bluhm Exp $
+
+# Copyright (c) 2010-2017 Alexander Bluhm <bluhm@openbsd.org>
+#
+# 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;
index 9b0a4d3..6656504 100644 (file)
@@ -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 <bluhm@openbsd.org>
 # Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org>
 #
 # Permission to use, copy, modify, and distribute this software for any
 
 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;