#!/usr/bin/perl # # This takes a stock RHEL or CentOS 6.x ISO as the source for generating a Anvil! install ISO. # # This DOES NOT rely on AN::Tools perl modules as most users will not have them installed when they run this. # For the same reason, translations are not (yet) supported. # # # It was created to avoid legal concerns regarding redistributing modified CentOS or RHEL ISOs. Please use it # with this spirit in mind and do not make available the resulting ISOs for general download unless you get # written permission from Alteeve's Niche! and either Red Hat or CentOS, depending on the generated ISO. # # (c) Alteeve's Niche! Inc, 2016 - https://alteeve.com # # This program is released under the GPL v2+ # # Return Codes # 0 = Successful run # 1 = Source ISO(s) not found # 2 = Source(es( not mounted and 'isoinfo' and/or 'iso-read' not installed. # 3 = Source ISO(s) have unrecognized mds5 sums. # 4 = No source ISO given # 5 = Failed to copy a file to Packages/ but the shell call itself didn't fail. # 6 = # 7 = Failed to download an RPM from an-repo. # 8 = User rejected the creation of the working directory. # 9 = The source ISO's .discinfo file wasn't copied to the source directory. # 10 = Error processing an RPM when running 'createrepo'. # 11 = mkisofs failed. # 12 = Copied file size is different from the source file size. # 13 = Failed to find the XML file needed for createrepo. # 14 = Failed to generate a UUID. # use strict; use warnings; use IO::Handle; my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; if (($running_directory =~ /^\./) && ($ENV{PWD})) { $running_directory =~ s/^\./$ENV{PWD}/; } my $conf = { # Executable programs. We will search for these in PATH if not pre-defined (start with '/...'). executable => { createrepo => "createrepo", cp => "cp", curl => "curl", # This is only needed on Ubuntu because it's iso-read is broken. fuseiso => "fuseiso", fusermount => "fusermount", isoinfo => "isoinfo", 'iso-read' => "iso-read", md5sum => "md5sum", mkisofs => "mkisofs", mount => "mount", rm => "rm", unzip => "unzip", uuidgen => "uuidgen", }, # Paths to files and directories path => { redhat_release => "/etc/redhat-release", os_release => "/etc/os-release", # This is the path and file name used to store the example 'striker-installer' call on newly # built Striker dashboards. example_install => "/root/striker-installer.example", # Directories and files we download, copy or create will be placed under this directory, # which itself will be appended to the user's home directory. working_dir => "anvil", # This is used to differentiate between CentOS and RHEL ISOs being generated. centos_dir => "CentOS", rhel_dir => "RHEL", # If this directory exists under the 'sys::user::current_dir', we'll check for a # 'sys::name::' subdirectory. If one is found, packages in there will be used to generate # the new ISO's 'Packages' directory. updates_dir => "Updates", # This will be the root of the ISO. The 'working_dir' above will hold other temporary files # like things we need to download. source_path => "source", # These diretories are needed for the ISO and will be made under 'source_path' as needed. iso => { file_name => "", kickstart => "ks", striker => "striker", # striker-master will be created by zip packages => "Packages", tools => "Tools", ### NOTE: Entries below here are appended to 'path::iso::tools'. fence => "fence", fujitsu => "Fujitsu", avago => "Avago", asix => "ASIX", ### NOTE: Entries above here are appended to 'path::iso::tools'. zip_iso => "ISO", # These keys are appended to the 'path::zip::' path. The 'target' is where the # files in these directories should be copied to. The 'target' will be appended to # 'path::source_path'. zip_copy => { isolinux => { target => "isolinux", }, syslinux => { target => "syslinux", }, EFI => { target => "EFI", }, ks => { target => "ks", }, } }, # This details files to be copied out of zip to a directory in the source. zip => { # This is where we find the root of the ISO data from the extracted zip file under # 'path::source_path'. root => "striker/ISO", # These are the subdirectory names we'll used under 'path::zip::root' depending on # the OS being build on. centos => "CentOS", rhel => "RHEL", suffix => "6/Anvil", }, }, source => {}, source_packages => {}, sys => { date => "", fuse_mounted => [], 'time' => "", debug => 0, md5sum => { 'b41cfb8ae90737f9945288bcc3475cc3' => "RHEL 6.10, Disk 1", '5e131530e18bef7ff0a5d70bd2eb9c3d' => "RHEL 6.10, Disk 1, EFDisk Fix", '8ffcc065c3110e6fa0144cb85e4bb4bc' => "CentOS 6.10, Disk 1", '8ff3b03bbac2224c56943e8936348a9d' => "CentOS 6.10, Disk 2", }, # This will be set to 'rhel' or 'centos', depending on the source ISO type. os => "", source => { iso => [], }, name => { centos => "CentOS", rhel => "RHEL", release => "6.10", suffix => "", mark => "m2", version => "2.0.9", }, # This is will be set to '1' if the source ISO is not mounted. use_isoread => 0, user => { name => $ENV{USER}, home => $ENV{HOME}, current_dir => $ENV{PWD}, }, # These are the request for permission to download third-party tools. third_party => { ASIX => { terms => "https://alteeve.com/an-repo/el6/Third_Party/ASIX/#tc", agreed => 0, }, Avago => { terms => "https://alteeve.com/an-repo/el6/Third_Party/Avago/#tc", agreed => 0, }, Fujitsu => { terms => "https://alteeve.com/an-repo/el6/Third_Party/Fujitsu/#tc", agreed => 0, }, }, # This contains (eventually) user-configurable values to use in the kickstart files. ks => { admin_user => "admin", anvil_name => "Anvil!", background => "splash.jpg", uefi_background => "pxe_voyager1.xpm.gz", bcn_netmask => "16", company_name => "Alteeve's Niche! Inc.", byline => "Intelligent Availability Platform", support_url => "https://alteeve.com/w/Support", domain => "alteeve.com", ifn_dns1 => "8.8.8.8", ifn_dns2 => "8.8.4.4", ifn_gateway => "10.255.255.254", ifn_netmask => "16", keyboard => "us", language => "en_CA.UTF-8", os_version => "6.8", password => "Initial1", # and 'root'. prefix => "an", striker_bcn => "10.20.4", striker_ifn => "10.255.4", striker_name => "Striker", timezone => "America/Toronto", }, }, url => { # By default, we will read the latest release from 'striker_latest'. If, however, we can't # access that file, we'll use 'striker_default'. Optionally, the user can request the latest # master with '--master', in which case we will download the 'striker_master' URL. In all # cases, the downloaded file will be saved as 'striker_zip_file'. striker_default => "https://github.com/ClusterLabs/striker/archive/v2.0.9.zip", striker_latest => "https://www.alteeve.com/an-repo/striker_latest.txt", striker_master => "https://codeload.github.com/ClusterLabs/striker/zip/master", striker_zip_file => "striker.zip", 'an-repo' => "https://alteeve.com/an-repo/el6/RPMS", third_party => "https://alteeve.com/an-repo/el6/Third_Party", # These are non-RPM packages we want to download for the ISO. ASIX => { ax8817x => { url => "https://alteeve.com/an-repo/el6/Third_Party/ASIX/AX88179_178A_LINUX_DRIVER_v1.14.4_SOURCE.zip", mode => 0644, }, }, Fujitsu => { primecollect => { url => "https://alteeve.com/an-repo/el6/Third_Party/Fujitsu/PrimeCollect.shar", mode => 0755, }, }, Tools => { fence_delay => { url => "https://raw.githubusercontent.com/ClusterLabs/striker/master/tools/fence_delay", mode => 0755, }, fence_apc_alteeve => { url => "https://raw.githubusercontent.com/ClusterLabs/striker/master/tools/fence_apc_alteeve", mode => 0755, }, fence_raritan_snmp => { url => "https://raw.githubusercontent.com/ClusterLabs/striker/master/tools/fence_raritan_snmp", mode => 0755, }, }, }, # Used to generate the kickstart files. ks => { dvd => { striker => { 1 => { contents => "", file_name => "", }, 2 => { contents => "", file_name => "", }, }, }, pxe => { striker => { 1 => { contents => "", file_name => "", }, 2 => { contents => "", file_name => "", }, }, node => { 1 => { source => { 1 => { contents => "", file_name => "", }, 2 => { contents => "", file_name => "", }, }, }, 2 => { source => { 1 => { contents => "", file_name => "", }, 2 => { contents => "", file_name => "", }, }, }, }, }, usb => { striker => { 1 => { contents => "", file_name => "", }, 2 => { contents => "", file_name => "", }, }, }, }, }; get_switches($conf); $conf->{sys}{debug} = 1 if $conf->{switches}{debug}; if (($conf->{switches}{h}) or ($conf->{switches}{'?'}) or ($conf->{switches}{help}) or (not $conf->{switches}{source})) { print_usage($conf); exit(0); } # Enable debugging if the user requested it. $conf->{sys}{debug} = 1 if $conf->{switches}{v}; $conf->{sys}{debug} = 2 if $conf->{switches}{vv}; $conf->{sys}{debug} = 3 if $conf->{switches}{vvv}; # Check the OS type check_os($conf); # Make sure we know where executables are on this system. find_executables($conf); # Prepare the current date and time for later use in mkisofs. get_date($conf); # Ask the user to ACK third party tools agree_to_terms($conf); # Genreate an MD5 sum and see if it matches one we know. validate_source($conf); # Setup our build environment. This creates the directories we'll need for the new ISO, copy over isolinux # and syslinux, ask the user to download binary files, load our kickstart scripts and splash screens, etc. setup_build_environment($conf); # Mount the source, if it isn't already prep_source($conf); # Get the list of packages we want to install for this OS. get_package_list($conf); # Build the list of source files to copy. build_source($conf); # Download things we need. download_striker($conf); # Prepare the 'Packages' repository prep_packages_repo($conf); exit(0); ############################################################################################################# # Functions # ############################################################################################################# # This determines what OS we're on and tunes to run appropriately. sub check_os { my ($conf) = @_; if (-e $conf->{path}{redhat_release}) { # We're a RHEL variant. my $os_string = ""; my $shell_call = $conf->{path}{redhat_release}; print __LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "<$shell_call") or die "Failed to read: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; $os_string = $_; print __LINE__."; [ Debug ] - os_string: [$os_string].\n" if $conf->{sys}{debug} >= 2; } close $file_handle; if ($os_string =~ /6\./) { $conf->{sys}{local_os} = "el6"; print __LINE__."; [ Debug ] - sys::local_os: [".$conf->{sys}{local_os}."].\n" if $conf->{sys}{debug} >= 2; } elsif ($os_string =~ /7\./) { $conf->{sys}{local_os} = "el7"; print __LINE__."; [ Debug ] - sys::local_os: [".$conf->{sys}{local_os}."].\n" if $conf->{sys}{debug} >= 2; } elsif ($os_string =~ /Fedora/) { $conf->{sys}{local_os} = "fedora"; print __LINE__."; [ Debug ] - sys::local_os: [".$conf->{sys}{local_os}."].\n" if $conf->{sys}{debug} >= 2; } } elsif (-e $conf->{path}{os_release}) { ### SUSE style # NAME="openSUSE Leap" # VERSION="42.1" # VERSION_ID="42.1" # PRETTY_NAME="openSUSE Leap 42.1 (x86_64)" # ID=opensuse # ANSI_COLOR="0;32" # CPE_NAME="cpe:/o:opensuse:opensuse:42.1" # BUG_REPORT_URL="https://bugs.opensuse.org" # HOME_URL="https://opensuse.org/" # ID_LIKE="suse" # ### Ubuntu style # NAME="Ubuntu" # VERSION="16.04.1 LTS (Xenial Xerus)" # ID=ubuntu # ID_LIKE=debian # PRETTY_NAME="Ubuntu 16.04.1 LTS" # VERSION_ID="16.04" # HOME_URL="http://www.ubuntu.com/" # SUPPORT_URL="http://help.ubuntu.com/" # BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" # UBUNTU_CODENAME=xenial my $os_name = ""; my $os_version = ""; my $shell_call = $conf->{path}{os_release}; print __LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "<$shell_call") or die "Failed to read: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line].\n" if $conf->{sys}{debug} >= 2; if ($line =~ /NAME="(.*?)"/) { $os_name = lc($line); print __LINE__."; [ Debug ] - os_name: [$os_name].\n" if $conf->{sys}{debug} >= 2; } if ($line =~ /VERSION="(.*?)"/) { $os_version = lc($line); print __LINE__."; [ Debug ] - os_version: [$os_version].\n" if $conf->{sys}{debug} >= 2; } } close $file_handle; if ($os_name) { if ($os_name =~ /suse/) { $conf->{sys}{local_os} = "suse"; print __LINE__."; [ Debug ] - sys::local_os: [".$conf->{sys}{local_os}."].\n" if $conf->{sys}{debug} >= 2; } elsif ($os_name =~ /ubuntu/) { $conf->{sys}{local_os} = "ubuntu"; print __LINE__."; [ Debug ] - sys::local_os: [".$conf->{sys}{local_os}."].\n" if $conf->{sys}{debug} >= 2; } } } return(0); } # This uses uuidget to get a UUID for the example --host-uuid switch. sub get_uuid { my ($conf) = @_; my $uuid = ""; my $shell_call = $conf->{executable}{uuidgen}; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/) { $uuid = $line; print __LINE__."; [ Debug ] - uuid: [$uuid]\n" if $conf->{sys}{debug} >= 2; } else { print " [ Error ] - I was asked to generate a UUID, but that failed. I called: [ Error ] '$shell_call' [ Error ] Which returned: [$line]. [ Error ] A UUID string was expected, and this didn't match. "; clean_up($conf); exit(14); } } close $file_handle; return($uuid); } # This builds the body of the kickstart file. sub generate_kickstart_body { my ($conf, $target, $machine, $number, $source) = @_; my $say_os = $conf->{sys}{os_type} eq "centos" ? "CentOS" : "RHEL"; my $os_dir = $conf->{sys}{os_type} eq "centos" ? "centos6" : "rhel6"; my $say_target = $target eq "pxe" ? "Network Install (PXE)" : $target eq "dvd" ? "Optical Media (DVD)" : "USB Drive"; my $say_machine = $machine eq "striker" ? $conf->{sys}{ks}{striker_name}." Dashboard" : $conf->{sys}{ks}{anvil_name}." Node"; $say_machine .= " #0".$number; my $peer_number = $number eq "1" ? 2 : 1; my $hostname = "new-${machine}0${number}"; my $peer_ip = $number eq "1" ? $conf->{sys}{ks}{striker_bcn}.".2" : $conf->{sys}{ks}{striker_bcn}.".1"; my $my_bcn_ip = $number eq "1" ? $conf->{sys}{ks}{striker_bcn}.".1" : $conf->{sys}{ks}{striker_bcn}.".2"; my $my_ifn_ip = $number eq "1" ? $conf->{sys}{ks}{striker_ifn}.".1" : $conf->{sys}{ks}{striker_ifn}.".2"; my $dhcp_range = "10.20.7.200:10.20.7.230"; my $selinux = $machine eq "striker" ? "permissive" : "enforcing"; my $packages = get_packages($conf, $machine); my $common_post = get_common_post($conf); my $copy_logs = get_copy_logs($conf); # (De)branding doesn't apply to the kickstart comment header. my $ks_body = "### ".$conf->{sys}{ks}{company_name}." - ".$conf->{sys}{ks}{anvil_name}." - ".$conf->{sys}{ks}{byline}." # License: GPLv2 # Built: ".$conf->{sys}{date}." at ".$conf->{sys}{'time'}." # Target: $say_target # OS: $say_os # Machine: $say_machine ### Setup values. # Run a text-based install install text "; if ($target eq "pxe") { # If it is a dashboard, install from the peer. If it is a node, install from the source. if ($machine eq "node") { $ks_body .= " # Installing from ".$conf->{sys}{ks}{striker_name}." 0${source}'s PXE server. url --url=http://".$conf->{sys}{ks}{striker_bcn}.".${source}/$os_dir/x86_64/img/ "; } else { $ks_body .= " # Installing from ".$conf->{sys}{ks}{striker_name}." 0${number}'s PXE server. url --url=http://$peer_ip/$os_dir/x86_64/img/ "; } } elsif ($target eq "dvd") { $ks_body .= " # Installing from DVD. cdrom "; } else { $ks_body .= " # Installing from USB, no argument needed. "; } $ks_body .= " # Set the language and keyboard type. lang ".$conf->{sys}{ks}{language}." keyboard ".$conf->{sys}{ks}{keyboard}." # Set the system clock to UTC and then define the timezone. timezone --utc ".$conf->{sys}{ks}{timezone}." # This sets the (first) ethernet device. There is currently no way to map # device names to physical interfaces. For this reason, we use DHCP for install # and configure the network manually post-install. network --device eth0 --bootproto dhcp --onboot yes --hostname $hostname.".$conf->{sys}{ks}{domain}." # This is the root user's password. The one below should be taken as an example # and changed as it is a terrible password. authconfig --enableshadow --passalgo=sha512 --enablefingerprint rootpw ".$conf->{sys}{ks}{password}." # Default admin user account. user --name=".$conf->{sys}{ks}{admin_user}." --plaintext --password=".$conf->{sys}{ks}{password}." # At this time, ".$conf->{sys}{ks}{striker_name}." does not yet work with SELinux in enforcing mode. This # is expected to change in a (near) future release. firewall --service=ssh selinux --$selinux # There is no need for the 'first boot' menu system to run, so we will disable # it. firstboot --disable # Set the installation logging level. logging --level=debug # Enable httpd so that the local repo is available on boot. services --enabled httpd,gpm,iptables services --disabled kdump # Reboot when the install is finished. reboot # This runs a script (below) that generates the partitioning information # depending on a rudamentary test for available storage devices. \%include /tmp/part-include $packages "; # From here, it starts to depend a lot on what we're building the KS for. if ($machine eq "node") { # Nodes are easier because they're only ever PXE. $ks_body .= " # Now it is time for the first chroot'ed configuration steps. \%post --log=/root/install/post-install_chroot.log $common_post # Download 'list-ips' from the Striker we're installing from. echo \"Downloading 'list-ips'.\" mkdir /sbin/striker curl http://".$conf->{sys}{ks}{striker_bcn}.".${source}/$os_dir/x86_64/img/striker/tools/list-ips > /sbin/striker/list-ips chown root:root /sbin/striker/list-ips chmod 755 /sbin/striker/list-ips # Download 'fence_raritan_snmp', 'fence_apc_alteeve' and 'fence_delay' from the Striker we're installing from. echo \"Downloading 'fence_raritan_snmp'.\" curl http://".$conf->{sys}{ks}{striker_bcn}.".${source}/$os_dir/x86_64/img/Tools/fence/fence_raritan_snmp > /usr/sbin/fence_raritan_snmp chown root:root /usr/sbin/fence_raritan_snmp chmod 755 /usr/sbin/fence_raritan_snmp echo \"Downloading 'fence_apc_alteeve'.\" curl http://".$conf->{sys}{ks}{striker_bcn}.".${source}/$os_dir/x86_64/img/Tools/fence/fence_apc_alteeve > /usr/sbin/fence_apc_alteeve chown root:root /usr/sbin/fence_apc_alteeve chmod 755 /usr/sbin/fence_apc_alteeve echo \"Downloading 'fence_delay'.\" curl http://".$conf->{sys}{ks}{striker_bcn}.".${source}/$os_dir/x86_64/img/Tools/fence/fence_delay > /usr/sbin/fence_delay chown root:root /usr/sbin/fence_delay chmod 755 /usr/sbin/fence_delay # Download 'anvil-map-network' from the Striker we're installing from. echo \"Downloading 'anvil-map-network'.\" curl http://".$conf->{sys}{ks}{striker_bcn}.".${source}/$os_dir/x86_64/img/striker/tools/anvil-map-network > /sbin/striker/anvil-map-network chown root:root /sbin/striker/hap-map-network chmod 755 /sbin/striker/anvil-map-network # Show details on boot. echo \"Setting plymouth to use detailed boot screen\" plymouth-set-default-theme details --rebuild-initrd sed -i 's/ rhgb//' /boot/grub/grub.conf sed -i 's/ quiet//' /boot/grub/grub.conf # Setup the Striker repos. "; if ($conf->{sys}{ks}{prefix}) { $ks_body .= "cat > /etc/yum.repos.d/".$conf->{sys}{ks}{prefix}."-striker01.repo << EOF\n"; $ks_body .= "[".$conf->{sys}{ks}{prefix}."-striker01]\n"; $ks_body .= "name=".$conf->{sys}{ks}{prefix}."-".$conf->{sys}{ks}{striker_name}." 01 Repository"; } else { $ks_body .= "cat > /etc/yum.repos.d/striker01.repo << EOF\n"; $ks_body .= "[striker01]\n"; $ks_body .= "name=".$conf->{sys}{ks}{striker_name}." 01 Repository"; } $ks_body .= " baseurl=http://".$conf->{sys}{ks}{striker_bcn}.".1/$os_dir/x86_64/img/ enabled=1 gpgcheck=0 skip_if_unavailable=1 priority=1 EOF "; if ($conf->{sys}{ks}{prefix}) { $ks_body .= "cat > /etc/yum.repos.d/".$conf->{sys}{ks}{prefix}."-striker02.repo << EOF\n"; $ks_body .= "[".$conf->{sys}{ks}{prefix}."-striker02]\n"; $ks_body .= "name=".$conf->{sys}{ks}{prefix}."-".$conf->{sys}{ks}{striker_name}." 02 Repository"; } else { $ks_body .= "cat > /etc/yum.repos.d/striker02.repo << EOF\n"; $ks_body .= "[striker02]\n"; $ks_body .= "name=".$conf->{sys}{ks}{striker_name}." 02 Repository"; } $ks_body .= " baseurl=http://".$conf->{sys}{ks}{striker_bcn}.".2/$os_dir/x86_64/img/ enabled=1 gpgcheck=0 skip_if_unavailable=1 priority=1 EOF \%end $copy_logs ### Script to setup partitions. \%pre --log=/tmp/ks-preinstall.log #!/bin/sh # Prepare the disks in the script below. It checks '/proc/partitions' to see # what configuration to use. ############################################################################### # Below is for 40 GiB / partitions with the balance of free space to be # # configured later. # ############################################################################### # Default is to use /dev/sda. At this time, software arrays are not supported. DRIVE=\"sda\"; # /dev/vda KVM virtual machine if grep -q vda /proc/partitions then DRIVE=\"vda\" fi ### Nodes with NVMe drives will use '/dev/nvme0n1' if grep -q nvme0n1 /proc/partitions; then DRIVE=\"nvme0n1\" fi # Zero-out the first 100GB to help avoid running into problems when a node that # was previously in a cluster gets rebuilt. Only run on real hardware, tends to # crash VMs. if ! grep -q vda /proc/partitions; then echo \"Please be patient! Zero'ing out the first 100 GiB of /dev/\${DRIVE}...\" dd if=/dev/zero of=/dev/\${DRIVE} bs=4M count=25000 fi ### Make sure we always create a GPT disk (https://access.redhat.com/solutions/55652) echo \"Creating a GPT disk label\" /usr/bin/dd bs=512 count=10 if=/dev/zero of=/dev/\${DRIVE} /usr/sbin/parted --script /dev/\${DRIVE} mklabel gpt /usr/sbin/parted -l /dev/\${DRIVE} /usr/bin/sleep 30 # Now write the partition script echo \"Done! Now creating and formatting partitions.\" cat > /tmp/part-include <{path}{iso}{file_name}." -O /mnt/sysimage/var/www/html/$os_dir/x86_64/iso/".$conf->{path}{iso}{file_name}." # Make sure our source is mounted. mount -o loop /mnt/sysimage/var/www/html/$os_dir/x86_64/iso/".$conf->{path}{iso}{file_name}." /mnt/source/ "; } elsif ($target eq "dvd") { # DVD/ISO target $ks_body .= " # Make sure the optical drive is mounted. mount /dev/cdrom /mnt/source; # Create a copy of the install ISO on ".$conf->{sys}{ks}{striker_name}.". echo 'Copying the install iso image using dd. Be patient' dd if=/dev/cdrom of=/mnt/sysimage/var/www/html/$os_dir/x86_64/iso/".$conf->{path}{iso}{file_name}." "; } else { # USB target $ks_body .= " # Make sure our USB source partition is mounted. if grep -q nvme0n1 /proc/partitions; then mount /dev/sda1 /mnt/source; else mount /dev/sdb1 /mnt/source; fi # Copy the install ISO into place echo 'Copying the install iso image. Be patient' cp -v /mnt/source/".$conf->{path}{iso}{file_name}." /mnt/sysimage/var/www/html/$os_dir/x86_64/iso/ "; } # Now the common bits. $ks_body .= " # Setup 'list-ips'. echo \"Setting up 'list-ips'.\" mkdir /mnt/sysimage/sbin/striker cp /mnt/source/striker/tools/list-ips /mnt/sysimage/sbin/striker/list-ips chown root:root /mnt/sysimage/sbin/striker/list-ips chmod 755 /mnt/sysimage/sbin/striker/list-ips # Setup the repo rebuild program echo \"Setting up 'striker-rebuild-repo.sh'.\" cp /mnt/source/striker/tools/striker-rebuild-repo.sh /mnt/sysimage/sbin/striker/striker-rebuild-repo.sh chown root:root /mnt/sysimage/sbin/striker/striker-rebuild-repo.sh chmod 755 /mnt/sysimage/sbin/striker/striker-rebuild-repo.sh # Copy the raritan and alteeve-variant APC fence agents into place. echo 'Copying fence_raritan_snmp into /usr/sbin/' cp /mnt/source/Tools/fence/fence_raritan_snmp /mnt/sysimage/usr/sbin/ echo 'Copying fence_apc_alteeve into /usr/sbin/' cp /mnt/source/Tools/fence/fence_apc_alteeve /mnt/sysimage/usr/sbin/ # Copy the node and dashboard KSes into place echo 'Copying the KS scripts into place.' cp /mnt/source/ks/pxe-new-node01_from-striker0${number}.ks /mnt/sysimage/var/www/html/$os_dir/x86_64/ks/pxe-new-node01.ks cp /mnt/source/ks/pxe-new-node02_from-striker0${number}.ks /mnt/sysimage/var/www/html/$os_dir/x86_64/ks/pxe-new-node02.ks cp /mnt/source/ks/pxe-new-striker01.ks /mnt/sysimage/var/www/html/$os_dir/x86_64/ks/ cp /mnt/source/ks/pxe-new-striker02.ks /mnt/sysimage/var/www/html/$os_dir/x86_64/ks/ # A little flair... echo 'Setting the PXE wallpaper.' cp /mnt/source/syslinux/".$conf->{sys}{ks}{background}." /mnt/sysimage/var/lib/tftpboot/ cp /mnt/source/efi64/".$conf->{sys}{ks}{uefi_background}." /mnt/sysimage/var/lib/tftpboot/efi64/ # Copy the ".$conf->{sys}{ks}{striker_name}." source files and installer into place cp -Rvp /mnt/source/".$conf->{path}{iso}{striker}." /mnt/sysimage/root/ cp /mnt/source/".$conf->{path}{iso}{striker}."/tools/striker-installer /mnt/sysimage/root/ echo \"Copying 'Tools' into /mnt/sysimage/var/www/html/$os_dir/x86_64/files/\" rsync -av /mnt/source/Tools /mnt/sysimage/var/www/html/$os_dir/x86_64/files/ ### No longer doing it this way. Now we'll rsync the contents of the ISO to the ### img directory. #echo 'Configuring /etc/fstab to mount the ISO on boot.' #echo '/var/www/html/$os_dir/x86_64/iso/".$conf->{path}{iso}{file_name}." /var/www/html/$os_dir/x86_64/img iso9660 loop 0 0' >> /mnt/sysimage/etc/fstab # Copy the ISO contents to the apache 'img' folder. echo 'Copying the ISO source to apache...' cp -Rvp /mnt/source/* /mnt/sysimage/var/www/html/$os_dir/x86_64/img/ cp /mnt/source/.discinfo /mnt/sysimage/var/www/html/$os_dir/x86_64/img/ cp /mnt/source/.treeinfo /mnt/sysimage/var/www/html/$os_dir/x86_64/img/ echo 'Copying isolinux to /var/lib/tftpboot/boot/$os_dir/x86_64/' rsync -av /mnt/source/isolinux/* /mnt/sysimage/var/lib/tftpboot/boot/$os_dir/x86_64/ rsync -av /mnt/source/EFI/* /mnt/sysimage/var/lib/tftpboot/boot/$os_dir/x86_64/ # */ # Ignore me, I am unbreaking syntax highlighting in vim... \%end # Now it is time for the first chroot'ed configuration steps. \%post --log=/root/install/post-install_chroot.log $common_post echo 'Writing out local yum repository config' "; if ($conf->{sys}{ks}{prefix}) { $ks_body .= "cat > /etc/yum.repos.d/".$conf->{sys}{ks}{prefix}."-striker0${number}.repo << EOF\n"; $ks_body .= "[".$conf->{sys}{ks}{prefix}."-striker0${number}-$os_dir]\n"; $ks_body .= "name=".$conf->{sys}{ks}{prefix}."-".$conf->{sys}{ks}{striker_name}." 0${number} $os_dir ".$conf->{sys}{name}{release}." + Custom Repository"; } else { $ks_body .= "cat > /etc/yum.repos.d/striker0${number}.repo << EOF\n"; $ks_body .= "[striker0${number}-$os_dir]\n"; $ks_body .= "name=".$conf->{sys}{ks}{striker_name}." 0${number} $os_dir ".$conf->{sys}{name}{release}." + Custom Repository"; } $ks_body .= " baseurl=http://localhost/$os_dir/x86_64/img/ enabled=1 gpgcheck=0 priority=1 EOF # Now setup the script for the user to call once booted. echo 'Writing out the sample striker-installer script' cat > ".$conf->{path}{example_install}." << EOF # This is an example 'striker-installer' call. Feel free to edit this file here # and then call it with 'sh ".$conf->{path}{example_install}."' to save typing # all this out. # # To understand what all these switches do, and what other switches are # available, please run './striker-installer --help'. ./striker-installer \\\\ -c \"".$conf->{sys}{ks}{company_name}."\" \\\\ -n \"".$conf->{sys}{ks}{prefix}."-striker0${number}.".$conf->{sys}{ks}{domain}."\" \\\\ -u \"".$conf->{sys}{ks}{admin_user}.":".$conf->{sys}{ks}{password}."\" \\\\ -i $my_ifn_ip/".$conf->{sys}{ks}{ifn_netmask}.",dg=".$conf->{sys}{ks}{ifn_gateway}.",dns1=".$conf->{sys}{ks}{ifn_dns1}.",dns2=".$conf->{sys}{ks}{ifn_dns2}." \\\\ -b $my_bcn_ip/".$conf->{sys}{ks}{bcn_netmask}." \\\\ -p $dhcp_range \\\\ --peer-dashboard hostname=".$conf->{sys}{ks}{prefix}."-striker0${peer_number}.".$conf->{sys}{ks}{domain}.",bcn_ip=$peer_ip \\\\ "; # Use '--rhn' on RHEL only. if ($conf->{sys}{os_type} eq "rhel") { $ks_body .= " --router-mode \\\\\n"; $ks_body .= " --rhn \"rhn_".$conf->{sys}{ks}{admin_user}.":rhn_".$conf->{sys}{ks}{password}."\"\n"; } else { $ks_body .= " --router-mode\n"; } $ks_body .= "EOF chmod 755 ".$conf->{path}{example_install}." # This writes out the custom PXE menu used when installing nodes and dashboard # from this system. echo 'Writing out the default PXE menu' cat > /var/lib/tftpboot/pxelinux.cfg/default << EOF # Use the high-colour menu system. UI vesamenu.c32 # Time out and use the default menu option. Defined as tenths of a second. TIMEOUT 600 # Prompt the user. Set to '1' to automatically choose the default option. This # is really meant for files matched to MAC addresses. PROMPT 0 # Set the boot menu to be 1024x768 with a nice background image. Be careful to # ensure that all your user's can see this resolution! Default is 640x480. MENU RESOLUTION 1024 768 # The background image MENU BACKGROUND ".$conf->{sys}{ks}{background}." # These do not need to be set. I set them here to show how you can customize or # localize your PXE server's dialogue. MENU TITLE ".$conf->{sys}{ks}{anvil_name}." Node and ".$conf->{sys}{ks}{striker_name}." Dashboard Install Server # Below, the hash (#) character is replaced with the countdown timer. The # '{,s}' allows for pluralizing a word and is used when the value is >= '2'. MENU AUTOBOOT Will boot the next device as configured in your BIOS in # second{,s}. MENU TABMSG Press the key to edit the boot parameters of the highlighted option. MENU NOTABMSG Editing of this option is disabled. # The following options set the various colours used in the menu. All possible # options are specified except for F# help options. The colour is expressed as # two hex characters between '00' and 'ff' for alpha, red, green and blue # respectively (#AARRGGBB). # Format is: MENU COLOR MENU COLOR screen 0 #80ffffff #00000000 std # background colour not covered by the splash image MENU COLOR border 0 #ffffffff #ee000000 std # The wire-frame border MENU COLOR title 0 #ffff3f7f #ee000000 std # Menu title text MENU COLOR sel 0 #ff00dfdf #ee000000 std # Selected menu option MENU COLOR hotsel 0 #ff7f7fff #ee000000 std # The selected hotkey (set with ^ in MENU LABEL) MENU COLOR unsel 0 #ffffffff #ee000000 std # Unselected menu options MENU COLOR hotkey 0 #ff7f7fff #ee000000 std # Unselected hotkeys (set with ^ in MENU LABEL) MENU COLOR tabmsg 0 #c07f7fff #00000000 std # Tab text MENU COLOR timeout_msg 0 #8000dfdf #00000000 std # Timout text MENU COLOR timeout 0 #c0ff3f7f #00000000 std # Timout counter MENU COLOR disabled 0 #807f7f7f #ee000000 std # Disabled menu options, including SEPARATORs MENU COLOR cmdmark 0 #c000ffff #ee000000 std # Command line marker - The '> ' on the left when editing an option MENU COLOR cmdline 0 #c0ffffff #ee000000 std # Command line - The text being edited # Options below haven't been tested, descriptions may be lacking. MENU COLOR scrollbar 0 #407f7f7f #00000000 std # Scroll bar MENU COLOR pwdborder 0 #80ffffff #20ffffff std # Password box wire-frame border MENU COLOR pwdheader 0 #80ff8080 #20ffffff std # Password box header MENU COLOR pwdentry 0 #80ffffff #20ffffff std # Password entry field MENU COLOR help 0 #c0ffffff #00000000 std # Help text, if set via 'TEXT HELP ... ENDTEXT' ### Now define the menu options # It is safest to return booting to the client as the first and default option. # This entry below will do just that. LABEL next MENU LABEL ^A) Boot the next device as configured in your BIOS MENU DEFAULT localboot -1 LABEL pxe-new-node01 MENU LABEL ^1) New ".$conf->{sys}{ks}{anvil_name}." Node 01 - $say_os ".$conf->{sys}{name}{release}." - PXE - Deletes All Existing Data! TEXT HELP .------------------------------------------------------------------. | WARNING: This install will appear to stall at first! BE PATIENT! | ^------------------------------------------------------------------^ To prevent traces of previous installs interrupting the Install Manifest run, this boot option starts by 'zeroing out' the first 100 GiB of the drive. There is no output while this runs. Installs a new ".$conf->{sys}{ks}{anvil_name}." Node 01 using $say_os ".$conf->{sys}{name}{release}.". Will create a traditional /boot + MBR install for systems with traditional BIOSes. Partition will be 0.5 GiB /boot, 4 GiB , 40 GiB /. ENDTEXT KERNEL boot/$os_dir/x86_64/vmlinuz IPAPPEND 2 APPEND initrd=boot/$os_dir/x86_64/initrd.img ks=http://$my_bcn_ip/$os_dir/x86_64/ks/pxe-new-node01.ks ksdevice=bootif LABEL pxe-new-node02 MENU LABEL ^2) New ".$conf->{sys}{ks}{anvil_name}." Node 02 - $say_os ".$conf->{sys}{name}{release}." - PXE - Deletes All Existing Data! TEXT HELP .------------------------------------------------------------------. | WARNING: This install will appear to stall at first! BE PATIENT! | ^------------------------------------------------------------------^ To prevent traces of previous installs interrupting the Install Manifest run, this boot option starts by 'zeroing out' the first 100 GiB of the drive. There is no output while this runs. Installs a new ".$conf->{sys}{ks}{anvil_name}." Node 02 using $say_os ".$conf->{sys}{name}{release}.". Will create a traditional /boot + MBR install for systems with traditional BIOSes. Partition will be 0.5 GiB /boot, 4 GiB , 40 GiB /. ENDTEXT KERNEL boot/$os_dir/x86_64/vmlinuz IPAPPEND 2 APPEND initrd=boot/$os_dir/x86_64/initrd.img ks=http://$my_bcn_ip/$os_dir/x86_64/ks/pxe-new-node02.ks ksdevice=bootif LABEL pxe-new-striker01 MENU LABEL ^3) New ".$conf->{sys}{ks}{striker_name}." 01 dashboard - $say_os ".$conf->{sys}{name}{release}." - PXE - Deletes All Existing Data! TEXT HELP Installs a new ".$conf->{sys}{ks}{striker_name}." 01 using $say_os ".$conf->{sys}{name}{release}.". Will create a traditional /boot + MBR install for systems with traditional BIOSes. Partition will be 0.5 GiB /boot, 4 GiB , remainder for /. ENDTEXT KERNEL boot/$os_dir/x86_64/vmlinuz IPAPPEND 2 APPEND initrd=boot/$os_dir/x86_64/initrd.img ks=http://$my_bcn_ip/$os_dir/x86_64/ks/pxe-new-striker01.ks ksdevice=bootif LABEL pxe-new-striker02 MENU LABEL ^4) New ".$conf->{sys}{ks}{striker_name}." 02 dashboard - $say_os ".$conf->{sys}{name}{release}." - PXE - Deletes All Existing Data! TEXT HELP Installs a new ".$conf->{sys}{ks}{striker_name}." 02 using $say_os ".$conf->{sys}{name}{release}.". Will create a traditional /boot + MBR install for systems with traditional BIOSes. Partition will be 0.5 GiB /boot, 4 GiB , remainder for /. ENDTEXT KERNEL boot/$os_dir/x86_64/vmlinuz IPAPPEND 2 APPEND initrd=boot/$os_dir/x86_64/initrd.img ks=http://$my_bcn_ip/$os_dir/x86_64/ks/pxe-new-striker02.ks ksdevice=bootif label rescue MENU LABEL ^B) Rescue installed system TEXT HELP Boot $say_os ".$conf->{sys}{name}{release}." in rescue mode. ENDTEXT KERNEL boot/$os_dir/x86_64/vmlinuz APPEND initrd=boot/$os_dir/x86_64/initrd.img rescue EOF echo 'Writing out the default PXE menu' cat > /var/lib/tftpboot/efi64/efidefault << EOF timeout 30 default 0 splashimage (nd)/pxe_voyager1.xpm.gz title Boot the next device as configured in your BIOS exit # Install Node 01 via pxe title New ".$conf->{sys}{ks}{anvil_name}." Node 01 - $say_os ".$conf->{sys}{name}{release}." - Deletes All Existing Data! root (nd) macappend kernel /vmlinuz ip=dhcp ks=http://$my_bcn_ip/$os_dir/x86_64/ks/pxe-new-node01.ks ksdevice=bootif initrd /initrd.img # Install Node 02 via pxe title New ".$conf->{sys}{ks}{anvil_name}." Node 02 - $say_os ".$conf->{sys}{name}{release}." - Deletes All Existing Data! root (nd) macappend kernel /vmlinuz ip=dhcp ks=http://$my_bcn_ip/$os_dir/x86_64/ks/pxe-new-node02.ks ksdevice=bootif initrd /initrd.img # Install Striker 1 via pxe title New ".$conf->{sys}{ks}{striker_name}." 01 dashboard - $say_os ".$conf->{sys}{name}{release}." - Deletes All Existing Data! root (nd) macappend kernel /vmlinuz ip=dhcp ks=http://$my_bcn_ip/$os_dir/x86_64/ks/pxe-new-striker01.ks ksdevice=bootif initrd /initrd.img # Install Striker 2 via pxe title New ".$conf->{sys}{ks}{striker_name}." 02 dashboard - $say_os ".$conf->{sys}{name}{release}." - Deletes All Existing Data! root (nd) macappend kernel /vmlinuz ip=dhcp ks=http://$my_bcn_ip/$os_dir/x86_64/ks/pxe-new-striker02.ks ksdevice=bootif initrd /initrd.img # Rescue Installed System Title Rescue installed system root (nd) kernel /vmlinuz rescue initrd /initrd.img EOF # Disable the libvirtd default bridge. echo \"Disabling the default libvirtd bridge 'virbr0'.\" cat /dev/null >/etc/libvirt/qemu/networks/default.xml # Rebuild the repo #/sbin/striker/striker-rebuild-repo.sh echo \"'chroot'ed post install script complete.\" \%end $copy_logs ### Script to setup partitions. \%pre --log=/tmp/ks-preinstall.log #!/bin/sh # Prepare the disks in the script below. It checks '/proc/partitions' to see # what configuration to use. ############################################################################### # Creates a 512 MiB /boot, 4 GiB and the balance to / # ############################################################################### # Default is to use /dev/sda. At this time, software arrays are not supported. DRIVE=\"sda\"; # /dev/vda KVM virtual machine if grep -q vda /proc/partitions; then DRIVE=\"vda\" fi "; if ($target eq "usb") { $ks_body .= " ### The USB drives come up as 'sdb', so the Eee's HDD is /dev/sdc # /dev/sdc ASUS EeeBox machine if grep -q sdc /proc/partitions; then DRIVE=\"sdc\" fi "; } else { $ks_body .= " # /dev/sdb ASUS EeeBox machine if grep -q sdb /proc/partitions; then DRIVE=\"sdb\" fi "; } $ks_body .= " ### Dashboards with NVMe drives will use '/dev/nvme0n1' if grep -q nvme0n1 /proc/partitions; then DRIVE=\"nvme0n1\" fi # Now write the partition script cat >> /tmp/part-include <{sys}{os_type} eq "centos") { $packages .= " # CentOS specific packages yum-plugin-priorities\n"; } if ($conf->{sys}{os_type} eq "rhel") { $packages .= " # RHEL specific packages redhat-support-tool rhnsd subscription-manager subscription-manager-rhsm subscription-manager-rhsm-certificates yum-rhn-plugin "; } if ($machine eq "striker") { $packages .= " # Striker packages needed for Striker dashboards abyssinica-fonts alsa-plugins-pulseaudio at-spi augeas-libs cjkuni-uming-fonts cluster-cim control-center control-center-extra cpp createrepo dbus dejavu-sans-fonts dejavu-sans-mono-fonts dejavu-serif-fonts dhcp dnsmasq ebtables eog firefox ftp gcalctool gdm gdm-plugin-fingerprint gdm-user-switch-applet glx-utils glibc-devel glusterfs glusterfs-api glusterfs-libs gnome-applets gnome-media gnome-packagekit gnome-panel gnome-power-manager gnome-screensaver gnome-session gnome-terminal gnome-utils gnome-vfs2-smb google-crosextra-caladea-fonts google-crosextra-carlito-fonts gpxe-roms-qemu gvfs-archive gvfs-fuse gvfs-smb hal httpd iscsi-initiator-utils jomolhari-fonts keyutils khmeros-base-fonts kurdit-unikurd-web-fonts libcdio liberation-mono-fonts liberation-sans-fonts liberation-serif-fonts libgssglue libtirpc libevent libvirt lklug-fonts lohit-assamese-fonts lohit-bengali-fonts lohit-devanagari-fonts lohit-gujarati-fonts lohit-kannada-fonts lohit-oriya-fonts lohit-punjabi-fonts lohit-tamil-fonts lohit-telugu-fonts lzop madan-fonts metacity mod_ssl mtr-gtk nano nautilus nautilus-open-terminal netcf-libs nfs-utils nfs-utils-lib notification-daemon ntfs-3g ntfsprogs numad openssh-askpass paktype-naqsh-fonts paktype-tehreer-fonts patch pcp-gui perl-CPAN perl-IO-Socket-SSL perl-Net-SSLeay perl-Test-Simple perl-YAML-Tiny perl-libwww-perl plymouth-system-theme polkit-gnome postgresql95-contrib postgresql95-libs postgresql95-plperl postgresql95-server pulseaudio-module-gconf pulseaudio-module-x11 qemu-img qemu-kvm radvd rpcbind seabios sgabios-bin shorewall sil-padauk-fonts smc-meera-fonts spice-server spice-vdagent stix-fonts syslinux-tftpboot tftp-server thai-scalable-waree-fonts tibetan-machine-uni-fonts un-core-dotum-fonts unzip vgabios vino virt-manager vlgothic-fonts wdaemon wqy-zenhei-fonts xdg-user-dirs-gtk xinetd xorg-x11-drivers xorg-x11-server-Xorg xorg-x11-server-utils xorg-x11-utils xorg-x11-xauth xorg-x11-xinit xvattr yelp "; } else { $packages .= " # Striker packages needed for Anvil! Nodes bridge-utils cman corosync drbd84-utils gd gfs2-utils kmod-drbd84 libvirt net-snmp qemu-kvm qemu-kvm-tools python-virtinst rgmanager ricci "; } $packages .= "\%end\n"; return($packages); } # This is common chroot'ed %post to all machines. sub get_common_post { my ($conf) = @_; my $common_post = " # Tell the machine to save downloaded RPM updates (for possible distribution to # other machines for low-bandwidth users). It also makes sure all NICs start on # boot. echo 'Configuring yum to keep its cache.' sed -i 's/keepcache=0/keepcache=1/g' /etc/yum.conf # Disable DNS lookup for SSH so that logins are quick when there is not Internet # access. echo 'Configuring sshd to not use DNS or GSSAPI authentication for fast logins without internet connections.' sed -i 's/#UseDNS yes/UseDNS no/' /etc/ssh/sshd_config sed -i 's/#GSSAPIAuthentication no/GSSAPIAuthentication no/' /etc/ssh/sshd_config sed -i 's/GSSAPIAuthentication yes/#GSSAPIAuthentication yes/' /etc/ssh/sshd_config # Show details on boot. echo 'Setting plymouth to use detailed boot screen' plymouth-set-default-theme details --rebuild-initrd sed -i 's/ rhgb//' /boot/grub/grub.conf sed -i 's/ quiet//' /boot/grub/grub.conf # Setup 'list-ips', which will display the node's post-stage-1 IP address # without the user having to log in. echo /sbin/striker/list-ips >> /etc/rc.local "; return($common_post); } # This is the second non-chroot'ed %post that copies the install logs to /root/ sub get_copy_logs { my ($conf) = @_; my $copy_logs = " # This is set to run at the end. It copies all of the kickstart logs into the # root user's home page. \%post --nochroot echo 'Copying all the anaconda related log files to /root/install/' if [ ! -e '/mnt/sysimage/root/install' ] then mkdir /mnt/sysimage/root/install fi cp -p /tmp/nochroot* /mnt/sysimage/root/install/ cp -p /tmp/kernel* /mnt/sysimage/root/install/ cp -p /tmp/anaconda* /mnt/sysimage/root/install/ cp -p /tmp/ks* /mnt/sysimage/root/install/ cp -p /tmp/program.log /mnt/sysimage/root/install/ cp -p /tmp/storage* /mnt/sysimage/root/install/ cp -p /tmp/yum.log /mnt/sysimage/root/install/ cp -p /tmp/ifcfg* /mnt/sysimage/root/install/ cp -p /tmp/syslog /mnt/sysimage/root/install/ \%end "; return($copy_logs); } # This generates the kickstart files. sub generate_kickstart_files { my ($conf) = @_; my $ks_directory = $conf->{path}{iso}{iso_kickstart}; print __LINE__."; [ Debug ] - ks_directory: [$ks_directory]\n" if $conf->{sys}{debug} >= 2; foreach my $target (sort {$a cmp $b} keys %{$conf->{ks}}) { print __LINE__."; [ Debug ] - target: [$target]\n" if $conf->{sys}{debug} >= 2; foreach my $machine (sort {$a cmp $b} keys %{$conf->{ks}{$target}}) { print __LINE__."; [ Debug ] - machine: [$machine]\n" if $conf->{sys}{debug} >= 2; foreach my $number (sort {$a cmp $b} keys %{$conf->{ks}{$target}{$machine}}) { print __LINE__."; [ Debug ] - number: [$number]\n" if $conf->{sys}{debug} >= 2; if ($machine eq "node") { # Nodes have two sources; PXE from striker 1 or 2. foreach my $source (1..2) { my $ks_file_name = $ks_directory."/pxe-new-node0${number}_from-striker0${source}.ks"; my $ks_body = generate_kickstart_body($conf, $target, $machine, $number, $source); print __LINE__."; [ Debug ] - ks_file_name: [$ks_file_name]\n" if $conf->{sys}{debug} >= 2; print __LINE__."; [ Debug ] - ks_body:\n====\n$ks_body\n====\n" if $conf->{sys}{debug} >= 3; # Remove the existing kickstart file, if it exists. if (-e $ks_file_name) { # Remove it. print "- Deleting the old kickstart file: [$ks_file_name]\n" if $conf->{sys}{debug} >= 1; unlink $ks_file_name or die __LINE__."; [ Error ] - Failed to delete old kickstart file: [$ks_file_name]. The error was: $!\n"; } # Write out the new one. my $shell_call = $ks_file_name; open (my $file_handle, ">$shell_call") or die __LINE__."; Failed to write: [$shell_call], error: $!\n"; print $file_handle $ks_body; close $file_handle; print "- Wrote the kickstart file: [$ks_file_name] successfully.\n" if $conf->{sys}{debug} >= 1; } } else { my $ks_file_name = $ks_directory."/${target}-new-${machine}0${number}.ks"; my $ks_body = generate_kickstart_body($conf, $target, $machine, $number, 0); print __LINE__."; [ Debug ] - ks_file_name: [$ks_file_name]\n" if $conf->{sys}{debug} >= 2; print __LINE__."; [ Debug ] - ks_body:\n====\n$ks_body\n====\n" if $conf->{sys}{debug} >= 3; # Remove the existing kickstart file, if it exists. if (-e $ks_file_name) { # Remove it. print "- Deleting the old kickstart file: [$ks_file_name]\n" if $conf->{sys}{debug} >= 1; unlink $ks_file_name or die __LINE__."; [ Error ] - Failed to delete old kickstart file: [$ks_file_name]. The error was: $!\n"; } # Write out the new one. my $shell_call = $ks_file_name; open (my $file_handle, ">$shell_call") or die __LINE__."; Failed to write: [$shell_call], error: $!\n"; print $file_handle $ks_body; close $file_handle; print "- Wrote the kickstart file: [$ks_file_name] successfully.\n" if $conf->{sys}{debug} >= 1; } } } } return(0); } # Prepare the 'Packages' repository sub prep_packages_repo { my ($conf) = @_; # Get the '.discinfo' number my $disc_id = get_disc_id($conf); print __LINE__."; [ Debug ] - disc_id: [$disc_id]\n" if $conf->{sys}{debug} >= 2; # Walk through 'repodata', clear out old files and get the name of the comps.xml file. my ($xml_file1, $xml_file2) = get_repodata_xml_file_name($conf); print __LINE__."; [ Debug ] - xml_file1: [$xml_file1], xml_file2: [$xml_file2]\n" if $conf->{sys}{debug} >= 2; # Build the repository build_repository($conf, $disc_id, $xml_file1, $xml_file2); # Finally, create the ISO! build_iso($conf); return(0); } # This builds the actual ISO. sub build_iso { my ($conf) = @_; my $os_type = $conf->{sys}{os_type}; my $os_name = $conf->{sys}{name}{$os_type}; print __LINE__."; [ Debug ] - os_name: [$os_name]\n" if $conf->{sys}{debug} >= 2; my $say_date = $conf->{sys}{date}." ".$conf->{sys}{'time'}; print __LINE__."; [ Debug ] - say_date: [$say_date]\n" if $conf->{sys}{debug} >= 2; # Get the current user's name and the hostname. my $say_user = $ENV{USERNAME} ? $ENV{USERNAME} : $ENV{USER}; my $say_host = $ENV{HOSTNAME}; print __LINE__."; [ Debug ] - say_user: [$say_user], say_host: [$say_host]\n" if $conf->{sys}{debug} >= 2; # Put the output path together. print __LINE__."; [ Debug ] - path::iso::file_name: [".$conf->{path}{iso}{file_name}."]\n" if $conf->{sys}{debug} >= 2; my $output_file = $conf->{sys}{user}{current_dir}."/".$conf->{path}{working_dir}."/".$conf->{path}{iso}{file_name}; print __LINE__."; [ Debug ] - output_file: [$output_file]\n" if $conf->{sys}{debug} >= 2; # Make sure that 'isolinux/isolinux.bin' is writable. my $isolinux_bin = $conf->{path}{iso}{source_directory}."/isolinux/isolinux.bin"; chmod 0644, $isolinux_bin; # Delete any existing ISO if (-e $output_file) { print "- Deleting old ISO: [$output_file]\n"; unlink $output_file or die __LINE__."; [ Error ] - Failed to delete old ISO: [$output_file]. The error was: $!\n"; } # Build! my $say_company = $conf->{sys}{ks}{company_name}; $say_company =~ s/!/\\\\\\/; my $say_anvil = $conf->{sys}{ks}{anvil_name}; $say_anvil =~ s/!/\\\\\\/; my $volume_id = "Anvil_".$conf->{sys}{name}{mark}."-v".$conf->{sys}{name}{version}; print __LINE__."; [ Debug ] - sys::name::version: [".$conf->{sys}{name}{version}."], volume_id: [$volume_id]\n" if $conf->{sys}{debug} >= 2; if ($conf->{sys}{name}{version} =~ /^v2/) { $volume_id = "Anvil_".$conf->{sys}{name}{mark}."-".$conf->{sys}{name}{version}; print __LINE__."; [ Debug ] - volume_id: [$volume_id]\n" if $conf->{sys}{debug} >= 2; } my $shell_call = $conf->{executable}{mkisofs}." -rock -joliet-long -translation-table -no-emul-boot -boot-load-size 4 -boot-info-table -V '".$volume_id."' -appid '".$say_anvil." ".$conf->{sys}{name}{mark}." v".$conf->{sys}{name}{version}." Installer' -publisher \"$say_company - $conf->{sys}{ks}{support_url}\" -preparer 'Built by $say_user on $say_host at $say_date' -eltorito-boot isolinux/isolinux.bin -eltorito-catalog isolinux/boot.cat -x 'lost+found' -o $output_file ".$conf->{path}{iso}{source_directory}; my $mkisofs_output = ""; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; $mkisofs_output .= "$line\n"; } close $file_handle; my $sum = ""; if (-e $output_file) { # All done! print "- New ISO generated. Calculating sum...\n"; my $shell_call = $conf->{executable}{md5sum}." ".$output_file; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /^(\S+)\s+$output_file/) { $sum = $1; print __LINE__."; [ Debug ] - sum: [$sum]\n" if $conf->{sys}{debug} >= 2; } } close $file_handle; my $size = (stat($output_file))[7]; my $suffix = "MiB"; if ($size >= (2 ** 30)) { # Gibibyte $size = sprintf("%.2f", ($size /= (2 ** 30))); $suffix = "GiB"; } else { # Mebibyte $size = sprintf("%.2f", ($size /= (2 ** 20))); } clean_up($conf); print "- All done! The ISO file: [$output_file] has been generated! - The size is: [$size $suffix] and the md5sum is: [$sum] "; } else { # So close! print " [ Error ] - It appears that the ISO: [$output_file] failed to build! The output [ Error ] from the build follows: ==== $mkisofs_output ==== [ Error ] - You can re-run this program with '-v', '-vv' or '-vvv' to [ Error ] additional debugging information. "; clean_up($conf); exit(11); } return(0); } # Unmounts the fuseiso mounted ISOs, if needed. sub clean_up { my ($conf) = @_; if (-x $conf->{executable}{fusermount}) { foreach my $mount_point (sort {$a cmp $b} @{$conf->{sys}{fuse_mounted}}) { my $shell_call = $conf->{executable}{fusermount}." -u ".$mount_point; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; } } else { print __LINE__."; [ Debug ] - fusermount: [".$conf->{executable}{fusermount}."] not found, skipping clean-up.\n" if $conf->{sys}{debug} >= 2; } return(0); } # This builds the repodata for the Packages directory. sub build_repository { my ($conf, $disc_id, $xml_file1, $xml_file2) = @_; my $shell_call = $conf->{executable}{createrepo}." -u media://".$disc_id." -g ".$xml_file1." ".$conf->{path}{iso}{source_directory}; if ($xml_file2) { $shell_call = $conf->{executable}{createrepo}." -u media://".$disc_id." -g ".$xml_file1." -g ".$xml_file2." ".$conf->{path}{iso}{source_directory}; } print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /Error opening Package/) { print " [ Error ] - There was an error when generating the 'Packages' repository data. [ Error ] It is possible some downloaded or copied 'RPM' package files are [ Error ] corrupt. Check that your Internet connection is working. If the [ Error ] error is not obvious, re-run this with '-vv' for debugging output. "; clean_up($conf); exit(10); } } close $file_handle; return(0); } # Walk through 'repodata', clear out old files and get the name of the comps.xml file. sub get_repodata_xml_file_name { my ($conf) = @_; my $xml_file1 = ""; my $xml_file2 = ""; my $source = $conf->{path}{iso}{source_directory}."/repodata"; print __LINE__."; [ Debug ] - source: [$source]\n" if $conf->{sys}{debug} >= 1; if (-e $source) { print __LINE__."; [ Debug ] - source: [$source]\n" if $conf->{sys}{debug} >= 1; local(*DIR); opendir(DIR, $source) or die __LINE__."; Failed to open the directory: [$source], error was: $!\n"; while (my $file = readdir(DIR)) { next if (($file eq ".") or ($file eq "..")); my $full_path = $source."/".$file; print __LINE__."; [ Debug ] - full_path: [$full_path]\n" if $conf->{sys}{debug} >= 1; #rm -f repodata/*.gz repodata/*.*.bz2 repodata/*.repomd.xml if ($file =~ /Server\.x86_64\.xml$/) { $xml_file2 = $full_path; } elsif ($file =~ /c6-x86_64-comps\.xml$/) { $xml_file2 = $full_path; } elsif (($file =~ /\.gz$/) or ($file =~ /\.bz2$/) or ($file =~ /\.repomd\.xml$/)) { print __LINE__."; [ Debug ] - Deleting: [$full_path]\n" if $conf->{sys}{debug} >= 1; unlink $full_path or die __LINE__."; [ Error ] - Failed to delete old 'repodata' file: [$full_path]. The error was: $!\n"; } elsif ($file =~ /comps\.xml$/) { $xml_file1 = $full_path; print __LINE__."; [ Debug ] - xml_file: [$xml_file1]\n" if $conf->{sys}{debug} >= 1; } elsif ($file =~ /repomd\.xml$/) { $xml_file1 = $full_path; print __LINE__."; [ Debug ] - xml_file: [$xml_file1]\n" if $conf->{sys}{debug} >= 1; } else { print __LINE__."; [ Debug ] - Ignoring: [$full_path]\n" if $conf->{sys}{debug} >= 1; } } closedir(DIR); } if (not $xml_file1) { print "[ Error ] - Failed to find the XML file used by 'createrepo' on the source ISO.\n"; clean_up($conf); exit(13); } print __LINE__."; [ Debug ] - xml_file1: [$xml_file1], xml_file2: [$xml_file2]\n" if $conf->{sys}{debug} >= 1; return($xml_file1, $xml_file2); } # This blows away all files in the source's 'repodata' directory. sub wipe_repodata { my ($conf) = @_; my $source = $conf->{path}{iso}{source_directory}."/repodata"; if (-e $source) { print __LINE__."; [ Debug ] - source: [$source]\n" if $conf->{sys}{debug} >= 2; local(*DIR); opendir(DIR, $source) or die __LINE__."; Failed to open the directory: [$source], error was: $!\n"; while (my $file = readdir(DIR)) { next if (($file eq ".") or ($file eq "..")); my $full_path = $source."/".$file; print __LINE__."; [ Debug ] - full_path: [$full_path]\n" if $conf->{sys}{debug} >= 2; print __LINE__."; [ Debug ] - Deleting: [$full_path]\n" if $conf->{sys}{debug} >= 2; unlink $full_path or die __LINE__."; [ Error ] - Failed to delete old 'repodata' file: [$full_path].\n"; } closedir(DIR); } return(0); } # This reads in the ISO disk 1's .discinfo file. sub get_disc_id { my ($conf) = @_; my $disc_id = ""; my $disc_info_file = $conf->{path}{iso}{source_directory}."/.discinfo"; print __LINE__."; [ Debug ] - disc_info_file: [$disc_info_file]\n" if $conf->{sys}{debug} >= 2; if (-e $disc_info_file) { my $shell_call = $disc_info_file; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "<$shell_call") or die __LINE__."; \n[ Error ] - Failed to read: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ((not $disc_id) && ($line =~ /^\d+/)) { $disc_id = $line; print __LINE__."; [ Debug ] - disc_id: [$disc_id]\n" if $conf->{sys}{debug} >= 2; } } close $file_handle; } else { # We might be able to generate this ourself later, but for now, error out. print " [ Error ] - The source ISO (disc 1) should have contained '.discinfo' in its [ Error ] root, but it either doesn't exist or it wasn't copied the the [ Error ] 'source' build directory. This is needed to generate the new ISO. "; clean_up($conf); exit(9); } return($disc_id); } # This date is used in the ISO sub get_date { my ($conf) = @_; my %time = (); ($time{sec}, $time{min}, $time{hour}, $time{mday}, $time{mon}, $time{year}, $time{wday}, $time{yday}, $time{isdst}) = localtime(); # Adjust the year and month $time{year} = ($time{year} + 1900); $time{mon}++; # 0-pad the numbers $time{pad_hour} = sprintf("%02d", $time{hour}); $time{pad_min} = sprintf("%02d", $time{min}); $time{pad_sec} = sprintf("%02d", $time{sec}); $time{pad_mon} = sprintf("%02d", $time{mon}); $time{pad_mday} = sprintf("%02d", $time{mday}); $conf->{sys}{date} = $time{year}."/".$time{pad_mon}."/".$time{pad_mday}; $conf->{sys}{'time'} = $time{pad_hour}.":".$time{pad_min}.":".$time{pad_sec}; print __LINE__."; [ Debug ] - sys::date: [".$conf->{sys}{date}."], sys::time: [".$conf->{sys}{'time'}."]\n" if $conf->{sys}{debug} >= 2; return(0); } # This downloads the latest version of striker. sub download_striker { my ($conf) = @_; # The github source file is simply 'master', so we save it as the 'url::striker_zip_file'. my $out_directory = $conf->{path}{iso}{working_directory}; my $source_directory = $conf->{path}{iso}{source_directory}; my $download_default = $conf->{url}{striker_default}; my $download_master = $conf->{url}{striker_master}; my $download = $download_default; my $file = $conf->{url}{striker_zip_file}; my $out_file = $out_directory."/".$file; my $source_name = $source_directory."/striker-master"; my $target_name = $source_directory."/striker"; my $striker_latest = $conf->{url}{striker_latest}; my $os_type = $conf->{sys}{os_type}; if ($conf->{sys}{debug} >= 2) { print __LINE__."; [ Debug ] - out_directory: .. [$out_directory]\n"; print __LINE__."; [ Debug ] - source_directory: [$source_directory]\n"; print __LINE__."; [ Debug ] - download_default: [$download_default]\n"; print __LINE__."; [ Debug ] - download_master: [$download_master]\n"; print __LINE__."; [ Debug ] - download: ....... [$download]\n"; print __LINE__."; [ Debug ] - file: ........... [$file]\n"; print __LINE__."; [ Debug ] - out_file: ....... [$out_file]\n"; print __LINE__."; [ Debug ] - source_name: .... [$source_name]\n"; print __LINE__."; [ Debug ] - target_name: .... [$target_name]\n"; print __LINE__."; [ Debug ] - striker_latest: . [$striker_latest]\n"; } # If I have an internet connection (and I really should), try to get the latest version from the # Alteeve site. if ($conf->{switches}{master}) { print __LINE__."; [ Debug ] - Download: [$download] as: [$out_file]\n" if $conf->{sys}{debug} >= 2; $download = $download_master; # We'll set the system version to 'master-' and update the ISO name. $conf->{sys}{name}{version} = "master_".$conf->{sys}{date}; $conf->{sys}{name}{version} =~ s/\//-/g; $conf->{path}{iso}{file_name} = "Anvil_".$conf->{sys}{name}{mark}."-".$conf->{sys}{name}{version}; if ($conf->{sys}{name}{suffix}) { $conf->{path}{iso}{file_name} .= "-".$conf->{sys}{name}{suffix}; } $conf->{path}{iso}{file_name} .= "_".$conf->{sys}{name}{$os_type}."-".$conf->{sys}{name}{release}.".iso"; print __LINE__."; [ Debug ] - download: [$download], sys::name::version: [".$conf->{sys}{name}{version}."], path::iso::file_name: [".$conf->{path}{iso}{file_name}."]\n" if $conf->{sys}{debug} >= 2; } elsif ($conf->{switches}{release}) { # print "- Specific release requested: [".$conf->{switches}{release}."]\n"; $download = "https://github.com/ClusterLabs/striker/archive/v".$conf->{switches}{release}.".zip"; print __LINE__."; [ Debug ] - download: [$download]\n" if $conf->{sys}{debug} >= 2; } else { # Try to get the latest version from the striker_latest file on Alteeve. my $shell_call = $conf->{executable}{curl}." --stderr /dev/null $striker_latest"; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /^striker:(http.*)$/) { $download = $1; print __LINE__."; [ Debug ] - download: [$download]\n" if $conf->{sys}{debug} >= 2; } } close $file_handle; } # Set the version based on the output version. if ($download =~ /.*\/(.*)\.zip$/) { $conf->{sys}{name}{version} = $1; print __LINE__."; [ Debug ] - sys::name::version: [".$conf->{sys}{name}{version}."]\n" if $conf->{sys}{debug} >= 2; $source_name = $source_directory."/striker-".$conf->{sys}{name}{version}; print __LINE__."; [ Debug ] - source_name: [$source_name]\n" if $conf->{sys}{debug} >= 2; $conf->{path}{iso}{file_name} = "Anvil_".$conf->{sys}{name}{mark}."-v".$conf->{sys}{name}{version}; print __LINE__."; [ Debug ] - path::iso::file_name: [".$conf->{path}{iso}{file_name}."]\n" if $conf->{sys}{debug} >= 2; if ($conf->{sys}{name}{version} =~ /^v/) { $conf->{path}{iso}{file_name} = "Anvil_".$conf->{sys}{name}{mark}."-".$conf->{sys}{name}{version}; print __LINE__."; [ Debug ] - path::iso::file_name: [".$conf->{path}{iso}{file_name}."]\n" if $conf->{sys}{debug} >= 2; } if ($conf->{sys}{name}{suffix}) { $conf->{path}{iso}{file_name} .= "-".$conf->{sys}{name}{suffix}; print __LINE__."; [ Debug ] - path::iso::file_name: [".$conf->{path}{iso}{file_name}."]\n" if $conf->{sys}{debug} >= 2; } $conf->{path}{iso}{file_name} .= "_".$conf->{sys}{name}{$os_type}."-".$conf->{sys}{name}{release}.".iso"; print __LINE__."; [ Debug ] - source_name: [$source_name], sys::name::version: [".$conf->{sys}{name}{version}."], path::iso::file_name: [".$conf->{path}{iso}{file_name}."]\n" if $conf->{sys}{debug} >= 2; # If the source doesn't exist, try again without a preceding 'v' on the version number. if ((not -e $source_name) && ($conf->{sys}{name}{version} =~ /^v2/)) { my $source_version = ($conf->{sys}{name}{version} =~ /^v(.*)$/)[0]; print __LINE__."; [ Debug ] - source_version: [$source_version]\n" if $conf->{sys}{debug} >= 2; $source_name = $source_directory."/striker-".$source_version; print __LINE__."; [ Debug ] - source_name: [$source_name]\n" if $conf->{sys}{debug} >= 2; } } if (-e $out_file) { print "- Striker source already downloaded.\n"; if ($conf->{switches}{'no-refresh'}) { print "- Refresh disabled, using the existing copy.\n"; } else { print "- Removing the old copy and downloading the latest version.\n"; unlink $out_file or die __LINE__."; [ Error ] - Failed to delete: [$out_file] to make room for the new file. The error was: $!\n"; print __LINE__."; [ Debug ] - About to download: [$download] to: [$out_file]\n" if $conf->{sys}{debug} >= 2; download_file($conf, $download, $out_file, 0644); } } else { print "- Downloading the Striker source code.\n"; print "- This might take a few minutes on slow connections, please be patient.\n"; print __LINE__."; [ Debug ] - About to download: [$download] to: [$out_file]\n" if $conf->{sys}{debug} >= 2; download_file($conf, $download, $out_file, 0644); } # Do nothing more if '--no-refresh' set. if ($conf->{switches}{'no-refresh'}) { return(0); } # Unzip the source file. print "- Extracting the Striker source file.\n"; my $shell_call = $conf->{executable}{unzip}." -uo $out_file -d $source_directory"; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 3; } close $file_handle; # Remove the old source directory. if ((-e $target_name) && ($target_name =~ /\/striker$/)) { print "- Removing old Striker from source.\n"; my $shell_call = $conf->{executable}{rm}." -rf $target_name"; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; } # Rename the extracted directory rename $source_name, $target_name or die "$THIS_FILE ".__LINE__."; Failed to rename: [$source_name] to: [$target_name]. The error was: $!\n"; # Download RPMs from our repo. download_anrepo_rpms($conf); # Download non-RPM tools download_anrepo_tools($conf); # Now start copying files into place. copy_files_from_source($conf); # Generate kickstart files. generate_kickstart_files($conf); # Check that EFI is setup. if (1) { print "- Auxiliary files now in place, ready to grab Anvil! files.\n"; print "- Verifying 'EFI' is setup.\n"; my $os_type = $conf->{sys}{os_type}; my $distro_directory = $conf->{path}{zip}{$os_type}; my $source_bootx64_conf = $conf->{path}{iso}{source_directory}."/".$conf->{path}{iso}{striker}."/".$conf->{path}{iso}{zip_iso}."/".$distro_directory."/".$conf->{path}{zip}{suffix}."/EFI/BOOT/BOOTX64.conf"; my $target_bootx64_conf = $conf->{path}{iso}{source_directory}."/EFI/BOOT/BOOTX64.conf"; my $source_background = $conf->{path}{iso}{source_directory}."/".$conf->{path}{iso}{striker}."/".$conf->{path}{iso}{zip_iso}."/".$distro_directory."/".$conf->{path}{zip}{suffix}."/EFI/BOOT/pxe_voyager1.xpm.gz"; my $target_background = $conf->{path}{iso}{source_directory}."/EFI/BOOT/pxe_voyager1.xpm.gz"; my $target_bootx64_efi = $conf->{path}{iso}{source_directory}."/EFI/BOOT/BOOTX64.efi"; print __LINE__."; [ Debug ] - path::iso::source_directory: [".$conf->{path}{iso}{source_directory}."] - path::iso::striker: ........ [".$conf->{path}{iso}{striker}."] - path::iso::zip_iso: ........ [".$conf->{path}{iso}{zip_iso}."] - distro_directory: .......... [".$distro_directory."] - path::zip::suffix: ......... [".$conf->{path}{zip}{suffix}."] - source_bootx64_conf: ....... [".$source_bootx64_conf."] - target_bootx64_conf: ....... [".$target_bootx64_conf."] - source_background: ......... [".$source_background."] - target_background: ......... [".$target_background."] " if $conf->{sys}{debug} >= 2; copy_file($conf, $source_bootx64_conf, $target_bootx64_conf, 1); copy_file($conf, $source_background, $target_background, 1); # Make sure the BOOTX64.efi and vmlinuz files are set to 755. chmod "0755", $target_bootx64_efi; } return(0); } # This copies files extracted from the striker source zip file. sub copy_files_from_source { my ($conf) = @_; # I need the OS-specific source. my $os_type = $conf->{sys}{os_type}; my $distro_directory = $conf->{path}{zip}{$os_type}; my $source_on_iso = $conf->{path}{iso}{source_directory}."/".$conf->{path}{iso}{striker}."/".$conf->{path}{iso}{zip_iso}."/".$distro_directory."/".$conf->{path}{zip}{suffix}; if ($conf->{sys}{debug} >= 2) { print __LINE__."; [ Debug ] - path::iso::source_directory: [".$conf->{path}{iso}{source_directory}."]\n"; print __LINE__."; [ Debug ] - path::iso::striker: ........ [".$conf->{path}{iso}{striker}."]\n"; print __LINE__."; [ Debug ] - path::iso::zip_iso: ........ [".$conf->{path}{iso}{zip_iso}."]\n"; print __LINE__."; [ Debug ] - distro_directory: .......... [$distro_directory]\n"; #print __LINE__."; [ Debug ] - path::iso::suffix: ......... [".$conf->{path}{iso}{suffix}."]\n"; print __LINE__."; [ Debug ] - source_on_iso: ............. [$source_on_iso]\n"; } # isolinux foreach my $directory (sort {$a cmp $b} keys %{$conf->{path}{iso}{zip_copy}}) { print __LINE__."; [ Debug ] - directory: [$directory], path::iso::zip_copy::${directory}::target: [".$conf->{path}{iso}{zip_copy}{$directory}{target}."]\n" if $conf->{sys}{debug} >= 2; my $source = $source_on_iso."/".$directory; my $target = make_directory($conf, $conf->{path}{iso}{source_directory}."/".$conf->{path}{iso}{zip_copy}{$directory}{target}); print __LINE__."; [ Debug ] - source: [$source], target: [$target]\n" if $conf->{sys}{debug} >= 2; $source =~ s/\/\/+/\//g; #$target =~ s/\/\/+/\//g; print __LINE__."; [ Debug ] - source: [$source], target: [$target]\n" if $conf->{sys}{debug} >= 2; local(*DIR); opendir(DIR, $source) or die __LINE__."; Failed to open the directory: [$source], error was: $!\n"; while (my $file = readdir(DIR)) { next if (($file eq ".") or ($file eq "..")); my $source_file = $source."/".$file; my $target_file = $target."/".$file; print __LINE__."; [ Debug ] - source_file: [$source_file], target_file: [$target_file]\n" if $conf->{sys}{debug} >= 2; copy_file($conf, $source_file, $target_file, 1); } closedir(DIR); } return(0); } # This copies a file from the given ISO file using 'iso-read' sub isoread_file { my ($conf, $iso, $source, $target) = @_; print __LINE__."; [ Debug ] - iso: [$iso], source: [$source], target: [$target]\n" if $conf->{sys}{debug} >= 2; if (-e $target) { print "- The target file: [$target] exists, no need to copy it.\n" if $conf->{sys}{debug} >= 2; } else { my $shell_call = $conf->{executable}{'iso-read'}." --image $iso --extract $source --output-file $target"; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; if (-e $target) { print "- The file: [$target] was written successfully.\n" if $conf->{sys}{debug} >= 1; } else { print "[ Error ] - Failed to copy: [$source]\n"; print "[ Error ] from: [$iso]\n"; print "[ Error ] to: [$target]\n"; print "[ Error ] - No error was reported, but the target file doesn't exist.\n"; clean_up($conf); exit(5); } } return(0); } # This handles copying a file. Fancy, I know. sub copy_file { my ($conf, $source, $target, $force) = @_; print __LINE__."; [ Debug ] - source: [$source], target: [$target], force: [$force]\n" if $conf->{sys}{debug} >= 2; # Delete the target if 'force'd and target exists. if (($force) && (-e $target)) { print "- The target file: [$target] exists and the copy is forced, deleting the old file.\n" if $conf->{sys}{debug} >= 2; unlink $target or warn __LINE__."; [ Warning ] - Failed to delete: [$target] to make room for the new file. The error was: [".$!."]. Will try to proceed...\n"; } # Copy unless the target exists. if (-e $target) { print "- The target file: [$target] exists, no need to copy it.\n" if $conf->{sys}{debug} >= 1; } else { # Copy it. print "- Copying the source file: [$source] to: [$target]\n" if $conf->{sys}{debug} >= 2; my $shell_call = $conf->{executable}{cp}." -av $source $target"; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 3; } close $file_handle; if (-e $target) { # Get the size of the file and the source, make sure they match. my $source_size = (stat($source))[7]; my $target_size = (stat($target))[7]; print __LINE__."; [ Debug ] - source_size: [$source_size], target_size: [$target_size], force: [$force]\n" if $conf->{sys}{debug} >= 3; if ($source_size ne $target_size) { print "[ Error ] - The file: [$target] files is not the same size as the source!\n"; print "[ Error ] - Source size (in bytes): [$source_size]\n"; print "[ Error ] - Target size (in bytes): [$target_size]\n"; clean_up($conf); exit(12); } elsif (not $target_size) { print "[ Note ] - The file: [$target] was written successfully, but it and the source are empty files.\n" if $conf->{sys}{debug} >= 1; } else { print "- The file: [$target] was written successfully.\n" if $conf->{sys}{debug} >= 1; } } else { print "[ Error ] - Failed to copy: [$target]!\n"; print "[ Error ] - Tried to copy via: [$shell_call]\n"; print "[ Error ] - No error was reported in the copy operation but the target file doesn't exist.\n"; clean_up($conf); exit(5); } } return(0); } # This asks the user to agree to terms of use for third party applications. sub agree_to_terms { my ($conf) = @_; system('clear'); print "-=] Thank you for using the 'Anvil!' Intelligent Availability platform! [=-\n\n"; ### We don't prompt the user anymore. $conf->{sys}{third_party}{Fujitsu}{agreed} = 1; $conf->{sys}{third_party}{ASIX}{agreed} = 1; $conf->{sys}{third_party}{Avago}{agreed} = 1; return(0); if ($conf->{switches}{'accept-third-party-licenses'}) { print "- Third-party licenses agreed via '--accept-third-party-licenses'.\n"; $conf->{sys}{third_party}{Fujitsu}{agreed} = 1; $conf->{sys}{third_party}{ASIX}{agreed} = 1; $conf->{sys}{third_party}{Avago}{agreed} = 1; } else { print "Before we begin, we need to address some important house-keeping. To simplify and automate as much of the build process as possible, the tool can download third party tools which add support for some hardware. These tools are not built, maintained or in any way owned by Alteeve's Niche! and have separate terms of service and use. You will be prompted now to read and accept or deny the terms of service for each third party vendor. If you decline any, the install will proceed but some features related to a given vendor may not work. Of course, if you do not have any hardware related to that vendor, it is safe to decline. In any case, the creation of the ISO will proceed. "; my $answer = ; $answer = ""; # Fujitsu. print "==== If you are using Fujitsu Primergy nodes, and if you agree to Fujitsu's 'Terms of use' found here: ".$conf->{sys}{third_party}{Fujitsu}{terms}." Support for 'Primecollect' will be added to the ISO. The 'Primecollect' tool is used to gather information about the hardware when opening a service request. Do you agree to Fujitsu's 'Terms of Use'? [y/N] "; $answer = ; chomp($answer); if ($answer =~ /^y/i) { print "- Thank you.\n\n"; $conf->{sys}{third_party}{Fujitsu}{agreed} = 1; } else { print "- Noted. Fujitsu support will not be added.\n\n"; } $answer = ""; sleep 1; # ASIX. print "==== If you are using an ASIX-based USB3 to Ethernet adapters (including Siig JU-NE0211 adapters), and if you agree to ASIX's 'Terms of Use' found here: ".$conf->{sys}{third_party}{ASIX}{terms}." Support for the ASIX-based ethernet adapters will be added and automatically configured on Striker dashboards. Do you agree to ASIX's 'Terms of Use'? [y/N] "; $answer = ; chomp($answer); if ($answer =~ /^y/i) { print "- Thank you.\n\n"; $conf->{sys}{third_party}{ASIX}{agreed} = 1; } else { print "- Noted. ASIX support will not be added.\n\n"; } $answer = ""; sleep 1; # Avago. print "==== If you are using an Avago (formerly LSI) based RAID controller, and if you agree to Avago's 'Download Agreement' found here: ".$conf->{sys}{third_party}{Avago}{terms}." Support for managing RAID arrays in Striker and monitoring of the controller hardware and attached drives will be added to ScanCore. LSI-based controllers are used by most Tier-1 vendors, like Fujitsu, Dell, IBM/Lenovo as well as most white-box servers from companies like Intel, Supermicro and so on. Do you agree to Avago's 'Download Agreement'? [y/N] "; $answer = ; chomp($answer); if ($answer =~ /^y/i) { print "- Thank you.\n\n"; $conf->{sys}{third_party}{Avago}{agreed} = 1; } else { print "- Noted. Avago support will not be added.\n\n"; } $answer = ""; sleep 1; print "====\n"; } return(0); } # This downloads various (third party) tools needed for the Anvil! that are not RPM packages. sub download_anrepo_tools { my ($conf) = @_; # Fujitsu stuff print "- Downloading non-RPM third party tools.\n"; print __LINE__."; [ Debug ] - sys::third_party::ASIX::agreed: [".$conf->{sys}{third_party}{ASIX}{agreed}."].\n" if $conf->{sys}{debug} >= 2; if ($conf->{sys}{third_party}{ASIX}{agreed}) { # ax8817[89] driver my $file_name = ($conf->{url}{ASIX}{ax8817x}{url} =~ /^.*\/(.*)$/)[0]; my $source = $conf->{url}{ASIX}{ax8817x}{url}; my $target = $conf->{path}{iso}{iso_asix}."/".$file_name; my $mode = $conf->{url}{ASIX}{ax8817x}{mode}; print __LINE__."; [ Debug ] - file_name: [$file_name], source: [$source], target: [$target].\n" if $conf->{sys}{debug} >= 2; download_file($conf, $source, $target, $mode); } print __LINE__."; [ Debug ] - sys::third_party::Fujitsu::agreed: [".$conf->{sys}{third_party}{Fujitsu}{agreed}."].\n" if $conf->{sys}{debug} >= 2; if ($conf->{sys}{third_party}{Fujitsu}{agreed}) { # Primecollect shar my $file_name = ($conf->{url}{Fujitsu}{primecollect}{url} =~ /^.*\/(.*)$/)[0]; my $source = $conf->{url}{Fujitsu}{primecollect}{url}; my $target = $conf->{path}{iso}{iso_fujitsu}."/".$file_name; my $mode = $conf->{url}{Fujitsu}{primecollect}{mode}; print __LINE__."; [ Debug ] - file_name: [$file_name], source: [$source], target: [$target].\n" if $conf->{sys}{debug} >= 2; download_file($conf, $source, $target, $mode); } # Delay fence agent if (1) { my $file_name = ($conf->{url}{Tools}{fence_delay}{url} =~ /^.*\/(.*)$/)[0]; my $source = $conf->{url}{Tools}{fence_delay}{url}; my $target = $conf->{path}{iso}{iso_fence}."/".$file_name; my $mode = $conf->{url}{Tools}{fence_delay}{mode}; print __LINE__."; [ Debug ] - file_name: [$file_name], source: [$source], target: [$target].\n" if $conf->{sys}{debug} >= 2; download_file($conf, $source, $target, $mode); } # Alteeve-variant APC agent. if (1) { my $file_name = ($conf->{url}{Tools}{fence_apc_alteeve}{url} =~ /^.*\/(.*)$/)[0]; my $source = $conf->{url}{Tools}{fence_apc_alteeve}{url}; my $target = $conf->{path}{iso}{iso_fence}."/".$file_name; my $mode = $conf->{url}{Tools}{fence_apc_alteeve}{mode}; print __LINE__."; [ Debug ] - file_name: [$file_name], source: [$source], target: [$target].\n" if $conf->{sys}{debug} >= 2; download_file($conf, $source, $target, $mode); } # Raritan Fence agent. if (1) { my $file_name = ($conf->{url}{Tools}{fence_raritan_snmp}{url} =~ /^.*\/(.*)$/)[0]; my $source = $conf->{url}{Tools}{fence_raritan_snmp}{url}; my $target = $conf->{path}{iso}{iso_fence}."/".$file_name; my $mode = $conf->{url}{Tools}{fence_raritan_snmp}{mode}; print __LINE__."; [ Debug ] - file_name: [$file_name], source: [$source], target: [$target].\n" if $conf->{sys}{debug} >= 2; download_file($conf, $source, $target, $mode); } return(0); } # This downloads the give file, if it is not already downloaded, and verifies that it is not a curl failure # message. sub download_file { my ($conf, $source, $target, $mode) = @_; $mode = "" if not defined $mode; if (-e $target) { print "- The file: [$target] is already downloaded.\n" if $conf->{sys}{debug} >= 2; } else { # Get the file size so that we can warn the user my $size = ""; my $shell_call = $conf->{executable}{curl}." --head $source"; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; $line =~ s/\r//; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 3; if ($line =~ /Content-Length: (\d+)$/) { $size = $1; print __LINE__."; [ Debug ] - size: [$size]\n" if $conf->{sys}{debug} >= 2; } } close $file_handle; # If I got a size, make it (crudely) human readable. my $say_size = ""; if ($size =~ /^\d+$/) { $say_size = hr_size($conf, $size); } my $start_time = time; print "- Downloading: [$source] to: [$target], which is: [$say_size]... "; print "\n" if $conf->{sys}{debug} >= 1; ### NOTE: Do NOT use 2>&1 as this pushes the download progress into the download file. If ### debug is enabled, we'll dump the progress to the screen. $shell_call = $conf->{executable}{curl}." $source 2>/dev/null > $target"; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; if ($conf->{sys}{debug}) { $shell_call = $conf->{executable}{curl}." $source > $target"; } print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; my $file_ok = 0; if (-e $target) { # If the file is less than 1024 bytes, read it in to make sure it is not a # failure message. my $size = (stat($target))[7]; print __LINE__."; [ Debug ] - Downloaded file: [$target] is: [$size] bytes.\n" if $conf->{sys}{debug} >= 1; if ($size < 1024) { # Is it binary or text? If it is text, make sure it is not a failure message. if (not $size) { # File is empty. print "\n" if not $conf->{sys}{debug}; print "[ Error ] - Failed to download: [$target].\n"; print "[ Error ] The downloaded file was 0-bytes.\n"; print "[ Error ] Is the source: [$source] accessible?\n"; } elsif (-B $target) { # Yay! $file_ok = 1; print __LINE__."; [ Debug ] - Downloaded file: [$target] is binary, good.\n" if $conf->{sys}{debug} >= 1; } else { # Read it in. my $shell_call = $target; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "<$shell_call") or die __LINE__."; \n[ Error ] - Failed to read: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /Could not resolve host/i) { # Bad URL print "\n" if not $conf->{sys}{debug}; print "[ Error ] - Failed to download: [$target].\n"; print "[ Error ] It appears that the domain name is invalid.\n"; } if ($line =~ /404 Not Found<\/title>/i) { # File not found. print "\n" if not $conf->{sys}{debug}; print "[ Error ] - Failed to download: [$target].\n"; print "[ Error ] It appears that the file was not found on the server.\n"; } if ($line =~ /<a href="(.*?)">redirected<\/a>/) { # Redirect my $new_source = $1; # Cleanup close $file_handle; unlink $target or die __LINE__."; [ Error ] - Failed to delete: [$target] (it is a failed download message, not the file). The error was: $!\n"; # Download the new source print "Redirected.\n"; print __LINE__."; [ Debug ] - Redirected source: [$source]: to: [$new_source].\n" if $conf->{sys}{debug} >= 1; download_file($conf, $new_source, $target, $mode); return(0); } } close $file_handle; } } else { # Too big to be a failure, so we're probably good. $file_ok = 1; print __LINE__."; [ Debug ] - Downloaded file: [$target] is large enough that analysis is not required.\n" if $conf->{sys}{debug} >= 1; } } print __LINE__."; [ Debug ] - target: [$target], file_ok: [$file_ok]\n" if $conf->{sys}{debug} >= 1; if ($file_ok) { my $duration = time - $start_time; print "Finished! Took: [$duration] second(s).\n"; } else { # Sadness. print "\n" if not $conf->{sys}{debug}; print "[ Error ] - There was a problem downloading: [$source] to: [$target]\n"; if (-e $target) { # Remove the sadness print "[ Note ] - Failed download target: [$target] will now be removed.\n"; unlink $target or die __LINE__."; [ Error ] - Failed to delete: [$target] (it is a failed download message, not the file). The error was: $!\n"; } clean_up($conf); exit(7); } } # Set the mode, if needed if ($mode) { chmod $mode, $target; } return(0); } # This converts a raw number of bytes to a human readable size. It always uses base-2. sub hr_size { my ($conf, $hr_size) = @_; if ($hr_size >= (2 ** 80)) { # Yebibyte $hr_size = sprintf("%.3f", ($hr_size /= (2 ** 80)))." YiB"; } elsif ($hr_size >= (2 ** 70)) { # Zebibyte $hr_size = sprintf("%.3f", ($hr_size /= (2 ** 70)))." ZiB"; } elsif ($hr_size >= (2 ** 60)) { # Exbibyte $hr_size = sprintf("%.3f", ($hr_size /= (2 ** 60)))." EiB"; } elsif ($hr_size >= (2 ** 50)) { # Pebibyte $hr_size = sprintf("%.3f", ($hr_size /= (2 ** 50)))." PiB"; } elsif ($hr_size >= (2 ** 40)) { # Tebibyte $hr_size = sprintf("%.2f", ($hr_size /= (2 ** 40)))." TiB"; } elsif ($hr_size >= (2 ** 30)) { # Gibibyte $hr_size = sprintf("%.2f", ($hr_size /= (2 ** 30)))." GiB"; } elsif ($hr_size >= (2 ** 20)) { # Mebibyte $hr_size = sprintf("%.2f", ($hr_size /= (2 ** 20)))." MiB"; } elsif ($hr_size >= (2 ** 10)) { # Kibibyte $hr_size = sprintf("%.1f", ($hr_size /= (2 ** 10)))." KiB"; } else { $hr_size .= " Bytes"; } return($hr_size); } # This curl's the 'x86_64' and 'noarch' directories in the 'url::an-repo' URL. Then it looks through the RPMs # in out package list to find matches and downloads them. sub download_anrepo_rpms { my ($conf) = @_; # First, get a list of the RPMs available print "- Downloading packages from the Alteeve's Niche! repository now.\n"; print "- If your connection is slow, this might take a few minutes. Please be patient.\n"; foreach my $arch ("x86_64", "noarch") { my $shell_call = $conf->{executable}{curl}." ".$conf->{url}{'an-repo'}."/$arch/"; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 3; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 3; if ($line =~ /href="(.*?)">/) { my $download_package = $1; next if $download_package !~ /\.rpm$/; my $download_url = $conf->{url}{'an-repo'}."/$arch/$download_package"; print __LINE__."; [ Debug ] - arch: [$arch], download_package: [$download_package], download_url: [$download_url]\n" if $conf->{sys}{debug} >= 3; $conf->{download_packages}{$download_package} = $download_url; } } close $file_handle; } # Match requirements to the available RPMs foreach my $package (sort {$a cmp $b} @{$conf->{sys}{packages}{alteeve}}) { my ($file, $arch) = ($package =~ /^(.*)\.(.*?)$/); my $found = 0; print __LINE__."; [ Debug ] - Looking for the: [$arch] package: [$package] in the source.\n" if $conf->{sys}{debug} >= 2; foreach my $download_package (sort {$a cmp $b} keys %{$conf->{download_packages}}) { next if $download_package !~ /^\Q$file\E/; next if $download_package !~ /\Q$arch\E/; print __LINE__."; [ Debug ] - Source file: [$download_package] POSSIBLE match...\n" if $conf->{sys}{debug} >= 2; if ($download_package =~ /^\Q$file\E-\d/) { print __LINE__."; [ Debug ] - Source file: [$download_package] matches!\n" if $conf->{sys}{debug} >= 2; $found++; $conf->{download}{$package}{url} = $conf->{download_packages}{$download_package}; } } ### It is normal for there to be multiple matches, so we don't fail on too many here. # If I didn't finf a match, or if I found too many matches, die. die __LINE__."; [ Error ] - Failed to find: [$package] (file: [$file], arch: [$arch])! Is the Internet connection working?\n" if not $found; } # Find the packages in the Third Party repo foreach my $vendor (sort {$a cmp $b} keys %{$conf->{sys}{packages}{third_party}}) { print __LINE__."; [ Debug ] - vendor: [$vendor], sys::third_party::${vendor}::agreed: [".$conf->{sys}{third_party}{$vendor}{agreed}."]\n" if $conf->{sys}{debug} >= 3; next if not $conf->{sys}{third_party}{$vendor}{agreed}; my $shell_call = $conf->{executable}{curl}." ".$conf->{url}{third_party}."/$vendor/"; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 3; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 3; if ($line =~ /href="(.*?)">/) { my $download_package = $1; next if $download_package !~ /\.rpm$/; my $download_url = $conf->{url}{third_party}."/$vendor/$download_package"; print __LINE__."; [ Debug ] - download_package: [$download_package], download_url: [$download_url]\n" if $conf->{sys}{debug} >= 3; $conf->{download_packages}{$download_package} = $download_url; } } } # Match our requirements to the packages available in the third party repo foreach my $vendor (sort {$a cmp $b} keys %{$conf->{sys}{packages}{third_party}}) { print __LINE__."; [ Debug ] - vendor: [$vendor], sys::third_party::${vendor}::agreed: [".$conf->{sys}{third_party}{$vendor}{agreed}."]\n" if $conf->{sys}{debug} >= 3; next if not $conf->{sys}{third_party}{$vendor}{agreed}; foreach my $package (sort {$a cmp $b} @{$conf->{sys}{packages}{third_party}{$vendor}}) { my ($file, $arch) = ($package =~ /^(.*)\.(.*?)$/); my $found = 0; print __LINE__."; [ Debug ] - Looking for the: [$arch] package: [$package] in the source.\n" if $conf->{sys}{debug} >= 3; foreach my $download_package (sort {$a cmp $b} keys %{$conf->{download_packages}}) { next if $download_package !~ /^\Q$file\E/; next if $download_package !~ /\Q$arch\E/; print __LINE__."; [ Debug ] - Source file: [$download_package] POSSIBLE match...\n" if $conf->{sys}{debug} >= 3; if ($download_package =~ /^\Q$file\E-\d/) { print __LINE__."; [ Debug ] - Source file: [$download_package] matches!\n" if $conf->{sys}{debug} >= 3; $found++; $conf->{download}{$package}{url} = $conf->{download_packages}{$download_package}; } } ### It is normal for there to be multiple matches, so we don't fail on too many here. # If I didn't finf a match, or if I found too many matches, die. die __LINE__."; [ Error ] - Failed to find: [$package] (file: [$file], arch: [$arch])! Is the Internet connection working?\n" if not $found; } } # Download everything foreach my $package (sort {$a cmp $b} keys %{$conf->{download}}) { my $package_name = ($conf->{download}{$package}{url} =~ /^.*\/(.*)$/)[0]; my $package_file = $conf->{path}{iso}{iso_packages}."/".$package_name; if (-e $package_file) { print "- The package: [$package_file] is already downloaded.\n" if $conf->{sys}{debug} >= 3; } else { download_file($conf, $conf->{download}{$package}{url}, $package_file); } } print "- Done! All Anvil! packages are in place.\n"; return(0); } # Setup our build environment. This creates the directories we'll need for the new ISO, copy over isolinux # and syslinux, ask the user to download binary files, load our kickstart scripts and splash screens, etc. sub setup_build_environment { my ($conf) = @_; # Which OS? my $hash_key = $conf->{sys}{os_type}."_dir"; $conf->{path}{iso}{working_directory} = $conf->{sys}{user}{current_dir}."/".$conf->{path}{working_dir}."/".$conf->{path}{$hash_key}; if (-e $conf->{path}{iso}{working_directory}) { print "- The working directory: [".$conf->{path}{iso}{working_directory}."] already exists.\n"; } else { print "- The working directory: [".$conf->{path}{iso}{working_directory}."] doesn't exist.\n"; print "- Can I create it (and needed subdirectories) now? [Y/n] "; if ($conf->{switches}{y}) { print "\n- Permission given via '-y', proceeding.\n"; } else { my $answer = <STDIN>; chomp($answer); if ((not $answer) or ($answer =~ /^y/i)) { print "- Thank you, proceeding now.\n"; } else { print "- Without a working directory, ISO generation can not proceed. Exiting.\n"; clean_up($conf); exit(8); } } $conf->{path}{iso}{working_directory} = make_directory($conf, $conf->{path}{iso}{working_directory}); } # Make sure the rest of the directories now exist. $conf->{path}{iso}{source_directory} = make_directory($conf, $conf->{path}{iso}{working_directory}."/".$conf->{path}{source_path}); $conf->{path}{iso}{iso_tools} = make_directory($conf, $conf->{path}{iso}{source_directory}."/".$conf->{path}{iso}{tools}); $conf->{path}{iso}{iso_kickstart} = make_directory($conf, $conf->{path}{iso}{source_directory}."/".$conf->{path}{iso}{kickstart}); $conf->{path}{iso}{iso_packages} = make_directory($conf, $conf->{path}{iso}{source_directory}."/".$conf->{path}{iso}{packages}); $conf->{path}{iso}{iso_striker} = make_directory($conf, $conf->{path}{iso}{iso_tools}."/".$conf->{path}{iso}{striker}); $conf->{path}{iso}{iso_fence} = make_directory($conf, $conf->{path}{iso}{iso_tools}."/".$conf->{path}{iso}{fence}); # These are only created if the user agreed to them $conf->{path}{iso}{iso_fujitsu} = make_directory($conf, $conf->{path}{iso}{iso_tools}."/".$conf->{path}{iso}{fujitsu}) if $conf->{sys}{third_party}{Fujitsu}{agreed}; $conf->{path}{iso}{iso_asix} = make_directory($conf, $conf->{path}{iso}{iso_tools}."/".$conf->{path}{iso}{asix}) if $conf->{sys}{third_party}{ASIX}{agreed}; $conf->{path}{iso}{iso_avago} = make_directory($conf, $conf->{path}{iso}{iso_tools}."/".$conf->{path}{iso}{avago}) if $conf->{sys}{third_party}{Avago}{agreed}; if ($conf->{sys}{debug} >= 2) { print __LINE__."; [ Debug ] - path::source_path: .......... [".$conf->{path}{source_path}."]\n"; print __LINE__."; [ Debug ] - path::iso::working_directory: [".$conf->{path}{iso}{working_directory}."]\n"; print __LINE__."; [ Debug ] - path::iso::source_directory: [".$conf->{path}{iso}{source_directory}."]\n"; print __LINE__."; [ Debug ] - path::iso::iso_tools: ....... [".$conf->{path}{iso}{iso_tools}."]\n"; print __LINE__."; [ Debug ] - path::iso::iso_kickstart: ... [".$conf->{path}{iso}{iso_kickstart}."]\n"; print __LINE__."; [ Debug ] - path::iso::iso_packages: .... [".$conf->{path}{iso}{iso_packages}."]\n"; print __LINE__."; [ Debug ] - path::iso::iso_striker: ..... [".$conf->{path}{iso}{iso_striker}."]\n"; print __LINE__."; [ Debug ] - path::iso::iso_fence: ....... [".$conf->{path}{iso}{iso_fence}."]\n"; print __LINE__."; [ Debug ] - path::iso::iso_fujitsu: ..... [".$conf->{path}{iso}{iso_fujitsu}."]\n"; print __LINE__."; [ Debug ] - path::iso::iso_asix: ........ [".$conf->{path}{iso}{iso_asix}."]\n"; print __LINE__."; [ Debug ] - path::iso::iso_avago: ....... [".$conf->{path}{iso}{iso_avago}."]\n"; } # Make sure any old repodata files are removed. wipe_repodata($conf); # Assemble the final ISO name my $os_type = $conf->{sys}{os_type}; $conf->{path}{iso}{file_name} = "Anvil_".$conf->{sys}{name}{mark}."-v".$conf->{sys}{name}{version}; print __LINE__."; [ Debug ] - path::iso::file_name: [".$conf->{path}{iso}{file_name}."]\n" if $conf->{sys}{debug} >= 3; if ($conf->{sys}{name}{version} =~ /^v/) { $conf->{path}{iso}{file_name} = "Anvil_".$conf->{sys}{name}{mark}."-".$conf->{sys}{name}{version}; print __LINE__."; [ Debug ] - path::iso::file_name: [".$conf->{path}{iso}{file_name}."]\n" if $conf->{sys}{debug} >= 3; } if ($conf->{sys}{name}{suffix}) { $conf->{path}{iso}{file_name} .= "-".$conf->{sys}{name}{suffix}; } $conf->{path}{iso}{file_name} .= "_".$conf->{sys}{name}{$os_type}."-".$conf->{sys}{name}{release}.".iso"; print __LINE__."; [ Debug ] - path::iso::file_name: [".$conf->{path}{iso}{file_name}."]\n" if $conf->{sys}{debug} >= 3; # This will be where we look for updated RPMs. $conf->{path}{updates_dir} = $conf->{sys}{user}{current_dir}."/".$conf->{path}{updates_dir}."/".$conf->{sys}{name}{$os_type}; print __LINE__."; [ Debug ] - path::updates_dir: [".$conf->{path}{updates_dir}."]\n" if $conf->{sys}{debug} >= 3; return(0); } # This makes a directory similar to 'mkdir -p' at the shell. sub make_directory { my ($conf, $directory) = @_; print __LINE__."; [ Debug ] - directory: [$directory]\n" if $conf->{sys}{debug} >= 3; # If the directory starts with '/', we're building from /. If the directory starts with '~', we're # building from the user's home. if ($directory =~ /^~/) { $directory =~ s/^~/$conf->{sys}{user}{home}/; print __LINE__."; [ Debug ] - directory: [$directory]\n" if $conf->{sys}{debug} >= 3; } if ($directory !~ /^\//) { $directory = $ENV{PWD}."/".$directory; print __LINE__."; [ Debug ] - directory: [$directory]\n" if $conf->{sys}{debug} >= 3; } $directory =~ s/\/+/\//g; print __LINE__."; [ Debug ] - directory: [$directory]\n" if $conf->{sys}{debug} >= 3; my $current_directory = ""; foreach my $sub_directory (split/\//, $directory) { next if not $sub_directory; $current_directory .= "/$sub_directory"; print __LINE__."; [ Debug ] - current_directory: [$current_directory]\n" if $conf->{sys}{debug} >= 3; if (not -e $current_directory) { print "- Creating the directory: [$current_directory]\n" if $conf->{sys}{debug} >= 1; mkdir $current_directory or die __LINE__."; failed to create the directory: [$current_directory]. The error was: $!\n"; } } print __LINE__."; [ Debug ] - current_directory: [$current_directory]\n" if $conf->{sys}{debug} >= 3; return($current_directory); } # Build the list of source files to copy. sub build_source { my ($conf) = @_; print "- We're looking through the source ISO(s) to find which installation packages are needed.\n"; print "- Please be patient! This can take a minute.\n"; # Build a list of all the packages needed for this OS (common plus distro-specific). my $extra_packages = $conf->{sys}{os_type}; my $distro_list = []; # Add the common files. foreach my $package (sort {$a cmp $b} @{$conf->{sys}{packages}{common}}) { push @{$distro_list}, $package; } # Now add the distro-specific files foreach my $package (sort {$a cmp $b} @{$conf->{sys}{packages}{$extra_packages}}) { push @{$distro_list}, $package; } ### TODO: Check to see if there is a matching RPM already in Packages, but with a different version ### name from what we plan to copy, and unlink it if so (so that updates don't leave their ### older copy in the ISO). # Find where we will copy files from. my $missing_packages = 0; foreach my $package (sort {$a cmp $b} @{$distro_list}) { my ($file, $arch) = ($package =~ /^(.*)\.(.*?)$/); my $found = 0; print __LINE__."; [ Debug ] - Looking for the: [$arch] package: [$package] in the source.\n" if $conf->{sys}{debug} >= 3; foreach my $iso (sort {$a cmp $b} keys %{$conf->{source_packages}}) { print __LINE__."; [ Debug ] - ISO: [$iso]\n" if $conf->{sys}{debug} >= 3; foreach my $directory (sort {$a cmp $b} keys %{$conf->{source_packages}{$iso}}) { print __LINE__."; [ Debug ] - ISO: [$iso], Directory: [$directory]\n" if $conf->{sys}{debug} >= 3; foreach my $source_package (sort {$a cmp $b} keys %{$conf->{source_packages}{$iso}{$directory}}) { next if $source_package !~ /^\Q$file\E/; next if $source_package !~ /\Q$arch\E/; print __LINE__."; [ Debug ] - Source file: [$iso]:[$directory]/[$source_package] POSSIBLE match...\n" if $conf->{sys}{debug} >= 3; if ($source_package =~ /^\Q$file\E-\d/) { print __LINE__."; [ Debug ] - Source file: [$iso]:[$directory]/[$source_package] matches!\n" if $conf->{sys}{debug} >= 3; $found++; $conf->{copy}{$package} = { source => $iso, directory => $directory, file => $source_package, }; } } } } # If I didn't finf a match, or if I found too many matches, die. if (not $found) { print __LINE__."; [ Error ] - Failed to find: [$package] (file: [$file], arch: [$arch]) in the source!\n"; $missing_packages = 1; } if (($found > 1) && ($conf->{sys}{debug} >= 2)) { print "[ Note ] - The package: [$package] had multiple matches, Using; source: [".$conf->{copy}{$package}{source}."], file: [".$conf->{copy}{$package}{file}."]!\n"; } } die if $missing_packages; # Copy! print "- Ready to copy files!\n"; foreach my $package (sort {$a cmp $b} keys %{$conf->{copy}}) { my $iso = $conf->{copy}{$package}{source}; my $destination = $conf->{path}{iso}{iso_packages}."/".$conf->{copy}{$package}{file}; if (-e $destination) { print __LINE__."; [ Debug ]- The package: [$destination] already exists, no need to copy it.\n" if $conf->{sys}{debug} >= 3; } else { # Copy it. But how? my $shell_call = ""; if ($conf->{source}{$iso}{mount}) { # copy my $source = $conf->{copy}{$package}{directory}."/".$conf->{copy}{$package}{file}; copy_file($conf, $source, $destination, 0); } else { # Use iso-read my $extract_file = $conf->{copy}{$package}{directory}."/".$conf->{copy}{$package}{file}; isoread_file($conf, $iso, $extract_file, $destination); } } } print "- Packages copied. Copying auxiliary source files now.\n"; #$conf->{sys}{debug} = 2; # Now copy the rest of the source files. Note that we'll watch for 'syslinux' and if we don't see it, # we'll copy 'isolinux' over. my $syslinux_found = 0; foreach my $iso (sort {$a cmp $b} keys %{$conf->{source_packages}}) { # We only care about disk 1 next if $conf->{source}{$iso}{name} !~ /disk 1/i; print __LINE__."; [ Debug ] - ISO: [$iso] -> [".$conf->{source}{$iso}{name}."]\n" if $conf->{sys}{debug} >= 2; foreach my $directory (sort {$a cmp $b} keys %{$conf->{source_packages}{$iso}}) { # Skip '/Packages' next if $directory =~ /\/Packages$/; $syslinux_found = 1 if $directory =~ /\/syslinux$/; print __LINE__."; [ Debug ] - directory: [$directory]\n" if $conf->{sys}{debug} >= 2; foreach my $file (sort {$a cmp $b} keys %{$conf->{source_packages}{$iso}{$directory}}) { next if $file eq "TRANS.TBL"; print __LINE__."; [ Debug ] - file: [$directory/$file]\n" if $conf->{sys}{debug} >= 3; # To determine the copy target, we first need to strip off the mount point # (if mounted) and append it to the source directory. my $destination = ""; my $destination_directory = ""; if ($conf->{source}{$iso}{mount}) { my $destination_sub_directory = $directory; $destination_sub_directory =~ s/\Q$conf->{source}{$iso}{mount}\E//; $destination = $conf->{path}{iso}{source_directory}."/".$destination_sub_directory."/".$file; $destination_directory = $conf->{path}{iso}{source_directory}."/".$destination_sub_directory; } else { $destination = $conf->{path}{iso}{source_directory}."/".$directory."/".$file; $destination_directory = $conf->{path}{iso}{source_directory}."/".$directory; } $destination =~ s/\/+/\//g; $destination_directory =~ s/\/+/\//g; print __LINE__."; [ Debug ] - destination: [$destination], destination_directory: [$destination_directory]\n" if $conf->{sys}{debug} >= 3; # Create the destination directory if needed $destination_directory = make_directory($conf, $destination_directory); if (-e $destination) { print __LINE__."; [ Debug ]- The file: [$destination] already exists, no need to copy it.\n" if $conf->{sys}{debug} >= 3; } else { # Copy it, but again, how? my $shell_call = ""; if ($conf->{source}{$iso}{mount}) { # copy my $source = $directory."/".$file; $source =~ s/\/+/\//g; copy_file($conf, $source, $destination, 0); } else { # Use iso-read my $extract_file = $directory."/".$file; $extract_file =~ s/\/+/\//g; isoread_file($conf, $iso, $extract_file, $destination); } } } } } print "- Verifying 'syslinux' was created.\n"; print __LINE__."; [ Debug ] - syslinux_found: [$syslinux_found]\n" if $conf->{sys}{debug} >= 2; if (not $syslinux_found) { print "- The 'syslinux' directory was not in the source. Creating it now, if needed.\n"; my $isolinux_directory = $conf->{path}{iso}{source_directory}."/isolinux"; my $syslinux_directory = $conf->{path}{iso}{source_directory}."/syslinux"; print __LINE__."; [ Debug ] - isolinux_directory: [$isolinux_directory], syslinux_directory: [$syslinux_directory]\n" if $conf->{sys}{debug} >= 2; $syslinux_directory = make_directory($conf, $syslinux_directory); # Read all the files in isolinux. For each file we find, copy it to syslinux if it doesn't # exist yet. We'll rename anything 'isolinux' to 'syslinux' in the process. local(*DIR); opendir(DIR, $isolinux_directory) or die __LINE__."; Failed to open the directory: [$isolinux_directory], error was: $!\n"; while (my $file = readdir(DIR)) { next if (($file eq ".") or ($file eq "..")); my $destination_file = $file; $destination_file =~ s/isolinux/syslinux/; my $source_path = $isolinux_directory."/".$file; my $destination_path = $syslinux_directory."/".$destination_file; print __LINE__."; [ Debug ] - source_path: [$source_path], destination_path: [$destination_path]\n" if $conf->{sys}{debug} >= 2; copy_file($conf, $source_path, $destination_path, 0); } closedir(DIR); } return(0); } # This checks to see if the source is mounted or not. If not, it mounts it sub prep_source { my ($conf) = @_; # First, are the sources already mounted? foreach my $iso (sort {$a cmp $b} @{$conf->{sys}{source}{iso}}) { print __LINE__."; [ Debug ] - iso: [$iso]\n" if $conf->{sys}{debug} >= 2; $conf->{source}{$iso}{mount} = ""; $conf->{source}{$iso}{source_file} = ($iso =~ /^.*\/(.*)$/)[0]; print __LINE__."; [ Debug ] - source::${iso}::source_file: [".$conf->{source}{$iso}{source_file}."]\n" if $conf->{sys}{debug} >= 2; my $shell_call = $conf->{executable}{mount}; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; print __LINE__."; [ Debug ] - source::${iso}::source_file: [".$conf->{source}{$iso}{source_file}."]\n" if $conf->{sys}{debug} >= 2; die if not defined $conf->{source}{$iso}{source_file}; if ($line =~ /^\/.*?\/\Q$conf->{source}{$iso}{source_file}\E on (.*?) /) { $conf->{source}{$iso}{mount} = $1; print __LINE__."; [ Debug ] - source::${iso}::mount: [".$conf->{source}{$iso}{mount}."]\n" if $conf->{sys}{debug} >= 2; print "- The source ISO: [$iso] was found to be mounted at: [".$conf->{source}{$iso}{mount}."].\n"; } } close $file_handle; # Did I mount it with fuse? if ((not $conf->{source}{$iso}{mount}) && ($conf->{source}{$iso}{fuse_mount})) { $conf->{source}{$iso}{mount} = $conf->{source}{$iso}{fuse_mount}; print __LINE__."; [ Debug ] - source::${iso}::mount: [".$conf->{source}{$iso}{mount}."]\n" if $conf->{sys}{debug} >= 2; } # If the source isn't mounted, we'll need to try and read the files out of the ISO directly because # 'mount' requires root, and we can't assume the user has that. if (not $conf->{source}{$iso}{mount}) { # Not mounted... Do we have the needed tools? print "- The source ISO: [$iso] is not mounted. Will read and copy files directly out of the ISO.\n"; if ((-x $conf->{executable}{isoinfo}) && (-x $conf->{executable}{'iso-read'})) { # We're good to use this method to get our files. print __LINE__."; [ Debug ] - 'isoinfo' and 'iso-read' found!\n" if $conf->{sys}{debug} >= 2; } else { # Nothing more we can do... print " [ Error ] - The source ISO: [$iso] [ Error ] is not mounted and the 'isoinfo' and/or 'iso-read' programs are not [ Error ] installed. Please mount the source ISO 'loop-back'; [ Error ] \$ sudo mkdir /mnt/iso [ Error ] \$ sudo mount -o loop $iso /mnt/iso [ Error ] or install 'libcdio' and 'genisoimage' (or your OS's version of) [ Error ] and then re-run this program. "; clean_up($conf); exit(2); } } # Now get a list of the source files. $conf->{source}{$iso}{files_read} = 0; $conf->{source}{$iso}{directories_read} = 0; print __LINE__."; [ Debug ] - source::${iso}::mount: [".$conf->{source}{$iso}{mount}."]\n" if $conf->{sys}{debug} >= 2; if (not $conf->{source}{$iso}{mount}) { # We'll neeed to dig the file names out the hard way. print "- Reading the contents of the: [".$conf->{source}{$iso}{source_file}."] ISO now...\n"; my $files = 0; my $directories = 0; my $in_path = ""; my $shell_call = $conf->{executable}{isoinfo}." -lR -i ".$iso; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; $line =~ s/^\s+//; $line =~ s/\s+$//; $line =~ s/\s+/ /g; next if not $line; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /Directory listing of (\/.*)?/) { $in_path = $1; $in_path =~ s/\/$// if $in_path ne "/"; print __LINE__."; [ Debug ] - in_path: [$in_path]\n" if $conf->{sys}{debug} >= 2; $conf->{source}{$iso}{directories_read}++; } next if not $in_path; if ($line =~ /^-.*? \d+ \d+ \d+ (\d+) \w+ \d+ \d+ \[.*?\] (.*)$/) { my $bytes = $1; my $file = $2; next if (($file eq ".") or ($file eq "..")); die __LINE__."; Failed to parse file name from: [$line]\n" if not $line; print __LINE__."; [ Debug ] - file: [$file], bytes: [$bytes]\n" if $conf->{sys}{debug} >= 2; $conf->{source_packages}{$iso}{$in_path}{$file} = $bytes; print __LINE__."; [ Debug ] - source_packages::${iso}::${in_path}::${file}: [".$conf->{source_packages}{$iso}{$in_path}{$file}."]\n" if $conf->{sys}{debug} >= 2; $conf->{source}{$iso}{files_read}++; } } close $file_handle; print "- Done. Read in: [".$conf->{source}{$iso}{files_read}."] files across: [".$conf->{source}{$iso}{directories_read}."] directories.\n"; } else { # Walk the mount point. print "- Reading the contents of the ISO: [$iso] now...\n"; read_directory($conf, $iso, $conf->{source}{$iso}{mount}); print "- Done. Read in: [".$conf->{source}{$iso}{files_read}."] files across: [".$conf->{source}{$iso}{directories_read}."] directories.\n"; } } # If the 'Updates' directory exists for this OS, read it in. if (-e $conf->{path}{updates_dir}) { # We use 'z_' to make sure it sorts last. my $iso = "z_updates"; read_directory($conf, $iso, $conf->{path}{updates_dir}); $conf->{source}{$iso}{name} = "Local updates"; $conf->{source}{$iso}{mount} = 1; } # Get a count of RPMs found in '/Packages' my $package_count = 0; foreach my $iso (sort {$a cmp $b} keys %{$conf->{source_packages}}) { print __LINE__."; [ Debug ] - ISO: [$iso]\n" if $conf->{sys}{debug} >= 3; foreach my $directory (sort {$a cmp $b} keys %{$conf->{source_packages}{$iso}}) { print __LINE__."; [ Debug ] - ISO: [$iso], Directory: [$directory]\n" if $conf->{sys}{debug} >= 3; foreach my $file (sort {$a cmp $b} keys %{$conf->{source_packages}{$iso}{$directory}}) { next if $file !~ /\.rpm$/; print __LINE__."; [ Debug ] - Source: [$iso]:[$directory]:[$file]\n" if $conf->{sys}{debug} >= 3; $package_count++; } } } print "- Found: [$package_count] source packages.\n"; return(0); } # This reads the files in from the given directory, decending into any subdirectories, and recording the # files it finds into the 'sourc_packages::<path>::<file>' hash, where '<path>' has the 'path::source_path' # stripped off. sub read_directory { my ($conf, $iso, $directory) = @_; $conf->{source}{$iso}{directories_read}++; print __LINE__."; [ Debug ] - iso: [$iso], directory: [$directory]\n" if $conf->{sys}{debug} >= 2; my $source_path = $directory; $source_path =~ s/^$iso//; $source_path =~ s/\/$//; $source_path = "/$source_path" if $source_path !~ /^\//; print __LINE__."; [ Debug ] - Reading in the contents of: [$directory], recording as: [$source_path]\n" if $conf->{sys}{debug} >= 2; local(*DIR); opendir(DIR, $directory) or die __LINE__."; Failed to open the directory: [$directory], error was: $!\n"; while (my $file = readdir(DIR)) { next if (($file eq ".") or ($file eq "..")); my $this_path = $directory."/".$file; print __LINE__."; [ Debug ] - this_path: [$this_path]\n" if $conf->{sys}{debug} >= 2; if (-d $this_path) { read_directory($conf, $iso, $this_path); } elsif (-f $this_path) { # Record the size. (position 7 in the returned array) $conf->{source_packages}{$iso}{$source_path}{$file} = (stat($this_path))[7]; print __LINE__."; [ Debug ] - source_packages:${iso}::${source_path}::${file}: [".$conf->{source_packages}{$iso}{$source_path}{$file}."]\n" if $conf->{sys}{debug} >= 2; $conf->{source}{$iso}{files_read}++; } } closedir(DIR); return(0) } # This validates the source ISO. It will exit if problems are found. sub validate_source { my ($conf) = @_; print __LINE__."; [ Debug ] - switches::source: [".$conf->{switches}{source}."]\n" if $conf->{sys}{debug} >= 2; if ($conf->{switches}{source}) { foreach my $source (split/,/, $conf->{switches}{source}) { if ($source !~ /\//) { # The source is in this directory, so prepend to get the full path. $source = $running_directory."/".$source; } elsif ($source =~ /^\.\//) { $source =~ s/^\./$running_directory/;; } print __LINE__."; [ Debug ] - source: [$source]\n" if $conf->{sys}{debug} >= 2; push @{$conf->{sys}{source}{iso}}, $source; } } else { print "\n[ Error ] - No source ISO provided!\n"; print "[ Note ] - Run './$THIS_FILE -h' for usage.\n\n"; exit(4); } my $disk = 1; foreach my $iso (sort {$a cmp $b} @{$conf->{sys}{source}{iso}}) { print __LINE__."; [ Debug ] - iso: [$iso]\n" if $conf->{sys}{debug} >= 2; if (-e $iso) { print __LINE__."; [ Debug ] - Parsing source: [$iso]\n" if $conf->{sys}{debug} >= 2; ($conf->{source}{$iso}{directory}, $conf->{source}{$iso}{file}) = ($iso =~ /^(.*)\/(.*)$/); print __LINE__."; [ Debug ] - Source directory: [".$conf->{source}{$iso}{directory}."], file: [".$conf->{source}{$iso}{file}."]\n" if $conf->{sys}{debug} >= 2; } else { print "\n[ Error ] - Source: [".$conf->{switches}{source}."] doesn't exist!\n\n"; exit(1); } $conf->{switches}{'no-source-validation'} = 0 if not defined $conf->{switches}{'no-source-validation'}; if ($conf->{switches}{'no-source-validation'}) { print "- Skipping validation on user request, assuming: [$iso] is good.\n"; if ($iso =~ /rhel/i) { $conf->{sys}{os_type} = "rhel"; $conf->{sys}{os} = "RHEL 6, Disk ".$disk; $conf->{sys}{name}{release} = "6"; if ($iso =~ /(6\.\d+)/) { $conf->{sys}{name}{release} = $1; $conf->{sys}{os} = "RHEL ".$conf->{sys}{name}{release}.", Disk ".$disk; } $conf->{source}{$iso}{name} = $conf->{sys}{os}; $disk++; print "- The source ISO is: [".$conf->{sys}{os}."] (guessing from ISO file name).\n"; } elsif ($iso =~ /centos/i) { $conf->{sys}{os_type} = "centos"; $conf->{sys}{os} = "CentOS 6, Disk ".$disk; $conf->{sys}{name}{release} = "6"; if ($iso =~ /(6\.\d+)/) { $conf->{sys}{name}{release} = $1; $conf->{sys}{os} = "CentOS ".$conf->{sys}{name}{release}.", Disk ".$disk; } $conf->{source}{$iso}{name} = $conf->{sys}{os}; $disk++; print "- The source ISO is: [".$conf->{sys}{os}."] (guessing from ISO file name).\n"; } else { print " [ Error ] - Failed to determine the underlying OS for the ISO: [$iso] [ Error ] If this is a custom ISO, please change the file name to contain the [ Error ] string 'RHEL' or 'CentOS', depending on which underlying OS your [ Error ] source ISO is based on. "; exit(3); } } else { print "- Generating md5sum of the source: [$iso], please wait a moment...\n"; $conf->{source}{$iso}{sum} = ""; my $shell_call = $conf->{executable}{md5sum}." ".$iso; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /^(\S+)\s+$iso/) { $conf->{source}{$iso}{sum} = $1; print __LINE__."; [ Debug ] - source::${iso}::sum: [".$conf->{source}{$iso}{sum}."]\n" if $conf->{sys}{debug} >= 2; } } close $file_handle; # If we got a sum, make sure it matches an expected one. if ($conf->{source}{$iso}{sum}) { foreach my $this_sum (sort {$a cmp $b} keys %{$conf->{sys}{md5sum}}) { print __LINE__."; [ Debug ] - source::${iso}::sum: [".$conf->{source}{$iso}{sum}."]\n" if $conf->{sys}{debug} >= 2; print __LINE__."; [ Debug ] - this_sum: [$this_sum]\n" if $conf->{sys}{debug} >= 2; if ($conf->{source}{$iso}{sum} eq $this_sum) { # Match! $conf->{sys}{os} = $conf->{sys}{md5sum}{$this_sum}; $conf->{source}{$iso}{name} = $conf->{sys}{md5sum}{$this_sum}; print "- Source identified as: [".$conf->{sys}{os}."] (sum: [$this_sum])\n" if $conf->{sys}{debug} >= 2; # If this is a 6.8 ISO, change the 'release' if ($conf->{sys}{os} =~ /(6\.\d+)/) { $conf->{sys}{name}{release} = $1; print "- Set the release version as: [".$conf->{sys}{name}{release}."].\n" if $conf->{sys}{debug} >= 2; } } } # Did I find an OS? if ($conf->{sys}{os} =~ /rhel/i) { $conf->{sys}{os_type} = "rhel"; print "- The source ISO is: [".$conf->{sys}{os}."].\n"; } elsif ($conf->{sys}{os} =~ /centos/i) { $conf->{sys}{os_type} = "centos"; print "- The source ISO is: [".$conf->{sys}{os}."].\n"; } else { print " [ Error ] - Failed to match the source ISO: [$iso] [ Error ] 'md5sum': [".$conf->{source}{$iso}{sum}."] to a known sum. [ Error ] It is important to verify the source ISO, so unfortunately, we can not proceed. "; exit(3); } } } } if (not $conf->{sys}{os}) { print "\n[ Error ] - Failed to match the source 'md5sum' for: [".$conf->{switches}{source}."] to a known OS.\n"; print "[ Error ] - It is important to verify the source ISO, so unfortunately, we can not proceed.\n\n"; exit(3); } # If this is Ubuntu, 'iso-read' is broken (at least on 16.04), so see if 'fuseiso' is installed and, # if so, mount the ISOs. print __LINE__."; [ Debug ] - sys::local_os: [".$conf->{sys}{local_os}."], executable::fuseiso: [".$conf->{executable}{fuseiso}."]\n" if $conf->{sys}{debug} >= 2; if (($conf->{sys}{local_os} eq "ubuntu") && (-x $conf->{executable}{fuseiso})) { my $i = 1; foreach my $iso (sort {$a cmp $b} @{$conf->{sys}{source}{iso}}) { my $mount_point = "/tmp/iso".$i; my $test_file = $mount_point."/.discinfo"; # Skip if I already mounted. if (-e $test_file) { # Make a note to clean it up print __LINE__."; [ Debug ] - skipping: [$iso], found: [$test_file] already.\n" if $conf->{sys}{debug} >= 2; $i++; push @{$conf->{sys}{fuse_mounted}}, $mount_point; $conf->{source}{$iso}{fuse_mount} = $mount_point; next; } my $shell_call = $conf->{executable}{fuseiso}." -p ".$iso." ".$mount_point; print __LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print __LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; $i++; push @{$conf->{sys}{fuse_mounted}}, $mount_point; $conf->{source}{$iso}{fuse_mount} = $mount_point; } } return(0); } # This searches the PATH environment variable looking for the location of any executable not pre-defined. sub find_executables { my ($conf) = @_; print __LINE__."; [ Debug ] - Searching for executables...\n" if $conf->{sys}{debug} >= 2; my $ok = 1; foreach my $executable (sort {$a cmp $b} keys %{$conf->{executable}}) { next if $conf->{executable}{$executable} =~ /^\//; print __LINE__."; [ Debug ] - Program: [$executable]... \n" if $conf->{sys}{debug} >= 2; foreach my $path (split/:/, $ENV{'PATH'}) { my $test_path = "$path/$executable"; if ((-e $test_path) && (-x $test_path)) { # Found it. $conf->{executable}{$executable} = $test_path; print __LINE__."; [ Debug ] - Found it: [".$conf->{executable}{$executable}."].\n" if $conf->{sys}{debug} >= 2; last; } } ### NOTE: We don't die on iso-read, isoinfo or fuseiso as they may not be needed. # Die if we didn't find it. if (($conf->{executable}{$executable} !~ /^\//) && ($executable ne "iso-read") && ($executable ne "isoinfo") && ($executable ne "fuseiso")) { $ok = 0; print "[ Error ] - Failed to find the: [$executable] program.\n"; } } if (not $ok) { print " [ Error ] - Some programs needed to build the Anvil! ISO appear to not be [ Error ] installed. Please ensure the following programs are installed: [ Error ] 'createrepo, iso-read, isoinfo and mkisofs'. [ Error ] On RPM-based distros, the following command should provide [ Error ] everything: [ Error ] "; if ($conf->{sys}{local_os} eq "fedora") { print "[ Error ] - \$ sudo dnf -y install createrepo genisoimage libcdio"; } elsif ($conf->{sys}{local_os} eq "ubuntu") { print "[ Error ] - \$ sudo apt-get install createrepo genisoimage libcdio-utils fuseiso"; } elsif ($conf->{sys}{local_os} =~ /suse/i) { print "[ Error ] - \$ sudo zypper in createrepo genisoimage libcdio16 libcdio-utils"; } else { print "[ Error ] - \$ sudo yum -y install createrepo genisoimage libcdio"; } print " [ Error ] [ Error ] - Note that your distro may use 'dnf' instead of 'yum'. On non-RPM [ Error ] based distros, these programs may be provided by different [ Error ] packages. Once these are installed, please run this program again. [ Error ] - Note that 'iso-read' and 'isoinfo' are not needed if you mount the [ Error ] ISO manually. (\$ sudo mount -o loop <iso> <mountpoint>) "; clean_up($conf); exit(1); } return(0); } # This is the list of RPMs. sub get_package_list { my ($conf) = @_; # These are common to CentOS and RHEL $conf->{sys}{packages}{common} = [ "ConsoleKit-libs.x86_64", "ConsoleKit-x11.x86_64", "ConsoleKit.x86_64", "DeviceKit-power.x86_64", "GConf2-gtk.x86_64", "GConf2.x86_64", "MAKEDEV.x86_64", "ORBit2.x86_64", "OpenIPMI-libs.x86_64", "OpenIPMI.x86_64", "PackageKit-device-rebind.x86_64", "PackageKit-glib.x86_64", "PackageKit-gtk-module.x86_64", "PackageKit-yum-plugin.x86_64", "PackageKit-yum.x86_64", "PackageKit.x86_64", "abyssinica-fonts.noarch", "acl.x86_64", "acpid.x86_64", "aic94xx-firmware.noarch", "alsa-lib.x86_64", "alsa-plugins-pulseaudio.x86_64", "alsa-utils.x86_64", "apr-util-ldap.x86_64", "apr-util.x86_64", "apr.x86_64", "at-spi.x86_64", "atk.x86_64", "atmel-firmware.noarch", "attr.x86_64", "audit-libs-python.x86_64", "audit-libs.x86_64", "audit.x86_64", "augeas-libs.x86_64", "authconfig.x86_64", "avahi-glib.x86_64", "avahi-libs.x86_64", "b43-openfwwf.noarch", "basesystem.noarch", "bash.x86_64", "bc.x86_64", "bfa-firmware.noarch", "bind-libs.x86_64", "bind-utils.x86_64", "binutils.x86_64", "bridge-utils.x86_64", "busybox.x86_64", "bzip2-libs.x86_64", "bzip2.x86_64", "ca-certificates.noarch", "cairo.x86_64", "ccs.x86_64", "cdparanoia-libs.x86_64", "celt051.x86_64", "checkpolicy.x86_64", "chkconfig.x86_64", "cifs-utils.x86_64", "cim-schema.noarch", "cjkuni-fonts-common.noarch", "cjkuni-uming-fonts.noarch", "cloog-ppl.x86_64", "cluster-cim.x86_64", "clusterlib.x86_64", "cman.x86_64", "compat-xcb-util.x86_64", "comps-extras.noarch", "control-center-extra.x86_64", "control-center-filesystem.x86_64", "control-center.x86_64", "coreutils-libs.x86_64", "coreutils.x86_64", "corosync.x86_64", "corosynclib.x86_64", "cpio.x86_64", "cpp.x86_64", "cracklib-dicts.x86_64", "cracklib.x86_64", "createrepo.noarch", "cronie-anacron.x86_64", "cronie.x86_64", "crontabs.noarch", "cryptsetup-luks-libs.x86_64", "cryptsetup-luks.x86_64", "cups-libs.x86_64", "curl.x86_64", "cvs.x86_64", "cyrus-sasl-lib.x86_64", "cyrus-sasl-md5.x86_64", "cyrus-sasl-plain.x86_64", "cyrus-sasl.x86_64", "dash.x86_64", "db4-cxx.x86_64", "db4-devel.x86_64", "db4-utils.x86_64", "db4.x86_64", "dbus-glib.x86_64", "dbus-libs.x86_64", "dbus-python.x86_64", "dbus-x11.x86_64", "dbus.x86_64", "dejavu-fonts-common.noarch", "dejavu-sans-fonts.noarch", "dejavu-sans-mono-fonts.noarch", "dejavu-serif-fonts.noarch", "deltarpm.x86_64", "desktop-file-utils.x86_64", "device-mapper-event-libs.x86_64", "device-mapper-event.x86_64", "device-mapper-libs.x86_64", "device-mapper-persistent-data.x86_64", "device-mapper.x86_64", "dhclient.x86_64", "dhcp-common.x86_64", "dhcp.x86_64", "diffutils.x86_64", "dmidecode.x86_64", "dmz-cursor-themes.noarch", "dnsmasq.x86_64", "docbook-dtds.noarch", "dos2unix.x86_64", "dosfstools.x86_64", "dracut-kernel.noarch", "dracut.noarch", "dracut-network.noarch", "e2fsprogs-libs.x86_64", "e2fsprogs.x86_64", "ebtables.x86_64", "efibootmgr.x86_64", "eggdbus.x86_64", "elfutils-libelf.x86_64", "elfutils-libs.x86_64", "eog.x86_64", "ethtool.x86_64", "evolution-data-server.x86_64", "exempi.x86_64", "expat.x86_64", "expect.x86_64", "fence-agents.x86_64", "fence-virt.x86_64", "file-libs.x86_64", "file.x86_64", "filesystem.x86_64", "findutils.x86_64", "fipscheck-lib.x86_64", "fipscheck.x86_64", "firefox.x86_64", "flac.x86_64", "fontconfig.x86_64", "fontpackages-filesystem.noarch", "fprintd-pam.x86_64", "fprintd.x86_64", "freeipmi-bmc-watchdog.x86_64", "freeipmi-ipmidetectd.x86_64", "freeipmi.x86_64", "freetype.x86_64", "ftp.x86_64", "fuse-libs.x86_64", "fuse.x86_64", "gamin.x86_64", "gawk.x86_64", "gcalctool.x86_64", "gcc-c++.x86_64", "gcc.x86_64", "gd.x86_64", "gdbm-devel.x86_64", "gdbm.x86_64", "gdk-pixbuf2.x86_64", "gdm-libs.x86_64", "gdm-plugin-fingerprint.x86_64", "gdm-user-switch-applet.x86_64", "gdm.x86_64", "gettext.x86_64", "gfs2-utils.x86_64", "glib-networking.x86_64", "glib2.x86_64", "glibc-common.x86_64", "glibc-devel.x86_64", "glibc-headers.x86_64", "glibc.i686", "glibc.x86_64", "glusterfs.x86_64", "glusterfs-api.x86_64", "glusterfs-client-xlators.x86_64", "glusterfs-libs.x86_64", "glx-utils.x86_64", "gmp.x86_64", "gnome-applets.x86_64", "gnome-desktop.x86_64", "gnome-disk-utility-libs.x86_64", "gnome-doc-utils-stylesheets.noarch", "gnome-icon-theme.noarch", "gnome-keyring-pam.x86_64", "gnome-keyring.x86_64", "gnome-media-libs.x86_64", "gnome-media.x86_64", "gnome-menus.x86_64", "gnome-packagekit.x86_64", "gnome-panel-libs.x86_64", "gnome-panel.x86_64", "gnome-power-manager.x86_64", "gnome-python2-applet.x86_64", "gnome-python2-bonobo.x86_64", "gnome-python2-canvas.x86_64", "gnome-python2-desktop.x86_64", "gnome-python2-extras.x86_64", "gnome-python2-gconf.x86_64", "gnome-python2-gnome.x86_64", "gnome-python2-gnomekeyring.x86_64", "gnome-python2-gnomevfs.x86_64", "gnome-python2-libegg.x86_64", "gnome-python2.x86_64", "gnome-screensaver.x86_64", "gnome-session-xsession.x86_64", "gnome-session.x86_64", "gnome-settings-daemon.x86_64", "gnome-terminal.x86_64", "gnome-themes.noarch", "gnome-user-docs.noarch", "gnome-utils-libs.x86_64", "gnome-utils.x86_64", "gnome-vfs2-smb.x86_64", "gnome-vfs2.x86_64", "gnupg2.x86_64", "gnutls-utils.x86_64", "gnutls.x86_64", "google-crosextra-caladea-fonts.noarch", "google-crosextra-carlito-fonts.noarch", "gpgme.x86_64", "gpm-libs.x86_64", "gpm.x86_64", "gpxe-roms-qemu.noarch", "grep.x86_64", "groff.x86_64", "grub.x86_64", "grubby.x86_64", "gstreamer-plugins-base.x86_64", "gstreamer-plugins-good.x86_64", "gstreamer-tools.x86_64", "gstreamer.x86_64", "gtk-vnc-python.x86_64", "gtk-vnc.x86_64", "gtk2-engines.x86_64", "gtk2.x86_64", "gucharmap.x86_64", "gvfs-archive.x86_64", "gvfs-fuse.x86_64", "gvfs-smb.x86_64", "gvfs.x86_64", "gzip.x86_64", "hal-info.noarch", "hal-libs.x86_64", "hal.x86_64", "hdparm.x86_64", "hicolor-icon-theme.noarch", "httpd-tools.x86_64", "httpd.x86_64", "hunspell.x86_64", "hwdata.noarch", "info.x86_64", "initscripts.x86_64", "ipmitool.x86_64", "iproute.x86_64", "iptables-ipv6.x86_64", "iptables.x86_64", "iputils.x86_64", "ipw2100-firmware.noarch", "ipw2200-firmware.noarch", "irqbalance.x86_64", "iscsi-initiator-utils.x86_64", "iso-codes.noarch", "ivtv-firmware.noarch", "iwl100-firmware.noarch", "iwl1000-firmware.noarch", "iwl3945-firmware.noarch", "iwl4965-firmware.noarch", "iwl5000-firmware.noarch", "iwl5150-firmware.noarch", "iwl6000-firmware.noarch", "iwl6000g2a-firmware.noarch", "iwl6050-firmware.noarch", "jasper-libs.x86_64", "jomolhari-fonts.noarch", "kbd-misc.noarch", "kbd.x86_64", "kernel-devel.x86_64", "kernel-firmware.noarch", "kernel-headers.x86_64", "kernel.x86_64", "kernel.x86_64", "kexec-tools.x86_64", "keyutils-libs-devel.x86_64", "keyutils-libs.x86_64", "keyutils.x86_64", "khmeros-base-fonts.noarch", "khmeros-fonts-common.noarch", "kpartx.x86_64", "krb5-devel.x86_64", "krb5-libs.x86_64", "kurdit-unikurd-web-fonts.noarch", "lcms-libs.x86_64", "less.x86_64", "libICE.x86_64", "libIDL.x86_64", "libSM.x86_64", "libX11-common.noarch", "libX11.x86_64", "libXScrnSaver.x86_64", "libXau.x86_64", "libXcomposite.x86_64", "libXcursor.x86_64", "libXdamage.x86_64", "libXdmcp.x86_64", "libXext.x86_64", "libXfixes.x86_64", "libXfont.x86_64", "libXft.x86_64", "libXi.x86_64", "libXinerama.x86_64", "libXmu.x86_64", "libXpm.x86_64", "libXrandr.x86_64", "libXrender.x86_64", "libXres.x86_64", "libXt.x86_64", "libXtst.x86_64", "libXv.x86_64", "libXvMC.x86_64", "libXxf86dga.x86_64", "libXxf86misc.x86_64", "libXxf86vm.x86_64", "libacl.x86_64", "libaio.x86_64", "libarchive.x86_64", "libart_lgpl.x86_64", "libasyncns.x86_64", "libatasmart.x86_64", "libattr.x86_64", "libavc1394.x86_64", "libblkid.x86_64", "libbonobo.x86_64", "libbonoboui.x86_64", "libcacard.x86_64", "libcanberra-gtk2.x86_64", "libcanberra.x86_64", "libcap-ng.x86_64", "libcap.x86_64", "libcdio.x86_64", "libcgroup.x86_64", "libcom_err-devel.x86_64", "libcom_err.x86_64", "libcroco.x86_64", "libcurl.x86_64", "libdmx.x86_64", "libdrm.x86_64", "libdv.x86_64", "libedit.x86_64", "libepoxy.x86_64", "liberation-fonts-common.noarch", "liberation-mono-fonts.noarch", "liberation-sans-fonts.noarch", "liberation-serif-fonts.noarch", "libertas-usb8388-firmware.noarch", "libevdev.x86_64", "libevent.x86_64", "libexif.x86_64", "libffi.x86_64", "libfontenc.x86_64", "libfprint.x86_64", "libgail-gnome.x86_64", "libgcc.i686", "libgcc.x86_64", "libgcrypt.x86_64", "libgdata.x86_64", "libglade2.x86_64", "libgnome.x86_64", "libgnomecanvas.x86_64", "libgnomekbd.x86_64", "libgnomeui.x86_64", "libgomp.x86_64", "libgovirt.x86_64", "libgpg-error.x86_64", "libgsf.x86_64", "libgssglue.x86_64", "libgtop2.x86_64", "libgudev1.x86_64", "libgweather.x86_64", "libibverbs.x86_64", "libical.x86_64", "libidn.x86_64", "libiec61883.x86_64", "libjpeg-turbo.x86_64", "libkadm5.x86_64", "libmcpp.x86_64", "libmng.x86_64", "libnih.x86_64", "libnl.x86_64", "libnotify.x86_64", "libogg.x86_64", "liboil.x86_64", "libpcap.x86_64", "libpciaccess.x86_64", "libpng.x86_64", "libproxy-bin.x86_64", "libproxy-python.x86_64", "libproxy.x86_64", "libraw1394.x86_64", "librdmacm.x86_64", "librsvg2.x86_64", "libsamplerate.x86_64", "libselinux-devel.x86_64", "libselinux-python.x86_64", "libselinux-utils.x86_64", "libselinux.x86_64", "libsemanage-python.x86_64", "libsemanage.x86_64", "libsepol-devel.x86_64", "libsepol.x86_64", "libshout.x86_64", "libsmbclient.x86_64", "libsndfile.x86_64", "libsoup.x86_64", "libss.x86_64", "libssh2.x86_64", "libstdc++.x86_64", "libtalloc.x86_64", "libtasn1.x86_64", "libtdb.x86_64", "libtevent.x86_64", "libthai.x86_64", "libtheora.x86_64", "libtiff.x86_64", "libtirpc.x86_64", "libtool-ltdl.x86_64", "libudev.x86_64", "libusb.x86_64", "libusb1.x86_64", "libuser.x86_64", "libutempter.x86_64", "libuuid.x86_64", "libv4l.x86_64", "libvirt-client.x86_64", "libvirt-python.x86_64", "libvirt.x86_64", "libvisual.x86_64", "libvorbis.x86_64", "libvpx.x86_64", "libwacom-data.noarch", "libwacom.x86_64", "libwnck.x86_64", "libxcb.x86_64", "libxkbfile.x86_64", "libxklavier.x86_64", "libxml2-python.x86_64", "libxml2.x86_64", "libxshmfence.x86_64", "libxslt.x86_64", "lklug-fonts.noarch", "lm_sensors-libs.x86_64", "logrotate.x86_64", "lohit-assamese-fonts.noarch", "lohit-bengali-fonts.noarch", "lohit-devanagari-fonts.noarch", "lohit-gujarati-fonts.noarch", "lohit-kannada-fonts.noarch", "lohit-oriya-fonts.noarch", "lohit-punjabi-fonts.noarch", "lohit-tamil-fonts.noarch", "lohit-telugu-fonts.noarch", "lua.x86_64", "lvm2-cluster.x86_64", "lvm2-libs.x86_64", "lvm2.x86_64", "lzo.x86_64", "lzop.x86_64", "m2crypto.x86_64", "m4.x86_64", "madan-fonts.noarch", "mailcap.noarch", "mailx.x86_64", "make.x86_64", "man.x86_64", "mcpp.x86_64", "mdadm.x86_64", "mesa-dri-drivers.x86_64", "mesa-dri-filesystem.x86_64", "mesa-dri1-drivers.x86_64", "mesa-libEGL.x86_64", "mesa-libGL.x86_64", "mesa-libGLU.x86_64", "mesa-libgbm.x86_64", "mesa-libxatracker.x86_64", "mesa-private-llvm.x86_64", "metacity.x86_64", "mingetty.x86_64", "mlocate.x86_64", "mod_ssl.x86_64", "modcluster.x86_64", "module-init-tools.x86_64", "mozilla-filesystem.x86_64", "mpfr.x86_64", "mtdev.x86_64", "mtools.x86_64", "mysql-libs.x86_64", "nano.x86_64", "nautilus-extensions.x86_64", "nautilus-open-terminal.x86_64", "nautilus.x86_64", "nc.x86_64", "ncurses-base.x86_64", "ncurses-libs.x86_64", "ncurses.x86_64", "net-snmp-libs.x86_64", "net-snmp-utils.x86_64", "net-snmp.x86_64", "net-tools.x86_64", "netcf-libs.x86_64", "newt-python.x86_64", "newt.x86_64", "nfs-utils-lib.x86_64", "nfs-utils.x86_64", "notification-daemon.x86_64", "nspr.x86_64", "nss-softokn-freebl.i686", "nss-softokn-freebl.x86_64", "nss-softokn.x86_64", "nss-sysinit.x86_64", "nss-tools.x86_64", "nss-util.x86_64", "nss.x86_64", "ntp.x86_64", "ntpdate.x86_64", "numactl.x86_64", "numad.x86_64", "oddjob.x86_64", "openais.x86_64", "openaislib.x86_64", "openldap.x86_64", "openslp.x86_64", "openssh-askpass.x86_64", "openssh-clients.x86_64", "openssh-server.x86_64", "openssh.x86_64", "openssl-devel.x86_64", "openssl.x86_64", "p11-kit-trust.x86_64", "p11-kit.x86_64", "paktype-fonts-common.noarch", "paktype-naqsh-fonts.noarch", "paktype-tehreer-fonts.noarch", "pam.x86_64", "pango.x86_64", "parted.x86_64", "passwd.x86_64", "patch.x86_64", "pciutils-libs.x86_64", "pciutils.x86_64", "pcre.x86_64", "perl.x86_64", "perl-CGI.x86_64", "perl-CPAN.x86_64", "perl-Compress-Raw-Zlib.x86_64", "perl-Compress-Zlib.x86_64", "perl-Crypt-SSLeay.x86_64", "perl-DBD-Pg.x86_64", "perl-DBI.x86_64", "perl-Digest-SHA.x86_64", "perl-Digest-SHA1.x86_64", "perl-ExtUtils-MakeMaker.x86_64", "perl-ExtUtils-ParseXS.x86_64", "perl-HTML-Parser.x86_64", "perl-HTML-Tagset.noarch", "perl-IO-Compress-Base.x86_64", "perl-IO-Compress-Zlib.x86_64", "perl-IO-Socket-SSL.noarch", "perl-JSON.noarch", "perl-Module-Pluggable.x86_64", "perl-Net-LibIDN.x86_64", "perl-Net-SSLeay.x86_64", "perl-Net-Telnet.noarch", "perl-PCP-PMDA.x86_64", "perl-Pod-Escapes.x86_64", "perl-Pod-Simple.x86_64", "perl-Sys-Virt.x86_64", "perl-TermReadKey.x86_64", "perl-Test-Harness.x86_64", "perl-Test-Simple.x86_64", "perl-Time-HiRes.x86_64", "perl-URI.noarch", "perl-XML-Parser.x86_64", "perl-YAML-Tiny.noarch", "perl-devel.x86_64", "perl-libs.x86_64", "perl-libwww-perl.noarch", "perl-version.x86_64", "pexpect.noarch", "phonon-backend-gstreamer.x86_64", "pinentry.x86_64", "pixman.x86_64", "pkgconfig.x86_64", "plymouth-core-libs.x86_64", "plymouth-gdm-hooks.x86_64", "plymouth-graphics-libs.x86_64", "plymouth-plugin-label.x86_64", "plymouth-plugin-two-step.x86_64", "plymouth-scripts.x86_64", "plymouth-system-theme.noarch", "plymouth-theme-rings.noarch", "plymouth-utils.x86_64", "plymouth.x86_64", "pm-utils.x86_64", "policycoreutils-python.x86_64", "policycoreutils.x86_64", "polkit-desktop-policy.noarch", "polkit-gnome.x86_64", "polkit.x86_64", "popt.x86_64", "portreserve.x86_64", "postfix.x86_64", "ppl.x86_64", "procps.x86_64", "psmisc.x86_64", "pth.x86_64", "pulseaudio-gdm-hooks.x86_64", "pulseaudio-libs-glib2.x86_64", "pulseaudio-libs.x86_64", "pulseaudio-module-gconf.x86_64", "pulseaudio-module-x11.x86_64", "pulseaudio-utils.x86_64", "pulseaudio.x86_64", "pyOpenSSL.x86_64", "pycairo.x86_64", "pygobject2.x86_64", "pygpgme.x86_64", "pygtk2-libglade.x86_64", "pygtk2.x86_64", "pyorbit.x86_64", "python-argparse.noarch", "python-dateutil.noarch", "python-decorator.noarch", "python-deltarpm.x86_64", "python-dmidecode.x86_64", "python-ethtool.x86_64", "python-iniparse.noarch", "python-libs.x86_64", "python-lxml.x86_64", "python-magic.x86_64", "python-pycurl.x86_64", "python-six.noarch", "python-suds.noarch", "python-urlgrabber.noarch", "python-virtinst.noarch", "python.x86_64", "qemu-img.x86_64", "qemu-kvm-tools.x86_64", "qemu-kvm.x86_64", "ql2100-firmware.noarch", "ql2200-firmware.noarch", "ql23xx-firmware.noarch", "ql2400-firmware.noarch", "ql2500-firmware.noarch", "qt-sqlite.x86_64", "qt-x11.x86_64", "qt.x86_64", "quota.x86_64", "radvd.x86_64", "rarian-compat.x86_64", "rarian.x86_64", "rdma.noarch", "readline.x86_64", "redhat-bookmarks.noarch", "redhat-logos.noarch", "redhat-menus.noarch", "resource-agents.x86_64", "rest.x86_64", "rgmanager.x86_64", "ricci.x86_64", "rootfiles.noarch", "rpcbind.x86_64", "rpm-libs.x86_64", "rpm-python.x86_64", "rpm.x86_64", "rsync.x86_64", "rsyslog.x86_64", "rt61pci-firmware.noarch", "rt73usb-firmware.noarch", "rtkit.x86_64", "samba-common.x86_64", "samba-winbind-clients.x86_64", "samba-winbind.x86_64", "screen.x86_64", "seabios.x86_64", "sed.x86_64", "selinux-policy-targeted.noarch", "selinux-policy.noarch", "setools-libs-python.x86_64", "setools-libs.x86_64", "setup.noarch", "sg3_utils-libs.x86_64", "sg3_utils.x86_64", "sgabios-bin.noarch", "sgml-common.noarch", "shadow-utils.x86_64", "shared-mime-info.x86_64", "sil-padauk-fonts.noarch", "slang.x86_64", "smc-fonts-common.noarch", "smc-meera-fonts.noarch", "smp_utils.x86_64", "snappy.x86_64", "sound-theme-freedesktop.noarch", "speex.x86_64", "spice-glib.x86_64", "spice-gtk-python.x86_64", "spice-gtk.x86_64", "spice-server.x86_64", "spice-vdagent.x86_64", "sqlite.x86_64", "startup-notification.x86_64", "stix-fonts.noarch", "sudo.x86_64", "syslinux-nonlinux.noarch", "syslinux.x86_64", "sysstat.x86_64", "system-config-firewall-base.noarch", "system-gnome-theme.noarch", "system-icon-theme.noarch", "system-setup-keyboard.x86_64", "sysvinit-tools.x86_64", "taglib.x86_64", "tar.x86_64", "tcl.x86_64", "tcp_wrappers-libs.x86_64", "tcp_wrappers.x86_64", "telnet.x86_64", "tftp.x86_64", "tftp-server.x86_64", "thai-scalable-fonts-common.noarch", "thai-scalable-waree-fonts.noarch", "tibetan-machine-uni-fonts.noarch", "tog-pegasus-libs.x86_64", "tog-pegasus.x86_64", "tuned.noarch", "tzdata.noarch", "udev.x86_64", "udisks.x86_64", "un-core-dotum-fonts.noarch", "un-core-fonts-common.noarch", "unique.x86_64", "unzip.x86_64", "upstart.x86_64", "usbredir.x86_64", "usbutils.x86_64", "usermode.x86_64", "ustr.x86_64", "util-linux-ng.x86_64", "vgabios.noarch", "vim-common.x86_64", "vim-enhanced.x86_64", "vim-filesystem.x86_64", "vim-minimal.x86_64", "vino.x86_64", "virt-manager.x86_64", "virt-viewer.x86_64", "virt-what.x86_64", "vlgothic-fonts-common.noarch", "vlgothic-fonts.noarch", "vte.x86_64", "wavpack.x86_64", "wdaemon.x86_64", "wget.x86_64", "which.x86_64", "wqy-zenhei-fonts.noarch", "xcb-util.x86_64", "xdg-user-dirs-gtk.x86_64", "xdg-user-dirs.x86_64", "xinetd.x86_64", "xkeyboard-config.noarch", "xml-common.noarch", "xorg-x11-drivers.x86_64", "xorg-x11-drv-acecad.x86_64", "xorg-x11-drv-aiptek.x86_64", "xorg-x11-drv-apm.x86_64", "xorg-x11-drv-ast.x86_64", "xorg-x11-drv-ati-firmware.noarch", "xorg-x11-drv-ati.x86_64", "xorg-x11-drv-cirrus.x86_64", "xorg-x11-drv-dummy.x86_64", "xorg-x11-drv-elographics.x86_64", "xorg-x11-drv-evdev.x86_64", "xorg-x11-drv-fbdev.x86_64", "xorg-x11-drv-fpit.x86_64", "xorg-x11-drv-glint.x86_64", "xorg-x11-drv-hyperpen.x86_64", "xorg-x11-drv-i128.x86_64", "xorg-x11-drv-i740.x86_64", "xorg-x11-drv-intel.x86_64", "xorg-x11-drv-keyboard.x86_64", "xorg-x11-drv-mach64.x86_64", "xorg-x11-drv-mga.x86_64", "xorg-x11-drv-mouse.x86_64", "xorg-x11-drv-mutouch.x86_64", "xorg-x11-drv-nouveau.x86_64", "xorg-x11-drv-nv.x86_64", "xorg-x11-drv-openchrome.x86_64", "xorg-x11-drv-penmount.x86_64", "xorg-x11-drv-qxl.x86_64", "xorg-x11-drv-r128.x86_64", "xorg-x11-drv-rendition.x86_64", "xorg-x11-drv-s3virge.x86_64", "xorg-x11-drv-savage.x86_64", "xorg-x11-drv-siliconmotion.x86_64", "xorg-x11-drv-sis.x86_64", "xorg-x11-drv-sisusb.x86_64", "xorg-x11-drv-synaptics.x86_64", "xorg-x11-drv-tdfx.x86_64", "xorg-x11-drv-trident.x86_64", "xorg-x11-drv-v4l.x86_64", "xorg-x11-drv-vesa.x86_64", "xorg-x11-drv-vmmouse.x86_64", "xorg-x11-drv-vmware.x86_64", "xorg-x11-drv-void.x86_64", "xorg-x11-drv-voodoo.x86_64", "xorg-x11-drv-wacom.x86_64", "xorg-x11-drv-xgi.x86_64", "xorg-x11-server-Xorg.x86_64", "xorg-x11-server-common.x86_64", "xorg-x11-server-utils.x86_64", "xorg-x11-utils.x86_64", "xorg-x11-xauth.x86_64", "xorg-x11-xinit.x86_64", "xorg-x11-xkb-utils.x86_64", "xulrunner.x86_64", "xvattr.x86_64", "xz-libs.x86_64", "xz-lzma-compat.x86_64", "xz.x86_64", "yajl.x86_64", "yelp.x86_64", "yum-metadata-parser.x86_64", "yum.noarch", "zd1211-firmware.noarch", "zenity.x86_64", "zlib-devel.x86_64", "zlib.x86_64", ### On CentOS DVD 2 "compat-libstdc++-33.i686", "libstdc++-devel.i686", "libstdc++.i686", ]; # These are packages specific to one source $conf->{sys}{packages}{rhel} = [ "python-gudev.x86_64", # python-gudev-147.1-4.el6_0.1.x86_64.rpm "python-rhsm.x86_64", # python-rhsm-1.14.3-1.el6.x86_64.rpm "python-rhsm-certificates.x86_64", # python-rhsm-certificates "redhat-indexhtml.noarch", # redhat-indexhtml-6-6.el6.noarch.rpm "redhat-release-server.x86_64", # redhat-release-server-6Server-6.7.0.3.el6.x86_64.rpm "redhat-support-lib-python.noarch", # redhat-support-lib-python-0.9.7-3.el6.noarch.rpm "redhat-support-tool.noarch", # redhat-support-tool-0.9.7-4.el6.noarch.rpm "rhn-check.noarch", # rhn-check-1.0.0.1-32.el6.noarch.rpm "rhn-client-tools.noarch", # rhn-client-tools-1.0.0.1-32.el6.noarch.rpm "rhn-setup.noarch", # rhn-setup-1.0.0.1-32.el6.noarch.rpm "rhnlib.noarch", # rhnlib-2.5.22-15.el6.noarch.rpm "rhnsd.x86_64", # rhnsd-4.9.3-2.el6.x86_64.rpm "subscription-manager.x86_64", # subscription-manager-1.14.10-1.el6.x86_64.rpm "subscription-manager-rhsm.x86_64", "subscription-manager-rhsm-certificates.x86_64", "yum-rhn-plugin.noarch", # yum-rhn-plugin-0.9.1-58.el6.noarch.rpm ]; $conf->{sys}{packages}{centos} = [ "yum-plugin-fastestmirror.noarch", # yum-plugin-fastestmirror-1.1.30-30.el6.noarch.rpm "centos-indexhtml.noarch", # centos-indexhtml-6-2.el6.centos.noarch.rpm "centos-release.x86_64", # centos-release-6-7.el6.centos.12.3.x86_64.rpm # These are not available on RHEL's ISO, so we get them from the AN!Repo when the user is # using RHEL, but from CentOS when the user is using CentOS. "mtr.x86_64", "mtr-gtk.x86_64", "pcp.x86_64", "pcp-conf.x86_64", "pcp-gui.x86_64", "pcp-libs.x86_64", "perl-Algorithm-Diff.noarch", "perl-Text-Diff.noarch", "python-pcp.x86_64", "sharutils.x86_64", "syslinux-tftpboot.noarch", "yum-plugin-priorities.noarch", ]; # Alteeve-source packages $conf->{sys}{packages}{alteeve} = [ "alteeve-repo.noarch", "bash-completion.noarch", "drbd84-utils.x86_64", "kmod-drbd84.x86_64", "ntfs-3g.x86_64", "ntfsprogs.x86_64", "perl-Mail-RFC822-Address.noarch", "perl-Net-SSH2.x86_64", "perl-XML-NamespaceSupport.noarch", "perl-XML-SAX.noarch", "perl-XML-SAX-Base.noarch", "perl-XML-SAX-Expat.noarch", "perl-XML-Simple.noarch", "postgresql95.x86_64", "postgresql95-contrib.x86_64", "postgresql95-libs.x86_64", "postgresql95-plperl.x86_64", "postgresql95-server.x86_64", "shorewall.noarch", "shorewall-core.noarch", "trickle.x86_64", ]; # If the user is using RHEL, we will append these packages to get them from AN!Repo as we can't get # them on the RHEL ISO. if ($conf->{sys}{os_type} eq "rhel") { push @{$conf->{sys}{packages}{alteeve}}, "mtr.x86_64"; push @{$conf->{sys}{packages}{alteeve}}, "mtr-gtk.x86_64"; # See rhbz#1315856 push @{$conf->{sys}{packages}{alteeve}}, "pcp.x86_64"; # Only pcp-gui is missing, but it's RPM complains about the other three not having .anvil... push @{$conf->{sys}{packages}{alteeve}}, "pcp-conf.x86_64"; push @{$conf->{sys}{packages}{alteeve}}, "pcp-gui.x86_64"; push @{$conf->{sys}{packages}{alteeve}}, "pcp-libs.x86_64"; push @{$conf->{sys}{packages}{alteeve}}, "python-pcp.x86_64"; push @{$conf->{sys}{packages}{alteeve}}, "perl-Algorithm-Diff.noarch"; push @{$conf->{sys}{packages}{alteeve}}, "perl-Text-Diff.noarch"; push @{$conf->{sys}{packages}{alteeve}}, "sharutils.x86_64"; push @{$conf->{sys}{packages}{alteeve}}, "syslinux-tftpboot.noarch"; # These disappeared in 6.8 and may not be needed now. # "xorg-x11-drv-modesetting.x86_64", # "xorg-x11-glamor.x86_64", } # Binary packages from third parties $conf->{sys}{packages}{third_party} = { Fujitsu => [ "SVSystemMonitor.noarch", "ServerViewConnectorService.x86_64", "srv-cimprovider.x86_64", "srvmagt-agents.x86_64", "srvmagt-eecd.x86_64", "srvmagt-mods_src.x86_64", ], Avago => [ "MegaCli.noarch", "storcli.noarch", ], }; return(0); } # This collects the command line switches sub get_switches { my ($conf) = @_; my $last_argument = ""; foreach my $argument (@ARGV) { if ($last_argument eq "raw") { # Don't process anything. $conf->{switches}{raw} .= " $argument"; } elsif ($argument =~ /^-/) { # If the argument is just '--', appeand everything after it to 'raw'. $conf->{sys}{switch_count}++; if ($argument eq "--") { $last_argument = "raw"; $conf->{switches}{raw} = ""; } else { ($last_argument) = ($argument =~ /^-{1,2}(.*)/)[0]; if ($last_argument =~ /=/) { # Break up the variable/value. ($last_argument, my $value) = (split /=/, $last_argument, 2); $conf->{switches}{$last_argument} = $value; } else { $conf->{switches}{$last_argument} = "#!SET!#"; } } } else { if ($last_argument) { $conf->{switches}{$last_argument} = $argument; $last_argument = ""; } elsif (($argument eq "start") or ($argument eq "stop") or ($argument eq "status")) { $conf->{switches}{$argument} = 1; } else { # Got a value without an argument. $conf->{switches}{error} = 1; } } } # Clean up the initial space added to 'raw'. if ($conf->{switches}{raw}) { $conf->{switches}{raw} =~ s/^ //; } # Debug if ($conf->{sys}{debug} >= 2) { foreach my $variable (sort {$a cmp $b} keys %{$conf->{switches}}) { print __LINE__."; [ Debug ] - switches::$variable: [".$conf->{switches}{$variable}."]\n"; } } return(0); } # Print the usage information. sub print_usage { my ($conf) = @_; my $help = " -=] Anvil! - Generate ISO DESCRIPTION This program will read in a Red Hat Enterprise Linux v6.10 or CentOS v6.10 ISO and use it to generate an Anvil! install ISO. This tool is provided as a way to respect the Red Hat and CentOS trademarks while providing users with a convenient way to build an Anvil! based on their preferred distibution. This tool does NOT alter any of the source RPMs. Instead, it creates a new 'Packages' directory with a subset of the source RPMs needed by the Striker dashboard and Anvil! nodes. It also adds additional RPMs as provided by Alteeve's Niche!. SWITCHES "; # Removed for now. =cut --accept-third-party-licenses This switch confirms that you have read and agree to the following third party agreements. Fujitsu: - ".$conf->{sys}{third_party}{Fujitsu}{terms}." ASIX: - ".$conf->{sys}{third_party}{ASIX}{terms}." Avago: - ".$conf->{sys}{third_party}{Avago}{terms}." With this agreement, support for this hardware will be added to the Anvil! ISO. =cut; $help .= " -h, -?, --help Show this dialogue and exit. --master By default, this installer will grab the latest release of Striker from the Github repository. If you want to download the testing version though, you can specify 'master' here to get the current git master. --no-refresh By default, if the Striker source file (master.zip) is found, it will be removed and re-downloaded to make sure it is the latest version. This switch tells the program NOT to download again, and just use the existing version. --no-source-validation Normally, this toold runs an md5sum against the source ISO(s) and varifies that the sum matches a known-good sum. If you are using a modified source, you can use this switch to skip that validation process. --release <tag> Downloads a specific Striker version based on a github release tag. --source This must be the full path and file name to the source ISO to base the Anvil! ISO on. If you provide a RHEL ISO, the resulting Anvil! ISO will include RPMs appropriate for registering against Red Hat. Please note that you will need to register against Red Hat and entitle the new machines with the 'Optional' Add-On. The nodes will also need the 'Resilient Storage' Add-On as well. If you provide a CentOS ISO, the RHEL packages will be skipped and CentOS-specific packages will be added. -v, -vv, -vvv Enable debugging output (each additional 'v' increases the verbosity). -y This automatically answers 'yes' to creating new directories. EXAMPLES Build a RHEL-based Anvil! ISO: ./anvil-generate-iso --source ./rhel-server-6.10-x86_64-dvd.iso Build a CentOS-based Anvil ISO: ./anvil-generate-iso --source ./CentOS-6.10-x86_64-bin-DVD1.iso,./CentOS-6.10-x86_64-bin-DVD2.iso SUPPORT ".$conf->{sys}{ks}{support_url}." Alteeve's Niche! Inc. "; open (my $file_handle, ">", "/tmp/${THIS_FILE}.help") or die __LINE__."; Couldn't write help to /tmp/, error was: $!\n"; print $file_handle $help; close $file_handle; system("/usr/bin/less /tmp/${THIS_FILE}.help"); return(0); } ### Contributed by Shaun Fryer and Viktor Pavlenko by way of TPM. # This is a helper to the below '_make_hash_reference' function. It is called # each time a new string is to be created as a new hash key in the passed hash # reference. sub _add_hash_reference { my ($href1, $href2) = @_; for my $key (keys %$href2) { if (ref $href1->{$key} eq 'HASH') { _add_hash_reference($href1->{$key}, $href2->{$key}); } else { $href1->{$key} = $href2->{$key}; } } } ### Contributed by Shaun Fryer and Viktor Pavlenko by way of TPM. # This takes a string with double-colon seperators and divides on those # double-colons to create a hash reference where each element is a hash key. sub _make_hash_reference { my ($href, $key_string, $value) = @_; my @keys = split /::/, $key_string; my $last_key = pop @keys; my $_href = {}; $_href->{$last_key} = $value; while (my $key = pop @keys) { my $elem = {}; $elem->{$key} = $_href; $_href = $elem; } _add_hash_reference($href, $_href); }