summaryrefslogtreecommitdiffstats
path: root/src/config-db/slxconfig-demuxer
diff options
context:
space:
mode:
authorSebastian Schmelzer2010-09-02 17:50:49 +0200
committerSebastian Schmelzer2010-09-02 17:50:49 +0200
commit416ab8a37f1b07dc9f6c0fb3ff1a8ff2036510b5 (patch)
tree4715f7d742fec50931017f38fe6ff0a89d4ceccc /src/config-db/slxconfig-demuxer
parentFix for the problem reported on the list (sed filter forgotten for the (diff)
downloadcore-416ab8a37f1b07dc9f6c0fb3ff1a8ff2036510b5.tar.gz
core-416ab8a37f1b07dc9f6c0fb3ff1a8ff2036510b5.tar.xz
core-416ab8a37f1b07dc9f6c0fb3ff1a8ff2036510b5.zip
change dir structure
Diffstat (limited to 'src/config-db/slxconfig-demuxer')
-rwxr-xr-xsrc/config-db/slxconfig-demuxer918
1 files changed, 918 insertions, 0 deletions
diff --git a/src/config-db/slxconfig-demuxer b/src/config-db/slxconfig-demuxer
new file mode 100755
index 00000000..b88efeb6
--- /dev/null
+++ b/src/config-db/slxconfig-demuxer
@@ -0,0 +1,918 @@
+#! /usr/bin/perl
+# -----------------------------------------------------------------------------
+# Copyright (c) 2006, 2007 - 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/
+# -----------------------------------------------------------------------------
+# slxconfig-demuxer
+# - OpenSLX configuration demultiplexer
+# -----------------------------------------------------------------------------
+use strict;
+use warnings;
+use Switch;
+
+
+my $abstract = q[
+slxconfig-demuxer
+ This script will read information about all systems, clients and
+ groups from the OpenSLX configuration database, mix & match the individual
+ configurational attributes and then demultiplex the resulting information
+ to a set of configuration files. These files are used by any OpenSLX-client
+ during boot to find out which systems to offer for booting.
+
+ The resulting files will be put into the OpenSLX-tftpboot-path.
+
+ Please use the --man option in order to read the full manual.
+];
+
+use Config::General;
+use Digest::MD5 qw(md5_hex);
+use File::Basename;
+use File::Find;
+use File::Path;
+use List::Util qw(first);
+use Getopt::Long qw(:config pass_through);
+use Pod::Usage;
+
+# add the lib-folder and the folder this script lives in to perl's search
+# path for modules:
+use FindBin;
+use lib "$FindBin::RealBin/../lib";
+use lib "$FindBin::RealBin";
+# development path to config-db stuff
+
+use OpenSLX::Basics;
+use OpenSLX::ConfigDB qw(:support);
+use OpenSLX::ConfigFolder;
+use OpenSLX::OSPlugin::Roster;
+use OpenSLX::Utils;
+
+my (
+ $systemConfCount,
+ # number of system configurations written
+ $systemErrCount,
+ # number of systems that had errors
+ $bootEnvErrCount,
+ # number of boot environments that had errors
+ $clientSystemConfCount,
+ # number of (system-specific) client configurations written
+ $initramfsCount,
+ # number of initramfs that were created
+ @targetSystems,
+ # systems to create initramfs for, defaults to all systems
+ %bootEnvMap,
+ # objects encapsulating the bootloader specific configurations
+ %option,
+ # cmdline option hash
+);
+
+if ($> != 0) {
+ die _tr("Sorry, this script can only be executed by the superuser!\n");
+}
+
+GetOptions(
+ 'dhcp-export-type=s' => \$option{dhcpType},
+ 'dry-run' => \$option{dryRun},
+ 'help|?' => \$option{helpReq},
+ 'man' => \$option{manReq},
+ 'version' => \$option{versionReq},
+ )
+ or pod2usage(2);
+pod2usage(-msg => $abstract, -verbose => 0, -exitval => 1) if $option{helpReq};
+if ($option{manReq}) {
+ # avoid dubious problem with perldoc in combination with UTF-8 that
+ # leads to strange dashes and single-quotes being used
+ $ENV{LC_ALL} = 'POSIX';
+ pod2usage(-verbose => 2);
+}
+if ($option{versionReq}) {
+ slxsystem('slxversion');
+ exit 1;
+}
+
+openslxInit();
+
+my $openslxDB = OpenSLX::ConfigDB->new();
+$openslxDB->connect();
+
+my $clientConfigPath = "$openslxConfig{'private-path'}/config";
+# make sure that the default config folders exist:
+if (createConfigFolderForDefaultSystem()) {
+ # this path should have been generated by earlier stage (slxsettings), so
+ # we indicate that there is some kind of problem:
+ warn _tr(
+ "Completed client-config-folder '%s', since at least some parts of it didn't exist!",
+ $clientConfigPath
+ );
+}
+
+# protect against parallel execution of this script
+my $demuxerLock = grabLock('slxconfig-demuxer');
+
+my $tempPath = "$openslxConfig{'temp-path'}/slxconfig-demuxer";
+if (!$option{dryRun}) {
+ rmtree($tempPath);
+ mkpath($tempPath);
+ if (!-d $tempPath) {
+ die _tr("Unable to create or access temp-path '%s'!", $tempPath);
+ }
+}
+
+my $deleteInFinalize = 0;
+
+my @demuxableSystems
+ = grep { $_->{name} ne '<<<default>>>' } $openslxDB->fetchSystemByFilter();
+if (@ARGV) {
+ # create initramfs only for systems given on cmdline
+ for my $systemName (@ARGV) {
+ if ($systemName eq '<<<default>>>') {
+ warn _tr(
+ 'The default-system can not be demuxed - it will be skipped.'
+ );
+ next;
+ }
+ my $system = first { $_->{name} eq $systemName } @demuxableSystems;
+ if (!$system) {
+ warn _tr(
+ 'The system "%s" is unknown and will be ignored.', $systemName
+ );
+ next;
+ }
+ push @targetSystems, $system;
+ }
+}
+else {
+ # create initramfs for all systems
+ @targetSystems = @demuxableSystems;
+ $deleteInFinalize = 1;
+}
+
+writeConfigurations();
+
+my $wr = $option{dryRun} ? 'would have written' : 'wrote';
+my $errCount = $systemErrCount ? $systemErrCount : 'no';
+my $systemStatusString
+ = $systemErrCount ? "$errCount system(s) had errors" : 'all systems ok';
+$errCount = $bootEnvErrCount ? $bootEnvErrCount : 'no';
+my $bootEnvStatusString
+ = $bootEnvErrCount
+ ? "$errCount boot environment(s) had errors"
+ : 'all boot-environments ok';
+print "\n", unshiftHereDoc(<<"End-of-Here");
+ $wr $systemConfCount system-specific and $clientSystemConfCount client-specific configurations
+ $initramfsCount initramfs were created
+ $systemStatusString
+ $bootEnvStatusString
+End-of-Here
+
+$openslxDB->disconnect();
+
+rmtree([$tempPath]);
+
+# allow all boot-environments to clean up and active the new configuration
+foreach my $bootEnv (values %bootEnvMap) {
+ $bootEnv->finalize($deleteInFinalize);
+}
+
+exit;
+
+################################################################################
+###
+################################################################################
+sub folderContainsFiles
+{
+ my $folder = shift;
+
+ return 0 unless -d $folder;
+
+ my $result = 0;
+ my $wanted = sub {
+ if ($result) {
+ # skip anything else if we have found a file already
+ $File::Find::prune = 1;
+ }
+ $result = 1 if -f;
+ };
+ find({wanted => $wanted, follow_fast => 1}, $folder);
+ vlog(2, "result for folderContainsFiles($folder): $result\n");
+ return $result;
+}
+
+sub digestAttributes
+{ # returns a digest-string for the given attribute hash, in order to
+ # facilitate comparing different attribute hashes.
+ my $object = shift;
+
+ my $attrs = $object->{attrs} || {};
+ my $attrsAsString
+ = join ';',
+ map { "$_=$attrs->{$_}" }
+ sort
+ grep { defined $attrs->{$_} }
+ keys %$attrs;
+
+ vlog(3, "Attribute-string: $attrsAsString");
+ return md5_hex($attrsAsString);
+}
+
+sub writeAttributesToFile
+{
+ my $object = shift;
+ my $fileName = shift;
+
+ return if $option{dryRun};
+
+ my $content = "# attributes set by slxconfig-demuxer:\n";
+ my $attrs = $object->{attrs} || {};
+ # filter out any plugin-specific attributes (we only want to handle
+ # the attributes relevant to the core here)
+ my @attrs = sort grep { index($_, '::') == -1 } keys %$attrs;
+ foreach my $attr (@attrs) {
+ my $attrVal = $attrs->{$attr};
+ next if !defined $attrVal;
+ $content .= qq[$attr="$attrVal"\n];
+ }
+ # Overwrite attribute file even if it exists, to make sure that our users
+ # will never try to fiddle with machine-setup directly in the file-system.
+ # The config-DB is the keeper of that info!
+ spitFile($fileName, $content);
+ if ($openslxConfig{'log-level'} > 2) {
+ vlog(0, "--- START OF $fileName ---");
+ vlog(0, $content);
+ vlog(0, "--- END OF $fileName --- ");
+ }
+ return;
+}
+
+sub writeSlxConfigToFile
+{
+ my $slxConf = shift;
+ my $fileName = shift;
+
+ return if $option{dryRun};
+
+ my $content = '';
+ foreach my $key (sort keys %$slxConf) {
+ $content .= qq[$key="$slxConf->{$key}"\n];
+ }
+ spitFile($fileName, $content);
+ return;
+}
+
+sub copyExternalSystemConfig
+{ # copies local configuration extensions of given system from private
+ # config folder (var/lib/openslx/config/...) into a temporary folder
+ my $systemName = shift;
+ my $targetPath = shift;
+ my $clientName = shift; # optional
+
+ if ($targetPath !~ m[^$tempPath]) {
+ # bail if target-path isn't within temp folder, as we do not dare
+ # executing 'rm -rf' in that case!
+ die _tr("system-error: illegal target-path <%s>!", $targetPath);
+ }
+ return if $option{dryRun};
+
+ slxsystem("rm -rf $targetPath");
+ mkpath $targetPath;
+
+ # first copy default files ...
+ my $defaultConfigPath = "$clientConfigPath/default";
+ vlog(2, "checking $defaultConfigPath for default config...");
+ if (-d $defaultConfigPath) {
+ slxsystem("cp -a $defaultConfigPath/* $targetPath");
+ }
+ # ... now pour system-specific configuration on top (if any) ...
+ my $systemSpecConfigPath = "$clientConfigPath/$systemName/default";
+ vlog(2, "checking $systemSpecConfigPath for system config...");
+ if (folderContainsFiles($systemSpecConfigPath)) {
+ slxsystem("cp -a $systemSpecConfigPath/* $targetPath");
+ }
+ if (defined $clientName) {
+ # ... and finally pour client-specific configuration on top (if any):
+ my $clientSpecConfigPath = "$clientConfigPath/$systemName/$clientName";
+ vlog(2, "checking $clientSpecConfigPath for client config...");
+ if (folderContainsFiles($clientSpecConfigPath)) {
+ slxsystem("cp -a $clientSpecConfigPath/* $targetPath");
+ }
+ }
+ return;
+}
+
+sub createTarOfPath
+{
+ my $buildPath = shift;
+ my $tarName = shift;
+ my $destinationPath = shift;
+
+ my $tarFile = "$destinationPath/$tarName";
+ vlog(1, _tr('creating tar %s', $tarFile));
+ return if $option{dryRun};
+
+ mkpath $destinationPath;
+ my $tarCmd = "cd $buildPath && tar czf $tarFile *";
+ if (slxsystem("$tarCmd") != 0) {
+ die _tr("unable to execute shell-command:\n\t%s \n\t(%s)", $tarCmd, $!);
+ }
+}
+
+sub bootEnvironmentForType
+{
+ my $bootTypeIn = shift || 'pxe';
+
+ my %bootTypeMap = (
+ 'pxe' => 'PXE',
+ 'preboot' => 'Preboot',
+ 'pbs' => 'PBS',
+ );
+ my $bootType = $bootTypeMap{lc($bootTypeIn)}
+ or die _tr(
+ "'%s' is not one of the supported boot-types (pxe,preboot)",
+ $bootTypeIn
+ );
+
+ if (!$bootEnvMap{$bootType}) {
+ my $bootEnv = instantiateClass("OpenSLX::BootEnvironment::$bootType");
+ $bootEnv->initialize( {
+ 'dry-run' => $option{dryRun},
+ } );
+ $bootEnvMap{$bootType} = $bootEnv;
+ }
+
+ return $bootEnvMap{$bootType};
+}
+
+
+
+################################################################################
+###
+################################################################################
+sub writeBootloaderMenus
+{
+ my @infos = @_;
+
+ # iterate over all clients and write a bootloader configuration for each
+ my @clients = $openslxDB->fetchClientByFilter();
+ foreach my $client (@clients) {
+ # fetch all infos relevant to this client (including the bootable
+ # systems)
+ my %systemIDs;
+ @systemIDs{$openslxDB->aggregatedSystemIDsOfClient($client)} = ();
+ my @systemInfos = grep { exists $systemIDs{$_->{id}} } @infos;
+
+ # now write bootloader menu with all bootable systems for this client
+ my $bootEnv = bootEnvironmentForType($client->{attrs}->{boot_type});
+ my $externalID = externalIDForClient($client);
+ my $success = eval {
+ $bootEnv->writeBootloaderMenuFor(
+ $client, $externalID, \@systemInfos
+ );
+ 1;
+ };
+ if (!$success) {
+ print STDERR $@;
+ $bootEnvErrCount++;
+ }
+ }
+ return;
+}
+
+sub writeDhcpConfig
+{
+ vlog(0, _tr("sorry, exporting dhcp data is not implemented yet!"));
+ my $dhcpModule = "OpenSLX::ConfigExport::DHCP::$option{dhcpType}";
+ if (!eval { require $dhcpModule } ) {
+ die _tr("unable to load DHCP-Export backend '%s'! (%s)\n",
+ $dhcpModule, $@);
+ }
+ my $dhcpBackend = $dhcpModule->new();
+ my @clients = $openslxDB->fetchClientByFilter();
+ $dhcpBackend->execute(\@clients);
+ return;
+}
+
+sub writeClientConfigurationsForSystem
+{
+ my $info = shift;
+ my $buildPath = shift;
+ my $attrFile = shift;
+ my $bootType = shift;
+ my $clients = shift || [];
+
+ foreach my $client (@$clients) {
+ next if $client->{name} eq '<<<default>>>';
+ # skip default client, as it doesn't need any config-tgz
+
+ next if ($client->{attrs}->{boot_type} || 'pxe') ne $bootType;
+ # skip clients with non-matching boot type
+
+ my $externalSystemID = $info->{'external-id'};
+ my $externalClientName = externalConfigNameForClient($client);
+ my $clientConfigPath
+ = "$clientConfigPath/$externalSystemID/$externalClientName";
+
+ # merge configurations of groups, default client and system into the
+ # current client (overwriting only values the client does not specify)
+ $openslxDB->mergeDefaultAndGroupAttributesIntoClient($client);
+ mergeAttributes($client, $info);
+
+ # compute a digest value of the merged attributes ...
+ my $clientAttrDigest = digestAttributes($client);
+ vlog(
+ 2,
+ _tr(
+ "attribute-digest for client '%s' is '%s'", $client->{name},
+ $clientAttrDigest
+ )
+ );
+ # ... and export client-specific config only if attributes are different
+ # from system and/or a client-specific config-folder exists:
+ if ($clientAttrDigest ne $info->{'attr-digest'}
+ || -d $clientConfigPath)
+ {
+ vlog(
+ 1,
+ _tr(
+ "creating config-tgz for client %d:%s", $client->{id},
+ $client->{name}
+ )
+ );
+ $clientSystemConfCount++;
+
+ # merge default, system and client configuration folders into
+ # a configuration folder specific to the current client:
+ copyExternalSystemConfig(
+ $externalSystemID, $buildPath, $externalClientName
+ );
+
+ # add plugin configuration and note if the client adds any active
+ # plugin (as opposed to current state)
+ my $activeClientPlugins = writePluginConfigurations(
+ $info, $buildPath, $client->{attrs}
+ );
+ my @additionalActivePlugins = grep {
+ my $activeClientPlugin = $_;
+ ! grep {
+ $activeClientPlugin eq $_
+ } @{$info->{'active-plugins'}};
+ } @$activeClientPlugins;
+ if (@additionalActivePlugins) {
+ push @{$info->{'active-plugins'}}, @additionalActivePlugins;
+ my $additionalActivePluginStr
+ = join ',', @additionalActivePlugins;
+ vlog(0, _tr(
+ "client '%s' activates additional plugins: %s",
+ $client->{name}, $additionalActivePluginStr
+ ));
+ }
+
+ # check attributes against illegal values and write them into
+ # a file if they're ok:
+ my $attrProblems = OpenSLX::AttributeRoster->findProblematicValues(
+ $client->{attrs}, $info->{'vendor-os'}->{name},
+ $info->{'installed-plugins'}
+ );
+ if ($attrProblems) {
+ my $complaint = join "\n", @$attrProblems;
+ $complaint =~ s{^}{client $client->{name}: }gms;
+ warn $complaint;
+ }
+ writeAttributesToFile($client, $attrFile);
+
+ # create a tar containing the external configuration folder
+ # and client attribute file, this time referring to the client
+ # via its external ID (the PXE-style MAC), as the TGZ needs to
+ # be accessed from the client-PC, which doesn't know about the
+ # name it is referred to in the openslx-config-DB:
+ my $externalClientID = externalIDForClient($client);
+ my $bootEnv = bootEnvironmentForType($bootType);
+ switch ($bootType) {
+ case 'pxe' {
+ createTarOfPath(
+ $buildPath, "${externalClientID}.tgz",
+ "$bootEnv->{'target-path'}/client-config/$externalSystemID"
+ );
+ }
+ case 'preboot' {
+ # for preboot types
+ my $cname = $client->{name};
+ createTarOfPath(
+ $buildPath, "${cname}.tgz",
+ "$bootEnv->{'target-path'}/client-config/$externalSystemID"
+ );
+ }
+ case 'pbs' {
+ # for preboot types
+ my $cname = $client->{name};
+ createTarOfPath(
+ $buildPath, "${cname}.tgz",
+ "$bootEnv->{'target-path'}/client-config/$externalSystemID"
+ );
+ }
+ }
+ }
+ }
+ return;
+}
+
+sub writePluginConfigurations
+{
+ my $info = shift || confess 'need to pass in info-hash!';
+ my $buildPath = shift || confess 'need to pass in build-path!';
+ my $attrs = shift || {};
+
+ my $pluginConfPath = "$buildPath/initramfs/plugin-conf";
+
+ my @activePlugins;
+ foreach my $pluginInfo (@{$info->{'installed-plugins'}}) {
+ my $pluginName = $pluginInfo->{plugin_name};
+ vlog(2, _tr("checking configuration of plugin '%s'", $pluginName));
+
+ # skip inactive plugins
+ next unless $attrs->{"${pluginName}::active"};
+ push @activePlugins, $pluginName;
+
+ my $plugin = OpenSLX::OSPlugin::Roster->getPlugin($pluginName);
+ my $requiredPlugins = $plugin->getInfo()->{required} || [];
+ my @missingPlugins
+ = grep {
+ my $required = $_;
+ ! grep {
+ $_->{plugin_name} eq $required
+ } @{$info->{'installed-plugins'}};
+ }
+ @$requiredPlugins;
+ if (@missingPlugins) {
+ die _tr(
+ 'the plugin "%s" requires the following plugins to be installed: "%s"!',
+ $pluginName, join(',', @missingPlugins)
+ );
+ }
+
+ next if $option{dryRun};
+
+ mkpath([ $pluginConfPath ]);
+
+ vlog(2, _tr("writing configuration file for plugin '%s'", $pluginName));
+ # write plugin configuration to a file:
+ my $content;
+ my @pluginAttrs = grep { $_ =~ m{^${pluginName}::} } keys %$attrs;
+ foreach my $attr (sort @pluginAttrs) {
+ my $attrVal = $attrs->{$attr};
+ if (!defined $attrVal) {
+ $attrVal = '';
+ }
+ my $attrName = substr($attr, index($attr, '::')+2);
+ $content .= qq[${pluginName}_$attrName="$attrVal"\n];
+ }
+ my $fileName = "$pluginConfPath/${pluginName}.conf";
+ spitFile($fileName, $content);
+ if ($openslxConfig{'log-level'} > 2) {
+ vlog(0, "--- START OF $fileName ---");
+ vlog(0, $content);
+ vlog(0, "--- END OF $fileName --- ");
+ }
+ }
+ return \@activePlugins;
+}
+
+sub createBootEnvironmentsForSystem
+{
+ my $info = shift;
+ my $buildPath = shift;
+ my $attrFile = shift;
+ my $clients = shift || [];
+
+ my %bootTypes;
+ foreach my $client (@$clients) {
+ my $type = $client->{attrs}->{boot_type} || 'pxe';
+ $bootTypes{$type}++;
+ }
+
+ foreach my $bootType (sort keys %bootTypes) {
+ vlog(0, _tr("creating boot environment (system part) for $bootType"));
+
+ my $bootEnv = bootEnvironmentForType($bootType);
+
+ # only create a default.tgz if required by boot environment
+ if ($bootEnv->requiresDefaultClientConfig()) {
+ writeAttributesToFile($info, $attrFile);
+
+ my $systemPath
+ = "$bootEnv->{'target-path'}/client-config/$info->{'external-id'}";
+ createTarOfPath($buildPath, "default.tgz", $systemPath);
+ }
+ }
+
+ foreach my $bootType (sort keys %bootTypes) {
+ vlog(0, _tr("creating boot environment (client part) for $bootType"));
+
+ my $bootEnv = bootEnvironmentForType($bootType);
+
+ writeClientConfigurationsForSystem(
+ $info, $buildPath, $attrFile, $bootType, $clients
+ );
+
+ # let boot environment copy the kernel and create the initramfs
+ $initramfsCount
+ += $bootEnv->writeFilesRequiredForBooting($info, $buildPath);
+ }
+
+ return;
+}
+
+sub writeSystemConfiguration
+{
+ my $info = shift;
+ my $isTargetSystem = shift;
+
+ $info->{'initramfs-name'} = "initramfs-$info->{id}";
+
+ # if this is not a target system, we shall not write any configurations,
+ # but we simply incorporate inherited attributes
+ if (!$isTargetSystem) {
+ $openslxDB->mergeDefaultAttributesIntoSystem($info);
+ return;
+ }
+
+ # write configuration files for this system
+ my $buildPath = "$tempPath/build";
+ copyExternalSystemConfig(externalIDForSystem($info), $buildPath);
+
+ $openslxDB->mergeDefaultAttributesIntoSystem(
+ $info, $info->{'installed-plugins'}
+ );
+ $info->{'attr-digest'} = digestAttributes($info);
+ vlog(
+ 2,
+ _tr(
+ "attribute-digest for system '%s' is '%s'", $info->{name},
+ $info->{'attr-digest'}
+ )
+ );
+
+ # check if uclibc-rootfs in corresponding vendor-OS matches the current
+ # version and add a warning if it does not:
+ my $uclibcVersionPath
+ = "$openslxConfig{'private-path'}/stage1/$info->{'vendor-os'}->{name}/opt/openslx/uclib-rootfs.version";
+ chomp(my $uclibcVersion
+ = slurpFile($uclibcVersionPath, { failIfMissing => 0 } ));
+ chomp(my $currVersion = qx{slxversion});
+ if ($currVersion !~ m{M$} && $uclibcVersion ne $currVersion) {
+ warn _tr(
+ "uclibc-rootfs for system '%s' may not be up-to-date - consider updating the vendor-OS!",
+ $info->{name}, $uclibcVersion, $currVersion
+ );
+ }
+
+ my $attrProblems = OpenSLX::AttributeRoster->findProblematicValues(
+ $info->{attrs}, $info->{'vendor-os'}->{name},
+ $info->{'installed-plugins'}
+ );
+ if ($attrProblems) {
+ my $complaint = join "\n", @$attrProblems;
+ $complaint =~ s{^}{system $info->{name}: }gms;
+ warn $complaint;
+ }
+
+ my $activePlugins
+ = writePluginConfigurations($info, $buildPath, $info->{attrs});
+ $info->{'active-plugins'} = $activePlugins;
+ my $activePluginStr
+ = @$activePlugins ? join ',', @$activePlugins : '<none>';
+ vlog(0, _tr("active plugins: %s", $activePluginStr));
+
+ # create all required (pre-)boot-environments (PXE, CD, ...)
+ my $attrFile = "$buildPath/initramfs/machine-setup";
+ my @clientIDs = $openslxDB->aggregatedClientIDsOfSystem($info);
+ my @clients = $openslxDB->fetchClientByID(\@clientIDs);
+ createBootEnvironmentsForSystem($info, $buildPath, $attrFile, \@clients);
+
+ slxsystem("rm -rf $buildPath") unless $option{dryRun};
+
+ $systemConfCount++;
+
+ return;
+}
+
+sub writeConfigurations
+{
+ $initramfsCount = $systemConfCount = $systemErrCount
+ = $clientSystemConfCount = 0;
+ my @infos;
+ foreach my $system (@demuxableSystems) {
+ my $isTargetSystem
+ = first { $_->{name} eq $system->{name} } @targetSystems;
+ if ($isTargetSystem) {
+ vlog(
+ 0,
+ _tr("\ndemuxing system %d : %s", $system->{id}, $system->{name})
+ );
+ }
+ else {
+ vlog(
+ 0,
+ _tr(
+ "\nlinking demuxed system %d : %s into bootloader menu",
+ $system->{id}, $system->{name}
+ )
+ );
+ }
+
+ my $success = eval {
+ my $info = $openslxDB->aggregatedSystemFileInfoFor($system);
+ $info->{'external-id'} = externalIDForSystem($system);
+
+ writeSystemConfiguration($info, $isTargetSystem);
+
+ push @infos, $info;
+ 1;
+ };
+ if (!$success) {
+ print STDERR $@;
+ $systemErrCount++;
+ }
+ }
+ my $imageBaseDir = "$openslxConfig{'public-path'}/images";
+ rmtree($imageBaseDir) unless $option{dryRun};
+ writeBootloaderMenus(@infos);
+ if (defined $option{dhcpType}) {
+ writeDhcpConfig();
+ }
+ return;
+}
+
+=head1 NAME
+
+slxconfig-demuxer - OpenSLX configuration demultiplexer
+
+=head1 SYNOPSIS
+
+slxconfig-demuxer [options] [<system-name> ...]
+
+=head3 Script Options
+
+ --dry-run avoids writing anything, for testing
+
+=head3 General Options
+
+ --help brief help message
+ --man full documentation
+ --version show version
+
+=head1 DESCRIPTION
+
+B<slxconfig-demuxer> will read information about all systems, clients and
+groups from the OpenSLX configuration database, mix & match the individual
+configurational attributes and then demultiplex the resulting information
+to a set of configuration files. These files are used by any OpenSLX-client
+during boot to find out which systems to offer for booting.
+
+If you invoke the script with one or more system names, only these systems
+will be demuxed. All other systems (which are expected to have been demuxed
+before) will just be linked into the bootloader menu.
+
+The resulting files will be put into the OpenSLX-tftpboot-path.
+
+=head2 FILE CREATION
+
+The following set of files will be created:
+
+=over 8
+
+=item B<Basic PXE Setup>
+
+The basic PXE files (F<menu.c32>, F<pxelinux.0>) will be copied into
+F<$SLX_PUBLIC_PATH/tftpboot> to make them available to any PXE-client via tftp.
+
+=item B<PXE Client Configurations>
+
+For each client, a PXE configuration file will be generated and written to
+F<$SLX_PUBLIC_PATH/tftpboot/pxelinux.cfg/01-<MAC-of-client>>. This file will
+contain information about the systems this client shall offer for booting.
+For each of these systems, the kernel cmdline options required for that
+particular system setup is specified (via PXE's APPEND option).
+
+Any client that is not known to OpenSLX (so it will not have a specific
+configuration file) will use the configuration from the default client
+(appropriately named 'default').
+
+=item B<System Kernels and Initialram-Filesystems>
+
+For each bootable system, that system's kernel will be copied to
+F<$SLX_PUBLIC_PATH/tftpboot/<vendor-os-name>/kernel and an OpenSLX-specific initramfs
+required for booting that particular system is generated (by means of
+slxmkramfs) and put into F<$SLX_PUBLIC_PATH/tftpboot/<vendor-os-name>/initramfs.
+
+These two files (kernel & initramfs) will be referenced by the PXE client
+configuration of all clients that offer this specific system for booting.
+
+=item B<OpenSLX Client Configurations>
+
+For each system, an OpenSLX configuration archive will be generated and written
+to F<$SLX_PUBLIC_PATH/tftpboot/client-config/<system-name>/default. Furthermore,
+every client of that system whose attributes differ from the system's default
+will get its own configuration archive generated here, too (e.g.
+F<$SLX_PUBLIC_PATH/tftpboot/client-config/<system-name>/01-<MAC-of-client>.tgz>).
+
+Each of these archives will contain the file F<initramfs/machine-setup>,
+specifying all the attributes of that particular system and/or client
+(e.g. whether or not it should start the X-server).
+
+Furthermore, each system-specific archive may contain additional system files
+that are required for that system (e.g. a special PAM-module required for LDAP
+authentication). These files are copied from F<$SLX_PRIVATE_PATH/config/default>
+and F<$SLX_PROVATE_PATH/config/<system-name>>.
+
+On top of that, each client may have its own set of system files, too (e.g.
+some config files required to install a special kind of hardware available only
+on that client). These files are copied from
+F<< $SLX_PROVATE_PATH/config/<system-name>/01-<MAC-of-client> >>.
+
+=back
+
+=head2 MIXING & MATCHING (THE DEMUXER)
+
+In the OpenSLX configuration database, each system, group and client may have
+several configurational attributes set to a specific value that will cause
+a client booting that system to behave in a certain way. The mixing of all
+these different attributes into one set that is relevant for a specific client
+booting one specific system is one important task of the slxconfig-demuxer.
+
+As an example, let's assume one system setup that is configured to boot directly
+into a special application that demands a rather low screen-resolution of
+1024x768 pixels, as otherwise the text would be unreadable due to very small
+fonts being used by that app. In order to achieve this, the administrator can
+set the I<hw_monitor>-attribute of the B<system> to '1024x768'.
+Let's say one of the clients, however, is connected to a very old monitor that
+has problems with this resolution and only supports 800x600 pixels. In that
+case, the administrator can set the I<hw_monitor>-attribute of that B<client> to
+'800x600'. The mixing & matching process would make sure that this specific
+client would run that system with a resolution of 800x600, while all other
+clients would run that system in 1024x768.
+
+So the slxconfig-demuxer demultiplexes the individual configurational attributes
+into a concrete set of configuration settings for specific clients and their
+offered systems, making sure that each client/system combination uses the
+appropriate settings.
+
+=head1 OPTIONS
+
+=head3 Script Options
+
+=over 8
+
+=item B<< --dry-run >>
+
+Runs the script but avoids writing anything. This is useful for testing, as
+you can learn from the logging output what would have been done.
+
+=back
+
+=head3 General Options
+
+=over 8
+
+=item B<< --help >>
+
+Prints a brief help message and exits.
+
+=item B<< --man >>
+
+Prints the manual page and exits.
+
+=item B<< --version >>
+
+Prints the version and exits.
+
+=back
+
+=head1 SEE ALSO
+
+slxsettings, slxos-setup, slxos-export, slxconfig
+
+=head1 GENERAL OPENSLX OPTIONS
+
+Being a part of OpenSLX, this script supports several other options
+which can be used to overrule the OpenSLX settings:
+
+ --db-name=<string> name of database
+ --db-spec=<string> full DBI-specification of database
+ --db-type=<string> type of database to connect to
+ --locale=<string> locale to use for translations
+ --log-level=<int> level of logging verbosity (0-3)
+ --logfile=<string> file to write logging output to
+ --private-path=<string> path to private data
+ --public-path=<string> path to public (client-accesible) data
+ --temp-path=<string> path to temporary data
+
+Please refer to the C<slxsettings>-manpage for a more detailed description
+of these options.
+
+=cut