#! /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/ # ----------------------------------------------------------------------------- use strict; use warnings; my $abstract = q[ slxconfig This script can be used to display or change the OpenSLX configuration database. You can create systems that use a specific vendor-OS and you can create clients for these systems, too. ]; use Clone qw(clone); use Getopt::Long qw(:config pass_through); use List::Util qw(max); use Pod::Usage; # add the folder this script lives in and the lib-folder to perl's # search path for modules: use FindBin; use lib "$FindBin::RealBin"; use lib "$FindBin::RealBin/../lib"; use lib "$FindBin::RealBin/../config-db"; # development path to config-db use OpenSLX::AttributeRoster; use OpenSLX::Basics; use OpenSLX::ConfigDB; use OpenSLX::ConfigFolder; use OpenSLX::Utils; my %option; GetOptions( 'help|?' => \$option{helpReq}, 'inherited' => \$option{inherited}, 'man' => \$option{manReq}, 'verbose' => \$option{verbose}, '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}) { system('slxversion'); exit 1; } # if the user requested to see inherited attributes, we activate verbose mode, # too, such that we actually show attributes if ($option{inherited}) { $option{verbose} = 1; } openslxInit(); my $openslxDB = OpenSLX::ConfigDB->new(); $openslxDB->connect(); my $action = shift @ARGV || ''; if ($action =~ m[^add-c]i) { addClientToConfigDB(@ARGV); } elsif ($action =~ m[^add-g]i) { addGroupToConfigDB(@ARGV); } elsif ($action =~ m[^add-s]i) { addSystemToConfigDB(@ARGV); } elsif ($action =~ m[^change-v]i) { changeVendorOSInConfigDB(@ARGV); } elsif ($action =~ m[^change-e]i) { changeExportInConfigDB(@ARGV); } elsif ($action =~ m[^change-g]i) { changeGroupInConfigDB(@ARGV); } elsif ($action =~ m[^change-s]i) { changeSystemInConfigDB(@ARGV); } elsif ($action =~ m[^change-c]i) { changeClientInConfigDB(@ARGV); } elsif ($action =~ m[^cleanup-db]i) { cleanupConfigDB(@ARGV); } elsif ($action =~ m[^list-a]) { listAttributes(@ARGV); } elsif ($action =~ m[^list-c]) { print _tr("List of clients:\n"); listClients(@ARGV); } elsif ($action =~ m[^list-e]) { print _tr("List of exports:\n"); listExports(@ARGV); } elsif ($action =~ m[^list-g]) { print _tr("List of groups:\n"); listGroups(@ARGV); } elsif ($action =~ m[^list-s]) { print _tr("List of systems:\n"); listSystems(@ARGV); } elsif ($action =~ m[^list-v]) { print _tr("List of vendor-OSes:\n"); listVendorOSes(@ARGV); } elsif ($action =~ m[^search-c]) { print _tr("Matching clients:\n"); searchClients(@ARGV); } elsif ($action =~ m[^search-e]) { print _tr("Matching exports:\n"); searchExports(@ARGV); } elsif ($action =~ m[^search-g]) { print _tr("Matching groups:\n"); searchGroups(@ARGV); } elsif ($action =~ m[^search-s]) { print _tr("Matching systems:\n"); searchSystems(@ARGV); } elsif ($action =~ m[^search-v]) { print _tr("Matching vendor-OSes:\n"); searchVendorOSes(@ARGV); } elsif ($action =~ m[^remove-c]i) { removeClientFromConfigDB(@ARGV); } elsif ($action =~ m[^remove-g]i) { removeGroupFromConfigDB(@ARGV); } elsif ($action =~ m[^remove-s]i) { removeSystemFromConfigDB(@ARGV); } else { vlog(0, _tr(unshiftHereDoc(<<' END-OF-HERE'), $0)); You need to specify exactly one of these actions: add-client add-group add-system change-client change-export change-group change-system change-vendor-os cleanup-db list-attributes list-client list-export list-group list-system list-vendor-os remove-client remove-group remove-system search-client search-export search-group search-system search-vendor-os Try '%s --help' for more info. END-OF-HERE } $openslxDB->disconnect(); sub parseKeyValueArgs { my $allowedKeys = shift; my $table = shift; my %dataHash; while (my $param = shift) { if ($param !~ m[^\s*([\w\-:]+)\s*=(.*)$]) { die _tr( "value specification %s has unknown format, expected =\n", $param ); } my $key = lc($1); my $value = $2; if (!grep { $_ eq $key } @$allowedKeys) { die _tr("unknown key '%s' specified for %s\n", $key, $table); } # replace escaped newlines and tab chars by the respective real thing $value =~ s{\\n}{\n}gms; $value =~ s{\\t}{\t}gms; # accept '-' as placeholder for undefined if ($value eq '-') { $value = undef; } $dataHash{$key} = $value; } return \%dataHash; } sub parseKeyValueArgsWithAttrs { my $allowedKeys = shift; my $allowedAttrKeys = shift; my $table = shift; my (%dataHash, %attrHash); while (my $param = shift) { if ($param !~ m[^\s*([\w\-:]+)\s*=(.*)$]) { die _tr( "value specification %s has unknown format, expected =\n", $param ); } my $key = lc($1); my $value = $2; # replace escaped newlines and tab chars by the respective real thing $value =~ s{\\n}{\n}gms; $value =~ s{\\t}{\t}gms; # accept '-' as placeholder for undefined if ($value eq '-') { $value = undef; } if (grep { $_ eq $key } @$allowedKeys) { $dataHash{$key} = $value; } elsif (grep { $_ eq $key } @$allowedAttrKeys) { $attrHash{$key} = $value; } else { die _tr("unknown key '%s' specified for %s\n", $key, $table); } } if (wantarray) { return (\%dataHash, \%attrHash); } else { if (%attrHash) { $dataHash{attrs} = \%attrHash; } return \%dataHash; } } sub checkGivenStage3Attrs { my $stage3Attrs = shift; my $vendorOSID = shift; my $attrProblems; if ($vendorOSID) { my $vendorOS = $openslxDB->fetchVendorOSByID($vendorOSID); my @installedPlugins = $openslxDB->fetchInstalledPlugins($vendorOSID); $attrProblems = OpenSLX::AttributeRoster->findProblematicValues( $stage3Attrs, $vendorOS->{name}, \@installedPlugins ); } else { $attrProblems = OpenSLX::AttributeRoster->findProblematicValues( $stage3Attrs ); } if ($attrProblems) { my $complaint = join "\n", @$attrProblems; die $complaint; } return 1; } sub cleanupConfigDB { return $openslxDB->cleanupAnyInconsistencies(); } sub mergeNonExistingAttributes { my $target = shift; my $source = shift; my $sourceAttrs = $source->{attrs} || {}; $target->{attrs} ||= {}; my $targetAttrs = $target->{attrs}; foreach my $key (keys %$sourceAttrs) { next if exists $targetAttrs->{$key}; $targetAttrs->{$key} = $sourceAttrs->{$key}; } return 1; } sub dumpElements { my $objName = shift; my $nameClause = shift || sub { "\t$_->{name}\n" }; if ($option{verbose}) { my $ind = ' ' x 4; foreach my $elem (@_) { print "$objName '$elem->{name}':\n"; my $spcLen = max map { length($_) } keys %$elem; print join( '', map { my $elemVal = defined $elem->{$_} ? $elem->{$_} : '-'; if (ref($elemVal) eq 'HASH') { my $spcLen = max(map { length($_) } keys %$elemVal) || 0; my $spc = ' ' x $spcLen; my $subLines = join( "\n", map { my $spc = ' ' x $spcLen; my $val = defined $elemVal->{$_} ? $elemVal->{$_} : ''; $val =~ s[\n][\n$ind$spc ]g; "$ind$_" . substr($spc, length($_)) . " = $val"; } sort { # drop [] construct (origin) from key for # sorting purposes (my $aa = $a) =~ s{^\s*\[.+\]\s*}{}; (my $bb = $b) =~ s{^\s*\[.+\]\s*}{}; return $aa cmp $bb; } keys %$elemVal ); $subLines ||= "$ind"; " $_:\n$subLines\n"; } elsif (ref($elemVal) eq 'ARRAY') { my $subLines = join( "\n", map { "$ind$_" } sort @$elemVal); $subLines ||= "$ind"; " $_:\n$subLines\n"; } else { my $spc = ' ' x $spcLen; $elemVal =~ s[\n][\n$ind$spc ]g; "$ind$_" . substr($spc, length($_)) . " = $elemVal\n"; } } sort { my $refCmp = ref($elem->{$a}) cmp ref($elem->{$b}); return $refCmp ? $refCmp : $a cmp $b; } grep { $_ ne 'name'; } keys %$elem ); } } else { print join('', sort map { $nameClause->($_); } @_); } return 1; } sub listAttributes { my $attrSpec = shift; my $listHeader = _tr("List of known attributes:\n"); my $attrInfo = OpenSLX::AttributeRoster->getAttrInfo( { scope => $attrSpec } ); if ($attrInfo && keys %$attrInfo) { $listHeader = _tr("List of known attributes for scope '%s':\n", $attrSpec); } else { $attrInfo = OpenSLX::AttributeRoster->getAttrInfo( { name => $attrSpec } ); $listHeader = _tr("Details for attribute '%s':\n", $attrSpec); $option{verbose} = 1; } print $listHeader; dumpElements( 'attribute', undef, map { my $attr = clone($attrInfo->{$_}); $attr->{name} = $_; delete $attr->{content_regex}; # no use for display purposes $attr; } sort keys %$attrInfo ); return 1; } sub listClients { my $name = _cleanName(shift); my %nameSpec; # set verbose mode if any params have been passed in: if (defined $name) { $option{verbose} = 1; $nameSpec{name} = $name; } dumpElements( 'client', undef, _expandClients( sort { $a->{name} cmp $b->{name} } $openslxDB->fetchClientByFilter(\%nameSpec) ) ); return 1; } sub listGroups { my $name = _cleanName(shift); my %nameSpec; # set verbose mode if any params have been passed in: if (defined $name) { $option{verbose} = 1; $nameSpec{name} = $name; } dumpElements( 'group', undef, _expandGroups( sort { $a->{name} cmp $b->{name} } $openslxDB->fetchGroupByFilter(\%nameSpec) ) ); return 1; } sub listExports { my $name = _cleanName(shift); my %nameSpec; # set verbose mode if any params have been passed in: if (defined $name) { $option{verbose} = 1; $nameSpec{name} = $name; } dumpElements( 'export', sub { "\t$_->{name}" . substr(' ' x 30, length($_->{name})) . "($_->{type})\n"; }, map { my $vendorOS = $openslxDB->fetchVendorOSByID($_->{vendor_os_id}, 'name'); if (defined $vendorOS) { $_->{vendor_os_id} .= " ($vendorOS->{name})"; } $_; } sort { $a->{name} eq $b->{name} || $a->{type} cmp $b->{type} } $openslxDB->fetchExportByFilter(\%nameSpec) ); return 1; } sub listSystems { my $name = _cleanName(shift); my %nameSpec; # set verbose mode if any params have been passed in: if (defined $name) { $option{verbose} = 1; $nameSpec{name} = $name; } dumpElements( 'system', undef, _expandSystems( sort { $a->{name} cmp $b->{name} } $openslxDB->fetchSystemByFilter(\%nameSpec) ) ); return 1; } sub listVendorOSes { my $name = _cleanName(shift); my %nameSpec; # set verbose mode if any params have been passed in: if (defined $name) { $option{verbose} = 1; $nameSpec{name} = $name; } dumpElements('vendor-OS', undef, map { my @plugins = $openslxDB->fetchInstalledPlugins($_->{id}); my %attrHash; foreach my $plugin (@plugins) { foreach my $attr (keys %{$plugin->{attrs}}) { $attrHash{$attr} = $plugin->{attrs}->{$attr}; } } $_->{ATTRIBUTES} = \%attrHash; $_->{PLUGINS} = @plugins ? join(',', sort map { $_->{plugin_name} } @plugins) : ''; $_; } sort { $a->{name} cmp $b->{name} } $openslxDB->fetchVendorOSByFilter(\%nameSpec)); return 1; } sub searchClients { my @clientKeys = $openslxDB->getColumnsOfTable('client'); my @clientAttrKeys = OpenSLX::AttributeRoster->getClientAttrs(); my ($clientData, $clientAttrs) = parseKeyValueArgsWithAttrs( \@clientKeys, \@clientAttrKeys, 'client', @_ ); # set verbose mode if any params have been passed in: $option{verbose} = 1 if %$clientData; dumpElements( 'client', undef, _expandClients( sort { $a->{name} cmp $b->{name} } $openslxDB->fetchClientByFilter($clientData, undef, $clientAttrs) ) ); return 1; } sub searchGroups { my @groupKeys = $openslxDB->getColumnsOfTable('groups'); my @groupAttrKeys = OpenSLX::AttributeRoster->getClientAttrs(); my ($groupData, $groupAttrs) = parseKeyValueArgsWithAttrs( \@groupKeys, \@groupAttrKeys, 'group', @_ ); # set verbose mode if any params have been passed in: $option{verbose} = 1 if %$groupData; dumpElements( 'group', undef, _expandGroups( sort { $a->{name} cmp $b->{name} } $openslxDB->fetchGroupByFilter($groupData, undef, $groupAttrs) ) ); return 1; } sub searchExports { my @exportKeys = $openslxDB->getColumnsOfTable('export'); my $exportData = parseKeyValueArgs(\@exportKeys, 'export', @_); # set verbose mode if any params have been passed in: $option{verbose} = 1 if %$exportData; dumpElements( 'export', sub { "\t$_->{name}" . substr(' ' x 30, length($_->{name})) . "($_->{type})\n"; }, map { my $vendorOS = $openslxDB->fetchVendorOSByID($_->{vendor_os_id}, 'name'); if (defined $vendorOS) { $_->{vendor_os_id} .= " ($vendorOS->{name})"; } $_; } sort { $a->{name} eq $b->{name} || $a->{type} cmp $b->{type} } $openslxDB->fetchExportByFilter($exportData) ); return 1; } sub searchSystems { my @systemKeys = $openslxDB->getColumnsOfTable('system'); my @systemAttrKeys = OpenSLX::AttributeRoster->getSystemAttrs(); my ($systemData, $systemAttrs) = parseKeyValueArgsWithAttrs( \@systemKeys, \@systemAttrKeys, 'system', @_ ); # set verbose mode if any params have been passed in: $option{verbose} = 1 if %$systemData; dumpElements( 'system', undef, _expandSystems( sort { $a->{name} cmp $b->{name} } $openslxDB->fetchSystemByFilter($systemData, undef, $systemAttrs) ) ); return 1; } sub searchVendorOSes { my @vendorOSKeys = $openslxDB->getColumnsOfTable('vendor_os'); my $vendorOSData = parseKeyValueArgs(\@vendorOSKeys, 'vendor_os', @_); # set verbose mode if any params have been passed in: $option{verbose} = 1 if %$vendorOSData; dumpElements( 'vendor-OS', undef, map { my @plugins = $openslxDB->fetchInstalledPlugins($_->{id}); $_->{plugins} = @plugins ? join(',', sort map { $_->{plugin_name} } @plugins) : ''; $_; } sort { $a->{name} cmp $b->{name} } $openslxDB->fetchVendorOSByFilter($vendorOSData) ); return 1; } sub changeVendorOSInConfigDB { my $vendorOSName = _cleanName(shift || ''); if (!length($vendorOSName)) { die _tr( "you have to specify the name for the vendor-OS you'd like to change!\n" ); } my @keys = $openslxDB->getColumnsOfTable('vendor_os'); my $vendorOSData = parseKeyValueArgs(\@keys, 'vendor_os', @_); my $vendorOS = $openslxDB->fetchVendorOSByFilter({'name' => $vendorOSName}); if (!defined $vendorOS) { die _tr("the vendor-OS '%s' doesn't exists in the DB, giving up!\n", $vendorOSName); } $openslxDB->changeVendorOS($vendorOS->{id}, [$vendorOSData]); vlog( 0, _tr("vendor-OS '%s' has been successfully changed\n", $vendorOSName) ); listVendorOSes("id=$vendorOS->{id}") if $option{verbose}; return 1; } sub changeExportInConfigDB { my $exportName = _cleanName(shift || ''); if (!length($exportName)) { die _tr( "you have to specify the name for the export you'd like to change!\n" ); } my @exportKeys = $openslxDB->getColumnsOfTable('export'); my $exportData = parseKeyValueArgs(\@exportKeys, 'export', @_); my $export = $openslxDB->fetchExportByFilter({'name' => $exportName}); if (!defined $export) { die _tr("the export '%s' doesn't exists in the DB, giving up!\n", $exportName); } $openslxDB->changeExport($export->{id}, [$exportData]); vlog(0, _tr("export '%s' has been successfully changed\n", $exportName)); listExports("id=$export->{id}") if $option{verbose}; return 1; } sub addClientToConfigDB { my $clientName = _cleanName(shift || ''); if (!length($clientName)) { die _tr("you have to specify the name for the new client\n"); } my @clientKeys = $openslxDB->getColumnsOfTable('client'); push @clientKeys, 'systems'; my @clientAttrKeys = OpenSLX::AttributeRoster->getClientAttrs(); my $clientData = parseKeyValueArgsWithAttrs( \@clientKeys, \@clientAttrKeys, 'client', @_ ); $clientData->{name} = $clientName; checkGivenStage3Attrs($clientData->{attrs}); my @systemIDs; if (exists $clientData->{systems}) { @systemIDs = map { my $system = $openslxDB->fetchSystemByFilter({'name' => $_}); if (!defined $system) { die _tr("system '%s' doesn't exist!\n", $_); } $system->{id}; } split '\s*,\s*', $clientData->{systems}; delete $clientData->{systems}; } if (!$clientData->{mac}) { die _tr("you have to specify the MAC for the new client\n"); } if ($clientData->{mac} !~ m[^(?:[[:xdigit:]][[:xdigit:]]:){5}?[[:xdigit:]][[:xdigit:]]$]) { die _tr( "unknown MAC-format given, expected something like '01:02:03:04:05:06'!\n" ); } if ($openslxDB->fetchClientByFilter({'name' => $clientName})) { die _tr("the client '%s' already exists in the DB, giving up!\n", $clientName); } if ($openslxDB->fetchClientByFilter({'mac' => $clientData->{mac}})) { die _tr( "a client with the MAC '%s' already exists in the DB, giving up!\n", $clientData->{mac} ); } my $clientID = $openslxDB->addClient([$clientData]); vlog( 0, _tr( "client '%s' has been successfully added to DB (ID=%s)\n", $clientName, $clientID ) ); if (@systemIDs) { $openslxDB->addSystemIDsToClient($clientID, \@systemIDs); } if ($option{verbose}) { listClients("id=$clientID"); } return 1; } sub addGroupToConfigDB { my $groupName = _cleanName(shift || ''); if (!length($groupName)) { die _tr("you have to specify the name for the new group\n"); } my @groupKeys = $openslxDB->getColumnsOfTable('groups'); push @groupKeys, 'systems', 'clients'; my @groupAttrKeys = OpenSLX::AttributeRoster->getClientAttrs(); my $groupData = parseKeyValueArgsWithAttrs( \@groupKeys, \@groupAttrKeys, 'group', @_ ); $groupData->{name} = $groupName; checkGivenStage3Attrs($groupData->{attrs}); my @systemIDs; if (exists $groupData->{systems}) { @systemIDs = map { my $system = $openslxDB->fetchSystemByFilter({'name' => $_}); if (!defined $system) { die _tr("system '%s' doesn't exist!\n", $_); } $system->{id}; } split '\s*,\s*', $groupData->{systems}; delete $groupData->{systems}; } my @clientIDs; if (exists $groupData->{clients}) { @clientIDs = map { my $client = $openslxDB->fetchClientByFilter({'name' => $_}); if (!defined $client) { die _tr("client '%s' doesn't exist in DB, giving up!\n", $_); } $client->{id}; } split '\s*,\s*', $groupData->{clients}; delete $groupData->{clients}; } if (!defined $groupData->{priority} || !length($groupData->{priority})) { $groupData->{priority} = 50; vlog(0, _tr("priority of new group has been set to default (50).")); } if ($openslxDB->fetchGroupByFilter({'name' => $groupName})) { die _tr("the group '%s' already exists in the DB, giving up!\n", $groupName); } my $groupID = $openslxDB->addGroup([$groupData]); vlog( 0, _tr( "group '%s' has been successfully added to DB (ID=%s)\n", $groupName, $groupID ) ); if (@systemIDs) { $openslxDB->addSystemIDsToGroup($groupID, \@systemIDs); } if (@clientIDs) { $openslxDB->addClientIDsToGroup($groupID, \@clientIDs); } listGroups("id=$groupID") if $option{verbose}; return 1; } sub addSystemToConfigDB { my $systemName = _cleanName(shift || ''); if (!length($systemName)) { die _tr("you have to specify the name of the new system!\n"); } my @systemKeys = $openslxDB->getColumnsOfTable('system'); push @systemKeys, 'clients', 'export'; my @systemAttrKeys = OpenSLX::AttributeRoster->getSystemAttrs(); my $systemData = parseKeyValueArgsWithAttrs( \@systemKeys, \@systemAttrKeys, 'system', @_ ); $systemData->{name} = $systemName; $systemData->{attrs} ||= {}; my $exportName = $systemData->{export} || ''; delete $systemData->{export}; if (!length($exportName)) { $exportName = $systemName; # try falling back to given system name } my $export = $openslxDB->fetchExportByFilter({'name' => $exportName}); if (!defined $export) { die _tr("export '%s' could not be found in DB, giving up!\n", $exportName); } $systemData->{export_id} = $export->{id}; checkGivenStage3Attrs($systemData->{attrs}, $export->{vendor_os_id}); my @clientIDs; if (exists $systemData->{clients}) { @clientIDs = map { my $client = $openslxDB->fetchClientByFilter({'name' => $_}); if (!defined $client) { die _tr("client '%s' doesn't exist in DB, giving up!\n", $_); } $client->{id}; } split '\s*,\s*', $systemData->{clients}; delete $systemData->{clients}; } else { # no clients given, so we add this system to the default client, # which will make this system bootable by *all* clients (unless # they are configured otherwise). my $defaultClient = $openslxDB->fetchClientByFilter({'name' => '<<>>'}); push @clientIDs, $defaultClient->{id}; } if ($openslxDB->fetchSystemByFilter({'name' => $systemName})) { die _tr("the system '%s' already exists in the DB, giving up!\n", $systemName); } my $systemConfigPath = "$openslxConfig{'private-path'}/config/$systemName/default"; if (!-e $systemConfigPath) { # create the default (empty) config folders for this system: createConfigFolderForSystem($systemName); } my $systemID = $openslxDB->addSystem([$systemData]); vlog( 0, _tr( "system '%s' has been successfully added to DB (ID=%s)\n", $systemName, $systemID ) ); if (@clientIDs) { $openslxDB->addClientIDsToSystem($systemID, \@clientIDs); } listSystems("id=$systemID") if $option{verbose}; return 1; } sub changeClientInConfigDB { my $clientName = _cleanName(shift || ''); if (!length($clientName)) { die _tr( "you have to specify the name of the client you'd like to change!\n" ); } my @clientKeys = $openslxDB->getColumnsOfTable('client'); push @clientKeys, 'systems', 'add-systems', 'remove-systems'; my @clientAttrKeys = OpenSLX::AttributeRoster->getClientAttrs(); my $clientData = parseKeyValueArgsWithAttrs( \@clientKeys, \@clientAttrKeys, 'client', @_ ); my $client = $openslxDB->fetchClientByFilter({'name' => $clientName}); if (!defined $client) { die _tr("the client '%s' doesn't exists in the DB, giving up!\n", $clientName); } checkGivenStage3Attrs($clientData->{attrs}); mergeNonExistingAttributes($clientData, $client); my @systemIDs; if (exists $clientData->{systems}) { @systemIDs = map { my $system = $openslxDB->fetchSystemByFilter({'name' => $_}); if (!defined $system) { die _tr("system '%s' doesn't exist!\n", $_); } $system->{id}; } split ",", $clientData->{systems}; delete $clientData->{systems}; } if (exists $clientData->{'add-systems'}) { @systemIDs = $openslxDB->fetchSystemIDsOfClient($client->{id}); push @systemIDs, map { my $system = $openslxDB->fetchSystemByFilter({'name' => $_}); if (!defined $system) { die _tr("system '%s' doesn't exist!\n", $_); } $system->{id}; } split ",", $clientData->{'add-systems'}; delete $clientData->{'add-systems'}; } if (exists $clientData->{'remove-systems'}) { @systemIDs = $openslxDB->fetchSystemIDsOfClient($client->{id}); foreach my $sysName (split ",", $clientData->{'remove-systems'}) { my $system = $openslxDB->fetchSystemByFilter({'name' => $sysName}); if (!defined $system) { die _tr("system '%s' doesn't exist!\n", $sysName); } @systemIDs = grep { $_ != $system->{id} } @systemIDs; } delete $clientData->{'remove-systems'}; } if ($clientData->{name} && $client->{name} eq '<<>>') { die _tr( "you can't rename the default client - no changes were made!\n"); } if ( $clientData->{mac} && $clientData->{mac} !~ m[^(?:[[:xdigit:]][[:xdigit:]]:){5}?[[:xdigit:]][[:xdigit:]]$]) { die _tr( "unknown MAC-format given, expected something like '01:02:03:04:05:06'!\n" ); } $openslxDB->changeClient($client->{id}, [$clientData]); vlog(0, _tr("client '%s' has been successfully changed\n", $clientName)); if (@systemIDs) { $openslxDB->setSystemIDsOfClient($client->{id}, \@systemIDs); } listClients("id=$client->{id}") if $option{verbose}; return 1; } sub changeGroupInConfigDB { my $groupName = _cleanName(shift || ''); if (!length($groupName)) { die _tr( "you have to specify the name of the group you'd like to change!\n" ); } my @groupKeys = $openslxDB->getColumnsOfTable('group'); push @groupKeys, qw( systems add-systems remove-systems clients add-clients remove-clients ); my @groupAttrKeys = OpenSLX::AttributeRoster->getClientAttrs(); my $groupData = parseKeyValueArgsWithAttrs( \@groupKeys, \@groupAttrKeys, 'group', @_ ); my $group = $openslxDB->fetchGroupByFilter({'name' => $groupName}); if (!defined $group) { die _tr("the group '%s' doesn't exists in the DB, giving up!\n", $groupName); } checkGivenStage3Attrs($groupData->{attrs}); mergeNonExistingAttributes($groupData, $group); my (@systemIDs, @clientIDs); if (exists $groupData->{systems}) { @systemIDs = map { my $system = $openslxDB->fetchSystemByFilter({'name' => $_}); if (!defined $system) { die _tr("system '%s' doesn't exist!\n", $_); } $system->{id}; } split ",", $groupData->{systems}; delete $groupData->{systems}; } if (exists $groupData->{'add-systems'}) { @systemIDs = $openslxDB->fetchSystemIDsOfGroup($group->{id}); push @systemIDs, map { my $system = $openslxDB->fetchSystemByFilter({'name' => $_}); if (!defined $system) { die _tr("system '%s' doesn't exist!\n", $_); } $system->{id}; } split ",", $groupData->{'add-systems'}; delete $groupData->{'add-systems'}; } if (exists $groupData->{'remove-systems'}) { @systemIDs = $openslxDB->fetchSystemIDsOfGroup($group->{id}); foreach my $sysName (split ',', $groupData->{'remove-systems'}) { my $system = $openslxDB->fetchSystemByFilter({'name' => $sysName}); if (!defined $system) { die _tr("system '%s' doesn't exist!\n", $sysName); } @systemIDs = grep { $_ != $system->{id} } @systemIDs; } delete $groupData->{'remove-systems'}; } if (exists $groupData->{clients}) { @clientIDs = map { my $client = $openslxDB->fetchClientByFilter({'name' => $_}); if (!defined $client) { die _tr("client '%s' doesn't exist in DB, giving up!\n", $_); } $client->{id}; } split ",", $groupData->{clients}; delete $groupData->{clients}; } if (exists $groupData->{'add-clients'}) { @clientIDs = $openslxDB->fetchClientIDsOfGroup($group->{id}); push @clientIDs, map { my $client = $openslxDB->fetchClientByFilter({'name' => $_}); if (!defined $client) { die _tr("client '%s' doesn't exist!\n", $_); } $client->{id}; } split ",", $groupData->{'add-clients'}; delete $groupData->{'add-clients'}; } if (exists $groupData->{'remove-clients'}) { @clientIDs = $openslxDB->fetchClientIDsOfGroup($group->{id}); foreach my $clientName (split ",", $groupData->{'remove-clients'}) { my $client = $openslxDB->fetchClientByFilter({'name' => $clientName}); if (!defined $client) { die _tr("client '%s' doesn't exist!\n", $clientName); } @clientIDs = grep { $_ != $client->{id} } @clientIDs; } delete $groupData->{'remove-clients'}; } if (defined $groupData->{priority} && $groupData->{priority} !~ m{^\d+$}) { die _tr("unknown priority-format given, expected an integer!\n"); } $openslxDB->changeGroup($group->{id}, [$groupData]); vlog(0, _tr("group '%s' has been successfully changed\n", $groupName)); if (@systemIDs) { $openslxDB->setSystemIDsOfGroup($group->{id}, \@systemIDs); } if (@clientIDs) { $openslxDB->setClientIDsOfGroup($group->{id}, \@clientIDs); } listGroups("id=$group->{id}") if $option{verbose}; return 1; } sub changeSystemInConfigDB { my $systemName = _cleanName(shift || ''); if (!length($systemName)) { die _tr( "you have to specify the name of the system you'd like to change!\n" ); } my $system = $openslxDB->fetchSystemByFilter({'name' => $systemName}); if (!defined $system) { die _tr("the system '%s' doesn't exists in the DB, giving up!\n", $systemName); } my @systemKeys = $openslxDB->getColumnsOfTable('system'); push @systemKeys, 'clients', 'add-clients', 'remove-clients'; my @systemAttrKeys = OpenSLX::AttributeRoster->getSystemAttrs(); my $systemData = parseKeyValueArgsWithAttrs( \@systemKeys, \@systemAttrKeys, 'system', @_ ); my $export = $openslxDB->fetchExportByID($system->{export_id}); checkGivenStage3Attrs($systemData->{attrs}, $export->{vendor_os_id}); mergeNonExistingAttributes($systemData, $system); my @clientIDs; if (exists $systemData->{clients}) { @clientIDs = map { my $client = $openslxDB->fetchClientByFilter({'name' => $_}); if (!defined $client) { die _tr("client '%s' doesn't exist in DB, giving up!\n", $_); } $client->{id}; } split ",", $systemData->{clients}; delete $systemData->{clients}; } if (exists $systemData->{'add-clients'}) { @clientIDs = $openslxDB->fetchClientIDsOfSystem($system->{id}); push @clientIDs, map { my $client = $openslxDB->fetchClientByFilter({'name' => $_}); if (!defined $client) { die _tr("client '%s' doesn't exist!\n", $_); } $client->{id}; } split ",", $systemData->{'add-clients'}; delete $systemData->{'add-clients'}; } if (exists $systemData->{'remove-clients'}) { @clientIDs = $openslxDB->fetchClientIDsOfSystem($system->{id}); foreach my $clientName (split ",", $systemData->{'remove-clients'}) { my $client = $openslxDB->fetchClientByFilter({'name' => $clientName}); if (!defined $client) { die _tr("client '%s' doesn't exist!\n", $clientName); } @clientIDs = grep { $_ != $client->{id} } @clientIDs; } delete $systemData->{'remove-clients'}; } if ($systemData->{name} && $system->{name} eq '<<>>') { die _tr( "you can't rename the default system - no changes were made!\n"); } $openslxDB->changeSystem($system->{id}, $systemData); vlog(0, _tr("system '%s' has been successfully changed\n", $systemName)); if (@clientIDs) { $openslxDB->setClientIDsOfSystem($system->{id}, \@clientIDs); } listSystems("id=$system->{id}")if $option{verbose}; return 1; } sub removeClientFromConfigDB { my $clientName = _cleanName(shift || ''); if (!length($clientName)) { die _tr( "you have to specify the name of the client you'd like to remove!\n" ); } my $clientData = parseKeyValueArgs(['name'], 'client', @_); my $client = $openslxDB->fetchClientByFilter({'name' => $clientName}); if (!defined $client) { die _tr("the client '%s' doesn't exists in the DB, giving up!\n", $clientName); } if ($client->{name} eq '<<>>') { die _tr("you can't remove the default client!\n"); } $openslxDB->removeClient($client->{id}); vlog(0, _tr("client '%s' has been successfully removed from DB\n", $clientName) ); return 1; } sub removeGroupFromConfigDB { my $groupName = _cleanName(shift || ''); if (!length($groupName)) { die _tr( "you have to specify the name of the group you'd like to remove!\n" ); } my $groupData = parseKeyValueArgs(['name'], 'group', @_); my $group = $openslxDB->fetchGroupByFilter({'name' => $groupName}); if (!defined $group) { die _tr("the group '%s' doesn't exists in the DB, giving up!\n", $groupName); } $openslxDB->removeGroup($group->{id}); vlog(0, _tr("group '%s' has been successfully removed from DB\n", $groupName) ); return 1; } sub removeSystemFromConfigDB { my $systemName = _cleanName(shift || ''); if (!length($systemName)) { die _tr( "you have to specify the name of the system you'd like to remove!\n" ); } my $systemData = parseKeyValueArgs(['name'], 'system', @_); my $system = $openslxDB->fetchSystemByFilter({'name' => $systemName}); if (!defined $system) { die _tr("the system '%s' doesn't exists in the DB, giving up!\n", $systemName); } if ($system->{name} eq '<<>>') { die _tr("you can't remove the default system!\n"); } $openslxDB->removeSystem($system->{id}); vlog(0, _tr("system '%s' has been successfully removed from DB\n", $systemName) ); return 1; } sub _expandClients { # expands info for given clients return map { my @sysIDs = $openslxDB->fetchSystemIDsOfClient($_->{id}); $_->{systems} = join "\n", map { $_->{name} } sort { $a->{name} cmp $b->{name} } $openslxDB->fetchSystemByID(\@sysIDs, 'name'); if ($option{inherited}) { my $mergedClient = clone($_); my $originInfo = {}; $openslxDB->mergeDefaultAndGroupAttributesIntoClient( $mergedClient, $originInfo ); my $mergedAttrs = $mergedClient->{attrs} || {}; $_->{attrs} = {}; foreach my $attr (keys %$mergedAttrs) { my $origin = $originInfo->{$attr}; my $enhancedName = $origin ? "[$origin] $attr" : $attr; $_->{attrs}->{$enhancedName} = $mergedAttrs->{$attr}; } } # rename attrs to ATTRIBUTES for display $_->{ATTRIBUTES} = $_->{attrs}; delete $_->{attrs}; $_; } @_; } sub _expandGroups { # expands info for given groups return map { my @systemIDs = $openslxDB->fetchSystemIDsOfGroup($_->{id}); $_->{systems} = join "\n", map { $_->{name} } sort { $a->{name} cmp $b->{name} } $openslxDB->fetchSystemByID(\@systemIDs, 'name'); my @clientIDs = $openslxDB->fetchClientIDsOfGroup($_->{id}); $_->{clients} = join "\n", map { $_->{name} } sort { $a->{name} cmp $b->{name} } $openslxDB->fetchClientByID(\@clientIDs, 'name'); # rename attrs to ATTRIBUTES for display $_->{ATTRIBUTES} = $_->{attrs}; delete $_->{attrs}; $_; } @_; } sub _expandSystems { # expands info for given systems return map { my @clientIDs = $openslxDB->fetchClientIDsOfSystem($_->{id}); $_->{clients} = join "\n", map { $_->{name} } sort { $a->{name} cmp $b->{name} } $openslxDB->fetchClientByID(\@clientIDs, 'name'); my @activePlugins; my $export = $openslxDB->fetchExportByID($_->{export_id}); if (defined $export) { $_->{export_id} = "$export->{id} ($export->{name})"; # fetch detailed info about active plugins my @installedPlugins = $openslxDB->fetchInstalledPlugins( $export->{vendor_os_id} ); my $mergedSystem = clone($_); my $originInfo = {}; $openslxDB->mergeDefaultAttributesIntoSystem( $mergedSystem, \@installedPlugins, $originInfo ); my $mergedAttrs = $mergedSystem->{attrs} || {}; foreach my $plugin (@installedPlugins) { next if !$mergedAttrs->{"$plugin->{plugin_name}::active"}; push @activePlugins, $plugin; } if ($option{inherited}) { $_->{attrs} = {}; foreach my $attr (keys %$mergedAttrs) { my $origin = $originInfo->{$attr}; my $enhancedName = $origin ? "[$origin] $attr" : $attr; $_->{attrs}->{$enhancedName} = $mergedAttrs->{$attr}; } } } $_->{PLUGINS} = [ sort map { $_->{plugin_name} } @activePlugins ]; # rename attrs to ATTRIBUTES for display $_->{ATTRIBUTES} = $_->{attrs}; delete $_->{attrs}; $_; } @_; } sub _cleanName { # removes 'name=""' constructs from the name, as it is rather tempting # for the user to type that ... (and we'd like to play along with DWIM) my $name = shift; return unless defined $name; if ($name =~ m[^name=(.+)$]) { return $1; } # for convenience, we alias default to <<>> $name = '<<>>' if $name eq 'default'; return $name; } =head1 NAME slxconfig - OpenSLX-script to view & change the configurational database =head1 SYNOPSIS slxconfig [options] =head3 Options --help brief help message --inherited show inherited attributes, too --man show full documentation --verbose be more verbose --version show version =head3 Actions =over 8 =item B<< add-client mac= [= ...] >> adds a new client to the config-DB =item B<< add-system [export=] \ >> =item B<< = ...] >> adds a new system to the config-DB =item B<< add-group [priority=] [= ...] >> adds a new group to the config-DB =item B<< change-vendor-os [= ...] >> changes the data of an existing vendor-OS in the config-DB. =item B<< change-export [= ...] >> changes the data of an existing export in the config-DB =item B<< change-client [= ...] >> changes the data of an existing client in the config-DB Note: you can use the special value '-' to unset a key (mostly useful for attributes). =item B<< change-group [= ...] >> changes the data of an existing group in the config-DB Note: you can use the special value '-' to unset a key (mostly useful for attributes). =item B<< change-system [= ...] >> changes the data of an existing system in the config-DB Note: you can use the special value '-' to unset a key (mostly useful for attributes). =item B<< cleanup-db >> utility command that looks for any inconsistencies in the DB (stale references and/or references to plugins that do not exists) and removes them. You should only invoke this if you are a developer and have removed one or more plugins from the repository and would like to get rid of the left-overs in your local DB. =item B<< list-attributes [] >> lists all attributes, the ones in the given scope or the one with the given name =item B<< list-client [] >> lists client with given name =item B<< list-export [] >> lists export with given name =item B<< list-group [] >> lists group with given name =item B<< list-system [] >> lists system with given name =item B<< list-vendor-os [] >> lists vendor-OS with given name =item B<< remove-client >> removes a client from the config-DB =item B<< remove-group >> removes a group from the config-DB =item B<< remove-system >> removes a system from the config-DB =item B<< search-client [= ...] >> shows all clients in config-DB (optionally matching given criteria) =item B<< search-export [= ...] >> shows all exports in config-DB (optionally matching given criteria) =item B<< search-group [= ...] >> shows all groups in config-DB (optionally matching given criteria) =item B<< search-system [= ...] >> shows all systems in config-DB (optionally matching given criteria) =item B<< search-vendor-os [= ...] >> shows all vendor-OSes in config-DB (optionally matching given criteria) =back =head1 DESCRIPTION B can be used to view the contents of the configurational database. Additionally, you can add systems as well as clients and change their specific boot configuration. =head1 OPTIONS =over 8 =item B<< --help >> Prints a brief help message and exits. =item B<< --man >> Prints the manual page and exits. =item B<< --verbose >> Prints more information during execution of any action. =item B<< --version >> Prints the version and exits. =back =head1 EXAMPLES =head3 Listing existing Clients / Exports / Groups / Systems / Vendor-OSes =over 8 =item B<< slxconfig list-client >> =item B<< slxconfig list-export >> =item B<< slxconfig list-group >> =item B<< slxconfig list-system >> =item B<< slxconfig list-vendor-os >> lists all existing instances of the respective DB-objects =item B<< slxconfig list-system "<<>>" >> Lists the details of the default-system. =item B<< slxconfig --inherited list-system suse-10.2::nfs >> Lists the details of the 'suse-10.2::nfs'-system with all the attributes that it inherits from the default-system or the default-client. =back =head3 Listing known attributes =over 8 =item B<< slxconfig list-attr >> lists all known attributes (--verbose will give details). =item B<< slxconfig list-attr >> lists all known attributes for the given scope (use 'core' to see only non-scoped attributes). =back =head3 Adding a new System to an exported Vendor-OS =over 8 =item B<< slxconfig add-system debian-4.0 >> adds a new system named 'debian-4.0' to the config-DB that will use the export of the same name. No client will be associated with this system, yet. =item B<< slxconfig add-system suse-11.1 export-name=suse-11.1-kde \ >> =item B<< clients=PC131,PC132,PC133 \ >> =item B<< label="Linux Desktop" >> adds a new system name 'suse-11.1' to the config-DB that will use the export named 'suse-11.1-kde'. The system will be labeled 'Linux Desktop' and the clients 'PC131, 'PC132' and 'PC133' are associated with this system (so they can boot it). =back =head3 Adding a new Client =over 8 =item B<< slxconfig add-client vmware-1 mac=01:02:03:04:05:06 >> adds a new client named 'vmware-1', being identified by the MAC '01:02:03:04:05:06' to the config-DB. No system will be associated with this client, yet (so it can't boot anything). =item B<< slxconfig add-client vmware-1 mac=01:02:03:04:05:06 \ >> =item B<< systems=suse-11.1,debian-4.0 \ >> =item B<< boot_type=pxe >> adds a new client named 'vmware-1', being identified by the MAC '01:02:03:04:05:06' to the config-DB. The systems 'suse-11.1' & 'Debian-4.0' will be associated with this client (so it will offer these systems for booting). This client will use PXE for booting (which is the default, anyway). =back =head3 Changing a System =over 8 =item B<< slxconfig change-system suse-11.1 boot_type=preboot-cd >> will change the system named 'suse-11.1' such that it will use a preboot-CD environment for booting. =item B<< slxconfig change-system suse-11.1 add-clients=vmware-1 >> will associate the client 'vmware-1' with the system named 'suse-11.1'. =item B<< slxconfig change-system suse-11.1 remove-clients=vmware-1 >> will remove the client 'vmware-1' from the system named 'suse-11.1'. =back =head3 Changing a Client =over 8 =item B<< slxconfig change-client PC131 start_snmp=yes >> will change the client named 'PC131' such that it will start the SNMP daemon on all systems that it boots. =item B<< slxconfig change-client PC131 add-systems=Debian-4.0 >> will associate the system 'Debian-4.0' with the client named 'PC131'. =item B<< slxconfig change-client PC131 remove-systems=Debian-4.0 >> will remove the system 'Debian-4.0' from the client named 'PC131'. =back =head3 Removing a Client / Group / System =over 8 =item B<< slxconfig remove-client >> =item B<< slxconfig remove-group >> =item B<< slxconfig remove-system >> removes the client/group/system with the given name. =back =head3 Searching for Clients / Exports / Groups / Systems / Vendor-OSes =over 8 =item B<< slxconfig search-client mac='01:02:03:04:05:06' >> displays all clients with the MAC '01:02:03:04:05:06' (should be only one) =item B<< slxconfig search-export type=nfs >> displays the exports of type 'nfs' =item B<< slxconfig list-group priority=50 >> displays the groups that have the default priority (50) =back =head1 SEE ALSO slxsettings, slxos-setup, slxos-export, slxconfig-demuxer =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= name of database --db-spec= full DBI-specification of database --db-type= type of database to connect to --locale= locale to use for translations --log-level= level of logging verbosity (0-3) --logfile= file to write logging output to --private-path= path to private data --public-path= path to public (client-accesible) data --temp-path= path to temporary data Please refer to the C-manpage for a more detailed description of these options. =cut