#!/usr/bin/perl # # This software was created by Alteeve's Niche! Inc. and has been released # under the terms of the GNU GPL version 2. # # https://alteeve.com # # Exit Codes: # 0 - Success # 1 - The referenced network interface seen when the user unplugged a network # cable did not (properly) record that interfaces MAC address. # 2 - The referenced network interface seen when the user unplugged a network # cable did not (properly) record that interfaces current device name. # 3 - # 4 - Insufficient number of network interfaces found. This program requires # two interfaces to run. Wireless interfaces are not supported. # 5 - The '$conf->{path}{backups}' value is not set. # 6 - The '$conf->{path}{backups}' backup directory failed to be created. The # failure warning should be displayed immediately prior to this error. # 7 - backup_file() was called without a specified file to backup. # 8 - Failed to find the '$conf->{path}{network_configs}' directory. I hope # for the user's sake that this is a program problem... # 9 - There was one or more problems while sanity checking the command line # options. The problems will be displayed prior to the exit. # 10 - No switches passed. # 11 - Failed to bring up a network interface and network configuration was # requested. # 12 - Failed to install all required packages. # 13 - OS isn't RHEL based. # 14 - OS isn't EL6.x # 15 - Unable to get the apache user's UID. # 16 - Unable to get the apache user's GID. # 17 - Installer didn't run as 'root'. # 18 - Failed to download a requested file. # 19 - Failed to copy a file. # 20 - Failed to create a symlink. # 21 - Failed to rsync a file or directory. # 22 - Failed to find CentOS source ISO. # 23 - Failed to find RHEL source ISO. # 24 - Unable to parse repo file name from path. # 25 - Failed to download the repository. # 26 - BCN not specified or found and DHCP server config requested. # 27 - BCN not specified and IP not defined in ifcfg file. # 28 - Failed to install one or more required packaged for the GUI. # 29 - Failed to download a GPG key for a repo that was just added. # 30 - Failed to find custom striker configuration file. # 31 - Failed to start the databased server. # 32 - Failed to set postgres to start on boot # 33 - Failed to create the Striker database user. # 34 - Failed to create the Striker database. # 35 - Failed to load initial database tables. # 36 - Failed to restart database server. # 37 - Failed to access the database server as 'postgres'. Striker password did # not work when set in the postgres user's ~/.pgpass file. # 38 - Failed to register against Red Hat. # 39 - Failed to configure shorewall because the BCN subnet mask could not be # converted to CIDR notation. # 40 - Failed to install perl-Crypt-SSLeay, unable to download a repository # at an HTTPS URL. # 41 - Failed to install AN::Tools # 42 - Failed to enable or disable ScanCore. # 43 - User specified a host UUID, but it is invalid. # 44 - The UEFI boot file wasn't found # 45 - The --source mount is not valid # # TODO: - When installing without --rhel-iso or --centos-iso, look for existing # source in /var/www/html/{centos6,rhel6} and, if so, setup kickstart/PXE. # # BUG: # - If the BCN isn't 10.20.4.{1,2}, the kickstart files fail. # - If the installer re-runs with a new subnet (IFN, at least) and shorewall is configured, shorewall breaks # access to the dashboard. # my $THIS_FILE = $0; $THIS_FILE =~ s/^.*\///; use strict; use warnings; use File::Path qw(make_path); # No capes!^H^H^H^Hbuffers! $| = 1; my $conf = { daemons => { # These will be both chkconfig on'ed and started after # 'disable' is processes. # NOTE: 'haldaemon' needs to restart before 'acpid' will work, # if 'acpid' was just installed. enable => [ "acpid", "gpm", "haldaemon", "httpd", "iptables", "irqbalance", "ktune", "network", "nfs", "ntpd", "ntpdate", "tuned", "xinetd" ], # These will be both chkconfig off'ed and stopped. disable => [ "ip6tables", "ipmidetectd", "kdump", "NetworkManager", "numad", "shorewall", ], }, directory => { apache => "/etc/httpd", apache_manifests => "/var/www/html/manifests", apache_subdirs => ["archive", "cache", "media", "status"], apache_files => "/var/www/html/files", # Files that will be used to populate /sbin/Striker/ will go here. centos_pxe_web => "html/centos6", # Appeanded to 'document_root' + arch appended after centos_tftp_boot => "centos6", # Appeanded to 'pxelinux_boot' + arch document_root => "/var/www/", media => "/var/www/home/media", perl_library => "/usr/share/perl5", perl_source => "/sbin/striker/AN", pxelinux_boot => "/var/lib/tftpboot/boot", pxelinux_config => "/var/lib/tftpboot/pxelinux.cfg", pxelinux => "/var/lib/tftpboot/", pxelinux_efi => "/var/lib/tftpboot/efi64", pxe_iso_subdirs => ["iso", "img", "ks", "files"], # appended to {centos,rhel}_pxe_web + arch rhel_pxe_web => "html/rhel6", # Appeanded to 'document_root' + arch appended after rhel_tftp_boot => "rhel6", # Appeanded to 'pxelinux_boot' + arch source => "./striker", sql_files => "/etc/striker/SQL", striker_config => "/etc/striker", striker_tools => "/sbin/striker", # Change the library directory in the various tools if this changes! udev_rules => "/etc/udev/rules.d/", yum_repos => "/etc/yum.repos.d", }, executable => { 'striker-update-drivers' => "/sbin/striker/striker-update-drivers", cp => "/bin/cp", chkconfig => "/sbin/chkconfig", 'chmod' => "/bin/chmod", 'chown' => "/bin/chown", createrepo => "/usr/bin/createrepo", curl => "/usr/bin/curl", dmidecode => "/usr/sbin/dmidecode", echo => "/bin/echo", gcc => "/usr/bin/gcc", git => "/usr/bin/git", hostname => "/bin/hostname", htpasswd => "/usr/bin/htpasswd", ifup => "/sbin/ifup", ip => "/sbin/ip", iptables => "/sbin/iptables", 'iptables-save' => "/sbin/iptables-save", ln => "/bin/ln", lsmod => "/sbin/lsmod", lspci => "/sbin/lspci", lsusb => "/usr/sbin/lsusb", 'lwp-download' => "/usr/bin/lwp-download", make => "/usr/bin/make", MegaCli64 => "/opt/MegaRAID/MegaCli/MegaCli64", MegaCli64_link => "/sbin/MegaCli64", modprobe => "/sbin/modprobe", mount => "/bin/mount", mv => "/bin/mv", passwd => "/usr/bin/passwd", ping => "/bin/ping", restorecon => "/sbin/restorecon", 'rhn-channel' => "/usr/sbin/rhn-channel", rhnreg_ks => "/usr/sbin/rhnreg_ks", rm => "/bin/rm", rsync => "/usr/bin/rsync", sed => "/bin/sed", semanage => "/usr/sbin/semanage", setenforce => "/usr/sbin/setenforce", setsebool => "/usr/sbin/setsebool", scancore => "/sbin/striker/ScanCore/ScanCore", start_udev => "/sbin/start_udev", storcli64 => "/opt/MegaRAID/storcli/storcli64", storcli64_link => "/sbin/storcli64", 'subscription-manager' => "/usr/sbin/subscription-manager", 'ssh-keygen' => "/usr/bin/ssh-keygen", su => "/bin/su", tar => "/bin/tar", touch => "/bin/touch", umount => "/bin/umount", uname => "/bin/uname", unzip => "/usr/bin/unzip", wget => "/usr/bin/wget", yum => "/usr/bin/yum", }, packages => { # These get set to '1' when their found to be installed. to_install => { acpid => 0, 'alteeve-repo' => 0, 'bash-completion' => 0, ccs => 0, 'cim-schema' => 0, 'cluster-cim' => 0, 'compat-libstdc++-33.i686' => 0, cpp => 0, createrepo => 0, 'cyrus-sasl' => 0, 'cyrus-sasl-plain' => 0, dhcp => 0, expect => 0, 'fence-agents' => 0, ftp => 0, gcalctool => 0, gcc => 0, 'gcc-c++' => 0, 'glibc-devel' => 0, httpd => 0, irqbalance => 0, 'kernel-devel' => 0, 'kernel-headers' => 0, libcdio => 0, 'libstdc++.i686' => 0, 'libstdc++-devel.i686' => 0, mailx => 0, man => 0, mlocate => 0, mod_ssl => 0, mtr => 0, 'mtr-gtk' => 0, nano => 0, 'ntfs-3g' => 0, ntfsprogs => 0, # NFS packages used for remote usb function. 'nfs-utils' => 0, 'nfs-utils-lib' => 0, ntp => 0, patch => 0, pciutils => 0, pcp => 0, 'pcp-gui' => 0, 'perl-CGI' => 0, 'perl-CPAN' => 0, 'perl-Crypt-SSLeay' => 0, 'perl-DBD-Pg' => 0, 'perl-Digest-SHA' => 0, 'perl-IO-Socket-SSL' => 0, 'perl-libwww-perl' => 0, 'perl-Net-SSH2' => 0, 'perl-Net-SSLeay' => 0, 'perl-Net-Telnet' => 0, 'perl-Mail-RFC822-Address' => 0, 'perl-TermReadKey' => 0, 'perl-Test-Simple' => 0, 'perl-XML-SAX' => 0, 'perl-XML-Simple' => 0, 'perl-YAML-Tiny' => 0, 'policycoreutils-python' => 0, postfix => 0, postgresql95 => 0, 'postgresql95-contrib' => 0, 'postgresql95-libs' => 0, 'postgresql95-plperl' => 0, 'postgresql95-server' => 0, rsync => 0, screen => 0, sharutils => 0, shorewall => 0, syslinux => 0, sysstat => 0, 'syslinux-tftpboot' => 0, 'openssl-devel' => 0, 'tftp-server' => 0, tuned => 0, unzip => 0, 'util-linux-ng' => 0, 'vim-common' => 0, 'vim-enhanced' => 0, wget => 0, xinetd => 0, }, ipmi_packages => { freeipmi => 0, 'freeipmi-bmc-watchdog' => 0, 'freeipmi-ipmidetectd' => 0, ipmitool => 0, #NetworkManager => 0, #'NetworkManager-gnome' => 0, OpenIPMI => 0, 'OpenIPMI-libs' => 0, }, gui_packages => { 'abyssinica-fonts' => 0, 'alsa-plugins-pulseaudio' => 0, 'at-spi' => 0, 'cjkuni-uming-fonts' => 0, 'control-center' => 0, 'control-center-extra' => 0, 'dejavu-sans-fonts' => 0, 'dejavu-sans-mono-fonts' => 0, 'dejavu-serif-fonts' => 0, dbus => 0, eog => 0, firefox => 0, gdm => 0, 'gdm-plugin-fingerprint' => 0, 'gdm-user-switch-applet' => 0, 'glx-utils' => 0, 'gnome-applets' => 0, 'gnome-media' => 0, 'gnome-packagekit' => 0, 'gnome-panel' => 0, 'gnome-power-manager' => 0, 'gnome-screensaver' => 0, 'gnome-session' => 0, 'gnome-terminal' => 0, 'gnome-utils' => 0, 'gnome-vfs2-smb' => 0, 'google-crosextra-caladea-fonts' => 0, 'google-crosextra-carlito-fonts' => 0, 'gvfs-archive' => 0, 'gvfs-fuse' => 0, 'gvfs-smb' => 0, hal => 0, 'jomolhari-fonts' => 0, 'khmeros-base-fonts' => 0, 'kurdit-unikurd-web-fonts' => 0, 'liberation-mono-fonts' => 0, 'liberation-sans-fonts' => 0, 'liberation-serif-fonts' => 0, 'lklug-fonts' => 0, 'lohit-assamese-fonts' => 0, 'lohit-bengali-fonts' => 0, 'lohit-devanagari-fonts' => 0, 'lohit-gujarati-fonts' => 0, 'lohit-kannada-fonts' => 0, 'lohit-oriya-fonts' => 0, 'lohit-punjabi-fonts' => 0, 'lohit-tamil-fonts' => 0, 'lohit-telugu-fonts' => 0, 'madan-fonts' => 0, metacity => 0, nautilus => 0, 'nautilus-open-terminal' => 0, 'notification-daemon' => 0, 'openssh-askpass' => 0, 'paktype-naqsh-fonts' => 0, 'paktype-tehreer-fonts' => 0, 'perl-Sys-Virt' => 0, 'perl-Text-Diff' => 0, 'plymouth-system-theme' => 0, 'polkit-gnome' => 0, 'pulseaudio-module-gconf' => 0, 'pulseaudio-module-x11' => 0, 'sil-padauk-fonts' => 0, 'smc-meera-fonts' => 0, 'spice-vdagent' => 0, 'stix-fonts' => 0, 'thai-scalable-waree-fonts' => 0, 'tibetan-machine-uni-fonts' => 0, 'un-core-dotum-fonts' => 0, vino => 0, 'virt-manager' => 0, 'vlgothic-fonts' => 0, wdaemon => 0, 'wqy-zenhei-fonts' => 0, 'xdg-user-dirs-gtk' => 0, 'xorg-x11-drivers' => 0, 'xorg-x11-server-utils' => 0, 'xorg-x11-server-Xorg' => 0, 'xorg-x11-utils' => 0, 'xorg-x11-xauth' => 0, 'xorg-x11-xinit' => 0, xvattr => 0, yelp => 0, }, # This is populated by 'get_list_of_installed_packages()' and # uses the found package name as the key and sets the value to # '1'. installed => {}, }, path => { an_repo => "/etc/yum.repos.d/an-el6.repo", apache_config => "/etc/httpd/conf/httpd.conf", apache_home => "/var/www/home", apache_htpasswd => "/var/www/home/htpasswd", apache_manifest_conf => "/etc/httpd/conf.d/manifest.conf", apache_shell => "/bin/bash", backups => "/root/anvil", cron_root => "/var/spool/cron/root", dhcpd_conf => "/etc/dhcp/dhcpd.conf", gcc => "/usr/bin/gcc", hostname => "/etc/sysconfig/network", host_uuid => "/etc/striker/host.uuid", hosts => "/etc/hosts", inittab => "/etc/inittab", iptables => "/etc/sysconfig/iptables", last_kernel => "/etc/striker/last_kernel", logrotate_config => "/etc/logrotate.d/anvil", network_configs => "/etc/sysconfig/network-scripts", network_map_cache => "/root/.striker-network-map.cache", ntp_conf => "/etc/ntp.conf", os_release => "/etc/redhat-release", passwd => "/etc/passwd", pg_hba_conf => "/var/lib/pgsql/9.5/data/pg_hba.conf", pgsql_log => "/var/lib/pgsql/9.5/data/pg_log/postgresql-.log", postgresql_conf => "/var/lib/pgsql/9.5/data/postgresql.conf", postgres_pgpass => "/var/lib/pgsql/9.5/.pgpass", power_sh => "/etc/acpi/actions/power.sh", pxe_efi_source => "/boot/efi/EFI/redhat/grub.efi", pxe_efi_target => "bootx64.efi", # This gets appended to directory::pxelinux_efi #pxe_background_source => "/var/www/html/skins/alteeve/images/pxe_splash_1024_768.png", pxe_background_source => "/var/www/html/skins/alteeve/images/splash.jpg", pxe_default_config => "/var/lib/tftpboot/pxelinux.cfg/default", uefi_default_config => "/var/lib/tftpboot/efi64/efidefault", rc_local => "/etc/rc.local", rhn_file => "/etc/sysconfig/rhn/systemid", selinux_config => "/etc/selinux/config", shorewall => { interfaces => "/etc/shorewall/interfaces", masq => "/etc/shorewall/masq", policy => "/etc/shorewall/policy", rules => "/etc/shorewall/rules", shorewall_conf => "/etc/shorewall/shorewall.conf", zones => "/etc/shorewall/zones", }, sshd_config => "/etc/ssh/sshd_config", striker_config => "/etc/striker/striker.conf", striker_log => "/var/log/striker.log", udev_persistent_net => "/etc/udev/rules.d/70-persistent-net.rules", udev_striker_usb => "/etc/udev/rules.d/99-striker-usb.rules", xinetd_tftp_config => "/etc/xinetd.d/tftp", }, # These will be compiled during the install. setuid => { # Compile by entering 'gcc -o $out_file $c_file' 'anvil-kick-apc-ups' => { c_file => "/sbin/striker/call_anvil-kick-apc-ups.c", out_file => "/sbin/striker/call_anvil-kick-apc-ups", }, 'gather-system-info' => { c_file => "/sbin/striker/call_gather-system-info.c", out_file => "/sbin/striker/call_gather-system-info", }, 'striker-push-ssh' => { c_file => "/sbin/striker/call_striker-push-ssh.c", out_file => "/sbin/striker/call_striker-push-ssh", }, 'striker-configure-vmm' => { c_file => "/sbin/striker/call_striker-configure-vmm.c", out_file => "/sbin/striker/call_striker-configure-vmm", }, 'striker-delete-anvil' => { c_file => "/sbin/striker/call_striker-delete-anvil.c", out_file => "/sbin/striker/call_striker-delete-anvil", }, 'striker-manage-install-target' => { c_file => "/sbin/striker/call_striker-manage-install-target.c", out_file => "/sbin/striker/call_striker-manage-install-target", }, 'striker-merge-dashboards' => { c_file => "/sbin/striker/call_striker-merge-dashboards.c", out_file => "/sbin/striker/call_striker-merge-dashboards", }, # These just pass along directly to existing systems calls, hence the lack of 'call_' # prefixes. 'control_dhcpd' => { c_file => "/sbin/striker/control_dhcpd.c", out_file => "/sbin/striker/control_dhcpd", }, 'control_iptables' => { c_file => "/sbin/striker/control_iptables.c", out_file => "/sbin/striker/control_iptables", }, 'control_libvirtd' => { c_file => "/sbin/striker/control_libvirtd.c", out_file => "/sbin/striker/control_libvirtd", }, 'control_shorewall' => { c_file => "/sbin/striker/control_shorewall.c", out_file => "/sbin/striker/control_shorewall", }, 'do_dd' => { c_file => "/sbin/striker/do_dd.c", out_file => "/sbin/striker/do_dd", }, 'touch_striker.log' => { c_file => "/sbin/striker/touch_striker.log.c", out_file => "/sbin/striker/touch_striker.log", }, }, permissions => { dhcpd_dir => { path => "/etc/dhcp", owner => "root", group => "root", mode => "0755", recursive => 0, }, document_root => { path => "/var/www", owner => "apache", group => "apache", mode => "g+w", recursive => 1, }, striker_log => { path => "/var/log/striker.log", owner => "apache", group => "apache", mode => "g+w", recursive => 0, selinux => "httpd_sys_content_t", }, striker_config_dir => { path => "/etc/striker", owner => "apache", group => "apache", mode => "g+w", recursive => 1, }, striker_config => { path => "/etc/striker/striker.conf", owner => "apache", group => "apache", mode => "0660", recursive => 0, selinux => "httpd_sys_content_t", }, ssh_config => { path => "/etc/ssh/ssh_config", owner => "root", group => "apache", mode => "g+w", recursive => 0, selinux => "httpd_sys_content_t", }, hosts => { path => "/etc/hosts", owner => "root", group => "apache", mode => "g+w", recursive => 0, selinux => "httpd_sys_content_t", }, pxelinux => { path => "/var/lib/tftpboot/pxelinux.cfg", owner => "root", group => "root", mode => "755", recursive => 0, }, striker_tools => { path => "/sbin/striker", owner => "root", group => "root", mode => "755", recursive => 1, }, # The following entries must sort last to ensure the setuid doesn't get changed by a change # to the parent directory. 'z_call_striker-push-ssh' => { path => "/sbin/striker/call_striker-push-ssh", owner => "root", group => "root", mode => "6755", recursive => 0, }, 'z_call_anvil-kick-apc-ups' => { path => "/sbin/striker/call_anvil-kick-apc-ups", owner => "root", group => "root", mode => "6755", recursive => 0, }, 'z_call_striker-configure-vmm' => { path => "/sbin/striker/call_striker-configure-vmm", owner => "root", group => "root", mode => "6755", recursive => 0, }, 'z_call_striker-delete-anvil' => { path => "/sbin/striker/call_striker-delete-anvil", owner => "root", group => "root", mode => "6755", recursive => 0, }, 'z_call_striker-manage-install-target' => { path => "/sbin/striker/call_striker-manage-install-target", owner => "root", group => "root", mode => "6755", recursive => 0, }, 'z_call_striker-merge-dashboards' => { path => "/sbin/striker/call_striker-merge-dashboards", owner => "root", group => "root", mode => "6755", recursive => 0, }, z_control_dhcpd => { path => "/sbin/striker/control_dhcpd", owner => "root", group => "root", mode => "6755", recursive => 0, }, z_control_shorewall => { path => "/sbin/striker/control_shorewall", owner => "root", group => "root", mode => "6755", recursive => 0, }, z_control_iptables => { path => "/sbin/striker/control_iptables", owner => "root", group => "root", mode => "6755", recursive => 0, }, z_control_libvirtd => { path => "/sbin/striker/control_libvirtd", owner => "root", group => "root", mode => "6755", recursive => 0, }, z_check_dvd => { path => "/sbin/striker/check_dvd", owner => "root", group => "root", mode => "6755", recursive => 0, }, z_do_dd => { path => "/sbin/striker/do_dd", owner => "root", group => "root", mode => "6755", recursive => 0, }, 'z_call_gather-system-info' => { path => "/sbin/striker/call_gather-system-info", owner => "root", group => "root", mode => "6755", recursive => 0, }, 'z_touch_striker.log' => { path => "/sbin/striker/touch_striker.log", owner => "root", group => "root", mode => "6755", recursive => 0, }, }, # Not used yet. scancore => { scan_agents => { bonding => { configuration_body => "", configuration_file => "bonding.conf", enabled => 1, }, }, }, switches => { 'autostart-dhcpd' => "", 'b' => "", # 'c' => "", # 'custom-config' => "", # /path/to/striker.conf 'd' => "", # 'ethtool-opts' => "", # "" gui => 1, 'h' => "", help => "", 'host-uuid' => "", 'i' => "", # ,dg=[,dns1=,dns2=] 'ipmi-user' => "", # 'ipmi-ip' => "", # [,gw=] 'use-lsi' => "", mtu => 1500, 'n' => "", # 'no-reboot' => "", 'no-striker-configure-vmm' => "", ntp => "", # 'p' => "", # 'peer-dashboard' => "", # rhn => "", # 'router-mode' => 0, 'run-rc-local' => "", source => "", 'u' => "", # 'y' => "", }, sys => { apache_group => "apache", apache_timeout => "60000", # This will go away with AJAX apache_user => "apache", asix_driver => "AX88179_178A_LINUX_DRIVER_v1.14.4_SOURCE", bonds_exist => 0, # We can't currently remap interfaces when bonds are in use because MACs aren't displayed properly in 'ip addr'. create_bonds => 0, # Gets set to '1' if 4 NICs are found. customer => "Striker Dashboard", # Debug level; 0 == silent except errors, 1 == normal output, 2 == basic debug, 3 == extra debug, 4 == prepare your buffers debug => 1, hostname => "", internet_access => 0, ipmi_gateway => "", ipmi_ip => "", ipmi_netmask => "", ipmi_password => "", ipmi_user => "", is_rhel => 0, logrotate => { 'striker.log' => { count => 5, # Backups made before deletion. frequency => "weekly", # daily, weekly, monthly, yearly maxsize => "100M", # Rotates if bigger than this, regardless of frequency }, 'ScanCore.log' => { count => 5, # Backups made before deletion. frequency => "weekly", # daily, weekly, monthly, yearly maxsize => "100M", # Rotates if bigger than this, regardless of frequency } }, network_map_cache_read => 0, reboot_needed => 0, register_with_rhn => 0, repositories => "", rhn_user => "", rhn_password => "", skip_hostname => 0, skip_mail => 1, skip_network => 0, ssl_checked => 0, stable_version => "2.0.9", ### If you change these, change the matching values in ### Common.pm so that InstallManifest runs match. scancore_database => "scancore", striker_user => "admin", striker_password => "", # no default password, evar! switch_count => 0, tarball_dir => "striker-#!striker_version!#", update_os => 1, yum_switches => "-y", # Adds: [--disablerepo='*' --enablerepo='striker*'] if no internet connection found. }, url => { an_repo => "https://www.alteeve.com/an-repo/el6/alteeve-repo-latest.noarch.rpm", git_master => "https://github.com/ClusterLabs/striker/archive/master.zip", striker => "https://github.com/ClusterLabs/striker/archive/#!striker_version!#.zip", latest_release => "https://alteeve.com/striker_latest.txt", }, # This will be changed to have two of each link if four NICs are found. nics => ["bcn_link1", "ifn_link1"], network => { bcn => { ip => "", netmask => "", network => "", interface => "", }, ifn => { ip => "", netmask => "", network => "", interface => "", gateway => "", dns1 => "", dns2 => "", }, }, pxe => { centos => { enabled => 0, node1_ks => "pxe-new-node01.ks", node2_ks => "pxe-new-node02.ks", striker1_ks => "pxe-new-striker01.ks", striker2_ks => "pxe-new-striker02.ks", short_name => "centos6", }, rhel => { enabled => 0, node1_ks => "pxe-new-node01.ks", node2_ks => "pxe-new-node02.ks", striker1_ks => "pxe-new-striker01.ks", striker2_ks => "pxe-new-striker02.ks", short_name => "rhel6", } }, }; # Make sure we're running as 'root' # $< == real UID, $> == effective UID if (($< != 0) && ($> != 0)) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - I am sorry, but this program must be run as the root user.\n", 0); exit(17); } # Make sure we got what we needed from the command line. get_switches($conf); if (($conf->{switches}{h}) or ($conf->{switches}{help}) or ($conf->{switches}{'?'})) { print_usage($conf); exit(0); } if (not $conf->{sys}{switch_count}) { print_usage($conf); exit(10); } # Catch typos. $conf->{switches}{'no-os-updates'} = 1 if $conf->{switches}{'no-os-update'}; system('clear'); # Credit to Martin Allchin for this ASCII art! logger($conf, $THIS_FILE, __LINE__, " ##############################################################################", 1); logger($conf, $THIS_FILE, __LINE__, " # ___ _ _ _ The Anvil! Dashboard #", 1); logger($conf, $THIS_FILE, __LINE__, " # / __| |_ _ _(_) |_____ _ _ -=] Installer #", 1); logger($conf, $THIS_FILE, __LINE__, " # \\__ \\ _| '_| | / / -_) '_| #", 1); logger($conf, $THIS_FILE, __LINE__, " # |___/\\__|_| |_|_\\_\\___|_| #", 1); logger($conf, $THIS_FILE, __LINE__, " # https://alteeve.com/w/Striker #", 1); logger($conf, $THIS_FILE, __LINE__, " ##############################################################################", 1); # This sanity-checks the passed in command line switches and returns '1' if # any problems were found. if (sanity_check_switches($conf)) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Exiting on command line switch error(s).\n", 1); exit(9); } else { logger($conf, $THIS_FILE, __LINE__, "Command line switches appear sane, proceeding.", 3); } # Make sure that the Vault repo is enabled and others are not. enable_vault_repo($conf); # Make sure we're on EL6.x verify_os($conf); # Check for and compile/install network drivers compile_network_drivers($conf); # Check to see if we have an internet connection so that, if not, we can disable external repos. verify_internet_access($conf, 0); # Get the hostname if the user didn't set it. get_hostname($conf); # Register with Red Hat, if needed. if ($conf->{sys}{register_with_rhn}) { register_with_rhn($conf); } # Add the AN!Repo (or remove it if the user doesn't want it). if ($conf->{switches}{'no-an-repo'}) { remove_an_repo($conf); } else { # Add our repo to the list of RPMs to install. $conf->{packages}{to_install}{'alteeve-repo'} = 0; add_an_repo($conf); } # Add the user's repo if specified if ($conf->{switches}{r}) { add_repo($conf); } # Go into the loop that asks the user to unplug cables until they are happy # with their selection. When it exits, we go to work. if (not $conf->{sys}{skip_network}) { # Make backups, of course. backup_original_files($conf); # Freeze NetworkManager if it is running. freeze_networkmanager($conf); # Make sure all NICs are up. If any aren't, start them. start_all_nics($conf); # Now ask the user to map their network. if (not $conf->{sys}{bonds_exist}) { logger($conf, $THIS_FILE, __LINE__, "-=] Configuring network to enable access to Anvil! systems.", 1); ### BUG: Caching is broken... probably worth a re-write #load_network_map_cache($conf); until(select_nics($conf)) { sleep 1; } } } # If passed, set the hostname. if (not $conf->{sys}{skip_hostname}) { set_hostname($conf); } # if the user has specified custom NTP servers, add them. if ($conf->{switches}{ntp}) { configure_ntp($conf); } # Now configure the Striker dashboard. setup_striker($conf); # The network needs to be configured last because, if the user installed a GUI, # NetworkManager would try to reconfigure the network immediately and mess # things up. By this point, now, NetworkManager should be disabled. if (not $conf->{sys}{skip_network}) { # If we're here, the user has OK'ed the changes. reconfigure_network($conf); } # If requested, run the contents of rc.local. This is sometimes needed if the # user has customizations that might get clobbered by a --gui install. if ($conf->{switches}{'run-rc-local'}) { my $shell_call = "sh ".$conf->{path}{rc_local}; logger($conf, $THIS_FILE, __LINE__, "Running: [$shell_call] on user request.", 1); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); } close $file_handle; } else { logger($conf, $THIS_FILE, __LINE__, "User did not request invocation of ".$conf->{path}{rc_local}.".", 3); } # If the user should reboot, tell them. logger($conf, $THIS_FILE, __LINE__, "Installation of Striker is complete!\n", 1); if ($conf->{sys}{reboot_needed}) { if ($conf->{switches}{'no-reboot'}) { logger($conf, $THIS_FILE, __LINE__, "- Some of the changes require a reboot to take effect.", 1); logger($conf, $THIS_FILE, __LINE__, " When you are ready, type 'reboot' and press .\n", 1); } else { # Reboot! logger($conf, $THIS_FILE, __LINE__, "- Some of the changes require a reboot to take effect.", 1); logger($conf, $THIS_FILE, __LINE__, " Rebooting in five seconds! (press + to abort.\n", 1); sleep 5; my $shell_call = "sync && reboot"; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); } close $file_handle; } } exit(0); ############################################################################### # Here be function! # ############################################################################### sub enable_vault_repo { my ($conf) = @_; print "Checking to see if CentOS Vault needs to be enabled.\n"; my $repo_dir = '/etc/yum.repos.d/'; local(*DIRECTORY); opendir(DIRECTORY, $repo_dir); logger($conf, $THIS_FILE, __LINE__, "- Opened: [".$repo_dir."]", 1); while(my $file = readdir(DIRECTORY)) { next if $file eq "."; next if $file eq ".."; next if $file !~ /^CentOS-/; my $full_path = $repo_dir."/".$file; print "- File: [".$full_path."]\n"; if ($file eq "CentOS-Vault.repo") { print "Checking the vault repo config now.\n"; my $update = 0; my $old_body = ""; open (my $file_handle, "<", $full_path) or die "Failed to read: [".$full_path."], error was: [".$!."]\n"; while(<$file_handle>) { $old_body .= $_; } close $file_handle; my $new_body = "# CentOS-Vault.repo # # CentOS Vault holds packages from previous releases within the same CentOS Version # these are packages obsoleted by the current release and should usually not # be used in production #----------------- [C6.10-base] name=CentOS-6.10 - Base baseurl=http://vault.centos.org/6.10/os/\$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6 enabled=1 [C6.10-updates] name=CentOS-6.10 - Updates baseurl=http://vault.centos.org/6.10/updates/\$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6 enabled=1 [C6.10-extras] name=CentOS-6.10 - Extras baseurl=http://vault.centos.org/6.10/extras/\$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6 enabled=1 [C6.10-contrib] name=CentOS-6.10 - Contrib baseurl=http://vault.centos.org/6.10/contrib/\$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6 enabled=0 [C6.10-centosplus] name=CentOS-6.10 - CentOSPlus baseurl=http://vault.centos.org/6.10/centosplus/\$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6 enabled=0 "; if ($old_body eq $new_body) { print "- Vault is already enabled.\n"; } else { print "- Backing up the original vault repo.\n"; backup_file($conf, $full_path); print "Writing the new vault repo... "; open (my $file_handle, ">", $full_path) or die "Failed to write: [".$full_path."], error was: [".$!."]\n";; print $file_handle $new_body; close $file_handle; print "Done.\n"; } } else { # Back it up and then remove it. print "- Backing up: [".$full_path."]\n"; backup_file($conf, $full_path); print "- Removing: [".$full_path."]\n"; unlink $full_path or die "Failed to remove: [".$full_path."], the error was: [".$!."]\n"; print "- Done.\n"; } } closedir(DIRECTORY); return(0); } # This adds the an-repo, if needed. sub add_an_repo { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Adding the AN! software repository.", 1); if (-e $conf->{path}{an_repo}) { logger($conf, $THIS_FILE, __LINE__, "- Already exists, skipping.", 1); } else { my $shell_call = $conf->{executable}{yum}." ".$conf->{sys}{yum_switches}." install ".$conf->{url}{an_repo}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; while(<$file_handle>) { print $_; } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; if (-e $conf->{path}{an_repo}) { logger($conf, $THIS_FILE, __LINE__, "- Successfully installed!", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- [ Warning ] - Failed to install the repo! Is there a working internet connection?", 1); logger($conf, $THIS_FILE, __LINE__, "- [ Warning ] - Can you access: [".$conf->{url}{an_repo}."]?", 1); logger($conf, $THIS_FILE, __LINE__, "- [ Warning ] - Install will continue, but it might fail if all packages aren't already available locally", 1); } } return(0); } # Check for and compile/install network drivers sub compile_network_drivers { my ($conf) = @_; ### For now, this only supports the AXIS # First, if setup where we'll find the ASIX driver. my $driver_directory = "/var/www/html/".$conf->{pxe}{centos}{short_name}."/x86_64/img/Tools/ASIX"; logger($conf, $THIS_FILE, __LINE__, "sys::is_rhel: [".$conf->{sys}{is_rhel}."], switches::source: [".$conf->{switches}{source}."].", 1); if ($conf->{switches}{source}) { $driver_directory = $conf->{switches}{source}."/Tools/ASIX"; # Make sure we've got our dependencies installed. verify_internet_access($conf, 0); if ($conf->{sys}{internet_access}) { logger($conf, $THIS_FILE, __LINE__, "- [ Note ] - Some packages are needed to compile the network drivers. Checking to see if they're installed now.", 1); if (-e $conf->{path}{last_kernel}) { # Remove any previous possibly-failed attempts. unlink $conf->{path}{last_kernel}; } my $shell_call = $conf->{executable}{yum}." ".$conf->{sys}{yum_switches}." install unzip kernel-devel-\$(uname -r) kernel-source-\$(uname -r) kernel-headers-\$(uname -r) gcc"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; while(<$file_handle>) { print $_; } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; } else { # Remind the user that they may need to install the needed packaged. logger($conf, $THIS_FILE, __LINE__, "- [ Warning ] - No internet access detected! Building the network drivers requires that build tools, not included with minimal installs, be available. The compilation of the driver may fail!", 1); } } elsif ($conf->{sys}{is_rhel}) { $driver_directory = "/var/www/html/".$conf->{pxe}{rhel}{short_name}."/x86_64/img/Tools/ASIX"; } logger($conf, $THIS_FILE, __LINE__, "driver_directory: [$driver_directory]", 2); # Do I have an ASIX AX8817[89] NIC? my $asix_exists = 0; my $shell_call = $conf->{executable}{lsusb}." -v"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if (($line =~ /AX88178/i) or ($line =~ /AX88179/i) or ($line =~ /D-Link DUB-1312/i)) { $asix_exists = 1; logger($conf, $THIS_FILE, __LINE__, "An ASIX AX8817{8,9}-based USB3 to Ethernet adapter was found.", 1); logger($conf, $THIS_FILE, __LINE__, "- If possible, the driver will be compiled and installed now.", 1); last; } } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "asix_exists: [$asix_exists]", 3); if ($asix_exists) { # Get a list of the interfaces we have already. We'll use this when we're done to find the # name of the new interface. get_interface_list($conf, "old"); # We've got the driver, so add the rc.local script. write_driver_update_script($conf); # Is the driver already loaded? my $driver_loaded = 0; my $shell_call = $conf->{executable}{lsmod}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /usbnet.*?ax8817/i) { $driver_loaded = 1; logger($conf, $THIS_FILE, __LINE__, "- The driver is already loaded.", 1); } } close $file_handle; my $driver_file = ""; my $driver_path = ""; logger($conf, $THIS_FILE, __LINE__, "driver_loaded: [$driver_loaded], driver_directory: [$driver_directory].", 2); if (not $driver_loaded) { logger($conf, $THIS_FILE, __LINE__, "- The driver is not loaded.", 1); } if ((not $driver_loaded) && (-e $driver_directory)) { # Do I have a driver? logger($conf, $THIS_FILE, __LINE__, "- The driver directory was found.", 1); local(*DIRECTORY); opendir(DIRECTORY, $driver_directory); while(my $file = readdir(DIRECTORY)) { my $full_path = "$driver_directory/$file"; logger($conf, $THIS_FILE, __LINE__, "file: [$file], full_path: [$full_path]", 3); next if not -f $full_path; if ($full_path =~ /AX88179_178A_LINUX_DRIVER_v(.*?)_SOURCE\.zip/) { $driver_path = $full_path; $driver_file = $file; logger($conf, $THIS_FILE, __LINE__, "driver_file: [$driver_file], driver_path: [$driver_path]", 3); logger($conf, $THIS_FILE, __LINE__, "- The driver file was found, it will be extracted and compiled.", 1); } } closedir(DIRECTORY); my $extract_directory = ""; logger($conf, $THIS_FILE, __LINE__, "driver_path: [$driver_path]", 3); if ($driver_path) { # Remove the existing extract if it exists. my $remove_directory = ""; local(*DIRECTORY); opendir(DIRECTORY, "/tmp"); while(my $file = readdir(DIRECTORY)) { if ($file =~ /^AX88179_178A_LINUX_DRIVER_/) { my $full_path = "/tmp/$file"; logger($conf, $THIS_FILE, __LINE__, "file: [$file], full_path: [$full_path]", 3); if (-d $full_path) { $remove_directory = $file; logger($conf, $THIS_FILE, __LINE__, "- The old driver source will be removed: [/tmp/$remove_directory]", 1); } } } closedir(DIRECTORY); # Remove the old driver if ($remove_directory =~ /^AX88/) { my $shell_call = $conf->{executable}{rm}." -rf /tmp/".$remove_directory; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); } } # Unzip it to /tmp/ logger($conf, $THIS_FILE, __LINE__, "- Extracting: [$driver_file].", 3); my $shell_call = $conf->{executable}{unzip}." $driver_path -d /tmp/"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /creating: (\/.*)\//) { $extract_directory = $1; logger($conf, $THIS_FILE, __LINE__, "extract_directory: [$extract_directory]", 3); } } close $file_handle; } logger($conf, $THIS_FILE, __LINE__, "extract_directory: [$extract_directory]", 3); if ($extract_directory) { logger($conf, $THIS_FILE, __LINE__, "- Driver extracted, compiling it now.", 1); my $make_ok = 0; my $shell_call = $conf->{executable}{make}." --directory=".$extract_directory; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /make: Leaving directory/i) { $make_ok = 1;; logger($conf, $THIS_FILE, __LINE__, "make_ok: [$make_ok]", 3); } } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "make_ok: [$make_ok]", 3); if ($make_ok) { my $driver = ""; my $shell_call = $conf->{executable}{make}." install --directory=".$extract_directory; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /(ax8817.*?)\.ko/i) { $driver = $1; logger($conf, $THIS_FILE, __LINE__, "driver: [$driver]", 3); } } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "driver: [$driver]", 3); if ($driver) { my $loaded = 0; my $shell_call = $conf->{executable}{modprobe}." $driver; echo rc:\$?"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /rc:(\d+)/) { $loaded = 1; logger($conf, $THIS_FILE, __LINE__, "- Successfully loaded the: [$driver] driver!", 3); } } close $file_handle; if ($loaded) { # Call 'start_udev' to make sure the new NIC is recorded. my $shell_call = $conf->{executable}{start_udev}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); } close $file_handle; # Record this kernel version $shell_call = $conf->{executable}{uname}." -r > ".$conf->{path}{last_kernel}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); } close $file_handle; # Write a skeleton config file that is set to DHCP # because often this interface is used as the IFN # and is needed to access the web, which is needed # to register the dashboard with Red Hat. setup_new_interface($conf); } else { # Load failed. logger($conf, $THIS_FILE, __LINE__, "- The driver does not appear to have loaded properly.", 1); logger($conf, $THIS_FILE, __LINE__, "- The network mapping operation may fail.", 1); } } else { # The driver didn't install... logger($conf, $THIS_FILE, __LINE__, "- The driver does not appear to have installed properly.", 1); logger($conf, $THIS_FILE, __LINE__, "- The network mapping operation may fail.", 1); } } else { # Something went wrong with the 'make' logger($conf, $THIS_FILE, __LINE__, "- The driver does not appear to have compiled properly.", 1); logger($conf, $THIS_FILE, __LINE__, "- The network mapping operation may fail.", 1); } } else { # Something went wrong with the extraction. logger($conf, $THIS_FILE, __LINE__, "- The driver does not appear to have extracted.", 1); logger($conf, $THIS_FILE, __LINE__, "- The network mapping operation may fail.", 1); } } elsif (not $driver_loaded) { # Warn the user that we don't have the driver. logger($conf, $THIS_FILE, __LINE__, "- The driver was not found.", 1); logger($conf, $THIS_FILE, __LINE__, "- The network mapping operation may fail.", 1); } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); } return(0); } # This writes out a cheesy little bash script to check for and automatically recompile the ASIX drivers when # the kernel changes. (DKMS isn't in base RHEL...) sub write_driver_update_script { my ($conf) = @_; my $driver_directory = "/var/www/html/".$conf->{pxe}{centos}{short_name}."/x86_64/img/Tools/ASIX"; logger($conf, $THIS_FILE, __LINE__, "sys::is_rhel: [".$conf->{sys}{is_rhel}."].", 2); if ($conf->{sys}{is_rhel}) { $driver_directory = "/var/www/html/".$conf->{pxe}{rhel}{short_name}."/x86_64/img/Tools/ASIX"; } logger($conf, $THIS_FILE, __LINE__, "driver_directory: [$driver_directory].", 2); my $asix_driver = $driver_directory."/".$conf->{sys}{asix_driver}.".zip"; # Make sure the directory we'll store the kernel version exists now. if (not -e $conf->{directory}{striker_config}) { mkdir $conf->{directory}{striker_config} or die "$THIS_FILE ".__LINE__."; Failed to create the directory: [".$conf->{directory}{striker_config}."]. The error was: $!\n"; } # If we're running with '--source ', we need to do a few extra things first. if ($conf->{switches}{source}) { # Make sure the files we're going to use are in place. if (not -e $conf->{directory}{striker_tools}) { mkdir $conf->{directory}{striker_tools} or die "$THIS_FILE ".__LINE__."; Failed to create the directory: [".$conf->{directory}{striker_tools}."]. The error was: $!\n"; } # Copy the driver out of the ISO directory. $asix_driver = $conf->{switches}{source}."/Tools/ASIX/".$conf->{sys}{asix_driver}.".zip"; } logger($conf, $THIS_FILE, __LINE__, "asix_driver: [".$asix_driver."].", 2); my $script = " #!/bin/bash # This is a dirty little program to recompile the Siig (ASIX-based) USB3 to Gbit # ehternet adapter often used with NUC-based Striker dashboards when the kernel # version changes. It will be replaced by a proper program later (hopefully). DATADIR=\"".$conf->{directory}{striker_config}."\"; DATAFILE=\"".$conf->{path}{last_kernel}."\"; ASIXDRIVER=\"".$asix_driver."\"; ASIXDIRECTORY=\"/tmp/".$conf->{sys}{asix_driver}."/\"; RECOMPILE=0; THISFILE=\$(basename \"\$0\"); logger -t \$THISFILE \"Checking if the ASIX driver needs to be recompiled...\" if [ -e \"\$DATADIR\" ]; then logger -t \$THISFILE \"Data directory exists, proceeding to check kernel version.\" if [ -e \"\$DATAFILE\" ]; then logger -t \$THISFILE \"Data file exists, comparing kernel version.\" OLDVERSION=\$(cat \$DATAFILE) CURRENTVERSION=\$(uname -r) logger -t \$THISFILE \"Old version was: [\$OLDVERSION], new version is: [\$CURRENTVERSION].\" if [ \$OLDVERSION == \$CURRENTVERSION ]; then logger -t \$THISFILE \"Kernel version is the same. No need to recompile.\" else logger -t \$THISFILE \"Kernel version is different, recompiling.\" RECOMPILE=1; fi else logger -t \$THISFILE \"Data file does not exist. Will create it and recompile.\" RECOMPILE=1; fi else logger -t \$THISFILE \"Data directorty: [\$DATADIR] doesn't exist, not checking drivers.\" fi if [ \$RECOMPILE -eq \"1\" ]; then logger -t \$THISFILE \"Recompiling now...\" ASIXFOUND=\$(lsusb | grep -q AX881; echo \$?) if [ \$ASIXFOUND -eq \"0\" ]; then logger -t \$THISFILE \"ASIX adapter found.\" if [ -e \$ASIXDRIVER ]; then logger -t \$THISFILE \"Driver found, extracting.\" unzip -o \$ASIXDRIVER -d /tmp/ >> /var/log/messages if [ -e \"\${ASIXDIRECTORY}/Makefile\" ]; then logger -t \$THISFILE \"Drivers extracted, compiling now.\" make --directory=\$ASIXDIRECTORY >> /var/log/messages make install --directory=\$ASIXDIRECTORY >> /var/log/messages modprobe ax88179_178a >> /var/log/messages logger -t \$THISFILE \"Restarting the network now.\" /etc/init.d/network restart >> /var/log/messages logger -t \$THISFILE \"Updating the last seen kernel version\" uname -r > \$DATAFILE logger -t \$THISFILE \"Done!\" uname -r > \$DATAFILE else logger -t \$THISFILE \"Failed to extract the drivers. Unable to proceed.\" fi else logger -t \$THISFILE \"Driver not found. Expected it to be in: [\$ASIXDRIVER]. Unable to proceed.\" fi else logger -t \$THISFILE \"ASIX adapter not found.\" fi fi "; # Write it out now. my $shell_call = $conf->{executable}{'striker-update-drivers'}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open (my $file_handle, ">$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to write: [$shell_call], error was: $!\n"; print $file_handle $script; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- ASIX Gigabit-over-USB3 driver recompiler tool written.", 1); # Make it executable. $shell_call = $conf->{executable}{'chmod'}." 755 ".$conf->{executable}{'striker-update-drivers'}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); } close $file_handle; # Now add it to rc.local $shell_call = $conf->{executable}{echo}." ".$conf->{executable}{'striker-update-drivers'}." >> ".$conf->{path}{rc_local}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); } close $file_handle; return(0); } # This function finds the unconfigured interface and sets it up as DHCP and tries to get a lease. sub setup_new_interface { my ($conf) = @_; # Get the updated list of devices get_interface_list($conf, "new"); # Now look for the new interface my $new_interface = ""; foreach my $interface (sort {$a cmp $b} keys %{$conf->{interface}{new}}) { # Did this one exist in the old hash? logger($conf, $THIS_FILE, __LINE__, "interface: [$interface]", 3); if (not $conf->{interface}{old}{$interface}) { # Found it. $new_interface = $interface; logger($conf, $THIS_FILE, __LINE__, "new_interface: [$new_interface]", 3); } } # Configure the interface if I found it. logger($conf, $THIS_FILE, __LINE__, "new_interface: [$new_interface]", 3); if ($new_interface) { logger($conf, $THIS_FILE, __LINE__, "- Found a new interface: [$new_interface], creating a base DHCP config, if needed.", 1); my $config_file = "/etc/sysconfig/network-scripts/ifcfg-".$new_interface; logger($conf, $THIS_FILE, __LINE__, "config_file: [".$config_file."]", 3); if (-e $config_file) { logger($conf, $THIS_FILE, __LINE__, "- The new interface file already has a config file.", 1); } else { my $new_config = " # Temporary interface configuration created by $THIS_FILE DEVICE=\"$new_interface\" BOOTPROTO=\"dhcp\" ONBOOT=\"yes\" "; # Write it out. my $shell_call = ">$config_file"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open (my $file_handle, "$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to write: [$shell_call], error was: $!\n"; print $file_handle $new_config; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Temporary interface config written.", 1); # Call it up. logger($conf, $THIS_FILE, __LINE__, "- Starting the new interface: [$new_interface].", 1); logger($conf, $THIS_FILE, __LINE__, "- This might take a minute if there are no DHCP servers on its network.", 1); $shell_call = $conf->{executable}{ifup}." ".$new_interface; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); } close $file_handle; } } else { # Warn, but proceed. logger($conf, $THIS_FILE, __LINE__, "- Did not find a new interface.", 1); } return(0); } # This records the network interfaces found on the system and stores them in the named hash key. sub get_interface_list { my ($conf, $key) = @_; logger($conf, $THIS_FILE, __LINE__, "Storing the current list of interfaces in the: [$key] hash key.", 3); my $shell_call = $conf->{executable}{ip}." addr"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /^\d+: (.*?): {interface}{$key}{$interface} = 1; logger($conf, $THIS_FILE, __LINE__, "interface::${key}::$interface: [".$conf->{interface}{$key}{$interface}."]", 3); } } close $file_handle; return(0); } # Configure Striker to be a ScanCore DB server sub configure_scancore { my ($conf) = @_; # First, see if we need to initialize the DB. If so, the 'pg_hba.conf' won't exist yet. my $initialized = 0; logger($conf, $THIS_FILE, __LINE__, "Setting up the ScanCore database.", 1); logger($conf, $THIS_FILE, __LINE__, "path::pg_hba_conf: [".$conf->{path}{pg_hba_conf}."]", 3); if (not -e $conf->{path}{pg_hba_conf}) { # Initialization required. my $shell_call = "/etc/init.d/postgresql-9.5 initdb && chkconfig postgresql-9.5 on && chkconfig --list postgresql-9.5"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /OK/) { logger($conf, $THIS_FILE, __LINE__, "- Database initialized.", 1); } elsif ($line =~ /3:on/i) { logger($conf, $THIS_FILE, __LINE__, "- Database set to start on boot.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - Database may not have been initialized or failed to set to start on boot. Expected 'OK' or '3:on', but got: [$line]", 1); } } close $file_handle; } else { logger($conf, $THIS_FILE, __LINE__, "- Database is already initialized.", 1); } # Make sure the server is set to start on boot. my $shell_call = "chkconfig postgresql-9.5 on && chkconfig --list postgresql-9.5"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /3:on/i) { logger($conf, $THIS_FILE, __LINE__, "- Database server will start on boot.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Database failed to set to start on boot. Expected '3:on', but got: [$line]", 0); exit(32); } } close $file_handle; # Prep some variables we will use to update pg_hba.conf. logger($conf, $THIS_FILE, __LINE__, "- Checking database access security.", 1); my $bcn_network = $conf->{network}{bcn}{ip}; my $bcn_subnet = $conf->{network}{bcn}{netmask}; my $ifn_network = $conf->{network}{ifn}{ip}; my $ifn_subnet = $conf->{network}{ifn}{netmask}; # Convert (crudely) the IPs into network ranges and the subnets into CIDR. my $cidr_bcn_netmask = convert_dotted_decimal_to_cidr($conf, $conf->{network}{bcn}{netmask}); if ($cidr_bcn_netmask eq "8") { $bcn_network =~ s/^(\d+)\.\d+\.\d+\.\d+$/$1.0.0.0/; } elsif ($cidr_bcn_netmask eq "16") { $bcn_network =~ s/^(\d+\.\d+)\.\d+\.\d+$/$1.0.0/; } elsif ($cidr_bcn_netmask eq "24") { $bcn_network =~ s/^(\d+\.\d+\.\d+)\.\d+$/$1.0/; } else { # Ya, this is where I need to get smarter logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Currently, only /8, /16 and /24 netmasks are supported for the BCN.", 1); } my $cidr_ifn_netmask = convert_dotted_decimal_to_cidr($conf, $conf->{network}{ifn}{netmask}); if ($cidr_ifn_netmask eq "8") { $ifn_network =~ s/^(\d+)\.\d+\.\d+\.\d+$/$1.0.0.0/; } elsif ($cidr_ifn_netmask eq "16") { $ifn_network =~ s/^(\d+\.\d+)\.\d+\.\d+$/$1.0.0/; } elsif ($cidr_ifn_netmask eq "24") { $ifn_network =~ s/^(\d+\.\d+\.\d+)\.\d+$/$1.0/; } else { # here, too logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Currently, only /8, /16 and /24 netmasks are supported for the IFN.", 1); } logger($conf, $THIS_FILE, __LINE__, "bcn_network: [$bcn_network], ifn_network: [$ifn_network].", 3); # Now update the pg_hba.conf. logger($conf, $THIS_FILE, __LINE__, "cidr_bcn_netmask: [$cidr_bcn_netmask], cidr_ifn_netmask: [$cidr_ifn_netmask]", 3); my $add_bcn = $cidr_bcn_netmask eq "#!INVALID!#" ? 0 : 1; my $add_ifn = $cidr_ifn_netmask eq "#!INVALID!#" ? 0 : 1; logger($conf, $THIS_FILE, __LINE__, "add_bcn: [$add_bcn], add_ifn: [$add_ifn]", 3); my $secure_local = 0; my $raw_pg_hba_config = ""; $shell_call = "<$conf->{path}{pg_hba_conf}"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($file_handle, "$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to read: [$shell_call], error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /^host\s+all\s+all\s+$bcn_network\/$cidr_bcn_netmask\s+md5$/) { logger($conf, $THIS_FILE, __LINE__, "- The BCN network is already configured.", 1); $add_bcn = 0; } if ($line =~ /^host\s+all\s+all\s+$ifn_network\/$cidr_ifn_netmask\s+md5$/) { logger($conf, $THIS_FILE, __LINE__, "- The IFN network is already configured.", 1); $add_ifn = 0; } if ($line =~ /local\s+all\s+all\s+(.*)$/) { my $security = $1; if ($security eq "md5") { logger($conf, $THIS_FILE, __LINE__, "- The local connection is already secured.", 1); } elsif ($security eq "ident") { $line =~ s/ident/md5/; logger($conf, $THIS_FILE, __LINE__, "- The local connection will be secured.", 1); $secure_local = 1; } } $raw_pg_hba_config .= "$line\n"; } close $file_handle; # Rewrite the config if something changed. if (($add_bcn) or ($add_ifn) or ($secure_local)) { # Loop through the raw config and insert the missing network my $new_config = ""; foreach my $line (split/\n/, $raw_pg_hba_config) { logger($conf, $THIS_FILE, __LINE__, ">> line: [$line]", 3); if ($line =~ /# TYPE DATABASE/) { $new_config .= "$line\n"; if ($add_bcn) { my $new_line = "host all all $bcn_network/$cidr_bcn_netmask md5"; $new_config .= "$new_line\n"; logger($conf, $THIS_FILE, __LINE__, "<< new_line: [$new_line]", 3); } if ($add_ifn) { my $new_line = "host all all $ifn_network/$cidr_ifn_netmask md5"; $new_config .= "$new_line\n"; logger($conf, $THIS_FILE, __LINE__, "<< new_line: [$new_line]", 3); } } else { $new_config .= "$line\n"; logger($conf, $THIS_FILE, __LINE__, "<< line: [$line]", 3); } } # Write out the new file. my $shell_call = ">$conf->{path}{pg_hba_conf}"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($file_handle, "$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to write: [$shell_call], error was: $!\n"; print $file_handle $new_config; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Database server security configured.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Database server security already configured.", 1); } # Now start the DB, if needed. $shell_call = " /etc/init.d/postgresql-9.5 status; if [ \"\$?\" -eq '0' ]; then echo 'Running'; else /etc/init.d/postgresql-9.5 start; if [ \"\$?\" -eq '0' ]; then echo 'Started'; else echo 'Failed to start'; fi; fi"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /Running/i) { logger($conf, $THIS_FILE, __LINE__, "Database server already running.", 3); } elsif ($line =~ /Started/i) { logger($conf, $THIS_FILE, __LINE__, "Database server was started.", 3); } elsif ($line =~ /Failed/i) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Database server is not running and failed to start! Unable to proceed.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] The cause of the error may be found in: [".$conf->{path}{pgsql_log}."].", 0); exit(31); } } close $file_handle; # Check to see if this is a re-run of the install. if it appears to be, create a temporary .pgpass # file for postgres my $striker_user = $conf->{sys}{striker_user}; my $striker_password = $conf->{sys}{striker_password}; my $use_password = 0; logger($conf, $THIS_FILE, __LINE__, "- Looks like the 'postgres' user's password is set. Creating temporary: [".$conf->{path}{postgres_pgpass}."]", 1); $shell_call = " if grep -q -P \"^local.*?md5\$\" ".$conf->{path}{pg_hba_conf}."; then echo 'use pgpass' echo \"*:*:*:postgres:$striker_password\" > ".$conf->{path}{postgres_pgpass}." && \ $conf->{executable}{chown} postgres:postgres ".$conf->{path}{postgres_pgpass}." && \ $conf->{executable}{chmod} 600 ".$conf->{path}{postgres_pgpass}." && \ su - postgres -c \"psql template1 -c 'SELECT usename, usesysid FROM pg_catalog.pg_user;'\" fi"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /use pgpass/) { $use_password = 1; } } close $file_handle; # Create the postgresql user, if needed. logger($conf, $THIS_FILE, __LINE__, "- Creating database user: [$striker_user] if needed.", 1); my $create_user = 1; $shell_call = "su - postgres -c \"psql template1 -c 'SELECT usename, usesysid FROM pg_catalog.pg_user;'\""; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; $line =~ s/^\s+//; $line =~ s/\s+$//; $line =~ s/\s+/ /g; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /^$striker_user \| (\d+)/i) { my $id = $1; logger($conf, $THIS_FILE, __LINE__, "- Database user: [$striker_user] already exists with ID: [$id].", 1); $create_user = 0; last; } } close $file_handle; if ($create_user) { logger($conf, $THIS_FILE, __LINE__, "- Creating database user: [$striker_user] now.", 1); my $shell_call = " su - postgres -c \"createuser --no-superuser --createdb --no-createrole $striker_user\" su - postgres -c \"psql template1 -c 'SELECT usename, usesysid FROM pg_catalog.pg_user;'\""; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; $line =~ s/^\s+//; $line =~ s/\s+$//; $line =~ s/\s+/ /g; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /^$striker_user \| (\d+)/i) { my $id = $1; logger($conf, $THIS_FILE, __LINE__, "- Database user: [$striker_user] created with ID: [$id].", 1); $create_user = 0; last; } } close $file_handle; if ($create_user) { # Failed to create the user. logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed to add the database user: [$striker_user]! Unable to proceed.", 0); exit(33); } } else { logger($conf, $THIS_FILE, __LINE__, "- No need to create database user: [$striker_user].", 1); } # Now set the passwords. logger($conf, $THIS_FILE, __LINE__, "- Updating or setting database user's passwords.", 1); foreach my $user ("postgres", $striker_user) { my $shell_call = "su - postgres -c \"psql template1 -c \\\"ALTER ROLE $user WITH PASSWORD '$striker_password';\\\"\""; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /ALTER ROLE/i) { logger($conf, $THIS_FILE, __LINE__, "- Database user: [$striker_user] password set.", 1); } } close $file_handle; } # Update the main postgresql.conf to listen on all interfaces. my $raw_postgresql_config = ""; my $updated = 0; $shell_call = "<$conf->{path}{postgresql_conf}"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($file_handle, "$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to read: [$shell_call], error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /^#listen_addresses = 'localhost'/) { logger($conf, $THIS_FILE, __LINE__, "- Telling the database server to listen on all interfaces.", 1); $line =~ s/#listen_addresses = 'localhost'/listen_addresses = '*'/; $updated = 1; } $raw_postgresql_config .= "$line\n"; } close $file_handle; if ($updated) { # Write it back out. my $shell_call = ">$conf->{path}{postgresql_conf}"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($file_handle, "$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to write: [$shell_call], error was: $!\n"; print $file_handle $raw_postgresql_config; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- interfaces opened.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Database server was already listening on all interfaces.", 1); } # Create the Striker database, if needed. my $scancore_database = $conf->{sys}{scancore_database}; my $create_database = 1; logger($conf, $THIS_FILE, __LINE__, "- Creating database: [$scancore_database] if needed.", 1); $shell_call = "su - postgres -c \"psql template1 -c 'SELECT datname FROM pg_catalog.pg_database;'\""; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; $line =~ s/^\s+//; $line =~ s/\s+$//; $line =~ s/\s+/ /g; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /^$scancore_database$/i) { logger($conf, $THIS_FILE, __LINE__, "- Striker database: [$scancore_database] already exists.", 1); $create_database = 0; last; } } close $file_handle; if ($create_database) { logger($conf, $THIS_FILE, __LINE__, "- Creating Striker database: [$scancore_database] now.", 1); my $shell_call = " su - postgres -c \"createdb --owner $striker_user $scancore_database\" su - postgres -c \"psql template1 -c 'SELECT datname FROM pg_catalog.pg_database;'\""; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; $line =~ s/^\s+//; $line =~ s/\s+$//; $line =~ s/\s+/ /g; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /^$scancore_database$/i) { logger($conf, $THIS_FILE, __LINE__, "- Striker database: [$scancore_database] created.", 1); $create_database = 0; last; } } close $file_handle; if ($create_database) { # Failed to create the user. logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed to add the database: [$create_database]! Unable to proceed.", 0); exit(34); } } else { logger($conf, $THIS_FILE, __LINE__, "- No need to create Striker database.", 1); } # Restart postgresql. logger($conf, $THIS_FILE, __LINE__, "- Restarting database server.", 1); $shell_call = " /etc/init.d/postgresql-9.5 restart /etc/init.d/postgresql-9.5 status; if [ \"\$?\" -eq '0' ]; then echo 'Running'; else echo 'Failed to restart'; fi"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /Running/i) { logger($conf, $THIS_FILE, __LINE__, "- Database server restarted.", 1); } elsif ($line =~ /Failed/i) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Database server failed to restart!", 0); exit(36); } } close $file_handle; # Copy AN::Tools into place. logger($conf, $THIS_FILE, __LINE__, "directory::perl_library: [".$conf->{directory}{perl_library}."], directory::perl_source: [".$conf->{directory}{source}."/AN].", 3); if ((not -e $conf->{directory}{perl_library}."/AN") && (-e $conf->{directory}{source}."/AN")) { logger($conf, $THIS_FILE, __LINE__, "- Copying AN::Tools perl modules to: [".$conf->{directory}{perl_library}."/].", 1); rsync_files($conf, $conf->{directory}{source}."/AN", $conf->{directory}{perl_library}."/"); } elsif (-e $conf->{directory}{perl_library}."/AN") { logger($conf, $THIS_FILE, __LINE__, "- AN::Tools perl modules already exists in: [".$conf->{directory}{perl_library}."/AN], skipping installing it.", 1); } elsif (not -e $conf->{directory}{source}."/AN") { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - AN::Tools perl modules source: [".$conf->{directory}{source}."/AN] doesn't exist nor is it in the target directory: [".$conf->{directory}{perl_library}."/AN], skipping installing it. This will cause Striker to fail!", 1); exit(41); } # Add scanCore to crontab. logger($conf, $THIS_FILE, __LINE__, "- Adding ScanCore to root's cron table.", 1); $shell_call = " if [ ! -e '".$conf->{path}{cron_root}."' ] then echo 'creating empty crontab for root.' echo 'MAILTO=\"\"' > ".$conf->{path}{cron_root}." $conf->{executable}{chown} root:root ".$conf->{path}{cron_root}." $conf->{executable}{chmod} 600 ".$conf->{path}{cron_root}." fi"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($file_handle, "$shell_call 2>&1 |") or die "Failed to read: [$shell_call], error was: $!\n"; while (<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /creating empty/i) { logger($conf, $THIS_FILE, __LINE__, "- Creating empty crontab file for 'root'.", 1); } } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Reading in 'root's cron table.", 1); my $dashboard = $conf->{executable}{scancore}; my $raw_cron = ""; $shell_call = $conf->{path}{cron_root}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call].", 3); open ($file_handle, "<$shell_call") or die "Failed to read: [$shell_call], error was: $!\n"; while (<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /$dashboard/) { # Skip it as we'll add it back later. logger($conf, $THIS_FILE, __LINE__, "- Existing dashboard entry found in 'root's cron, removing it.", 1); } else { $raw_cron .= "$line\n"; } } close $file_handle; # Add our entry $raw_cron .= "*/1 * * * * $dashboard 2>&1\n"; logger($conf, $THIS_FILE, __LINE__, "- Writing out 'root's updated cron table.", 1); $shell_call = $conf->{path}{cron_root}; open ($file_handle, ">$shell_call") or die "Failed to write: [$shell_call], error was: $!\n"; print $file_handle $raw_cron; close $file_handle; # Delete the .pgpass file, if it was created. if ($use_password) { logger($conf, $THIS_FILE, __LINE__, "- Deleting the temporary .pgpass file that was created for the 'postgres' user.", 1); my $shell_call = "rm -f ".$conf->{path}{postgres_pgpass}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] - line: [$line]", 3); } close $file_handle; } # Enable ScanCore in striker.conf, unless requested not to, in which case, disable it.. $shell_call = $conf->{executable}{sed}." -i 's/^scancore::enabled\\(\\s*\\)=\\(\\s*\\)0/scancore::enabled\\1=\\21/' ".$conf->{path}{striker_config}."\n"; my $message = "Enabling ScanCore."; logger($conf, $THIS_FILE, __LINE__, "- $message", 1); $shell_call .= " if \$(grep -q '^scancore::enabled\\s*=\\s*1' ".$conf->{path}{striker_config}."); then echo enabled; else echo disabled; fi"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($file_handle, "$shell_call 2>&1 |") or die "Failed to read: [$shell_call], error was: $!\n"; while (<$file_handle>) { chomp; my $line = $_; if ($line eq "enabled") { logger($conf, $THIS_FILE, __LINE__, "- ScanCore is now enabled.", 1); } elsif ($line eq "disabled") { # Failed to enable! logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed to enable ScanCore!\n", 0); exit(42); } else { logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); } } close $file_handle; # Done. logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # Write entries to syslog and STDOUT. sub logger { my ($conf, $file, $line_number, $message, $level) = @_; $level = 0 if not $level; # Return if the log level is too low. return(0) if $level > $conf->{sys}{debug}; # Print the message to the screen. # If the level is 0 or 1, don't print the file and line numnber if ($level < 2) { $message =~ s/^; //; print "$message\n"; } else { print "$file $line_number; [ Debug ] - $message\n"; } # We split on new-lines so that multi-line message render better in # syslog. my $is_debug = $message =~ /\[ Debug \]/ ? 1 : 0; foreach my $line (split/\n/, $message) { next if not $line; if (($is_debug) && ($line !~ /\[ Debug \]/)) { $line = "[ Debug ] $line"; } $line =~ s/\t/ /g; open my $filehandle, '-|', "logger", "-t", $file, "$line_number: $line" or die "Failed to call: [logger -t $THIS_FILE $line]. Error: $!\n"; while (<$filehandle>) { print $_; } close $filehandle; } return(0); } # Configure the various dashboard tools, like the striker-usb tools. sub configure_striker_tools { my ($conf) = @_; # Create the tools directory, if needed. if (not -e $conf->{directory}{striker_tools}) { mkdir $conf->{directory}{striker_tools} or die "$THIS_FILE ".__LINE__."; Failed to create the directory: [".$conf->{directory}{striker_tools}."]. The error was: $!\n"; } my $source = $conf->{directory}{source}; if (not -e $source."/striker.conf") { $source = $conf->{directory}{source}."/striker"; if (not -e $source."/striker.conf") { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Unable to find the source striker.conf in either: [".$conf->{directory}{source}."/striker.conf] or: [".$source."/striker/striker.conf].\n", 0); exit(30); } } # Now copy the tools we want into place, if they're not already there. logger($conf, $THIS_FILE, __LINE__, "Copying Striker tools and ScanCore into place.", 1); rsync_files($conf, $source."/striker.conf", $conf->{directory}{striker_tools}."/"); rsync_files($conf, $source."/tools/*", $conf->{directory}{striker_tools}."/"); rsync_files($conf, $source."/ScanCore", $conf->{directory}{striker_tools}."/"); rsync_files($conf, $source."/AN", $conf->{directory}{striker_tools}."/"); rsync_files($conf, $source."/cgi-bin/Data", $conf->{directory}{striker_tools}."/"); logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); # Create a tarball for downloading by nodes. if (not -e $conf->{directory}{apache_files}) { # Create the directory logger($conf, $THIS_FILE, __LINE__, "- Creating the apache-accessible download directory: [".$conf->{directory}{apache_files}."]", 1); mkdir $conf->{directory}{apache_files} or die "$THIS_FILE ".__LINE__."; Failed to create the directory: [".$conf->{directory}{apache_files}."]. The error was: $!\n"; } logger($conf, $THIS_FILE, __LINE__, "Creating downloadable '".$conf->{directory}{apache_files}."' directory for nodes to grab.", 1); my $shell_call = $conf->{executable}{tar}." -cvjf ".$conf->{directory}{apache_files}."/striker_tools.tar.bz2 -C ".$conf->{directory}{striker_tools}."/ ."; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); } close $file_handle; # Copy the tools into /var/www/html/files/ so that InstallManifest can grab what it needs, as needed. rsync_files($conf, $source."/tools/*", $conf->{directory}{apache_files}."/"); # Setup the USB tools. write_udev_striker_usb($conf); logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # This writes out the udev rules file used to trigger mounting or unmounting USB drives. sub write_udev_striker_usb { my ($conf) = @_; my $shell_call = $conf->{path}{udev_striker_usb}; logger($conf, $THIS_FILE, __LINE__, "Writing the new udev rules file: [".$conf->{path}{udev_striker_usb}."]", 1); open (my $file_handle, ">$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; print $file_handle "# Generated by: [$THIS_FILE] on: [".get_date($conf)."].\n"; print $file_handle " # This triggers the insert and eject scripts to mount and unmount USB drives on # remote servers when a USB storage device is plugged into a managed port. Both # scripts will simple exit if ".$conf->{directory}{striker_config}."/striker.conf has the value # remote-usb::enable_remote_usb_mount = 0 ACTION==\"add\", KERNEL==\"sd*[!0-9]\", RUN+=\"".$conf->{directory}{striker_tools}."/striker-usb-insert\" ACTION==\"remove\", KERNEL==\"sd*[!0-9]\", RUN+=\"".$conf->{directory}{striker_tools}."/striker-usb-remove\" "; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # Add the user's NTP server(s) if not already in ntp.conf. sub configure_ntp { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Adding NTP servers if needed.", 1); foreach my $ntp_server (split/,/, $conf->{switches}{ntp}) { next if not $ntp_server; logger($conf, $THIS_FILE, __LINE__, "- Checking to see if: [$ntp_server] needs to be added.", 1); my $shell_call = " if \$(grep -q 'server $ntp_server iburst' ".$conf->{path}{ntp_conf}."); then echo exists; else echo adding $ntp_server; echo 'server $ntp_server iburst' >> ".$conf->{path}{ntp_conf}." if \$(grep -q 'server $ntp_server iburst' ".$conf->{path}{ntp_conf}."); then echo added else echo failed fi; fi"; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /exists/i) { logger($conf, $THIS_FILE, __LINE__, "- Already added, skipping.", 1); } elsif ($line =~ /added/i) { logger($conf, $THIS_FILE, __LINE__, "- Added successfully.", 1); } elsif ($line =~ /failed/i) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed to add: [$ntp_server] to: [".$conf->{path}{ntp_conf}."]", 0); } } close $file_handle; } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # Get the hostname if the user didn't set it themselves. sub get_hostname { my ($conf) = @_; if (not $conf->{switches}{n}) { # user didn't set it, so read it in. my $shell_call = $conf->{executable}{hostname}; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); $conf->{sys}{hostname} = $1; last; } close $file_handle; # In case we didn't read it... $conf->{sys}{hostname} = "localhost.localdomain" if not $conf->{sys}{hostname}; } # Pull out the prefix and domain. $conf->{sys}{prefix} = "an"; $conf->{sys}{domain} = "alteeve.com"; if ($conf->{sys}{hostname} =~ /^(\w+)-/) { $conf->{sys}{prefix} = $1; } if ($conf->{sys}{hostname} =~ /^\w+\.(.*)$/) { $conf->{sys}{domain} = $2; } logger($conf, $THIS_FILE, __LINE__, "- sys::prefix: [".$conf->{sys}{prefix}."], sys::domain: [".$conf->{sys}{domain}."].", 2); return(0); } # This checks to see if NetworkManager is running and if the active interface # is NM_CONTROLLER="yes". If so, it sets it to 'no' and restarts NetworkManager sub freeze_networkmanager { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Checking if we need to freeze NetworkManager on the active interface.", 1); my $nm_running = 0; my $active_iface = ""; my $shell_call = "if [ -e '/etc/init.d/NetworkManager' ]; then /etc/init.d/NetworkManager status; echo rc:\$? else echo rc:1 fi"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /^rc:(\d+)/) { my $rc = $1; if ($rc eq "0") { logger($conf, $THIS_FILE, __LINE__, "- NetworkManager is running, will examine interfaces.", 1); $nm_running = 1; } else { # '3' is stopped logger($conf, $THIS_FILE, __LINE__, "- NetworkManager isn't running, freeze not needed.", 1); } } } close $file_handle; my $freeze = {}; if ($nm_running) { # Check running interfaces for IP addresses, check their # ifcfg-X file for 'NM_CONTROLLED' and see if it is 'yes'. If # so, queue it up for switch to 'no'. check_nic_states($conf); foreach my $iface (sort {$a cmp $b} keys %{$conf->{nic}{by_name}}) { # See if it is up already. my $link_state = $conf->{nic}{by_name}{$iface}{link_state}; my $mac = $conf->{nic}{by_name}{$iface}{mac}; my $ip = $conf->{nic}{by_name}{$iface}{ip}; # Link states: # 0 = Up no no link # 1 = Up with link # 2 = Down # I only care about the interface the user might be # using to run the install, so if the NIC isn't up with # an IP address, I can ignore it. if (($link_state eq "1") && ($ip)) { # Link is up. Check its config file. my $nm_controlled = ""; $shell_call = "cat ".$conf->{path}{network_configs}."/ifcfg-$iface"; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /^NM_CONTROLLED=(.*)/) { $nm_controlled = $1; $nm_controlled =~ s/"//; $nm_controlled = lc($nm_controlled); if ($nm_controlled =~ /yes/i) { $freeze->{$iface} = 1; } } } } else { # These links we'll disable in NetworkManager # now. my $shell_call = ""; } } # Now for each interface to freeze, switch 'ONBOOT' to yes, # 'NM_CONTROLLED' to 'no' and then restart NetworkManager. my $shell_call = ""; my $message = "Freezing interfaces: "; foreach my $iface (sort {$a cmp $b} keys %{$freeze}) { my $nic_file = $conf->{path}{network_configs}."/ifcfg-$iface"; $shell_call .= $conf->{executable}{sed}." -i 's/ONBOOT=.*/ONBOOT=\"yes\"/\' $nic_file; "; $shell_call .= $conf->{executable}{sed}." -i 's/NM_CONTROLLED=.*/NM_CONTROLLED=\"no\"/\' $nic_file; "; $message .= "$iface, "; } # If I had any, append the restart if ($shell_call) { $message =~ s/, $//; logger($conf, $THIS_FILE, __LINE__, "- $message", 1); logger($conf, $THIS_FILE, __LINE__, "- Note: Other interfaces may go down temporarily.", 1); $shell_call .= "/etc/init.d/NetworkManager stop; /etc/init.d/network start"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); } close $file_handle; } else { # Nothing needed to be frozen. logger($conf, $THIS_FILE, __LINE__, "- No NICs needed to be frozen.", 1); } } logger($conf, $THIS_FILE, __LINE__, "Done\n", 1); return(0); } # This adds a repo if it doesn't already exist. sub add_repo { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Adding custom repositories:", 1); if ($conf->{switches}{r}) { $conf->{sys}{repositories} .= ",$conf->{switches}{r}"; } logger($conf, $THIS_FILE, __LINE__, "repositories: [".$conf->{switches}{r}."]", 3); foreach my $url (split/,/, $conf->{switches}{r}) { next if not $url; my ($repo_url, $repo_file) = ($url =~ /^(.*)\/(.*?)$/); logger($conf, $THIS_FILE, __LINE__, "repo_url: [$repo_url], repo_file: [$repo_file], url: [$url]", 3); if (not $repo_file) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Unable to parse repository file from path:", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] [$url]", 0); exit(24); } my $full_repo_path = $conf->{directory}{yum_repos}."/$repo_file"; if (-e $full_repo_path) { logger($conf, $THIS_FILE, __LINE__, "- Already downloaded, skipping.", 1); next; } else { # If the $url is 'https://', make sure that # perl-Crypt-SSLeay is available. if (($url =~ /^https:/) && (not $conf->{sys}{ssl_checked})) { logger($conf, $THIS_FILE, __LINE__, "- The repository: [$url] requires SSL to download. Verifying the perl SSL module is installed.", 1); get_list_of_installed_packages($conf); if ($conf->{packages}{installed}{'perl-Crypt-SSLeay'}) { } else { # It needs to be installed. logger($conf, $THIS_FILE, __LINE__, "- The module is not installed, attempting to install it now.", 1); my $shell_call = $conf->{executable}{yum}." ".$conf->{sys}{yum_switches}." install perl-Crypt-SSLeay"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; while(<$file_handle>) { print $_; } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Verifying it installed...\n", 1); get_list_of_installed_packages($conf); if ($conf->{packages}{installed}{'perl-Crypt-SSLeay'}) { logger($conf, $THIS_FILE, __LINE__, "- Success!\n", 1); $conf->{sys}{ssl_checked} = 1; } else { logger($conf, $THIS_FILE, __LINE__, "- Failed! Without this module, we can not download your repository. Unable to proceed.\n", 1); exit(40); } } } logger($conf, $THIS_FILE, __LINE__, "- Downloading: [$url]", 1); my $shell_call = $conf->{executable}{curl}." $url > $full_repo_path && ".$conf->{executable}{yum}." clean expire-cache"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); while(<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; if (-e $full_repo_path) { logger($conf, $THIS_FILE, __LINE__, "- Success!", 1); # If it is using a GPG key, make sure it exits. open (my $file_handle, "<", $full_repo_path) or die "$THIS_FILE ".__LINE__."; Failed to read: [$full_repo_path], the error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; # Update the apache user entry. if ($line =~ /^gpgkey=(.*)$/) { my $gpg_file = $1; if ($gpg_file =~ /^file:\/\/(\/.*)$/) { my $local_key = $1; if (not -e $local_key) { logger($conf, $THIS_FILE, __LINE__, "- This repository uses GPG, which is good, but the key isn't on the local", 1); logger($conf, $THIS_FILE, __LINE__, " system. I will try to find: [$local_key] now.", 1); my $key_name = ($local_key =~ /^.*\/(.*?)$/)[0]; my $key_url = "$repo_url/$key_name"; logger($conf, $THIS_FILE, __LINE__, "- Trying to download: [$key_url].", 1); my $shell_call = $conf->{executable}{'lwp-download'}." $key_url $local_key"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); while(<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; if (-e $local_key) { logger($conf, $THIS_FILE, __LINE__, "- File was downloaded successfully!", 1); } else { # Well crap... logger($conf, $THIS_FILE, __LINE__, "[ Error ] - GPG key file was not downloaded and doesn't exist! The repository:", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] [$url]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Requires a local copy of its GPG key to exist here:", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] [$local_key]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Often, this key is found in the same web address as the .repo file", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] so we tried to download:", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] [$key_url]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] However, that didn't work. Please locate this GPG key file and copy", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] it into place, then try running the installer again.\n", 0); exit(29); } } } } } close $file_handle; } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - It would appear that the repository download failed.", 0); exit(25); } } } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # This basically handles all of the stuff normally handled by the kickstart script when doing stage-1 that # may not have happened. sub do_pre_setup { my ($conf) = @_; my $source = $conf->{switches}{source}; my $test_file = $source."/striker/anvil.version"; # Make sure we're looking at an actual if (not -e $test_file) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The --source: [".$source."] does not appear to be a valid mount point for the Anvil! ISO.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] The test file: [".$test_file."] wasn't found.", 0); exit(45); } $conf->{directory}{source} = $source; logger($conf, $THIS_FILE, __LINE__, "[ Note ] - Performing steps usually handle by Anvil!'s kickstart to prepare for striker install.", 1); my $striker_number = 1; if (($conf->{switches}{n}) && (($conf->{switches}{n} =~ /striker0(\d)/) or ($conf->{switches}{n} =~ /dashboard(\d)/))) { $striker_number = $1; logger($conf, $THIS_FILE, __LINE__, "striker_number: [$striker_number]", 3); } my $os_dir = $conf->{pxe}{centos}{short_name}; logger($conf, $THIS_FILE, __LINE__, "sys::is_rhel: [".$conf->{sys}{is_rhel}."].", 2); if ($conf->{sys}{is_rhel}) { $os_dir = $conf->{pxe}{rhel}{short_name}; $conf->{pxe}{rhel}{enabled} = 1; } else { $conf->{pxe}{centos}{enabled} = 1; } logger($conf, $THIS_FILE, __LINE__, "os_dir: [$os_dir]", 3); # Copy the source ISO to the iso directory. my $source_iso = ""; my $shell_call = $conf->{executable}{mount}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 3); if ($line =~ /^(\/.*?) on $source/) { $source_iso = $1; logger($conf, $THIS_FILE, __LINE__, "source_iso: [$source_iso]", 2); last; } } close $file_handle; if (not $source_iso) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed to find where: [".$source."] is mounted.", 0); exit(45); } my $iso_target_directory = "/var/www/html/".$os_dir."/x86_64/iso/"; logger($conf, $THIS_FILE, __LINE__, "The source ISO is: [".$source_iso."], copying to the web-accessible directory: [".$iso_target_directory."]", 0); copy_file($conf, $source_iso, $iso_target_directory); logger($conf, $THIS_FILE, __LINE__, "Setting up 'list-ips'.", 0); my $list_ips_source = $source."/striker/tools/list-ips"; my $list_ips_target = "/sbin/striker/list-ips"; copy_file($conf, $source_iso, $iso_target_directory); chown 0, 0, $list_ips_target; chmod 0755, $list_ips_target; logger($conf, $THIS_FILE, __LINE__, "Copying fence_raritan_snmp into /usr/sbin/", 0); copy_file($conf, $source."/Tools/fence/fence_raritan_snmp", "/usr/sbin/"); logger($conf, $THIS_FILE, __LINE__, "Setting the PXE wallpaper.", 0); copy_file($conf, $source."/syslinux/splash.jpg", "/var/lib/tftpboot/"); copy_file($conf, $source."/EFI/BOOT/pxe_voyager1.xpm.gz", "/var/lib/tftpboot/efi64/"); logger($conf, $THIS_FILE, __LINE__, "Copying Striker to /root/ for the rest of the install to run.", 0); rsync_files($conf, $source."/striker", "/root/"); logger($conf, $THIS_FILE, __LINE__, "Copying 'Tools' into '/var/www/html/".$os_dir."/x86_64/files/'.", 0); rsync_files($conf, $source."/Tools", "/var/www/html/".$os_dir."/x86_64/files/"); logger($conf, $THIS_FILE, __LINE__, "Copying the ISO source to apache.", 0); rsync_files($conf, $source."/*", "/var/www/html/".$os_dir."/x86_64/img/"); rsync_files($conf, $source."/.discinfo", "/var/www/html/".$os_dir."/x86_64/img/"); rsync_files($conf, $source."/.treeinfo", "/var/www/html/".$os_dir."/x86_64/img/"); logger($conf, $THIS_FILE, __LINE__, "Copying Striker program and HTML files into place.", 0); rsync_files($conf, $source."/striker/cgi-bin/*", "/var/www/cgi-bin/"); rsync_files($conf, $source."/striker/html/*", "/var/www/html/"); rsync_files($conf, $source."/striker/tools/*", "/sbin/striker/"); rsync_files($conf, $source."/striker/tools/*", "/var/www/html/".$os_dir."/x86_64/files/Tools/striker/"); rsync_files($conf, $source."/striker/ScanCore", "/sbin/striker/"); rsync_files($conf, $source."/striker/AN", "/usr/share/perl5/"); logger($conf, $THIS_FILE, __LINE__, "Copying ISO Linux and EFI boot files.", 0); rsync_files($conf, $source."/isolinux/*", "/var/lib/tftpboot/boot/".$os_dir."/x86_64/"); rsync_files($conf, $source."/EFI/*", "/var/lib/tftpboot/boot/".$os_dir."/x86_64/"); rsync_files($conf, $source."/images/pxeboot/*", "/var/lib/tftpboot/efi64/"); logger($conf, $THIS_FILE, __LINE__, "Configuring yum to keep its cache.", 0); $shell_call = $conf->{executable}{sed}." -i 's/keepcache=0/keepcache=1/g' /etc/yum.conf"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "Configuring sshd to not use DNS or GSSAPI authentication for fast logins without internet connections.", 0); $shell_call = $conf->{executable}{sed}." -i 's/#UseDNS yes/UseDNS no/' /etc/ssh/sshd_config"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; $shell_call = $conf->{executable}{sed}." -i 's/#GSSAPIAuthentication no/GSSAPIAuthentication no/' /etc/ssh/sshd_config"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; $shell_call = $conf->{executable}{sed}." -i 's/GSSAPIAuthentication yes/#GSSAPIAuthentication yes/' /etc/ssh/sshd_config"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "Setting plymouth to use detailed boot screen", 0); $shell_call = "plymouth-set-default-theme details --rebuild-initrd"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; $shell_call = $conf->{executable}{sed}." -i 's/ rhgb//' /boot/grub/grub.conf"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; $shell_call = $conf->{executable}{sed}." -i 's/ quiet//' /boot/grub/grub.conf"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "Writing out local yum repository config.", 0); my $yum_config = "[striker01-".$os_dir."] name=Striker 0".$striker_number." ".$os_dir." repository baseurl=http://localhost/".$os_dir."/x86_64/img/ enabled=1 gpgcheck=0 priority=1 "; logger($conf, $THIS_FILE, __LINE__, "- Writing the local yum.conf.d repository.", 1); $shell_call = "/etc/yum.repos.d/striker0".$striker_number.".repo"; open ($file_handle, ">$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to write to: [$shell_call], error was: $!\n"; print $file_handle $yum_config; close $file_handle; # Finally, call createrepo to update our RPM repodata. logger($conf, $THIS_FILE, __LINE__, "- Generating updated RPM repo data for the local repo.", 0); $shell_call = $conf->{executable}{createrepo}." /var/www/html/".$os_dir."/x86_64/img"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); open ($file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; while(<$file_handle>) { print $_; } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Done.", 1); return(0); } sub create_directories { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Creating directories.", 0); my $os_dir = $conf->{sys}{is_rhel} ? $conf->{pxe}{rhel}{short_name} : $conf->{pxe}{centos}{short_name}; logger($conf, $THIS_FILE, __LINE__, "os_dir: [$os_dir]", 1); make_path("/var/www/html/".$os_dir."/x86_64/img", "/var/www/html/".$os_dir."/x86_64/iso", "/var/www/html/".$os_dir."/x86_64/ks", "/var/www/html/".$os_dir."/x86_64/files", "/var/lib/tftpboot/boot/".$os_dir."/x86_64/", "/var/lib/tftpboot/pxelinux.cfg", "/var/lib/tftpboot/efi64", "/sbin/striker"); return(0); } # This handles the rest of the install (everything except the network). sub setup_striker { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "-=] Beginning configuration and installation processes now. [=-\n", 1); # First up, install RPMs to make sure we've got everything we need. install_packages($conf); # If requested, this will install and configure the GUI. configure_gui($conf); # Make sure directories we'll need exist create_directories($conf); # When installing on a minimal install, the user will use '--source' to point to the mounted Anvil! # ISO. if ($conf->{switches}{source}) { do_pre_setup($conf); } # If the user installed from github master, we'll need to update the source directory. if (not -e $conf->{directory}{source}) { my $test_source = $conf->{directory}{source}."-master"; if (-e $test_source) { $conf->{directory}{source} = $test_source; } } # Configure apache. configure_apache($conf); # Configure SSH to allow faster logins when there is not Internet # access. Also, create an SSH RSA keyset for apache, though it # probably isn't needed anymore and might go away in a later update. configure_ssh($conf); # Configure iptables to allow incoming HTTP and HTTPS traffic. configure_iptables($conf); # Copy Striker files into place install_striker_files($conf); # Configure the various dashboard tools, like the striker-usb tools. configure_striker_tools($conf); # This sets up the dashboard to be a ScanCore database server. configure_scancore($conf); # This compiles the setuid C-wrappers (mode and owner are set in the next function, not here). compile_c_wrappers($conf); # Set owners and file modes. set_file_ownership_and_modes($conf); # Create a downloadable compressed file other machines to grab during their stage-1 install. create_striker_source($conf); # Configure IPMI configure_ipmi($conf); # (Re)start daemons. manage_daemons($conf); # Configure PXE and dhcpd stuff configure_pxe($conf); # Generate kickstart files. write_kickstarts($conf); # Make sure that selinux is enabled. This runs after the main installer so that # we can ensure any files we created or installed have the right # labels/contexts. configure_selinux($conf); # If a GUI was installed and unless disabled, call 'tools/striker-configure-vmm --enable' to have # Virtual Machine Manager be configured on user login. if ($conf->{switches}{gui}) { if ($conf->{switches}{'no-striker-configure-vmm'}) { logger($conf, $THIS_FILE, __LINE__, "- 'tools/striker-configure-vmm' use disabled by '--no-striker-configure-vmm', skipping it.", 1); } else { # Call 'tools/striker-configure-vmm'. my $shell_call = $conf->{directory}{striker_tools}."/striker-configure-vmm --enable"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; } } # Configure logrotate configure_logrotate($conf); # NOTE: Always call this last. If the password gets set, there is a # notice box informing the user that we want to ensure doesn't # get scrolled off the screen. # Set the root user's password. set_root_password($conf); return(0); } # Generate kickstart files. sub write_kickstarts { my ($conf) = @_; # Write out the kickstart files. logger($conf, $THIS_FILE, __LINE__, "- Generating and recording kickstart files.", 1); my $say_os = $conf->{sys}{is_rhel} ? "rhel" : "centos"; my $node1_ks = generate_kickstart($conf, $say_os, "node", 1); my $node2_ks = generate_kickstart($conf, $say_os, "node", 2); my $striker1_ks = generate_kickstart($conf, $say_os, "striker", 1); my $striker2_ks = generate_kickstart($conf, $say_os, "striker", 2); my $node1_ks_file = "/var/www/html/".$say_os."6/x86_64/ks/".$conf->{pxe}{$say_os}{node1_ks}; my $node2_ks_file = "/var/www/html/".$say_os."6/x86_64/ks/".$conf->{pxe}{$say_os}{node2_ks}; my $striker1_ks_file = "/var/www/html/".$say_os."6/x86_64/ks/".$conf->{pxe}{$say_os}{striker1_ks}; my $striker2_ks_file = "/var/www/html/".$say_os."6/x86_64/ks/".$conf->{pxe}{$say_os}{striker2_ks}; # Write out the files. logger($conf, $THIS_FILE, __LINE__, "- Writing out ".$say_os." node 01 kickstart file.", 1); open (my $file_handle, ">", $node1_ks_file) or die "$THIS_FILE ".__LINE__."; Failed to write: [$node1_ks_file], the error was: $!\n"; print $file_handle $node1_ks; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Wrote: [$node1_ks_file].", 1); logger($conf, $THIS_FILE, __LINE__, "- Writing out ".$say_os." node 02 kickstart file.", 1); open ($file_handle, ">", $node2_ks_file) or die "$THIS_FILE ".__LINE__."; Failed to write: [$node2_ks_file], the error was: $!\n"; print $file_handle $node2_ks; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Wrote: [$node2_ks_file].", 1); logger($conf, $THIS_FILE, __LINE__, "- Writing out ".$say_os." striker 01 kickstart file.", 1); open ($file_handle, ">", $striker1_ks_file) or die "$THIS_FILE ".__LINE__."; Failed to write: [$striker1_ks_file], the error was: $!\n"; print $file_handle $striker1_ks; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Wrote: [$striker1_ks_file].", 1); logger($conf, $THIS_FILE, __LINE__, "- Writing out ".$say_os." striker 02 kickstart file.", 1); open ($file_handle, ">", $striker2_ks_file) or die "$THIS_FILE ".__LINE__."; Failed to write: [$striker2_ks_file], the error was: $!\n"; print $file_handle $striker2_ks; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Wrote: [$striker2_ks_file].", 1); logger($conf, $THIS_FILE, __LINE__, "- ".$say_os." PXE configuration complete.", 1); logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # Create a downloadable compressed file other machines to grab during their stage-1 install. sub create_striker_source { my ($conf) = @_; # Now start copying things into place. logger($conf, $THIS_FILE, __LINE__, "Creating the striker source tarball for future installs.", 1); my $source_directory = "/var/www/html/".$conf->{pxe}{centos}{short_name}."/x86_64"; logger($conf, $THIS_FILE, __LINE__, "sys::is_rhel: [".$conf->{sys}{is_rhel}."].", 2); if ($conf->{sys}{is_rhel}) { $source_directory = "/var/www/html/".$conf->{pxe}{rhel}{short_name}."/x86_64"; } logger($conf, $THIS_FILE, __LINE__, "source_directory: [$source_directory]", 2); my $shell_call = " cd ".$source_directory."/img; tar -cvzf ".$source_directory."/files/striker.tar.gz ./striker; cd /root"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 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 = $_; logger($conf, $THIS_FILE, __LINE__, "Output: [".$line."]", 2); } close $file_handle; $shell_call = " cd ".$source_directory."; tar -cvzf ".$source_directory."/files/img.tar.gz ./img; cd /root"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 2); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "Output: [".$line."]", 2); } close $file_handle; return(0); } # Writes out the logrotate configuration for Striker and ScanCore. sub configure_logrotate { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Configuring logrotate.", 1); my $logrotate = "compress"; foreach my $log_file (sort {$a cmp $b} keys %{$conf->{sys}{logrotate}}) { my $count = $conf->{sys}{logrotate}{$log_file}{count} ? $conf->{sys}{logrotate}{$log_file}{count} : 5; my $frequency = $conf->{sys}{logrotate}{$log_file}{frequency} ? $conf->{sys}{logrotate}{$log_file}{frequency} : "weekly"; my $maxsize = $conf->{sys}{logrotate}{$log_file}{maxsize} ? $conf->{sys}{logrotate}{$log_file}{maxsize} : "100M"; $logrotate .= " /var/log/$log_file { rotate $count $frequency maxsize $maxsize } "; } my $logrotate_config = $conf->{path}{logrotate_config}; logger($conf, $THIS_FILE, __LINE__, "- Writing: [$logrotate_config].", 1); my $shell_call = $logrotate_config; open (my $file_handle, ">$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to write to: [$shell_call], error was: $!\n"; print $file_handle $logrotate; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Done.", 1); return(0); } # This compiles the setuid C-wrappers (mode and owner are set in the next function, not here). sub compile_c_wrappers { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Compiling setuid C-wrappers.", 1); foreach my $wrapper (sort {$a cmp $b} keys %{$conf->{setuid}}) { logger($conf, $THIS_FILE, __LINE__, "- Compiling: [$wrapper]", 1); my $c_file = $conf->{setuid}{$wrapper}{c_file}; my $out_file = $conf->{setuid}{$wrapper}{out_file}; logger($conf, $THIS_FILE, __LINE__, "c_file: [$c_file], out_file: [$out_file]", 3); # Unlink the existing output file, if it exists. if (-e $out_file) { logger($conf, $THIS_FILE, __LINE__, "- Removing the existing c-wrapper: [$out_file]", 1); unlink $out_file or warn "$THIS_FILE ".__LINE__."; [ Warning ] - Failed to delete: [$out_file]. The error was: $!\n"; } # Compile my $shell_call = $conf->{path}{gcc}." -o $out_file $c_file"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; while(<$file_handle>) { print $_; } close $file_handle; if (-e $out_file) { logger($conf, $THIS_FILE, __LINE__, "- Successfully compiled: [$out_file]", 1); } } logger($conf, $THIS_FILE, __LINE__, "- Finished.\n", 1); return(0); } # This removes the 'priority=1' from our repos so that future attempts by the user to upgrade the OS will # work. sub remove_yum_priority { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Removing priority assignments from yum repositories.", 1); logger($conf, $THIS_FILE, __LINE__, "- Searching for repositories...", 1); local(*DIRECTORY); opendir(DIRECTORY, $conf->{directory}{yum_repos}); while(my $file = readdir(DIRECTORY)) { next if $file !~ /\.repo$/; my $full_path = $conf->{directory}{yum_repos}."/$file"; next if not -f $full_path; logger($conf, $THIS_FILE, __LINE__, "- Checking: [$full_path]", 1); my $rewrite = 0; my $raw_file = ""; # Open it up my $shell_call = "$full_path"; open (my $file_handle, "<$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to read: [$shell_call], the error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; # Update the apache user entry. if ($line =~ /^priority=/) { $rewrite = 1; logger($conf, $THIS_FILE, __LINE__, "- Priority set, will remove.", 1); } else { $raw_file .= "$line\n"; } } close $file_handle; if (($rewrite) && ($raw_file)) { backup_file($conf, $full_path); logger($conf, $THIS_FILE, __LINE__, "- Rewriting: [$full_path].", 1); my $shell_call = "$full_path"; open (my $file_handle, ">$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to write to: [$shell_call], error was: $!\n"; print $file_handle $raw_file; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Done.", 1); } } closedir(DIRECTORY); logger($conf, $THIS_FILE, __LINE__, "- Finished processing repositories.\n", 1); return(0); } # This installs a GUI and configures it, if requested. sub configure_gui { my ($conf) = @_; if (not $conf->{switches}{gui}) { # GUI not requested. return(0); } logger($conf, $THIS_FILE, __LINE__, "Configuring Striker to use a graphical interface.", 1); my $stop_NetworkManager = 0; my $to_install = ""; foreach my $package (sort {$a cmp $b} keys %{$conf->{packages}{gui_packages}}) { if ((exists $conf->{packages}{installed}{$package}) && ($conf->{packages}{installed}{$package} == 1)) { $conf->{packages}{gui_packages}{$package} = 1; logger($conf, $THIS_FILE, __LINE__, "- GUI Package: [$package] already installed.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Package: [$package] needs to be installed.", 1); $to_install .= "$package "; if ($package eq "NetworkManager") { $stop_NetworkManager = 1; } } } # Install anything needed. if ($to_install) { # Install the packages, then verify they actually installed. logger($conf, $THIS_FILE, __LINE__, "- Installing IPMI packages now. Please be patient.", 1); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] Please be patient! It may appear that nothing is happening for a", 3); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] while. This is likely because of how buffering is handled. If you", 3); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] really think nothing is happening, please open a new terminal and", 3); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] look for activity using 'top' or 'ps aux'.", 3); my $shell_call = $conf->{executable}{yum}." ".$conf->{sys}{yum_switches}." install $to_install"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; while(<$file_handle>) { print $_; } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Done.", 1); # Verify that everything is installed. sleep 5; get_list_of_installed_packages($conf); logger($conf, $THIS_FILE, __LINE__, "- Verifying that everything is installed now.", 1); my $missing = 0; foreach my $package (sort {$a cmp $b} keys %{$conf->{packages}{gui_packages}}) { # Watch for autovivication... if ((exists $conf->{packages}{installed}{$package}) && ($conf->{packages}{installed}{$package} == 1)) { $conf->{packages}{gui_packages}{$package} = 1; logger($conf, $THIS_FILE, __LINE__, "- Package: [$package] installed.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - GUI Package: [$package] failed to install!", 0); $missing = 1; } } # If anything is missing, fail. if ($missing) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Not all required packages for the GUI installed properly.", 0); exit(28); } else { logger($conf, $THIS_FILE, __LINE__, "- All required packages are now installed.\n", 1); } # Immediately disable NetworkManager. if ($stop_NetworkManager) { logger($conf, $THIS_FILE, __LINE__, "- Disabling the just-installed NetworkManager.", 1); my $shell_call = "/etc/init.d/NetworkManager stop"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; while(<$file_handle>) { print $_; } close $file_handle; } } else { logger($conf, $THIS_FILE, __LINE__, "- All required packages already installed.", 1); } ### Now configure the desktop. # First, ensure the GUI loads on boot. if (-e $conf->{path}{inittab}) { logger($conf, $THIS_FILE, __LINE__, "- Enabling GUI on boot.", 1); backup_file($conf, $conf->{path}{inittab}); my $shell_call = "sed -i 's/id:3:initdefault/id:5:initdefault/' ".$conf->{path}{inittab}; open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; while(<$file_handle>) { print $_; } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; } # Add a system user. if (1) { # See if the user exists. my $striker_user = $conf->{sys}{striker_user}; my $striker_password = $conf->{sys}{striker_password}; my $create_user = 1; logger($conf, $THIS_FILE, __LINE__, "- Checking if the system user: [$striker_user] exists.", 1); open (my $file_handle, "<", $conf->{path}{passwd}) or die "$THIS_FILE ".__LINE__."; Failed to read: [".$conf->{path}{passwd}."], the error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; # Update the apache user entry. if ($line =~ /^$striker_user:/) { logger($conf, $THIS_FILE, __LINE__, "- User found.", 1); $create_user = 0; } } close $file_handle; # Create the user account if needed. if ($create_user) { logger($conf, $THIS_FILE, __LINE__, "- Creating the user account: [$striker_user]", 1); my $shell_call = "useradd $striker_user -m"; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Done.", 1); } # Create the ssh key if needed. if (not -e "/home/$striker_user/.ssh/id_rsa") { logger($conf, $THIS_FILE, __LINE__, "- Creating the SSH RSA key for: [$striker_user]", 1); my $shell_call = $conf->{executable}{su}." $striker_user -c \"".$conf->{executable}{'ssh-keygen'}." -t rsa -N \\\"\\\" -b 8191 -f ~/.ssh/id_rsa\""; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Done.", 1); } # Set the password. if (1) { logger($conf, $THIS_FILE, __LINE__, "- Setting the system password for: [$striker_user]", 1); my $shell_call = $conf->{executable}{echo}." \"$striker_password\" | ".$conf->{executable}{passwd}." $striker_user --stdin"; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Done.", 1); } } # Force instant shutdown in the GUI instead of waiting 60 seconds if (-e $conf->{path}{power_sh}) { logger($conf, $THIS_FILE, __LINE__, "- Configuring immediate shut down in GUI.", 1); backup_file($conf, $conf->{path}{power_sh}); my $power_sh = "#!/bin/sh\n\n"; $power_sh .= "PATH=/sbin:/bin:/usr/bin\n"; $power_sh .= "shutdown -h now\n"; my $shell_call = $conf->{path}{power_sh}; open (my $file_handle, ">", "$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to write to: [$shell_call], error was: $!\n"; print $file_handle $power_sh; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Done.", 1); } # Desktop icons logger($conf, $THIS_FILE, __LINE__, "- Configuring desktop icons:", 1); my $desktop = "/home/$conf->{sys}{striker_user}/Desktop"; if (not -e $desktop) { # Create the directory. logger($conf, $THIS_FILE, __LINE__, "- Creating the desktop directory: [$desktop]", 1); mkdir $desktop or die "$THIS_FILE ".__LINE__."; Failed to create the directory: [$desktop]. The error was: $!\n"; $conf->{permissions}{user_desktop}{path} = $desktop; $conf->{permissions}{user_desktop}{owner} = $conf->{sys}{striker_user}; $conf->{permissions}{user_desktop}{group} = $conf->{sys}{striker_user}, $conf->{permissions}{user_desktop}{mode} = "755"; $conf->{permissions}{user_desktop}{recursive} = 0; } if (1) { # Icon files my $firefox_shortcut = "$desktop/firefox.desktop"; my $virt_manager_shortcut = "$desktop/virt-manager.desktop"; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] ".__LINE__." - virt_manager_shortcut: [$virt_manager_shortcut]", 3); ### Add them to the permission setting hash # Firefox $conf->{permissions}{user_firefox_icon}{path} = "$desktop/firefox.desktop"; $conf->{permissions}{user_firefox_icon}{owner} = $conf->{sys}{striker_user}; $conf->{permissions}{user_firefox_icon}{group} = $conf->{sys}{striker_user}, $conf->{permissions}{user_firefox_icon}{mode} = "755"; $conf->{permissions}{user_firefox_icon}{recursive} = 0; # Virtual Machine Manager $conf->{permissions}{user_virt_manager_icon}{path} = "$desktop/virt-manager.desktop"; $conf->{permissions}{user_virt_manager_icon}{owner} = $conf->{sys}{striker_user}; $conf->{permissions}{user_virt_manager_icon}{group} = $conf->{sys}{striker_user}, $conf->{permissions}{user_virt_manager_icon}{mode} = "755"; $conf->{permissions}{user_virt_manager_icon}{recursive} = 0; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] ".__LINE__." - permissions::user_virt_manager_icon::path: [".$conf->{permissions}{user_virt_manager_icon}{path}."]", 3); ### Icon contents # Firefox my $firefox_body = "[Desktop Entry]\n"; $firefox_body .= "Version=1.0\n"; $firefox_body .= "Encoding=UTF-8\n"; $firefox_body .= "Name=Firefox Web Browser\n"; $firefox_body .= "GenericName=Web Browser\n"; $firefox_body .= "Comment=Browse the Web\n"; $firefox_body .= "Exec=firefox http://localhost/\n"; $firefox_body .= "Icon=firefox\n"; $firefox_body .= "Terminal=false\n"; $firefox_body .= "Type=Application\n"; $firefox_body .= "StartupWMClass=Firefox-bin\n"; $firefox_body .= "MimeType=text/html;text/xml;application/xhtml+xml;application/vnd.mozilla.xul+xml;text/mml;\n"; $firefox_body .= "StartupNotify=true\n"; $firefox_body .= "X-Desktop-File-Install-Version=0.15\n"; $firefox_body .= "Categories=Network;WebBrowser;\n"; $firefox_body .= "GenericName[en_US.UTF-8]=Firefox - Striker Dashboard\n"; $firefox_body .= "Comment[en_US.UTF-8]=Connect to the Striker Dashboard\n"; $firefox_body .= "Name[en_US]=Striker Dashboard\n"; # Virt-manager my $virt_manager_body = "#!/usr/bin/env xdg-open\n\n"; $virt_manager_body .= "[Desktop Entry]\n"; $virt_manager_body .= "Name=Virtual Machine Manager\n"; $virt_manager_body .= "Comment=Manage virtual machines\n"; $virt_manager_body .= "Icon=virt-manager\n"; $virt_manager_body .= "Exec=virt-manager\n"; $virt_manager_body .= "Type=Application\n"; $virt_manager_body .= "Terminal=false\n"; $virt_manager_body .= "Encoding=UTF-8\n"; $virt_manager_body .= "Categories=System;\n"; ### Write them out. # Firefox my $shell_call = "$firefox_shortcut"; open (my $file_handle, ">", "$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to write to: [$shell_call], error was: $!\n"; print $file_handle $firefox_body; close $file_handle; # Virt-manager $shell_call = "$virt_manager_shortcut"; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] ".__LINE__." - shell_call: [$shell_call]", 3); open ($file_handle, ">", "$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to write to: [$shell_call], error was: $!\n"; print $file_handle $virt_manager_body; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Done.", 1); } # Make sure NetworkManager is disabled. if (1) { logger($conf, $THIS_FILE, __LINE__, "- Disabling 'NetworkManager'", 1); my $shell_call = "if [ -e /etc/init.d/NetworkManager ]; then chkconfig NetworkManager off; fi"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; $line =~ s/\n//; $line =~ s/\r//; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; } # Make sure network is enabled. if (1) { logger($conf, $THIS_FILE, __LINE__, "- Enabling 'network'", 1); my $shell_call = "chkconfig network on"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; $line =~ s/\n//; $line =~ s/\r//; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; } logger($conf, $THIS_FILE, __LINE__, "Done!\n", 1); return(0); } # If the user has requested IPMI be configured, do so. sub configure_ipmi { my ($conf) = @_; if ((not $conf->{sys}{ipmi_user}) && (not $conf->{sys}{ipmi_ip})) { # The user hasn't asked for IPMI, so we're out. return(0); } # Ok, still alive. We now need to install the packages for IPMI. my $ipmi_ok = 1; logger($conf, $THIS_FILE, __LINE__, "Configuring IPMI:", 1); my $to_install = ""; foreach my $package (sort {$a cmp $b} keys %{$conf->{packages}{ipmi_packages}}) { logger($conf, $THIS_FILE, __LINE__, "packages::installed::$package: [".$conf->{packages}{installed}{$package}."]", 3); if ((exists $conf->{packages}{installed}{$package}) && ($conf->{packages}{installed}{$package} == 1)) { $conf->{packages}{ipmi_packages}{$package} = 1; logger($conf, $THIS_FILE, __LINE__, "- IPMI Package: [$package] already installed.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Package: [$package] needs to be installed.", 1); $to_install .= "$package "; } } # Install anything needed. if ($to_install) { # Install the packages, then verify they actually installed. logger($conf, $THIS_FILE, __LINE__, "- Installing IPMI packages now. Please be patient.", 1); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] Please be patient! It may appear that nothing is happening for a", 3); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] while. This is likely because of how buffering is handled. If you", 3); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] really think nothing is happening, please open a new terminal and", 3); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] look for activity using 'top' or 'ps aux'.", 3); my $shell_call = $conf->{executable}{yum}." ".$conf->{sys}{yum_switches}." install $to_install"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; while(<$file_handle>) { print $_; } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Done.", 1); # Verify that everything is installed. sleep 5; get_list_of_installed_packages($conf); logger($conf, $THIS_FILE, __LINE__, "- Verifying that everything is installed now.", 1); my $missing = 0; foreach my $package (sort {$a cmp $b} keys %{$conf->{packages}{ipmi_packages}}) { # Watch for autovivication... if ((exists $conf->{packages}{installed}{$package}) && ($conf->{packages}{installed}{$package} == 1)) { $conf->{packages}{ipmi_packages}{$package} = 1; logger($conf, $THIS_FILE, __LINE__, "- Package: [$package] installed.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - IPMI Package: [$package] failed to install!", 0); $missing = 1; } } # If anything is missing, fail. if ($missing) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Not all required packages for IPMI installed properly.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] The install will continue, but please note that IPMI has not been", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] configured.", 0); $ipmi_ok = 0; } else { logger($conf, $THIS_FILE, __LINE__, "- All required packages are now installed.\n", 1); } } else { logger($conf, $THIS_FILE, __LINE__, "- All required packages already installed.", 1); } # Start the ipmi daemon and check that the IPMI device is found. if ($ipmi_ok) { my $shell_call = "if [ ! -e '/dev/ipmi0' ]; then /etc/init.d/ipmi start; if [ ! -e '/dev/ipmi0' ]; then echo 'Failed to start IPMI'; echo 'rc:2' else echo 'IPMI started successfully.'; echo 'rc:0' fi; else echo 'IPMI already running.'; echo 'rc:1' fi"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; if ($line =~ /^rc:(\d+)/) { my $rc = $1; if ($rc eq "0") { logger($conf, $THIS_FILE, __LINE__, "- IPMI daemon started successfully.", 1); } elsif ($rc eq "1") { logger($conf, $THIS_FILE, __LINE__, "- IPMI daemon was already running.", 1); } elsif ($rc eq "2") { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - IPMI daemon failed to start.", 0); $ipmi_ok = 0; } } else { logger($conf, $THIS_FILE, __LINE__, "output: [$line]", 3); } } close $file_handle; } # Configure IPMI. my $channel = 0; my $channel_found = 0; if ($ipmi_ok) { logger($conf, $THIS_FILE, __LINE__, "- Seaching for IPMI LAN channel:", 1); while (not $channel_found) { logger($conf, $THIS_FILE, __LINE__, "channel: [$channel]", 3); if ($channel > 20) { # Give up... logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed to find the IPMI LAN channel! Giving up.", 0); $ipmi_ok = 0; return($ipmi_ok); } # check to see if this is the write channel my $rc = ""; my $shell_call = "ipmitool lan print $channel 2>&1; echo rc:\$?"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /Invalid channel: /) { logger($conf, $THIS_FILE, __LINE__, "Wrong LAN channel: [$channel]", 3); } elsif ($line =~ "rc:0") { # Found it! $channel_found = 1; logger($conf, $THIS_FILE, __LINE__, "- Found the LAN channel: [$channel]!", 1); } } close $file_handle; $channel++ if not $channel_found; } logger($conf, $THIS_FILE, __LINE__, "LAN channel: [$channel]", 3); } # Now find the requested user ID number. my $user_id = 0; my $user_id_found = 0; if (($conf->{sys}{ipmi_user}) && ($ipmi_ok)) { logger($conf, $THIS_FILE, __LINE__, "- Searching for the user ID for: [".$conf->{sys}{ipmi_user}."]", 1); while (not $user_id_found) { logger($conf, $THIS_FILE, __LINE__, "user_id: [$user_id]", 3); if ($user_id > 20) { # Give up... logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - Failed to find the IPMI admin user ID for: [".$conf->{sys}{ipmi_user}."].", 1); $user_id = 0; last; } # check to see if this is the write channel my $rc = ""; my $ipmi_user = $conf->{sys}{ipmi_user}; my $shell_call = "ipmitool user list $channel 2>&1"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open (my $filehandle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $?\n"; while (<$filehandle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); $line =~ s/\s+/ /g; if ($line =~ /^(\d+) $ipmi_user /) { $user_id = $1; $user_id_found = 1; logger($conf, $THIS_FILE, __LINE__, "Found the user ID: [$user_id]", 3); } } $user_id++ if not $user_id_found; } # Set the password. logger($conf, $THIS_FILE, __LINE__, "- Setting the password for: [".$conf->{sys}{ipmi_user}." ($user_id)]:", 1); # Set the password. my $shell_call = "ipmitool user set password $user_id '".$conf->{sys}{ipmi_password}."'"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); } close $file_handle; if ($ipmi_ok) { # Test the password. If this fails with '16', try '20'. my $password_ok = 0; my $try_20 = 0; my $shell_call = "ipmitool user test $user_id 16 '".$conf->{sys}{ipmi_password}."'"; open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $?\n"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); while (<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /Success/i) { # Woo! logger($conf, $THIS_FILE, __LINE__, "- IPMI password for: [".$conf->{sys}{ipmi_user}."] set successfully as a 16-byte string!", 1); } elsif ($line =~ /wrong password size/i) { $try_20 = 1; logger($conf, $THIS_FILE, __LINE__, "IPMI password is not a 16-byte string, will try 20-byte.", 3); } elsif ($line =~ /password incorrect/i) { # Password didn't take. :( logger($conf, $THIS_FILE, __LINE__, "[ Error ] - IPMI password failed to be set properly.", 0); $ipmi_ok = 0; } } close $file_handle; if ($try_20) { my $shell_call = "ipmitool user test $user_id 20 '".$conf->{sys}{ipmi_password}."'"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /Success/i) { # Woo! logger($conf, $THIS_FILE, __LINE__, "- IPMI password for: [".$conf->{sys}{ipmi_user}."] set successfully as a 20-byte string!", 1); } elsif ($line =~ /password incorrect/i) { # Password didn't take. :( logger($conf, $THIS_FILE, __LINE__, "[ Error ] - IPMI password failed to be set properly.", 0); $ipmi_ok = 0; } } close $file_handle; } } } # Set the IPMI address if requested. if (($conf->{sys}{ipmi_ip}) && ($ipmi_ok)) { # Setup the IPMI IP to static my $shell_call = "ipmitool lan set $channel ipsrc static"; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "return line: [$line]", 3); } close $file_handle; # Now set the IP $shell_call = "ipmitool lan set $channel ipaddr ".$conf->{sys}{ipmi_ip}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "return line: [$line]", 3); } close $file_handle; # Now the netmask $shell_call = "ipmitool lan set $channel netmask ".$conf->{sys}{ipmi_netmask}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "return line: [$line]", 3); } # If the user has specified a gateway, set it if ($conf->{sys}{ipmi_gateway}) { my $shell_call = "ipmitool lan set $channel defgw ipaddr ".$conf->{sys}{ipmi_gateway}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "return line: [$line]", 3); } close $file_handle; } ### Now read it back. # Now the netmask $shell_call = "ipmitool lan print $channel"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, ">> return line: [$line]", 3); $line =~ s/^\s+//; $line =~ s/\s+$//; $line =~ s/\s+/ /g; logger($conf, $THIS_FILE, __LINE__, "<< return line: [$line]", 3); if ($line =~ /IP Address Source/i) { if ($line =~ /Static/i) { logger($conf, $THIS_FILE, __LINE__, "- IPMI IP is static now.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - IPMI IP is NOT static!", 0); $ipmi_ok = 0; } } if ($line =~ /IP Address :/i) # Needs the ' :' to not match 'IP Address Source' { my $ip = ($line =~ /(\d+\.\d+\.\d+\.\d+)$/)[0]; if ($ip eq $conf->{sys}{ipmi_ip}) { logger($conf, $THIS_FILE, __LINE__, "- IPMI IP is now: [".$conf->{sys}{ipmi_ip}."]", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - IPMI reported IP: [$ip] doesn't match desired IP: [".$conf->{sys}{ipmi_ip}."]!", 0); $ipmi_ok = 0; } } if ($line =~ /Subnet Mask/i) { my $ip = ($line =~ /(\d+\.\d+\.\d+\.\d+)$/)[0]; if ($ip eq $conf->{sys}{ipmi_netmask}) { logger($conf, $THIS_FILE, __LINE__, "- IPMI subnet is now: [".$conf->{sys}{ipmi_netmask}."]", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - IPMI reported subnet: [$ip] doesn't match desired subnet: [".$conf->{sys}{ipmi_netmask}."]!", 0); $ipmi_ok = 0; } } if (($line =~ /Default Gateway IP/i) && ($conf->{sys}{ipmi_gateway})) { my $ip = ($line =~ /(\d+\.\d+\.\d+\.\d+)$/)[0]; if ($ip eq $conf->{sys}{ipmi_gateway}) { logger($conf, $THIS_FILE, __LINE__, "- IPMI gateway is now: [".$conf->{sys}{ipmi_gateway}."]", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - IPMI reported gateway: [$ip] doesn't match desired gateway: [".$conf->{sys}{ipmi_gateway}."]!", 0); $ipmi_ok = 0; } } } close $file_handle; } if ($ipmi_ok) { logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); } else { logger($conf, $THIS_FILE, __LINE__, "\n##############################################################################", 1); logger($conf, $THIS_FILE, __LINE__, "# [ Error ] - IPMI configuration failed. Please configure IPMI manually! #", 0); logger($conf, $THIS_FILE, __LINE__, "# [ Error ] - Install will attempt to continue. #", 0); logger($conf, $THIS_FILE, __LINE__, "##############################################################################\n", 1); sleep 10; } return(0); } # This configures DHCP and PXE servers. sub configure_pxe { my ($conf) = @_; # Expand these by prepending the docroot. $conf->{directory}{centos_pxe_web} = $conf->{directory}{document_root}."/".$conf->{directory}{centos_pxe_web}; $conf->{directory}{rhel_pxe_web} = $conf->{directory}{document_root}."/".$conf->{directory}{rhel_pxe_web}; if ($conf->{switches}{p} eq "none") { logger($conf, $THIS_FILE, __LINE__, "PXE configuration disabled, skipping.", 1); push @{$conf->{daemons}{disable}}, "dhcpd"; } else { configure_dhcp($conf); configure_xinetd($conf); configure_iso_source($conf); configure_pxe_config($conf); if ($conf->{switches}{'router-mode'}) { configure_shorewall($conf); } if ($conf->{switches}{'autostart-dhcpd'}) { logger($conf, $THIS_FILE, __LINE__, "Auto-starting 'dhcpd' on start.", 1); push @{$conf->{daemons}{enable}}, "dhcpd"; logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); } else { logger($conf, $THIS_FILE, __LINE__, "Disabling auto-start of 'dhcpd'.", 1); push @{$conf->{daemons}{disable}}, "dhcpd"; logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); } } return(0); } # If '--router-mode' mode was set, this will install and configure shorewall to masq the BCN (thus providing # Internet access, assuming there is Internet access on the IFN. sub configure_shorewall { my ($conf) = @_; # To start, we need to know what the IFN and BCN devices are. logger($conf, $THIS_FILE, __LINE__, "Configuring shorewall to provide Internet access to the BCN.", 1); my $bcn_interface = $conf->{network}{bcn}{interface}; my $ifn_interface = $conf->{network}{ifn}{interface}; logger($conf, $THIS_FILE, __LINE__, "bcn_interface: [$bcn_interface], ifn_interface: [$ifn_interface]", 3); if (not $bcn_interface) { logger($conf, $THIS_FILE, __LINE__, "sys::create_bonds: [".$conf->{sys}{create_bonds}."], sys::bonds_exist: [".$conf->{sys}{bonds_exist}."]", 3); if (($conf->{sys}{create_bonds}) or ($conf->{sys}{bonds_exist})) { $bcn_interface = "bcn_bond1"; $ifn_interface = "ifn_bond1"; logger($conf, $THIS_FILE, __LINE__, "bcn_interface: [$bcn_interface], ifn_interface: [$ifn_interface]", 3); } else { $bcn_interface = "bcn_link1"; $ifn_interface = "ifn_link1"; logger($conf, $THIS_FILE, __LINE__, "bcn_interface: [$bcn_interface], ifn_interface: [$ifn_interface]", 3); } } # Get the BCN network block. my ($bcn_network, $bcn_broadcast) = get_network_and_broadcast_from_ip_and_subnet($conf, $conf->{network}{bcn}{ip}, $conf->{network}{bcn}{netmask}); logger($conf, $THIS_FILE, __LINE__, "bcn_network: [$bcn_network]", 3); my $cidr_bcn_netmask = convert_dotted_decimal_to_cidr($conf, $conf->{network}{bcn}{netmask}); logger($conf, $THIS_FILE, __LINE__, "cidr_bcn_netmask: [$cidr_bcn_netmask]", 3); if ($cidr_bcn_netmask eq "#!INVALID!#") { # We can't setup the firewall. logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed to convert the BCN subnet mask: [".$conf->{network}{bcn}{netmask}."] to CIDR notation.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Unable to configure Striker in router-mode.", 0); exit(39); } ### Now prepare my configuration files. # Zones - https://alteeve.com/w/Shorewall_on_RPM-based_Servers#zones my $zones = " # # Shorewall version 4 - Zones File # # For information about this file, type \"man shorewall-zones\" # # The manpage is also online at # http://www.shorewall.net/manpages/shorewall-zones.html # ############################################################################### #ZONE TYPE OPTIONS IN OUT # OPTIONS OPTIONS fw firewall net ipv4 loc ipv4 "; # Interfaces - https://alteeve.com/w/Shorewall_on_RPM-based_Servers#interfaces my $interfaces = " # # Shorewall version 4 - Interfaces File # # For information about entries in this file, type \"man shorewall-interfaces\" # # The manpage is also online at # http://www.shorewall.net/manpages/shorewall-interfaces.html # ############################################################################### ?FORMAT 2 ############################################################################### #ZONE INTERFACE OPTIONS net $ifn_interface loc $bcn_interface dhcp "; # Policy - https://alteeve.com/w/Shorewall_on_RPM-based_Servers#policy my $policy = " # # Shorewall version 4 - Policy File # # For information about entries in this file, type \"man shorewall-policy\" # # The manpage is also online at # http://www.shorewall.net/manpages/shorewall-policy.html # ############################################################################### #SOURCE DEST POLICY LOG LIMIT: CONNLIMIT: # LEVEL BURST MASK # Let Striker go anywhere it wants, let the BCN out onto the net, drop # everything else. fw net ACCEPT fw loc ACCEPT net all DROP loc fw DROP loc net ACCEPT "; # Rules - https://alteeve.com/w/Shorewall_on_RPM-based_Servers#rules my $rules = " # # Shorewall version 4 - Rules File # # For information on the settings in this file, type \"man shorewall-rules\" # # The manpage is also online at # http://www.shorewall.net/manpages/shorewall-rules.html # ###################################################################################################################################################################################################### #ACTION SOURCE DEST PROTO DEST SOURCE ORIGINAL RATE USER/ MARK CONNLIMIT TIME HEADERS SWITCH HELPER # PORT(S) PORT(S) DEST LIMIT GROUP ?SECTION ALL ?SECTION ESTABLISHED ?SECTION RELATED ?SECTION INVALID ?SECTION UNTRACKED ?SECTION NEW # Respond to pings Ping(ACCEPT) net fw Ping(ACCEPT) loc fw # Let ssh, http and https in from both the IFN and BCN. ACCEPT net fw tcp 22 ACCEPT net fw tcp 67 ACCEPT net fw tcp 68 ACCEPT net fw udp 69 ACCEPT net fw tcp 69 ACCEPT net fw tcp 80 ACCEPT net fw tcp 443 ACCEPT net fw udp 2049 ACCEPT net fw tcp 2049 ACCEPT net fw tcp 5432 ACCEPT net fw tcp 5800 ACCEPT net fw tcp 5900 ACCEPT net fw tcp 6000 ACCEPT net fw tcp 8099 ACCEPT loc fw tcp 22 ACCEPT loc fw tcp 67 ACCEPT loc fw tcp 68 ACCEPT loc fw udp 69 ACCEPT loc fw tcp 69 ACCEPT loc fw tcp 80 ACCEPT loc fw tcp 443 ACCEPT loc fw udp 2049 ACCEPT loc fw tcp 2049 ACCEPT loc fw tcp 5432 ACCEPT loc fw tcp 5800 ACCEPT loc fw tcp 5900 ACCEPT loc fw tcp 6000 ACCEPT loc fw tcp 8099 "; # Masquarade - https://alteeve.com/w/Shorewall_on_RPM-based_Servers#masq my $masq = " # # Shorewall version 4 - Masq file # # For information about entries in this file, type \"man shorewall-masq\" # # The manpage is also online at # http://www.shorewall.net/manpages/shorewall-masq.html # ################################################################################################################ #INTERFACE:DEST SOURCE ADDRESS PROTO PORT(S) IPSEC MARK USER/ SWITCH ORIGINAL # GROUP DEST $ifn_interface $bcn_network/$cidr_bcn_netmask "; # Read in the shorewall.conf file and change 'STARTUP_ENABLED=No' to # 'STARTUP_ENABLED=Yes'. my $shorewall_conf = ""; my $shell_call = $conf->{path}{shorewall}{shorewall_conf}; open (my $file_handle, "<$shell_call") or die "Failed to read: [$shell_call], error was: $!\n"; while (<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /STARTUP_ENABLED=/) { $line = "STARTUP_ENABLED=Yes"; } $shorewall_conf .= "$line\n"; } close $file_handle; ### Now start writing things out! # Zones. if ($zones) { my $shell_call = $conf->{path}{shorewall}{zones}; open (my $file_handle, ">", $shell_call) or die "$THIS_FILE ".__LINE__."; Failed to write: [$shell_call], the error was: $!\n"; print $file_handle $zones; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Wrote: [$shell_call].", 1); } # Interfaces. if ($interfaces) { my $shell_call = $conf->{path}{shorewall}{interfaces}; open (my $file_handle, ">", $shell_call) or die "$THIS_FILE ".__LINE__."; Failed to write: [$shell_call], the error was: $!\n"; print $file_handle $interfaces; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Wrote: [$shell_call].", 1); } # Policy if ($policy) { my $shell_call = $conf->{path}{shorewall}{policy}; open (my $file_handle, ">", $shell_call) or die "$THIS_FILE ".__LINE__."; Failed to write: [$shell_call], the error was: $!\n"; print $file_handle $policy; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Wrote: [$shell_call].", 1); } # Rules if ($rules) { my $shell_call = $conf->{path}{shorewall}{rules}; open (my $file_handle, ">", $shell_call) or die "$THIS_FILE ".__LINE__."; Failed to write: [$shell_call], the error was: $!\n"; print $file_handle $rules; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Wrote: [$shell_call].", 1); } # Masquarade if ($masq) { my $shell_call = $conf->{path}{shorewall}{masq}; open (my $file_handle, ">", $shell_call) or die "$THIS_FILE ".__LINE__."; Failed to write: [$shell_call], the error was: $!\n"; print $file_handle $masq; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Wrote: [$shell_call].", 1); } # Finally, shorewall.conf if ($shorewall_conf) { my $shell_call = $conf->{path}{shorewall}{shorewall_conf}; open (my $file_handle, ">", $shell_call) or die "$THIS_FILE ".__LINE__."; Failed to write: [$shell_call], the error was: $!\n"; print $file_handle $shorewall_conf; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Wrote: [$shell_call].", 1); } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # Converts an IP address and dotted-decimal subnet mask into a network range # and broadcast IP. # Credit for this to: http://www.perlmonks.org/?node_id=465359 sub get_network_and_broadcast_from_ip_and_subnet { my ($conf, $address, $subnet) = @_; ### From "monarch" (http://www.perlmonks.org/?node_id=460541): # What I'm doing is converting the dot notation into an integer. That # way I can do bit manipulation on the address as a whole rather than # trying to deal with individual bytes. my @address_bytes = split("[.]", $address); my ($address_value) = unpack("N", pack("C4", @address_bytes)); my @subnet_bytes = split("[.]", $subnet); my ($subnet_value) = unpack( "N", pack("C4", @subnet_bytes)); # calculate network address my $network_value = ($address_value & $subnet_value); # calculate broadcast address my $broadcast_value = ($address_value & $subnet_value) + ( ~ $subnet_value); # convert network address to IP address my @network_bytes = unpack("C4", pack("N", $network_value)); my $network = join(".", @network_bytes); # convert broadcast address to IP address my @broadcast_bytes = unpack( "C4", pack("N", $broadcast_value)); my $broadcast = join(".", @broadcast_bytes); return($network, $broadcast); } # This sets up the install ISOs, if available. sub configure_iso_source { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Setting up the install ISOs for PXE installs.", 1); if ($conf->{switches}{'centos-iso'}) { my $file_name = ""; my $local_copy = ""; if ($conf->{switches}{'centos-iso'} eq "none") { logger($conf, $THIS_FILE, __LINE__, "- CentOS disabled, skipping.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- CentOS source defined, configuring.", 1); # Setup the directories. my @subdirectories = ($conf->{directory}{centos_pxe_web}, $conf->{directory}{centos_pxe_web}."/x86_64"); foreach my $subdirectory (@{$conf->{directory}{pxe_iso_subdirs}}) { logger($conf, $THIS_FILE, __LINE__, "directory::centos_pxe_web: [".$conf->{directory}{centos_pxe_web}."], subdirectory: [$subdirectory], combined: [".$conf->{directory}{centos_pxe_web}."/x86_64/$subdirectory]", 3); push @subdirectories, $conf->{directory}{centos_pxe_web}."/x86_64/$subdirectory"; } foreach my $directory (@subdirectories) { logger($conf, $THIS_FILE, __LINE__, "directory: [$directory]", 3); if (-e $directory) { logger($conf, $THIS_FILE, __LINE__, "- Directory already exists: [$directory]", 1); } else { mkdir $directory or die "$THIS_FILE ".__LINE__."; Failed to create the directory: [$directory], error was: $!\n"; logger($conf, $THIS_FILE, __LINE__, "- Created: [$directory]", 1); } } # Ok, acquire it! if ($conf->{switches}{'centos-iso'} eq "dvd") { logger($conf, $THIS_FILE, __LINE__, "- A CentOS ISO will be generated from the disk in the optical drive.", 1); my ($rc, $local_copy) = generate_iso($conf, $conf->{directory}{centos_pxe_web}."/x86_64/iso/", "centos"); # 0 == OK # 1 == Rip failed. # 2 == Wrong disk. # 3 == Device not found # 4 == No disc in drive # 5 == Resulting image too small if ($rc) { ### Errors will already be reported. exit(22); } else { # \o/ } } elsif ($conf->{switches}{'centos-iso'} =~ /:\/\//) { logger($conf, $THIS_FILE, __LINE__, "- CentOS ISO appears to be a URL, will attempt to download now...", 1); $file_name = ($conf->{switches}{'centos-iso'} =~ /^.*\/(.*?)$/)[0]; $local_copy = $conf->{directory}{centos_pxe_web}."/x86_64/iso/$file_name"; my $shell_call = $conf->{executable}{wget}." -c ".$conf->{switches}{'centos-iso'}." -O $local_copy"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); while(<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; # Make sure it downloaded. if (-e $local_copy) { logger($conf, $THIS_FILE, __LINE__, "- Download successful.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed to download: [".$conf->{switches}{'centos-iso'}."]", 0); exit(22); } } elsif (-e $conf->{switches}{'centos-iso'}) { logger($conf, $THIS_FILE, __LINE__, "- CentOS ISO is local, will copy into place.", 1); $file_name = ($conf->{switches}{'centos-iso'} =~ /^.*\/(.*?)$/)[0]; $local_copy = $conf->{directory}{centos_pxe_web}."/x86_64/iso/$file_name"; if ($local_copy eq $conf->{switches}{'centos-iso'}) { logger($conf, $THIS_FILE, __LINE__, "- Local copy already in the right place.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Copying the local ISO into the apache directory.", 1); rsync_files($conf, $conf->{switches}{'centos-iso'}, $conf->{directory}{centos_pxe_web}."/x86_64/iso/"); } } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The CentOS ISO doesn't appear to be valid. Is this right?:", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] [".$conf->{switches}{'centos-iso'}."]", 0); exit(22); } # If I'm here, we're ready to configure. logger($conf, $THIS_FILE, __LINE__, "- Mounting the ISO: [$local_copy]", 1); logger($conf, $THIS_FILE, __LINE__, "- At mount point: [".$conf->{directory}{centos_pxe_web}."/x86_64/img/]", 1); mount_iso($conf, $local_copy, $conf->{directory}{centos_pxe_web}."/x86_64/img/"); # copy the isolinux directory. logger($conf, $THIS_FILE, __LINE__, "- Copying isolinux boot files to TFTP.", 1); my $isolinux_source = $conf->{directory}{centos_pxe_web}."/x86_64/img/isolinux/*"; my $isolinux_target = $conf->{directory}{pxelinux_boot}."/".$conf->{directory}{centos_tftp_boot}."/x86_64/"; if (not -e $isolinux_target) { logger($conf, $THIS_FILE, __LINE__, "- Creating tftp boot target: [$isolinux_target]", 1); make_path($isolinux_target); if (-e $isolinux_target) { logger($conf, $THIS_FILE, __LINE__, "- target created.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed!", 0); } } rsync_files($conf, "$isolinux_source", "$isolinux_target"); $conf->{pxe}{centos}{enabled} = 1; } } # Now RHEL if ($conf->{switches}{'rhel-iso'}) { my $file_name = ""; my $local_copy = ""; if ($conf->{switches}{'rhel-iso'} eq "none") { logger($conf, $THIS_FILE, __LINE__, "- RHEL disabled, skipping.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- RHEL source defined, configuring.", 1); # Setup the directories. my @subdirectories = ($conf->{directory}{rhel_pxe_web}, $conf->{directory}{rhel_pxe_web}."/x86_64"); foreach my $subdirectory (@{$conf->{directory}{pxe_iso_subdirs}}) { push @subdirectories, $conf->{directory}{rhel_pxe_web}."/x86_64/$subdirectory"; } foreach my $directory (@subdirectories) { if (-e $directory) { logger($conf, $THIS_FILE, __LINE__, "- Directory already exists: [$directory]", 1); } else { mkdir $directory or die "$THIS_FILE ".__LINE__."; Failed to create the directory: [$directory], error was: $!\n"; logger($conf, $THIS_FILE, __LINE__, "- Created: [$directory]", 1); } } # Ok, acquire it! if ($conf->{switches}{'rhel-iso'} eq "dvd") { logger($conf, $THIS_FILE, __LINE__, "- A RHEL ISO will be generated from the disk in the optical drive.", 1); (my $rc, $local_copy) = generate_iso($conf, $conf->{directory}{rhel_pxe_web}."/x86_64/iso/", "rhel"); # 0 == OK # 1 == Rip failed. # 2 == Wrong disk. # 3 == Device not found # 4 == No disc in drive # 5 == Resulting image too small if ($rc) { ### Errors will already be reported. exit(22); } else { # \o/ } } elsif ($conf->{switches}{'rhel-iso'} =~ /:\/\//) { logger($conf, $THIS_FILE, __LINE__, "- RHEL ISO appears to be a URL, will attempt to download now...", 1); $file_name = ($conf->{switches}{'rhel-iso'} =~ /^.*\/(.*?)$/)[0]; $local_copy = $conf->{directory}{rhel_pxe_web}."/x86_64/iso/$file_name"; my $shell_call = $conf->{executable}{wget}." -c ".$conf->{switches}{'rhel-iso'}." -O $local_copy"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); while(<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; # Make sure it downloaded. if (-e $local_copy) { logger($conf, $THIS_FILE, __LINE__, "- Download successful.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed to download: [".$conf->{switches}{'rhel-iso'}."]", 0); exit(22); } } elsif (-e $conf->{switches}{'rhel-iso'}) { logger($conf, $THIS_FILE, __LINE__, "- RHEL ISO is local, will copy into place.", 1); $file_name = ($conf->{switches}{'rhel-iso'} =~ /^.*\/(.*?)$/)[0]; $local_copy = $conf->{directory}{rhel_pxe_web}."/x86_64/iso/$file_name"; if ($local_copy eq $conf->{switches}{'rhel-iso'}) { logger($conf, $THIS_FILE, __LINE__, "- Local copy already in the right place.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Copying the local ISO into the apache directory.", 1); rsync_files($conf, $conf->{switches}{'rhel-iso'}, $conf->{directory}{rhel_pxe_web}."/x86_64/iso/"); } } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The RHEL ISO doesn't appear to be valid. Is this right?:", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] [".$conf->{switches}{'rhel-iso'}."]", 0); exit(22); } # If I'm here, we're ready to configure. logger($conf, $THIS_FILE, __LINE__, "- Mounting the ISO: [$local_copy]", 1); logger($conf, $THIS_FILE, __LINE__, "- At mount point: [".$conf->{directory}{rhel_pxe_web}."/x86_64/img/]", 1); mount_iso($conf, $local_copy, $conf->{directory}{rhel_pxe_web}."/x86_64/img/"); # copy the isolinux directory. logger($conf, $THIS_FILE, __LINE__, "- Copying isolinux boot files to TFTP.", 1); my $isolinux_source = $conf->{directory}{rhel_pxe_web}."/x86_64/img/isolinux/*"; my $isolinux_target = $conf->{directory}{pxelinux_boot}."/".$conf->{directory}{rhel_tftp_boot}."/x86_64/"; if (not -e $isolinux_target) { logger($conf, $THIS_FILE, __LINE__, "- Creating tftp boot target: [$isolinux_target]", 1); make_path($isolinux_target); if (-e $isolinux_target) { logger($conf, $THIS_FILE, __LINE__, "- target created.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed!", 0); } } rsync_files($conf, "$isolinux_source", "$isolinux_target"); $conf->{pxe}{rhel}{enabled} = 1; } } logger($conf, $THIS_FILE, __LINE__, "Done!\n", 1); return(0); } # This generates an ISO from the disc in an optical drive. sub generate_iso { my ($conf, $output_directory, $distro) = @_; # 0 == OK # 1 == Rip failed. # 2 == Wrong disk. # 3 == Device not found # 4 == No disc in drive # 5 == Resulting image too small my $return_code = 0; # Some variables. my $device = ""; my $volume = ""; my $file_name = ""; if ($distro eq "rhel") { logger($conf, $THIS_FILE, __LINE__, "- Generating a RHEL disk image from the DVD in the optical drive.", 1); } elsif ($distro eq "centos") { logger($conf, $THIS_FILE, __LINE__, "- Generating a CentOS disk image from the DVD in the optical drive.", 1); } # To divine the file name, and to confirm the disc is still available, # I need to check the disc. my $shell_call = "cd-info --dvd --no-cddb --no-device-info --no-disc-mode --no-vcd"; 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 = $_; if ($line =~ /CD location\s+:\s+(.*)/i) { $device = $1; } elsif ($line =~ /Volume\s+:\s+(.*)/i) { $volume = $1; } elsif ($line =~ /No medium found/i) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - No disc found in the optical drive.", 0); $return_code = 4; last; } else { logger($conf, $THIS_FILE, __LINE__, " cd-info:$line", 3); } } close $file_handle; # Now make sure we're sane. if (not $device) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Unable to find an optical drive.", 0); $return_code = 3; } elsif ($volume) { if (($distro eq "rhel") && ($volume !~ /RHEL-6/i)) { # Not a RHEL 6 disk. $return_code = 2; logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Disk in drive is labelled: [$volume],", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] which does not appear to be a RHEL 6 disk.", 0); } elsif (($distro eq "centos") && ($volume !~ /CentOS_6/)) { # Not a CentOS 6 disk. $return_code = 2; logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Disk in drive is labelled: [$volume],", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] which does not appear to be a CentOS 6 disk.", 0); } } # Are we ready? if (not $return_code) { # Yup! logger($conf, $THIS_FILE, __LINE__, "- Creating image of optical disk.", 1); $volume =~ s/ /_/g; $file_name = "$output_directory/$volume.iso"; if (-e $file_name) { logger($conf, $THIS_FILE, __LINE__, "- Output file: [$file_name]", 1); logger($conf, $THIS_FILE, __LINE__, " already exists, skipping ISO generation.", 1); } else { logger($conf, $THIS_FILE, __LINE__, " Please be patient as this might take a few minutes.", 1); my $iso_size = 0; my $shell_call = "dd if=$device of=$file_name bs=1M"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /(\d+) bytes/) { $iso_size = $1; logger($conf, $THIS_FILE, __LINE__, "iso_size: [$iso_size]", 3); } } close $file_handle; # Make sure it is at least 50 MiB if (-e $file_name) { logger($conf, $THIS_FILE, __LINE__, "iso_size: [$iso_size]", 3); # 3782213632 if ($iso_size > 52428800) { # Looks good! logger($conf, $THIS_FILE, __LINE__, "- Output file: [$file_name]", 1); logger($conf, $THIS_FILE, __LINE__, " Write complete!", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Output file: [$file_name]", 1); logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Write appears to be too small.", 0); $return_code = 5; } } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Write appears to have failed. The output file:", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] [$file_name] wasn't created.", 0); $return_code = 1; } } } return($return_code, $file_name); } ### NOTE: Not used currently. # Unmounts an ISO sub unmount_iso { my ($conf, $target) = @_; # First make sure the target isn't already mounted. my $shell_call = $conf->{executable}{umount}." $target; echo \"umount:\$?\""; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; if ($line =~ /^umount:(\d+)/) { my $rc = $1; if ($rc == 0) { logger($conf, $THIS_FILE, __LINE__, "- Unmounted successfully", 1); } elsif ($rc == 1) { logger($conf, $THIS_FILE, __LINE__, "- Was already unmounted!", 1); } } logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 3); } close $file_handle; return(0); } # Mounts an ISO at the specified path, if not already mounted. sub mount_iso { my ($conf, $source, $target) = @_; # First make sure the target isn't already mounted. $source =~ s/\/\//\//g; my $iso_file = ($source =~ /^.*\/(.*)$/)[0]; my $shell_call = "if \$(grep -q $source /etc/fstab); then echo 'already added to /etc/fstab'; else echo '$source $target auto loop 0 0' >> /etc/fstab; echo 'added ISO to /etc/fstab'; fi; if \$(mount | grep -q $iso_file); then echo 'already mounted'; else mount $target; echo mount:$?; fi"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; if ($line =~ /already added/) { logger($conf, $THIS_FILE, __LINE__, "- ISO file: [$iso_file] already in '/etc/fstab'.", 1); } if ($line =~ /added ISO/) { logger($conf, $THIS_FILE, __LINE__, "- ISO file: [$iso_file] added to '/etc/fstab'.", 1); } if ($line =~ /already mounted/) { logger($conf, $THIS_FILE, __LINE__, "- ISO file: [$iso_file] Was already mounted!", 1); } if ($line =~ /^mount:(\d+)/) { my $rc = $1; if ($rc == 0) { logger($conf, $THIS_FILE, __LINE__, "- ISO file: [$iso_file] was mounted successfully.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - ISO file: [$iso_file] appears to have NOT mounted!", 1); logger($conf, $THIS_FILE, __LINE__, "[ Warning ] Return code was: [$rc], expected '0'.", 1); } } logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 3); } close $file_handle; return(0); } # This generates a kickstart file. sub generate_kickstart { my ($conf, $os, $machine, $number) = @_; my $short_os_name = $os eq "rhel" ? $conf->{pxe}{rhel}{short_name} : $conf->{pxe}{centos}{short_name}; my $machine_name = "new-node0".$number; if ($machine eq "striker") { $machine_name = "new-striker0".$number; } my $domain_name = ($conf->{sys}{hostname} =~ /^.*?\.(.*)$/)[0]; $domain_name = "localdomain" if not $domain_name; # Common part. my $kickstart = " # Generated by: [$THIS_FILE] on: [".get_date($conf)."]. ### Setup values. # Doing a full, text-based install. install text # This is the web server with the installation image. url --url=http://".$conf->{network}{bcn}{ip}."/$short_os_name/x86_64/img/ # Set the language and keyboard to en_US, UTF-8. Adjust as needed. lang en_CA.UTF-8 keyboard us # Set the timezone to Eastern Standard/Daylight Time. To get your preferred # time zone, run 'tzselect'. When finished, looks for the line like: # - Therefore TZ='America/Toronto' will be used. # Take the value in \"TZ='...'\" and use it below. timezone --utc America/Toronto # This sets the (first) ethernet device. There is currently no way to map # device names to physical interfaces. For this reason, I use DHCP for install # and configure the network manually post-install. network --device eth0 --bootproto dhcp --onboot yes --hostname ".$machine_name.".".$domain_name." # 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 Initial1 # Default admin user account. user --name=admin --plaintext --password=Initial1 # At this time, Striker does not yet work with SELinux in enforcing mode. This # is expected to change in a (near) future release. firewall --service=ssh "; if ($machine eq "striker") { $kickstart .= "selinux --permissive\n"; } else { $kickstart .= "selinux --enforcing\n"; } $kickstart .= " # 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 # This installs most of the stuff we'll need for stage-2 as well as the stage-1 # requirements. \%packages # Common packages #-\@Core #-\@Base acl acpid aic94xx-firmware alteeve-repo atmel-firmware attr audit b43-openfwwf basesystem bash bash-completion bfa-firmware ccs cim-schema compat-libstdc++-33.i686 coreutils cpio cronie cyrus-sasl cyrus-sasl-plain dhclient dmidecode dracut-network e2fsprogs efibootmgr expect fence-agents filesystem freeipmi freeipmi-bmc-watchdog freeipmi-ipmidetectd gcc gcc-c++ glibc gpm grub initscripts ipmitool iproute iptables iptables-ipv6 iputils ipw2100-firmware ipw2200-firmware irqbalance ivtv-firmware iwl100-firmware iwl1000-firmware iwl3945-firmware iwl4965-firmware iwl5000-firmware iwl5150-firmware iwl6000-firmware iwl6000g2a-firmware iwl6050-firmware kbd kernel-devel kernel-firmware kernel-headers kexec-tools libertas-usb8388-firmware libstdc++-devel.i686 libstdc++.i686 mailx man mlocate mtr ncurses ntp OpenIPMI OpenIPMI-libs openssh-clients openssh-server openssl-devel parted passwd pciutils pcp perl perl-CGI perl-Crypt-SSLeay perl-DBD-Pg perl-Digest-SHA perl-Digest-SHA1 perl-Mail-RFC822-Address perl-Net-SSH2 perl-Net-Telnet perl-Sys-Virt perl-TermReadKey perl-Text-Diff perl-Time-HiRes perl-XML-Simple policycoreutils policycoreutils-python postfix postgresql95 procps ql2100-firmware ql2200-firmware ql23xx-firmware ql2400-firmware ql2500-firmware rootfiles rpm rsync rsyslog rt61pci-firmware rt73usb-firmware screen selinux-policy-targeted setup shadow-utils sharutils sudo syslinux sysstat tuned usbutils util-linux-ng vim vim-common vim-enhanced vim-minimal virt-viewer wget xorg-x11-drv-ati-firmware yum zd1211-firmware "; if ($os eq "centos") { $kickstart .= " # CentOS specific packages yum-plugin-priorities "; } if ($machine eq "striker") { $kickstart .= " # 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 { $kickstart .= " # 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 "; } $kickstart .= " \%end # 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. \%post sed -i 's/keepcache=0/keepcache=1/g' /etc/yum.conf #for nic in \$(ls /etc/sysconfig/network-scripts/ifcfg-eth*); do sed -i 's/ONBOOT=.*/ONBOOT=\"yes\"/' \$nic; done # Disable DNS lookup for SSH so that logins are quick when there is not Internet # access. 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 plymouth-set-default-theme details -R 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 # Download 'list-ips' from the Striker we're installing from. echo \"Downloading 'list-ips'.\" mkdir /sbin/striker curl http://".$conf->{network}{bcn}{ip}."/$short_os_name/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->{network}{bcn}{ip}."/$short_os_name/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->{network}{bcn}{ip}."/$short_os_name/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->{network}{bcn}{ip}."/$short_os_name/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->{network}{bcn}{ip}."/$short_os_name/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 "; my $os_directory = $conf->{pxe}{centos}{short_name}; my $say_os = "CentOS"; if ($os eq "rhel") { $os_directory = $conf->{pxe}{rhel}{short_name}; $say_os = "RHEL"; } my $short_name = ($conf->{sys}{hostname} =~ /^(.*?)\..*$/)[0]; $short_name = $conf->{sys}{hostname} if not $short_name; if ($machine eq "striker") { my $peer_number = $number eq "1" ? "2" : "1"; # Striker stuff logger($conf, $THIS_FILE, __LINE__, "- sys::prefix: [".$conf->{sys}{prefix}."], sys::domain: [".$conf->{sys}{domain}."].", 1); $kickstart .= " # Download striker source package. echo \"Downloading striker source.\" curl http://".$conf->{network}{bcn}{ip}."/$short_os_name/x86_64/files/striker.tar.gz > /root/striker.tar.gz tar -xvzf /root/striker.tar.gz --directory=/root/ # Download apache 'img' source. echo \"Downloading img source.\" mkdir -p /var/www/html/$short_os_name/x86_64/files curl http://".$conf->{network}{bcn}{ip}."/$short_os_name/x86_64/files/img.tar.gz > /var/www/html/$short_os_name/x86_64/files/img.tar.gz tar -xvzf /var/www/html/$short_os_name/x86_64/files/img.tar.gz --directory=/var/www/html/$short_os_name/x86_64/ # Copy 'striker-installer'. echo \"Preparing 'striker-installer'.\" cp /root/striker/tools/striker-installer /root/ chown root:root /root/striker-installer chmod 755 /root/striker-installer # Now setup the script for the user to call once booted. echo 'Writing out the sample striker-installer script' cat > /root/striker-installer.example << EOF # This is an example 'striker-installer' call. Feel free to edit this file here # and then call it with 'sh /root/striker-installer.example' to save typing # all this out. # # To understand what all these switches do, and what other switches are # available, please run './striker-installer --help'. /root/striker-installer \\\\ -c \"".$conf->{sys}{customer}."\" \\\\ -n \"".$conf->{sys}{prefix}."-striker0".$number.".".$conf->{sys}{domain}."\" \\\\ -u \"admin:Initial1\" \\\\ -i 10.255.4.".$number."/16,dg=10.255.255.254,dns1=8.8.8.8,dns2=8.8.4.4 \\\\ -b 10.20.4.".$number."/16 \\\\ -p 10.20.7.200:10.20.7.230 \\\\ --peer-dashboard hostname=".$conf->{sys}{prefix}."-striker0".$peer_number.".".$conf->{sys}{domain}.",bcn_ip=10.20.4.".$peer_number." \\\\ "; # Use '--rhn' on RHEL only. if ($os eq "rhel") { $kickstart .= " --router-mode \\\\\n"; $kickstart .= " --rhn \"rhn_admin:rhn_Initial1\"\n"; } else { $kickstart .= " --router-mode\n"; } $kickstart .= "EOF chmod 755 /root/striker-installer.example "; } else { # Node stuff $kickstart .= ""; } $kickstart .= " # Add local repos cat > /etc/yum.repos.d/".$short_name."_".$os.".repo << EOF [".$short_name."-$os] name=Striker ".$short_name." repository baseurl=http://".$conf->{network}{bcn}{ip}."/$os_directory/x86_64/img/ http://".$conf->{network}{ifn}{ip}."/$os_directory/x86_64/img/ enabled=1 gpgcheck=0 skip_if_unavailable=1 priority=1 EOF \%end # 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 ### Script to setup partitions. \%pre --log=/tmp/ks-preinstall.log #!/bin/sh "; if ($machine eq "node") { $kickstart .= " # 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 <> /tmp/part-include <{pxe}{centos}{short_name}; if ($conf->{sys}{is_rhel}) { $os_short = $conf->{pxe}{rhel}{short_name}; $say_os = "RHEL"; } my $pxe_background = ($conf->{path}{pxe_background_source} =~ /^.*\/(.*)$/)[0]; my $uefi_config = "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 net title New Anvil! Node 1 - ".$say_os." v6 - PXE - Deletes All Existing Data! root (nd) macappend kernel /vmlinuz ip=dhcp ks=http://".$conf->{network}{bcn}{ip}."/".$os_short."/x86_64/ks/pxe-new-node01.ks ksdevice=bootif initrd /initrd.img # Install Node 02 via net title New Anvil! Node 2 - ".$say_os." v6 - PXE - Deletes All Existing Data! root (nd) macappend kernel /vmlinuz ip=dhcp ks=http://".$conf->{network}{bcn}{ip}."/".$os_short."/x86_64/ks/pxe-new-node02.ks ksdevice=bootif initrd /initrd.img # Install Striker 1 via net title New Striker Dashboard 01 - ".$say_os." v6 - Network Installation - Deletes All Existing Data! root (nd) macappend kernel /vmlinuz ip=dhcp ks=http://".$conf->{network}{bcn}{ip}."/".$os_short."/x86_64/ks/pxe-new-striker01.ks ksdevice=bootif initrd /initrd.img # Install Striker 2 via net title New Striker Dashboard 02 - ".$say_os." v6 - Network Installation - Deletes All Existing Data! root (nd) macappend kernel /vmlinuz ip=dhcp ks=http://".$conf->{network}{bcn}{ip}."/".$os_short."/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 # Memory Test title Memory test memtest "; # Write out the new one. logger($conf, $THIS_FILE, __LINE__, "- Writing out dhcp daemon PXE UEFI configuration file.", 1); open (my $file_handle, ">", $conf->{path}{uefi_default_config}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{uefi_default_config}."], the error was: $!\n"; print $file_handle $uefi_config; close $file_handle; # Now traditional BIOS my $default_config = "# 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 $pxe_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 Anvil! node and Striker 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 LABEL next MENU LABEL ^0) Boot the next device as configured in your BIOS MENU DEFAULT localboot -1 LABEL node1 MENU LABEL ^1) Anvil! m2, Node 01 - ".$say_os." 6 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. ENDTEXT KERNEL boot/$os_short/x86_64/vmlinuz IPAPPEND 2 APPEND initrd=boot/$os_short/x86_64/initrd.img inst.repo=http://".$conf->{network}{bcn}{ip}."/$os_short/x86_64/img/ ks=http://".$conf->{network}{bcn}{ip}."/$os_short/x86_64/ks/".$conf->{pxe}{centos}{node1_ks}." ksdevice=bootif LABEL node2 MENU LABEL ^2) Anvil! m2, Node 02 - ".$say_os." 6 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. ENDTEXT KERNEL boot/$os_short/x86_64/vmlinuz IPAPPEND 2 APPEND initrd=boot/$os_short/x86_64/initrd.img inst.repo=http://".$conf->{network}{bcn}{ip}."/$os_short/x86_64/img/ ks=http://".$conf->{network}{bcn}{ip}."/$os_short/x86_64/ks/".$conf->{pxe}{centos}{node2_ks}." ksdevice=bootif LABEL striker1 MENU LABEL ^3) Striker 01 Dashboard - ".$say_os." 6 TEXT HELP Installs a new Striker 01 using ".$say_os." 6. ENDTEXT KERNEL boot/$os_short/x86_64/vmlinuz IPAPPEND 2 APPEND initrd=boot/$os_short/x86_64/initrd.img inst.repo=http://".$conf->{network}{bcn}{ip}."/$os_short/x86_64/img/ ks=http://".$conf->{network}{bcn}{ip}."/$os_short/x86_64/ks/".$conf->{pxe}{centos}{striker1_ks}." ksdevice=bootif LABEL generic-centos-striker MENU LABEL ^4) Striker 02 Dashboard - ".$say_os." 6 TEXT HELP Installs a new Striker 02 using ".$say_os." 6. ENDTEXT KERNEL boot/$os_short/x86_64/vmlinuz IPAPPEND 2 APPEND initrd=boot/$os_short/x86_64/initrd.img inst.repo=http://".$conf->{network}{bcn}{ip}."/$os_short/x86_64/img/ ks=http://".$conf->{network}{bcn}{ip}."/$os_short/x86_64/ks/".$conf->{pxe}{centos}{striker2_ks}." ksdevice=bootif "; # Write out the new one. logger($conf, $THIS_FILE, __LINE__, "- Writing out dhcp daemon configuration file.", 1); open ($file_handle, ">", $conf->{path}{pxe_default_config}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{pxe_default_config}."], the error was: $!\n"; print $file_handle $default_config; close $file_handle; # Copy the PXE menu background into place. logger($conf, $THIS_FILE, __LINE__, "- Copying PXE server's wallpaper into place.", 1); copy_file($conf, "/var/www/html/".$os_short."/x86_64/img/striker/ISO/".$say_os."/6/Anvil/syslinux/splash.jpg", "/var/lib/tftpboot/"); copy_file($conf, "/var/www/html/".$os_short."/x86_64/img/striker/ISO/".$say_os."/6/Anvil/EFI/BOOT/pxe_voyager1.xpm.gz", "/var/lib/tftpboot/efi64/"); # Setup UEFI PXE booting. my $efi_source = $conf->{path}{pxe_efi_source}; my $efi_target = $conf->{directory}{pxelinux_efi}."/".$conf->{path}{pxe_efi_target}; if (not -e $efi_target) { logger($conf, $THIS_FILE, __LINE__, "- Creating EFI PXE Linux directory: [".$conf->{directory}{pxelinux_efi}."]", 1); make_path($conf->{directory}{pxelinux_efi}); # Copy (and rename) the logger($conf, $THIS_FILE, __LINE__, "- Copying the UEFI PXE boot image.", 1); if (not -e $efi_source) { # Can't copy what doesn't exist... logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The UEFI boot image: [".$efi_source."] doesn't exist, can't copy it to: [".$efi_target."].\n", 0); exit(44); } copy_file($conf, $efi_source, $efi_target); if (-e $efi_target) { # Success! logger($conf, $THIS_FILE, __LINE__, "- Successfully copied the UEFI boot image: [".$efi_source."] to: [".$efi_target."].", 1); } else { # Something went wrong with the copy. logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed to copy UEFI boot image: [".$efi_source."] to: [".$efi_target."]. The error might be shown above\n", 0); exit(45); } } # Make sure the 'efi64/bootx64.efi' file is chmod 755 chmod 0755, $efi_target; my $bios_vmlinuz = $conf->{directory}{pxelinux_boot}."/".$os_short."/x86_64/vmlinuz"; my $bios_initrd = $conf->{directory}{pxelinux_boot}."/".$os_short."/x86_64/initrd.img"; if (not -e $bios_vmlinuz) { copy_file($conf, "/var/www/html/".$os_short."/x86_64/img/images/pxeboot/vmlinuz", $bios_vmlinuz); } if (not -e $bios_initrd) { copy_file($conf, "/var/www/html/".$os_short."/x86_64/img/images/pxeboot/initrd.img", $bios_initrd); } my $efi_vmlinuz = $conf->{directory}{pxelinux_efi}."/vmlinuz"; my $efi_initrd = $conf->{directory}{pxelinux_efi}."/initrd.img"; if (not -e $efi_vmlinuz) { copy_file($conf, "/var/www/html/".$os_short."/x86_64/img/images/pxeboot/vmlinuz", $efi_vmlinuz); } if (not -e $efi_initrd) { copy_file($conf, "/var/www/html/".$os_short."/x86_64/img/images/pxeboot/initrd.img", $efi_initrd); } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # Configure xinetd to enable tftp access. sub configure_xinetd { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Configuring xinetd/tftp server for PXE installs.", 1); if (-e $conf->{path}{xinetd_tftp_config}) { backup_file($conf, $conf->{path}{xinetd_tftp_config}); } logger($conf, $THIS_FILE, __LINE__, "- Reading in current: [".$conf->{path}{xinetd_tftp_config}."]", 1); my $xinetd_tftp_config = ""; my $shell_call = $conf->{path}{xinetd_tftp_config}; open (my $file_handle, "<", $shell_call) or die "$THIS_FILE ".__LINE__."; Failed to read: [$shell_call], the error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] >> line: [$line]", 3); $line =~ s/^(\s+)disable(\s+)=.*$/${1}disable${2}= no/; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] << line: [$line]", 3); $xinetd_tftp_config .= "$line\n"; } close $file_handle; # Next, write it out. logger($conf, $THIS_FILE, __LINE__, "- Writing out the new version.", 1); open ($file_handle, ">", $conf->{path}{xinetd_tftp_config}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{xinetd_tftp_config}."], the error was: $!\n"; print $file_handle $xinetd_tftp_config; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Verifying the PXE config directory exists.", 1); if (not -e $conf->{directory}{pxelinux_config}) { mkdir $conf->{directory}{pxelinux_config} or die "$THIS_FILE ".__LINE__."; Failed to create the directory: [".$conf->{directory}{pxelinux_config}."], error was: $!\n"; logger($conf, $THIS_FILE, __LINE__, "- Created: [".$conf->{directory}{pxelinux_config}."]", 1); } logger($conf, $THIS_FILE, __LINE__, "- Verifying the PXE boot directory exists.", 1); if (not -e $conf->{directory}{pxelinux_boot}) { mkdir $conf->{directory}{pxelinux_boot} or die "$THIS_FILE ".__LINE__."; Failed to create the directory: [".$conf->{directory}{pxelinux_boot}."], error was: $!\n"; logger($conf, $THIS_FILE, __LINE__, "- Created: [".$conf->{directory}{pxelinux_boot}."]", 1); } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # This sets up DHCP sub configure_dhcp { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Configuring DHCP server for PXE installs.", 1); # Backup any existing config. if (-e $conf->{path}{dhcpd_conf}) { backup_file($conf, $conf->{path}{dhcpd_conf}); } # If the user didn't specify a BCN IP, then read it from the config # file. If that doesn't exist, fail. my $ifcfg_file = ""; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] ".__LINE__."; - network::bcn::ip: [".$conf->{network}{bcn}{ip}."]", 3); if (not $conf->{network}{bcn}{ip}) { # BCN not specified. if (-e $conf->{path}{network_configs}."/ifcfg-bcn_bond1") { # Read in the IP from the bond file. $ifcfg_file = $conf->{path}{network_configs}."/ifcfg-bcn_bond1"; my $shell_call = "grep -e IPADDR -e NETMASK $ifcfg_file"; 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 = $_; $line =~ s/"//g; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /IPADDR=(\d+\.\d+\.\d+\.\d+)/i) { $conf->{network}{bcn}{ip} = $1; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] ".__LINE__."; - network::bcn::ip: [".$conf->{network}{bcn}{ip}."]", 3); } if ($line =~ /NETMASK=(\d+\.\d+\.\d+\.\d+)/i) { $conf->{network}{bcn}{netmask} = $1; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] ".__LINE__."; - network::bcn::netmask: [".$conf->{network}{bcn}{netmask}."]", 3); } } close $file_handle; } elsif (-e $conf->{path}{network_configs}."/ifcfg-bcn_link1") { # Read in the IP from the link file $ifcfg_file = $conf->{path}{network_configs}."/ifcfg-bcn_link1"; my $shell_call = "grep IPADDR $ifcfg_file"; 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 = $_; $line =~ s/"//g; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /IPADDR=(\d+\.\d+\.\d+\.\d+)/i) { $conf->{network}{bcn}{ip} = $1; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] ".__LINE__."; - network::bcn::ip: [".$conf->{network}{bcn}{ip}."]", 3); } if ($line =~ /NETMASK=(\d+\.\d+\.\d+\.\d+)/i) { $conf->{network}{bcn}{netmask} = $1; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] ".__LINE__."; - network::bcn::netmask: [".$conf->{network}{bcn}{netmask}."]", 3); } } close $file_handle; } else { # Error out. logger($conf, $THIS_FILE, __LINE__, "[ Error ] - PXE configuration was requested but the BCN IP wasn't specified.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Tried to read the current BCN IP from either config file:", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Bond: [".$conf->{path}{network_configs}."/ifcfg-bcn_bond1]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Link: [".$conf->{path}{network_configs}."/ifcfg-bcn_link1]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Neither was found, so the BCN IP can not be determined. There is", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] no way to configure the DHCP server.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Install exiting.\n", 0); exit(26); } } # See if we have the IP now. logger($conf, $THIS_FILE, __LINE__, "[ Debug ] ".__LINE__."; - network::bcn::ip: [".$conf->{network}{bcn}{ip}."]", 1); if (not $conf->{network}{bcn}{ip}) { # Error out. logger($conf, $THIS_FILE, __LINE__, "[ Error ] - PXE configuration was requested but the BCN IP wasn't specified.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Tried to read the IP from: [$ifcfg_file]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] but the IP address wasn't defined (or found).", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Install exiting.\n", 0); exit(27); } my $domain_name = ($conf->{sys}{hostname} =~ /^.*?\.(.*)$/)[0]; $domain_name = "localdomain" if not $domain_name; my $bcn_network = ""; if ($conf->{network}{bcn}{netmask} eq "255.255.0.0") { $bcn_network = ($conf->{network}{bcn}{ip} =~ /^(\d+\.\d+\.)/)[0]; $bcn_network .= "0.0"; } if ($conf->{network}{bcn}{netmask} eq "255.255.255.0") { $bcn_network = ($conf->{network}{bcn}{netmask} =~ /^(\d+\.\d+\.\d+\.)/)[0]; $bcn_network .= "0"; } logger($conf, $THIS_FILE, __LINE__, "[ Debug ] ".__LINE__."; - network::bcn::netmask: [".$conf->{network}{bcn}{netmask}."]", 3); # Not all users will use DNS. If not specified, see if the DNS has already been configured in /etc/resolv.conf. if ((not $conf->{network}{ifn}{dns1}) && (not $conf->{network}{ifn}{dns2})) { my $count = 1; my $shell_call = "grep ^nameserver /etc/resolv.conf"; 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 = $_; $line =~ s/"//g; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /nameserver (\d+\.\d+\.\d+\.\d+)/i) { my $key = "dns".$count; $conf->{network}{ifn}{$key} = $1; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] ".__LINE__."; - network::ifn::".$key.": [".$conf->{network}{ifn}{$key}."]", 3); $count++; } } close $file_handle; } my $say_dns = "option domain-name-servers ".$conf->{network}{ifn}{dns1}.", ".$conf->{network}{ifn}{dns2}.";"; if ((not $conf->{network}{ifn}{dns1}) && (not $conf->{network}{ifn}{dns2})) { $say_dns = "# DNS disabled by the installer. Uncomment the line below to enable.\n"; $say_dns .= "#option domain-name-servers 8.8.8.8, 8.8.4.4;"; } elsif (not $conf->{network}{ifn}{dns2}) { # Only DNS1 defined $say_dns = "option domain-name-servers ".$conf->{network}{ifn}{dns1}.";"; } elsif (not $conf->{network}{ifn}{dns1}) { # Only DNS2 defined... Whatever, I don't judge people. $say_dns = "option domain-name-servers ".$conf->{network}{ifn}{dns2}.";"; } # This is the new UEFI one. The commented out one below is legacy-BIOS only my $dhcpd_config = " # Generated by: [$THIS_FILE] on: [".get_date($conf)."]. # DHCP Daemon config for PXE-based installs of nodes and striker dashboards. ### Global options # General domain information option domain-name \"$domain_name\"; $say_dns # Tell the server that it is authoritative on our network. authoritative; # This controls how dynamic DNS updating is handled. In our case, we aren't # concerned about DDNS so we'll set it to 'none'. ddns-update-style none; # May be needed for UEFI, but probably not as both are default-enabled anyway. #allow booting; #allow bootp; ### Subnet options subnet $bcn_network netmask ".$conf->{network}{bcn}{netmask}." { # These two options tell clients where to go to get the file needed to # start the boot process. next-server ".$conf->{network}{bcn}{ip}."; # Our servers are installed on the BCN where they have no Internet # access, so this value is largely meaningless. We set it to this # machine for reasons. option routers ".$conf->{network}{bcn}{ip}."; # Set our range. This can be whatever you want so long as it fits in # your netmask. range ".$conf->{switches}{lease_start}." ".$conf->{switches}{lease_end}."; # If clients don't ask, make the lease available for the following # number of seconds. If the client does ask, allow up to this number of # seconds. Being that we're only handing out IPs long enough to install # an OS, we can keep this quite short. One hour is more than enough. default-lease-time 3600; max-lease-time 3600; } # This enables booting clients depending on whether they're UEFI or traditional # BIOS based systems. class \"pxeclient\" { match if substring (option vendor-class-identifier, 0, 9) = \"PXEClient\"; if substring (option vendor-class-identifier, 15, 5) = \"00000\" { filename \"pxelinux.0\"; } elsif substring (option vendor-class-identifier, 15, 5) = \"00006\" { filename \"efi32/syslinux.efi\"; } else { filename \"efi64/bootx64.efi\"; } } \n"; # Write out the new one. logger($conf, $THIS_FILE, __LINE__, "- Writing out dhcp daemon configuration file.", 1); open (my $file_handle, ">", $conf->{path}{dhcpd_conf}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{dhcpd_conf}."], the error was: $!\n"; print $file_handle $dhcpd_config; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # This is call restart or stop on daemons and makes sure they're set to (not) # run on boot. sub manage_daemons { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Configuring daemons to start/stop on boot and start/stop daemons.", 1); foreach my $daemon (@{$conf->{daemons}{disable}}) { # No sense proceeding if the daemon doesn't exist. next if not -e "/etc/init.d/$daemon"; my $shell_call = $conf->{executable}{chkconfig}." $daemon off && /etc/init.d/$daemon stop"; if ($daemon eq "NetworkManager") { $shell_call = $conf->{executable}{chkconfig}." $daemon off"; logger($conf, $THIS_FILE, __LINE__, "- Disabling: [$daemon] on boot but NOT stopping it.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Disabling: [$daemon] on boot and stopping it.", 1); } logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; $line =~ s/\n//g; $line =~ s/\r//g; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 3); } close $file_handle; sleep 1; } foreach my $daemon (@{$conf->{daemons}{enable}}) { # No sense proceeding if the daemon doesn't exist. next if not -e "/etc/init.d/$daemon"; my $shell_call = $conf->{executable}{chkconfig}." $daemon on && /etc/init.d/$daemon restart"; if ($daemon eq "network") { # Checkconfig only $shell_call = $conf->{executable}{chkconfig}." $daemon on"; logger($conf, $THIS_FILE, __LINE__, "- Enabling: [$daemon] on boot but NOT restarting it.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Enabling: [$daemon] on boot and (re)starting it.", 1); } logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; $line =~ s/\n//g; $line =~ s/\r//g; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 3); if (($line =~ /FAILED/i) && ($daemon eq "dhcpd")) { logger($conf, $THIS_FILE, __LINE__, "[ Note ] - Starting DHCP daemon will fail if the BCN IP changed and the system", 1); logger($conf, $THIS_FILE, __LINE__, "[ Note ] hasn't rebooted yet. It should work after the reboot.", 1); } } close $file_handle; sleep 1; } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # Set the owning user and group for various files and set the mode/permissions # as well. sub set_file_ownership_and_modes { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Setting ownership and permissions/modes on Striker files.", 1); # I make shell calls here because it is too tricky to handle the # recursive calls in native perl. foreach my $file (sort {$a cmp $b} keys %{$conf->{permissions}}) { my $path = $conf->{permissions}{$file}{path}; my $owner = $conf->{permissions}{$file}{owner}; my $group = $conf->{permissions}{$file}{group}; my $mode = $conf->{permissions}{$file}{mode}; my $recursive = $conf->{permissions}{$file}{recursive} ? "-R" : ""; my $selinux = $conf->{permissions}{$file}{selinux}; logger($conf, $THIS_FILE, __LINE__, "- Setting ownership and mode to: [$owner:$group] and: [$mode] (".$conf->{permissions}{$file}{recursive}.") on: [$path]", 1); # Recompile the setuid C-wrappers. my $c_file = $path.".c"; if (-e $c_file) { logger($conf, $THIS_FILE, __LINE__, "- This is a 'setuid' C-wrapper; recompiling it first.", 1); my $shell_call = $conf->{executable}{gcc}." -o $path $c_file"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- gcc output: [$line]", 1); } close $file_handle; } my $shell_call = $conf->{executable}{'chown'}." $recursive $owner:$group $path"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; if ($line !~ /Read-only file system/i) { logger($conf, $THIS_FILE, __LINE__, "- chown output: [$line]", 1); } } close $file_handle; $shell_call = $conf->{executable}{'chmod'}." $recursive $mode $path"; logger($conf, $THIS_FILE, __LINE__, "[ Debug ] ".__LINE__." - shell_call: [$shell_call]", 3); open ($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 = $_; if ($line !~ /Read-only file system/i) { logger($conf, $THIS_FILE, __LINE__, "- chmod output: [$line]", 1); } } close $file_handle; # NOTE: Disabled until I get the media library tools working # with selinux. #if ($selinux) if (0) { #[root@striker ~]# semanage fcontext -a -t httpd_sys_content_t /var/log/striker.log #[root@striker ~]# restorecon /var/log/striker.log logger($conf, $THIS_FILE, __LINE__, " Setting selinux context to: [$selinux].", 1); my $shell_call = $conf->{executable}{semanage}." fcontext -a -t $selinux $path && ".$conf->{executable}{restorecon}." $path"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; } } # Allow httpd to make outbound network connections. if (0) { logger($conf, $THIS_FILE, __LINE__, "- Giving apache permission to create network connections.", 1); my $shell_call = $conf->{executable}{setsebool}." -P httpd_can_network_connect on"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; } # Allow fence_virsh to call ssh if (0) { logger($conf, $THIS_FILE, __LINE__, "- Allowing fence_virsh to call ssh.", 1); my $shell_call = $conf->{executable}{setsebool}." -P fenced_can_ssh on"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # Copy the files for Striker from the source directory into the apache # directory. sub install_striker_files { my ($conf) = @_; # Copy the 'html' directory logger($conf, $THIS_FILE, __LINE__, "Copying Striker program files into place.", 1); if (-e $conf->{path}{backups}."/www") { logger($conf, $THIS_FILE, __LINE__, "- Backup already made, skipping.", 1); } else { my $www_directory = $conf->{directory}{document_root}; $www_directory =~ s/\/$//; logger($conf, $THIS_FILE, __LINE__, "- Backing up: [$www_directory].", 1); backup_file($conf, $www_directory); } logger($conf, $THIS_FILE, __LINE__, "- Copying HTML files and skins.", 1); rsync_files($conf, $conf->{directory}{source}."/html", $conf->{directory}{document_root}); logger($conf, $THIS_FILE, __LINE__, "- Copying executable files and languages.", 1); rsync_files($conf, $conf->{directory}{source}."/cgi-bin", $conf->{directory}{document_root}); #logger($conf, $THIS_FILE, __LINE__, "- Copying tools.", 1); #rsync_files($conf, $conf->{directory}{source}."/tools", $conf->{directory}{document_root}); #logger($conf, $THIS_FILE, __LINE__, "- Copying ScanCore.", 1); #rsync_files($conf, $conf->{directory}{source}."/ScanCore", $conf->{directory}{document_root}); # Split the directory from the file. my ($striker_config_directory, $striker_config_file) = ($conf->{path}{striker_config} =~ /^(.*)\/(.*)$/); if (-e $conf->{path}{striker_config}) { logger($conf, $THIS_FILE, __LINE__, "- Existing Striker configuration file found, not replacing it.", 1); } else { # Make sure the striker /etc/ directory exists. my $parent_dir = ($striker_config_directory =~ /^.*\/(.*)$/)[0]; if (-d $striker_config_directory) { logger($conf, $THIS_FILE, __LINE__, "- Striker configuration directory already exists.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Creating the Striker configuration directory.", 1); mkdir $striker_config_directory or die "$THIS_FILE ".__LINE__."; Failed to create the directory: [$striker_config_directory], error was: $!\n"; } # If the user has specified a custom config file, use it. if ($conf->{switches}{'custom-config'}) { # They have. Does it exist? logger($conf, $THIS_FILE, __LINE__, "- Custom configuration specified. Checking that it exists.", 1); if (-e $conf->{switches}{'custom-config'}) { logger($conf, $THIS_FILE, __LINE__, "- It does.", 1); logger($conf, $THIS_FILE, __LINE__, "- Copying: [".$conf->{switches}{'custom-config'}."]", 1); logger($conf, $THIS_FILE, __LINE__, " to: [".$conf->{path}{striker_config}."]", 1); copy_file($conf, $conf->{switches}{'custom-config'}, $conf->{path}{striker_config}); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - I was asked to load the custom Striker configuration file:", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] [".$conf->{switches}{'custom-config'}."]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] However, it doesn't seem to exist.\n", 0); exit(30); } } else { logger($conf, $THIS_FILE, __LINE__, "- Copying base Striker configuration file.", 1); my $source = $conf->{directory}{source}."/$striker_config_file"; my $destination = "$striker_config_directory/"; # If we're installing from a minimal install, striker.conf may be under a different directory. if (not -e $source) { $source = $conf->{directory}{source}."/striker/$striker_config_file"; if (not -e $source) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Unable to find the source striker.conf in either: [".$conf->{directory}{source}."/$striker_config_file] or: [".$conf->{directory}{source}."/striker/$striker_config_file].\n", 0); exit(30); } } logger($conf, $THIS_FILE, __LINE__, "- Copying: [$source]", 1); logger($conf, $THIS_FILE, __LINE__, " to: [$destination]", 1); copy_file($conf, $source, $destination); } } # If the user has given us a specific host uuid, write it now. if ($conf->{switches}{'host-uuid'}) { # Is it valid? logger($conf, $THIS_FILE, __LINE__, "- User-defined host UUID, validating it.", 1); if ($conf->{switches}{'host-uuid'} =~ /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i) { # Valid, record it. logger($conf, $THIS_FILE, __LINE__, "- The UUID is valid, recording it.", 1); open (my $file_handle, ">", $conf->{path}{host_uuid}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{host_uuid}."], the error was: $!\n"; print $file_handle lc($conf->{switches}{'host-uuid'})."\n"; close $file_handle; } else { # Bad UUID, error out. logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The requested host UUID: [".$conf->{switches}{'host-uuid'}."] appears to be invalid.\n", 0); exit(43); } } else { # Use the system UUID as reported by dmidecode. my $shell_call = $conf->{executable}{dmidecode}." --string system-uuid"; my $host_uuid = ""; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); if ($line =~ /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i) { $host_uuid = lc($line); logger($conf, $THIS_FILE, __LINE__, "- host_uuid: [$host_uuid]", 1); } } close $file_handle; if ($host_uuid) { logger($conf, $THIS_FILE, __LINE__, "- The system UUID is being used for the host UUID.", 1); open (my $file_handle, ">", $conf->{path}{host_uuid}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{host_uuid}."], the error was: $!\n"; print $file_handle $host_uuid."\n"; close $file_handle; } } # Update /etc/striker/striker.conf if a custom config was not used. if (not $conf->{switches}{'custom-config'}) { # Update the striker.conf file. logger($conf, $THIS_FILE, __LINE__, "- Updating striker.conf.", 1); logger($conf, $THIS_FILE, __LINE__, "Reading: [".$conf->{path}{striker_config}."]", 3); my $striker_config = ""; open (my $file_handle, "<", $conf->{path}{striker_config}) or die "$THIS_FILE ".__LINE__."; Failed to read: [".$conf->{path}{striker_config}."], the error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; # Inject this machine's details for the local ScanCore DB. $striker_config .= "$line\n"; if ($line =~ /#scancore::db::2::password\s+=\s+Initial1/) { $striker_config .= "scancore::db::1::host = ".$conf->{sys}{hostname}."\n"; $striker_config .= "scancore::db::1::port = 5432\n"; $striker_config .= "scancore::db::1::name = $conf->{sys}{scancore_database}\n"; $striker_config .= "scancore::db::1::user = $conf->{sys}{striker_user}\n"; $striker_config .= "scancore::db::1::password = $conf->{sys}{striker_password}\n\n"; # If they told us about the peer, add it. logger($conf, $THIS_FILE, __LINE__, "switches::peer-dashboard: [".$conf->{switches}{'peer-dashboard'}."]", 3); if ($conf->{switches}{'peer-dashboard'}) { my $peer_hostname = ($conf->{switches}{'peer-dashboard'} =~ /hostname=(.*)$/)[0]; $peer_hostname =~ s/,bcn_ip.*//; logger($conf, $THIS_FILE, __LINE__, "peer_hostname: [$peer_hostname]", 3); if ($peer_hostname) { $striker_config .= "scancore::db::2::host = $peer_hostname\n"; $striker_config .= "scancore::db::2::port = 5432\n"; $striker_config .= "scancore::db::2::name = $conf->{sys}{scancore_database}\n"; $striker_config .= "scancore::db::2::user = $conf->{sys}{striker_user}\n"; $striker_config .= "scancore::db::2::password = $conf->{sys}{striker_password}\n\n"; } } } } close $file_handle; # Next, write it out. logger($conf, $THIS_FILE, __LINE__, "- Writing out the new version.", 1); open ($file_handle, ">", $conf->{path}{striker_config}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{striker_config}."], the error was: $!\n"; print $file_handle $striker_config; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Done.", 1); } # Copy the striker.conf file to /sbin/striker/ so that nodes can use it # as a template. #logger($conf, $THIS_FILE, __LINE__, "- Copying striker.conf for use as a template by nodes.", 1); #rsync_files($conf, $conf->{path}{striker_config}, $conf->{directory}{striker_tools}."/"); # Create an empty log file logger($conf, $THIS_FILE, __LINE__, "- Creating empty Striker log file.", 1); open (my $file_handle, ">", $conf->{path}{striker_log}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{striker_log}."], the error was: $!\n"; print $file_handle ""; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # This copies multiple files using rsync sub rsync_files { my ($conf, $source, $destination) = @_; my $shell_call = $conf->{executable}{rsync}." -a $source $destination"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; # Verify it copied successfully. if ($source !~ /\*$/) { my $file = ($source =~ /^.*\/(.*)$/)[0]; $file = "$destination/$file"; if (not -e $file) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed to rsync: [$source]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] to: [$destination]", 0); exit(21); } } } # This copies a file... Hey, what it says on the tin. sub copy_file { my ($conf, $source, $destination) = @_; my $shell_call = $conf->{executable}{cp}." -af $source $destination"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; # Verify it copied successfully. # If the destination ends in '/', look for the source file appeanded to # the destination. Othewise, the name changed. if ($destination =~ /\/$/) { # Source file copied into a directory. my $file = ($source =~ /^.*\/(.*)$/)[0]; $file = "$destination/$file"; if (not -e $file) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed to copy: [$source]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] to: [$destination]", 0); exit(19); } } elsif (not -e $destination) { # Copied to a new target name. logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Failed to copy: [$source]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] to: [$destination]", 0); exit(19); } return(0); } # Download Striker, if needed. sub download_striker { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Downloading Striker, if needed.", 1); my $extract = ""; my $download = ""; if ($conf->{sys}{install_version} eq "git") { logger($conf, $THIS_FILE, __LINE__, "- Downloading the developer version from github.", 1); $download = $conf->{url}{git_master}; $extract = ($conf->{url}{git_master} =~ /^.*\/(.*)$/)[0]; $conf->{directory}{source} = "./striker-master"; logger($conf, $THIS_FILE, __LINE__, "directory::source: [".$conf->{directory}{source}."]", 3); } else { if (-e "./version") { my $version; logger($conf, $THIS_FILE, __LINE__, "- Version file exists, checking if it matches the requested version.", 1); open (my $file_handle, "<", "./version") or die "$THIS_FILE ".__LINE__."; Failed to read: [./version], the error was: $!\n"; while(<$file_handle>) { chomp; $version = $_; } close $file_handle; if ($version eq $conf->{sys}{install_version}) { logger($conf, $THIS_FILE, __LINE__, "- Download not needed, files already available.", 1); $conf->{directory}{source} = "./"; } else { logger($conf, $THIS_FILE, __LINE__, "- Local version: [$version] doesn't match requested version: [".$conf->{sys}{install_version}."]", 1); logger($conf, $THIS_FILE, __LINE__, "- Downloading requested version from: [".$conf->{url}{striker}."]", 1); $download = $conf->{url}{striker}; $extract = ($conf->{url}{striker} =~ /^.*\/(.*)$/)[0]; $conf->{directory}{source} = "./striker-".$conf->{sys}{install_version}; } } else { # Download needed. logger($conf, $THIS_FILE, __LINE__, "- Downloading version: [".$conf->{sys}{install_version}."] from: [".$conf->{url}{striker}."]", 1); $download = $conf->{url}{striker}; $extract = ($conf->{url}{striker} =~ /^.*\/(.*)$/)[0]; $conf->{directory}{source} = "./striker-".$conf->{sys}{install_version}; } } # Download, if needed if ($download) { # See if it is already downloaded. if (-e "./$extract") { logger($conf, $THIS_FILE, __LINE__, "- The source: [$extract] already exists, skipping.", 1); } else { my $shell_call = $conf->{executable}{wget}." -c $download"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); while(<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; # Make sure it downloaded. if (not -e $extract) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The download failed!", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Does: [$download] exist?", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Is there a working network connection?", 0); exit(18); } else { logger($conf, $THIS_FILE, __LINE__, "- Download complete.", 1); } } } # Extract, if needed. if ($extract) { logger($conf, $THIS_FILE, __LINE__, "- Extracting: [$extract]", 1); logger($conf, $THIS_FILE, __LINE__, "Checking if the directory: [".$conf->{directory}{source}."] exists already.", 3); if (-d $conf->{directory}{source}) { logger($conf, $THIS_FILE, __LINE__, "- The source appears to already be extracted, skipping.", 1); } else { my $source_dir_found = 0; my $shell_call = $conf->{executable}{tar}." -xvf $extract"; if ($extract =~ /\.zip$/) { $shell_call = $conf->{executable}{unzip}." $extract"; } logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; if (($line =~ /^(.*?)\/$/) && (not $source_dir_found)) { #print __LINE__."; line: [$line]\n"; # This overrides anything set above # (though those need to be set in-case # extraction wasn't needed). my $source = $1; #print __LINE__."; source: [$source]\n"; # When using 'unzip', the line as 'creating:' in it. if ($line =~ /creating: (.*?\/)$/) { $source = $1; #print __LINE__."; source: [$source]\n"; } $conf->{directory}{source} = "./$source"; #print __LINE__."; directory::source: [".$conf->{directory}{source}."]\n"; $source_dir_found = 1; } logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; } } logger($conf, $THIS_FILE, __LINE__, "- Will use the source directory: [".$conf->{directory}{source}."]", 1); logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # Configure iptables to allow incoming HTTP and HTTPS traffic. sub configure_iptables { my ($conf) = @_; # Read in the current rules so we don't recreate rules that already exist. logger($conf, $THIS_FILE, __LINE__, "Configuring the firewall to allow normal and secure web access.", 1); if (-e $conf->{path}{backups}."/iptables") { logger($conf, $THIS_FILE, __LINE__, "- Backup already made, skipping.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Backing up: [".$conf->{path}{iptables}."].", 1); backup_file($conf, $conf->{path}{iptables}); } my $changes = 0; my $tftp_open = 0; my $http_open = 0; my $https_open = 0; my $vnc_open = 0; my $pgsql_open = 0; my $nfs_open = 0; # This is needed for remote USB mount. logger($conf, $THIS_FILE, __LINE__, "- Reading the current firewall configuration.", 1); my $shell_call = $conf->{executable}{'iptables-save'}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 3); if (($line =~ /--state NEW/) && ($line =~ /--dport 69/) && ($line =~ /ACCEPT/)) { if ($line =~ /tcp/) { logger($conf, $THIS_FILE, __LINE__, "- Trivial FTP, tcp port is already enabled.", 1); } elsif ($line =~ /udp/) { logger($conf, $THIS_FILE, __LINE__, "- Trivial FTP, udp port is already enabled.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Trivial FTP port is already enabled.", 1); } $tftp_open = 1; } if (($line =~ /--state NEW/) && ($line =~ /--dport 2049/) && ($line =~ /ACCEPT/)) { if ($line =~ /tcp/) { logger($conf, $THIS_FILE, __LINE__, "- NFS for Remote USB, tcp port is already enabled.", 1); } elsif ($line =~ /udp/) { logger($conf, $THIS_FILE, __LINE__, "- NFS for Remote USB, udp port is already enabled.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- NFS for Remote USB port is already enabled.", 1); } $nfs_open = 1; } if (($line =~ /--state NEW/) && ($line =~ /--dport 80/) && ($line =~ /ACCEPT/)) { logger($conf, $THIS_FILE, __LINE__, "- Standard web access is already enabled.", 1); $http_open = 1; } if (($line =~ /--state NEW/) && ($line =~ /--dport 443/) && ($line =~ /ACCEPT/)) { logger($conf, $THIS_FILE, __LINE__, "- Secure web access is already enabled.", 1); $https_open = 1; } if (($line =~ /--state NEW/) && ($line =~ /--dport 5900/) && ($line =~ /ACCEPT/)) { logger($conf, $THIS_FILE, __LINE__, "- VNC is already enabled.", 1); $vnc_open = 1; } if (($line =~ /--state NEW/) && ($line =~ /--dport 5432/) && ($line =~ /ACCEPT/)) { logger($conf, $THIS_FILE, __LINE__, "- PostgreSQL is already enabled.", 1); $pgsql_open = 1; } } close $file_handle; if (not $tftp_open) { logger($conf, $THIS_FILE, __LINE__, "- Opening access for trivial FTP (needed for PXE booting).", 1); my $shell_call = $conf->{executable}{iptables}." -I INPUT -m state --state NEW -p tcp --dport 69 -j ACCEPT && "; $shell_call .= $conf->{executable}{iptables}." -I INPUT -m state --state NEW -p udp --dport 69 -j ACCEPT"; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; $changes = 1; } if (not $nfs_open) { logger($conf, $THIS_FILE, __LINE__, "- Opening access for NFS (needed for Remote USB).", 1); my $shell_call = $conf->{executable}{iptables}." -I INPUT -m state --state NEW -p tcp --dport 2049 -j ACCEPT && "; $shell_call .= $conf->{executable}{iptables}." -I INPUT -m state --state NEW -p udp --dport 2049 -j ACCEPT"; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; $changes = 1; } if (not $http_open) { logger($conf, $THIS_FILE, __LINE__, "- Opening access for standard web access.", 1); my $shell_call = $conf->{executable}{iptables}." -I INPUT -m state --state NEW -p tcp --dport 80 -j ACCEPT"; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; $changes = 1; } if (not $https_open) { logger($conf, $THIS_FILE, __LINE__, "- Opening access for secure web access.", 1); my $shell_call = $conf->{executable}{iptables}." -I INPUT -m state --state NEW -p tcp --dport 443 -j ACCEPT"; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; $changes = 1; } if (not $vnc_open) { logger($conf, $THIS_FILE, __LINE__, "- Opening access for VNC to the dashboard desktop.", 1); my $shell_call = $conf->{executable}{iptables}." -I INPUT -m state --state NEW -p tcp --dport 5800 -j ACCEPT; "; $shell_call = $conf->{executable}{iptables}." -I INPUT -m state --state NEW -p tcp --dport 5900 -j ACCEPT; "; $shell_call = $conf->{executable}{iptables}." -I INPUT -m state --state NEW -p tcp --dport 6000 -j ACCEPT; "; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; $changes = 1; } if (not $pgsql_open) { logger($conf, $THIS_FILE, __LINE__, "- Opening access for PostgreSQL.", 1); my $shell_call = $conf->{executable}{iptables}." -I INPUT -m state --state NEW -p tcp --dport 5432 -j ACCEPT; "; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; $changes = 1; } if ($changes) { # Save the changes. logger($conf, $THIS_FILE, __LINE__, "- Saving the new iptables configuration.", 1); my $shell_call = "/etc/init.d/iptables save"; 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 = $_; # The formatting here isn't nice. $line =~ s/\n//g; $line =~ s/\r//g; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 3); } close $file_handle; } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # Configre SSH to disable IP lookup to enable faster SSH access when there is # no Internet access. Also, create the apache user's RSA keys. sub configure_ssh { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Configuring SSH daemon to allow faster logins.", 1); if (-e $conf->{path}{backups}."/sshd_config") { logger($conf, $THIS_FILE, __LINE__, "- Backup already made, skipping.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Backing up: [".$conf->{path}{sshd_config}."].", 1); backup_file($conf, $conf->{path}{sshd_config}); } # Read in and then write out the server.xml file to enable UTF-8 my $sshd_config = ""; logger($conf, $THIS_FILE, __LINE__, "- Reading in the existing ssh daemon configuration file.", 1); open (my $file_handle, "<", $conf->{path}{sshd_config}) or die "$THIS_FILE ".__LINE__."; Failed to read: [".$conf->{path}{sshd_config}."], the error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; # Change the lines, if needed. $line =~ s/#GSSAPIAuthentication no/GSSAPIAuthentication no/; $line =~ s/GSSAPIAuthentication yes/#GSSAPIAuthentication yes/; $line =~ s/#UseDNS yes/UseDNS no/; $sshd_config .= "$line\n"; } close $file_handle; # Next, write it out. logger($conf, $THIS_FILE, __LINE__, "- Writing out the new version.", 1); open ($file_handle, ">", $conf->{path}{sshd_config}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{sshd_config}."], the error was: $!\n"; print $file_handle $sshd_config; close $file_handle; if (-e $conf->{path}{apache_home}."/.ssh/id_rsa") { logger($conf, $THIS_FILE, __LINE__, "- SSH RSA keys for: [".$conf->{sys}{apache_user}."] already created, skipping.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Generating: [".$conf->{sys}{apache_user}."]'s RSA keys.", 1); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] Please be patient! It might take time to collect entropy.", 1); my $shell_call = $conf->{executable}{su}." apache -c \"".$conf->{executable}{'ssh-keygen'}." -t rsa -N \\\"\\\" -b 8191 -f ~/.ssh/id_rsa\""; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; } # Create an SSH key for root. if (not -e "/root/.ssh/id_rsa") { logger($conf, $THIS_FILE, __LINE__, "- Creating the SSH RSA key for 'root'.", 1); my $shell_call = $conf->{executable}{'ssh-keygen'}." -t rsa -N \"\" -b 8191 -f ~/.ssh/id_rsa"; logger($conf, $THIS_FILE, __LINE__, "- shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # This makes sure that selinux is enabled. sub configure_selinux { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Making sure that selinux is enabled.", 3); logger($conf, $THIS_FILE, __LINE__, "Making sure that selinux is permissive.", 1); if (-e $conf->{path}{backups}."/config") { logger($conf, $THIS_FILE, __LINE__, "- Backup already made, skipping.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Backing up: [".$conf->{path}{selinux_config}."3].", 1); backup_file($conf, $conf->{path}{selinux_config}); } # If selinux is off, warn the user that the next boot might be slow. my $current_policy = ""; my $selinux_config = ""; logger($conf, $THIS_FILE, __LINE__, "- Reading in the existing selinux config file.", 1); open (my $file_handle, "<", $conf->{path}{selinux_config}) or die "$THIS_FILE ".__LINE__."; Failed to read: [".$conf->{path}{selinux_config}."], the error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; # Update the SELinux policy. if ($line =~ /^SELINUX=(.*)/) { # Record the current policy in case we want to alert # the user. $current_policy = $1; #$selinux_config .= "SELINUX=enforcing\n"; $selinux_config .= "SELINUX=permissive\n"; } else { $selinux_config .= "$line\n"; } } close $file_handle; # If the old policy was 'disabled', want that the next reboot could be # slow. if ($current_policy eq "disabled") { logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - It looks like selinux was disabled.", 1); logger($conf, $THIS_FILE, __LINE__, "[ Warning ] The next reboot might take longer than normal if a filesystem", 1); logger($conf, $THIS_FILE, __LINE__, "[ Warning ] relabel is required.", 1); sleep 5; } elsif ($current_policy eq "enforcing") { # Set enforcing to 0. logger($conf, $THIS_FILE, __LINE__, "- Setting selinux to 'permissive'.", 1); my $shell_call = $conf->{executable}{setenforce}." 0"; 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; } # Next, write it out. logger($conf, $THIS_FILE, __LINE__, "- Writing out the new version.", 1); open ($file_handle, ">", $conf->{path}{selinux_config}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{selinux_config}."], the error was: $!\n"; print $file_handle $selinux_config; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # This sets the hostname of the system. sub set_hostname { my ($conf) = @_; # First, edit the hostname file. logger($conf, $THIS_FILE, __LINE__, "Configuring this system's host name.", 1); logger($conf, $THIS_FILE, __LINE__, "- Reading in the existing hostname file.", 1); my $hostname = ""; open (my $file_handle, "<", $conf->{path}{hostname}) or die "$THIS_FILE ".__LINE__."; Failed to read: [".$conf->{path}{hostname}."], the error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; # Update the apache user entry. if ($line =~ /^HOSTNAME=/) { # Now insert our new authentication lines $hostname .= "HOSTNAME=".$conf->{sys}{hostname}."\n"; } else { $hostname .= "$line\n"; } } close $file_handle; # Next, write it out. logger($conf, $THIS_FILE, __LINE__, "- Writing out the new version.", 1); open ($file_handle, ">", $conf->{path}{hostname}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{hostname}."], the error was: $!\n"; print $file_handle $hostname; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); # And finally, change the hostname for the current session. system("hostname ".$conf->{sys}{hostname}); return(0); } # This handles configuring apache. sub configure_apache { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Configuring Apache.", 1); # Start by backing things up. if (-e $conf->{path}{backups}."/httpd") { logger($conf, $THIS_FILE, __LINE__, "- Backup already made, skipping.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Backing up original files.", 1); backup_file($conf, $conf->{directory}{apache}); } # Enable the 'apache' user. enable_apache_user($conf); # Create the apache user's home directory and sub-directories. create_apache_home_directories($conf); # Create the htpasswd file. create_apache_htpasswd($conf); # Now to start updating. logger($conf, $THIS_FILE, __LINE__, "- Reading in the existing config file and adjusting as needed.", 1); my $apache_config = ""; open (my $file_handle, "<", $conf->{path}{apache_config}) or die "$THIS_FILE ".__LINE__."; Failed to read: [".$conf->{path}{apache_config}."], the error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; # Skip lines from previous htpasswd configs. next if $line =~ /^\s+# Password login/; next if $line =~ /^\s+AuthType /; next if $line =~ /^\s+AuthName /; next if $line =~ /^\s+AuthUserFile /; next if $line =~ /^\s+Require user /; ### TODO: Set SSL as default and add an option for a user to ### load their own certs. # Now insert and/or modify things. if ($line =~ /{sys}{customer}."\"\n"; $apache_config .= " AuthUserFile ".$conf->{path}{apache_htpasswd}."\n"; $apache_config .= " Require user ".$conf->{sys}{striker_user}."\n"; } elsif ($line =~ /^Timeout /) { $apache_config .= "Timeout ".$conf->{sys}{apache_timeout}."\n"; } else { $apache_config .= "$line\n"; } } close $file_handle; # Now write it out. logger($conf, $THIS_FILE, __LINE__, "- Writing out the new version.", 1); open ($file_handle, ">", $conf->{path}{apache_config}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{apache_config}."], the error was: $!\n"; print $file_handle $apache_config; close $file_handle; # Write out the manifest.conf file logger($conf, $THIS_FILE, __LINE__, "- Writing out the apache manifest configuration file.", 1); if (-e $conf->{path}{apache_manifest_conf}) { logger($conf, $THIS_FILE, __LINE__, "- Apache manifest configuration file already exists.", 1); } else { open ($file_handle, ">", $conf->{path}{apache_manifest_conf}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{apache_manifest_conf}."], the error was: $!\n"; print $file_handle "\n"; print $file_handle " \n"; print $file_handle " Header set Content-Disposition attachment\n"; print $file_handle " \n"; print $file_handle "\n"; close $file_handle; } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # This edits the passwd file to enable logins by apache. sub enable_apache_user { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "- Reading in the existing system user file.", 1); my $passwd = ""; open (my $file_handle, "<", $conf->{path}{passwd}) or die "$THIS_FILE ".__LINE__."; Failed to read: [".$conf->{path}{passwd}."], the error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; # Update the apache user entry. if ($line =~ /apache:x:(\d+):(\d+):(.*?):(.*?):(.*)/) { # Now insert our new authentication lines my $uid = $1; my $gid = $2; my $name = $3; # I don't care about this my $home = $4; # or this. $passwd .= "apache:x:$uid:$gid:$name:$conf->{path}{apache_home}:$conf->{path}{apache_shell}\n"; } else { $passwd .= "$line\n"; } } close $file_handle; # Now write it out. logger($conf, $THIS_FILE, __LINE__, "- Writing out the new version.", 1); open ($file_handle, ">", $conf->{path}{passwd}) or die "$THIS_FILE ".__LINE__."; Failed to write: [".$conf->{path}{passwd}."], the error was: $!\n"; print $file_handle $passwd; close $file_handle; return(0); } # This sets the local 'root' user's password. sub set_root_password { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Setting root user's password.", 1); if ($conf->{sys}{striker_password}) { $conf->{sys}{striker_password} =~ s/"/\\"/g; my $shell_call = $conf->{executable}{echo}." \"".$conf->{sys}{striker_password}."\" | ".$conf->{executable}{passwd}." root --stdin"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "Done!\n", 1); logger($conf, $THIS_FILE, __LINE__, " ##############################################################################", 1); logger($conf, $THIS_FILE, __LINE__, " # NOTE: Your 'root' user password is now the same as the Striker user's #", 1); logger($conf, $THIS_FILE, __LINE__, " # password you just specified. If you want a different password, #", 1); logger($conf, $THIS_FILE, __LINE__, " # change it now with 'passwd'! #", 1); logger($conf, $THIS_FILE, __LINE__, " ##############################################################################\n", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Password not set, leaving it unchanged.", 1); logger($conf, $THIS_FILE, __LINE__, "Done!\n", 1); } return(0); } # Create the htpasswd file. sub create_apache_htpasswd { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "- Setting up Striker's authentication via Apache's htpasswd...", 1); if (-e $conf->{path}{apache_htpasswd}) { logger($conf, $THIS_FILE, __LINE__, "- Old: [".$conf->{path}{apache_htpasswd}."] found, deleting it.", 1); unlink $conf->{path}{apache_htpasswd} or die "$THIS_FILE ".__LINE__."; Failed to delete: [".$conf->{path}{apache_htpasswd}."], error was: $!\n"; } # Escape any single-quotes in the user's password. $conf->{sys}{striker_password} =~ s/"/\\\\\\\"/g; logger($conf, $THIS_FILE, __LINE__, "- Generating: [".$conf->{sys}{striker_user}."]'s: [".$conf->{path}{apache_htpasswd}."] password file.", 1); my $shell_call = $conf->{executable}{su}." apache -c \"".$conf->{executable}{htpasswd}." -cdb ".$conf->{path}{apache_htpasswd}." ".$conf->{sys}{striker_user}." \\\"".$conf->{sys}{striker_password}."\\\"\""; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; return(0); } # This creates the apache home directories and sets their permissions. sub create_apache_home_directories { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "- Creating the apache home directories.", 1); my @dirs; push @dirs, $conf->{path}{apache_home}; if (not -e $conf->{path}{apache_home}) { logger($conf, $THIS_FILE, __LINE__, "- Creating: [".$conf->{path}{apache_home}."].", 1); mkdir $conf->{path}{apache_home} or die "$THIS_FILE ".__LINE__."; Failed to create the directory: [".$conf->{path}{apache_home}."], the error was: $!\n"; } else { logger($conf, $THIS_FILE, __LINE__, "- Home already exists: [".$conf->{path}{apache_home}."].", 1); } foreach my $directory (@{$conf->{directory}{apache_subdirs}}) { my $full_path = $conf->{path}{apache_home}."/$directory"; push @dirs, $full_path; if (not -e $full_path) { logger($conf, $THIS_FILE, __LINE__, "- Creating: [$full_path].", 1); mkdir $full_path or die "$THIS_FILE ".__LINE__."; Failed to create the directory: [$full_path], the error was: $!\n"; } else { logger($conf, $THIS_FILE, __LINE__, "- Already exists: [$full_path].", 1); } } # Make sure the directory that install manifests will be stored in is available. if (not -e $conf->{directory}{apache_manifests}) { logger($conf, $THIS_FILE, __LINE__, "- Creating: [".$conf->{directory}{apache_manifests}."].", 1); mkdir $conf->{directory}{apache_manifests} or die "$THIS_FILE ".__LINE__."; Failed to create the directory: [".$conf->{directory}{apache_manifests}."], the error was: $!\n"; } else { logger($conf, $THIS_FILE, __LINE__, "- Install manifests directory already exists: [".$conf->{directory}{apache_manifests}."].", 1); } logger($conf, $THIS_FILE, __LINE__, "- Setting ownership to: [".$conf->{sys}{apache_user}.":".$conf->{sys}{apache_group}."].", 1); my $apache_uid = getpwnam($conf->{sys}{apache_user}); my $apache_gid = getgrnam($conf->{sys}{apache_group}); my $dir_mode = 0775; if (not $apache_uid) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Unable to get the User ID for the apache user: [".$conf->{sys}{apache_user}."]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Did apache actually install properly?", 0); exit(15); } if (not $apache_gid) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Unable to get the Group ID for the apache group: [".$conf->{sys}{apache_group}."]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Did apache actually install properly?", 0); exit(16); } chown $apache_uid, $apache_gid, @dirs; logger($conf, $THIS_FILE, __LINE__, "- Setting mode to: [".sprintf("%04o", $dir_mode)."].", 1); chmod $dir_mode, @dirs; return(0); } # This checks to see if this server is already registered with Red Hat yet. sub check_if_registered_with_rhn { my ($conf) = @_; my $registered = 0; my $optional_repo = 0; my $shell_call = $conf->{executable}{'subscription-manager'}." identity; ".$conf->{executable}{'echo'}." subscription-manager:\$?"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 2); if ($line =~ /subscription-manager:(\d+)/) { my $exit_code = $1; if ($exit_code == 0) { $registered = 1; logger($conf, $THIS_FILE, __LINE__, "The system is registered.", 2); } } elsif ($line =~ /system identity: (.*)$/) { my $uuid = $1; $registered = 1; logger($conf, $THIS_FILE, __LINE__, "The system's registration UUID is: [$uuid].", 2); } } close $file_handle; # If it is enabled, see if the "optional" repo has been enabled. if ($registered) { my $shell_call = $conf->{executable}{'subscription-manager'}." repos --list-enabled"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 2); if ($line =~ /Repo ID:\s+(.*)$/) { my $repo = $1; logger($conf, $THIS_FILE, __LINE__, "repo: [$repo]", 2); if ($repo eq "rhel-6-server-optional-rpms") { $optional_repo = 1; logger($conf, $THIS_FILE, __LINE__, "- optional_repo: [$optional_repo]", 2); } } } close $file_handle; } logger($conf, $THIS_FILE, __LINE__, "registered: [$registered], optional_repo: [$optional_repo]", 2); return($registered, $optional_repo); } # This does the actual work of registering with Red Hat. sub register_with_rhn { my ($conf) = @_; if (not $conf->{sys}{internet_access}) { logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - Red Hat credentials given, but no internet access found.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - Registration will probably fail.", 0); } ### TODO: Escape bash special vars in passwords. logger($conf, $THIS_FILE, __LINE__, "Red Hat credentials given. Checking if this system has been registered yet.", 1); # Am I already registered? my ($registered, $optional_repo) = check_if_registered_with_rhn($conf); logger($conf, $THIS_FILE, __LINE__, "registered: [$registered], optional_repo: [$optional_repo]", 2); if ($registered) { logger($conf, $THIS_FILE, __LINE__, "- This machine is already registered.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Registration required, proceeding now.", 1); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] Please be patient, this might take a minute...", 1); my $shell_call = $conf->{executable}{'subscription-manager'}." register --username '".$conf->{sys}{rhn_user}."' --password '".$conf->{sys}{rhn_password}."' --name=".$conf->{sys}{hostname}." --auto-attach --force"; logger($conf, $THIS_FILE, __LINE__, "sys::rhn_user: [".$conf->{sys}{rhn_password}."]", 3); logger($conf, $THIS_FILE, __LINE__, "sys::rhn_user: [".$conf->{sys}{rhn_user}."]", 3); logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 2); } close $file_handle; ($registered, $optional_repo) = check_if_registered_with_rhn($conf); logger($conf, $THIS_FILE, __LINE__, "registered: [$registered]", 2); } # Registered now? if (not $registered) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Registration with Red Hat appears to have failed!", 1); logger($conf, $THIS_FILE, __LINE__, "[ Error ] You can run without the '--rhn' switch to install using local packages.", 1); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Installation aborted. ", 1); exit(38); } # If we're alive, we're registered. if ($optional_repo) { logger($conf, $THIS_FILE, __LINE__, "- The 'optional' repo has already been enabled.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Adding the 'Optional' channel now.", 1); my $shell_call = $conf->{executable}{'subscription-manager'}." repos --enable=rhel-6-server-optional-rpms"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 2); } close $file_handle; ($registered, $optional_repo) = check_if_registered_with_rhn($conf); logger($conf, $THIS_FILE, __LINE__, "registered: [$registered], optional_repo: [$optional_repo]", 2); if ($optional_repo) { logger($conf, $THIS_FILE, __LINE__, "- 'Optional' channel added successfully.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - Adding the 'Optional' Red Hat channel appears to have failed!", 1); } } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # This verifies that this is running on a RHEL 6 (or derivative) OS. sub verify_os { my ($conf) = @_; # If the release file doesn't exist, it is most likely not EL. logger($conf, $THIS_FILE, __LINE__, "Checking the operating system to ensure it is compatible.", 1); if (not -e $conf->{path}{os_release}) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - This does not appear to be a RHEL (based) operating system.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] At this time, only RHEL and derivatives version 6.x are supported.", 0); exit(13); } # Read the release file and dig out the version. my $major = -1; my $minor = -1; logger($conf, $THIS_FILE, __LINE__, "- We're on a RHEL (based) OS, good. Checking version.", 1); open (my $file_handle, "<", $conf->{path}{os_release}) or die "$THIS_FILE ".__LINE__."; Failed to read: [".$conf->{path}{os_release}."], error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line].", 1); if ($line =~ /release (\d+)\.(.*)/) { $major = $1; $minor = $2; # CentOS uses 'CentOS Linux release 7.0.1406 (Core)', # so I need to parse off the second '.' and whatever # is after it. $minor =~ s/\..*$//; # Some have 'x.y (Final)', this strips that last bit off. $minor =~ s/\ \(.*?\)$//; } if ($line =~ /Red Hat/i) { $conf->{sys}{is_rhel} = 1; } } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "sys::is_rhel: [".$conf->{sys}{is_rhel}."].", 2); if ($major == 6) { # We're ok, but warn if minor is < 5 if ($minor < 5) { logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - This OS appears to be a little old, you are on: [$major.$minor].", 1); logger($conf, $THIS_FILE, __LINE__, "[ Warning ] Upgrading to at least version 6.5 is recommended, though not", 1); logger($conf, $THIS_FILE, __LINE__, "[ Warning ] strictly required. If you encounter issues, please update before", 1); logger($conf, $THIS_FILE, __LINE__, "[ Warning ] before filing a bug. Thank you.\n", 1); sleep 5; } else { logger($conf, $THIS_FILE, __LINE__, "- Looks good! You're on: [$major.$minor]", 1); } } elsif ($major > 6) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - I am sorry, but EL$major is not yet supported.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Please use RHEL 6.x or similar.", 0); exit(14); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - I am sorry, but EL$major is not supported.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Please use RHEL 6.x or similar.", 0); exit(14); } # If the OS is RHEL proper, see if the OS needs to be activated and do # so if the user has passed the --rhn switch. if ($conf->{sys}{is_rhel}) { logger($conf, $THIS_FILE, __LINE__, "- This OS is RHEL proper.", 1); if (-e $conf->{path}{rhn_file}) { logger($conf, $THIS_FILE, __LINE__, "- The system appears to already be registered with Red Hat.", 1); } elsif (($conf->{sys}{rhn_user}) && ($conf->{sys}{rhn_password})) { $conf->{sys}{register_with_rhn} = 1; } else { # Only show this warning if there is an internet # connection. if ($conf->{sys}{internet_access}) { logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - This system does not appear to be registered with Red Hat and no Red Hat", 1); logger($conf, $THIS_FILE, __LINE__, "[ Warning ] credentials have been given. The install will likely fail if any", 1); logger($conf, $THIS_FILE, __LINE__, "[ Warning ] packages need to be installed and are not locally available.", 1); sleep 2; } else { logger($conf, $THIS_FILE, __LINE__, "- This system will not be registered with Red Hat.", 1); logger($conf, $THIS_FILE, __LINE__, " Red Hat credentials not given and no Internet connection detected.", 1); } } } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # Check to see if we can ping 8.8.8.8 sub verify_internet_access { my ($conf, $quiet) = @_; $quiet = 0 if not $quiet; logger($conf, $THIS_FILE, __LINE__, "Checking for an Internet connection...", 1); my $ok = 0; my $shell_call = $conf->{executable}{ping}." 8.8.8.8 -c 3 -q"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /(\d+) packets transmitted, (\d+) received/) { my $pings_sent = $1; my $pings_received = $2; logger($conf, $THIS_FILE, __LINE__, "pings_sent: [$pings_sent], pings_received: [$pings_received]", 3); if ($pings_received > 0) { $ok = 1; $conf->{sys}{internet_access} = 1; logger($conf, $THIS_FILE, __LINE__, "ok: [$ok]", 3); } else { $conf->{sys}{internet_access} = 0; logger($conf, $THIS_FILE, __LINE__, "not internet access: [".$conf->{sys}{internet_access}."]", 3); } } } if ($ok) { logger($conf, $THIS_FILE, __LINE__, "- Internet access detected.", 1); } else { # Disable external repos $conf->{sys}{yum_switches} = "-y --disablerepo=* --enablerepo=*striker*"; logger($conf, $THIS_FILE, __LINE__, "- No internet access detected, disabling external repositories.", 1); } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); logger($conf, $THIS_FILE, __LINE__, "ok: [$ok]", 3); return($ok); } # This looks at the list of installed RPMs, compares them against the list of # needed RPMs and installs any that are missing. sub install_packages { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Checking if anything needs to be installed.", 1); # Check for OS updates. This needs to come before RPM install to avoid multilib errors in case we're # calling in a new library that would be newer than an installed (different arch) version. $conf->{sys}{update_os} = 0 if $conf->{switches}{'no-os-updates'}; logger($conf, $THIS_FILE, __LINE__, "Checking for OS updates (with priority on on local repo).", 1); if ($conf->{sys}{update_os}) { # Update the OS update_os($conf); # Remove the 'priority=1' from our repos. logger($conf, $THIS_FILE, __LINE__, "Removing priority on on local repo.", 1); remove_yum_priority($conf); # Running the update again without local priority logger($conf, $THIS_FILE, __LINE__, "Checking for OS updates again.", 1); update_os($conf); } else { logger($conf, $THIS_FILE, __LINE__, "- OS update disabled, removing the local repo priority only.", 1); remove_yum_priority($conf); } # If the user has set 'router-mode', add 'shorewall' to the list of packages to install. if ($conf->{switches}{'router-mode'}) { # It was. logger($conf, $THIS_FILE, __LINE__, "- Router mode enabled, will add 'shorewall' to the package list.", 1); $conf->{packages}{to_install}{shorewall} = 0; # Now, should it start on boot? if ($conf->{switches}{'autostart-dhcpd'}) { logger($conf, $THIS_FILE, __LINE__, "- Auto-start of dhcpd was selected, so shorewall will be auto-started as well.", 1); push @{$conf->{daemons}{enable}}, "shorewall"; # Delete iptables from startup my $array = []; foreach my $daemon (sort {$a cmp $b} @{$conf->{daemons}{enable}}) { if ($daemon eq "iptables") { logger($conf, $THIS_FILE, __LINE__, "- Removing iptables from the list of start-up daemons.", 1); } else { push @{$array}, $daemon; } } delete $conf->{daemons}{enable}; $conf->{daemons}{enable} = $array; } else { logger($conf, $THIS_FILE, __LINE__, "- Auto-start of dhcpd was not selected, so shorewall will not be auto-started either.", 1); push @{$conf->{daemons}{disable}}, "shorewall"; } } # Add the storcli and MegaCli RPMs to the install list if the user has # requested them. if ($conf->{switches}{'use-lsi'}) { $conf->{packages}{to_install}{storcli} = 0; $conf->{packages}{to_install}{MegaCli} = 0; } # Get a list of already installed packages. get_list_of_installed_packages($conf); # Install missing packages, if any. install_missing_packages($conf); # Make sure the LSI storage tools have their symlinks created. if ($conf->{switches}{'use-lsi'}) { logger($conf, $THIS_FILE, __LINE__, "- Setting up storage symlinks.", 1); # MegaCli64 first my $shell_call = " if [ -e '".$conf->{executable}{MegaCli64}."' ]; then if [ -e '".$conf->{executable}{MegaCli64_link}."' ] then # echo '".$conf->{executable}{MegaCli64_link}." symlink exists'; else ln -s ".$conf->{executable}{MegaCli64}." ".$conf->{executable}{MegaCli64_link}." if [ -e '".$conf->{executable}{MegaCli64_link}."' ] then echo '".$conf->{executable}{MegaCli64_link}." symlink created'; else echo 'Failed to create ".$conf->{executable}{MegaCli64_link}." symlink'; fi fi else echo 'MegaCli64 not installed.' fi"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /symlink exists/i) { logger($conf, $THIS_FILE, __LINE__, "- The 'MegaCli64' symlink already exists.", 1); } if ($line =~ /symlink created/i) { logger($conf, $THIS_FILE, __LINE__, "- The 'MegaCli64' symlink was created.", 1); } if ($line =~ /Failed to create/i) { logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - Failed to create the 'MegaCli64' symlink!", 1); } if ($line =~ /not installed/i) { logger($conf, $THIS_FILE, __LINE__, "- 'MegaCli64' not installed.", 1); } } close $file_handle; # Now storcli $shell_call = " if [ -e '".$conf->{executable}{storcli64}."' ]; then if [ -e '".$conf->{executable}{storcli64_link}."' ] then echo '".$conf->{executable}{storcli64_link}." symlink exists'; else ln -s ".$conf->{executable}{storcli64}." ".$conf->{executable}{storcli64_link}." if [ -e '".$conf->{executable}{storcli64_link}."' ] then echo '".$conf->{executable}{storcli64_link}." symlink created'; else echo 'Failed to create ".$conf->{executable}{storcli64_link}." symlink'; fi fi else echo 'storcli64 not installed.' fi"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /symlink exists/i) { logger($conf, $THIS_FILE, __LINE__, "- The 'storcli64' symlink already exists.", 1); } if ($line =~ /symlink created/i) { logger($conf, $THIS_FILE, __LINE__, "- The 'storcli64' symlink was created.", 1); } if ($line =~ /Failed to create/i) { logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - Failed to create the 'storcli64' symlink!", 1); } if ($line =~ /not installed/i) { logger($conf, $THIS_FILE, __LINE__, "- 'storcli64' not installed.", 1); } } close $file_handle; } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # This will compare the list of required packages against the list of installed # packages and, if any are missing, install them sub install_missing_packages { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Checking to see if any packages need to be installed.", 1); my $to_install = ""; foreach my $package (sort {$a cmp $b} keys %{$conf->{packages}{to_install}}) { # Watch for autovivication... if ((exists $conf->{packages}{installed}{$package}) && ($conf->{packages}{installed}{$package} == 1)) { $conf->{packages}{to_install}{$package} = 1; logger($conf, $THIS_FILE, __LINE__, "- Package: [$package] already installed.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Package: [$package] needs to be installed.", 1); $to_install .= "$package "; } } logger($conf, $THIS_FILE, __LINE__, "- Done\n", 1); if ($to_install) { # Install the packages, then verify they actually installed. logger($conf, $THIS_FILE, __LINE__, "Installing missing packages now. Please be patient.", 1); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] Please be patient! It may appear that nothing is happening for a", 3); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] while. This is likely because of how buffering is handled. If you", 3); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] really think nothing is happening, please open a new terminal and", 3); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] look for activity using 'top' or 'ps aux'.", 3); my $shell_call = $conf->{executable}{yum}." ".$conf->{sys}{yum_switches}." install $to_install"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; while(<$file_handle>) { print $_; } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); # Verify that everything is installed. sleep 5; get_list_of_installed_packages($conf); logger($conf, $THIS_FILE, __LINE__, "Verifying that everything is installed now.", 1); my $missing = 0; foreach my $package (sort {$a cmp $b} keys %{$conf->{packages}{to_install}}) { # Watch for autovivication... if ((exists $conf->{packages}{installed}{$package}) && ($conf->{packages}{installed}{$package} == 1)) { $conf->{packages}{to_install}{$package} = 1; logger($conf, $THIS_FILE, __LINE__, "- Package: [$package] installed.", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Package: [$package] failed to install!", 0); $missing = 1; } } # If anything is missing, fail. if ($missing) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Not all required packages installed properly.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] I am sorry, but this is a fatal error.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] If you are on RHEL, is this machine registered?", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] If there was a problem accessing the repos, please check the", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] network connection and try again.\n", 0); exit(12); } else { logger($conf, $THIS_FILE, __LINE__, "- All required packages are now installed.\n", 1); } } return(0); } # This calls the actual yum update. sub update_os { my ($conf) = @_; # As per https://access.redhat.com/solutions/3573891, occassionally yum update will fail unless this directory is created. if (not -e "/var/lib/rpm-state") { logger($conf, $THIS_FILE, __LINE__, "- Creating '/var/lib/rpm-state' to address 'https://access.redhat.com/solutions/3573891'.", 1); mkdir "/var/lib/rpm-state" or warn "$THIS_FILE ".__LINE__."; Failed to create the directory: [/var/lib/rpm-state], error was: $!\n"; } logger($conf, $THIS_FILE, __LINE__, "- [ Note ] Please be patient! It may appear that nothing is happening for a", 3); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] while. This is likely because of how buffering is handled. If you", 3); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] really think nothing is happening, please open a new terminal and", 3); logger($conf, $THIS_FILE, __LINE__, "- [ Note ] look for activity using 'top' or 'ps aux'.", 3); my $shell_call = $conf->{executable}{yum}." clean all && ".$conf->{executable}{yum}." ".$conf->{sys}{yum_switches}." update"; open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); while(<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "$line", 1); if ($line =~ /Installing : kernel/) { $conf->{sys}{reboot_needed} = 1; } } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); return(0); } # This gets a list of installed packages sub get_list_of_installed_packages { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Getting a list of currently installed packages.", 1); my $shell_call = $conf->{executable}{yum}." list installed"; 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 = $_; next if $line =~ /^Loaded plugins/; next if $line =~ /^Loading mirror/; next if $line =~ /^Installed Packages/; next if $line =~ /^\s/; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /^(.*?)\.(.*?)\s+(.*?)\s+\@/) { my $package = $1; my $arch = $2; my $version = $3; # Some packages are defined with the arch to ensure # other versions than the active arch of libraries are # installed. To be sure we see that they're installed, # we record the package with arch as '1'. my $package_with_arch = "$package.$arch"; # NOTE: Someday record the version. $conf->{packages}{installed}{$package} = 1; $conf->{packages}{installed}{$package_with_arch} = 1; logger($conf, $THIS_FILE, __LINE__, "a) Package: [$package], arch: [$arch], version: [$version]", 3); } elsif ($line =~ /^(.*?)\.(.*?)\s+(.*)/) { my $package = $1; my $arch = $2; my $version = $3; # Some packages are defined with the arch to ensure # other versions than the active arch of libraries are # installed. To be sure we see that they're installed, # we record the package with arch as '1'. my $package_with_arch = "$package.$arch"; # NOTE: Someday record the version. $conf->{packages}{installed}{$package} = 1; $conf->{packages}{installed}{$package_with_arch} = 1; logger($conf, $THIS_FILE, __LINE__, "b) Package: [$package], arch: [$arch], version: [$version]", 3); } elsif ($line =~ /^(.*?)\.(\S*)$/) { my $package = $1; my $arch = $2; # Some packages are defined with the arch to ensure # other versions than the active arch of libraries are # installed. To be sure we see that they're installed, # we record the package with arch as '1'. my $package_with_arch = "$package.$arch"; $conf->{packages}{installed}{$package} = 1; $conf->{packages}{installed}{$package_with_arch} = 1; logger($conf, $THIS_FILE, __LINE__, "c) Package: [$package], arch: [$arch]", 3); } } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "Done!\n", 1); return(0); } # This looks at all NICs and 'ifup's any that are down. sub start_all_nics { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Making sure all network interfaces are up.", 1); check_nic_states($conf); # If 'check_nic_state' found a bond, we're out. if ($conf->{sys}{bonds_exist}) { logger($conf, $THIS_FILE, __LINE__, "- Done.\n", 1); return (0); } foreach my $this_iface (sort {$a cmp $b} keys %{$conf->{nic}{by_name}}) { # See if it is up already. my $link_state = $conf->{nic}{by_name}{$this_iface}{link_state}; my $mac = $conf->{nic}{by_name}{$this_iface}{mac}; next if $link_state != 2; # If I am here, the interface is down my $nic = $this_iface; my $nic_file = $conf->{path}{network_configs}."/ifcfg-$nic"; logger($conf, $THIS_FILE, __LINE__, "- The network interface: [$nic] is down. It must be started for the next stage.", 1); logger($conf, $THIS_FILE, __LINE__, "- Checking if: [$nic_file] exists.", 1); if (not -e $nic_file) { logger($conf, $THIS_FILE, __LINE__, "- New device, writing a skeleton config file... ", 1); my $shell_call = $nic_file; open (my $file_handle, '>', "$shell_call") or die "\nFailed to write: [$shell_call], error was: $!\n"; print $file_handle "# Generated by: [$THIS_FILE] on: [".get_date($conf)."].\n"; print $file_handle "# Temporary configuration file for discovered NIC.\n"; print $file_handle "HWADDR=\"$mac\"\n" if $mac; print $file_handle "DEVICE=\"$nic\"\n"; print $file_handle "BOOTPROTO=\"none\"\n"; print $file_handle "NM_CONTROLLED=\"no\"\n"; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Done.", 1); } else { # It exists, edit it if needed. logger($conf, $THIS_FILE, __LINE__, "- Config file exists, changing BOOTPROTO to 'none'.", 1); my $shell_call = $conf->{executable}{sed}." -i 's/BOOTPROTO=.*/BOOTPROTO=\"none\"/\' $nic_file"; logger($conf, $THIS_FILE, __LINE__, "Calling: [$shell_call]", 3); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; while(<$file_handle>) { # TODO: Should this cause the installer to bail? # This should not return anything, but just in case... chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - Unexpected output: [$line]", 1); } close $file_handle; } # Now start the nic. logger($conf, $THIS_FILE, __LINE__, "- Attempting to bring up: [$nic]...", 1); my $shell_call = $conf->{executable}{ifup}." $nic"; 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 = $_; next if not $line; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 1); } close $file_handle; # See that it came up. logger($conf, $THIS_FILE, __LINE__, "- Checking to see if it is up now.", 1); sleep 2; my $is_up = 0; $shell_call = $conf->{executable}{ip}." link show $nic"; open ($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 = $_; if ($line =~ /UP/) { $is_up = 1; last; } } close $file_handle; if ($is_up) { logger($conf, $THIS_FILE, __LINE__, "- The interface: [$nic] is now up!", 1); } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Unable to bring up the interface: [$nic]!", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Please bring it up manually and try again.", 0); exit(11); } } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # This is a wrapper function that calls the various task-specific functions # that make the actual changes to the network. sub reconfigure_network { my ($conf) = @_; # Write the udev file, if bonds don't already exist. if (not $conf->{sys}{bonds_exist}) { write_udev_persistent_net($conf); } # Update/edit the hosts file. update_hosts($conf); # Remove existing ifcfg-* files and then write the new ones. update_network_config_files($conf); return(0); } # Update the hosts file. sub update_hosts { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "- Updating: [".$conf->{path}{hosts}."]", 1); # See if the user passed the peer dashboard info. my $peer_short_name = ""; my $peer_hostname = ""; my $peer_bcn_ip = ""; logger($conf, $THIS_FILE, __LINE__, "switches::peer-dashboard: [".$conf->{switches}{'peer-dashboard'}."]", 3); if ($conf->{switches}{'peer-dashboard'}) { $peer_hostname = ($conf->{switches}{'peer-dashboard'} =~ /hostname=(.*)$/)[0]; $peer_hostname =~ s/,bcn_ip.*//; $peer_bcn_ip = ($conf->{switches}{'peer-dashboard'} =~ /bcn_ip=(.*)$/)[0]; $peer_bcn_ip =~ s/,hostname.*//; if ($peer_hostname) { $peer_short_name = ($peer_hostname =~ /^(.*?)\./)[0]; } logger($conf, $THIS_FILE, __LINE__, "peer_hostname: [$peer_hostname], peer_bcn_ip: [$peer_bcn_ip], peer_short_name: [$peer_short_name]", 3); } # Read in the hosts file and look for previous entries with our hostname. my $notice_added = 0; my $new_hosts = ""; my $hostname = $conf->{sys}{hostname}; my $short_name = ($hostname =~ /^(.*?)\./)[0]; $short_name = $hostname if not $short_name; open (my $file_handle, "<", $conf->{path}{hosts}) or die "$THIS_FILE ".__LINE__."; Failed to read: [".$conf->{path}{hosts}."], the error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; next if $line =~ /$hostname/; next if $line =~ /$short_name/; if ($line =~ /^# NOTE: /) { $notice_added = 1; } if ($peer_hostname) { next if $line =~ /$peer_hostname/; } $new_hosts .= "$line\n"; } close $file_handle; # Warn the user that the hosts file is not the primary place that host names are resolved from. my $updated = 0; if (not $notice_added) { $new_hosts .= "# NOTE: Striker uses internal name resolution primarily and falls back to\n"; $new_hosts .= "# system-based name resolution only when internal resolution is not\n"; $new_hosts .= "# possible.\n"; $updated = 1; } # Append our hostname if we have either the BCN or IFN IP. if ($conf->{network}{bcn}{ip}) { logger($conf, $THIS_FILE, __LINE__, "- Using the BCN IP for the hostname: [$hostname]", 1); $new_hosts .= $conf->{network}{bcn}{ip}."\t$hostname $short_name ${short_name}.bcn\n"; $updated = 1; } if ($conf->{network}{ifn}{ip}) { logger($conf, $THIS_FILE, __LINE__, "- Using the IFN IP for the hostname: [$hostname]", 1); $new_hosts .= $conf->{network}{ifn}{ip}."\t${short_name}.ifn\n"; $updated = 1; } # See if I need to inject the peer's details. logger($conf, $THIS_FILE, __LINE__, "peer_hostname: [$peer_hostname], peer_bcn_ip: [$peer_bcn_ip]", 3); if (($peer_hostname) && ($peer_bcn_ip)) { logger($conf, $THIS_FILE, __LINE__, "- Adding peer dashboard's hostname: [$peer_hostname]", 1); my $new_line = "$peer_bcn_ip\t$peer_hostname\n"; logger($conf, $THIS_FILE, __LINE__, "peer_short_name: [$peer_short_name]", 3); if ($peer_short_name) { $new_line = "$peer_bcn_ip\t$peer_hostname $peer_short_name ${peer_short_name}.bcn\n"; } logger($conf, $THIS_FILE, __LINE__, "new_line: [$new_line]", 3); $new_hosts .= $new_line; $updated = 1; } if ($updated) { # Write out the updated hosts file. logger($conf, $THIS_FILE, __LINE__, "- Writing out the updated hosts file.", 1); open (my $file_handle, ">", $conf->{path}{hosts}) or die "Failed to write to: [".$conf->{path}{hosts}."], error was: $!\n"; print $file_handle $new_hosts; close $file_handle; } logger($conf, $THIS_FILE, __LINE__, "- Done\n", 1); return(0); } # This will remove all 'ifcfg-*' (save for 'ifcfg-lo') files and the writes # the new config files. sub update_network_config_files { my ($conf) = @_; # First up, delete the old files. logger($conf, $THIS_FILE, __LINE__, "Reconfiguting network.", 1); logger($conf, $THIS_FILE, __LINE__, "- Deleting old network configuration files:", 1); $conf->{path}{network_configs} =~ s/\/$//g; local(*DIRECTORY); opendir(DIRECTORY, $conf->{path}{network_configs}); while(my $file = readdir(DIRECTORY)) { next if $file eq "."; next if $file eq ".."; next if $file eq "ifcfg-lo"; if ($file =~ /^ifcfg-/) { my $full_path = $conf->{path}{network_configs}."/$file"; logger($conf, $THIS_FILE, __LINE__, "- Deleting file: [$full_path]", 1); unlink $full_path or die "Unable to delete: [$full_path]. The error was: $!\n"; } } closedir(DIRECTORY); logger($conf, $THIS_FILE, __LINE__, "- Writing new network configuration files.", 1); # The 'ethtool' options can include variables, so we'll need to escape '$' if found. $conf->{switches}{ethtool_opts} =~ s/\$/\\\$/g if $conf->{switches}{ethtool_opts}; ### Now write out the config files. # How I proceed depends on whether I am creating bonds or not. if ($conf->{sys}{create_bonds}) { my $ifcfg_bcn_bond1_file = $conf->{path}{network_configs}."/ifcfg-bcn_bond1"; my $ifcfg_bcn_link1_file = $conf->{path}{network_configs}."/ifcfg-bcn_link1"; my $ifcfg_bcn_link2_file = $conf->{path}{network_configs}."/ifcfg-bcn_link2"; my $ifcfg_ifn_bond1_file = $conf->{path}{network_configs}."/ifcfg-ifn_bond1"; my $ifcfg_ifn_link1_file = $conf->{path}{network_configs}."/ifcfg-ifn_link1"; my $ifcfg_ifn_link2_file = $conf->{path}{network_configs}."/ifcfg-ifn_link2"; my $ifcfg_bcn_link1 = "# Generated by: [$THIS_FILE] on: [".get_date($conf)."].\n"; $ifcfg_bcn_link1 .= "# Back-Channel Network - Link 1\n"; $ifcfg_bcn_link1 .= "DEVICE=\"bcn_link1\"\n"; $ifcfg_bcn_link1 .= "MTU=\"".$conf->{switches}{mtu}."\n" if $conf->{switches}{mtu} ne "1500"; $ifcfg_bcn_link1 .= "NM_CONTROLLED=\"no\"\n"; $ifcfg_bcn_link1 .= "BOOTPROTO=\"none\"\n"; $ifcfg_bcn_link1 .= "ONBOOT=\"yes\"\n"; $ifcfg_bcn_link1 .= "SLAVE=\"yes\"\n"; $ifcfg_bcn_link1 .= "MASTER=\"bcn_bond1\""; if ($conf->{switches}{ethtool_opts}) { $ifcfg_bcn_link1 .= "\nETHTOOL_OPTS=\"".$conf->{switches}{ethtool_opts}."\""; } #$conf->{path}{nodes}{bcn_link2_config}; my $ifcfg_bcn_link2 = "# Generated by: [$THIS_FILE] on: [".get_date($conf)."].\n"; $ifcfg_bcn_link2 .= "# Back-Channel Network - Link 2\n"; $ifcfg_bcn_link2 .= "DEVICE=\"bcn_link2\"\n"; $ifcfg_bcn_link2 .= "MTU=\"".$conf->{switches}{mtu}."\"\n" if $conf->{switches}{mtu} ne "1500"; $ifcfg_bcn_link2 .= "NM_CONTROLLED=\"no\"\n"; $ifcfg_bcn_link2 .= "BOOTPROTO=\"none\"\n"; $ifcfg_bcn_link2 .= "ONBOOT=\"yes\"\n"; $ifcfg_bcn_link2 .= "SLAVE=\"yes\"\n"; $ifcfg_bcn_link2 .= "MASTER=\"bcn_bond1\""; if ($conf->{switches}{ethtool_opts}) { $ifcfg_bcn_link2 .= "\nETHTOOL_OPTS=\"".$conf->{switches}{ethtool_opts}."\""; } #$conf->{path}{nodes}{bcn_bond1_config}; my $ifcfg_bcn_bond1 = "# Generated by: [$THIS_FILE] on: [".get_date($conf)."].\n"; $ifcfg_bcn_bond1 .= "# Back-Channel Network - Bond 1\n"; $ifcfg_bcn_bond1 .= "DEVICE=\"bcn_bond1\"\n"; $ifcfg_bcn_bond1 .= "MTU=\"".$conf->{switches}{mtu}."\"\n" if $conf->{switches}{mtu} ne "1500"; $ifcfg_bcn_bond1 .= "BOOTPROTO=\"static\"\n"; $ifcfg_bcn_bond1 .= "ONBOOT=\"yes\"\n"; $ifcfg_bcn_bond1 .= "BONDING_OPTS=\"mode=1 miimon=100 use_carrier=1 updelay=120000 downdelay=0 primary=bcn_link1 primary_reselect=always\"\n"; $ifcfg_bcn_bond1 .= "IPADDR=\"".$conf->{network}{bcn}{ip}."\"\n"; $ifcfg_bcn_bond1 .= "NETMASK=\"".$conf->{network}{bcn}{netmask}."\"\n"; $ifcfg_bcn_bond1 .= "DEFROUTE=\"no\""; ### Setup my IFN files. # IFN Link 1 my $ifcfg_ifn_link1 = "# Generated by: [$THIS_FILE] on: [".get_date($conf)."].\n"; $ifcfg_ifn_link1 .= "# Internet-Facing Network - Link 1\n"; $ifcfg_ifn_link1 .= "DEVICE=\"ifn_link1\"\n"; $ifcfg_ifn_link1 .= "MTU=\"".$conf->{switches}{mtu}."\"\n" if $conf->{switches}{mtu} ne "1500"; $ifcfg_ifn_link1 .= "NM_CONTROLLED=\"no\"\n"; $ifcfg_ifn_link1 .= "BOOTPROTO=\"none\"\n"; $ifcfg_ifn_link1 .= "ONBOOT=\"yes\"\n"; $ifcfg_ifn_link1 .= "SLAVE=\"yes\"\n"; $ifcfg_ifn_link1 .= "MASTER=\"ifn_bond1\""; if ($conf->{switches}{ethtool_opts}) { $ifcfg_ifn_link1 .= "\nETHTOOL_OPTS=\"".$conf->{switches}{ethtool_opts}."\""; } # IFN Link 2 my $ifcfg_ifn_link2 = "# Generated by: [$THIS_FILE] on: [".get_date($conf)."].\n"; $ifcfg_ifn_link2 .= "# Internet-Facing Network - Link 2\n"; $ifcfg_ifn_link2 .= "DEVICE=\"ifn_link2\"\n"; $ifcfg_ifn_link2 .= "MTU=\"".$conf->{switches}{mtu}."\"\n" if $conf->{switches}{mtu} ne "1500"; $ifcfg_ifn_link2 .= "NM_CONTROLLED=\"no\"\n"; $ifcfg_ifn_link2 .= "BOOTPROTO=\"none\"\n"; $ifcfg_ifn_link2 .= "ONBOOT=\"yes\"\n"; $ifcfg_ifn_link2 .= "SLAVE=\"yes\"\n"; $ifcfg_ifn_link2 .= "MASTER=\"ifn_bond1\""; if ($conf->{switches}{ethtool_opts}) { $ifcfg_ifn_link2 .= "\nETHTOOL_OPTS=\"".$conf->{switches}{ethtool_opts}."\""; } # IFN Bond 1 my $ifcfg_ifn_bond1 = "# Generated by: [$THIS_FILE] on: [".get_date($conf)."].\n"; $ifcfg_ifn_bond1 .= "# Internet-Facing Network - Bond 1\n"; $ifcfg_ifn_bond1 .= "DEVICE=\"ifn_bond1\"\n"; $ifcfg_ifn_bond1 .= "MTU=\"".$conf->{switches}{mtu}."\"\n" if $conf->{switches}{mtu} ne "1500"; $ifcfg_ifn_bond1 .= "BOOTPROTO=\"static\"\n"; $ifcfg_ifn_bond1 .= "ONBOOT=\"yes\"\n"; $ifcfg_ifn_bond1 .= "BONDING_OPTS=\"mode=1 miimon=100 use_carrier=1 updelay=120000 downdelay=0 primary=ifn_link1 primary_reselect=always\"\n"; $ifcfg_ifn_bond1 .= "IPADDR=\"".$conf->{network}{ifn}{ip}."\"\n"; $ifcfg_ifn_bond1 .= "NETMASK=\"".$conf->{network}{ifn}{netmask}."\"\n"; $ifcfg_ifn_bond1 .= "GATEWAY=\"".$conf->{network}{ifn}{gateway}."\"\n"; $ifcfg_ifn_bond1 .= "DNS1=\"".$conf->{network}{ifn}{dns1}."\"\n" if $conf->{network}{ifn}{dns1}; $ifcfg_ifn_bond1 .= "DNS2=\"".$conf->{network}{ifn}{dns2}."\"\n" if $conf->{network}{ifn}{dns2}; $ifcfg_ifn_bond1 .= "DEFROUTE=\"yes\""; ### Don't die here! Only warn so hopefully at least one link ### will be up in an oops. ## Start writing! # BCN Bond 1 open (my $file_handle, '>', "$ifcfg_bcn_bond1_file") or warn "Failed to write: [$ifcfg_bcn_bond1_file], error was: $!\n"; print $file_handle $ifcfg_bcn_bond1; close $file_handle; # BCN Link 1 open ($file_handle, '>', "$ifcfg_bcn_link1_file") or warn "Failed to write: [$ifcfg_bcn_link1_file], error was: $!\n"; print $file_handle $ifcfg_bcn_link1; close $file_handle; # BCN Link 2 open ($file_handle, '>', "$ifcfg_bcn_link2_file") or warn "Failed to write: [$ifcfg_bcn_link2_file], error was: $!\n"; print $file_handle $ifcfg_bcn_link2; close $file_handle; # IFN Bond 1 open ($file_handle, '>', "$ifcfg_ifn_bond1_file") or warn "Failed to write: [$ifcfg_ifn_bond1_file], error was: $!\n"; print $file_handle $ifcfg_ifn_bond1; close $file_handle; # IFN Link 1 open ($file_handle, '>', "$ifcfg_ifn_link1_file") or warn "Failed to write: [$ifcfg_ifn_link1_file], error was: $!\n"; print $file_handle $ifcfg_ifn_link1; close $file_handle; # IFN Link 2 open ($file_handle, '>', "$ifcfg_ifn_link2_file") or warn "Failed to write: [$ifcfg_ifn_link2_file], error was: $!\n"; print $file_handle $ifcfg_ifn_link2; close $file_handle; } else { # BCN first my $bcn_link1_file = "".$conf->{path}{network_configs}."/ifcfg-bcn_link1"; logger($conf, $THIS_FILE, __LINE__, "- BCN Link 1: [$bcn_link1_file]", 1); my $shell_call = $bcn_link1_file; open (my $file_handle, '>', "$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; print $file_handle "# Generated by: [$THIS_FILE] on: [".get_date($conf)."].\n"; print $file_handle "# Back-Channel Network - Link 1\n"; print $file_handle "DEVICE=\"bcn_link1\"\n"; if ($conf->{switches}{mtu} ne "1500") { print $file_handle "MTU=\"".$conf->{switches}{mtu}."\"\n"; } print $file_handle "BOOTPROTO=\"static\"\n"; print $file_handle "NM_CONTROLLED=\"no\"\n"; print $file_handle "IPADDR=\"".$conf->{network}{bcn}{ip}."\"\n"; print $file_handle "NETMASK=\"".$conf->{network}{bcn}{netmask}."\"\n"; print $file_handle "DEFROUTE=\"no\"\n"; if ($conf->{switches}{ethtool_opts}) { print $file_handle "\nETHTOOL_OPTS=\"".$conf->{switches}{ethtool_opts}."\""; } close $file_handle; # Now the IFN my $ifn_link1_file = $conf->{path}{network_configs}."/ifcfg-ifn_link1"; logger($conf, $THIS_FILE, __LINE__, "- IFN Link 1: [$ifn_link1_file]", 1); $shell_call = $ifn_link1_file; open ($file_handle, '>', "$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; print $file_handle "# Generated by: [$THIS_FILE] on: [".get_date($conf)."].\n"; print $file_handle "# Internet-Facing Network - Link 1\n"; print $file_handle "DEVICE=\"ifn_link1\"\n"; if ($conf->{switches}{mtu} ne "1500") { print $file_handle "MTU=\"".$conf->{switches}{mtu}."\"\n"; } print $file_handle "BOOTPROTO=\"static\"\n"; print $file_handle "NM_CONTROLLED=\"no\"\n"; print $file_handle "IPADDR=\"".$conf->{network}{ifn}{ip}."\"\n"; print $file_handle "NETMASK=\"".$conf->{network}{ifn}{netmask}."\"\n"; print $file_handle "GATEWAY=\"".$conf->{network}{ifn}{gateway}."\"\n"; print $file_handle "DNS1=\"".$conf->{network}{ifn}{dns1}."\"\n"; print $file_handle "DNS2=\"".$conf->{network}{ifn}{dns2}."\"\n"; print $file_handle "DEFROUTE=\"yes\"\n"; if ($conf->{switches}{ethtool_opts}) { print $file_handle "\nETHTOOL_OPTS=\"".$conf->{switches}{ethtool_opts}."\""; } close $file_handle; } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); # Reboot to make sure changes take effect. $conf->{sys}{reboot_needed} = 1; return(0); } # This returns the current date and time in 'YYYY/MM/DD HH:MM:SS' format. It # always uses 24-hour time and it zero-pads single digits. sub get_date { my ($conf) = @_; my $date = ""; # This doesn't support offsets or other advanced features. my %time; ($time{sec}, $time{min}, $time{hour}, $time{mday}, $time{mon}, $time{year}, $time{wday}, $time{yday}, $time{isdst}) = localtime(); # Increment the month by one. $time{mon}++; # 24h time. $time{pad_hour} = sprintf("%02d", $time{hour}); $time{pad_min} = sprintf("%02d", $time{min}); $time{pad_sec} = sprintf("%02d", $time{sec}); $time{year} = ($time{year} + 1900); $time{pad_mon} = sprintf("%02d", $time{mon}); $time{pad_mday} = sprintf("%02d", $time{mday}); $time{mon}++; $date = "$time{year}/$time{pad_mon}/$time{pad_mday} $time{pad_hour}:$time{pad_min}:$time{pad_sec}"; return($date); } # This writes out the udev 70-persistent-net.rules file. sub write_udev_persistent_net { my ($conf) = @_; my $shell_call = $conf->{path}{udev_persistent_net}; logger($conf, $THIS_FILE, __LINE__, "Writing the new udev rules file: [".$conf->{path}{udev_persistent_net}."]", 1); open (my $file_handle, ">$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; print $file_handle "# Generated by: [$THIS_FILE] on: [".get_date($conf)."].\n"; foreach my $nic (@{$conf->{nics}}) { my $say_nic = $nic; if ($nic =~ /^bcn_link(\d)/) { $say_nic = "Back-Channel Network, Link $1" } elsif ($nic =~ /^ifn_link(\d)/) { $say_nic = "Internet-Facing Network, Link $1" } my $this_mac = $conf->{nic}{name}{$nic}; if (not $this_mac) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - There seems to be a problem.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Desired NIC: [$nic] doesn't have a referenced MAC address!", 0); exit(1); } print $file_handle "# $say_nic\n"; print $file_handle "SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"?*\", ATTR{address}==\"$this_mac\", NAME=\"$nic\"\n\n"; } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # This backs up the udev (if it exists) and network files before making any actual changes to the system. sub backup_original_files { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Backing up some network related system files.", 1); if (not $conf->{path}{backups}) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The backup directory is not defined!", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Unable to safely proceed, exiting.", 0); exit(5); } elsif (not -e $conf->{path}{backups}) { logger($conf, $THIS_FILE, __LINE__, "- The backup directory: [".$conf->{path}{backups}."] doesn't exist, creting it.", 1); mkdir $conf->{path}{backups} or die warn "[ Warning ] - Failed to create the directory: [".$conf->{path}{backups}."].\n[ Warning ] - The error was: $!\n"; if (not -e $conf->{path}{backups}) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The backup directory was not created.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Unable to safely proceed, exiting.", 0); exit(6); } else { logger($conf, $THIS_FILE, __LINE__, "- Backup directory successfully created.", 1); } } # If there is already a backup created, skip it as we're probably being # run a second (of thirteenth) time. logger($conf, $THIS_FILE, __LINE__, "- Backing up: [".$conf->{path}{udev_persistent_net}."]", 1); if (-e $conf->{path}{backups}."/70-persistent-net.rules") { logger($conf, $THIS_FILE, __LINE__, "- Previous backup exists, skipping.", 1); } else { # Backup '70-persistent-net.rules' if it exists. if (-e $conf->{path}{udev_persistent_net}) { logger($conf, $THIS_FILE, __LINE__, "- It exists, backing it up.", 1); backup_file($conf, $conf->{path}{udev_persistent_net}); } else { logger($conf, $THIS_FILE, __LINE__, "- Doesn't exist, skipping.", 1); } } # Backup the '/etc/sysconfig/network-scripts' directory. logger($conf, $THIS_FILE, __LINE__, "- Backing up: [".$conf->{path}{network_configs}."]", 1); if (-e $conf->{path}{backups}."/network-scripts") { logger($conf, $THIS_FILE, __LINE__, "- Previous backup exists, skipping.", 1); } else { if (-e $conf->{path}{network_configs}) { # No need to say it exists backup_file($conf, $conf->{path}{network_configs}); } else { # Ok, the user has bigger problems logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The network configuration directory was not found!", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] This should not be possible.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Please check the internal path::network_configs value.", 0); exit(8); } } # Backup rc.local logger($conf, $THIS_FILE, __LINE__, "- Backing up: [".$conf->{path}{rc_local}."]", 1); if (-e $conf->{path}{backups}."/rc.local") { logger($conf, $THIS_FILE, __LINE__, "- Previous backup exists, skipping.", 1); } else { # Backup 'rc.local'. if (-e $conf->{path}{rc_local}) { logger($conf, $THIS_FILE, __LINE__, "- It exists, backing it up.", 1); backup_file($conf, $conf->{path}{rc_local}); } else { logger($conf, $THIS_FILE, __LINE__, "- Doesn't exist, skipping.", 1); } } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return(0); } # This backups up a file (or directory) to the backups directory. It is # expected that the existance of the source and backup directories are done. sub backup_file { my ($conf, $file) = @_; if (not $file) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The backup function was called, but no source file was given.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] This is likely a program error, exiting.", 0); exit(7); } # I want the source, if it is a directory, to not have a trailing # slash. Conversly, I want the backup directory to have a trailing # slash, if it doesn't have it already. $file =~ s/\/$//g; $conf->{path}{backups} .= "/" if $conf->{path}{backups} !~ /\/$/; my $shell_call = $conf->{executable}{cp}." -ab $file ".$conf->{path}{backups}; logger($conf, $THIS_FILE, __LINE__, "- Copying: [$file] to: [".$conf->{path}{backups}."]", 1); 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Shell output: [$line]", 1); } close $file_handle; ### TODO: Verify the backed up file exists in the backup directory. logger($conf, $THIS_FILE, __LINE__, "- Done", 3); return(0); } # This looks to see what the current BCN and IFN IPs are. sub get_existing_ips { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "- Reading existing config:", 1); my $ok = 1; my $in_iface = ""; my $shell_call = $conf->{executable}{ip}." addr show"; 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 = $_; if ($line =~ /^\d+: (.*?): <.*?> mtu/) { $in_iface = $1; } if (($in_iface) && ($line =~ /inet (\d+\.\d+\.\d+\.\d+)\/(\d+) /)) { $conf->{network}{raw}{$in_iface}{ip} = $1; $conf->{network}{raw}{$in_iface}{netmask} = convert_cidr_to_dotted_decimal($conf, $2); logger($conf, $THIS_FILE, __LINE__, "in_iface: [$in_iface], IP: [".$conf->{network}{raw}{$in_iface}{ip}."], netmask: [".$conf->{network}{raw}{$in_iface}{netmask}."]", 3); } } close $file_handle; # See if the user has a bond for BCN if ($conf->{network}{raw}{bcn_bond1}{ip}) { # Yup! $conf->{network}{bcn}{ip} = $conf->{network}{raw}{bcn_bond1}{ip}; $conf->{network}{bcn}{netmask} = $conf->{network}{raw}{bcn_bond1}{netmask}; $conf->{network}{bcn}{interface} = "bcn_bond1"; logger($conf, $THIS_FILE, __LINE__, "- BCN IP found on 'bcn_bond1': [".$conf->{network}{bcn}{ip}."/".$conf->{network}{bcn}{netmask}."]", 1); } elsif ($conf->{network}{raw}{bcn_link1}{ip}) { # Nope, normal network. $conf->{network}{bcn}{ip} = $conf->{network}{raw}{bcn_link1}{ip}; $conf->{network}{bcn}{netmask} = $conf->{network}{raw}{bcn_link1}{netmask}; $conf->{network}{bcn}{interface} = "bcn_link1"; logger($conf, $THIS_FILE, __LINE__, "- BCN IP found on 'bcn_link1': [".$conf->{network}{bcn}{ip}."/".$conf->{network}{bcn}{netmask}."]", 1); } else { # Failed to find the BCN. logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Network configuration is disabled, but the BCN network's IP address", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] and network mask were not found. Is the device 'bcn_bond1' or 'bcn_link1'", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] configured with an IP address?", 0); $ok = 0; } # See if the user has a bond for IFN if ($conf->{network}{raw}{ifn_bond1}{ip}) { # Yup! $conf->{network}{ifn}{ip} = $conf->{network}{raw}{ifn_bond1}{ip}; $conf->{network}{ifn}{netmask} = $conf->{network}{raw}{ifn_bond1}{netmask}; $conf->{network}{ifn}{interface} = "ifn_bond1"; logger($conf, $THIS_FILE, __LINE__, "- IFN IP found on 'ifn_bond1': [".$conf->{network}{ifn}{ip}."/".$conf->{network}{ifn}{netmask}."]", 1); } elsif ($conf->{network}{raw}{ifn_link1}{ip}) { # Nope, normal network. $conf->{network}{ifn}{ip} = $conf->{network}{raw}{ifn_link1}{ip}; $conf->{network}{ifn}{netmask} = $conf->{network}{raw}{ifn_link1}{netmask}; $conf->{network}{ifn}{interface} = "ifn_link1"; logger($conf, $THIS_FILE, __LINE__, "- IFN IP found on 'ifn_link1': [".$conf->{network}{ifn}{ip}."/".$conf->{network}{ifn}{netmask}."]", 1); } else { # Failed to find the IFN. logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Network configuration is disabled, but the IFN network's IP address", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] and network mask were not found. Is the device 'ifn_bond1' or 'ifn_link1'", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] configured with an IP address?", 0); $ok = 0; } return($ok); } # This breaks out the command-line switches and varifies that their values are # (more or less) sane sub sanity_check_switches { my ($conf) = @_; # Something has been set for all values, sanity check them. # This will fail out if set to '1' once all sanity checking is done. my $bad_value = 0; # The network is a little tricker logger($conf, $THIS_FILE, __LINE__, "\nSanity-checking command line switches:", 1); if (($conf->{switches}{b}) && ($conf->{switches}{i})) { # Both defined, sanity check them. ($bad_value) = sanity_check_network($conf, $bad_value); } elsif ((not $conf->{switches}{b}) && (not $conf->{switches}{i})) { logger($conf, $THIS_FILE, __LINE__, "- No network details given. Skipping Network config!", 1); $conf->{sys}{skip_network} = 1; # Get the current IFN and BCN addresses. Returns '0' if the BCN # or IFN IPs are not found. $bad_value = 1 if not get_existing_ips($conf); } elsif (not $conf->{switches}{b}) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The IFN settings were given, but not the BCN. I am sorry but both", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] or neither network must be defined at this time.", 0); $bad_value = 1; } elsif (not $conf->{switches}{i}) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The BCN settings were given, but not the IFN. I am sorry but both", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] or neither network must be defined at this time.", 0); $bad_value = 1; } # Check the DHCP/PXE stuff if ($conf->{switches}{p}) { ($bad_value) = sanity_check_dhcp($conf, $bad_value); } elsif (($conf->{switches}{'centos-iso'}) or ($conf->{switches}{'rhel-iso'})) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The DHCP lease range must be defined for PXE-based installs to", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] work. Note that the DHCP server will *only* run when enabled via", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] the Striker dashboard. It will never start on boot.", 0); $bad_value = 1; } # Check the hostname if ($conf->{switches}{n}) { # Make sure the hostname is sane. $conf->{sys}{hostname} = $conf->{switches}{n}; ($bad_value) = sanity_check_hostname($conf, $conf->{switches}{n}, 0, $bad_value); } else { logger($conf, $THIS_FILE, __LINE__, "- No hostname given. Will not change it.", 1); $conf->{sys}{skip_hostname} = 1; } # See if they set an owner name if (not $conf->{switches}{c}) { logger($conf, $THIS_FILE, __LINE__, "- No customer/owner name given, will use: [".$conf->{sys}{customer}."]", 1); } else { $conf->{sys}{customer} = $conf->{switches}{c}; } # See if they've asked for a specific version. # if ((not $conf->{switches}{d}) or ($conf->{switches}{d} eq "latest")) # { # logger($conf, $THIS_FILE, __LINE__, "- No specific version requested, will install: [".$conf->{sys}{stable_version}."]", 1); # $conf->{sys}{install_version} = $conf->{sys}{stable_version}; # $conf->{url}{striker} =~ s/#!striker_version!#/$conf->{sys}{install_version}/; # $conf->{sys}{tarball_dir} =~ s/#!striker_version!#/$conf->{sys}{install_version}/; # # # If we can access the web, try to get the version from there. # get_latest_version($conf); # } # else # { # # Make sure the request is sane. First check for a supported # # version: # $conf->{sys}{install_version} = $conf->{switches}{d}; # $conf->{url}{striker} =~ s/#!striker_version!#/$conf->{sys}{install_version}/; # $conf->{sys}{tarball_dir} =~ s/#!striker_version!#/$conf->{sys}{install_version}/; # if ($conf->{switches}{d} eq "git") # { # logger($conf, $THIS_FILE, __LINE__, "- [ Note ] - Will install the latest version from git.", 1); # logger($conf, $THIS_FILE, __LINE__, "- [ Note ] - Please be cautious about using the git version in production.", 1); # #sleep 5; # } # } # Check to see if either the htpasswd file exists or the user is giving us a user now. if ((not -e $conf->{path}{apache_htpasswd}) && (not $conf->{switches}{u})) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - No apache credentials were set, and: [".$conf->{path}{apache_htpasswd}."]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] doesn't exist. This is required for Striker to work securely.", 0); $bad_value = 1; } elsif ($conf->{switches}{u}) { if ($conf->{switches}{u} =~ /^(.*?):(.*)$/) { $conf->{sys}{striker_user} = $1; $conf->{sys}{striker_password} = $2; } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The Striker user name and password were not specified correctly.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Please use '-u \"\".", 0); $bad_value = 1; } } # Make sure the --rhn' switch parses, if set. if ($conf->{switches}{rhn}) { if ($conf->{switches}{rhn} =~ /^(.*?):(.*)$/) { $conf->{sys}{rhn_user} = $1; $conf->{sys}{rhn_password} = $2; } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The Red Hat Network user name and password were not specified", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] correctly. Please use '--rhn \"\".", 0); $bad_value = 1; } } ### Sanity check IPMI if set. # Credentials if ($conf->{switches}{'ipmi-user'}) { if ($conf->{switches}{'ipmi-user'} =~ /^(.*?):(.*)$/) { $conf->{sys}{ipmi_user} = $1; $conf->{sys}{ipmi_password} = $2; } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The IPMI BMC user name and password were not specified correctly.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Please use '--ipmi-user \"\".", 0); $bad_value = 1; } } # IP address if ($conf->{switches}{'ipmi-ip'}) { # Did the user specify a gateway? if ($conf->{switches}{'ipmi-ip'} =~ /^(.*?)\/(.*?),dg=(.*)$/) { $conf->{sys}{ipmi_ip} = $1; $conf->{sys}{ipmi_netmask} = $2; $conf->{sys}{ipmi_gateway} = $3; } elsif ($conf->{switches}{'ipmi-ip'} =~ /^(.*?)\/(.*)$/) { $conf->{sys}{ipmi_ip} = $1; $conf->{sys}{ipmi_netmask} = $2; } else { # Unparsable logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The IPMI BMC user name and password were not specified correctly.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Please use '--ipmi-user \"\".", 0); $bad_value = 1; } ### Verify sanity # IP address $conf->{sys}{ipmi_ip} = validate_ip_address($conf, $conf->{sys}{ipmi_ip}); if ($conf->{sys}{ipmi_ip} eq "#!INVALID!#") { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The specified IPMI IP address: [".$conf->{sys}{ipmi_ip}."] is not valid.", 0); $bad_value = 1; } # Netmask if ($conf->{sys}{ipmi_netmask} =~ /^\d{1,2}$/) { # Convert from CIDR to dotted-decimal $conf->{sys}{ipmi_netmask} = convert_cidr_to_dotted_decimal($conf, $conf->{sys}{ipmi_netmask}); } else { $conf->{sys}{ipmi_netmask} = validate_ip_address($conf, $conf->{sys}{ipmi_netmask}); } if ($conf->{sys}{ipmi_netmask} eq "#!INVALID!#") { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The specified IPMI netmask value: [".$conf->{sys}{ipmi_netmask}."] is not valid.", 0); $bad_value = 1; } # If defined, validate the gateway if ($conf->{sys}{ipmi_gateway}) { $conf->{sys}{ipmi_gateway} = validate_ip_address($conf, $conf->{sys}{ipmi_gateway}); if ($conf->{sys}{ipmi_gateway} eq "#!INVALID!#") { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The specified IPMI gateway: [".$conf->{sys}{ipmi_gateway}."] is not valid.", 0); $bad_value = 1; } } } # If NTP servers are set, make sure the values look like IPs. if ($conf->{switches}{ntp}) { foreach my $ntp_server (split/,/, $conf->{switches}{ntp}) { next if not $ntp_server; if ((not is_string_ipv4($conf, $ntp_server)) && (not is_domain_name($conf, $ntp_server))) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The specified NTP server: [$ntp_server] is not valid.", 0); $bad_value = 1; } } } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); return($bad_value); } # This tries to determine the latest release by checking the alteeve website. sub get_latest_version { my ($conf) = @_; my $set = 0; my $shell_call = $conf->{executable}{curl}." --stderr /dev/null ".$conf->{url}{latest_release}; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 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 = $_; logger($conf, $THIS_FILE, __LINE__, "- Output: [$line]", 2); if ($line =~ /^striker:(http.*)$/) { $conf->{url}{striker} = $1; $set = 1; logger($conf, $THIS_FILE, __LINE__, "url::striker: [".$conf->{url}{striker}."]", 2); } } close $file_handle; logger($conf, $THIS_FILE, __LINE__, "set: [$set]", 2); if ($set) { if ($conf->{url}{striker} =~ /.*\/(.*)\.zip$/) { $conf->{sys}{install_version} = $1; logger($conf, $THIS_FILE, __LINE__, "sys::install_version: [".$conf->{sys}{install_version}."]", 2); } elsif ($conf->{url}{striker} =~ /.*\/(.*)\.tar.gz$/) { $conf->{sys}{install_version} = $1; logger($conf, $THIS_FILE, __LINE__, "sys::install_version: [".$conf->{sys}{install_version}."]", 2); } elsif ($conf->{url}{striker} =~ /.*\/(.*)\.tar.bz2$/) { $conf->{sys}{install_version} = $1; logger($conf, $THIS_FILE, __LINE__, "sys::install_version: [".$conf->{sys}{install_version}."]", 2); } $conf->{sys}{tarball_dir} = "striker-".$conf->{sys}{install_version}; logger($conf, $THIS_FILE, __LINE__, "sys::tarball_dir: [".$conf->{sys}{tarball_dir}."]", 2); } return(0); } # This checks the DHCP lease start and end range. sub sanity_check_dhcp { my ($conf, $bad_value) = @_; $conf->{switches}{lease_start} = ""; $conf->{switches}{lease_end} = ""; if ($conf->{switches}{p} =~ /(\d+\.\d+\.\d+\.\d+):(\d+\.\d+\.\d+\.\d+)/) { $conf->{switches}{lease_start} = $1; $conf->{switches}{lease_end} = $2; } if ((not $conf->{switches}{lease_start}) && (not $conf->{switches}{lease_end})) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - You need to specify the start and end IP lease range as", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] '-p :'", 0); $bad_value = 1; } elsif ((not $conf->{switches}{lease_start}) or (not $conf->{switches}{lease_end})) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - Both ':' are needed with the '-p' switch.", 0); $bad_value = 1; } # Lastly, make sure the start and end range are in the same subnet. ### TODO: Make this work across all subnet/CIDR ranges my $start_prefix = ""; my $end_prefix = ""; if ($conf->{network}{bcn}{netmask} eq "255.255.0.0") { $start_prefix = ($conf->{switches}{lease_start} =~ /^(\d+\.\d+\.)/)[0]; $end_prefix = ($conf->{switches}{lease_end} =~ /^(\d+\.\d+\.)/)[0]; } elsif ($conf->{network}{bcn}{netmask} eq "255.255.255.0") { $start_prefix = ($conf->{switches}{lease_start} =~ /^(\d+\.\d+\.\d+\.)/)[0]; $end_prefix = ($conf->{switches}{lease_end} =~ /^(\d+\.\d+\.\d+\.)/)[0]; } if ((not $start_prefix) && (not $end_prefix)) { ### TODO: This is hit when the BCN isn't specified. Move the ### function that reads the BCN config (if it exists) ### into a function and call it here. logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - Unable to check if the start and end IPs are in the same subnet.", 3); } elsif ($start_prefix ne $end_prefix) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The DHCP lease start: [".$conf->{switches}{lease_start}."] and end: [".$conf->{switches}{lease_end}."]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] IPs do not appear to be in the same subnet: [xxx.xxx.xxx.xxx].", 0); logger($conf, $THIS_FILE, __LINE__, "- Only the BCN subnet is used for DHCP.", 1); $bad_value = 1; } return($bad_value); } # This sanity-checks the host name. sub sanity_check_hostname { my ($conf, $hostname, $port_allowed, $bad_value) = @_; my $port; if ($hostname =~ /(.*?):(\d+)$/) { $hostname = $1; $port = $2; if (not $port_allowed) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - A port number was set for: [$hostname]", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] but it is not allowed here.", 0); $bad_value = 1; } elsif (($port < 1) or ($port > 65535)) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The port number: [$port] is out of range!", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Port numbers must be between 1 and 65535.", 0); $bad_value = 1; } } if ($hostname =~ /\.\./) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The hostname: [$hostname] has multiple periods!", 0); $bad_value = 1; } if ($hostname =~ /^\./) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The hostname: [$hostname] starts with a period!", 0); $bad_value = 1; } if ($hostname =~ /\.$/) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The hostname: [$hostname] ends with a period!", 0); $bad_value = 1; } # Now loop through the hostname's elements and make sure they're sane. foreach my $name (split/\./, $hostname) { if (not $name) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The hostname: [$hostname] appears to have an empty element!", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] This might be a program error.", 0); $bad_value = 1; } if (length($name) > 255) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The hostname: [$hostname]'s section: [$name] is too long!", 0); $bad_value = 1; } if ($name =~ /[^0-9a-zA-Z\-]/) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The hostname: [$hostname]'s section: [$name] contains an illegal character!", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] only alpha-numeric and the '-' character are allowed", 0); $bad_value = 1; } } return($bad_value); } # This sanity checks the network settings passed by the user. sub sanity_check_network { my ($conf, $bad_value) = @_; ### Now sanity check passed values # First up, the BCN if ($conf->{switches}{b} =~ /^(.*?)\/(.*)$/) { my $ip = $1; my $netmask = $2; ### Verify sanity # IP address $ip = validate_ip_address($conf, $ip); if ($ip eq "#!INVALID!#") { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The specified BCN IP address is not valid.", 0); $bad_value = 1; } else { $conf->{network}{bcn}{ip} = $ip; } # Netmask if ($netmask =~ /^\d{1,2}$/) { # Convert from CIDR to dotted-decimal $netmask = convert_cidr_to_dotted_decimal($conf, $netmask); } else { $netmask = validate_ip_address($conf, $netmask); } if ($netmask eq "#!INVALID!#") { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The specified BCN netmask value is not valid.", 0); $bad_value = 1; } else { $conf->{network}{bcn}{netmask} = $netmask; } } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The specified BCN IP address and netmask are malformed.", 0); $bad_value = 1; } # Next is the IFN, which is a little more complicated. if ($conf->{switches}{i} =~ /^(.*?)\/(.*)$/) { my $ip = $1; my $netmask = $2; my $gateway = ""; my $dns1 = ""; my $dns2 = ""; # The netmask should have ',dg=X' and possibly 'dns[12]=[YZ]', # so pull those out. logger($conf, $THIS_FILE, __LINE__, "$THIS_FILE ".__LINE__."; ip: [$ip], netmask: [$netmask]", 3); if ($netmask =~ /,dg=/) { # Good, strip it out. if ($netmask =~ /,dg=(.*?),/) { $gateway = $1; logger($conf, $THIS_FILE, __LINE__, "$THIS_FILE ".__LINE__."; gateway: [$gateway]", 3); } else { $gateway = ($netmask =~ /,dg=(.*)/)[0]; logger($conf, $THIS_FILE, __LINE__, "$THIS_FILE ".__LINE__."; gateway: [$gateway]", 3); } } # Now the nameservers. if ($netmask =~ /,dns1=/) { # Good, strip it out. if ($netmask =~ /,dns1=(.*?),/) { $dns1 = $1; logger($conf, $THIS_FILE, __LINE__, "$THIS_FILE ".__LINE__."; dns1: [$dns1]", 3); } else { $dns1 = ($netmask =~ /,dns1=(.*)/)[0]; logger($conf, $THIS_FILE, __LINE__, "$THIS_FILE ".__LINE__."; dns1: [$dns1]", 3); } } if ($netmask =~ /,dns2=/) { # Good, strip it out. if ($netmask =~ /,dns2=(.*?),/) { $dns2 = $1; logger($conf, $THIS_FILE, __LINE__, "$THIS_FILE ".__LINE__."; dns2: [$dns2]", 3); } else { $dns2 = ($netmask =~ /,dns2=(.*)/)[0]; logger($conf, $THIS_FILE, __LINE__, "$THIS_FILE ".__LINE__."; dns2: [$dns2]", 3); } } # Now delete everything from the netmask after the ',' $netmask =~ s/,.*$//; logger($conf, $THIS_FILE, __LINE__, "$THIS_FILE ".__LINE__."; netmask: [$netmask]", 3); ### Verify sanity # IP address $ip = validate_ip_address($conf, $ip); if ($ip eq "#!INVALID!#") { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The specified IFN IP address is not valid.", 0); $bad_value = 1; } else { $conf->{network}{ifn}{ip} = $ip; } # Netmask if ($netmask =~ /^\d{1,2}$/) { # Convert from CIDR to dotted-decimal $netmask = convert_cidr_to_dotted_decimal($conf, $netmask); } else { $netmask = validate_ip_address($conf, $netmask); } if ($netmask eq "#!INVALID!#") { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The specified IFN netmask value is not valid.", 0); $bad_value = 1; } else { $conf->{network}{ifn}{netmask} = $netmask; } # Gateway logger($conf, $THIS_FILE, __LINE__, "$THIS_FILE ".__LINE__."; gateway: [$gateway]", 3); if ($gateway) { $gateway = validate_ip_address($conf, $gateway); logger($conf, $THIS_FILE, __LINE__, "$THIS_FILE ".__LINE__."; gateway: [$gateway]", 3); if ($gateway eq "#!INVALID!#") { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The specified IFN gateway value is not valid.", 0); $bad_value = 1; } else { $conf->{network}{ifn}{gateway} = $gateway; logger($conf, $THIS_FILE, __LINE__, "$THIS_FILE ".__LINE__."; network::ifn::gateway: [".$conf->{network}{ifn}{gateway}."]", 3); } } else { # This is a warning, not an error. logger($conf, $THIS_FILE, __LINE__, "[ Warning ] - The IFN gateway is not specified. This means there will be no", 0); logger($conf, $THIS_FILE, __LINE__, "[ Warning ] access to *any* external networks.", 0); } # DNS 1 and 2, if defined. if ($dns1) { $dns1 = validate_ip_address($conf, $dns1); if ($dns1 eq "#!INVALID!#") { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The specified IFN dns1 value is not valid.", 0); $bad_value = 1; } else { $conf->{network}{ifn}{dns1} = $dns1; } } if ($dns2) { $dns2 = validate_ip_address($conf, $dns2); if ($dns2 eq "#!INVALID!#") { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The specified IFN dns2 value is not valid.", 0); $bad_value = 1; } else { $conf->{network}{ifn}{dns2} = $dns2; } } } else { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - The specified IFN IP address and netmask are malformed.", 0); $bad_value = 1; } return($bad_value); } # This validates a string as being an IPv4 address in dotted-decimal notation. sub validate_ip_address { my ($conf, $ip) = @_; if ($ip =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) { my $first_octal = $1; my $second_octal = $2; my $third_octal = $3; my $fourth_octal = $4; # First octal has to be >= 1. The rest can be 0. if (($first_octal < 1) or ($first_octal > 255) or ($second_octal < 0) or ($second_octal > 255) or ($third_octal < 0) or ($third_octal > 255) or ($fourth_octal < 0) or ($fourth_octal > 255)) { $ip = "#!INVALID!#"; } } else { # Not even formatted properl. $ip = "#!INVALID!#"; } return($ip); } # This takes an integer and, if it is a valid CIDR range, returns the # dotted-decimal equivalent. If it is not, it returns '#!INVALID!#'. sub convert_cidr_to_dotted_decimal { my ($conf, $netmask) = @_; if ($netmask =~ /^\d{1,2}$/) { # Make sure it is a (useful) CIDR if (($netmask >= 1) && ($netmask <= 29)) { # Yes, I know that technically '0' and '30-32' are # valid, but not in our case. if ($netmask == 1) { $netmask = "128.0.0.0"; } elsif ($netmask == 2) { $netmask = "192.0.0.0"; } elsif ($netmask == 3) { $netmask = "224.0.0.0"; } elsif ($netmask == 4) { $netmask = "240.0.0.0"; } elsif ($netmask == 5) { $netmask = "248.0.0.0"; } elsif ($netmask == 6) { $netmask = "252.0.0.0"; } elsif ($netmask == 7) { $netmask = "254.0.0.0"; } elsif ($netmask == 8) { $netmask = "255.0.0.0"; } elsif ($netmask == 9) { $netmask = "255.128.0.0"; } elsif ($netmask == 10) { $netmask = "255.192.0.0"; } elsif ($netmask == 11) { $netmask = "255.224.0.0"; } elsif ($netmask == 12) { $netmask = "255.240.0.0"; } elsif ($netmask == 13) { $netmask = "255.248.0.0"; } elsif ($netmask == 14) { $netmask = "255.252.0.0"; } elsif ($netmask == 15) { $netmask = "255.254.0.0"; } elsif ($netmask == 16) { $netmask = "255.255.0.0"; } elsif ($netmask == 17) { $netmask = "255.255.128.0"; } elsif ($netmask == 18) { $netmask = "255.255.192.0"; } elsif ($netmask == 19) { $netmask = "255.255.224.0"; } elsif ($netmask == 20) { $netmask = "255.255.240.0"; } elsif ($netmask == 21) { $netmask = "255.255.248.0"; } elsif ($netmask == 22) { $netmask = "255.255.252.0"; } elsif ($netmask == 23) { $netmask = "255.255.254.0"; } elsif ($netmask == 24) { $netmask = "255.255.255.0"; } elsif ($netmask == 25) { $netmask = "255.255.255.128"; } elsif ($netmask == 26) { $netmask = "255.255.255.192"; } elsif ($netmask == 27) { $netmask = "255.255.255.224"; } elsif ($netmask == 28) { $netmask = "255.255.255.240"; } elsif ($netmask == 29) { $netmask = "255.255.255.248"; } else { # This should never be hit. $netmask = "#!INVALID!#"; } } else { $netmask = "#!INVALID!#"; } } return($netmask); } # This removes the an-repo if '--no-an-repo' is used. sub remove_an_repo { my ($conf) = @_; logger($conf, $THIS_FILE, __LINE__, "Removing AN! software repository if it exists.", 1); if (-e $conf->{path}{an_repo}) { my $shell_call = $conf->{executable}{yum}." remove alteeve-repo"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; while(<$file_handle>) { print $_; } logger($conf, $THIS_FILE, __LINE__, "==============================================================================", 1); close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- Verifying it is gone...\n", 1); get_list_of_installed_packages($conf); if (not $conf->{packages}{installed}{'alteeve-repo'}) { logger($conf, $THIS_FILE, __LINE__, "- Success!\n", 1); } else { logger($conf, $THIS_FILE, __LINE__, "- Failed! Removing it manually...\n", 1); unlink $conf->{path}{an_repo} or die "Unable to delete: [".$conf->{path}{an_repo}."]. The error was: $!\n"; logger($conf, $THIS_FILE, __LINE__, "- removed.", 1); } } else { logger($conf, $THIS_FILE, __LINE__, "- Not found.", 1); } # Remove the alteeve-repo RPM from the list of packages to install. delete $conf->{packages}{to_install}{'alteeve-repo'}; $conf->{sys}{repositories} = ""; logger($conf, $THIS_FILE, __LINE__, "- Done!\n", 1); return(0); } # Print the usage information. sub print_usage { my ($conf) = @_; my $help = q| -=] Striker installer DESCRIPTION This program performs the "Stage 2" Striker install. This is where you get to customize the install to suit your liking. SWITCHES --autostart-dhcpd By default, Striker will not run its "Install Target" feature on boot. This is done to avoid an accidental conflict with your site's main DHCP server(s). Ideally, the BCN will be isolated from the main network. If so, the DHCP server on the dashboard can safely run all the time, avoiding the need to manually enable the "Install Target" when needed. If you know that your BCN is VLAN or hard isolated, then you can use this switch to auto-start the "Install Target" feature on boot. -b This sets the IP address and subnet mask to be assigned to the Back-Channel Network interface. -c This sets the name of the customer/owner of the Anvil!. This is used by the web browser at the top of the input box when asking the user for their login credentials. If this is not set, "Striker Dashboard" will be used alone. --custom-config /path/to/striker.conf This is an optional way to specify a custom striker.conf file to use instead of the default example striker.conf provided by the installer. Note that the file name does *not* have to be 'striker.conf'. It must have all core variables set though. That is, the specified file must be complete because it will be used instead of, not appended to, the default configuration file. --ethtool-opts "" If you need to pass any special 'ethtool' configuration options to your network devices, you can do so using this option. If you want to pass multiple options, be sure to use double-quotes to contain the options. Note that these options will be applied to both/all ifn_linkX and bcn_link* interfaces. -h, -?, --help Show this dialog and exit. --host-uuid If you want to use a set UUID to identify this machine in ScanCore, you can do so using this switch. This will only make sense if you are rebuilding or replacing an old dashboard with new hardware and you want to use the old UUID to link the systems in the ScanCore database. Normally, the system UUID reported by dmidecode is used. -i ,dg=[,dns1=,dns2=] This sets the IP address and subnet to be assigned to the Internet-Facing Network interface. Unlike -b switch, this switch requires a default gateway be specified after the IP address via: dg= IPv4 address of the default gateway. Optionally, one or two DNS servers may be specified via: dns1= dns2= IPv4 addresses of the first and/or secondary DNS server. If these are not set, they will not be used! This disables DNS on the DHCP server as well! --ipmi-user If specified, and if an IPMI BMC is found, the installer will set the IPMI BMC password for the specified user. Note: This will *not* create a user! If the specified user is not found in the IPMI user list, the password will not be set and an error will be printed. --ipmi-ip [,gw=] If specified, and if an IPMI BMC is found, the installer will set the IPMI BMC to have the specified IP address and network mask/subnet. Optionally, if 'gw' is set, the BMC's gateway will also be set. --use-lsi If your dashboard machine uses an LSI-brand storage controller, and if you've installed (or specify a repo with) the LSI 'storcli' and 'MegaCli' RPMs, then they will be installed and configured. --mtu If your network can handle jumbo frames, you can use this switch to set the MTU byte size. This will be assigned to both/all interfaces. If you are not certain that your network can handle large packets, do not set this. -n This sets the host name of the Striker dashboard. --no-os-updates By default, the operating system will be updated if there is Internet access. You can disable this behaviour with this switch. --no-striker-configure-vmm By default, the 'striker-configure-vmm' will be used to configure Virtual Machine Manager so that Anvil! systems added via Striker are automatically added to and removed from VMM. In some cases, this can cause login trouble (though that is a bug and should be reported, please). Using this option prevents the automatic management of Striker dashboard to Anvil! node access and Virtuam machine Manager configuration. --no-reboot By default, the installer will reboot the system when it is finished. This option prevents this from happening. If you are installing over an ssh connection, using this switch is recommended. --ntp If one or more NTP IPs are passed, they will be added to Striker's NTP configuration file. Multiple NTP servers can be specified using commas to separate the IPs. -p This sets the DHCP range that this Striker dashboard will offer IPs on for new machines being installed off of the local PXE server. If set to 'none', PXE will not be configured. NOTE: This lease range must be in the BCN subet. We do not want to interfere with existing DHCP servers on the IFN! --peer-dashboard This allows you to tell this Striker dashboard about its peer. When set, the installer will configure the other dashboard as a ScanCore database host and record its data to the peer as well. NOTE: This only works if the ScanCore database configuration and Striker password is the same as this dashboard! If you have a differing configuration or password, please manually add the peer information to striker.conf and /etc/hosts after the install completes. --rhn When installing on a RHEL server, these credentials can be passed to add the machine to yout Red Hat account. NOTE: This uses Red Hat's subscription-manager, NOT the legacy Red Hat tools. --router-mode When Striker is configured to be an install target, using this switch tells Striker to run as a router, providing Internet access on the BCN to the machines getting DHCP IPs from it on the BCN. If '-p' is not set, this option will be ignored. NOTE: This will expose the BCN to the IFN! Care has been taken to block all traffic from the IFN to the BCN, but no program is perfect. If you are particularly concerned about this, audit the code before using this. In any case, it is a good idea to disable the "Install Target" feature in Striker when not needed. That will tear down the bridge as well. --run-rc-local In some cases, some users have customizations in /etc/rc.local that can be clobbered by the GUI install. Using this switch causes the installer to call 'sh /etc/rc.local' just prior to the end of the install process. --source /mnt/ This points to where the Anvil! install ISO is mounted. NOTE: This is required when running on a normal minimal install of CentOS / RHEL. -u This is the user name and password to set for Striker. This will be used in apache's htpasswd authentication and will be stored in the |; $help .= " $conf->{path}{apache_htpasswd} file.\n"; $help .= q| NOTE: This also sets the 'root' user's password on the striker appliance! -vvvv, -vvv, -vv, -v This sets the debug level to 4, 3, 2, or 1 respectively. -y Automatically answers "yes" to prompts. Specifically, this prevents confirmation request for network remapping, so it should only be used by scripts. NOTE: that all IP addresses must be IPv4 in dotted-decimal notation. The netmask can be in CIDR or dotted-decimal notation. Do not leave a space between the IP address and netmask EXAMPLE Normal install ./striker-installer -c "Alteeve's Niche\!" \ -n an-striker01.alteeve.com \ -u "admin:secret password" \ -p 10.20.7.200:10.20.7.230 \ -b 10.20.4.1/16 \ -i 10.255.4.1/16,dg=10.255.255.254,dns1=8.8.8.8,dns2=8.8.4.4 Developer install, using local copies of ISOs: ./striker-installer -c "Alteeve's Niche\!" \ -n an-striker01.alteeve.com \ -d git \ -u "admin:secret password" \ -p 10.20.7.200:10.20.7.230 \ -b 10.20.4.1/16 \ -i 10.255.4.1/16,dg=10.255.255.254,dns1=8.8.8.8,dns2=8.8.4.4 Note: As with any bash call, you must escape '!' characters with '\!'. SUPPORT https://alteeve.com/w/Support Alteeve's Niche! |; open (my $file_handle, ">", "/tmp/striker-installer-help") or die "Couldn't write help to /tmp/, error was: $!\n"; print $file_handle $help; close $file_handle; system("/usr/bin/less /tmp/striker-installer-help"); 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 = ""; } 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/^ //; } # If we were passed '-vvv', '-vv' or '-v', increase debug. if ($conf->{switches}{vvvv}) { $conf->{sys}{debug} = 4; } elsif ($conf->{switches}{vvv}) { $conf->{sys}{debug} = 3; } elsif ($conf->{switches}{vv}) { $conf->{sys}{debug} = 2; } elsif ($conf->{switches}{v}) { $conf->{sys}{debug} = 1; } # Debug foreach my $variable (sort {$a cmp $b} keys %{$conf->{switches}}) { logger($conf, $THIS_FILE, __LINE__, "Variable: [$variable]\t-> value: [".$conf->{switches}{$variable}."]", 3); } #exit; return(0); } # This asks the user to unplug (and plug back in) NICs to identify which # physical interface they want to use for a given role. sub select_nics { my ($conf, $cache_read) = @_; # If a cache file exists, read it and see if we've got the same MAC addresses. logger($conf, $THIS_FILE, __LINE__, "sys::network_map_cache_read: [".$conf->{sys}{network_map_cache_read}."]", 3); ### BUG: Cache is broken, fix it. 'Til then... #if (not $conf->{sys}{network_map_cache_read}) if (1) { logger($conf, $THIS_FILE, __LINE__, "- Beginning NIC identification...", 1); foreach my $nic (@{$conf->{nics}}) { my $plug_in_message_printed = 0; while (my $down_links = check_nic_states($conf)) { if (not $plug_in_message_printed) { logger($conf, $THIS_FILE, __LINE__, "- Please plug in all network cables to proceed.", 1); $plug_in_message_printed = 1; } logger($conf, $THIS_FILE, __LINE__, "MAC address of down links: [$down_links]", 3); sleep 1; } my $say_nic = $nic; if ($nic =~ /^bcn_link(\d)/) { $say_nic = "Back-Channel Network, Link $1" } elsif ($nic =~ /^ifn_link(\d)/) { $say_nic = "Internet-Facing Network, Link $1" } logger($conf, $THIS_FILE, __LINE__, "- Please unplug the interface you want to make:", 1); logger($conf, $THIS_FILE, __LINE__, " [$say_nic]", 1); my $last_conflict = ""; while(1) { my $down_links = check_nic_states($conf); if (($down_links) && ($down_links !~ /,/)) { if ($conf->{nic}{mac}{$down_links}{new_name}) { logger($conf, $THIS_FILE, __LINE__, "- The NIC with MAC address: [$down_links] is already allocated to: [".$conf->{nic}{mac}{$down_links}{new_name}."]", 1); $last_conflict = $down_links; } else { $conf->{nic}{mac}{$down_links}{new_name} = $nic; $conf->{nic}{name}{$nic} = $down_links; logger($conf, $THIS_FILE, __LINE__, "- NIC with MAC: [$down_links] will become: [".$conf->{nic}{mac}{$down_links}{new_name}."]", 1); logger($conf, $THIS_FILE, __LINE__, " (it is currently: [".$conf->{nic}{mac}{$down_links}{current_name}."])", 1); last; } } sleep 1; } } my $plug_in_message_printed = 0; while (my $down_links = check_nic_states($conf)) { if (not $plug_in_message_printed) { logger($conf, $THIS_FILE, __LINE__, "- Please plug in all network cables to proceed.", 1); $plug_in_message_printed = 1; } sleep 1; } logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); logger($conf, $THIS_FILE, __LINE__, "Here is what you selected:", 1); } else { logger($conf, $THIS_FILE, __LINE__, "Here is what was read:", 1); } my $proceed = 1; foreach my $nic (@{$conf->{nics}}) { my $say_nic = $nic; logger($conf, $THIS_FILE, __LINE__, "say_nic: [$say_nic]", 3); if ($nic =~ /^bcn_link(\d)/) { $say_nic = "Back-Channel Network, Link $1" } elsif ($nic =~ /^ifn_link(\d)/) { $say_nic = "Internet-Facing Network, Link $1" } $conf->{cache}{nic}{name}{$nic} = "" if not defined $conf->{cache}{nic}{name}{$nic}; logger($conf, $THIS_FILE, __LINE__, "nic::name::$nic: [".$conf->{nic}{name}{$nic}."], cache::nic::name::$nic: [".$conf->{cache}{nic}{name}{$nic}."]", 3); my $this_mac = $conf->{cache}{nic}{name}{$nic} ? $conf->{cache}{nic}{name}{$nic} : $conf->{nic}{name}{$nic}; logger($conf, $THIS_FILE, __LINE__, "this_mac: [$this_mac]", 3); if (not $this_mac) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - There seems to be a problem.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Desired NIC: [$nic] doesn't have a referenced MAC address!", 0); exit(1); } my $current_name = $conf->{nic}{mac}{$this_mac}{current_name}; if (not $current_name) { logger($conf, $THIS_FILE, __LINE__, "[ Error ] - There seems to be a problem.", 0); logger($conf, $THIS_FILE, __LINE__, "[ Error ] Desired NIC: [$nic] doesn't have a referenced interface!", 0); exit(2); } logger($conf, $THIS_FILE, __LINE__, "- Interface: [".uc($this_mac)."], currently named: [$current_name],", 1); logger($conf, $THIS_FILE, __LINE__, " will be renamed to: [$nic]", 1); logger($conf, $THIS_FILE, __LINE__, "- $say_nic will use the NIC with MAC: [$this_mac] ($current_name -> $nic)", 3); $conf->{nic}{name}{$nic} = lc($this_mac); } my $bcn_ip = $conf->{network}{bcn}{ip}; my $bcn_netmask = $conf->{network}{bcn}{netmask}; logger($conf, $THIS_FILE, __LINE__, "\nThe Back-Channel Network interface will be set to:", 1); logger($conf, $THIS_FILE, __LINE__, "- IP: [$bcn_ip]", 1); logger($conf, $THIS_FILE, __LINE__, "- Netmask: [$bcn_netmask]\n", 1); my $ifn_ip = $conf->{network}{ifn}{ip}; my $ifn_netmask = $conf->{network}{ifn}{netmask}; my $ifn_gateway = $conf->{network}{ifn}{gateway}; my $ifn_dns1 = $conf->{network}{ifn}{dns1}; my $ifn_dns2 = $conf->{network}{ifn}{dns2}; logger($conf, $THIS_FILE, __LINE__, "The Internet-Facing Network interface will be set to:", 1); logger($conf, $THIS_FILE, __LINE__, "- IP: [$ifn_ip]", 1); logger($conf, $THIS_FILE, __LINE__, "- Netmask: [$ifn_netmask]", 1); logger($conf, $THIS_FILE, __LINE__, "- Gateway: [$ifn_gateway]", 1); logger($conf, $THIS_FILE, __LINE__, "- DNS1: [$ifn_dns1]", 1); logger($conf, $THIS_FILE, __LINE__, "- DNS2: [$ifn_dns2]\n", 1); logger($conf, $THIS_FILE, __LINE__, "Shall I proceed? [Y/n] ", 1); if ($conf->{switches}{y}) { logger($conf, $THIS_FILE, __LINE__, "\n- Auto-yes used, proceeding.\n", 1); } else { my $answer = ; chomp($answer); logger($conf, $THIS_FILE, __LINE__, "answer: [$answer]\n", 3); if (($answer) && (lc($answer) =~ /^n/)) { $proceed = 0; if ($conf->{sys}{network_map_cache_read}) { $conf->{sys}{network_map_cache_read} = 0; logger($conf, $THIS_FILE, __LINE__, "- Ok, we will map the network.", 1); $conf->{nic}{mac} = {}; } else { logger($conf, $THIS_FILE, __LINE__, "- Ok, starting over.\n", 1); $conf->{nic}{mac} = {}; } #sleep 2; #system('clear'); } else { logger($conf, $THIS_FILE, __LINE__, "- Thank you, I will start to work now.\n", 1); # If I am here, we're ready to go. Record the MAC addresses the user # selected. if (not $conf->{sys}{network_map_cache_read}) { record_network_map($conf); } } } logger($conf, $THIS_FILE, __LINE__, "proceed: [$proceed]", 3); return($proceed); } # This looks for the network map cache file and, if it finds it, checks to see # if the MAC addresses in it match what we see on disk. Sets # $conf->{sys}{network_map_cache_read} to '1' if successful. sub load_network_map_cache { my ($conf) = @_; # Read in the cache, if it exists and is readable. my $ok = 1; if ((-e $conf->{path}{network_map_cache}) && (-r $conf->{path}{network_map_cache})) { # Woot! logger($conf, $THIS_FILE, __LINE__, "- Network mapping cache file found, reading it.", 1); my $shell_call = "<$conf->{path}{network_map_cache}"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open (my $file_handle, "$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to read: [$shell_call], error was: $!\n"; while(<$file_handle>) { chomp; my $line = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line]", 3); if ($line =~ /^(\w.*?),(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)/) { my $nic = $1; my $mac = $2; $conf->{cache}{nic}{$nic}{mac} = $mac; $conf->{cache}{mac}{$mac}{nic} = $nic; logger($conf, $THIS_FILE, __LINE__, "- Cached interface called: [$nic (".$conf->{cache}{mac}{$mac}{nic}.")] uses mac: [".$conf->{cache}{nic}{$nic}{mac}." ($mac)].", 1); #$conf->{cache}{nic}{name}{$nic} = lc($mac); #$conf->{cache}{nic}{mac}{$mac} = $nic; #logger($conf, $THIS_FILE, __LINE__, "- Cached interface: [$nic (".$conf->{cache}{nic}{name}{$nic}.")] to use: [$mac (".$conf->{cache}{nic}{mac}{$mac}.")].", 1); } } close $file_handle; # Call 'ip addr' my $this_iface = ""; my $this_mac = ""; $shell_call = $conf->{executable}{ip}." addr"; open ($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 = $_; logger($conf, $THIS_FILE, __LINE__, "line: [$line].", 3); if ($line =~ /^\d+: (.*?): <(.*?)>/) { $this_iface = $1; $this_mac = ""; logger($conf, $THIS_FILE, __LINE__, "this_iface: [$this_iface].", 3); } next if not $this_iface; next if $this_iface eq "lo"; next if $this_iface =~ /wlan/; # If the user is re-running the install, bridge interfaces will exist. If this # happens, we won't use the cache. if ($this_iface =~ /bond/) { # We're not going to bother configuring the network, so just bail out now. logger($conf, $THIS_FILE, __LINE__, "- Bonds exist, network will not be configured.", 1); $conf->{cache} = {}; $conf->{sys}{bonds_exist} = 1; return(0); } $line =~ s/^\s+//; $line =~ s/\s+$//; logger($conf, $THIS_FILE, __LINE__, "line: [$line].", 3); if ($line =~ /ether ([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})/) { $this_mac = lc($1); logger($conf, $THIS_FILE, __LINE__, "this_mac: [$this_mac], this_iface: [$this_iface], nic: [".$conf->{cache}{mac}{$this_mac}{nic}."]", 3); # Is this MAC cached? if ($conf->{cache}{mac}{$this_mac}{nic}) { # Yes. push @{$conf->{cache}{nics}}, $conf->{cache}{mac}{$this_mac}{nic}; logger($conf, $THIS_FILE, __LINE__, ": this_mac: [$this_mac], this_iface: [$this_iface]", 3); $conf->{nic}{name}{$this_iface} = $this_mac; $conf->{nic}{mac}{$this_mac}{current_name} = $this_iface; logger($conf, $THIS_FILE, __LINE__, ": nic::name::$this_iface: [".$conf->{nic}{name}{$this_iface}."], nic::mac::${this_mac}::current_name: [".$conf->{nic}{mac}{$this_mac}{current_name}."]", 3); # Find the NIC we want this mac to be. #my $new_nic = $conf->{cache}{mac}{$mac}{nic}; #$conf->{nic}{name}{$new_nic} = $this_mac; } else { # A NIC we don't know, remap. logger($conf, $THIS_FILE, __LINE__, "- The NIC with MAC address: [$this_mac] is not cached, remap required.", 1); $conf->{cache} = {}; return(0); } } } close $file_handle; # If nothing killed the 'ok', we're able to skip mapping. if ($ok) { logger($conf, $THIS_FILE, __LINE__, "- Cache read successfully, remap not required.", 1); logger($conf, $THIS_FILE, __LINE__, "Done.\n", 1); #$conf->{nic}{name}{$nic} } } else { logger($conf, $THIS_FILE, __LINE__, "- Network mapping cache not found, manual mapping required.", 1); $conf->{cache} = {}; $ok = 0; } $conf->{sys}{network_map_cache_read} = $ok; logger($conf, $THIS_FILE, __LINE__, "sys::network_map_cache_read: [".$conf->{sys}{network_map_cache_read}."]", 3); return($ok); } # This writes the user's selected network mapping to a cache file in case the # user needs to restart the installer. sub record_network_map { my ($conf) = @_; my $cache_data = ""; foreach my $nic (@{$conf->{nics}}) { my $this_mac = $conf->{nic}{name}{$nic}; next if not $this_mac; $cache_data .= "$nic,".lc($this_mac)."\n"; } if ($cache_data) { my $shell_call = ">$conf->{path}{network_map_cache}"; logger($conf, $THIS_FILE, __LINE__, "shell_call: [$shell_call]", 3); open (my $file_handle, "$shell_call") or die "$THIS_FILE ".__LINE__."; Failed to write: [$shell_call], error was: $!\n"; print $file_handle $cache_data; close $file_handle; logger($conf, $THIS_FILE, __LINE__, "- network mapping cached.", 1); } return(0); } sub check_nic_states { my ($conf) = @_; my $unplugged_macs = ""; my $this_iface = ""; my $this_mac = ""; my $this_link_state = ""; my $this_ip = ""; my $shell_call = $conf->{executable}{ip}." addr"; 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 = $_; if ($line =~ /^\d+: (.*?): <(.*?)>/) { $this_iface = $1; $this_link_state = $2; $this_mac = ""; if ($this_link_state =~ /UP/) { # This will be either BROADCAST or NO-CARRIER $this_link_state =~ s/,.*//; } else { # The interface has been 'ifdown'ed or was not # started on boot. $this_link_state = "DOWN"; } next; } next if not $this_iface; next if $this_iface eq "lo"; next if $this_iface =~ /wlan/; next if $this_iface =~ /virbr/; # If the user is re-running the install, bridge interfaces will # exist, we ignore those. if ($this_iface =~ /bond/) { logger($conf, $THIS_FILE, __LINE__, "- Bonded interfaces exist. This network appears to already be mapped,", 1); logger($conf, $THIS_FILE, __LINE__, " skipping. If you want to force a remap, please run:", 1); logger($conf, $THIS_FILE, __LINE__, " rsync -av --delete ".$conf->{path}{backups}."/network-scripts /etc/sysconfig/", 1); logger($conf, $THIS_FILE, __LINE__, " and then reboot your dashboard. This will reset the networking to how it was", 1); logger($conf, $THIS_FILE, __LINE__, " before the first install, so your IP address will change!", 1); $conf->{sys}{bonds_exist} = 1; last; } $line =~ s/^\s+//; $line =~ s/\s+$//; if ($line =~ /ether ([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})/) { # Link states: # 0 = Up no no link # 1 = Up with link # 2 = Down $this_mac = $1; logger($conf, $THIS_FILE, __LINE__, "this_mac: [$this_mac]", 3); if ($this_link_state eq "DOWN") { $this_link_state = 2; } else { $this_link_state = $this_link_state eq "NO-CARRIER" ? 0 : 1; } $conf->{nic}{mac}{$this_mac}{current_name} = $this_iface; $conf->{nic}{mac}{$this_mac}{link_state} = $this_link_state; $conf->{nic}{by_name}{$this_iface}{link_state} = $this_link_state; $conf->{nic}{by_name}{$this_iface}{mac} = $this_mac; } if ($line =~ /inet (.*?)\/.*? brd/) { my $this_ip = $1; $conf->{nic}{mac}{$this_mac}{ip} = $this_ip; $conf->{nic}{by_name}{$this_iface}{ip} = $this_ip; } } close $file_handle; my $nic_count = 0; foreach my $this_mac (sort {$a cmp $b} keys %{$conf->{nic}{mac}}) { # Somewhere in the loop, an empty '$this_mac' is being created... # (I'm looking at you, autovivication >_>) next if not $this_mac; my $this_iface = $conf->{nic}{mac}{$this_mac}{current_name}; # I don't care about localhost, wireless or bonded interfaces. if (($this_iface eq "lo") or ($this_iface =~ /wlan/) or ($this_iface =~ /bond/)) { logger($conf, $THIS_FILE, __LINE__, "Skipping interface: [$this_iface]", 3); } else { $nic_count++; logger($conf, $THIS_FILE, __LINE__, "NIC: [$this_iface] w/ MAC: [$this_mac]; Link: [".$conf->{nic}{mac}{$this_mac}{link_state}."], current name: [".$conf->{nic}{mac}{$this_mac}{current_name}."]", 3); } if ($conf->{nic}{mac}{$this_mac}{link_state} ne "1") { $unplugged_macs .= "$this_mac,"; } } # If there are four NICs, we'll want to create bonds. logger($conf, $THIS_FILE, __LINE__, "- NIC count: [$nic_count]", 2); if ((not $conf->{sys}{skip_network}) && ($nic_count >= 4)) { # Woo! I like this user. :3 $conf->{nics} = ["bcn_link1", "bcn_link2", "ifn_link1", "ifn_link2"]; $conf->{sys}{create_bonds} = 1; } $unplugged_macs =~ s/,$//; logger($conf, $THIS_FILE, __LINE__, "- DEBUG: unpluggd_macs: [$unplugged_macs]", 3); return($unplugged_macs); } # Check if the passed string is a valid IP address and subnet mask (CIDR or # dotted-decimal). sub is_string_ipv4_with_subnet { my ($conf, $ip) = @_; my $subnet = ""; my $valid = 1; # Make sure the string has a subnet after it. if ($ip =~ /^(.*?)\/(.*)$/) { $ip = $1; $subnet = $2; # Check the IP if (is_string_ipv4($conf, $ip)) { # IP is ok, not convert to dotted decimal if needed. if ($subnet =~ /^\d+$/) { ($subnet) = convert_cidr_to_dotted_decimal($conf, $subnet); } # Should be dotted-decimal now, check it. if (not is_string_ipv4($conf, $subnet)) { # No luck. $valid = 0; } } else { # IP isn't valid, no sense checking the subnet $valid = 0; } } else { # This isn't an 'ip/nm' string at all. $valid = 0; } return($valid); } # Checks if the passed-in string is an IPv4 address (with or without a subnet # mask). Returns '1' if OK, 0 if not. sub is_string_ipv4 { my ($conf, $ip) = @_; my $valid = 1; if ($ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { # It is in the right format. my $first_octal = $1; my $second_octal = $2; my $third_octal = $3; my $fourth_octal = $4; if (($first_octal < 0) or ($first_octal > 255) or ($second_octal < 0) or ($second_octal > 255) or ($third_octal < 0) or ($third_octal > 255) or ($fourth_octal < 0) or ($fourth_octal > 255)) { # One of the octals is out of range. $valid = 0; } } else { # Not in the right format. $valid = 0; } return($valid); } # Check to see if the string looks like a valid hostname sub is_domain_name { my ($conf, $name) = @_; my $valid = 1; if (not $name) { $valid = 0; } elsif (($name !~ /^((([a-z]|[0-9]|\-)+)\.)+([a-z])+$/i) && (($name !~ /^\w+$/) && ($name !~ /-/))) { # Doesn't appear to be valid. $valid = 0; } return($valid); } # This takes a dotted-decimal subnet mask and converts it to it is CIDR equivalent. If it is not, it returns # '#!INVALID!#'. sub convert_dotted_decimal_to_cidr { my ($conf, $netmask) = @_; if ($netmask eq "128.0.0.0") { $netmask = 1; } elsif ($netmask eq "192.0.0.0") { $netmask = 2; } elsif ($netmask eq "224.0.0.0") { $netmask = 3; } elsif ($netmask eq "240.0.0.0") { $netmask = 4; } elsif ($netmask eq "248.0.0.0") { $netmask = 5; } elsif ($netmask eq "252.0.0.0") { $netmask = 6; } elsif ($netmask eq "254.0.0.0") { $netmask = 7; } elsif ($netmask eq "255.0.0.0") { $netmask = 8; } elsif ($netmask eq "255.128.0.0") { $netmask = 9; } elsif ($netmask eq "255.192.0.0") { $netmask = 10; } elsif ($netmask eq "255.224.0.0") { $netmask = 11; } elsif ($netmask eq "255.240.0.0") { $netmask = 12; } elsif ($netmask eq "255.248.0.0") { $netmask = 13; } elsif ($netmask eq "255.252.0.0") { $netmask = 14; } elsif ($netmask eq "255.254.0.0") { $netmask = 15; } elsif ($netmask eq "255.255.0.0") { $netmask = 16; } elsif ($netmask eq "255.255.128.0") { $netmask = 17; } elsif ($netmask eq "255.255.192.0") { $netmask = 18; } elsif ($netmask eq "255.255.224.0") { $netmask = 19; } elsif ($netmask eq "255.255.240.0") { $netmask = 20; } elsif ($netmask eq "255.255.248.0") { $netmask = 21; } elsif ($netmask eq "255.255.252.0") { $netmask = 22; } elsif ($netmask eq "255.255.254.0") { $netmask = 23; } elsif ($netmask eq "255.255.255.0") { $netmask = 24; } elsif ($netmask eq "255.255.255.128") { $netmask = 25; } elsif ($netmask eq "255.255.255.192") { $netmask = 26; } elsif ($netmask eq "255.255.255.224") { $netmask = 27; } elsif ($netmask eq "255.255.255.240") { $netmask = 28; } elsif ($netmask eq "255.255.255.248") { $netmask = 29; } elsif ($netmask eq "255.255.255.252") { $netmask = 30; } elsif ($netmask eq "255.255.255.255") { $netmask = 32; } else { $netmask = "#!INVALID!#"; } return($netmask); }