summaryrefslogblamecommitdiffstats
path: root/src/config-db/slxconfig
blob: d4749f97699bb34216b92353895f4785dfa34cf8 (plain) (tree)
1
2
3
4
5
6
7
8
9
                

                                                                               
 

                                                                    
 

                                                                         
 

                                                                               
           
             


                 

                                                                          
                                                      

  
                    
                                          
                       








                                                                  

                               
 
                             
                    
                      
                          
                   
 
           

           




                                        
                  

                                                                               



                                                                       
 
                          

                         

 


                                                                              
                         

 

              

                                         
 
                               
                            
                               
 
                               
                              

                               
                               
 
                                  
                                    

                                  
                                  
 
                                  
                                 
 
                                  
                                  
 
                                  
                                  
 


                                    
                               
                          
 
                               

                                    

                               

                                    
 
                               

                                   
 
                               

                                    

                               

                                        

                                 

                                     

                                 

                                     
 
                                 

                                    
 
                                 

                                     

                                 

                                         
 
                                  
                                    

                                  
                                   
 
                                  
                                    

      









                                                          
                      















                                      

 
                         
 

                     





























                                                                                      

 

                              









































                                                                                      

 




                             

                     

                                                                  



                                                                              

          







                                                                        
     



             




                                                   

                              

                       
 
                                             
 

                                       
 



                                                    
 
             

 

                































































                                                                                

 

                  
                         
 














                                                                           


                           
                                              







                                                                           

 

               
                                 
 
                 
 




                                                         
 






                                                       
 
             



              
                                 
 
                 
 




                                                         
 






                                                      
 
             



               





























                                                                          



               
                                 
 
                 
 




                                                         
 






                                                       
 
             



                  












                                                                      







                                                                 








                                                                         



                 

















                                                                             

 
                
 

















                                                                          



                 

























                                                                          

 
                 
 

















                                                                             

 
                    
 




















                                                                          

 

                            
                                               
 




                                                                                    
 

                                                                  
 




                                                                                
 



                                                                               
 
                                                             
 
             



                          
                                             
 




                                                                                 
 

                                                                   
 




                                                                          
 

                                                                             
 
                                                        
 
             

 

                       













                                                                     

                                                

















































                                                                                      



                      












                                                                    

                                               


















































                                                                             

 

                       




























                                                                          

                                                                         

























                                                                             




















                                                                      

 
                          
 






                                                                                
    












                                                                          

                                                



























































                                                                                      



                         






















                                                                               

                                               























































































                                                                               

 

                          







                                                                                




                                                                          






                                                                    

                                                                         



















































                                                                             
 
 

                            























                                                                                

 
                           
 




















                                                                               
 
 

                            























                                                                                
 
 
                  









                                                                      
                                             

















                                                                           


                 


















                                                                        


                  




                                                                         
                               





                                                                      
                                                                    




                                                                         
                                             
























                                                                               

 
              


                                                                           
 
                                
 


                                  
 


                                                        
                 

 

           
                                                                        
 
               
 

                                              
              

                                                 
                                                             



                                                      
              
 
       
 
                                                                       
 
                                  
 
                                                                  
 
                                              
 
                                  
 



                                                                               

                                                                      
                                                           




                                                                
                                                                
 
                                                       
 


                                                                     



                                                              


                                                                     
                                                                
 
                                                       
 


                                                                     








                                                                              
                                                            
 

                                                                           
 
                                            
 
                            
 
                                            
 
                            
 



                                          
                                            
 
                            
 
                                                 
 
                               
 
                                            
 
                                   
 



                                          
                                            

                                   
 







                                                                   



                                                                  




                                                         
                                                     


                                                                       
     
 
                  
 


                                                                              
 
              
 
       
 
                       


                                      
                      


                                 
                          


                                                       
                          




                             

               
                                                                          
 


                                      
 
                                      
 

                                     
                                      
 
                                         
 









                                                                        
 
     
 







                                                         




                                                                       

     
                                                   
 

       
                                                
 


                                                               
 
                                                                           



                                                                 
 

                                                                

                                                               
 

     
                          
 

       
                                                                    
 


                                                               
 
                                                                      
 
                                                                    
 
                                                   
 
                                                               
                                                               

                                                            
 
                                                                    
 

     
                        
 

       
                                                                       
 

                                                                           
 
                                                                       
 
                                                          
            
 
                                                                          
 
                                                       
            
 

     
                        
 

       
                                                             
 

                                                            
 
                                                                     
 

                                                            
 
                                                                        
 

                                                         
 

     
                                         
 
       
 



                                                      
                                                      
 
















                                                                          
 
                                                       
 

     








                                                                   


                                                                 
                                                             
                                                               


                                                                     
                                                     



                                                                          
    
#! /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 <key>=<value>\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 <key>=<value>\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<none>";
                        "  $_:\n$subLines\n";
                    } elsif (ref($elemVal) eq 'ARRAY') {
                        my $subLines 
                            = join( "\n", map { "$ind$_"    } sort @$elemVal);
                        $subLines ||= "$ind<none>";
                        "  $_:\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) 
                    : '<none>';
            $_;
        }
        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) 
                    : '<none>';
            $_;
        }
        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' => '<<<default>>>'});
        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 '<<<default>>>') {
        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 '<<<default>>>') {
        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 '<<<default>>>') {
        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 '<<<default>>>') {
        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 <<<default>>>
    $name = '<<<default>>>' if $name eq 'default';

    return $name;
}

=head1 NAME

slxconfig - OpenSLX-script to view & change the configurational database

=head1 SYNOPSIS

slxconfig [options] <action> <key-value-pairs>

=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 <client-name> mac=<MAC> [<key>=<value> ...] >>

adds a new client to the config-DB

=item B<<     add-system <system-name> [export=<export-name>] \ >>

=item B<<                <key>=<value> ...] >>

adds a new system to the config-DB

=item B<<     add-group <group-name> [priority=<Number>] [<key>=<value> ...] >>

adds a new group to the config-DB

=item B<<     change-vendor-os <vendor-os-name> [<key>=<value> ...] >>

changes the data of an existing vendor-OS in the config-DB.

=item B<<     change-export <export-name> [<key>=<value> ...] >>

changes the data of an existing export in the config-DB

=item B<<     change-client <client-name> [<key>=<value> ...] >>

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 <group-name> [<key>=<value> ...] >>

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 <system-name> [<key>=<value> ...] >>

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 [<attr-scope-or-attr-name>] >>

lists all attributes, the ones in the given scope or the one with the given
name

=item B<<     list-client [<client-name>] >>

lists client with given name

=item B<<     list-export [<export-name>] >>

lists export with given name

=item B<<     list-group [<group-name>] >>

lists group with given name

=item B<<     list-system [<system-name>] >>

lists system with given name

=item B<<     list-vendor-os [<vendorOS-name>] >>

lists vendor-OS with given name

=item B<<     remove-client <client-name> >>

removes a client from the config-DB

=item B<<     remove-group <group-name> >>

removes a group from the config-DB

=item B<<     remove-system <system-name> >>

removes a system from the config-DB

=item B<<     search-client [<key>=<value> ...] >>

shows all clients in config-DB (optionally matching given criteria)

=item B<<     search-export [<key>=<value> ...] >>

shows all exports in config-DB (optionally matching given criteria)

=item B<<     search-group [<key>=<value> ...] >>

shows all groups in config-DB (optionally matching given criteria)

=item B<<     search-system [<key>=<value> ...] >>

shows all systems in config-DB (optionally matching given
criteria)

=item B<<     search-vendor-os [<key>=<value> ...] >>

shows all vendor-OSes in config-DB (optionally matching given criteria)

=back

=head1 DESCRIPTION

B<slxconfig> 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 "<<<default>>>" >>

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 <scope> >>

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 <client-name> >>

=item B<<     slxconfig remove-group <group-name> >>

=item B<<     slxconfig remove-system <system-name> >>

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=<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