diff options
Diffstat (limited to 'src/boot-env/OpenSLX/BootEnvironment')
-rw-r--r-- | src/boot-env/OpenSLX/BootEnvironment/Base.pm | 160 | ||||
-rw-r--r-- | src/boot-env/OpenSLX/BootEnvironment/PBS.pm | 247 | ||||
-rw-r--r-- | src/boot-env/OpenSLX/BootEnvironment/PXE.pm | 336 | ||||
-rw-r--r-- | src/boot-env/OpenSLX/BootEnvironment/Preboot.pm | 209 | ||||
-rw-r--r-- | src/boot-env/OpenSLX/BootEnvironment/Preboot/Base.pm | 111 | ||||
-rw-r--r-- | src/boot-env/OpenSLX/BootEnvironment/Preboot/CD.pm | 155 |
6 files changed, 1218 insertions, 0 deletions
diff --git a/src/boot-env/OpenSLX/BootEnvironment/Base.pm b/src/boot-env/OpenSLX/BootEnvironment/Base.pm new file mode 100644 index 00000000..aa4cbe5b --- /dev/null +++ b/src/boot-env/OpenSLX/BootEnvironment/Base.pm @@ -0,0 +1,160 @@ +# Copyright (c) 2008 - OpenSLX GmbH +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# BootEnvironment::Base.pm +# - provides empty base of the BootEnvironment API. +# ----------------------------------------------------------------------------- +package OpenSLX::BootEnvironment::Base; + +use strict; +use warnings; + +our $VERSION = 1.01; # API-version . implementation-version + +use Clone qw(clone); +use File::Basename; +use File::Path; + +use OpenSLX::Basics; +use OpenSLX::ConfigDB; +use OpenSLX::MakeInitRamFS::Engine::SlxBoot; +use OpenSLX::Utils; + +our %initramfsMap; + +sub new +{ + my $class = shift; + + my $self = {}; + + return bless $self, $class; +} + +sub initialize +{ + my $self = shift; + my $params = shift; + + $self->{'dry-run'} = $params->{'dry-run'}; + + return 1; +} + +sub finalize +{ + my $self = shift; + my $delete = shift; + + return 1 if $self->{'dry-run'}; + + my $rsyncDeleteClause = $delete ? '--delete' : ''; + my $rsyncCmd + = "rsync -a $rsyncDeleteClause --delay-updates $self->{'target-path'}/ $self->{'original-path'}/"; + slxsystem($rsyncCmd) == 0 + or die _tr( + "unable to rsync files from '%s' to '%s'! (%s)", + $self->{'target-path'}, $self->{'original-path'}, $! + ); + rmtree([$self->{'target-path'}]); + + return 1; +} + +sub requiresDefaultClientConfig +{ + my $self = shift; + + return $self->{'requires-default-client-config'}; +} + +sub writeBootloaderMenuFor +{ + my $self = shift; + my $client = shift; + my $externalClientID = shift; + my $systemInfos = shift; + + return; +} + +sub writeFilesRequiredForBooting +{ + my $self = shift; + my $info = shift; + my $buildPath = shift; + + my $kernelFile = $info->{'kernel-file'}; + my $kernelName = basename($kernelFile); + + my $vendorOSPath = "$self->{'target-path'}/$info->{'vendor-os'}->{name}"; + mkpath $vendorOSPath unless -e $vendorOSPath || $self->{'dry-run'}; + + my $targetKernel = "$vendorOSPath/$kernelName"; + if (!-e $targetKernel) { + vlog(1, _tr('copying kernel %s to %s', $kernelFile, $targetKernel)); + slxsystem(qq[cp -p "$kernelFile" "$targetKernel"]) + unless $self->{'dry-run'}; + } + + # create initramfs: + my $initramfsName = "$vendorOSPath/$info->{'initramfs-name'}"; + vlog(1, _tr('generating initialramfs %s', $initramfsName)); + $self->_makeInitRamFS($info, $initramfsName); + return 1; +} + +sub _makeInitRamFS +{ + my $self = shift; + my $info = shift; + my $initramfs = shift; + + my $vendorOS = $info->{'vendor-os'}; + my $kernelFile = basename(followLink($info->{'kernel-file'})); + + my $attrs = clone($info->{attrs} || {}); + + chomp(my $slxVersion = qx{slxversion}); + + my $params = { + 'attrs' => $attrs, + 'export-name' => $info->{export}->{name}, + 'export-uri' => $info->{'export-uri'}, + 'initramfs' => $initramfs, + 'kernel-params' + => [ split ' ', ($info->{attrs}->{kernel_params} || '') ], + 'kernel-version' => $kernelFile =~ m[-(.+)$] ? $1 : '', + 'plugins' => $info->{'active-plugins'}, + 'root-path' + => "$openslxConfig{'private-path'}/stage1/$vendorOS->{name}", + 'slx-version' => $slxVersion, + 'system-name' => $info->{name}, + }; + + # TODO: make debug-level an explicit attribute, it's used in many places! + my $kernelParams = $info->{attrs}->{kernel_params} || ''; + if ($kernelParams =~ m{debug(?:=(\d+))?}) { + my $debugLevel = defined $1 ? $1 : '1'; + $params->{'debug-level'} = $debugLevel; + } + + my $makeInitRamFSEngine + = OpenSLX::MakeInitRamFS::Engine::SlxBoot->new($params); + $makeInitRamFSEngine->execute($self->{'dry-run'}); + + # copy back kernel-params, as they might have been changed (by plugins) + $info->{attrs}->{kernel_params} + = join ' ', $makeInitRamFSEngine->kernelParams(); + + return; +} + +1; diff --git a/src/boot-env/OpenSLX/BootEnvironment/PBS.pm b/src/boot-env/OpenSLX/BootEnvironment/PBS.pm new file mode 100644 index 00000000..2072884b --- /dev/null +++ b/src/boot-env/OpenSLX/BootEnvironment/PBS.pm @@ -0,0 +1,247 @@ +# Copyright (c) 2008 - OpenSLX GmbH +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# BootEnvironment::Preboot.pm +# - provides general preboot implementation of the BootEnvironment API. +# ----------------------------------------------------------------------------- +package OpenSLX::BootEnvironment::PBS; + +use strict; +use warnings; + +use base qw(OpenSLX::BootEnvironment::Base); + +use OpenSLX::MakeInitRamFS::Engine::PBS; + +use Clone qw(clone); +use File::Basename; +use File::Path; + +use Data::Dumper; + +use JSON; +use HTTP::Request::Common; +use LWP::UserAgent; + +use OpenSLX::Basics; +use OpenSLX::ConfigDB qw(:support); +use OpenSLX::Utils; + +sub initialize +{ + my $self = shift; + my $params = shift; + + return if !$self->SUPER::initialize($params); + + $self->{'original-path'} = "$openslxConfig{'public-path'}/pbs"; + $self->{'target-path'} = "$openslxConfig{'public-path'}/pbs.new"; + + $self->{'requires-default-client-config'} = 0; + # we do not need a default.tgz since there's always an explicit client + + + if (!$self->{'dry-run'}) { + mkpath([$self->{'original-path'}]); + rmtree($self->{'target-path'}); + mkpath("$self->{'target-path'}/client-config"); + } + + return 1; +} + +sub writeBootloaderMenuFor +{ + my $self = shift; + my $client = shift; + my $externalClientID = shift; + my $systemInfos = shift || []; + + my $prebootSystemInfo + = clone($self->_pickSystemWithNewestKernel($systemInfos)); + + vlog( + 0, + _tr( + "\nsend preboot information for client '%s' to pbs (%s)\n". + " (image templates provided based of %s) ...", + $client->{name}, $client->{attrs}->{preboot_server}, $prebootSystemInfo->{name} + ) + ); + + $self->_createPrebootStuff($client, $prebootSystemInfo); + + my $kernel = "$self->{'target-path'}/imagebase/vmlinuz"; + my $initramfs = "$self->{'target-path'}/imagebase/initramfs"; + + my $kernel_md5 = qx/md5sum $kernel | awk '{print \$1}'/; + my $initramfs_md5 = qx/md5sum $initramfs | awk '{print \$1}'/; + + my $data_json = to_json({ + 'slxinfo' => qx/slxversion/, + 'kernel' => basename($prebootSystemInfo->{'kernel-file'}), + 'kernel_md5' => trim($kernel_md5), + 'initramfs_md5' => trim($initramfs_md5), + 'systems' => $systemInfos + }); + my $ua = LWP::UserAgent->new; + my $res = $ua->request(POST 'http://pbs.experimental.openslx.org/backend/system/sync', [data => $data_json]); + + if ($res->is_success) { + my $resData = from_json($res->content); + if ($resData->{'getKernel'} eq 'fresh') { + $res = $ua->request(POST 'http://pbs.experimental.openslx.org/backend/system/addkernel', + ['kernel' => basename($prebootSystemInfo->{'kernel-file'}), + 'kernelFile' => ["$self->{'target-path'}/imagebase/vmlinuz"], + 'initramfsFile' => ["$self->{'target-path'}/imagebase/initramfs"], + ], + 'Content_Type' => 'form-data' + ); + + print Dumper($res->content); + + } else { + if ($resData->{'getKernel'} eq 'update') { + $res = $ua->request(POST 'http://pbs.experimental.openslx.org/backend/system/updatekernel', + ['kernel' => basename($prebootSystemInfo->{'kernel-file'}), + 'kernelFile' => ["$self->{'target-path'}/imagebase/vmlinuz"], + ], + 'Content_Type' => 'form-data' + ); + + print Dumper($res->content); + } else { + # do nothing + } + if ($resData->{'getInitramfs'} eq 'update') { + $res = $ua->request(POST 'http://pbs.experimental.openslx.org/backend/system/updateinitramfs', + ['kernel' => basename($prebootSystemInfo->{'kernel-file'}), + 'initramfsFile' => ["$self->{'target-path'}/imagebase/initramfs"], + ], + 'Content_Type' => 'form-data' + ); + + print Dumper($res->content); + } else { + # do nothing + } + } + } else { + vlog(0, 'communication with pbs failed.. please check and rerun..'); + } + + return 1; +} + +sub _createPrebootStuff +{ + my $self = shift; + my $client = shift; + my $info = shift; + + my $prebootClass = instantiateClass( + "OpenSLX::BootEnvironment::Preboot::Base" + ); + + my $imagebase = "$self->{'target-path'}/imagebase"; + + $prebootClass->initialize($self); + $client->{attrs}->{boot_uri} = $client->{attrs}->{preboot_server}; + mkpath("$imagebase"); + $self->_makePBSInitRamFS($info, "$imagebase/initramfs", $client); + + my $kernelFile = $info->{'kernel-file'}; + my $kernelName = basename($kernelFile); + slxsystem(qq{cp -p "$kernelFile" "$imagebase/vmlinuz"}) + unless $self->{'dry-run'}; + + return 1; +} + +sub _pickSystemWithNewestKernel +{ + my $self = shift; + my $systemInfos = shift; + + my $systemWithNewestKernel; + my $newestKernelFileSortKey = ''; + foreach my $system (@$systemInfos) { + next unless $system->{'kernel-file'} =~ m{ + (?:vmlinuz|x86)-(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?-(\d+(?:\.\d+)?) + }x; + my $sortKey + = sprintf("%02d.%02d.%02d.%02d-%2.1f", $1, $2, $3, $4||0, $5); + if ($newestKernelFileSortKey lt $sortKey) { + $systemWithNewestKernel = $system; + $newestKernelFileSortKey = $sortKey; + } + } + + if (!defined $systemWithNewestKernel) { + die _tr("unable to pick a system to be used for preboot!"); + } + return $systemWithNewestKernel; +} + +sub _makePBSInitRamFS +{ + my $self = shift; + my $info = shift; + my $initramfs = shift; + my $client = shift; + + my $vendorOS = $info->{'vendor-os'}; + my $kernelFile = basename(followLink($info->{'kernel-file'})); + + my $attrs = clone($info->{attrs} || {}); + + my $bootURI = $client->{attrs}->{boot_uri}; + if (!$bootURI) { + die _tr("client $client->{name} needs an URI in attribute 'boot_uri' to be used for preboot!"); + } + + chomp(my $slxVersion = qx{slxversion}); + + my $params = { + 'attrs' => $attrs, + 'export-name' => undef, + 'export-uri' => undef, + 'initramfs' => $initramfs, + 'kernel-params' + => [ split ' ', ($info->{attrs}->{kernel_params} || '') ], + 'kernel-version' => $kernelFile =~ m[-(.+)$] ? $1 : '', + 'plugins' => '', + 'root-path' + => "$openslxConfig{'private-path'}/stage1/$vendorOS->{name}", + 'slx-version' => $slxVersion, + 'system-name' => $info->{name}, + 'preboot-id' => $client->{name}, + 'boot-uri' => $bootURI, + }; + + # TODO: make debug-level an explicit attribute, it's used in many places! + my $kernelParams = $info->{attrs}->{kernel_params} || ''; + if ($kernelParams =~ m{debug(?:=(\d+))?}) { + my $debugLevel = defined $1 ? $1 : '1'; + $params->{'debug-level'} = $debugLevel; + } + + my $makeInitRamFSEngine + = OpenSLX::MakeInitRamFS::Engine::PBS->new($params); + $makeInitRamFSEngine->execute($self->{'dry-run'}); + + # copy back kernel-params, as they might have been changed (by plugins) + $info->{attrs}->{kernel_params} + = join ' ', $makeInitRamFSEngine->kernelParams(); + + return; +} + +1; diff --git a/src/boot-env/OpenSLX/BootEnvironment/PXE.pm b/src/boot-env/OpenSLX/BootEnvironment/PXE.pm new file mode 100644 index 00000000..d46786d0 --- /dev/null +++ b/src/boot-env/OpenSLX/BootEnvironment/PXE.pm @@ -0,0 +1,336 @@ +# Copyright (c) 2008..2009 - OpenSLX GmbH +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# BootEnvironment::PXE.pm +# - provides PXE-specific implementation of the BootEnvironment API. +# ----------------------------------------------------------------------------- +package OpenSLX::BootEnvironment::PXE; + +use strict; +use warnings; + +use base qw(OpenSLX::BootEnvironment::Base); + +use File::Basename; +use File::Path; +# for sha1 passwd encryption +use Digest::SHA1; +use MIME::Base64; + +use OpenSLX::Basics; +use OpenSLX::Utils; + +sub initialize +{ + my $self = shift; + my $params = shift; + + return if !$self->SUPER::initialize($params); + + $self->{'original-path'} = "$openslxConfig{'public-path'}/tftpboot"; + $self->{'target-path'} = "$openslxConfig{'public-path'}/tftpboot.new"; + + $self->{'requires-default-client-config'} = 1; + + if (!$self->{'dry-run'}) { + mkpath([$self->{'original-path'}]); + rmtree($self->{'target-path'}); + mkpath("$self->{'target-path'}/client-config"); + } + + return 1; +} + +sub writeBootloaderMenuFor +{ + my $self = shift; + my $client = shift; + my $externalClientID = shift; + my $systemInfos = shift; + + $self->_prepareBootloaderConfigFolder() + unless $self->{preparedBootloaderConfigFolder}; + + my $pxePath = $self->{'target-path'}; + my $pxeConfigPath = "$pxePath/pxelinux.cfg"; + + my $pxeConfig = $self->_getTemplate(); + my $pxeFile = "$pxeConfigPath/$externalClientID"; + my $clientAppend = $client->{attrs}->{kernel_params_client} || ''; + my $bootURI = $client->{attrs}->{boot_uri} || ''; + vlog(1, _tr("writing PXE-file %s", $pxeFile)); + + # set label for each system + foreach my $info (@$systemInfos) { + my $label = $info->{label} || ''; + if (!length($label) || $label eq $info->{name}) { + if ($info->{name} =~ m{^(.+)::(.+)$}) { + my $system = $1; + my $exportType = $2; + $label = $system . ' ' x (40-length($system)) . $exportType; + } else { + $label = $info->{name}; + } + } + $info->{menuLabel} = $label; + } +# if kernel=*xen* then run sub _xenLabel from xen.pm + my $slxLabels = ''; + foreach my $info (sort { $a->{label} cmp $b->{label} } @$systemInfos) { + my $vendorOSName = $info->{'vendor-os'}->{name}; + my $kernelName = basename($info->{'kernel-file'}); + my $append = $info->{attrs}->{kernel_params}; + my $pxeLabel = $info->{'external-id'}; + $pxeLabel =~ s/::/-/g; + my $pxePrefix = ''; + my $tftpPrefix = ''; + $info->{'pxe_prefix_ip'} ||= ''; + + # pxe_prefix_ip set and looks like a ip + if ($info->{'pxe_prefix_ip'} =~ m/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) { + $pxePrefix = "$info->{'pxe_prefix_ip'}::"; + $tftpPrefix = "tftp://$info->{'pxe_prefix_ip'}" + if ! length($bootURI); + } + + # set default menu entry + my $pxeDefault = ""; + if (defined $openslxConfig{'pxe-default-menu-entry'}) { + if ($openslxConfig{'pxe-default-menu-entry'} eq + $info->{'external-id'}) + { + $pxeDefault = "\tMENU DEFAULT\n"; + } + } + $append .= " initrd=$pxePrefix$vendorOSName/$info->{'initramfs-name'}"; + $append .= " file=$bootURI" if length($bootURI); + $append .= " file=$tftpPrefix" if length($tftpPrefix); + $append .= " $clientAppend"; + $slxLabels .= "LABEL openslx-$pxeLabel\n"; + $slxLabels .= $pxeDefault; + $slxLabels .= "\tMENU LABEL ^$info->{menuLabel}\n"; + $slxLabels .= "\tKERNEL $pxePrefix$vendorOSName/$kernelName\n"; + $slxLabels .= "\tAPPEND $append\n"; + $slxLabels .= "\tIPAPPEND 3\n"; +# if kernel=*xen* then run sub _xenBootEntry from xen.pm +# if (!defined $xenKernel) {...} + my $helpText = $info->{description} || ''; + if (length($helpText)) { + # make sure that text matches the given margin + my $menuMargin; + while ($pxeConfig =~ m{^\s*MENU MARGIN (\S+?)\s*$}gims) { + chomp($menuMargin = $1); + } + my $margin + = defined $menuMargin + ? "$menuMargin" + : "0"; + my $marginAsText = ' ' x $margin; + + my $menuWidth; + while ($pxeConfig =~ m{^\s*MENU WIDTH (\S+?)\s*$}gims) { + chomp($menuWidth = $1); + } + my $width + = defined $menuWidth + ? "$menuWidth" + : "80"; + $width = $width - 2* $margin + 2; + + my @atomicHelpText = split(/ /, $helpText); + my $lineCounter = 0; + + $helpText = ""; + + foreach my $word (@atomicHelpText){ + if ($lineCounter + length($word) + 1 < $width) { + $helpText .= "$word "; + $lineCounter += length($word) + 1; + } else { + my $nobreak = 1; + while ($nobreak == 1) { + my $pos = index($word,"-"); + $nobreak = 0; + if ($pos != -1) { + if ($lineCounter + $pos + 1 < $width) { + $helpText .= substr($word, 0, $pos+1); + $word = substr($word, $pos + 1, length($word)); + $nobreak = 1; + } + } + } + $helpText .= "\n$word "; + $lineCounter = length($word); + } + } + + $helpText =~ s{^}{$marginAsText}gms; + $slxLabels .= "\tTEXT HELP\n"; + $slxLabels .= "$helpText\n"; + $slxLabels .= "\tENDTEXT\n"; + } + } + # now add the slx-labels (inline or appended) and write the config file + if (!($pxeConfig =~ s{\@\@\@SLX_LABELS\@\@\@}{$slxLabels})) { + $pxeConfig .= $slxLabels; + # fetch PXE-bottom iclude, if exists (overwrite existing definitions) + my $pxeBottomFile + = "$openslxConfig{'config-path'}/boot-env/syslinux/pxemenu-bottom"; + if (-e $pxeBottomFile) { + $pxeConfig .= "\n# configuration from include $pxeBottomFile\n"; + $pxeConfig .= slurpFile($pxeBottomFile); + } + } + + # PXE uses 'cp850' (codepage 850) but our string is in utf-8, we have + # to convert in order to avoid showing gibberish on the client side... + spitFile($pxeFile, $pxeConfig, { 'io-layer' => 'encoding(cp850)' } ) + unless $self->{'dry-run'}; + + return 1; +} + +sub _getTemplate +{ + my $self = shift; + + return $self->{'pxe-template'} if $self->{'pxe-template'}; + + my $basePath = $openslxConfig{'base-path'}; + my $configPath = $openslxConfig{'config-path'}; + my $pxeTheme = $openslxConfig{'syslinux-theme'}; + + my ($sec, $min, $hour, $day, $mon, $year) = (localtime); + $mon++; + $year += 1900; + my $callDate = sprintf('%04d-%02d-%02d', $year, $mon, $day); + my $callTime = sprintf('%02d:%02d:%02d', $hour, $min, $sec); + + # generate PXE-Menu + my $pxeTemplate = + "# generated by slxconfig-demuxer (on $callDate at $callTime)\n"; + $pxeTemplate .= "\nDEFAULT vesamenu.c32\n"; + # include static defaults + $pxeTemplate .= "\n# static configuration (override with include file)\n"; + $pxeTemplate .= "NOESCAPE 0\n"; + $pxeTemplate .= "PROMPT 0\n"; + + # first check for theme + # let user stuff in config path win over our stuff in base path + my $pxeThemePath; + my $pxeThemeInConfig + = "$configPath/boot-env/syslinux/themes/${pxeTheme}"; + my $pxeThemeInBase + = "$basePath/share/boot-env/syslinux/themes/${pxeTheme}"; + if (-e "$pxeThemeInConfig/theme.conf") { + $pxeThemePath = $pxeThemeInConfig; + } + else { + if (-e "$pxeThemeInBase/theme.conf") { + $pxeThemePath = $pxeThemeInBase; + } + } + # include theme specific stuff + if (defined $pxeThemePath) { + $pxeTemplate .= "\n# theme specific configuration from $pxeThemePath\n"; + $pxeTemplate .= slurpFile("$pxeThemePath/theme.conf"); + } + + # copy background picture if exists + my $pic; + if (defined $pxeTheme) { + while ($pxeTemplate =~ m{^\s*MENU BACKGROUND (\S+?)\s*$}gims) { + chomp($pic = $1); + } + } + if (defined $pic) { + my $pxeBackground = "$pxeThemePath/$pic"; + if (-e $pxeBackground && !$self->{'dry-run'}) { + slxsystem(qq[cp "$pxeBackground" $self->{'target-path'}/]); + } + } + + # include slxsettings + $pxeTemplate .= "\n# slxsettings configuration\n"; + $pxeTemplate .= "TIMEOUT $openslxConfig{'pxe-timeout'}\n" || ""; + $pxeTemplate .= "TOTALTIMEOUT $openslxConfig{'pxe-totaltimeout'}\n" || ""; + my $sha1pass = $self->_sha1pass($openslxConfig{'pxe-passwd'}); + $pxeTemplate .= "MENU MASTER PASSWD $sha1pass\n" || ""; + $pxeTemplate .= "MENU TITLE $openslxConfig{'pxe-title'}\n" || ""; + + # fetch PXE-include, if exists (overwrite existing definitions) + my $pxeIncludeFile + = "$openslxConfig{'config-path'}/boot-env/syslinux/pxemenu-include"; + if (-e $pxeIncludeFile) { + $pxeTemplate .= "\n# configuration from include $pxeIncludeFile\n"; + $pxeTemplate .= slurpFile($pxeIncludeFile); + } + + $pxeTemplate .= "\n# slxsystems:\n"; + $self->{'pxe-template'} = $pxeTemplate; + + return $pxeTemplate; +} + +sub _prepareBootloaderConfigFolder +{ + my $self = shift; + + my $basePath = $openslxConfig{'base-path'}; + my $pxePath = $self->{'target-path'}; + my $pxeConfigPath = "$pxePath/pxelinux.cfg"; + + if (!$self->{'dry-run'}) { + rmtree($pxeConfigPath); + mkpath($pxeConfigPath); + + for my $file ('pxelinux.0', 'pxechain.com', 'vesamenu.c32', + 'mboot.c32', 'kernel-shutdown', 'initramfs-shutdown') { + if (!-e "$pxePath/$file") { + slxsystem( + qq[cp -p "$basePath/share/boot-env/syslinux/$file" $pxePath/] + ); + } + } + } + + $self->{preparedBootloaderConfigFolder} = 1; + + return 1; +} + +# from syslinux 3.73: http://syslinux.zytor.com +sub _random_bytes +{ + my $self = shift; + my $n = shift; + my($v, $i); + + # using perl rand because of problems with encoding(cp850) and 'bytes' + srand($$ ^ time); + $v = ''; + for ( $i = 0 ; $i < $n ; $i++ ) { + $v .= ord(int(rand() * 256)); + } + + return $v; +} + +sub _sha1pass +{ + my $self = shift; + my $pass = shift; + my $salt = shift || MIME::Base64::encode($self->_random_bytes(6), ''); + $pass = Digest::SHA1::sha1_base64($salt, $pass); + + return sprintf('$4$%s$%s$', $salt, $pass); +} + +1; diff --git a/src/boot-env/OpenSLX/BootEnvironment/Preboot.pm b/src/boot-env/OpenSLX/BootEnvironment/Preboot.pm new file mode 100644 index 00000000..b06de7d2 --- /dev/null +++ b/src/boot-env/OpenSLX/BootEnvironment/Preboot.pm @@ -0,0 +1,209 @@ +# Copyright (c) 2008 - OpenSLX GmbH +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# BootEnvironment::Preboot.pm +# - provides general preboot implementation of the BootEnvironment API. +# ----------------------------------------------------------------------------- +package OpenSLX::BootEnvironment::Preboot; + +use strict; +use warnings; + +use base qw(OpenSLX::BootEnvironment::Base); + +use Clone qw(clone); +use File::Basename; +use File::Path; + +use OpenSLX::Basics; +use OpenSLX::ConfigDB qw(:support); +use OpenSLX::Utils; + +sub initialize +{ + my $self = shift; + my $params = shift; + + return if !$self->SUPER::initialize($params); + + $self->{'original-path'} = "$openslxConfig{'public-path'}/preboot"; + $self->{'target-path'} = "$openslxConfig{'public-path'}/preboot.new"; + + $self->{'requires-default-client-config'} = 0; + # we do not need a default.tgz since there's always an explicit client + + if (!$self->{'dry-run'}) { + mkpath([$self->{'original-path'}]); + rmtree($self->{'target-path'}); + mkpath("$self->{'target-path'}/client-config"); + } + + return 1; +} + +sub writeBootloaderMenuFor +{ + my $self = shift; + my $client = shift; + my $externalClientID = shift; + my $systemInfos = shift || []; + + $self->_prepareBootloaderConfigFolder() + unless $self->{preparedBootloaderConfigFolder}; + + my $prebootSystemInfo + = clone($self->_pickSystemWithNewestKernel($systemInfos)); + + $self->_createImages($client, $prebootSystemInfo); + + my $externalClientName = externalConfigNameForClient($client); + my $bootloaderPath = "$self->{'target-path'}/bootloader"; + my $bootloaderConfigPath = "$bootloaderPath/$externalClientName"; + mkpath($bootloaderConfigPath) unless $self->{'dry-run'}; + my $menuFile = "$bootloaderConfigPath/bootmenu.dialog"; + + my $clientAppend = $client->{attrs}->{kernel_params_client} || ''; + vlog(1, _tr("writing bootmenu %s", $menuFile)); + + # set label for each system + foreach my $info (@$systemInfos) { + my $label = $info->{label} || ''; + if (!length($label) || $label eq $info->{name}) { + $label = $info->{name}; + } + $info->{label} = $label; + } + my $bootmenuEntries = ''; + my $entryState = 'on'; + my $counter = 1; + foreach my $info (sort { $a->{label} cmp $b->{label} } @$systemInfos) { + my $vendorOSName = $info->{'vendor-os'}->{name}; + my $kernelName = basename($info->{'kernel-file'}); + my $append = $info->{attrs}->{kernel_params} || ''; + $append .= " $clientAppend"; + $bootmenuEntries .= qq{ "$counter" "$info->{label}" }; + $entryState = 'off'; + + # create a file containing the boot-configuration for this system + my $systemDescr = unshiftHereDoc(<<" End-of-Here"); + label="$info->{label}" + kernel="$vendorOSName/$kernelName" + initramfs="$vendorOSName/$info->{'initramfs-name'}" + append="$append" + End-of-Here + my $systemFile = "$bootloaderConfigPath/$info->{name}"; + spitFile( + $systemFile, $systemDescr, { 'io-layer' => 'encoding(iso8859-1)' } + ) unless $self->{'dry-run'}; + slxsystem(qq{ln -sf $info->{name} $bootloaderConfigPath/$counter}); + $counter++; + } + + my $entryCount = @$systemInfos; + my $bootmenu = unshiftHereDoc(<<" End-of-Here"); + --no-cancel --menu "OpenSLX Boot Menu" 20 65 $entryCount $bootmenuEntries + End-of-Here + + if (!$self->{'dry-run'}) { + # default to iso encoding, let's see how uclibc copes with it ... + spitFile($menuFile, $bootmenu, { 'io-layer' => 'encoding(iso8859-1)' }); + + # copy the preboot script into the folder to be tared + my $prebootBasePath + = "$openslxConfig{'base-path'}/share/boot-env/preboot"; + slxsystem(qq{cp $prebootBasePath/preboot.sh $bootloaderConfigPath/}); + slxsystem(qq{cp -r $prebootBasePath/preboot-scripts $bootloaderConfigPath/}); + slxsystem(qq{chmod a+x $bootloaderConfigPath/preboot.sh}); + + # create a tar which can/will be downloaded by prebooting clients + my $tarCMD + = qq{cd $bootloaderConfigPath; tar -czf "${bootloaderConfigPath}.env" *}; + slxsystem($tarCMD); + rmtree($bootloaderConfigPath); + } + + return 1; +} + +sub _createImages +{ + my $self = shift; + my $client = shift; + my $info = shift; + + my %mediaMap = ( + 'cd' => 'CD', + ); + my $prebootMedia = $client->{attrs}->{preboot_media} || ''; + if (!$prebootMedia) { + warn _tr( + "no preboot-media defined for client %s, no images will be generated!", + $client->{name} + ); + return 0; + } + foreach my $mediumName (split m{, }, $prebootMedia) { + my $moduleName = $mediaMap{$mediumName} + or die _tr( + "'%s' is not one of the supported preboot-medias (cd)", + $mediumName + ); + + my $prebootMedium = instantiateClass( + "OpenSLX::BootEnvironment::Preboot::$moduleName" + ); + $prebootMedium->initialize($self); + $prebootMedium->createImage($client, $info); + } + + return 1; +} + +sub _prepareBootloaderConfigFolder +{ + my $self = shift; + + my $bootloaderPath = "$self->{'target-path'}/bootloader"; + if (!$self->{'dry-run'}) { + rmtree($bootloaderPath); + mkpath($bootloaderPath); + } + + $self->{preparedBootloaderConfigFolder} = 1; + + return 1; +} + +sub _pickSystemWithNewestKernel +{ + my $self = shift; + my $systemInfos = shift; + + my $systemWithNewestKernel; + my $newestKernelFileSortKey = ''; + foreach my $system (@$systemInfos) { + next unless $system->{'kernel-file'} =~ m{ + (?:vmlinuz|x86)-(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?-(\d+(?:\.\d+)?) + }x; + my $sortKey + = sprintf("%02d.%02d.%02d.%02d-%2.1f", $1, $2, $3, $4||0, $5); + if ($newestKernelFileSortKey lt $sortKey) { + $systemWithNewestKernel = $system; + $newestKernelFileSortKey = $sortKey; + } + } + + if (!defined $systemWithNewestKernel) { + die _tr("unable to pick a system to be used for preboot!"); + } + return $systemWithNewestKernel; +} + +1; diff --git a/src/boot-env/OpenSLX/BootEnvironment/Preboot/Base.pm b/src/boot-env/OpenSLX/BootEnvironment/Preboot/Base.pm new file mode 100644 index 00000000..89f0e07e --- /dev/null +++ b/src/boot-env/OpenSLX/BootEnvironment/Preboot/Base.pm @@ -0,0 +1,111 @@ +# Copyright (c) 2008-2009 - OpenSLX GmbH +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# BootEnvironment::Preboot::Base.pm +# - base of the Preboot-BootEnvironment API. +# ----------------------------------------------------------------------------- +package OpenSLX::BootEnvironment::Preboot::Base; + +use strict; +use warnings; + +use File::Basename; + +use Clone qw(clone); + +use OpenSLX::Basics; +use OpenSLX::MakeInitRamFS::Engine::Preboot; +use OpenSLX::Utils; + +sub new +{ + my $class = shift; + + my $self = {}; + + return bless $self, $class; +} + +sub initialize +{ + my $self = shift; + my $params = shift; + + $self->{'dry-run'} = $params->{'dry-run'}; + + return 1; +} + +sub makePrebootInitRamFS +{ + my $self = shift; + my $info = shift; + my $initramfs = shift; + my $client = shift; + + my $vendorOS = $info->{'vendor-os'}; + my $kernelFile = basename(followLink($info->{'kernel-file'})); + + my $attrs = clone($info->{attrs} || {}); + + my $bootURI = $client->{attrs}->{boot_uri}; + if (!$bootURI) { + die _tr("client $client->{name} needs an URI in attribute 'boot_uri' to be used for preboot!"); + } + + chomp(my $slxVersion = qx{slxversion}); + + my $params = { + 'attrs' => $attrs, + 'export-name' => undef, + 'export-uri' => undef, + 'initramfs' => $initramfs, + 'kernel-params' + => [ split ' ', ($info->{attrs}->{kernel_params} || '') ], + 'kernel-version' => $kernelFile =~ m[-(.+)$] ? $1 : '', + 'plugins' => '', + 'root-path' + => "$openslxConfig{'private-path'}/stage1/$vendorOS->{name}", + 'slx-version' => $slxVersion, + 'system-name' => $info->{name}, + 'preboot-id' => $client->{name}, + 'boot-uri' => $bootURI, + }; + + # TODO: make debug-level an explicit attribute, it's used in many places! + my $kernelParams = $info->{attrs}->{kernel_params} || ''; + if ($kernelParams =~ m{debug(?:=(\d+))?}) { + my $debugLevel = defined $1 ? $1 : '1'; + $params->{'debug-level'} = $debugLevel; + } + + my $makeInitRamFSEngine + = OpenSLX::MakeInitRamFS::Engine::Preboot->new($params); + $makeInitRamFSEngine->execute($self->{'dry-run'}); + + # copy back kernel-params, as they might have been changed (by plugins) + $info->{attrs}->{kernel_params} + = join ' ', $makeInitRamFSEngine->kernelParams(); + + return; +} + +sub createImage +{ + my $self = shift; + my $client = shift; + my $info = shift; + + # override in subclasses! + + return 1; +} + +1; diff --git a/src/boot-env/OpenSLX/BootEnvironment/Preboot/CD.pm b/src/boot-env/OpenSLX/BootEnvironment/Preboot/CD.pm new file mode 100644 index 00000000..a6c36cd7 --- /dev/null +++ b/src/boot-env/OpenSLX/BootEnvironment/Preboot/CD.pm @@ -0,0 +1,155 @@ +# Copyright (c) 2008-2009 - OpenSLX GmbH +# +# This program is free software distributed under the GPL version 2. +# See http://openslx.org/COPYING +# +# If you have any feedback please consult http://openslx.org/feedback and +# send your suggestions, praise, or complaints to feedback@openslx.org +# +# General information about OpenSLX can be found at http://openslx.org/ +# ----------------------------------------------------------------------------- +# BootEnvironment::Preboot::CD.pm +# - provides CD-specific implementation of the Preboot-BootEnvironment API. +# ----------------------------------------------------------------------------- +package OpenSLX::BootEnvironment::Preboot::CD; + +use strict; +use warnings; + +use base qw(OpenSLX::BootEnvironment::Preboot::Base); + +use File::Basename; +use File::Path; + +use OpenSLX::Basics; +use OpenSLX::Utils; + +sub createImage +{ + my $self = shift; + my $client = shift; + my $info = shift; + + vlog( + 0, + _tr( + "\ncreating CD-image for client %s (based on %s) ...", + $client->{name}, $info->{name} + ) + ); + + my $imageDir = "$openslxConfig{'public-path'}/images/$client->{name}/cd"; + my $isoDir = "$imageDir/iso/isolinux"; + mkpath($isoDir) unless $self->{'dry-run'}; + + # copy static data + my $dataDir = "$openslxConfig{'base-path'}/share/boot-env/syslinux"; + for my $file ('LICENSE', 'README.iso', 'vesamenu.c32', 'isolinux.bin') { + if (!-e "$isoDir/$file") { + slxsystem( + qq[cp -p "$dataDir/$file" "$isoDir/"] + ) + unless $self->{'dry-run'}; + } + } + + # copy kernel (take the one from the given system info) + my $kernelFile = $info->{'kernel-file'}; + my $kernelName = basename($kernelFile); + slxsystem(qq{cp -p "$kernelFile" "$isoDir/vmlinuz"}) + unless $self->{'dry-run'}; + + # create initramfs + my $initramfsName = qq{"$isoDir/initramfs"}; + $self->makePrebootInitRamFS($info, $initramfsName, $client); + + # write trivial isolinux config + # include static defaults + my $isolinuxConfig = "DEFAULT vesamenu.c32\n"; + $isolinuxConfig .= "PROMPT 0\n"; + $isolinuxConfig .= "TIMEOUT 100\n"; + + # theme stuff + my $basePath = $openslxConfig{'base-path'}; + my $configPath = $openslxConfig{'config-path'}; + my $isoTheme = $openslxConfig{'syslinux-theme'}; + + my $isoThemePath; + my $isoThemeInConfig + = "$configPath/boot-env/syslinux/themes/${isoTheme}"; + my $isoThemeInBase + = "$basePath/share/boot-env/syslinux/themes/${isoTheme}"; + if (-e "$isoThemeInConfig/theme.conf") { + $isoThemePath = $isoThemeInConfig; + } + else { + if (-e "$isoThemeInBase/theme.conf") { + $isoThemePath = $isoThemeInBase; + } + } + # include theme specific stuff + if (defined $isoThemePath) { + $isolinuxConfig .= slurpFile("$isoThemePath/theme.conf"); + } + + # copy background picture if exists + my $pic; + if (defined $isoTheme) { + while ($isolinuxConfig =~ m{^\s*MENU BACKGROUND (\S+?)\s*$}gims) { + chomp($pic = $1); + } + } + if (defined $pic) { + my $isoBackground = "$isoThemePath/$pic"; + if (-e $isoBackground && !$self->{'dry-run'}) { + slxsystem(qq[cp "$isoBackground" "$isoDir/"]); + } + } + + # write trivial isolinux config + $isolinuxConfig .= unshiftHereDoc(<<" End-of-Here"); + MENU TITLE Welcome to OpenSLX PreBoot ISO/CD (Mini Linux/Kexec) + LABEL SLXSTDBOOT + MENU LABEL OpenSLX PreBoot - Stateless Netboot Linux ... + MENU DEFAULT + KERNEL vmlinuz + APPEND initrd=initramfs vga=0x317 + TEXT HELP + Use this (default) entry if you have configured your client. + You have chance to edit the kernel commandline by hitting + the TAB key (e.g. for adding debug=3 to it for bug hunting). + ENDTEXT + LABEL LOCALBOOT + MENU LABEL Boot locally (skip OpenSLX PreBoot) ... + LOCALBOOT -1 + TEXT HELP + Gets you out of here by booting from next device in BIOS + boot order. + ENDTEXT + End-of-Here + spitFile("$isoDir/isolinux.cfg", $isolinuxConfig); + + my $mkisoCmd = unshiftHereDoc(<<" End-of-Here"); + mkisofs + -o "$imageDir/../$client->{name}.iso" + -b isolinux/isolinux.bin -no-emul-boot -boot-load-size 4 + -r -J -l -boot-info-table -joliet-long + -publisher "OpenSLX Project - http://www.openslx.org" + -p "OpenSLX Project - openslx-devel\@openslx.org" + -V "OpenSLX BootISO" + -volset "OpenSLX Project - PreBoot ISO/CD for non PXE/TFTP start of a Linux Stateless Client" + -c isolinux/boot.cat "$imageDir/iso" + End-of-Here + $mkisoCmd =~ s{\n\s*}{ }gms; + my $logFile = "$imageDir/../$client->{name}.iso.log"; + if (slxsystem(qq{$mkisoCmd 2>"$logFile"})) { + my $log = slurpFile($logFile); + die _tr("unable to create ISO-image - log follows:\n%s", $log); + } + + rmtree($imageDir); + + return 1; +} + +1; |