Merge pull request #478 from ylei-tsubame/patch-screenshot

New screenshot system
This commit is contained in:
Digimer 2023-09-24 01:38:18 -04:00 committed by GitHub
commit a0b188ffbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 645 additions and 77 deletions

View File

@ -1118,6 +1118,7 @@ sub _set_paths
pgsql => "/var/lib/pgsql/",
resource_status => "/sys/kernel/debug/drbd/resources",
scan_agents => "/usr/sbin/scancore-agents",
screenshots => "/opt/alteeve/screenshots",
shared => {
archives => "/mnt/shared/archives",
base => "/mnt/shared",
@ -1263,6 +1264,8 @@ sub _set_paths
pgrep => "/usr/bin/pgrep",
ps => "/usr/bin/ps",
psql => "/usr/bin/psql",
pamtopng => "/usr/bin/pamtopng",
pnmtojpeg => "/usr/bin/pnmtojpeg",
'postgresql-setup' => "/usr/bin/postgresql-setup",
postmap => "/usr/sbin/postmap",
postqueue => "/usr/sbin/postqueue",
@ -1285,6 +1288,7 @@ sub _set_paths
storcli64 => "/opt/MegaRAID/storcli/storcli64",
strings => "/usr/bin/strings",
'striker-auto-initialize-all' => "/usr/sbin/striker-auto-initialize-all",
'striker-get-screenshots' => "/usr/sbin/striker-get-screenshots",
'striker-get-peer-data' => "/usr/sbin/striker-get-peer-data",
'striker-initialize-host' => "/usr/sbin/striker-initialize-host",
'striker-manage-install-target' => "/usr/sbin/striker-manage-install-target",

View File

@ -5175,6 +5175,10 @@ Parameters;
When writing to a file that already exists, and C<< overwrite >> is true, the existing backup will be backed up prior to being rewritten.
=head3 binary (optional, default '0')
When set to '1', this indicates that the body is binary data, which prevents logging of the file body.
=head3 body (optional)
This is the contents of the file. If it is blank, an empty file will be created (similar to using 'C<< touch >>' on the command line).
@ -5233,6 +5237,7 @@ sub write_file
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Storage->write_file()" }});
my $backup = defined $parameter->{backup} ? $parameter->{backup} : 1;
my $binary = defined $parameter->{binary} ? $parameter->{binary} : 0;
my $body = defined $parameter->{body} ? $parameter->{body} : "";
my $file = defined $parameter->{file} ? $parameter->{file} : "";
my $group = defined $parameter->{group} ? $parameter->{group} : getgrgid($();
@ -5247,7 +5252,7 @@ sub write_file
my $error = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
backup => $backup,
body => (not $secure) ? $body : $anvil->Log->is_secure($body),
binary => $binary,
file => $file,
group => $group,
mode => $mode,
@ -5259,6 +5264,12 @@ sub write_file
user => $user,
remote_user => $remote_user,
}});
if (not $binary)
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, list => {
body => (not $secure) ? $body : $anvil->Log->is_secure($body),
}});
}
# Make sure the user and group and just one digit or word.
$user =~ s/^(\S+)\s.*$/$1/;
@ -5480,6 +5491,7 @@ fi";
$temp_file .= ".".$anvil->Get->uuid({debug => $debug, short => 1});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { temp_file => $temp_file }});
$anvil->Storage->write_file({
binary => $binary,
body => $body,
debug => $debug,
file => $temp_file,

View File

@ -2598,6 +2598,16 @@ The file: [#!variable!file!#] needs to be updated. The difference is:
<key name="log_0800">- Ejecting: [#!variable!device_path!#] from: [#!variable!device_target!#].</key>
<key name="log_0801">Updating the stored definition and undefining the server now:</key>
<key name="log_0802">Working with the server: [#!variable!server_name!#], UUID: [#!variable!server_uuid!#]</key>
<key name="log_0803">Looking for access to: [#!variable!host_name!#]</key>
<key name="log_0804">Connecting: [#!variable!host_name!#] via: [#!variable!target_ip!#]</key>
<key name="log_0805">Getting a screenshot from: [#!variable!server_name!#] (uuid: [#!variable!server_uuid!#]) from the host: [#!variable!host_name!#].</key>
<key name="log_0806">- Saving the screenshot: [#!variable!ppm_file!#].</key>
<key name="log_0807">- Converting: [#!variable!ppm_file!#] to:
[#!variable!new_file!#]</key>
<key name="log_0808">Deleting screenshots older than: [#!variable!maximum_age!#].</key>
<key name="log_0809">- Deleting the server: [#!variable!server_name!#]'s screenshot: [#!variable!file!#].</key>
<key name="log_0810">- No access to: [#!variable!host_name!#] found.</key>
<key name="log_0811">- The host: [#!variable!host_name!#] is not configured, skipping it.</key>
<!-- Messages for users (less technical than log entries), though sometimes used for logs, too. -->
<key name="message_0001">The host name: [#!variable!target!#] does not resolve to an IP address.</key>
@ -3638,6 +3648,14 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st
<key name="unit_0038">%</key>
<key name="unit_0039">Amps</key>
<key name="unit_0040">Going Back</key> <!-- Bond state -->
<key name="unit_0041">- Server is running.</key>
<key name="unit_0042">- Server is blocked (IO contention?).</key>
<key name="unit_0043">- Server is paused (migration target?).</key>
<key name="unit_0044">- Server is shutting down.</key>
<key name="unit_0045">- Server is shut off.</key>
<key name="unit_0046">- Server is crashed!</key>
<key name="unit_0047">- Server is suspended.</key>
<key name="unit_0048">- Server is in an unknown state (int: [#!variable!state!#]).</key>
<!-- These are special. These are used to describe the UPSes that ScanCore supports. These
are used when adding UPSes to the system for use in Install Manifests.
@ -3901,6 +3919,11 @@ We will try to proceed anyway.</key>
#!variable!error!#
====================
We will try to proceed anyway.</key>
<key name="warning_0162">Failed to connect to libvirtd on: [#!variable!host_name!#] via the URI: [#!variable!uri!#]. The error, if any, was:
====
#!variable!error!#
====
</key>
</language>
<!-- 日本語 -->

File diff suppressed because one or more lines are too long

View File

@ -10,6 +10,11 @@ const EMPTY_SERVER_PATHS: ServerPath = {
incoming: {},
},
},
opt: {
alteeve: {
screenshots: {},
},
},
tmp: {},
usr: {
bin: {

View File

@ -1,12 +1,31 @@
import assert from 'assert';
import { execSync } from 'child_process';
import { RequestHandler } from 'express';
import { readFileSync, readdirSync } from 'fs';
import path from 'path';
import { REP_UUID, SERVER_PATHS } from '../../consts';
import { P_UUID, REP_UUID, SERVER_PATHS } from '../../consts';
import { getVncinfo } from '../../accessModule';
import { sanitize } from '../../sanitize';
import { stderr, stdout } from '../../shell';
import { stderr, stdout, stdoutVar } from '../../shell';
type ServerSsMeta = {
name: string;
timestamp: number;
uuid: string;
};
const disassembleServerSsName = (name: string): ServerSsMeta => {
const csv = name.replace(
new RegExp(`^server-uuid_(${P_UUID})_timestamp-(\\d+).*$`),
'$1,$2',
);
const [uuid, t] = csv.split(',', 2);
const timestamp = Number(t);
return { name, timestamp, uuid };
};
export const getServerDetail: RequestHandler<
ServerDetailParamsDictionary,
@ -38,20 +57,54 @@ export const getServerDetail: RequestHandler<
}
if (ss) {
const rsbody: ServerDetailScreenshot = { screenshot: '' };
const rsBody: ServerDetailScreenshot = { screenshot: '' };
const ssDir = SERVER_PATHS.opt.alteeve.screenshots.self;
let ssNames: string[];
try {
rsbody.screenshot = execSync(
`${SERVER_PATHS.usr.sbin['anvil-get-server-screenshot'].self} --convert --resize 500x500 --server-uuid '${serverUuid}'`,
{ encoding: 'utf-8' },
);
ssNames = readdirSync(SERVER_PATHS.opt.alteeve.screenshots.self, {
encoding: 'utf-8',
});
} catch (error) {
stderr(`Failed to server ${serverUuid} screenshot; CAUSE: ${error}`);
stderr(
`Failed to list server ${serverUuid} screenshots; CAUSE: ${error}`,
);
return response.status(500).send();
}
return response.send(rsbody);
const ssMetas = ssNames
.reduce<ServerSsMeta[]>((previous, name) => {
const meta = disassembleServerSsName(name);
if (meta.uuid === serverUuid) {
previous.push(meta);
}
return previous;
}, [])
.sort((a, b) => {
if (a.timestamp === b.timestamp) return 0;
return a.timestamp > b.timestamp ? 1 : -1;
});
const ssMetaLatest = ssMetas.pop();
stdoutVar(ssMetaLatest, `Latest server screenshot: `);
if (ssMetaLatest) {
const { name } = ssMetaLatest;
const ssLatest = readFileSync(path.join(ssDir, name), {
encoding: 'base64',
});
rsBody.screenshot = ssLatest;
}
return response.send(rsBody);
} else if (vnc) {
let rsbody: ServerDetailVncInfo;

View File

@ -552,8 +552,13 @@ sub handle_periodic_tasks
}});
# Even when this runs, it should finish in under ten seconds so we don't need to background it.
my ($parse_output, $return_code) = $anvil->System->call({debug => 3, shell_call => $anvil->data->{path}{exe}{'anvil-parse-fence-agents'}.$anvil->Log->switches, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { parse_output => $parse_output }});
my $shell_call = $anvil->data->{path}{exe}{'anvil-parse-fence-agents'}.$anvil->Log->switches;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($parse_output, $return_code) = $anvil->System->call({shell_call => $shell_call, source => $THIS_FILE, line => __LINE__});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
parse_output => $parse_output,
return_code => $return_code,
}});
# Scan the local network.
update_state_file($anvil);
@ -570,6 +575,25 @@ sub handle_periodic_tasks
# Check for stale db_in_use states.
check_db_in_use_states($anvil);
# Do Striker-specific minute tasks
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { host_type => $host_type }});
if ($host_type eq "striker")
{
# This can take a while, but it's been optimized to minimize how long it takes to
# run. To be safe, we'll still background it.
my $shell_call = $anvil->data->{path}{exe}{'striker-get-screenshots'}.$anvil->Log->switches;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({
background => 1,
shell_call => $shell_call,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => {
output => $output,
return_code => $return_code,
}});
}
}
# Now check to see if it's time to run less frequent tasks.

View File

@ -6,6 +6,7 @@
use strict;
use warnings;
use Anvil::Tools;
use Sys::Virt;
$| = 1;
@ -18,12 +19,11 @@ if (($running_directory =~ /^\./) && ($ENV{PWD}))
my $anvil = Anvil::Tools->new();
$anvil->Get->switches;
my $debug = $anvil->data->{switches}{debug};
$anvil->Get->switches({list => ["convert", "job-uuid", "out-file-id", "resize", "request-host-name". "server-uuid'"], man => $THIS_FILE});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}});
$anvil->Database->connect;
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => $debug, secure => 0, key => "log_0132" });
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132" });
if (not $anvil->data->{sys}{database}{connections})
{
# No databases, exit.
@ -35,7 +35,7 @@ if (not $anvil->data->{sys}{database}{connections})
if (not $anvil->data->{switches}{'job-uuid'})
{
$anvil->data->{switches}{'job-uuid'} = $anvil->Job->get_job_uuid({ program => $THIS_FILE });
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => {
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, list => {
"switches::job-uuid" => $anvil->data->{switches}{'job-uuid'}
} });
}
@ -54,25 +54,19 @@ if ($anvil->data->{switches}{'job-uuid'})
foreach my $line (split/\n/, $anvil->data->{jobs}{job_data})
{
if ($line =~ /server-uuid=(.*?)$/)
{
$anvil->data->{switches}{'server-uuid'} = $1;
}
my ($variable, $value) = ($line =~ /^(.*)=(.*)$/);
$value =~ s/^"(.*)\"/$1/;
$value =~ s/^'(.*)\'/$1/;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:line' => $line,
's2:variable' => $variable,
's3:value' => $value,
}});
if ($line =~ /resize=(.*?)$/)
{
$anvil->data->{switches}{'resize'} = $1;
}
if ($line =~ /request-host-uuid=(.*?)$/)
{
$anvil->data->{switches}{'request-host-uuid'} = $1;
}
if ($line =~ /out-file-id=(.*?)$/)
{
$anvil->data->{switches}{'out-file-id'} = $1;
}
$anvil->data->{switches}{$variable} = $value;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"switches::${variable}" => $anvil->data->{switches}{$variable},
}});
}
}
@ -82,41 +76,43 @@ my $out_file_id = $anvil->data->{switches}{'out-file-id'};
my $resize_args = $anvil->data->{switches}{'resize'};
my $request_host_name = $anvil->data->{switches}{'request-host-name'};
my $server_uuid = $anvil->data->{switches}{'server-uuid'};
$out_file_id = ( (defined $out_file_id) && ($out_file_id ne "#!SET!#") ) ? "_${out_file_id}" : "";
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => {
debug => $debug,
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, list => {
is_convert => $is_convert,
job_uuid => $job_uuid,
request_host_name => $request_host_name,
out_file_id => $out_file_id,
resize_args => $resize_args,
request_host_name => $request_host_name,
server_uuid => $server_uuid,
} });
}});
$out_file_id = ((defined $out_file_id) && ($out_file_id ne "#!SET!#")) ? "_".$out_file_id : "";
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, list => { out_file_id => $out_file_id }});
if ($server_uuid)
{
my $out_file_path = $anvil->data->{path}{directories}{tmp}."/${server_uuid}_screenshot${out_file_id}";
my $out_file_path = $anvil->data->{path}{directories}{tmp}."/".$server_uuid."_screenshot".$out_file_id;
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, list => { out_file_path => $out_file_path }});
if ($is_convert)
{
my ($rcode, $encoded_image) = convert_server_screenshot({
debug => $debug,
my ($return_code, $encoded_image) = convert_server_screenshot({
resize_args => $resize_args,
source_file => $out_file_path,
});
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, list => { return_code => $return_code }});
print($encoded_image) if ($rcode == 0);
print($encoded_image) if ($return_code == 0);
$anvil->nice_exit({ exit_code => $rcode });
$anvil->nice_exit({ exit_code => $return_code });
}
my ($rcode) = get_server_screenshot({
debug => $debug,
my ($return_code) = get_server_screenshot({
output_file => $out_file_path,
server_uuid => $server_uuid,
});
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, list => { return_code => $return_code }});
if ($rcode > 0)
if ($return_code > 0)
{
$anvil->Job->update_progress({ progress => 100, message => "message_0265" });
@ -127,19 +123,15 @@ if ($server_uuid)
{
chomp $request_host_name;
my $rsync = $anvil->data->{path}{exe}{rsync};
foreach my $host_name ( split(/,/, $request_host_name) )
{
my $shell_call = "$rsync -e \"ssh -o BatchMode=yes\" -ac '$out_file_path' '$host_name':'$out_file_path'";
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => {
host_name => $host_name,
out_file_path => $out_file_path,
shell_call => $shell_call,
my $shell_call = $anvil->data->{path}{exe}{rsync}." -e \"ssh -o BatchMode=yes\" -ac '".$out_file_path."' '".$host_name."':'".$out_file_path."'";
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, list => {
host_name => $host_name,
shell_call => $shell_call,
} });
system_call({ debug => $debug, shell_call => $shell_call });
system_call({shell_call => $shell_call });
}
}
@ -154,16 +146,16 @@ else
$anvil->nice_exit({ exit_code => 0 });
#
# Functions
#
#############################################################################################################
# Functions #
#############################################################################################################
sub convert_server_screenshot
{
my $parameters = shift;
my $resize_args = $parameters->{resize_args};
my $source_file = $parameters->{source_file};
my $debug = $parameters->{debug};
my $host_type = $anvil->Get->host_type();
@ -179,12 +171,12 @@ sub convert_server_screenshot
{
my ($resize_x, $resize_y) = split(/x/ , $resize_args);
$shell_call .= " | $pamscale -quiet -xyfit $resize_x $resize_y";
$shell_call .= " | ".$pamscale." -quiet -xyfit ".$resize_x $resize_y;
}
$shell_call .= " | $pamtopng -quiet | $base64 --wrap 0";
$shell_call .= " | ".$pamtopng." -quiet | ".$base64." --wrap 0";
my ($output, $return_code) = system_call({ debug => $debug, shell_call => $shell_call });
my ($output, $return_code) = system_call({shell_call => $shell_call });
return ($return_code, $output);
}
@ -194,16 +186,15 @@ sub get_server_screenshot
my $parameters = shift;
my $output_file = $parameters->{output_file};
my $server_uuid = $parameters->{server_uuid};
my $debug = $parameters->{debug};
return (1) if ( (not $server_uuid) || (not $output_file) );
my $setsid = $anvil->data->{path}{exe}{setsid};
my $virsh = $anvil->data->{path}{exe}{virsh};
my $shell_call = "$setsid --wait $virsh --quiet screenshot --domain $server_uuid --file $output_file";
my $shell_call = $anvil->data->{path}{exe}{setsid}." --wait ".$anvil->data->{path}{exe}{virsh}." --quiet screenshot --domain ".$server_uuid." --file ".$output_file;
my ($output, $return_code) = system_call({ debug => $debug, shell_call => $shell_call });
my ($output, $return_code) = system_call({shell_call => $shell_call });
return ($return_code, $output);
}
@ -211,12 +202,11 @@ sub get_server_screenshot
sub system_call
{
my $parameters = shift;
my $debug = $parameters->{debug};
my @call_result = $anvil->System->call($parameters);
my ($output, $return_code) = @call_result;
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => {
%$parameters,
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, list => {
%{$parameters},
output => $output,
return_code => $return_code,
} });

457
tools/striker-get-screenshots Executable file
View File

@ -0,0 +1,457 @@
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use Sys::Virt;
use Anvil::Tools;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
$running_directory =~ s/^\./$ENV{PWD}/;
}
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
my $anvil = Anvil::Tools->new();
# Read switches
$anvil->Get->switches({list => ["job-uuid"], man => $THIS_FILE});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }});
# We'll try to connect in case we're adding additional peers.
$anvil->Database->connect();
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132"});
if ($anvil->data->{switches}{'job-uuid'})
{
# Load the job data.
$anvil->Job->clear();
$anvil->Job->get_job_details();
$anvil->Job->update_progress({
progress => 1,
job_picked_up_by => $$,
job_picked_up_at => time,
message => "message_0251",
});
}
$anvil->data->{job}{progress} = 1;
# Which subnodes are up?
$anvil->Database->get_hosts();
$anvil->Database->get_dr_links();
foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{sys}{hosts}{by_name}})
{
my $host_uuid = $anvil->data->{sys}{hosts}{by_name}{$host_name};
my $short_host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{short_host_name};
my $host_status = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_status};
my $host_type = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type};
my $anvil_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{anvil_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
host_name => $host_name,
host_uuid => $host_uuid,
short_host_name => $short_host_name,
host_status => $host_status,
host_type => $host_type,
}});
next if $host_status ne "online";
next if $host_type eq "striker";
$anvil->Job->update_progress({
progress => $anvil->data->{job}{progress} < 99 ? ++$anvil->data->{job}{progress} : $anvil->data->{job}{progress},
message => "log_0803",
log_level => 2,
variables => { host_name => $short_host_name }
});
# If it's a subnode, make sure it's in an Anvil! node and skip it if not.
next if (($host_type eq "node") && (not $anvil_name));
# If it's a DR host, make sure it's linked to at least one Anvil! node and skip it if not.
if ($host_type eq "dr")
{
my $link_count = 0;
if (exists $anvil->data->{dr_links}{by_host_uuid}{$host_uuid})
{
$link_count = keys %{$anvil->data->{dr_links}{by_host_uuid}{$host_uuid}{dr_link_anvil_name}};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { link_count => $link_count }});
}
next if not $link_count;
}
# Make sure the target is configured. If not, skip it.
my ($configured, $variable_uuid, $modified_date) = $anvil->Database->read_variable({
variable_name => "system::configured",
variable_source_uuid => $host_uuid,
variable_source_table => "hosts",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
configured => $configured,
variable_uuid => $variable_uuid,
modified_date => $modified_date,
}});
if (not $configured)
{
$anvil->Job->update_progress({
progress => $anvil->data->{job}{progress} < 99 ? ++$anvil->data->{job}{progress} : $anvil->data->{job}{progress},
message => "log_0811",
log_level => 2,
variables => { host_name => $short_host_name }
});
next;
}
### Test access using the bcn, then ifn.
# Can we access the host?
my $matches = $anvil->Network->find_access({
debug => 2,
target => $host_name,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { matches => $matches }});
my $connected = 0;
foreach my $preferred_network ("bcn", "ifn", "any")
{
next if $connected;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { preferred_network => $preferred_network }});
foreach my $network_name (sort {$a cmp $b} keys %{$anvil->data->{network_access}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { network_name => $network_name }});
if (($network_name !~ /^$preferred_network/) && ($preferred_network ne "any"))
{
next;
}
my $target_ip = $anvil->data->{network_access}{$network_name}{target_ip_address};
my $test_access = $anvil->Remote->test_access({target => $target_ip});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:network_name' => $network_name,
's2:target_ip' => $target_ip,
's3:test_access' => $test_access,
}});
if ($test_access)
{
# Collect screenshots!
$anvil->Job->update_progress({
progress => $anvil->data->{job}{progress} < 99 ? ++$anvil->data->{job}{progress} : $anvil->data->{job}{progress},
message => "log_0804",
log_level => 2,
variables => {
host_name => $short_host_name,
target_ip => $target_ip,
}
});
get_screenshots($anvil, $host_uuid, $target_ip);
$connected = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { connected => $connected }});
}
}
}
if (not $connected)
{
$anvil->Job->update_progress({
progress => $anvil->data->{job}{progress} < 99 ? ++$anvil->data->{job}{progress} : $anvil->data->{job}{progress},
message => "log_0810",
log_level => 2,
variables => { host_name => $short_host_name }
});
}
}
# Look through screenshots and delete any more than 2 hours old.
remove_old_screenshots($anvil);
# Done!
$anvil->Job->update_progress({
progress => 100,
message => "job_0281",
});
$anvil->nice_exit({exit_code => 0});
#############################################################################################################
# Functions #
#############################################################################################################
sub remove_old_screenshots
{
my ($anvil) = @_;
# Delete any screenshots over 10 minutes old.
my $maximum_age = 36000;
my $current_time = time;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:maximum_age' => $maximum_age,
's2:current_time' => $current_time,
's3:path::directories::screenshots' => $anvil->data->{path}{directories}{screenshots},
}});
$anvil->Job->update_progress({
progress => 99,
message => "job_0281",
variables => {
maximum_age => $anvil->Convert->time({'time' => $maximum_age, translate => 1}),
},
});
$anvil->Database->get_servers();
local(*DIRECTORY);
opendir(DIRECTORY, $anvil->data->{path}{directories}{screenshots});
while(my $file = readdir(DIRECTORY))
{
next if $file eq ".";
next if $file eq "..";
# "server-uuid_".$server_uuid."_timestamp-".$unix_time
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file => $file }});
if ($file =~ /^server-uuid_(.*?)_timestamp-(\d+)\./)
{
my $server_uuid = $1;
my $timestamp = $2;
my $server_name = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_name} // "";
my $file_age = $current_time - $timestamp;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
server_uuid => $server_uuid,
server_name => $server_name,
timestamp => $timestamp,
file_age => $file_age,
}});
if ($file_age > $maximum_age)
{
# Remove it.
my $full_path = $anvil->data->{path}{directories}{screenshots}."/".$file;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0809", variables => {
file => $full_path,
server_name => $server_name,
}});
unlink $full_path;
}
}
}
closedir(DIRECTORY);
return(0);
}
sub get_screenshots
{
my ($anvil, $host_uuid, $target_ip) = @_;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
host_uuid => $host_uuid,
target_ip => $target_ip,
}});
my $host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name};
my $short_host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{short_host_name};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
host_name => $host_name,
short_host_name => $short_host_name,
}});
my $connection = "";
my $uri = "qemu+ssh://".$target_ip."/system";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { uri => $uri }});
eval { $connection = Sys::Virt->new(uri => $uri); };
if ($@)
{
$anvil->Job->update_progress({
progress => $anvil->data->{job}{progress} < 99 ? ++$anvil->data->{job}{progress} : $anvil->data->{job}{progress},
message => "warning_0162",
log_level => 1,
priority => "alert",
variables => {
host_name => $short_host_name,
uri => $uri,
error => $@,
},
});
return(1);
}
my $stream = $connection->new_stream();
my @domains = $connection->list_all_domains();
foreach my $domain (@domains)
{
my $server_name = $domain->get_name;
my $server_id = $domain->get_id == -1 ? "" : $domain->get_id;
my $server_uuid = $domain->get_uuid_string;
my $server_xml = $domain->get_xml_description;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
server_name => $server_name,
server_id => $server_id,
server_uuid => $server_uuid,
}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { server_xml => $server_xml }});
my ($state, $reason) = $domain->get_state();
### States:
# 0 = no state
# 1 = running - The domain is currently running on a CPU
# 2 = blocked (idle) - the domain is blocked on resource. This can be caused because the domain is waiting on IO (a traditional wait state) or has gone to sleep because there was nothing else for it to do.
# 3 = paused - The domain has been paused, usually occurring through the administrator running virsh suspend. When in a paused state the domain will still consume allocated resources like memory, but will not be eligible for scheduling by the hypervisor.
# 4 = in shutdown - The domain is in the process of shutting down, i.e. the guest operating system has been notified and should be in the process of stopping its operations gracefully.
# 5 = shut off - The domain is not running. Usually this indicates the domain has been shut down completely, or has not been started.
# 6 = crashed - The domain has crashed, which is always a violent ending. Usually this state can only occur if the domain has been configured not to restart on crash.
# 7 = pmsuspended - The domain has been suspended by guest power management, e.g. entered into s3 state.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
'state' => $state,
reason => $reason,
}});
### Reasons are dependent on the state.
### See: https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainShutdownReason
if ($state == 1) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "unit_0041"}); } # Server is running.
elsif ($state == 2) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "unit_0042"}); } # Server is blocked (IO contention?).
elsif ($state == 3) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "unit_0043"}); } # Server is paused (migration target?).
elsif ($state == 4) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "unit_0044"}); } # Server is shutting down.
elsif ($state == 5) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "unit_0045"}); } # Server is shut off.
elsif ($state == 6) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "unit_0046"}); } # Server is crashed!
elsif ($state == 7) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "unit_0047"}); } # Server is suspended.
else { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "unit_0048", variables => { 'state' => $state }}); } # Server is in an unknown state
# Only take screenshots of running servers.
return(1) if $state != 1;
$anvil->Job->update_progress({
progress => $anvil->data->{job}{progress} < 99 ? ++$anvil->data->{job}{progress} : $anvil->data->{job}{progress},
message => "log_0805",
log_level => 2,
variables => {
server_name => $server_name,
server_uuid => $server_uuid,
host_name => $short_host_name,
},
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"path::directories::screenshots" => $anvil->data->{path}{directories}{screenshots},
}});
if (not -d $anvil->data->{path}{directories}{screenshots})
{
$anvil->Storage->make_directory({
debug => 2,
directory => $anvil->data->{path}{directories}{screenshots},
mode => "0666",
});
}
# /opt/alteeve/screenshots/<server_uuid>/<unixtime>.jpg
my $unix_time = time;
my $file_name = "server-uuid_".$server_uuid."_timestamp-".$unix_time;
my $ppm_file = $anvil->data->{path}{directories}{screenshots}."/".$file_name.".ppm";
my $mimetype = $domain->screenshot($stream, 0);
my $screenshot = "";
my $screenshot_size = 0;
my $handle_ss_chunk = sub {
my ($unused_stream, $ss_chunk, $ss_chunk_size) = @_;
$screenshot .= $ss_chunk;
$screenshot_size += $ss_chunk_size;
};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
unix_time => $unix_time,
file_name => $file_name,
ppm_file => $ppm_file,
mimetype => $mimetype,
screenshot_size => $screenshot_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $screenshot_size}).")",
}});
$stream->recv_all($handle_ss_chunk);
# Write out the screenshot.
$anvil->Storage->write_file({
debug => 2,
file => $ppm_file,
body => $screenshot,
mode => "0666",
binary => 1,
});
$anvil->Job->update_progress({
progress => $anvil->data->{job}{progress} < 99 ? ++$anvil->data->{job}{progress} : $anvil->data->{job}{progress},
message => "log_0806",
log_level => 2,
variables => { ppm_file => $ppm_file },
});
### TODO: Make these user-configurable later.
my $make_jpeg = 1;
my $make_png = 0;
my $delete_ppm = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
make_jpeg => $make_jpeg,
make_png => $make_png,
delete_ppm => $delete_ppm,
}});
# Convert to jpg
if ($make_jpeg)
{
my $jpg_file = $anvil->data->{path}{directories}{screenshots}."/".$file_name.".jpg";
my $shell_call = $anvil->data->{path}{exe}{pnmtojpeg}." ".$ppm_file." > ".$jpg_file;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
shell_call => $shell_call,
jpg_file => $jpg_file,
}});
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
$anvil->Job->update_progress({
progress => $anvil->data->{job}{progress} < 99 ? ++$anvil->data->{job}{progress} : $anvil->data->{job}{progress},
message => "log_0807",
log_level => 2,
variables => {
ppm_file => $ppm_file,
new_file => $jpg_file,
'format' => "jpeg",
},
});
}
# Convert to png
if ($make_png)
{
my $png_file = $anvil->data->{path}{directories}{screenshots}."/".$file_name.".png";
my $shell_call = $anvil->data->{path}{exe}{pamtopng}." ".$ppm_file." > ".$png_file;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
shell_call => $shell_call,
png_file => $png_file,
}});
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
$anvil->Job->update_progress({
progress => $anvil->data->{job}{progress} < 99 ? ++$anvil->data->{job}{progress} : $anvil->data->{job}{progress},
message => "log_0807",
log_level => 2,
variables => {
ppm_file => $ppm_file,
new_file => $png_file,
'format' => "png",
},
});
}
# Delete the original PPM file?
if ($delete_ppm)
{
unlink $ppm_file;
$anvil->Job->update_progress({
progress => $anvil->data->{job}{progress} < 99 ? ++$anvil->data->{job}{progress} : $anvil->data->{job}{progress},
message => "log_0589",
log_level => 2,
variables => { file => $ppm_file },
});
}
}
return(0);
}