summaryrefslogtreecommitdiffstats
path: root/src/boot-env/OpenSLX/BootEnvironment
diff options
context:
space:
mode:
Diffstat (limited to 'src/boot-env/OpenSLX/BootEnvironment')
-rw-r--r--src/boot-env/OpenSLX/BootEnvironment/Base.pm160
-rw-r--r--src/boot-env/OpenSLX/BootEnvironment/PBS.pm247
-rw-r--r--src/boot-env/OpenSLX/BootEnvironment/PXE.pm336
-rw-r--r--src/boot-env/OpenSLX/BootEnvironment/Preboot.pm209
-rw-r--r--src/boot-env/OpenSLX/BootEnvironment/Preboot/Base.pm111
-rw-r--r--src/boot-env/OpenSLX/BootEnvironment/Preboot/CD.pm155
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;