diff --git a/Anvil/Tools.pm b/Anvil/Tools.pm index 7c0f0990..d9d3747e 100755 --- a/Anvil/Tools.pm +++ b/Anvil/Tools.pm @@ -846,6 +846,12 @@ sub _set_defaults server => "", tag => "anvil", }, + # NOTE: These are here to allow foreign users to override western defaults in anvil.conf. + kickstart => { + keyboard => "--vckeymap=us --xlayouts='us'", + password => "Initial1", + timezone => "Etc/GMT --isUtc", + }, ## Network stuff... The second octet auto-increments to handle N-number of netowrks. As such, ## we need to use a wider spread between the BCNs, SNs and IFNs than we had ## in v2. diff --git a/Anvil/Tools/Storage.pm b/Anvil/Tools/Storage.pm index bc9646b7..7cd74e63 100755 --- a/Anvil/Tools/Storage.pm +++ b/Anvil/Tools/Storage.pm @@ -383,7 +383,7 @@ sub change_mode This changes the owner and/or group of a file or directory. - $anvil->Storage->change_owner({target => "/tmp/foo", mode => "0644"}); + $anvil->Storage->change_owner({target => "/tmp/foo", user => "apache", group => "apache" }); If it fails to write the file, an alert will be logged and 'C<< 1 >>' will be returned. Otherwise, 'C<< 0 >>' will be returned. diff --git a/anvil.conf b/anvil.conf index cf128905..75c053cd 100644 --- a/anvil.conf +++ b/anvil.conf @@ -134,3 +134,22 @@ #defaults::template::html = alteeve +### Install Target options +# Note; Please see 'pxe.txt' for editable templates for 'dhcpd.conf', (tftpboot's BIOS) 'default' and the +# kickstart templates. +# +# This section allows for adapting certain installations of systems via the Install Target feature. +# Generally, these don't need to be edited. +# +# This controls the keyboard configuration. See: +# - https://docs.fedoraproject.org/en-US/fedora/f28/install-guide/appendixes/Kickstart_Syntax_Reference/#sect-kickstart-commands-keyboard +#kickstart::keyboard = --vckeymap=us --xlayouts='us' +# +# This sets the default password of newly stage-1 built machines. Generally, this shouldn't be change. It is +# recorded in plain text and it is used in the stage-2 configuration tools. +#kickstart::password = Initial1 +# +# This is the system timezone to be set. Generally, it's recommended to leave the Anvil! machines to UTC, but +# you might want to change this is if you spend time working directly on the various Anvil! cluster machines. +#kickstart::timezone = Etc/GMT --isUtc + diff --git a/html/skins/alteeve/images/bios-splash.jpg b/html/skins/alteeve/images/bios-splash.jpg new file mode 100644 index 00000000..4046b5ba Binary files /dev/null and b/html/skins/alteeve/images/bios-splash.jpg differ diff --git a/html/skins/alteeve/pxe.txt b/html/skins/alteeve/pxe.txt index 06aecca8..9c1a8112 100644 --- a/html/skins/alteeve/pxe.txt +++ b/html/skins/alteeve/pxe.txt @@ -23,6 +23,405 @@ subnet #!variable!network!# netmask 255.255.0.0 { } + + +### Alteeve's Niche! Inc. - Anvil! Intelligent Availability(tm) Platform +# License: GPLv2 +# Built: #!variable!date!# +# Target: Network Install (PXE) +# OS: #!variable!os!# +# Machine: #!variable!say_type!# +# NOTE: Do no use any non-ASCII characters in this kickstart script. + +# Install using text screens, most compatible with modest hardware, too. +text + +### TODO: Might want to remove this for Striker. +# Agree to the eula +eula --agreed + +# Don't run the "firstboot" tool, we should have everything configured. +firstboot --disable + +# Reboot when the install is done. +reboot + +# Install from the source Striker +url --url=#!variable!url!# + +### TODO: These should be configurable eventually. +# Keyboard layouts +keyboard #!variable!keyboard!# +timezone #!variable!timezone!# + +# NOTE: DON'T CHANGE THIS WITHOUT TESTING! The Anvil! code makes system calls and parses output. Changing +# this could alter what the program can parse. The language of the Anvil! itself (and this most things +# users will see) is controlled in /etc/anvil/anvil.conf +lang en_CA.UTF-8 + +# Network information +network --hostname=#!variable!hostname!# +# Root and admin passwords are '#!variable!password!#' +rootpw --plaintext #!variable!password!# +user --name=admin --password "#!variable!password!#" --plaintext --gecos "admin" --groups wheel + +# TEMPORARY: Set selinux to permissive +#selinux --permissive + +# Partitioning plan is generated by the %pre script. +%include /tmp/plan_partitions.out + +%packages +@^server-product-environment +@server-hardware-support + +%end + +%addon com_redhat_kdump --disable --reserve-mb='128' + +%end + +%anaconda +pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty +pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok +pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty +%end + + + +############################################################################################################# +# %ost, --nochroot scripts # +############################################################################################################# + +### TODO: Might want to remove this pre-production. Useful for debugging until then. +# Record all the install logs for future reference. +%post --nochroot +echo 'Copying all the anaconda related log files to /root/install/' + +mkdir -p /mnt/sysimage/root/install_logs/var +mkdir -p /mnt/sysimage/root/install_logs/run +rsync -av /tmp /mnt/sysimage/root/install_logs/ +rsync -av /run/install /mnt/sysimage/root/install_logs/run/ +rsync -av /var/log /mnt/sysimage/root/install_logs/var/ +%end + + +############################################################################################################# +# %pre scripts # +############################################################################################################# + +### This is the small anaconda-friendly perl program that looks at the +### available storage and chooses a drive to install on. Then it generates +### the kickstart partition instructions and records them in: +### /tmp/plan_partitions.out + +### NOTE: This must be copied from 'scripts/plan_partitions', don't edit directly *EXCEPT* to set the '$type' +### just below, which *MUST* be set, or the script will fail. +%pre --interpreter /bin/perl +#!/bin/perl +# +# This script is designed to identify hard drives and decide where and how to partition it for installation +# during a kickstart install. +# +# Exit codes; +# 0 - Success +# 1 - Target type not specified. +# 2 - Failed to find a drive to install on. +# +# NOTE: This is restricted to what is available during an anaconda install session. That is to same, bare +# minimum. +# TODO: If multiple matching drives are found (same medium and size, build an appropriate RAID array. +# TODO: in pre, wipefs on all disks to clear old LVM and DRBD data +# + +use strict; +use warnings; + +# Set to '1' for verbose output +my $debug = #!variable!debug!#; + +### NOTE: This must be set to 'striker', 'node' or 'dr' when incorporated into a kickstart %pre script! +my $type = "#!variable!type!#"; +if ($type =~ /striker/i) +{ + print "#!string!message_0103!#\n"; + $type = "striker"; +} +elsif ($type =~ /node/i) +{ + print "#!string!message_0104!#\n"; + $type = "node"; +} +elsif ($type =~ /dr/i) +{ + print "#!string!message_0105!#\n"; + $type = "dr"; +} +else +{ + print "#!string!message_0106!#\n"; + exit(1); +} + +my $device = {}; + +# We might want to add HCTL (Host:Channel:Target:Lun for SCSI) and/or SUBSYSTEMS later +my $target = ""; +my $lsblk = system_call("/bin/lsblk --bytes --paths --pairs --output NAME,RM,HOTPLUG,TYPE,SIZE,TRAN,ROTA"); +foreach my $line (split/\n/, $lsblk) +{ + ### NOTE: If a drive has no transport, is not removable, but is hotplugable and the device path is + ### mmcblk0, it is probably an SDCard. It doesn't seem to be a directly divinable state. We + ### don't currently plan to use them, but it might come to pass later. + print __LINE__."; [ Debug ] - lsblk: [".$line."]\n" if $debug; + my ($path, $removable, $hotplug, $type, $size, $transport, $rotational) = ($line =~ /NAME="(.*?)" RM="(\d)" HOTPLUG="(\d)" TYPE="(.*?)" SIZE="(\d+)" TRAN="(.*?)" ROTA="(\d)"/); + print __LINE__."; [ Debug ] - Device: [".$path."], type: [".$type."], remvoable? [".$removable."], hotplug? [".$hotplug."], rotational? [".$rotational."], transport: [".$transport."], size: [".$size."]\n" if $debug; + # Skip 'zramX' devices + next if ($path =~ /^\/dev\/zram\d/); + # Skip removable disks and anything that just isn't a disk at all. + next if (($removable) or ($hotplug) or ($type ne "disk")); + $device->{$path} = { + type => $type, + size => $size, + transport => $transport, + rotational => $rotational, + }; + if ($device->{$path}{rotational}) + { + print "#!string!message_0107!#\n"; + } + else + { + print "#!string!message_0108!#\n"; + } +} + +### Usage selection priority +# on Striker, we'll simply use whatever is the biggest avalable drive. +# on Node and DR, we'll prefer slowest first (rotational, sata before nvme/scsi), and smallest second. +my $use_drive = ""; +if ($type eq "striker") +{ + my $biggest_size = 0; + foreach my $path (sort {$a cmp $b} keys %{$device}) + { + print __LINE__."; [ Debug ] - path: [".$path."], ${path}::size: [".$device->{$path}{size}." (".hr_size($device->{$path}{size}).")] < biggest_size: [".$biggest_size." (".hr_size($biggest_size).")]\n" if $debug; + if ($device->{$path}{size} > $biggest_size) + { + $biggest_size = $device->{$path}{size}; + $use_drive = $path; + print __LINE__."; [ Debug ] - use_drive: [".$use_drive."], biggest_size: [".$biggest_size." (".hr_size($biggest_size).")]\n" if $debug; + } + } + if ($use_drive) + { + print "#!string!message_0109!#\n"; + } +} +else +{ + # Node and DR are handled the same + my $first_disk_seen = 0; + my $smallest_size = 0; + my $selected_is_platter = 0; + foreach my $path (sort {$a cmp $b} keys %{$device}) + { + print __LINE__."; [ Debug ] - first_disk_seen: [".$first_disk_seen."], path: [".$path."], ${path}::rotational: [".$device->{$path}{rotational}."]\n" if $debug; + if (not $first_disk_seen) + { + # Select this one + $first_disk_seen = 1; + $use_drive = $path; + $smallest_size = $device->{$path}{size}; + $selected_is_platter = $device->{$path}{rotational}; + print __LINE__."; [ Debug ] - first_disk_seen: [".$first_disk_seen."], use_drive: [".$use_drive."], selected_is_platter: [".$selected_is_platter."], smallest_size: [".$smallest_size." (".hr_size($smallest_size).")]\n" if $debug; + } + elsif ($device->{$path}{rotational}) + { + # This takes priority + print __LINE__."; [ Debug ] - selected_is_platter: [".$selected_is_platter."]\n" if $debug; + if ($selected_is_platter) + { + # Was the previously seen drive bigger? + print __LINE__."; [ Debug ] - ".$path."::size: [".$first_disk_seen." (".hr_size($first_disk_seen).")], smallest_size: [".$smallest_size." (".hr_size($smallest_size).")]\n" if $debug; + if ($device->{$path}{size} < $smallest_size) + { + # This is smaller, use it. + $use_drive = $path; + $smallest_size = $device->{$path}{size}; + print __LINE__."; [ Debug ] - use_drive: [".$use_drive."], smallest_size: [".$smallest_size." (".hr_size($smallest_size).")]\n" if $debug; + } + } + else + { + # The previous drive is an SSD, so use this one regardless + $use_drive = $path; + $smallest_size = $device->{$path}{size}; + $selected_is_platter = $device->{$path}{rotational}; + print __LINE__."; [ Debug ] - use_drive: [".$use_drive."], selected_is_platter: [".$selected_is_platter."], smallest_size: [".$smallest_size." (".hr_size($smallest_size).")]\n" if $debug; + } + } + elsif (not $selected_is_platter) + { + # This is an SSD, but we haven't seen a platter drive yet, so use it if it is + # smaller. + print __LINE__."; [ Debug ] - ".$path."::size: [".$first_disk_seen." (".hr_size($first_disk_seen).")], smallest_size: [".$smallest_size." (".hr_size($smallest_size).")]\n" if $debug; + if ($device->{$path}{size} < $smallest_size) + { + # This is smaller, use it. + $use_drive = $path; + $smallest_size = $device->{$path}{size}; + print __LINE__."; [ Debug ] - use_drive: [".$use_drive."], smallest_size: [".$smallest_size." (".hr_size($smallest_size).")]\n" if $debug; + } + } + } + + # Did we find a drive? + if ($use_drive) + { + if ($selected_is_platter) + { + print "#!string!message_0110!#\n"; + } + else + { + print "#!string!message_0111!#\n"; + } + } +} + +# Did we find a disk to use? +if (not $use_drive) +{ + print "#!string!message_0112!#\n"; + exit(2); +} + +# Pick up a bit of a UUID to add to the volume group name. +my $id = time; +if ((-e "/sys/class/dmi/id/product_uuid") && (-r "/sys/class/dmi/id/product_uuid")) +{ + # We should be able to read the system UUID. If so, we'll take the starting part of the string for + # the short ID. + my $uuid = ""; + my $shell_call = "/sys/class/dmi/id/product_uuid"; + print __LINE__."; [ Debug ] - shell_call: [".$shell_call."]\n" if $debug; + open (my $file_handle, "<", $shell_call) or die "Failed to read: [".$shell_call."], the error was: ".$!."\n"; + while(<$file_handle>) + { + chomp; + $uuid = $_; + print __LINE__."; [ Debug ] - uuid: [".$uuid."]\n" if $debug; + } + close $file_handle; + + if ($uuid =~ /^(\w+)-/) + { + $id = $1; + print __LINE__."; [ Debug ] - id: [".$id."]\n" if $debug; + } +} + +# Finally, we've got our output. +my $say_grow = $type eq "striker" ? "--grow " : ""; +my $vg_name = $type."_".$id; +my $partition_file = "/tmp/plan_partitions.out"; +my $partition_body = "zerombr +clearpart --all --drives=".$use_drive." +ignoredisk --only-use=".$use_drive." +bootloader --location=mbr --driveorder=".$use_drive." --boot-drive=".$use_drive." + +# Partitions +part biosboot --fstype=biosboot --size=1 +part /boot --fstype=ext4 --size=1024 --asprimary --ondisk=".$use_drive." +part pv.01 --fstype=lvmpv --size=100 --asprimary --ondisk=".$use_drive." --grow + +# LVM Volume groups +volgroup ".$vg_name." --pesize=4096 pv.01 + +# LVM logical volumes +logvol swap --fstype=swap --size=8188 --name=lv_swap --vgname=".$vg_name." +logvol / --fstype=xfs --size=40960 --name=lv_root --vgname=".$vg_name." ".$say_grow." +"; +print __LINE__."; [ Debug ] - partition_body: [".$partition_body."]\n" if $debug; +print "#!string!message_0113!#\n"; + +# Write it to the temp file that the kickstart's %include will look for. +my $shell_call = $partition_file; +print __LINE__."; [ Debug ] - shell_call: [".$shell_call."]\n" if $debug; +open (my $file_handle, ">", $shell_call) or die "Failed to write: [".$shell_call."], the error was: ".$!."\n"; +print $file_handle $partition_body; +close $file_handle; +print "#!string!message_0114!#\n"; + +# We're done. +exit(0); + + +### Functions +# Make the size easier to read for users +sub hr_size +{ + my ($size) = @_; + + my $hr_size = $size; + + if ($size < 1023) + { + # Bytes + $hr_size .= " B"; + } + elsif ($size < (2 ** 20)) + { + # Kibibyte + $hr_size = sprintf("%.1f", ($size /= (2 ** 10)))." KiB"; + } + elsif ($size < (2 ** 30)) + { + # Mebibyte + $hr_size = sprintf("%.2f", ($size /= (2 ** 20)))." MiB"; + } + elsif ($size < (2 ** 40)) + { + # Gibibyte + $hr_size = sprintf("%.2f", ($size /= (2 ** 30)))." GiB"; + } + elsif ($size < (2 ** 50)) + { + # Tebibyte + $hr_size = sprintf("%.2f", ($size /= (2 ** 40)))." TiB"; + } + else + { + # Pebibyte or higher + $hr_size = sprintf("%.3f", ($size /= (2 ** 40)))." PiB"; + } + + return($hr_size); +} + +sub system_call +{ + my ($command) = @_; + my $output = ""; + open (my $file_handle, $command." 2>&1 |") or die "Failed to call: [".$command."], error was: [".$!."]\n"; + while (<$file_handle>) + { + chomp; + my $line = $_; + $line =~ s/\n$//; + $line =~ s/\r$//; + $output .= $line."\n"; + } + close $file_handle; + $output =~ s/\n$//s; + + return($output); +} +%end + + + # Tradional BIOS based PXE menu # @@ -89,7 +488,8 @@ TEXT HELP #!string!message_0085!# ENDTEXT kernel fedora28/vmlinuz -append initrd=fedora28/initrd.img root=live:#!variable!base_url!#/os/images/install.img inst.stage2=#!variable!base_url!#/os/ ip=dhcp inst.ks=#!variable!base_url!#/kickstart/striker.ks inst.sshd rd.debug +# NOTE: add ' rd.debug' below for debugging +append initrd=fedora28/initrd.img root=live:#!variable!base_url!#/os/images/install.img inst.stage2=#!variable!base_url!#/os/ ip=dhcp inst.ks=#!variable!base_url!#/kickstart/striker.ks inst.sshd label node menu label #!string!message_0086!# @@ -97,7 +497,7 @@ TEXT HELP #!string!message_0087!# ENDTEXT kernel fedora28/vmlinuz -append initrd=fedora28/initrd.img root=live:#!variable!base_url!#/os/images/install.img inst.stage2=#!variable!base_url!#/os/ ip=dhcp inst.ks=#!variable!base_url!#/kickstart/node.ks inst.sshd rd.debug +append initrd=fedora28/initrd.img root=live:#!variable!base_url!#/os/images/install.img inst.stage2=#!variable!base_url!#/os/ ip=dhcp inst.ks=#!variable!base_url!#/kickstart/node.ks inst.sshd label node menu label #!string!message_0088!# @@ -105,7 +505,7 @@ TEXT HELP #!string!message_0089!# ENDTEXT kernel fedora28/vmlinuz -append initrd=fedora28/initrd.img root=live:#!variable!base_url!#/os/images/install.img inst.stage2=#!variable!base_url!#/os/ ip=dhcp inst.ks=#!variable!base_url!#/kickstart/dr.ks inst.sshd rd.debug +append initrd=fedora28/initrd.img root=live:#!variable!base_url!#/os/images/install.img inst.stage2=#!variable!base_url!#/os/ ip=dhcp inst.ks=#!variable!base_url!#/kickstart/dr.ks inst.sshd label rescue menu label #!string!message_0090!# diff --git a/notes b/notes index 676cde12..19f6febd 100644 --- a/notes +++ b/notes @@ -1,3 +1,5 @@ +NEXT; - Copy os/isolinux/* stuff from syslinux-nonlinux + dnf -y install perl-Net-Netmask perl-Text-Diff dnf-utils hdparm lsscsi createrepo dhcp kernel-core syslinux tftp-server Firewall config stuff. diff --git a/share/words.xml b/share/words.xml index a8536a3f..32c4254f 100644 --- a/share/words.xml +++ b/share/words.xml @@ -197,7 +197,32 @@ NOTE: Please be patient! Copying the syslinux files: [#!data!path::directories::syslinux!#/*] into the tftpboot directory: [#!data!path::directories::tftpboot!#]. The syslinux files from: [#!data!path::directories::syslinux!#] appear to already be in the tftpboot directory: [#!data!path::directories::tftpboot!#], skipping. Checking that the "Install Target" function is configured and updated. - + + Finding install drive for a Striker dashboard. + Finding install drive for an Anvil! node. + Finding install drive for a DR (disaster recovery) host. + +[ Error ] - Target type not specified. Be sure that '\$type' is set to + 'striker', 'node' or 'dr' in the \%pre section of the kickstart + script. + + + {$path}{transport}."], of the size: [".$device->{$path}{size}." (".hr_size($device->{$path}{size}).")]]]> + {$path}{transport}."], of the size: [".$device->{$path}{size}." (".hr_size($device->{$path}{size}).")]]]> + {$use_drive}{size})."]]]> + {$use_drive}{size})."]]]> + {$use_drive}{size})."] (no platter drives found)]]> + + + + + Striker Dashboard + Anvil! Node + Dsaster Recovery (DR) Host + + + + Starting: [#!variable!program!#]. diff --git a/tools/anvil-manage-install-target b/tools/anvil-manage-install-target index 72c8309e..92a72a8a 100755 --- a/tools/anvil-manage-install-target +++ b/tools/anvil-manage-install-target @@ -16,6 +16,7 @@ # 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). +# 5 = Not running as the 'root' user. # # TODO: # - Support building the install target by mounting the ISO and checking /mnt/shared/incoming for needed @@ -47,6 +48,15 @@ $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, secure = $anvil->data->{switches}{'y'} = ""; $anvil->Get->switches; +# Make sure we're running as 'root' +# $< == real UID, $> == effective UID +if (($< != 0) && ($> != 0)) +{ + # Not root + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "error_0005"}); + $anvil->nice_exit({code => 5}); +} + 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; @@ -191,8 +201,7 @@ sub setup_boot_environment } - ### PXE BIOD 'default' file. - # Setup the BIOS boot menu. + ### PXE BIOS 'default' file. 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 => 3, list => { bios_default_body => $bios_default_body }}); @@ -220,6 +229,62 @@ sub setup_boot_environment print $anvil->Words->string({key => "message_0096", variables => { file => $anvil->data->{path}{configs}{pxe_default} }})."\n"; } + ### Generate kickstart files. + foreach my $type ("striker", "node", "dr") + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); + my $say_type = "#!string!message_0115!#"; + if ($type eq "node") + { + $say_type = "#!string!message_0116!#"; + } + elsif ($type eq "dr") + { + $say_type = "#!string!message_0117!#"; + } + my $kickstart_body = $anvil->Template->get({file => "pxe.txt", show_name => 0, name => "kickstart", variables => { + type => $type, + say_type => $say_type, + hostname => "new-".$type.".".$domain, + date => $anvil->Get->date_and_time({date_only => 1}), + os => $anvil->data->{host_os}{os_type}, + url => $base_url."/os/", + keyboard => $anvil->data->{kickstart}{keyboard} ? $anvil->data->{kickstart}{keyboard} : $anvil->data->{defaults}{kickstart}{keyboard}, + timezone => $anvil->data->{kickstart}{timezone} ? $anvil->data->{kickstart}{timezone} : $anvil->data->{defaults}{kickstart}{timezone}, + password => $anvil->data->{kickstart}{password} ? $anvil->data->{kickstart}{password} : $anvil->data->{defaults}{kickstart}{password}, + debug => 0, # This isn't the same as the rest of our code, just '1' or '0'. + }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { kickstart_body => $kickstart_body }}); + + # Return code if '1' means the file was changed, '2' indicates it didn't change. '0' means + # something went wrong. + my $kickstart_file = "/var/www/html/".$anvil->data->{host_os}{os_type}."/".$anvil->data->{host_os}{os_arch}."/kickstart/".$type.".ks"; + my $kickstart_success = $anvil->Storage->update_file({ + body => $kickstart_body, + file => $kickstart_file, + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { kickstart_success => $kickstart_success }}); + if (not $kickstart_success) + { + # Failed. + print $anvil->Words->string({key => "error_0043", variables => { file => $kickstart_file }})."\n"; + $anvil->nice_exit({code => 3}); + } + elsif ($dhcpd_conf_success eq "1") + { + # Updated + $anvil->Storage->change_mode({target => $kickstart_file, mode => "0664"}); + $anvil->Storage->change_owner({target => $kickstart_file, user => "apache", group => "apache" }); + + print $anvil->Words->string({key => "message_0097", variables => { file => $kickstart_file }})."\n"; + } + elsif ($dhcpd_conf_success eq "2") + { + # Update not needed. + print $anvil->Words->string({key => "message_0096", variables => { file => $kickstart_file }})."\n"; + } + } + # Configure apache to show hidden (dot) files. my $old_autoindex_conf = $anvil->Storage->read_file({file => $anvil->data->{path}{configs}{'autoindex.conf'}}); my $new_autoindex_conf = ""; @@ -403,6 +468,20 @@ sub setup_boot_environment print $anvil->Words->string({key => "message_0101"})."\n"; } + ### TODO: Add UEFI + # Copy the background splash image for the PXE boot images. + my $bios_splash = $anvil->data->{path}{directories}{skins}."/".$anvil->Template->skin."/images/bios-splash.jpg"; + if (-e $bios_splash) + { + # We don't bother checking if this needs to be updated because rsync can figure it out more + # efficiently. + $anvil->Storage->rsync({ + debug => 2, + source => $bios_splash, + destination => $anvil->data->{path}{directories}{tftpboot}."/splash.jpg", + }); + } + return(0); } @@ -457,6 +536,9 @@ sub update_install_source { my ($anvil) = @_; + ### TODO: Make this only run once every 24 hours (track in the database; + ### 'system::install-target::source-updated'). + ### TODO: Make sure this path exists. my $download_path = "/var/www/html/".$anvil->data->{host_os}{os_type}."/".$anvil->data->{host_os}{os_arch}."/os/Packages/"; my $array_size = @{$anvil->data->{packages}};