* Created Get->network() that returns the base network address for a given IP/subnet.

* Fixed a bug in Storage->read_file() where the last newline wasn't always being faithfully recorded.
* Created System->restart_daemon (as opposed to ->reload_daemon).
* Got creating/updating dhcpd.conf / dhcpd working in tools/anvil-manage-install-target.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 6 years ago
parent 380b11ce55
commit 6f5541b6fd
  1. 1
      Anvil/Tools.pm
  2. 58
      Anvil/Tools/Get.pm
  3. 22
      Anvil/Tools/Storage.pm
  4. 44
      Anvil/Tools/System.pm
  5. 25
      html/skins/alteeve/pxe.txt
  6. 1
      rpm/SPECS/anvil.spec
  7. 10
      share/words.xml
  8. 136
      tools/anvil-manage-install-target

@ -890,6 +890,7 @@ sub _set_paths
configs => {
'anvil.conf' => "/etc/anvil/anvil.conf",
'anvil.version' => "/etc/anvil/anvil.version",
dhcpd_conf => "/etc/dhcp/dhcpd.conf",
'firewalld.conf' => "/etc/firewalld/firewalld.conf",
'httpd.conf' => "/etc/httpd/conf/httpd.conf",
'journald_anvil' => "/etc/systemd/journald.conf.d/anvil.conf",

@ -9,6 +9,7 @@ use Scalar::Util qw(weaken isweak);
use Data::Dumper;
use Encode;
use UUID::Tiny qw(:std);
use Net::Netmask;
our $VERSION = "3.0.0";
my $THIS_FILE = "Get.pm";
@ -19,6 +20,7 @@ my $THIS_FILE = "Get.pm";
# date_and_time
# host_uuid
# md5sum
# network
# network_details
# switches
# users_home
@ -618,6 +620,62 @@ sub md5sum
return($sum);
}
=head2 network
This takes an IP address and subnet and returns the network it belongs too. For example;
my $network = $anvil->Get->network({ip => "10.2.4.1", subnet => "255.255.0.0"});
This would set C<< $network >> to C<< 10.2.0.0 >>.
If the network can't be caluclated for any reason, and empty string will be returned.
Parameters;
=head3 ip (required)
This is the IPv4 IP address being calculated.
=head3 subnet (optional)
This is the subnet of the IP address being calculated.
=cut
sub network
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $network = "";
my $ip = defined $parameter->{ip} ? $parameter->{ip} : "";
my $subnet = defined $parameter->{subnet} ? $parameter->{subnet} : "";
if (not $ip)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Get->network()", parameter => "ip" }});
}
if (not $subnet)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Get->network()", parameter => "subnet" }});
}
my $block = Net::Netmask->new($ip."/".$subnet);
my $base = $block->base();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { base => $base }});
if ($anvil->Validate->is_ipv4({ip => $base}))
{
$network = $base;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { network => $network }});
}
return($network);
}
### TODO: Is this a waste of time / duplicate of System->get_ips()?
=head2 network_details
This method returns the local hostname and IP addresses.

@ -299,7 +299,8 @@ fi";
}
else
{
die;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0234", variables => { source => $source_file, destination => $target_file }});
$target_file = "";
}
}
@ -1308,13 +1309,12 @@ sub read_file
open (my $file_handle, "<", $shell_call) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0015", variables => { shell_call => $shell_call, error => $! }});
while(<$file_handle>)
{
chomp;
### NOTE: Don't chop this, we want to record exactly what we read
my $line = $_;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0023", variables => { line => $line }});
$body .= $line."\n";
$body .= $line;
}
close $file_handle;
$body =~ s/\n$//s;
if ($cache)
{
@ -1359,19 +1359,18 @@ sub read_file
}
else
{
# Read from disk.
# Read from storage.
my $shell_call = $file;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0012", variables => { shell_call => $shell_call }});
open (my $file_handle, "<", $shell_call) or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0015", variables => { shell_call => $shell_call, error => $! }});
while(<$file_handle>)
{
chomp;
### NOTE: Don't chop this, we want to record exactly what we read
my $line = $_;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0023", variables => { line => $line }});
$body .= $line."\n";
$body .= $line;
}
close $file_handle;
$body =~ s/\n$//s;
if ($cache)
{
@ -1977,7 +1976,7 @@ sub update_config
This reads in a file (if it already exists), compares it against a new body and updates it if there is a difference. This can work on remote files as well as local ones.
C<< 0 >> is returns on success (either the new file was written or the old file was not changed). Any problem will cause C<< 1 >> to be returned.
The return code indicates success; C<< 0 >> is returns if anything goes wrong. C<< 1 >> is returned if the file was updated and C<< 2 >> is returned if the file did not need to be updated.
Parameters;
@ -2096,6 +2095,7 @@ sub update_file
{
# Update not needed.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0231", variables => { file => $file }});
return(2);
}
# Update/write?
@ -2121,11 +2121,11 @@ sub update_file
{
# Something went wrong.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0233", variables => { file => $file, 'return' => $return }});
return(1);
return(0);
}
}
return(0);
return(1);
}
=head2 write_file

@ -35,6 +35,7 @@ my $THIS_FILE = "System.pm";
# read_ssh_config
# reload_daemon
# reboot_needed
# restart_daemon
# start_daemon
# stop_daemon
# stty_echo
@ -2034,6 +2035,49 @@ sub reboot_needed
return($reboot_needed);
}
=head2 restart_daemon
This method restarts a daemon (typically to pick up a change in configuration). The return code from the start request will be returned.
If the return code for the restart command wasn't read, C<< !!error!! >> is returned. If it did restart, C<< 0 >> is returned. If the restart failed, a non-0 return code will be returned.
Parameters;
=head3 daemon (required)
This is the name of the daemon to restart.
=cut
sub restart_daemon
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $return = undef;
my $daemon = defined $parameter->{daemon} ? $parameter->{daemon} : "";
my $say_daemon = $daemon =~ /\.service$/ ? $daemon : $daemon.".service";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { daemon => $daemon, say_daemon => $say_daemon }});
my $shell_call = $anvil->data->{path}{exe}{systemctl}." restart ".$say_daemon."; ".$anvil->data->{path}{exe}{'echo'}." return_code:\$?";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
my $output = $anvil->System->call({shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { output => $output }});
foreach my $line (split/\n/, $output)
{
if ($line =~ /return_code:(\d+)/)
{
$return = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'return' => $return }});
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'return' => $return }});
return($return);
}
=head2 start_daemon
This method starts a daemon. The return code from the start request will be returned.

@ -1,3 +1,28 @@
<!-- start dhcpd_conf -->
### Global options
option domain-name "#!variable!domain!#";
option domain-name-servers #!variable!dns!#;
authoritative;
ddns-update-style none;
# refer to RFC4758 for possible arch option values
option arch code 93 = unsigned integer 16;
subnet #!variable!network!# netmask 255.255.0.0 {
default-lease-time 600;
max-lease-time 1200;
range #!variable!range!#;
option routers #!variable!router!#;
if option arch = 00:07 {
filename "uefi/shim.efi";
} else {
filename "pxelinux.0";
}
next-server #!variable!router!#;
}
<!-- end dhcpd_conf -->
<!-- start tftp_bios -->
# Tradional BIOS based PXE menu
#

@ -44,6 +44,7 @@ Requires: perl-HTML-Strip
Requires: perl-JSON
Requires: perl-Log-Journald
Requires: perl-Net-SSH2
Requires: perl-Net-Netmask
Requires: perl-NetAddr-IP
Requires: perl-Proc-Simple
Requires: perl-Sys-Syslog

@ -189,9 +189,9 @@ NOTE: Please be patient!
</key>
<!-- \-----------------------------------------------------------------------------------------------------------/ -->
<key name="message_0094">Boot from Next ^Boot Device</key>
<key name="message_0095"></key>
<key name="message_0096"></key>
<key name="message_0097"></key>
<key name="message_0095">Restarting: [#!variable!daemon!#] after updating the file: [#!variable!file!#].</key>
<key name="message_0096">The file: [#!variable!file!#] did not need to be updated.</key>
<key name="message_0097">The file: [#!variable!file!#] was updated.</key>
<key name="message_0098"></key>
<key name="message_0099"></key>
@ -467,6 +467,7 @@ The body of the file: [#!variable!file!#] does not match the new body. The file
<key name="log_0231">The file: [#!variable!file!#] is already the same as the passed in body, so no update is needed.</key>
<key name="log_0232">The file: [#!variable!file!#] will now be updated.</key>
<key name="log_0233">There was a problem updating file: [#!variable!file!#], expected the write to return '0' but got: [#!variable!return!#]. Please check the logs for details.</key>
<key name="log_0234">Failed to backup the file: [#!variable!source!#] to: [#!variable!destination!#]. Details may be found in the logs above.</key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key>
@ -685,8 +686,9 @@ The update appears to have not completed successfully. The output was:
<key name="error_0039"><![CDATA[The password for the host was not provided by '--password-file' or in 'job_data' (as 'password=<secret>').]]></key>
<key name="error_0040">Logging out failed. The user's UUID wasn't passed and 'sys::users::user_uuid' wasn't set. Was the user already logged out?</key>
<key name="error_0041">Failed to install the Alteeve repo, unable to proceed.</key>
<key name="error_0042">No BCN IP address found. Unable to configure the install target feature yet.</key>
<key name="error_0042">No BCN interface found. Unable to configure the install target feature yet.</key>
<key name="error_0043">Failed to write or update the file: [#!variable!file!#]. Please see the system log for more information.</key>
<key name="error_0044">This is not a configured Striker dashboard, exiting.</key>
<!-- These are units, words and so on used when displaying information. -->
<key name="unit_0001">Yes</key>

@ -15,6 +15,7 @@
# 1 = Alteeve repo not installed and not installable.
# 2 = BCN IP not found, unable to configure yet.
# 3 = Failed to write or update a configuration file.
# 4 = Not a striker dashboard (or isn't yet).
#
# TODO:
# - Support building the install target by mounting the ISO and checking /mnt/shared/incoming for needed
@ -25,6 +26,7 @@
use strict;
use warnings;
use Anvil::Tools;
use Data::Dumper;
# Disable buffering
$| = 1;
@ -45,16 +47,23 @@ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, secure =
$anvil->data->{switches}{'y'} = "";
$anvil->Get->switches;
my ($os_type, $os_arch) = $anvil->System->get_os_type({debug => 2});
my ($os_type, $os_arch) = $anvil->System->get_os_type();
$anvil->data->{host_os}{os_type} = $os_type;
$anvil->data->{host_os}{os_arch} = $os_arch;
$anvil->data->{host_os}{version} = ($os_type =~ /(\d+)$/)[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"host_os::os_type" => $anvil->data->{host_os}{os_type},
"host_os::os_arch" => $anvil->data->{host_os}{os_arch},
"host_os::version" => $anvil->data->{host_os}{version},
}});
# If this isn't a Striker dashboard, exit.
if ($anvil->_short_hostname !~ /striker/)
{
print $anvil->Words->string({key => "error_0044"})."\n";
$anvil->nice_exit({code => 4});
}
# load the list of packages;
load_packages($anvil);
@ -80,50 +89,133 @@ sub setup_boot_environment
my ($anvil) = @_;
# Get my BCN IP and active OS.
my $network = $anvil->Get->network_details();
my $bcn_ip = "";
foreach my $in_interface (sort {$a cmp $b} keys %{$network->{interface}})
$anvil->System->get_ips();
my $bcn_interface = "";
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{sys}{network}{interface}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_interface => $in_interface }});
next if $in_interface !~ /^bcn/;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { interface => $interface }});
next if $interface !~ /^bcn/;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "interface::${in_interface}::ip" => $network->{interface}{$in_interface}{ip} }});
if ($anvil->Validate->is_ipv4({ip => $network->{interface}{$in_interface}{ip} }))
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sys::network::${interface}::ip" => $anvil->data->{sys}{network}{interface}{$interface}{ip} }});
if ($anvil->Validate->is_ipv4({ip => $anvil->data->{sys}{network}{interface}{$interface}{ip} }))
{
$bcn_ip = $network->{interface}{$in_interface}{ip};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bcn_ip => $bcn_ip }});
$bcn_interface = $interface;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bcn_interface => $bcn_interface }});
}
last if $bcn_ip;
last if $bcn_interface;
}
if (not $bcn_ip)
if (not $bcn_interface)
{
# Can't set this up yet.
print $anvil->Words->string({key => "error_0042"})."\n";
$anvil->nice_exit({code => 2});
}
# Build and update the PXE BIOS 'default' menu.
my $base_url = "http://".$bcn_ip."/".$anvil->data->{host_os}{os_type}."/".$anvil->data->{host_os}{os_arch};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { base_url => $base_url }});
my $bcn_ip = $anvil->data->{sys}{network}{interface}{$bcn_interface}{ip};
my $bcn_subnet = $anvil->data->{sys}{network}{interface}{$bcn_interface}{subnet};
my $bcn_network = $anvil->Get->network({ip => $bcn_ip, subnet => $bcn_subnet});
my $dns = $anvil->data->{sys}{network}{interface}{$bcn_interface}{dns} ? $anvil->data->{sys}{network}{interface}{$bcn_interface}{dns} : $anvil->data->{defaults}{network}{dns};
my $domain = "localdomain";
my $base_url = "http://".$bcn_ip."/".$anvil->data->{host_os}{os_type}."/".$anvil->data->{host_os}{os_arch};
if ($anvil->_hostname =~ /\./)
{
$domain = $anvil->_hostname;
$domain =~ s/^.*?\.//;
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
bcn_interface => $bcn_interface,
bcn_ip => $bcn_ip,
bcn_subnet => $bcn_subnet,
bcn_network => $bcn_network,
dns => $dns,
domain => $domain,
base_url => $base_url,
}});
### NOTE: The DNS range is a bit tricky, so for now, we'll assume that the BCN is always a /16
### network. Someday this might change, and if so, we'll need to make this a lot smarter.
my $striker_number = ($anvil->_short_hostname =~ /striker(\d+)/)[0];
$striker_number = 1 if not $striker_number;
$striker_number =~ s/^0//;
my $third_octet = (10 * $striker_number) + 4;
$third_octet = 254 if $third_octet > 254;
my $first_part = ($bcn_network =~ /^(\d+\.\d+)\./)[0].".".$third_octet;
my $range = $first_part.".10 ".$first_part.".250";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:striker_number' => $striker_number,
's2:third_octet' => $third_octet,
's3:first_part' => $first_part,
's4:range' => $range,
}});
### DHCP server config
my $dhcpd_conf_body = $anvil->Template->get({file => "pxe.txt", show_name => 0, name => "dhcpd_conf", variables => {
dns => $dns,
domain => $domain,
network => $bcn_network,
range => $range,
router => $bcn_ip,
}});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { dhcpd_conf_body => $dhcpd_conf_body }});
# Return code if '1' means the file was changed, '2' indicates it didn't change. '0' means something
# went wrong.
my $dhcpd_conf_success = $anvil->Storage->update_file({
body => $dhcpd_conf_body,
file => $anvil->data->{path}{configs}{dhcpd_conf},
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { dhcpd_conf_success => $dhcpd_conf_success }});
if (not $dhcpd_conf_success)
{
# Failed.
print $anvil->Words->string({key => "error_0043", variables => { file => $anvil->data->{path}{configs}{dhcpd_conf} }})."\n";
$anvil->nice_exit({code => 3});
}
elsif ($dhcpd_conf_success eq "1")
{
# Restart dhcpd.
print $anvil->Words->string({key => "message_0095", variables => {
daemon => "dhcpd",
file => $anvil->data->{path}{configs}{dhcpd_conf},
}})."\n";
$anvil->System->restart_daemon({daemon => "dhcpd"});
}
elsif ($dhcpd_conf_success eq "2")
{
# Update not needed.
print $anvil->Words->string({key => "message_0096", variables => { file => $anvil->data->{path}{configs}{dhcpd_conf} }})."\n";
}
### PXE BIOD 'default' file.
# Setup the BIOS boot menu.
my $bios_default_body = $anvil->Template->get({file => "pxe.txt", show_name => 0, name => "tftp_bios", variables => { base_url => $base_url }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bios_default_body => $bios_default_body }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { bios_default_body => $bios_default_body }});
# Read in the existing default file, if it exists, and compare it to the one we just generated.
my $bios_default_return_code = $anvil->Storage->update_file({
debug => 3,
# Return code if '1' means the file was changed, '2' indicates it didn't change. '0' means something
# went wrong.
my $bios_default_success = $anvil->Storage->update_file({
body => $bios_default_body,
file => $anvil->data->{path}{configs}{pxe_default},
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bios_default_body => $bios_default_body }});
if ($bios_default_return_code)
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { bios_default_success => $bios_default_success }});
if (not $bios_default_success)
{
# Failed.
print $anvil->Words->string({key => "error_0043", variables => { file => $anvil->data->{path}{configs}{pxe_default} }})."\n";
$anvil->nice_exit({code => 3});
}
elsif ($dhcpd_conf_success eq "1")
{
# Updated
print $anvil->Words->string({key => "message_0096", variables => { file => $anvil->data->{path}{configs}{pxe_default} }})."\n";
}
elsif ($dhcpd_conf_success eq "2")
{
# Update not needed.
print $anvil->Words->string({key => "message_0097", variables => { file => $anvil->data->{path}{configs}{pxe_default} }})."\n";
}
die;

Loading…
Cancel
Save