From 17b69f04ff5ecb05ffd7e55173428fdfac16bdf7 Mon Sep 17 00:00:00 2001 From: Pragmatic Software Date: Wed, 10 Apr 2024 01:21:18 -0700 Subject: [PATCH] pbot-vm: massive refactor * replace heartbeat with health-check * instead of steady stream of newlines every 5s, now awaits input and responds with `vmstat` output * more reliably use host/config/vm-exec.json to get libvirt domain name for snapshot-revert, server address, serial ports, vagrant setting, etc * use iptables/nftables to disable networking * added guest/bin/disable-network-[iptables,nftables] * added guest/bin/enable-network-[iptables,nftables] * replace ugly ___OUTPUT___ texts in sh, bash, ksh, zsh languages * documentation updates and tweaks --- .../guest/bin/disable-network-iptables | 9 ++ .../guest/bin/disable-network-nftables | 8 ++ .../pbot-vm/guest/bin/enable-network-iptables | 3 + .../pbot-vm/guest/bin/enable-network-nftables | 3 + applets/pbot-vm/guest/bin/guest-server | 34 ++--- applets/pbot-vm/guest/bin/setup-guest | 19 +-- applets/pbot-vm/guest/lib/Guest.pm | 11 +- applets/pbot-vm/guest/provision/debian-trixie | 13 +- applets/pbot-vm/guest/provision/tumbleweed | 14 +- applets/pbot-vm/host/bin/vm-exec | 130 ++++++++++++++--- applets/pbot-vm/host/bin/vm-server | 133 +++++++----------- applets/pbot-vm/host/config/vm-exec.json | 16 ++- applets/pbot-vm/host/devices/add-serials | 4 +- applets/pbot-vm/host/lib/Languages/bash.pm | 10 +- applets/pbot-vm/host/lib/Languages/ksh.pm | 10 +- applets/pbot-vm/host/lib/Languages/sh.pm | 10 +- applets/pbot-vm/host/lib/Languages/zsh.pm | 10 +- .../vagrant/Debian-testing64/Vagrantfile | 4 +- applets/pbot-vm/vagrant/README.md | 50 +++++-- .../pbot-vm/vagrant/common/Vagrantfile.common | 7 +- .../vagrant/openSUSE-Tumbleweed/Vagrantfile | 4 +- doc/README.md | 3 +- doc/VirtualMachine.md | 42 ++++-- lib/PBot/VERSION.pm | 4 +- 24 files changed, 343 insertions(+), 208 deletions(-) create mode 100755 applets/pbot-vm/guest/bin/disable-network-iptables create mode 100755 applets/pbot-vm/guest/bin/disable-network-nftables create mode 100755 applets/pbot-vm/guest/bin/enable-network-iptables create mode 100755 applets/pbot-vm/guest/bin/enable-network-nftables diff --git a/applets/pbot-vm/guest/bin/disable-network-iptables b/applets/pbot-vm/guest/bin/disable-network-iptables new file mode 100755 index 00000000..63cb7c6a --- /dev/null +++ b/applets/pbot-vm/guest/bin/disable-network-iptables @@ -0,0 +1,9 @@ +#!/bin/sh +# disables all incoming, outgoing and forwarded traffic except incoming/established SSH +iptables -F +iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT +iptables -A INPUT -p tcp --dport 22 -j ACCEPT +iptables -P INPUT DROP +iptables -P FORWARD DROP +iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT +iptables -P OUTPUT DROP diff --git a/applets/pbot-vm/guest/bin/disable-network-nftables b/applets/pbot-vm/guest/bin/disable-network-nftables new file mode 100755 index 00000000..5823ea8c --- /dev/null +++ b/applets/pbot-vm/guest/bin/disable-network-nftables @@ -0,0 +1,8 @@ +#!/bin/sh +# disables all incoming, outgoing and forwarded traffic except incoming/established SSH +nft add table ip filter +nft add chain ip filter INPUT '{ type filter hook input priority 0; policy drop; }' +nft add chain ip filter OUTPUT '{ type filter hook output priority 0; policy drop; }' +nft 'add rule ip filter INPUT ct state related,established counter accept' +nft 'add rule ip filter INPUT tcp dport 22 counter accept' +nft 'add rule ip filter OUTPUT ct state related,established counter accept' diff --git a/applets/pbot-vm/guest/bin/enable-network-iptables b/applets/pbot-vm/guest/bin/enable-network-iptables new file mode 100755 index 00000000..ceb5905f --- /dev/null +++ b/applets/pbot-vm/guest/bin/enable-network-iptables @@ -0,0 +1,3 @@ +#!/bin/sh +# removes all iptables rules to re-enable networking +iptables -F diff --git a/applets/pbot-vm/guest/bin/enable-network-nftables b/applets/pbot-vm/guest/bin/enable-network-nftables new file mode 100755 index 00000000..291c9810 --- /dev/null +++ b/applets/pbot-vm/guest/bin/enable-network-nftables @@ -0,0 +1,3 @@ +#!/bin/sh +# deletes filter table to re-enable networking +nft delete table ip filter diff --git a/applets/pbot-vm/guest/bin/guest-server b/applets/pbot-vm/guest/bin/guest-server index 564a6b1a..bd28a907 100755 --- a/applets/pbot-vm/guest/bin/guest-server +++ b/applets/pbot-vm/guest/bin/guest-server @@ -5,7 +5,7 @@ # Purpose: PBot VM Guest server. Runs inside PBot VM Guest and processes # incoming VM commands from vm-exec. -# SPDX-FileCopyrightText: 2022 Pragmatic Software +# SPDX-FileCopyrightText: 2022-2024 Pragmatic Software # SPDX-License-Identifier: MIT use 5.020; @@ -20,7 +20,7 @@ use constant { USERNAME => 'vm', MOD_DIR => '/usr/local/share/pbot-vm/', SERIAL => '/dev/ttyS2', - HEARTBEAT => '/dev/ttyS3', + HEALTH => '/dev/ttyS3', VPORT => $ENV{PBOTVM_VPORT} // 5555, }; @@ -30,7 +30,6 @@ use lib MOD_DIR . "Languages"; use Guest; use File::Basename; -use IPC::Shareable; my %languages; @@ -68,11 +67,9 @@ sub serial_server() { open(my $input, '<', SERIAL) or die $!; open(my $output, '>', SERIAL) or die $!; - tie my $running, 'IPC::Shareable', { key => 'running' }; - my $buffer = ''; - while ($running) { + while (1) { my $command = Guest::read_input($input, \$buffer, 'Serial'); if (not defined $command) { @@ -119,19 +116,20 @@ sub do_server() { } } -sub do_heartbeat() { - open(my $heartbeat, '>', HEARTBEAT) or die $!; +sub do_healthcheck() { + open(my $health_in, '<', HEALTH) or die $!; + open(my $health_out, '>', HEALTH) or die $!; - tie my $running, 'IPC::Shareable', { key => 'running' }; + print "Healthcheck listening on PID $$...\n"; - print "Heart beating on PID $$...\n"; - - while ($running) { - print $heartbeat "\n"; - sleep 5; + while (1) { + my $input = <$health_in>; + my $vmstat = `vmstat`; + print $health_out "$vmstat\n"; + print $health_out ":END\n"; } - print "Heart beat stopped.\n"; + print "Healthcheck stopped.\n"; exit; # exit child process } @@ -160,14 +158,10 @@ sub main() { install_signal_handlers(); - tie my $running, 'IPC::Shareable', { key => 'running', create => 1, destroy => 1 }; - - $running = 1; - my $pid = fork // die "Fork failed: $!"; if ($pid == 0) { - do_heartbeat(); + do_healthcheck(); } else { do_server(); } diff --git a/applets/pbot-vm/guest/bin/setup-guest b/applets/pbot-vm/guest/bin/setup-guest index 5e965c00..fbcb7c86 100755 --- a/applets/pbot-vm/guest/bin/setup-guest +++ b/applets/pbot-vm/guest/bin/setup-guest @@ -1,11 +1,11 @@ -#!/bin/sh +#!/bin/bash # File: setup-guest # # Purpose: Sets up PBot VM Guest. Copies necessary files to the appropriate # location, sets up environment variables and various configuration details. -# SPDX-FileCopyrightText: 2022 Pragmatic Software +# SPDX-FileCopyrightText: 2022-2024 Pragmatic Software # SPDX-License-Identifier: MIT # determine OS/distribution @@ -56,9 +56,6 @@ cp guest/include/prelude.h /usr/include # require root password for polkit actions cp guest/polkit/* /etc/polkit-1/rules.d/ -# disable networking -nmcli networking off - # set environment variables if ! grep -qF "pbot-vm" /root/.bashrc; then echo '# pbot-vm' >> /root/.bashrc @@ -66,8 +63,12 @@ if ! grep -qF "pbot-vm" /root/.bashrc; then echo export ASAN_OPTIONS=detect_leaks=0 >> /root/.bashrc fi -echo PBot Guest VM is now set up. +export DEBUGINFOD_URLS +export ASAN_OPTIONS=detect_leaks=0 + +echo PBot Guest VM is set up. echo -echo !! Networking is now disabled. To re-enable networking run: nmcli networking on -echo -echo For changes to take effect, run this command now: source /root/.bashrc +echo To start PBot Guest Server: guest-server + +# make environment variables take effect +exec /bin/bash diff --git a/applets/pbot-vm/guest/lib/Guest.pm b/applets/pbot-vm/guest/lib/Guest.pm index 58fd45b7..fbfe398d 100644 --- a/applets/pbot-vm/guest/lib/Guest.pm +++ b/applets/pbot-vm/guest/lib/Guest.pm @@ -5,7 +5,7 @@ # Purpose: Collection of functions to interface with the PBot VM Guest and # execute VM commands. -# SPDX-FileCopyrightText: 2022 Pragmatic Software +# SPDX-FileCopyrightText: 2022-2024 Pragmatic Software # SPDX-License-Identifier: MIT package Guest; @@ -22,7 +22,6 @@ use English; use Encode; use File::Basename; use JSON::XS; -use IPC::Shareable; use Data::Dumper; sub read_input($input, $buffer, $tag) { @@ -59,7 +58,13 @@ sub read_input($input, $buffer, $tag) { print STDERR "-" x 40, "\n"; print STDERR "$tag got [$line]\n"; - my $command = decode_json($line); + my $command = eval { decode_json($line) }; + + if ($@) { + print STDERR "Failed to decode JSON: $@\n"; + return undef; + } + $command->{arguments} //= ''; $command->{input} //= ''; diff --git a/applets/pbot-vm/guest/provision/debian-trixie b/applets/pbot-vm/guest/provision/debian-trixie index 32889563..4637a202 100755 --- a/applets/pbot-vm/guest/provision/debian-trixie +++ b/applets/pbot-vm/guest/provision/debian-trixie @@ -16,10 +16,17 @@ apt install -y socat # for `cc` C language support apt install -y libubsan1 libasan8 gdb gcc gcc-multilib clang -# for pbot-vm guest-server support -apt install -y --no-install-recommends libipc-shareable-perl libipc-run-perl libjson-xs-perl - # for `cc` additional languages apt install -y ksh zsh tcl lua5.4 php8.2-cli nodejs guile3.0 beef bc g++ apt install -y clisp golang-go apt install -y --no-install-recommends default-jre default-jdk + +# for pbot-vm guest-server support +apt install -y --no-install-recommends libipc-run-perl libjson-xs-perl + +# disable networking +./guest/bin/disable-network-iptables + +echo 'Networking disabled.' +echo 'To re-enable, run ./guest/bin/enable-networking-iptables' +echo 'To disable again, run ./guest/bin/disable-network-iptables' diff --git a/applets/pbot-vm/guest/provision/tumbleweed b/applets/pbot-vm/guest/provision/tumbleweed index cdc5e0ca..38f9ea0d 100755 --- a/applets/pbot-vm/guest/provision/tumbleweed +++ b/applets/pbot-vm/guest/provision/tumbleweed @@ -14,10 +14,16 @@ zypper -n in socat # for `cc` C language support zypper -n in libubsan1 libasan8 gdb gcc gcc-32bit glibc-32bit clang -# for pbot-vm guest-server support -zypper -n in perl-IPC-Run perl-JSON-XS make cpanm -cpanm -n IPC::Shareable - # for `cc` additional languages zypper -n in ksh zsh tcl lua php8-cli nodejs-common guile bff bc gcc-c++ zypper -n in --no-recommends clisp gcc-go java java-devel + +# for pbot-vm guest-server support +zypper -n in perl-IPC-Run perl-JSON-XS + +# disable networking +./guest/bin/disable-network-nftables + +echo 'Networking disabled.' +echo 'To re-enable, run ./guest/bin/enable-networking-nftables' +echo 'To disable again, run ./guest/bin/disable-network-nftables' diff --git a/applets/pbot-vm/host/bin/vm-exec b/applets/pbot-vm/host/bin/vm-exec index b9c37a9c..86f54f30 100755 --- a/applets/pbot-vm/host/bin/vm-exec +++ b/applets/pbot-vm/host/bin/vm-exec @@ -2,15 +2,18 @@ # File: vm-exec # -# Purpose: Process and send commands to the PBot Guest server (guest-server) on -# the default VM socket CID/port (7/5555) or the default serial TCP port (5555). +# Purpose: Process and send commands to the PBot Guest server (guest-server) +# using the details from the config/vm-exec.json configuration file. +# +# Additionally, takes `-revert` and `-health` options to revert VM or check +# VM's health. # # Use the PBOTVM_CID, PBOTVM_VPORT and/or PBOTVM_SERIAL environment variables to -# override these defaults. E.g.: +# override the config/vm-exec.json values. E.g.: # # $ PBOTVM_CID=42 PBOTVM_SERIAL=7777 vm-exec -lang=sh echo test -# SPDX-FileCopyrightText: 2021 Pragmatic Software +# SPDX-FileCopyrightText: 2021-2024 Pragmatic Software # SPDX-License-Identifier: MIT use 5.020; @@ -22,9 +25,13 @@ use feature qw(signatures); no warnings qw(experimental::signatures); use constant { - SERIAL => $ENV{PBOTVM_SERIAL} // 5555, - CID => $ENV{PBOTVM_CID} // 7, - VPORT => $ENV{PBOTVM_VPORT} // 5555, + DOMAIN => $ENV{PBOTVM_DOMAIN} // 'pbot-vm', + ADDR => $ENV{PBOTVM_ADDR} // '127.0.0.1', + SERIAL => $ENV{PBOTVM_SERIAL} // 5555, + HEALTH => $ENV{PBOTVM_HEALTH} // 5556, + CID => $ENV{PBOTVM_CID} // 7, + VPORT => $ENV{PBOTVM_VPORT} // 5555, + VAGRANT => $ENV{PBOTVM_VAGRANT} // 0, }; use File::Basename; @@ -64,7 +71,7 @@ sub connect_serial($context) { print STDERR "Connecting to remote VM serial port $context->{'vm-serial'}\n"; my $vm = IO::Socket::INET->new( - PeerAddr => '127.0.0.1', + PeerAddr => $context->{'vm-addr'}, PeerPort => $context->{'vm-serial'}, Proto => 'tcp', Type => SOCK_STREAM @@ -96,6 +103,12 @@ sub connect_vm($context) { sub make_context_from_args(@args_in) { my $args = join ' ', @args_in; + # extract leading options + my %opts; + while ($args =~ s/^-(revert|health)\s+//) { + $opts{$1} = 1; + } + my $context = eval { decode_json $args }; if ($@) { @@ -108,8 +121,14 @@ sub make_context_from_args(@args_in) { } } + # set extracted leading options + foreach my $opt (keys %opts) { + print STDERR "Setting option `$opt`.\n"; + $context->{$opt} = 1; + } + # parse options specific to vm-exec - while ($context->{code} =~ s/^-(lang|vm-cid|vm-vport|vm-serial|vm)=([^ ]+)\s*//) { + while ($context->{code} =~ s/^-(lang|revert|health|vm-domain|vm-health|vm-cid|vm-vport|vm-serial|vm)=([^ ]+)\s*//) { my ($option, $value) = ($1, $2); print STDERR "Overriding `$option` to `$value`.\n"; $context->{$option} = lc $value; @@ -190,13 +209,17 @@ sub configure_context($context, $config) { if (not defined $entry) { my $machines = list_machines($config); print "Unknown machine '$machine'; available machines are: $machines\n"; - exit 1; + exit 3; } # override values - $context->{'vm-serial'} = $entry->{'serial'}; - $context->{'vm-cid'} = $entry->{'cid'}; - $context->{'vm-vport'} = $entry->{'vport'}; + $context->{'vm-domain'} = $machine; + $context->{'vm-addr'} = $entry->{'addr'}; + $context->{'vm-health'} = $entry->{'health'}; + $context->{'vm-serial'} = $entry->{'serial'}; + $context->{'vm-cid'} = $entry->{'cid'}; + $context->{'vm-vport'} = $entry->{'vport'}; + $context->{'vm-vagrant'} = $entry->{'vagrant'}; } else { # otherwise configure any undefined values as default machine my $machine = $config->{'default-machine'}; @@ -207,22 +230,30 @@ sub configure_context($context, $config) { if (not defined $entry) { my $machines = list_machines($config); print "Unknown machine '$machine'; available machines are: $machines\n"; - exit 1; + exit 3; } # update any undefined values, preserving any existing values - $context->{'vm-serial'} //= $entry->{'serial'}; - $context->{'vm-cid'} //= $entry->{'cid'}; - $context->{'vm-vport'} //= $entry->{'vport'}; + $context->{'vm-domain'} //= $machine; + $context->{'vm-addr'} //= $entry->{'addr'}; + $context->{'vm-health'} //= $entry->{'health'}; + $context->{'vm-serial'} //= $entry->{'serial'}; + $context->{'vm-cid'} //= $entry->{'cid'}; + $context->{'vm-vport'} //= $entry->{'vport'}; + $context->{'vm-vagrant'} //= $entry->{'vagrant'}; } # set any undefined values to default values - $context->{nick} //= 'vm'; - $context->{channel} //= 'vm'; - $context->{lang} //= 'c2x'; - $context->{'vm-serial'} //= SERIAL; - $context->{'vm-cid'} //= CID; - $context->{'vm-vport'} //= VPORT; + $context->{nick} //= 'vm'; + $context->{channel} //= 'vm'; + $context->{lang} //= 'c2x'; + $context->{'vm-domain'} //= DOMAIN; + $context->{'vm-addr'} //= ADDR; + $context->{'vm-health'} //= HEALTH; + $context->{'vm-serial'} //= SERIAL; + $context->{'vm-cid'} //= CID; + $context->{'vm-vport'} //= VPORT; + $context->{'vm-vagrant'} //= VAGRANT; } sub main() { @@ -232,6 +263,59 @@ sub main() { configure_context($context, $config); + # instructed to revert machine + if ($context->{revert}) { + if (exists $config->{aliases}->{$context->{'vm-domain'}}) { + $context->{'vm-domain'} = $config->{aliases}->{$context->{'vm-domain'}}; + } + + print STDERR "REVERT $context->{'vm-domain'}\n"; + + if ($context->{'vm-vagrant'}) { + system("virsh -c qemu:///system snapshot-revert $context->{'vm-domain'} 1"); + } else { + system("virsh snapshot-revert $context->{'vm-domain'} 1"); + } + + exit 0; + } + + # instructed to check health + if ($context->{health}) { + my $health = IO::Socket::INET->new( + PeerAddr => $context->{'vm-addr'}, + PeerPort => $context->{'vm-health'}, + Proto => 'tcp', + Type => SOCK_STREAM + ); + + if (not defined $health) { + print STDERR "Unable to connect to health $context->{'vm-addr'} $context->{'vm-health'}\n"; + exit 2; + } + + print $health "\n"; + + + eval { + alarm 2; + local $SIG{ALRM} = sub { die "Health timed-out\n"; }; + + while (my $output = <$health>) { + last if $output eq ":END\r\n"; + print $output; + } + close $health; + }; + + if ($@) { + print STDERR "Failed to get health: $@\n"; + exit 1; + } + + exit 0; + } + # load language before checking usage in order to handle -lang=? flag # to list languages instead of showing a usage message my $lang = load_language($context); diff --git a/applets/pbot-vm/host/bin/vm-server b/applets/pbot-vm/host/bin/vm-server index 0bba07b2..e88a09a5 100755 --- a/applets/pbot-vm/host/bin/vm-server +++ b/applets/pbot-vm/host/bin/vm-server @@ -6,7 +6,7 @@ # listens for incoming commands from vm-client. Invokes vm-exec to send # commands to the PBot Guest Server (guest-server). -# SPDX-FileCopyrightText: 2021 Pragmatic Software +# SPDX-FileCopyrightText: 2021-2024 Pragmatic Software # SPDX-License-Identifier: MIT use 5.020; @@ -25,19 +25,24 @@ use Encode; use constant { SERVER_PORT => $ENV{PBOTVM_PORT} // 9000, - HEARTBEAT_PORT => $ENV{PBOTVM_HEART} // 5556, - DOMAIN_NAME => $ENV{PBOTVM_DOMAIN} // 'pbot-vm', COMPILE_TIMEOUT => $ENV{PBOTVM_TIMEOUT} // 10, }; -sub vm_revert() { +sub vm_revert($input) { return if $ENV{PBOTVM_NOREVERT}; print "Reverting vm...\n"; - system('time virsh snapshot-revert '.DOMAIN_NAME.' 1'); + execute("perl vm-exec -revert $input", 1000); print "Reverted.\n"; } -sub execute($command) { +sub vm_check_health($input) { + print "Checking health...\n"; + my ($ret, $result) = execute("perl vm-exec -health $input", 2); + print "$result\n" if length $result; + return ($ret, $result); +} + +sub execute($command, $timeout = COMPILE_TIMEOUT) { print "execute ($command)\n"; # to get $? from pipe @@ -53,7 +58,7 @@ sub execute($command) { my $result = eval { my $output = ''; local $SIG{ALRM} = sub { kill 9, $pid; die "Timed-out: $output\n"; }; - alarm(COMPILE_TIMEOUT); + alarm($timeout); while (my $line = decode('UTF-8', <$fh>)) { $output .= $line; @@ -80,47 +85,6 @@ sub execute($command) { return ($ret, $result); } -sub connect_to_heartbeat() { - my $heartbeat; - my $attempts = 15; - - while (!$heartbeat && $attempts > 0) { - print "Connecting to heartbeat on port ".HEARTBEAT_PORT." ... "; - - $heartbeat = IO::Socket::INET->new ( - PeerAddr => '127.0.0.1', - PeerPort => HEARTBEAT_PORT, - Proto => 'tcp', - Type => SOCK_STREAM, - ); - - if (!$heartbeat) { - print "failed.\n"; - --$attempts; - print "Trying again in 2 seconds ($attempts attempts remaining) ...\n" if $attempts > 0; - sleep 2; - } else { - print "success!\n"; - } - } - - return $heartbeat; -} - -sub do_heartbeat() { - tie my $heartbeat, 'IPC::Shareable', { key => 'heartbeat' }; - tie my $running, 'IPC::Shareable', { key => 'running' }; - - while ($running) { - my $heartbeat_monitor = connect_to_heartbeat(); - - while ($running and <$heartbeat_monitor>) { - $heartbeat = time; - } - } - exit; -} - sub server_listen($port) { my $server = IO::Socket::INET->new ( Proto => 'tcp', @@ -135,29 +99,25 @@ sub server_listen($port) { } sub do_server() { - tie my $heartbeat, 'IPC::Shareable', { key => 'heartbeat' }; - tie my $running, 'IPC::Shareable', { key => 'running' }; - print "Starting PBot VM Server on port " . SERVER_PORT . "\n"; my $server = eval { server_listen(SERVER_PORT) }; if ($@) { print STDERR $@; - $running = 0; return; } - while ($running and my $client = $server->accept) { + while (my $client = $server->accept) { print '-' x 20, "\n"; my $hostinfo = gethostbyaddr($client->peeraddr); print "Connect from ", $client->peerhost, " at ", scalar localtime, "\n"; - handle_client($client, $heartbeat); + handle_client($client); } print "Shutting down server.\n"; } -sub handle_client($client, $heartbeat) { +sub handle_client($client) { my ($timed_out, $killed) = (0, 0); my $r = fork; @@ -177,30 +137,50 @@ sub handle_client($client, $heartbeat) { $client->autoflush(1); - eval { + my $input = eval { # give client 5 seconds to send a line local $SIG{ALRM} = sub { die "Client I/O timed-out\n"; }; alarm 5; - while (my $line = decode('UTF-8', <$client>)) { - $line =~ s/[\r\n]+$//; - next if $line =~ m/^\s*$/; + my $input; + + while ($input = decode('UTF-8', <$client>)) { + $input =~ s/[\r\n]+$//; + next if $input =~ m/^\s*$/; # give client 5 more seconds alarm 5; - print "[$$] Read [$line]\n"; + print "[$$] Read [$input]\n"; - if (time - $heartbeat > 5) { - print "[$$] Lost heartbeat, ignoring compile attempt.\n"; - print $client "Virtual machine is resetting, try again soon.\n"; + # check health + my ($health, $health_message) = vm_check_health($input); + + if ($health == 2) { + print "[$$] Unable to connect to VM health check, ignoring compile attempt.\n"; + print $client "Virtual machine is offline.\n"; + last; + } + + if ($health == 1 || $health == -13) { + print "[$$] VM not responding to health check, ignoring compile attempt.\n"; + print $client "Virtual machine is temporarily unavailable, try again soon.\n"; + last; + } + + if ($health != 0) { + if (length $health_message) { + print $client $health_message; + } else { + print $client "Virtual machine is misbehaving, try again soon.\n"; + } last; } # disable client time-out alarm 0; - my ($ret, $result) = execute("perl vm-exec $line"); + my ($ret, $result) = execute("perl vm-exec $input"); $result =~ s/\s+$//; print "Ret: $ret; result: [$result]\n"; @@ -217,6 +197,8 @@ sub handle_client($client, $heartbeat) { print $client encode('UTF-8', $result . "\n"); last; } + + return $input; }; # print client time-out exception @@ -228,11 +210,11 @@ sub handle_client($client, $heartbeat) { print "[$$] timed out: $timed_out; killed: $killed\n"; if ($timed_out || $killed) { - vm_revert(); + vm_revert($input); } # child done - print "[$$] client exiting\n"; + print "[$$] client exit\n"; print "=" x 20, "\n"; exit; } @@ -244,23 +226,8 @@ sub main() { # let OS clean-up child exits $SIG{CHLD} = 'IGNORE'; - tie my $heartbeat, 'IPC::Shareable', { key => 'heartbeat', create => 1, destroy => 1 }; - tie my $running, 'IPC::Shareable', { key => 'running', create => 1, destroy => 1 }; - - $running = 1; - $heartbeat = 0; - - my $heartbeat_pid = fork // die "Heartbeat fork failed: $!"; - - if ($heartbeat_pid == 0) { - do_heartbeat(); - } else { - do_server(); - } - - print "Waiting for heart to stop...\n"; - waitpid($heartbeat_pid, 0); - print "Heart stopped.\n"; + # start server + do_server(); } main(); diff --git a/applets/pbot-vm/host/config/vm-exec.json b/applets/pbot-vm/host/config/vm-exec.json index b971dd1e..bc07e264 100644 --- a/applets/pbot-vm/host/config/vm-exec.json +++ b/applets/pbot-vm/host/config/vm-exec.json @@ -1,21 +1,25 @@ { "machines" : { "pbot-vm" : { + "addr" : "127.0.0.1", "serial" : 5555, - "heart" : 5556, + "health" : 5556, "cid" : 7, - "vport" : 5555 + "vport" : 5555, + "vagrant" : 0 }, "pbot-test-vm" : { + "addr" : "127.0.0.1", "serial" : 7777, - "heart" : 7778, + "health" : 7778, "cid" : 42, - "vport" : 5555 + "vport" : 5555, + "vagrant" : 1 } }, "aliases" : { - "openSUSE" : "pbot-vm", + "main" : "pbot-vm", "test" : "pbot-test-vm" }, - "default-machine" : "openSUSE" + "default-machine" : "main" } diff --git a/applets/pbot-vm/host/devices/add-serials b/applets/pbot-vm/host/devices/add-serials index aaebf5b9..c4f92d97 100755 --- a/applets/pbot-vm/host/devices/add-serials +++ b/applets/pbot-vm/host/devices/add-serials @@ -2,7 +2,7 @@ DOMAIN="${PBOTVM_DOMAIN:-pbot-vm}" SERIAL="${PBOTVM_SERIAL:-5555}" -HEART="${PBOTVM_HEART:-5556}" +HEALTH="${PBOTVM_HEALTH:-5556}" cat > serial-2.xml < @@ -14,7 +14,7 @@ EOF cat > serial-3.xml < - + diff --git a/applets/pbot-vm/host/lib/Languages/bash.pm b/applets/pbot-vm/host/lib/Languages/bash.pm index c5a4b989..1964ee01 100755 --- a/applets/pbot-vm/host/lib/Languages/bash.pm +++ b/applets/pbot-vm/host/lib/Languages/bash.pm @@ -1,6 +1,6 @@ #!/usr/bin/env perl -# SPDX-FileCopyrightText: 2021 Pragmatic Software +# SPDX-FileCopyrightText: 2021-2024 Pragmatic Software # SPDX-License-Identifier: MIT use warnings; @@ -17,11 +17,11 @@ sub initialize { $self->{default_options} = ''; $self->{cmdline} = 'bash $options $sourcefile'; - $self->{cmdline_opening_comment} = ": <<'____CMDLINE____'\n"; - $self->{cmdline_closing_comment} = "____CMDLINE____\n"; + $self->{cmdline_opening_comment} = ": <<'CMDLINE'\n"; + $self->{cmdline_closing_comment} = "CMDLINE\n"; - $self->{output_opening_comment} = ": << '____OUTPUT____'\n"; - $self->{output_closing_comment} = "____OUTPUT____\n"; + $self->{output_opening_comment} = ": << 'OUTPUT'\n"; + $self->{output_closing_comment} = "OUTPUT\n"; } 1; diff --git a/applets/pbot-vm/host/lib/Languages/ksh.pm b/applets/pbot-vm/host/lib/Languages/ksh.pm index 5f8d1b61..f00ab7eb 100755 --- a/applets/pbot-vm/host/lib/Languages/ksh.pm +++ b/applets/pbot-vm/host/lib/Languages/ksh.pm @@ -1,6 +1,6 @@ #!/usr/bin/env perl -# SPDX-FileCopyrightText: 2021 Pragmatic Software +# SPDX-FileCopyrightText: 2021-2024 Pragmatic Software # SPDX-License-Identifier: MIT use warnings; @@ -17,11 +17,11 @@ sub initialize { $self->{default_options} = ''; $self->{cmdline} = 'ksh $options $sourcefile'; - $self->{cmdline_opening_comment} = ": <<'____CMDLINE____'\n"; - $self->{cmdline_closing_comment} = "____CMDLINE____\n"; + $self->{cmdline_opening_comment} = ": <<'CMDLINE'\n"; + $self->{cmdline_closing_comment} = "CMDLINE\n"; - $self->{output_opening_comment} = ": << '____OUTPUT____'\n"; - $self->{output_closing_comment} = "____OUTPUT____\n"; + $self->{output_opening_comment} = ": << 'OUTPUT'\n"; + $self->{output_closing_comment} = "OUTPUT\n"; } 1; diff --git a/applets/pbot-vm/host/lib/Languages/sh.pm b/applets/pbot-vm/host/lib/Languages/sh.pm index 5872c0a9..4a24106e 100755 --- a/applets/pbot-vm/host/lib/Languages/sh.pm +++ b/applets/pbot-vm/host/lib/Languages/sh.pm @@ -1,6 +1,6 @@ #!/usr/bin/env perl -# SPDX-FileCopyrightText: 2021 Pragmatic Software +# SPDX-FileCopyrightText: 2021-2024 Pragmatic Software # SPDX-License-Identifier: MIT use warnings; @@ -17,11 +17,11 @@ sub initialize { $self->{default_options} = ''; $self->{cmdline} = 'sh $options $sourcefile'; - $self->{cmdline_opening_comment} = ": <<'____CMDLINE____'\n"; - $self->{cmdline_closing_comment} = "____CMDLINE____\n"; + $self->{cmdline_opening_comment} = ": <<'CMDLINE'\n"; + $self->{cmdline_closing_comment} = "CMDLINE\n"; - $self->{output_opening_comment} = ": << '____OUTPUT____'\n"; - $self->{output_closing_comment} = "____OUTPUT____\n"; + $self->{output_opening_comment} = ": << 'OUTPUT'\n"; + $self->{output_closing_comment} = "OUTPUT\n"; } 1; diff --git a/applets/pbot-vm/host/lib/Languages/zsh.pm b/applets/pbot-vm/host/lib/Languages/zsh.pm index 3152b86d..3822925c 100755 --- a/applets/pbot-vm/host/lib/Languages/zsh.pm +++ b/applets/pbot-vm/host/lib/Languages/zsh.pm @@ -1,6 +1,6 @@ #!/usr/bin/env perl -# SPDX-FileCopyrightText: 2021 Pragmatic Software +# SPDX-FileCopyrightText: 2021-2024 Pragmatic Software # SPDX-License-Identifier: MIT use warnings; @@ -17,11 +17,11 @@ sub initialize { $self->{default_options} = ''; $self->{cmdline} = 'zsh $options $sourcefile'; - $self->{cmdline_opening_comment} = ": <<'____CMDLINE____'\n"; - $self->{cmdline_closing_comment} = "____CMDLINE____\n"; + $self->{cmdline_opening_comment} = ": <<'CMDLINE'\n"; + $self->{cmdline_closing_comment} = "CMDLINE\n"; - $self->{output_opening_comment} = ": << '____OUTPUT____'\n"; - $self->{output_closing_comment} = "____OUTPUT____\n"; + $self->{output_opening_comment} = ": << 'OUTPUT'\n"; + $self->{output_closing_comment} = "OUTPUT\n"; } 1; diff --git a/applets/pbot-vm/vagrant/Debian-testing64/Vagrantfile b/applets/pbot-vm/vagrant/Debian-testing64/Vagrantfile index 0d8fb4a6..425fc29c 100644 --- a/applets/pbot-vm/vagrant/Debian-testing64/Vagrantfile +++ b/applets/pbot-vm/vagrant/Debian-testing64/Vagrantfile @@ -4,8 +4,8 @@ ENV['VAGRANT_DEFAULT_PROVIDER'] = 'libvirt' PBOTVM_SERIAL = ENV['PBOTVM_SERIAL'] || 5555 -PBOTVM_HEART = ENV['PBOTVM_HEART'] || 5556 -PBOTVM_NAME = ENV['PBOTVM_NAME'] || 'pbot-vagrant-vm' +PBOTVM_HEALTH = ENV['PBOTVM_HEALTH'] || 5556 +PBOTVM_DOMAIN = ENV['PBOTVM_DOMAIN'] || 'pbot-vm' Vagrant.configure("2") do |config| # Every Vagrant development environment requires a box. You can search for diff --git a/applets/pbot-vm/vagrant/README.md b/applets/pbot-vm/vagrant/README.md index 5e0dd2d6..104b112e 100644 --- a/applets/pbot-vm/vagrant/README.md +++ b/applets/pbot-vm/vagrant/README.md @@ -9,47 +9,73 @@ section, then return to this guide. To install vagrant on openSUSE, use: - zypper install --no-recommends vagrant vagrant-libvirt + zypper install --no-recommends vagrant Otherwise see https://vagrant-libvirt.github.io/vagrant-libvirt/installation.html for installation instructions for your platform. ### Install vagrant-libvirt -If your distribution does not have a `vagrant-libvirt` package or if you need an up-to-date version use Vagrant's plugin manager: - vagrant plugin install vagrant-libvirt ### Start Vagrant Box To start a virtual machine, `cd` into one of the PBot-VM Vagrant sub-directories and run the following command. This will download -the appropriate virtual machine image and automatically configure it as a PBot VM Guest. +the appropriate virtual machine image and automatically configure it as the default PBot VM Guest, `pbot-vm` described by +[`host/config/vm-exec.json`](../host/config/vm-exec.json): vagrant up -You may pass optional environment variables to override pbot-vm default configuration (see [PBot VM Environment Variables](../../../doc/VirtualMachine.md#environment-variables)): +You may pass optional environment variables to override pbot-vm default configuration (see [PBot VM Environment Variables](../../../doc/VirtualMachine.md#environment-variables)). +For example, to create `pbot-test-vm` described by [`host/config/vm-exec.json`](../host/config/vm-exec.json): - PBOTVM_SERIAL=7777 PBOTVM_HEART=7778 vagrant up + PBOTVM_DOMAIN=pbot-test-vm PBOTVM_SERIAL=7777 PBOTVM_HEALTH=7778 vagrant up ### Connect to Vagrant Box +Use SSH to connect to the PBot VM Guest: + vagrant ssh +If you specified a `PBOTVM_DOMAIN`, e.g. `pbot-test-vm`, you must specify it: + + PBOTVM_DOMAIN=pbot-test-vm vagrant ssh + ### Start PBot VM Guest Server - sudo guest-server +Once connected to the PBot VM Guest via SSH, start `guest-server` in the background: + + sudo nohup guest-server &> log & Some distributions may require you to specify the full path: - sudo /usr/local/bin/guest-server + sudo nohup /usr/local/bin/guest-server &> log & + +### Disconnect from Vagrant Box + +Now you can type `logout` to exit the PBot VM Guest. + +### Create snapshot of PBot VM Guest + +After you've logged out of the PBot VM Guest with `guest-server` running in the background, create a snapshot. This allows PBot to revert to a known good state when a command times out. +If a `PBOTVM_DOMAIN` was defined, replace `pbot-vm` with that name. + + virsh -c qemu:///system snapshot-create-as pbot-vm 1 + +### Edit vm-exec.json + +If you used `vagrant up` without specifying a `PBOTVM_DOMAIN`, you must edit the [`../host/config/vm-exec.json`](../host/config/vm-exec.json) +configuration file to set the `vagrant` value to `1` for the `pbot-vm` machine. + +If you have specified a `PBOTVM_DOMAIN`, ensure the appropriate entries exist in the `vm-exec.json` configuration file. + +By default, `pbot-test-vm` already has `vagrant` set to `1`. ### Start PBot VM Host Server -After starting the guest-server, you must now start the host server. - - ../host/bin/vm-server + cd ../host/bin/ + ./vm-server ### Test PBot VM - In your instance of PBot, the `sh` and `cc`, etc, commands should now produce output: sh echo Hello world! diff --git a/applets/pbot-vm/vagrant/common/Vagrantfile.common b/applets/pbot-vm/vagrant/common/Vagrantfile.common index 153d1342..1215d4a9 100644 --- a/applets/pbot-vm/vagrant/common/Vagrantfile.common +++ b/applets/pbot-vm/vagrant/common/Vagrantfile.common @@ -4,15 +4,18 @@ Vagrant.configure("2") do |config| config.vm.box_check_update = false - config.vm.hostname = PBOTVM_NAME + config.vm.define PBOTVM_DOMAIN + config.vm.hostname = PBOTVM_DOMAIN config.vm.provider :libvirt do |libvirt| + libvirt.title = PBOTVM_DOMAIN + libvirt.default_prefix = "" libvirt.qemuargs :value => "-chardev" libvirt.qemuargs :value => "socket,id=charserial1,host=127.0.0.1,port=#{PBOTVM_SERIAL},server=on,wait=off" libvirt.qemuargs :value => "-device" libvirt.qemuargs :value => '{"driver":"isa-serial","chardev":"charserial1","id":"serial1","index":2}' libvirt.qemuargs :value => "-chardev" - libvirt.qemuargs :value => "socket,id=charserial2,host=127.0.0.1,port=#{PBOTVM_HEART},server=on,wait=off" + libvirt.qemuargs :value => "socket,id=charserial2,host=127.0.0.1,port=#{PBOTVM_HEALTH},server=on,wait=off" libvirt.qemuargs :value => "-device" libvirt.qemuargs :value => '{"driver":"isa-serial","chardev":"charserial2","id":"serial2","index":3}' end diff --git a/applets/pbot-vm/vagrant/openSUSE-Tumbleweed/Vagrantfile b/applets/pbot-vm/vagrant/openSUSE-Tumbleweed/Vagrantfile index 3d11a3c8..5e18bcf1 100644 --- a/applets/pbot-vm/vagrant/openSUSE-Tumbleweed/Vagrantfile +++ b/applets/pbot-vm/vagrant/openSUSE-Tumbleweed/Vagrantfile @@ -4,8 +4,8 @@ ENV['VAGRANT_DEFAULT_PROVIDER'] = 'libvirt' PBOTVM_SERIAL = ENV['PBOTVM_SERIAL'] || 5555 -PBOTVM_HEART = ENV['PBOTVM_HEART'] || 5556 -PBOTVM_NAME = ENV['PBOTVM_NAME'] || 'pbot-vagrant-vm' +PBOTVM_HEALTH = ENV['PBOTVM_HEALTH'] || 5556 +PBOTVM_DOMAIN = ENV['PBOTVM_DOMAIN'] || 'pbot-vm' Vagrant.configure("2") do |config| # Every Vagrant development environment requires a box. You can search for diff --git a/doc/README.md b/doc/README.md index 1df81cfe..e0be2885 100644 --- a/doc/README.md +++ b/doc/README.md @@ -353,7 +353,8 @@ * [Initial virtual machine set-up complete](VirtualMachine.md#initial-virtual-machine-set-up-complete) * [Start PBot VM Host](VirtualMachine.md#start-pbot-vm-host) * [Test PBot](VirtualMachine.md#test-pbot) -* [QEMU command from libvirt](VirtualMachine.md#qemu-command-from-libvirt) + * [Adding additional VMs](VirtualMachine.md#adding-additional-vms) + * [QEMU command from libvirt](VirtualMachine.md#qemu-command-from-libvirt) * [Frequently Asked Questions](FAQ.md#frequently-asked-questions) diff --git a/doc/VirtualMachine.md b/doc/VirtualMachine.md index 95b5b645..cb2f45b0 100644 --- a/doc/VirtualMachine.md +++ b/doc/VirtualMachine.md @@ -40,11 +40,12 @@ PBOTVM_DOMAIN | `pbot-vm` | The libvirt domain identifier PBOTVM_ADDR | `127.0.0.1` | `vm-server` address for incoming `vm-client` commands PBOTVM_PORT | `9000` | `vm-server` port for incoming `vm-client` commands PBOTVM_SERIAL | `5555` | TCP port for serial communication -PBOTVM_HEART | `5556` | TCP port for serial heartbeats +PBOTVM_HEALTH | `5556` | TCP port for serial health-check PBOTVM_CID | `7` | Context ID for VM socket (if using VSOCK) PBOTVM_VPORT | `5555` | VM socket service port (if using VSOCK) PBOTVM_TIMEOUT | `10` | Duration before command times out (in seconds) PBOTVM_NOREVERT | not set | If set then the VM will not revert to previous snapshot +PBOTVM_VAGRANT | not set | If set then commands suitable for Vagrant VMs will be used # Initial virtual machine set-up These steps need to be done only once during the first-time set-up. @@ -281,14 +282,13 @@ Go into the `applets/pbot-vm/host/devices` directory and run the `add-serials` s This will enable the `/dev/ttyS2` and `/dev/ttyS3` serial ports in the guest and connect them to the following TCP addresses on the host: `127.0.0.1:5555` and `127.0.0.1:5556`, respectively. `ttyS2/5555` is the data channel used to send commands or code to the -virtual machine and to read back output. `ttyS3/5556` is simply a newline sent every -5 seconds, representing a heartbeat, used to ensure that the PBot communication -channel is healthy. +virtual machine and to read back output. `ttyS3/5556` responds with the output of the `vmstat` +command to let us know about the virtual machine's health. -You may use the `PBOTVM_DOMAIN`, `PBOTVM_SERIAL` and `PBOTVM_HEART` environment variables to override +You may use the `PBOTVM_DOMAIN`, `PBOTVM_SERIAL` and `PBOTVM_HEALTH` environment variables to override the default values. To use ports `7777` and `7778` instead: - host$ PBOTVM_SERIAL=7777 PBOTVM_HEART=7778 ./add-serials + host$ PBOTVM_SERIAL=7777 PBOTVM_HEALTH=7778 ./add-serials If you later want to change the serial ports or the TCP ports, execute the command `virsh edit pbot-vm` on the host. This will open the `pbot-vm` XML configuration @@ -302,7 +302,7 @@ Add the following options to your `qemu` command-line arguments: -chardev socket,id=charserial2,host=127.0.0.1,port=5556,server=on,wait=off -device {"driver":"isa-serial","chardev":"charserial2","id":"serial2","index":3} -If necessary, replace `5555` and `5556` with your preferred `PBOTVM_SERIAL` and `PBOTVM_HEART` values. +If necessary, replace `5555` and `5556` with your preferred `PBOTVM_SERIAL` and `PBOTVM_HEALTH` values. ### Set up virtio-vsock VM sockets (AF_VSOCK) are a Linux-specific feature (at the time of this writing). They @@ -433,7 +433,7 @@ this running. ### Test PBot VM Guest Let's make sure everything's working up to this point. On the host, there should -be two open TCP ports on `PBOTVM_SERIAL` and `PBOTVM_HEART` (default values `5555` and `5556`). +be two open TCP ports on `PBOTVM_SERIAL` and `PBOTVM_HEALTH` (default values `5555` and `5556`). On the host, execute the command: host$ nc -zv 127.0.0.1 5555-5556 @@ -468,8 +468,7 @@ to save a snapshot of the virtual machine waiting for incoming commands. host$ virsh snapshot-create-as pbot-vm 1 -If the virtual machine ever times-out or its heartbeat stops responding, PBot -will revert the virtual machine to this saved snapshot. +If the virtual machine times-out, PBot will revert to this saved snapshot. ## Install host packages Ensure the following packages are installed on the host machine: @@ -491,12 +490,12 @@ and execute the `vm-server` script: This will start a TCP server on port `9000`. It will listen for incoming commands and pass them along to the virtual machine's TCP serial port `5555`. It will also monitor -the heartbeat port `5556` to ensure the PBot VM Guest server is alive. +the health-check port `5556` to ensure the PBot VM Guest server is alive. You may override any of the defaults by setting environment variables. For example, to -use `other-vm` with a longer `30` second timeout, on different serial and heartbeat ports: +use `pbot-test-vm` with a longer `30` second timeout, on different serial and health-check ports: - host$ PBOTVM_DOMAIN="other-vm" PBOTVM_SERVER=9001 PBOTVM_SERIAL=7777 PBOTVM_HEART=7778 PBOTVM_TIMEOUT=30 ./vm-server + host$ PBOTVM_DOMAIN="pbot-test-vm" PBOTVM_SERVER=9001 PBOTVM_SERIAL=7777 PBOTVM_HEALTH=7778 PBOTVM_TIMEOUT=30 ./vm-server ## Test PBot All done. Everything is set up now. @@ -516,7 +515,22 @@ In your instance of PBot, the `sh echo hello` command should output `hello`. sh echo hello hello -# QEMU command from libvirt +## Adding additional VMs +You may add as many virtual machines as your system can handle. Edit the [`vm-exec.json`](../applets/pbot-vm/host/config/vm-exec.json) configuration +file to do so. Then use the `-vm=...` option to select them with the `sh`, `cc`, etc, commands. + +For instance, if you create a FreeBSD VM and have added it to [`vm-exec.json`](../applets/pbot-vm/host/config/vm-exec.json) with an alias of `freebsd`: + + sh -vm=freebsd echo testing + testing + +You can create aliases to omit the `-vm=...` option: + + factalias bsd-sh sh -vm=freebsd $args + bsd-sh foobar + foobar + +## QEMU command from libvirt This is the QEMU command-line arguments used by libvirt. Extract flags as needed, e.g. `-chardev`. /usr/bin/qemu-system-x86_64 -name guest=pbot-vm,debug-threads=on -S -object {"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain-2-pbot-vm/master-key.aes"} -machine pc-q35-6.2,usb=off,vmport=off,dump-guest-core=off,memory-backend=pc.ram -accel kvm -cpu IvyBridge-IBRS,ss=on,vmx=on,pdcm=on,pcid=on,hypervisor=on,arat=on,tsc-adjust=on,umip=on,md-clear=on,stibp=on,arch-capabilities=on,ssbd=on,xsaveopt=on,ibpb=on,ibrs=on,amd-stibp=on,amd-ssbd=on,skip-l1dfl-vmentry=on,pschange-mc-no=on,aes=off,rdrand=off -m 2048 -object {"qom-type":"memory-backend-ram","id":"pc.ram","size":2147483648} -overcommit mem-lock=off -smp 2,sockets=2,cores=1,threads=1 -uuid ec9eebba-8ba1-4de3-8ec0-caa6fd808ad4 -no-user-config -nodefaults -chardev socket,id=charmonitor,fd=38,server=on,wait=off -mon chardev=charmonitor,id=monitor,mode=control -rtc base=utc,driftfix=slew -global kvm-pit.lost_tick_policy=delay -no-hpet -no-shutdown -global ICH9-LPC.disable_s3=1 -global ICH9-LPC.disable_s4=1 -boot strict=on -device {"driver":"pcie-root-port","port":16,"chassis":1,"id":"pci.1","bus":"pcie.0","multifunction":true,"addr":"0x2"} -device {"driver":"pcie-root-port","port":17,"chassis":2,"id":"pci.2","bus":"pcie.0","addr":"0x2.0x1"} -device {"driver":"pcie-root-port","port":18,"chassis":3,"id":"pci.3","bus":"pcie.0","addr":"0x2.0x2"} -device {"driver":"pcie-root-port","port":19,"chassis":4,"id":"pci.4","bus":"pcie.0","addr":"0x2.0x3"} -device {"driver":"pcie-root-port","port":20,"chassis":5,"id":"pci.5","bus":"pcie.0","addr":"0x2.0x4"} -device {"driver":"pcie-root-port","port":21,"chassis":6,"id":"pci.6","bus":"pcie.0","addr":"0x2.0x5"} -device {"driver":"pcie-root-port","port":22,"chassis":7,"id":"pci.7","bus":"pcie.0","addr":"0x2.0x6"} -device {"driver":"pcie-root-port","port":23,"chassis":8,"id":"pci.8","bus":"pcie.0","addr":"0x2.0x7"} -device {"driver":"pcie-root-port","port":24,"chassis":9,"id":"pci.9","bus":"pcie.0","multifunction":true,"addr":"0x3"} -device {"driver":"pcie-root-port","port":25,"chassis":10,"id":"pci.10","bus":"pcie.0","addr":"0x3.0x1"} -device {"driver":"pcie-root-port","port":26,"chassis":11,"id":"pci.11","bus":"pcie.0","addr":"0x3.0x2"} -device {"driver":"pcie-root-port","port":27,"chassis":12,"id":"pci.12","bus":"pcie.0","addr":"0x3.0x3"} -device {"driver":"pcie-root-port","port":28,"chassis":13,"id":"pci.13","bus":"pcie.0","addr":"0x3.0x4"} -device {"driver":"pcie-root-port","port":29,"chassis":14,"id":"pci.14","bus":"pcie.0","addr":"0x3.0x5"} -device {"driver":"qemu-xhci","p2":15,"p3":15,"id":"usb","bus":"pci.2","addr":"0x0"} -device {"driver":"virtio-serial-pci","id":"virtio-serial0","bus":"pci.3","addr":"0x0"} -blockdev {"driver":"file","filename":"/home/pbot/pbot-vms/openSUSE-Tumbleweed-Minimal-VM.x86_64-kvm-and-xen.qcow2","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"} -blockdev {"node-name":"libvirt-1-format","read-only":false,"driver":"qcow2","file":"libvirt-1-storage","backing":null} -device {"driver":"virtio-blk-pci","bus":"pci.4","addr":"0x0","drive":"libvirt-1-format","id":"virtio-disk0","bootindex":1} -netdev {"type":"tap","fd":"39","vhost":true,"vhostfd":"41","id":"hostnet0"} -device {"driver":"virtio-net-pci","netdev":"hostnet0","id":"net0","mac":"52:54:00:03:16:5a","bus":"pci.1","addr":"0x0"} -chardev pty,id=charserial0 -device {"driver":"isa-serial","chardev":"charserial0","id":"serial0","index":0} -chardev socket,id=charserial1,host=127.0.0.1,port=5555,server=on,wait=off -device {"driver":"isa-serial","chardev":"charserial1","id":"serial1","index":2} -chardev socket,id=charserial2,host=127.0.0.1,port=5556,server=on,wait=off -device {"driver":"isa-serial","chardev":"charserial2","id":"serial2","index":3} -chardev socket,id=charchannel0,fd=37,server=on,wait=off -device {"driver":"virtserialport","bus":"virtio-serial0.0","nr":1,"chardev":"charchannel0","id":"channel0","name":"org.qemu.guest_agent.0"} -chardev spicevmc,id=charchannel1,name=vdagent -device {"driver":"virtserialport","bus":"virtio-serial0.0","nr":2,"chardev":"charchannel1","id":"channel1","name":"com.redhat.spice.0"} -device {"driver":"usb-tablet","id":"input0","bus":"usb.0","port":"1"} -audiodev {"id":"audio1","driver":"spice"} -spice port=5901,addr=127.0.0.1,disable-ticketing=on,image-compression=off,seamless-migration=on -device {"driver":"virtio-vga","id":"video0","max_outputs":1,"bus":"pcie.0","addr":"0x1"} -device {"driver":"ich9-intel-hda","id":"sound0","bus":"pcie.0","addr":"0x1b"} -device {"driver":"hda-duplex","id":"sound0-codec0","bus":"sound0.0","cad":0,"audiodev":"audio1"} -chardev spicevmc,id=charredir0,name=usbredir -device {"driver":"usb-redir","chardev":"charredir0","id":"redir0","bus":"usb.0","port":"2"} -chardev spicevmc,id=charredir1,name=usbredir -device {"driver":"usb-redir","chardev":"charredir1","id":"redir1","bus":"usb.0","port":"3"} -device {"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.5","addr":"0x0"} -object {"qom-type":"rng-random","id":"objrng0","filename":"/dev/urandom"} -device {"driver":"virtio-rng-pci","rng":"objrng0","id":"rng0","bus":"pci.6","addr":"0x0"} -loadvm 1 -sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny -device {"driver":"vhost-vsock-pci","id":"vsock0","guest-cid":7,"vhostfd":"28","bus":"pci.7","addr":"0x0"} -msg timestamp=on diff --git a/lib/PBot/VERSION.pm b/lib/PBot/VERSION.pm index a8a9552a..c6698aa9 100644 --- a/lib/PBot/VERSION.pm +++ b/lib/PBot/VERSION.pm @@ -25,8 +25,8 @@ use PBot::Imports; # These are set by the /misc/update_version script use constant { BUILD_NAME => "PBot", - BUILD_REVISION => 4740, - BUILD_DATE => "2024-04-07", + BUILD_REVISION => 4741, + BUILD_DATE => "2024-04-10", }; sub initialize {}