Skip to content

Instantly share code, notes, and snippets.

/ISOcreator Secret

Created August 16, 2012 17:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/82e81240a3ec2a996fb6 to your computer and use it in GitHub Desktop.
Save anonymous/82e81240a3ec2a996fb6 to your computer and use it in GitHub Desktop.
use strict;
use warnings;
use Cwd;
use Compress::Zlib;
use Digest::SHA;
use File::Basename;
use File::Path;
use Getopt::Long 2.36 qw(GetOptionsFromString);
use IO::Zlib;
use LWP::UserAgent;
my $whoami = basename $0;
my $dir = getcwd;
###
### COMMAND-LINE AND CONFIGURATION-FILE OPTION PARSING
###
my $usage = "Usage: $whoami [--help] [--dry-run] [--debug] [--verbose] [...options...]
Script options:
[--centos-iso=filename.iso] [--rpm-cache=directory-name]
[--appliance-manager=hostname] [--output-file=filename.iso]
[--template=template-name] [--config-file=file-name]
VM options:
--virtual|--physical [--dell] [--hyperv] [--scsi|--ide] [--[no]vmware-tools]
--isv-release=#.#.#.#.#.#.# --netproto=static|dhcp --hostname=host-name
[--address=ip-address] [--netmask=netmask] [--gateway=ip-address]
[--nameserver=ip-address[,ip-address[,...]]] [--root-password=pw]
[--grub-password=pw] [--[no]emacs] [--[no]64bit] [--[no]boston]\n";
# Hidden options, primary for development and debugging:
#
# --nofetch - skip fetching RPMs, they're already fetched
# --preserve - Preserve temporary files
my %templates = (
"vmimage" => {
"hostname" => "vmimage",
"netproto" => "static",
"address" => "10.22.60.74",
"netmask" => "255.255.255.0",
"gateway" => "10.22.60.254",
"nameserver" => "10.22.74.97,10.22.74.197",
"virtual" => 1,
},
"stockimage" => {
"hostname" => "stockimage",
"netproto" => "static",
"address" => "10.22.60.76",
"netmask" => "255.255.255.0",
"gateway" => "10.22.60.254",
"nameserver" => "10.22.74.97,10.22.74.197",
"physical" => 1,
},
"stockimage_dell" => {
"hostname" => "stockimage_dell",
"netproto" => "static",
"address" => "10.22.60.76",
"netmask" => "255.255.255.0",
"gateway" => "10.22.60.254",
"nameserver" => "10.22.74.97,10.22.74.197",
"physical" => 1,
"dell" => 1
}
);
my $prod_app_manager = 'download01.tamalesoftware.com';
my(%centos_iso_urls) = (
"bit32" => "http://vault.centos.org/5.4/isos/i386/CentOS-5.4-i386-bin-1of6.iso",
"bit64" => "http://vault.centos.org/5.4/isos/x86_64/CentOS-5.4-x86_64-bin-1of7.iso",
);
my(@gpg_keys) = ('BEC-RPM-GPG-KEY', 'TS-RPM-GPG-KEY');
my(@emacs_rpms) = qw(emacs emacs-common Xaw3d giflib
xorg-x11-fonts-ISO8859-1-75dpi);
my(@vmware_tools_rpms) = qw(binutils gcc glibc-devel glibc-headers
kernel-devel libgomp);
# Eventually we may need to support different versions of VMware-tools for
# different VM host platforms, but for the time being we're using just one.
my $vmware_tools_repo = "vmware-tools";
my $vmware_tools_url_tail = "$vmware_tools_repo/VMwareTools-3.5.0-153875.i386.rpm";
my(%c, %c2, %c3);
my $wd = "/tmp/$whoami.$$";
my(@arg_specs) = qw(dryrun|dry-run debug verbose
virtual physical dell hyperv scsi ide vmwaretools|vmware-tools!
isvrelease|isv-release=s netproto=s hostname=s address=s netmask=s
gateway=s nameserver=s rootpassword|root-password=s
grubpassword|grub-password=s emacs! bit64|64bit! boston!
centosiso|centos-iso=s rpmcache|rpm-cache=s
appmanager|app-manager|appliance-manager=s
outputfile|output-file=s preserve
fetch!);
print "check 1 ";
$c{fetch} = 1;
GetOptions(\%c, "help", "configfile|config-file|configuration-file=s", "template=s", @arg_specs)
or die $usage;
if ($c{help}) {
print $usage;
exit;
}
print "check 2 ";
if ($^O eq "linux") {
print "check 3 ";
if ($>) {
print "check 4 ";
#die "$whoami: Must be run as root on Linux\n";
}
}
print "check 5 @ARGV";
if (@ARGV) {
die "$whoami: Extra arguments: @ARGV\n";
}
if ($c{configfile}) {
my($data);
if (! -f $c{configfile}) {
die "$whoami: Config file $c{configfile} does not exist\n";
}
open(CONFIGFILE, "<", $c{configfile}) or
die "$whoami: open($c{configfile}): $!\n";
{ local($/) = undef; $data = <CONFIGFILE>; }
close(CONFIGFILE) or die "$whoami: close($c{configfile}): $!\n";
my($ret, $args) = GetOptionsFromString($data, \%c2, "template=s",
@arg_specs);
if ($args && @{$args}) {
die "$whoami: Extra arguments in $c{configfile}: @{$args}\n";
}
if (! $ret) {
die "$whoami: Invalid option(s) in $c{configfile}\n$usage";
}
}
if ($c{template} || $c2{template}) {
my $template = $c{template} || $c2{template};
if (! $templates{$template}) {
warn "$whoami: Invalid template: \"$template\"\n";
die("$whoami: Valid templates are: ", join(", ", sort keys %templates),
"\n");
}
%c3 = %{$templates{$template}};
}
if ($c{physical} or $c{virtual}) {
if (delete $c2{physical} and $c{virtual}) {
warn "$whoami: Warning: Ignoring --physical in config file\n";
}
if (delete $c2{virtual} and $c{physical}) {
warn "$whoami: Warning: Ignoring --virtual in config file\n";
}
if (delete $c2{dell} and $c{virtual}) {
warn "$whoami: Warning: Ignoring --dell in config file\n";
}
if (delete $c3{physical} and $c{virtual}) {
warn "$whoami: Warning: Ignoring --physical in template\n";
}
if (delete $c3{virtual} and $c{physical}) {
warn "$whoami: Warning: Ignoring --virtual in template\n";
}
if (delete $c3{dell} and $c{virtual}) {
warn "$whoami: Warning: Ignoring --dell in template\n";
}
}
if ($c2{physical} or $c2{virtual}) {
if (delete $c3{physical} and $c{virtual}) {
warn "$whoami: Warning: Ignoring --physical in template\n";
}
if (delete $c3{virtual} and $c{physical}) {
warn "$whoami: Warning: Ignoring --virtual in template\n";
}
if (delete $c3{dell} and $c{virtual}) {
warn "$whoami: Warning: Ignoring --dell in template\n";
}
}
if ($c{ide} and $c{scsi}) {
die "$whoami: Can't specify both --ide and --scsi on command line\n";
}
if ($c2{ide} and $c2{scsi}) {
die "$whoami: Can't specify both --ide and --scsi in config file\n";
}
if ($c3{ide} and $c3{scsi}) {
die "$whoami: Internal error: can't specify both --ide and --scsi in template\n";
}
if ($c{dell} and $c{virtual}) {
die "$whoami: Can't specify both --dell and --virtual on command line\n";
}
if ($c2{dell} and $c2{virtual}) {
die "$whoami: Can't specify both --dell and --virtual in config file\n";
}
if ($c3{dell} and $c3{virtual}) {
die "$whoami: Internal error: can't specify both --dell and --virtual in template\n";
}
if ($c{hyperv}) {
$c{ide} = 1 if (! $c{scsi});
$c{virtual} = 1 if (! $c{physical});
$c{vmwaretools} = 0 if (! defined($c{vmwaretools}));
}
if ($c2{hyperv}) {
$c2{ide} = 1 if (! ($c{scsi} or $c2{scsi}));
$c2{virtual} = 1 if (! $c2{physical});
$c2{vmwaretools} = 0 if (! defined($c2{vmwaretools}));
}
if ($c3{hyperv}) {
$c3{ide} = 1 if (! ($c{scsi} or $c2{scsi} or $c3{scsi}));
$c3{virtual} = 1 if (! $c3{physical});
$c3{vmwaretools} = 0 if (! defined($c3{vmwaretools}));
}
if ($c{dell}) {
$c{physical} = 1;
}
if ($c2{dell}) {
$c2{physical} = 1;
}
if ($c3{dell}) {
$c3{physical} = 1;
}
if ($c{ide}) {
if ($c2{scsi}) {
delete $c2{scsi};
warn "$whoami: Ignoring --scsi in config file\n";
}
if ($c3{scsi}) {
delete $c3{scsi};
warn "$whoami: Ignoring --scsi in template\n";
}
}
if ($c2{ide}) {
if ($c3{scsi}) {
delete $c3{scsi};
warn "$whoami: Ignore --scsi in template\n";
}
}
if ($c{scsi}) {
if ($c2{ide}) {
delete $c2{ide};
warn "$whoami: Ignoring --ide in config file\n";
}
if ($c3{ide}) {
delete $c3{ide};
warn "$whoami: Ignoring --ide in template\n";
}
}
if ($c2{scsi}) {
if ($c3{ide}) {
delete $c3{ide};
warn "Ignoring --ide in template\n";
}
}
if (defined($c{vmwaretools})) {
if (defined($c2{vmwaretools}) and $c{vmwaretools} != $c2{vmwaretools}) {
delete $c2{vmwaretools};
warn "$whoami: Ignoring VMware tools setting in config file\n";
}
if (defined($c3{vmwaretools}) and $c{vmwaretools} != $c3{vmwaretools}) {
delete $c3{vmwaretools};
warn "$whoami: Ignoring VMware tools setting in template\n";
}
}
if (defined($c2{vmwaretools})) {
if (defined($c3{vmwaretools}) and $c2{vmwaretools} != $c3{vmwaretools}) {
delete $c3{vmwaretools};
warn "$whoami: Ignoring VMware tools setting in template\n";
}
}
foreach my $setting (keys %c) {
if (defined($c2{$setting})) {
delete $c2{$setting};
warn "$whoami: Warning: Overriding --$setting in config file from command line\n";
}
if (defined($c3{$setting})) {
delete $c3{$setting};
warn "$whoami: Warning: Overriding --$setting in template from command line\n";
}
}
foreach my $setting (keys %c2) {
if (defined($c3{$setting})) {
delete $c3{$setting};
warn "$whoami: Warning: Overriding --$setting in template from config file\n";
}
}
%c = (%c3, %c2, %c);
if (! defined($c{rootpassword})) {
$c{rootpassword} = 'be1nformed';
}
elsif (! $c{rootpassword}) {
die "$whoami: Can't set empty root password\n";
}
if (! defined($c{grubpassword})) {
$c{grubpassword} = $c{rootpassword};
}
if ($c{grubpassword} =~ /[\"\\]/) {
die "$whoami: Grub password can't contain \" or \\\n";
}
if ($c{dell} and $c{virtual}) {
die "$whoami: Can't specify both --dell and --virtual\n";
}
if ($c{physical} and $c{virtual}) {
die "$whoami: Can't specify both --physical and --virtual\n";
}
elsif (! ($c{physical} or $c{virtual})) {
die "$whoami: Must specify --physical or --virtual\n";
}
else {
$c{physical} = ! $c{virtual};
}
if ($c{virtual}) {
if (! defined($c{vmwaretools})) {
$c{vmwaretools} = 1;
}
}
else {
$c{vmwaretools} = 0;
}
if (! $c{ide}) {
$c{scsi} = 1;
}
if (! $c{isvrelease}) {
die "$whoami: Must specify ISV release with --isv-release\n";
}
elsif ($c{isvrelease} !~ /^(?:\d+.){6}\d+$/) {
die "$whoami: Invalid ISV release number: $c{isvrelease}\n";
}
if (! $c{netproto}) {
die "$whoami: Must specify --netproto=static|dhcp\n";
}
else {
$c{netproto} =~ tr/A-Z/a-z/;
if ($c{netproto} !~ /^(?:static|dhcp)$/) {
die "$whoami: invalid netproto \"$c{netproto}\"; should be \"static\" or \"dhcp\"\n";
}
}
if ($c{netproto} eq "static") {
foreach my $setting ("address", "netmask", "gateway") {
die "$whoami: Must specify --$setting with --netproto=static\n"
if (! $c{$setting});
}
if (! $c{nameserver}) {
warn "$whoami: Warning: No --nameserver specified; configuring without nameservers\n";
}
}
else {
foreach my $setting ("address", "netmask", "gateway", "nameserver") {
if ($c{$setting}) {
warn "$whoami: Warning: Ignoring --$setting for DHCP\n";
delete $c{$setting};
}
}
}
if (! $c{hostname}) {
die "$whoami: Must specify --hostname\n";
}
if (! &check_address($c{address})) {
die "$whoami: Invalid IP address $c{address}\n";
}
if (! &check_netmask($c{netmask})) {
die "$whoami: Invalid netmask $c{netmask}\n";
}
if (! &check_address($c{gateway})) {
die "$whoami: Invalid gateway IP address $c{gateway}\n";
}
if ($c{nameserver}) {
$c{nameserver} =~ s/\s+//g;
foreach my $nameserver (split(/,/, $c{nameserver})) {
if (! &check_address($nameserver)) {
die "$whoami: Invalid nameserver $nameserver\n";
}
}
}
sub check_address {
return 1 if (! $_[0]);
my(@nums) = split(/\./, $_[0]);
return 0 if (@nums != 4);
foreach my $num (@nums) {
return 0 if ($num !~ /^\d+$/);
return 0 if ($num > 255);
}
return 1;
}
sub check_netmask {
return 1 if (! $_[0]);
return 0 if (! &check_address($_[0]));
my(@nums) = split(/\./, $_[0]);
my $binary = sprintf("%b%b%b%b", @nums);
if ($binary =~ /0.*1/) {
warn "$whoami: Warning: Weird netmask $_[0]; please make sure it's correct\n";
}
return 1;
}
if ($c{centosiso} and ! -f $c{centosiso}) {
die "$whoami: $c{centosiso} does not exist\n";
}
if (! $c{rpmcache}) {
$c{rpmcache} = "/tmp/$whoami-cache";
}
if (! -d $c{rpmcache}) {
eval { mkpath($c{rpmcache}); };
if ($@) {
warn "$@";
die "$whoami: Error creating $c{rpmcache}\n";
}
}
if (! $c{appmanager}) {
my $ipconfig;
if ($^O eq "cygwin" or $^O eq "MSWin32") {
$ipconfig = `ipconfig`;
}
else {
$ipconfig = `ifconfig`;
}
if ($ipconfig =~ /\b(?:10\.2\.159|10\.5\.3[23])\./) {
$c{appmanager} = 'app-manager.bos.tamalesoftware.com';
}
else {
$c{appmanager} = $prod_app_manager;
}
warn "$whoami: Using appliance manager $c{appmanager}\n";
}
if (! $c{outputfile}) {
$c{outputfile} = "rms.iso";
warn "$whoami: Saving ISO as $c{outputfile}\n";
}
###
### END COMMAND-LINE AND CONFIGURATION-FILE OPTION PARSING
###
my $app_manager_url = &app_manager_url($c{appmanager});
#my $cs_scripts_url = &app_manager_url($prod_app_manager);
my $cs_scripts_url = $app_manager_url;
my $ua = LWP::UserAgent->new;
&verbose("Verifying that ISV release $c{isvrelease} exists...");
my $isv_response = &get_memory("$app_manager_url/isv/$c{isvrelease}/RPMs/");
&verbose(" done\n");
&verbose("Verifying that ISV release $c{isvrelease} is correct architecture...");
my $platform_response =
&get_memory("$app_manager_url/isv/$c{isvrelease}/RPMs/platform/RPMs/");
if ($platform_response->content =~ /\.x86_64\.rpm/) {
if (! $c{bit64}) {
die "$whoami: 64-bit ISV specified for 32-bit ISO\n";
}
}
elsif ($c{bit64}) {
#die "$whoami: 32-bit ISV specified for 64-bit ISO\n";
}
print "check 6 ";
&verbose(" done\n");
&verbose("Fetching CS scripts repository listing...");
my $cs_response = &get_memory("$cs_scripts_url/cs/mkrmsiso/");
print "check 7 ";
&verbose(" done\n");
# Fetch the CentOS ISO if necessary
if (! $c{centosiso}) {
print "check 8 ";
my $url = $centos_iso_urls{$c{bit64} ? "bit64" : "bit32"};
$c{centosiso} = &maybe_fetch($url);
}
# Download and collect names of needed RPMs
&verbose("Building RPM lists...");
print "check 9 ";
eval { rmtree($wd); };
mkpath($wd);
mkpath("$wd/platform-manager");
chdir("$wd/platform-manager")
or die "$whoami: chdir($wd/platform-manager: $!\n";
print "check 10 ";
my $pmfile = &maybe_fetch(&find_rpm_urls("platform-manager", $platform_response),
{always => 1});
system("rpm2cpio $pmfile | cpio --quiet --extract --make-directories &>/dev/null") and die;
my(@platform_rpms, @isv_rpms, @cs_scripts_rpms);
# Fetch the appropriate platform-manager-* RPM. Extract the file list
# from it and fetch all the matching RPMs.
sub get_rpms_from_manager_rpm {
local($_);
my($rpm, $rpm_file, $response) = @_;
my $url = &find_rpm_urls("$rpm", $response);
my $file = &maybe_fetch($url, {always => 1});
&debug("Extracting RPM list from $rpm RPM...");
system("rpm2cpio $file | cpio --extract --quiet --make-directories &>/dev/null")
and die;
open(LIST, "<", "./etc/rpmmgr/rpms.d/$rpm_file")
or die "$whoami: open($wd/platform-manager/etc/rpmmgr/rpms.d/$rpm_file): $!\n";
my(@rpms) = <LIST>;
chomp(@rpms);
@rpms = grep($_ ne "gpg-pubkey", @rpms);
close(LIST) or
die "$whoami: close($wd/platform-manager/etc/rpmmgr/rpms.d/$rpm_file): $!\n";
&debug(" done (@rpms)\n");
return(@rpms);
}
push(@platform_rpms, &get_rpms_from_manager_rpm("platform-manager-linux",
"linux", $platform_response));
push(@platform_rpms, &get_rpms_from_manager_rpm("platform-manager-tamaleserver",
"tamaleserver",
$platform_response));
if ($c{physical}) {
push(@platform_rpms, &get_rpms_from_manager_rpm("platform-manager-hardware",
"hardware",
$platform_response));
if($c{dell}){
push(@platform_rpms, &get_rpms_from_manager_rpm("platform-manager-hardware_dell",
"hardware_dell",
$platform_response));
}
}
else {
push(@platform_rpms, &get_rpms_from_manager_rpm("platform-manager-virtual",
"virtual",
$platform_response));
}
push(@cs_scripts_rpms, &get_rpms_from_manager_rpm("cs-scripts",
"cs-scripts.rpms",
$cs_response));
if ($c{emacs}) {
&debug("Adding emacs RPMs @emacs_rpms to platform RPM list\n");
push(@platform_rpms, @emacs_rpms);
}
if ($c{vmwaretools}) {
&debug("Adding VMwareTools dependencies @vmware_tools_rpms to RPM list\n");
push(@platform_rpms, @vmware_tools_rpms);
}
push(@isv_rpms, &get_rpms_from_manager_rpm("platform-manager-isv", "isv",
$isv_response));
&verbose(" done\n");
&verbose("Downloading RPMs...");
my @rpm_files;
print "check 11 ";
foreach my $rpm (@platform_rpms) {
push(@rpm_files, &maybe_fetch(&find_rpm_urls($rpm, $platform_response)));
}
print "check 12 ";
foreach my $rpm (@isv_rpms) {
push(@rpm_files, &maybe_fetch(&find_rpm_urls($rpm, $isv_response)));
}
print "check 13 ";
foreach my $rpm (@cs_scripts_rpms) {
push(@rpm_files, &maybe_fetch(&find_rpm_urls($rpm, $cs_response)));
# There might be a newer version in the platform
if (my(@urls) = &find_rpm_urls($rpm, $platform_response, {optional=>1})) {
push(@rpm_files, &maybe_fetch(@urls));
}
}
print "check 14 ";
if ($c{vmwaretools}) {
push(@rpm_files, &maybe_fetch("$app_manager_url/$vmware_tools_url_tail"));
}
print "check 15 ";
chdir("..") or die;
print "check 16 ";
if (! $c{preserve}) {
rmtree("$wd/platform-manager");
}
&verbose(" done\n");
# Build ISO tree
&verbose("Extracting needed files from CentOS ISO...");
mkpath("$wd/iso-root");
chdir("iso-root") or die;
print "check 17 ";
mkdir("CentOS") or die "$whoami: mkdir($wd/iso-root/CentOS): $!\n";
mkdir("repodata") or die "$whoami: mkdir($wd/iso-root/repodata): $!\n";
mkdir("gpg-keys") or die "$whoami: mkdir($wd/iso-root/gpg-keys): $!\n";
my(@wanted) = qw(.discinfo .treeinfo images/stage2.img isolinux
repodata/comps.xml);
if (! $c{dryrun}) {
if ($^O eq "linux") {
my $mp = "/mnt/CentOS";
system("mkdir -p $mp") and die;
print "check 18 ";
#print "<br>centosiso: " . $c{centosiso};
my $cmd = "sudo mount -o loop $c{centosiso} $mp";
print "<br>CMD: " . $cmd; # This line prints: sudo mount -o loop /tmp/test.pl-cache/CentOS-5.4-x86_64-bin-1of7.iso /mnt/CentOS
system("sudo mount -o loop $c{centosiso} $mp") and die;
print " check 18.1 ";
system("tar -C $mp -c @wanted | tar -x") and die;
print "check 19 ";
system("umount $mp") or die;
}
else {
my $zip = "/cygdrive/c/Program Files/7-Zip/7z.exe";
die "$whoami: Couldn't find $zip; do you have 7-Zip installed?\n"
if (! -f $zip);
chomp(my $wp = `cygpath -w $c{centosiso}`);
my $pid = fork;
die "$whoami: fork: $!\n" if (! defined($pid));
if (! $pid) {
close(STDOUT);
exec($zip, "x", "-y", $wp, @wanted) and die;
}
else {
waitpid($pid, 0);
die "$whoami: CentOS ISO extract with 7-Zip failed\n" if ($?);
}
}
}
&verbose(" done\n");
&verbose("Fixing isolinux configuration...");
if (! $c{dryrun}) {
my($match1, $match2, $match3, $match4);
#print exec("pwd");
print "check 20 ";
open(CFGIN, "<", "isolinux/isolinux.cfg") or die;
print "check 21 ";
open(CFGOUT, ">", "isolinux/isolinux.cfg.new") or die;
while (<CFGIN>) {
if (!$match1 && s/^default linux$/default ks/) {
$match1 = 1;
}
elsif (!$match2 && s/^timeout 600$/timeout 1/) {
$match2 = 1;
}
elsif (!$match3 && s/append ks /append ks=cdrom:\/ks.cfg /) {
$match3 = 1;
}
elsif (!$match4 && /^label ks$/) {
$match4 = 1;
}
print "check 17.3 ";
print(CFGOUT $_) or die;
}
print "check 17.4 ";
#close(CFGIN) or die;
print "check 17.5 ";
close(CFGOUT) or die;
print "check 17.6 ";
die "$whoami: isolinux/isolinux.cfg in unexpected format\n"
if (! ($match1 && $match2 && $match3 && $match4));
print "check 17.7 ";
rename("isolinux/isolinux.cfg.new", "isolinux/isolinux.cfg") or die;
}
print "check 18 "; die;
&verbose(" done\n");
&verbose("Putting RPMs into ISO root...");
if (! $c{dryrun}) {
foreach my $rpm_path (@rpm_files) {
my $target = "CentOS/" . basename($rpm_path);
if (! -f $target) {
symlink($rpm_path, $target)
or die "$whoami: symlink($rpm_path, $target: $!\n";
}
}
}
&verbose(" done\n");
&verbose("Putting GPG keys into ISO root...");
foreach my $key (@gpg_keys) {
&get_file("$app_manager_url/$key", "gpg-keys/$key");
}
&verbose(" done\n");
&verbose("Updating ISO yum metadata...");
if (! $c{dryrun}) {
foreach my $type (qw(filelists other primary)) {
my $xml = "repodata/$type.xml.gz";
&debug("Building $xml...");
my $fh = IO::Zlib->new($xml, "wb") or die;
my $first = 1;
my $last;
foreach my $repo ("$app_manager_url/isv/$c{isvrelease}",
"$app_manager_url/isv/$c{isvrelease}/RPMs/platform",
"$cs_scripts_url/cs/mkrmsiso") {
my $r = &get_memory("$repo/repodata/$type.xml.gz");
my $data = Compress::Zlib::memGunzip($r->content);
$data =~ s/(<[^>]+>\s*)$//;
$last = $1;
if ($first) {
$first = undef;
}
else {
$data =~ s/^(.*\n.*\n)//;
}
$data =~ s/(location href=\")(?:[^\"]+\/)?([^\"]+)/$1CentOS\/$2/g;
$fh->print($data) or die;
}
$fh->print($last) or die;
$fh->close or die;
&debug(" done\n");
}
my $xml = "repodata/repomd.xml";
&debug("Building $xml...");
open(XML, ">", $xml) or die;
print(XML "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<repomd xmlns=\"http://linux.duke.edu/metadata/repo\">\n") or die;
foreach my $type (qw(other filelists primary comps)) {
my $file = "repodata/$type.xml";
my $data_type = ($type eq "comps") ? "group" : $type;
my($compressed, $open_checksum);
if (! -f $file) {
$compressed = 1;
$file = "$file.gz";
}
my($timestamp) = (stat($file))[9];
my($checksum) = Digest::SHA->new(1)->addfile($file)->hexdigest
or die;
if ($compressed) {
open(UNCOMPRESSED, "-|", "zcat $file") or die;
$open_checksum = Digest::SHA->new(1)->addfile(*UNCOMPRESSED)->hexdigest
or die;
close(UNCOMPRESSED) or die;
}
print(XML " <data type=\"$data_type\">
<location href=\"$file\"/>
<checksum type=\"sha\">$checksum</checksum>
<timestamp>$timestamp</timestamp>\n") or die;
if ($compressed) {
print(XML " <open-checksum type=\"sha\">$open_checksum</open-checksum>\n") or die;
}
print (XML " </data>\n") or die;
}
print(XML "</repomd>\n") or die;
close(XML) or die;
&debug(" done\n");
}
&verbose(" done\n");
&verbose("Creating kickstart configuration file...");
open(KS, ">", "ks.cfg") or die;
print(KS "# Firewall configuration
firewall --disabled
# Install OS instead of upgrade
install
# Root password
rootpw $c{rootpassword}
# Network information\n") or die;
if ($c{netproto} eq "static") {
print(KS "network --bootproto=static --device=eth0 --gateway=$c{gateway} --ip=$c{address} ", ($c{nameserver} ? "--nameserver=$c{nameserver} " : ""), "--netmask=$c{netmask} --noipv6 --onboot=on --hostname=$c{hostname}\n") or die;
}
else {
print(KS "network --bootproto=dhcp --device=eth0 --onboot=on --hostname=$c{hostname}\n") or die;
}
my $divider;
if ($c{hyperv}) {
$divider = "--append divider=10";
}
else {
$divider = "";
}
print(KS "# System authorization information
auth --useshadow --passalgo=md5
# Use text mode install
text
# System keyboard
keyboard us
# System language
lang en_US
# SELinux configuration
selinux --disabled
# Do not configure the X Window System
skipx
# Installation logging level
logging --level=info
# Use CDROM installation media
cdrom
# Halt after installation
halt
# System timezone
# See Red Hat Bugzilla #481617
timezone --utc America/New_York
# System bootloader configuration
bootloader --location=mbr $divider") or die;
if ($c{grubpassword}) {
print(KS " --password=\"$c{grubpassword}\"") or die;
}
print(KS "
# Partition clearing information
clearpart --all
zerombr
# Disk partitioning information\n") or die;
my $hd_prefix;
if ($c{scsi}) {
$hd_prefix = "sd";
}
else {
$hd_prefix = "hd";
}
if ($c{virtual}) {
print(KS "part /boot --asprimary --fstype=\"ext3\" --ondisk=${hd_prefix}a --size=75
part / --asprimary --fstype=\"ext3\" --ondisk=${hd_prefix}a --size=4096
part /tmp --asprimary --fstype=\"ext2\" --grow --ondisk=${hd_prefix}a --size=1
part swap --fstype=\"swap\" --ondisk=${hd_prefix}a --size=2048
part /home --asprimary --fstype=\"ext3\" --grow --ondisk=${hd_prefix}b --size=1
part /var --asprimary --fstype=\"ext3\" --grow --ondisk=${hd_prefix}c --size=1\n") or die;
}
else {
print(KS "part /boot --asprimary --fstype=\"ext3\" --ondisk=${hd_prefix}a --size=75
part /var --asprimary --fstype=\"ext3\" --ondisk=${hd_prefix}a --size=39936
part /tmp --asprimary --fstype=\"ext2\" --ondisk=${hd_prefix}a --size=50176
part swap --fstype=\"swap\" --ondisk=${hd_prefix}a --size=2048
part / --fstype=\"ext3\" --ondisk=${hd_prefix}a --size=4096
part /home --fstype=\"ext3\" --grow --ondisk=${hd_prefix}a --size=1\n") or die;
}
print(KS "%packages --nobase\n") or die;
map {
print(KS "$_\n") or die;
} @platform_rpms, @isv_rpms, @cs_scripts_rpms;
print(KS "%pre
# See Red Hat Bugzilla #502936
ifconfig lo up
# See Red Hat Bugzilla #383531
chmod 666 /dev/null
hostname $c{hostname} # otherwise host_manager.sh will refuse to run
cp -r /mnt/source/gpg-keys /tmp\n") or die;
if ($c{vmwaretools}) {
print(KS "cp /mnt/source/CentOS/VMwareTools*.rpm /tmp\n") or die;
}
print(KS "# Needed later by expect, see Red Hat Bugzilla #502980
\(while [ ! -d /mnt/sysimage/dev/pts ]; do sleep 5; done; mount -t devpts devpts /mnt/sysimage/dev/pts\) &
%post --nochroot
exec >/mnt/sysimage/root/install-post.log 2>&1
# This has to be done outside of a chroot because otherwise the files
# aren't available to import.
for file in /tmp/gpg-keys/*; do
rpm --root /mnt/sysimage --import \$file
done\n\n") or die;
if ($c{vmwaretools}) {
print(KS "# This has to be done outside of a chroot because otherwise the file
# isn't available to copy.
cp /tmp/VMwareTools*.rpm /mnt/sysimage/root\n") or die;
}
print(KS "\n# Do the rest inside a chroot.
chroot /mnt/sysimage /bin/bash <<\\CHROOTEOF
# See Red Hat Bugzilla #481617
ln -f /usr/share/zoneinfo/Etc/UTC /etc/localtime
perl -i -e '\$/ = undef; \$_ = <>;
s,America/New_York,Etc/UTC, or warn \"Could not update time zone in \$ARGV\\n\";
print;' /etc/sysconfig/clock
# Otherwise rpm_manager.sh will complain that gpg-pubkey isn't installed
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5\n\n") or die;
if ($c{emacs}) {
print(KS "# Make sure emacs will not get uninstalled
cat > /etc/rpmmgr/rpms.d/emacs <<EOF\n", join("\n", @emacs_rpms), "\nEOF\n")
or die;
}
if ($c{vmwaretools}) {
print(KS "# Make sure VMwareTools won't get uninstalled
echo VMwareTools > /etc/rpmmgr/rpms.d/VMwareTools
# Gross but effective -- configure VMware Tools at next reboot, before
# the network is brought up.
VMTC=/etc/rc.d/rc3.d/S09VMwareToolsConfig
cat > \$VMTC <<EOF
#!/bin/bash -e
rpm -U /root/VMwareTools*.rpm
/usr/bin/vmware-config-tools.pl -d
/usr/sbin/vmware-guestd --cmd 'vmx.set_option synctime 0 1'
/usr/sbin/rpm_manager.sh --nodebug --nonagios || :
/bin/rm -f /root/VMwareTools*.rpm \$VMTC
EOF
chmod +x \$VMTC\n") or die;
}
print(KS "RCLOCAL=/etc/rc.d/rc.local
cat > \$RCLOCAL.ts <<EOF
#!/bin/sh
exec >/root/rc.local.postinstall.log 2>&1
set -x
# Put things here that we want to run just once the first time the appliance
# is booted after it is installed.\n\n") or die;
if ($c{boston}) {
print(KS "perl -i -e '\\\$/ = undef; \\\$_ = <>; s/download01/app-manager.bos/; print;' \\
/etc/yum.repos.d/rms.repo\n") or die;
}
print(KS "# Definitely necessary for DHCP, not always necessary for static, but
# doesn't hurt and might help to ensure that things are in a
# consistent state.
/usr/sbin/host_manager.sh
# Prevent NSCA spool file from having incorrect permissions.
python -c 'import shelve; db = shelve.open(\"/var/spool/send_nsca_spool/nscaspool.db\", \"c\");'
chown nagios /var/spool/send_nsca_spool/nscaspool.db
# Run the original rc.local and move it back into place.
\. \$RCLOCAL.post
mv -f \$RCLOCAL.post \$RCLOCAL
EOF
mv -f \$RCLOCAL \$RCLOCAL.post
mv -f \$RCLOCAL.ts \$RCLOCAL
chmod +x \$RCLOCAL
CHROOTEOF\n") or die;
close(KS) or die;
&verbose(" done\n");
&verbose("Creating ISO\n");
chdir($dir) or die;
if (! $c{dryrun}) {
chmod('0755', "isolinux/isolinux.bin");
system("mkisofs", "-f", "-R", "-J", "-T", "-r", "-l", "-d", "-o",
$c{outputfile}, "-b", "isolinux/isolinux.bin", "-c",
"isolinux/boot.cat", "-no-emul-boot", "--boot-load-size", "4",
"-boot-info-table", "$wd/iso-root") and die;
}
# Clean up
if (! $c{preserve}) {
&verbose("Cleaning up...");
rmtree("$wd");
&verbose(" done\n");
}
&verbose("\nDone!\n");
###
### UTILITY FUNCTIONS
###
# Display a message if running in verbose mode
sub verbose {
my(@message) = @_;
return if (! $c{verbose});
print(STDERR @message);
}
# Display a message if running in debug mode
sub debug {
my(@message) = @_;
return if (! $c{debug});
print(STDERR @message);
}
# Fetch a URL into memory
sub get_memory {
my($url) = @_;
&debug("Fetching $url into memory...");
my $response = $ua->get($url);
if (! $response->is_success) {
die "$whoami: Failed to fetch $url\n";
}
&debug(" done\n");
return $response;
}
# Fetch a URL into a file
sub get_file {
my($url, $file) = @_;
my $response = $ua->get($url, ":content_file" => $file);
if (! $response->is_success) {
die "$whoami: Failed to fetch $url into $file\n";
}
return $response;
}
# Fetch files if we don't already have them with the correct size.
# Also, update the timestamps on the files to the current time even if
# we don't fetch them (so that they won't get cleaned up
# automatically). Returns the paths of the files.
sub maybe_fetch {
my(@urls) = @_;
my(%o);
if (ref $urls[-1] eq 'HASH') {
%o = %{pop @urls};
}
my(@files);
foreach my $url (@urls) {
my $file = $c{rpmcache} . "/" . basename($url);
push(@files, $file);
if ($c{fetch}) {
my $bytes;
my $head = $ua->head($url);
if ($head->is_success) {
$bytes = $head->header('content-length');
if (-f $file) {
&debug("Checking if $url is same size as $file...");
if ($bytes == -s $file) {
&debug(" yes; refreshing timestamp\n");
utime(time(), time(), $file);
next;
}
else {
&debug(" no\n");
unlink($file);
}
}
}
else {
&debug("Failed to fetch header for $url\n");
}
&verbose("Fetching $url into $file", ($bytes ? " ($bytes bytes)" : ""),
"...");
&get_file($url, $file) if ($o{always} || ! $c{dryrun});
&verbose(" done\n");
}
}
return wantarray ? @files : $files[0];
}
# Find the URL of a particular RPM in the listing in the specified
# HTTP response
sub find_rpm_urls {
my($rpm, $response, $o) = @_;
my(%o) = $o ? %{$o} : ();
my $base = $response->base->as_string;
my $rpm_re = $rpm;
$rpm_re =~ s/(\W)/\\$1/g;
my(@basenames) = ($response->content =~ /\"($rpm_re-[^-\"]+-[^-\"]+\.[^.\"]+\.rpm)\"/g);
if (! (@basenames || $o{optional})) {
print "$whoami: Couldn't find RPM $rpm on appliance manager\n";
}
my(@urls) = map($base . $_, @basenames);
return wantarray ? @urls : (@urls ? $urls[0] : undef);
}
# Get the appliance manager base URL for a particular appliance manager host
sub app_manager_url {
"https://tamaleserver:gyqsdi34wesd\@$_[0]/yum";
}
print " last check";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment