# 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/
# -----------------------------------------------------------------------------
package OpenSLX::ConfigDB;
use strict;
use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
$VERSION = 1.01; # API-version . implementation-version
################################################################################
### This module defines the data abstraction layer for the OpenSLX configuration
### database.
### Aim of this abstraction is to hide the details of the data layout and
### the peculiarities of individual database types behind a simple interface
### that offers straightforward access to and manipulation of the
### OpenSLX-systems and -clients (without the need to use SQL).
### The interface is divided into four parts:
### - data access methods (getting data)
### - data manipulation methods (adding, removing and changing data)
### - data aggregation methods (combining data in ways useful for apps)
### - support methods
################################################################################
use Exporter;
@ISA = qw(Exporter);
my @supportExports = qw(
isAttribute mergeAttributes
externalIDForSystem externalIDForClient
externalAttrName generatePlaceholderFor
);
@EXPORT = ();
@EXPORT_OK = (@supportExports);
%EXPORT_TAGS = (
'support' => [ @supportExports ],
);
################################################################################
### private stuff
################################################################################
use Carp;
use OpenSLX::Basics;
use OpenSLX::DBSchema;
sub _checkAndUpgradeDBSchemaIfNecessary
{
my $metaDB = shift;
vlog 2, "trying to determine schema version...";
my $currVersion = $metaDB->schemaFetchDBVersion();
if (!defined $currVersion) {
# that's bad, someone has messed with our DB, as there is a
# database, but the 'meta'-table is empty. There might still
# be data in the other tables, but we have no way to find out
# which schema version they're in. So it's safer to give up:
croak _tr('Could not determine schema version of database');
}
if ($currVersion < $DbSchema->{version}) {
vlog 1, _tr('Our schema-version is %s, DB is %s, upgrading DB...',
$DbSchema->{version}, $currVersion);
foreach my $v (sort { $a <=> $b } keys %DbSchemaHistory) {
next if $v <= $currVersion;
my $changeSet = $DbSchemaHistory{$v};
foreach my $c (0..scalar(@$changeSet)-1) {
my $changeDescr = @{$changeSet}[$c];
my $cmd = $changeDescr->{cmd};
if ($cmd eq 'add-table') {
$metaDB->schemaAddTable($changeDescr->{'table'},
$changeDescr->{'cols'},
$changeDescr->{'vals'});
} elsif ($cmd eq 'drop-table') {
$metaDB->schemaDropTable($changeDescr->{'table'});
} elsif ($cmd eq 'rename-table') {
$metaDB->schemaRenameTable($changeDescr->{'old-table'},
$changeDescr->{'new-table'},
$changeDescr->{'cols'});
} elsif ($cmd eq 'add-columns') {
$metaDB->schemaAddColumns($changeDescr->{'table'},
$changeDescr->{'new-cols'},
$changeDescr->{'cols'});
} elsif ($cmd eq 'drop-columns') {
$metaDB->schemaDropColumns($changeDescr->{'table'},
$changeDescr->{'drop-cols'},
$changeDescr->{'cols'});
} elsif ($cmd eq 'rename-columns') {
$metaDB->schemaRenameColumns($changeDescr->{'table'},
$changeDescr->{'col-renames'},
$changeDescr->{'cols'});
} else {
confess _tr('UnknownDbSchemaCommand', $cmd);
}
}
}
vlog 1, _tr('upgrade done');
} else {
vlog 1, _tr('DB matches current schema version %s', $currVersion);
}
}
sub _aref
{ # transparently converts the given reference to an array-ref
my $ref = shift;
return [] unless defined $ref;
$ref = [ $ref ] unless ref($ref) eq 'ARRAY';
return $ref;
}
sub _unique
{ # return given array filtered to unique elements
my %seenIDs;
return grep { !$seenIDs{$_}++; } @_;
}
################################################################################
### data access interface
################################################################################
sub new
{
my $class = shift;
my $self = {};
return bless $self, $class;
}
sub connect
{
my $self = shift;
my $dbParams = shift;
# hash-ref with any additional info that might be required by
# specific metadb-module (not used yet)
my $dbType = $openslxConfig{'db-type'};
# name of underlying database module
my $dbModule = "OpenSLX::MetaDB::$dbType";
unless (eval "require $dbModule") {
if ($! == 2) {
die _tr("Unable to load DB-module <%s>\nthat database type is not supported (yet?)\n",
$dbModule);
} else {
die _tr("Unable to load DB-module <%s> (%s)\n", $dbModule, $@);
}
}
my $modVersion = $dbModule->VERSION;
if ($modVersion < $VERSION) {
confess _tr('Could not load module <%s> (Version <%s> required, but <%s> found)',
$dbModule, $VERSION, $modVersion);
}
my $metaDB = $dbModule->new();
$metaDB->connect($dbParams);
$self->{'db-type'} = $dbType;
$self->{'meta-db'} = $metaDB;
foreach my $tk (keys %{$DbSchema->{tables}}) {
$metaDB->schemaDeclareTable($tk, $DbSchema->{tables}->{$tk});
}
_checkAndUpgradeDBSchemaIfNecessary($metaDB);
}
sub disconnect
{
my $self = shift;
$self->{'meta-db'}->disconnect();
}
sub fetchVendorOSByFilter
{
my $self = shift;
my $filter = shift;
my $resultCols = shift;
my @vendorOS
= $self->{'meta-db'}->fetchVendorOSByFilter($filter, $resultCols);
return wantarray() ? @vendorOS : shift @vendorOS;
}
sub fetchVendorOSByID
{
my $self = shift;
my $ids = _aref(shift);
my $resultCols = shift;
my @vendorOS
= $self->{'meta-db'}->fetchVendorOSByID($ids, $resultCols);
return wantarray() ? @vendorOS : shift @vendorOS;
}
sub fetchExportByFilter
{
my $self = shift;
my $filter = shift;
my $resultCols = shift;
my @exports
= $self->{'meta-db'}->fetchExportByFilter($filter, $resultCols);
return wantarray() ? @exports : shift @exports;
}
sub fetchExportByID
{
my $self = shift;
my $ids = _aref(shift);
my $resultCols = shift;
my @exports
= $self->{'meta-db'}->fetchExportByID($ids, $resultCols);
return wantarray() ? @exports : shift @exports;
}
sub fetchExportIDsOfVendorOS
{
my $self = shift;
my $vendorOSID = shift;
return $self->{'meta-db'}->fetchExportIDsOfVendorOS($vendorOSID);
}
sub fetchSystemByFilter
{
my $self = shift;
my $filter = shift;
my $resultCols = shift;
my @systems
= $self->{'meta-db'}->fetchSystemByFilter($filter, $resultCols);
return wantarray() ? @systems : shift @systems;
}
sub fetchSystemByID
{
my $self = shift;
my $ids = _aref(shift);
my $resultCols = shift;
my @systems = $self->{'meta-db'}->fetchSystemByID($ids, $resultCols);
return wantarray() ? @systems : shift @systems;
}
sub fetchSystemIDsOfExport
{
my $self = shift;
my $exportID = shift;
return $self->{'meta-db'}->fetchSystemIDsOfExport($exportID);
}
sub fetchSystemIDsOfClient
{
my $self = shift;
my $clientID = shift;
return $self->{'meta-db'}->fetchSystemIDsOfClient($clientID);
}
sub fetchSystemIDsOfGroup
{
my $self = shift;
my $groupID = shift;
return $self->{'meta-db'}->fetchSystemIDsOfGroup($groupID);
}
sub fetchClientByFilter
{
my $self = shift;
my $filter = shift;
my @clients = $self->{'meta-db'}->fetchClientByFilter($filter);
return wantarray() ? @clients : shift @clients;
}
sub fetchClientByID
{
my $self = shift;
my $ids = _aref(shift);
my $resultCols = shift;
my @clients = $self->{'meta-db'}->fetchClientByID($ids, $resultCols);
return wantarray() ? @clients : shift @clients;
}
sub fetchClientIDsOfSystem
{
my $self = shift;
my $systemID = shift;
return $self->{'meta-db'}->fetchClientIDsOfSystem($systemID);
}
sub fetchClientIDsOfGroup
{
my $self = shift;
my $groupID = shift;
return $self->{'meta-db'}->fetchClientIDsOfGroup($groupID);
}
sub fetchGroupByFilter
{
my $self = shift;
my $filter = shift;
my $resultCols = shift;
my @groups
= $self->{'meta-db'}->fetchGroupByFilter($filter, $resultCols);
return wantarray() ? @groups : shift @groups;
}
sub fetchGroupByID
{
my $self = shift;
my $ids = _aref(shift);
my $resultCols = shift;
my @groups = $self->{'meta-db'}->fetchGroupByID($ids, $resultCols);
return wantarray() ? @groups : shift @groups;
}
sub fetchGroupIDsOfSystem
{
my $self = shift;
my $systemID = shift;
return $self->{'meta-db'}->fetchGroupIDsOfSystem($systemID);
}
sub fetchGroupIDsOfClient
{
my $self = shift;
my $clientID = shift;
return $self->{'meta-db'}->fetchGroupIDsOfClient($clientID);
}
sub fetchSettings
{
my $self = shift;
my @rows = $self->{'meta-db'}->fetchSettings();
return shift @rows;
}
################################################################################
### data manipulation interface
################################################################################
sub addVendorOS
{
my $self = shift;
my $valRows = _aref(shift);
return $self->{'meta-db'}->addVendorOS($valRows);
}
sub removeVendorOS
{
my $self = shift;
my $vendorOSIDs = _aref(shift);
return $self->{'meta-db'}->removeVendorOS($vendorOSIDs);
}
sub changeVendorOS
{
my $self = shift;
my $vendorOSIDs = _aref(shift);
my $valRows = _aref(shift);
return $self->{'meta-db'}->changeVendorOS($vendorOSIDs, $valRows);
}
sub addExport
{
my $self = shift;
my $valRows = _aref(shift);
return $self->{'meta-db'}->addExport($valRows);
}
sub removeExport
{
my $self = shift;
my $exportIDs = _aref(shift);
return $self->{'meta-db'}->removeExport($exportIDs);
}
sub changeExport
{
my $self = shift;
my $exportIDs = _aref(shift);
my $valRows = _aref(shift);
return $self->{'meta-db'}->changeExport($exportIDs, $valRows);
}
sub addSystem
{
my $self = shift;
my $valRows = _aref(shift);
foreach my $valRow (@$valRows) {
if (!length($valRow->{kernel})) {
$valRow->{kernel} = 'vmlinuz';
}
if (!length($valRow->{label})) {
$valRow->{label} = $valRow->{name};
}
if (!length($valRow->{ramfs_debug_level})) {
$valRow->{ramfs_debug_level} = '0';
}
if (!length($valRow->{ramfs_use_glibc})) {
$valRow->{ramfs_use_glibc} = '0';
}
if (!length($valRow->{ramfs_use_busybox})) {
$valRow->{ramfs_use_busybox} = '1';
}
}
return $self->{'meta-db'}->addSystem($valRows);
}
sub removeSystem
{
my $self = shift;
my $systemIDs = _aref(shift);
foreach my $system (@$systemIDs) {
$self->setGroupIDsOfSystem($system);
$self->setClientIDsOfSystem($system);
}
return $self->{'meta-db'}->removeSystem($systemIDs);
}
sub changeSystem
{
my $self = shift;
my $systemIDs = _aref(shift);
my $valRows = _aref(shift);
return $self->{'meta-db'}->changeSystem($systemIDs, $valRows);
}
sub setClientIDsOfSystem
{
my $self = shift;
my $systemID = shift;
my $clientIDs = _aref(shift);
my @uniqueClientIDs = _unique(@$clientIDs);
return $self->{'meta-db'}->setClientIDsOfSystem($systemID,
\@uniqueClientIDs);
}
sub addClientIDsToSystem
{
my $self = shift;
my $systemID = shift;
my $newClientIDs = _aref(shift);
my @clientIDs = $self->{'meta-db'}->fetchClientIDsOfSystem($systemID);
push @clientIDs, @$newClientIDs;
return $self->setClientIDsOfSystem($systemID, \@clientIDs);
}
sub removeClientIDsFromSystem
{
my $self = shift;
my $systemID = shift;
my $removedClientIDs = _aref(shift);
my %toBeRemoved;
@toBeRemoved{@$removedClientIDs} = ();
my @clientIDs
= grep { !exists $toBeRemoved{$_} }
$self->{'meta-db'}->fetchClientIDsOfSystem($systemID);
return $self->setClientIDsOfSystem($systemID, \@clientIDs);
}
sub setGroupIDsOfSystem
{
my $self = shift;
my $systemID = shift;
my $groupIDs = _aref(shift);
my @uniqueGroupIDs = _unique(@$groupIDs);
return $self->{'meta-db'}->setGroupIDsOfSystem($systemID,
\@uniqueGroupIDs);
}
sub addGroupIDsToSystem
{
my $self = shift;
my $systemID = shift;
my $newGroupIDs = _aref(shift);
my @groupIDs = $self->{'meta-db'}->fetchGroupIDsOfSystem($systemID);
push @groupIDs, @$newGroupIDs;
return $self->setGroupIDsOfSystem($systemID, \@groupIDs);
}
sub removeGroupIDsFromSystem
{
my $self = shift;
my $systemID = shift;
my $toBeRemovedGroupIDs = _aref(shift);
my %toBeRemoved;
@toBeRemoved{@$toBeRemovedGroupIDs} = ();
my @groupIDs
= grep { !exists $toBeRemoved{$_} }
$self->{'meta-db'}->fetchGroupIDsOfSystem($systemID);
return $self->setGroupIDsOfSystem($systemID, \@groupIDs);
}
sub addClient
{
my $self = shift;
my $valRows = _aref(shift);
foreach my $valRow (@$valRows) {
if (!length($valRow->{boot_type})) {
$valRow->{boot_type} = 'pxe';
}
}
return $self->{'meta-db'}->addClient($valRows);
}
sub removeClient
{
my $self = shift;
my $clientIDs = _aref(shift);
foreach my $client (@$clientIDs) {
$self->setGroupIDsOfClient($client);
$self->setSystemIDsOfClient($client);
}
return $self->{'meta-db'}->removeClient($clientIDs);
}
sub changeClient
{
my $self = shift;
my $clientIDs = _aref(shift);
my $valRows = _aref(shift);
return $self->{'meta-db'}->changeClient($clientIDs, $valRows);
}
sub setSystemIDsOfClient
{
my $self = shift;
my $clientID = shift;
my $systemIDs = _aref(shift);
my @uniqueSystemIDs = _unique(@$systemIDs);
return $self->{'meta-db'}->setSystemIDsOfClient($clientID,
\@uniqueSystemIDs);
}
sub addSystemIDsToClient
{
my $self = shift;
my $clientID = shift;
my $newSystemIDs = _aref(shift);
my @systemIDs = $self->{'meta-db'}->fetchSystemIDsOfClient($clientID);
push @systemIDs, @$newSystemIDs;
return $self->setSystemIDsOfClient($clientID, \@systemIDs);
}
sub removeSystemIDsFromClient
{
my $self = shift;
my $clientID = shift;
my $removedSystemIDs = _aref(shift);
my %toBeRemoved;
@toBeRemoved{@$removedSystemIDs} = ();
my @systemIDs
= grep { !exists $toBeRemoved{$_} }
$self->{'meta-db'}->fetchSystemIDsOfClient($clientID);
return $self->setSystemIDsOfClient($clientID, \@systemIDs);
}
sub setGroupIDsOfClient
{
my $self = shift;
my $clientID = shift;
my $groupIDs = _aref(shift);
my @uniqueGroupIDs = _unique(@$groupIDs);
return $self->{'meta-db'}->setGroupIDsOfClient($clientID,
\@uniqueGroupIDs);
}
sub addGroupIDsToClient
{
my $self = shift;
my $clientID = shift;
my $newGroupIDs = _aref(shift);
my @groupIDs = $self->{'meta-db'}->fetchGroupIDsOfClient($clientID);
push @groupIDs, @$newGroupIDs;
return $self->setGroupIDsOfClient($clientID, \@groupIDs);
}
sub removeGroupIDsFromClient
{
my $self = shift;
my $clientID = shift;
my $toBeRemovedGroupIDs = _aref(shift);
my %toBeRemoved;
@toBeRemoved{@$toBeRemovedGroupIDs} = ();
my @groupIDs
= grep { !exists $toBeRemoved{$_} }
$self->{'meta-db'}->fetchGroupIDsOfClient($clientID);
return $self->setGroupIDsOfClient($clientID, \@groupIDs);
}
sub addGroup
{
my $self = shift;
my $valRows = _aref(shift);
return $self->{'meta-db'}->addGroup($valRows);
}
sub removeGroup
{
my $self = shift;
my $groupIDs = _aref(shift);
foreach my $group (@$groupIDs) {
$self->setSystemIDsOfGroup($group, []);
$self->setClientIDsOfGroup($group, []);
}
return $self->{'meta-db'}->removeGroup($groupIDs);
}
sub changeGroup
{
my $self = shift;
my $groupIDs = _aref(shift);
my $valRows = _aref(shift);
return $self->{'meta-db'}->changeGroup($groupIDs, $valRows);
}
sub setClientIDsOfGroup
{
my $self = shift;
my $groupID = shift;
my $clientIDs = _aref(shift);
my @uniqueClientIDs = _unique(@$clientIDs);
return $self->{'meta-db'}->setClientIDsOfGroup($groupID,
\@uniqueClientIDs);
}
sub addClientIDsToGroup
{
my $self = shift;
my $groupID = shift;
my $newClientIDs = _aref(shift);
my @clientIDs = $self->{'meta-db'}->fetchClientIDsOfGroup($groupID);
push @clientIDs, @$newClientIDs;
return $self->setClientIDsOfGroup($groupID, \@clientIDs);
}
sub removeClientIDsFromGroup
{
my $self = shift;
my $groupID = shift;
my $removedClientIDs = _aref(shift);
my %toBeRemoved;
@toBeRemoved{@$removedClientIDs} = ();
my @clientIDs
= grep { !exists $toBeRemoved{$_} }
$self->{'meta-db'}->fetchClientIDsOfGroup($groupID);
return $self->setClientIDsOfGroup($groupID, \@clientIDs);
}
sub setSystemIDsOfGroup
{
my $self = shift;
my $groupID = shift;
my $systemIDs = _aref(shift);
my @uniqueSystemIDs = _unique(@$systemIDs);
return $self->{'meta-db'}->setSystemIDsOfGroup($groupID,
\@uniqueSystemIDs);
}
sub addSystemIDsToGroup
{
my $self = shift;
my $groupID = shift;
my $newSystemIDs = _aref(shift);
my @systemIDs = $self->{'meta-db'}->fetchSystemIDsOfGroup($groupID);
push @systemIDs, @$newSystemIDs;
return $self->setSystemIDsOfGroup($groupID, \@systemIDs);
}
sub removeSystemIDsFromGroup
{
my $self = shift;
my $groupID = shift;
my $removedSystemIDs = _aref(shift);
my %toBeRemoved;
@toBeRemoved{@$removedSystemIDs} = ();
my @systemIDs
= grep { !exists $toBeRemoved{$_} }
$self->{'meta-db'}->fetchSystemIDsOfGroup($groupID);
return $self->setSystemIDsOfGroup($groupID, \@systemIDs);
}
sub emptyDatabase
{ # clears all user-data from the database
my $self = shift;
my @groupIDs
= map { $_->{id} }
$self->fetchGroupByFilter();
$self->removeGroup(\@groupIDs);
my @clientIDs
= map { $_->{id} }
grep { $_->{id} > 0 }
$self->fetchClientByFilter();
$self->removeClient(\@clientIDs);
my @sysIDs
= map { $_->{id} }
grep { $_->{id} > 0 }
$self->fetchSystemByFilter();
$self->removeSystem(\@sysIDs);
my @exportIDs
= map { $_->{id} }
grep { $_->{id} > 0 }
$self->fetchExportByFilter();
$self->removeExport(\@exportIDs);
my @vendorOSIDs
= map { $_->{id} }
grep { $_->{id} > 0 }
$self->fetchVendorOSByFilter();
$self->removeVendorOS(\@vendorOSIDs);
}
sub changeSettings
{
my $self = shift;
my $settings = shift;
return $self->{'meta-db'}->changeSettings($settings);
}
################################################################################
### data aggregation interface
################################################################################
sub mergeDefaultAttributesIntoSystem
{ # merge default system configuration into given system
my $self = shift;
my $system = shift;
my $defaultSystem = shift;
$defaultSystem = $self->fetchSystemByID(0)
unless defined $defaultSystem;
mergeAttributes($system, $defaultSystem);
}
sub mergeDefaultAndGroupAttributesIntoClient
{ # merge default and group configurations into given client
my $self = shift;
my $client = shift;
# step over all groups this client belongs to
# (ordered by priority from highest to lowest):
my @groupIDs = $self->fetchGroupIDsOfClient($client->{id});
my @groups = sort { $b->{priority} <=> $a->{priority} }
$self->fetchGroupByID(\@groupIDs);
foreach my $group (@groups) {
# merge configuration from this group into the current client:
vlog 3, _tr('merging from group %d:%s...', $group->{id}, $group->{name});
mergeAttributes($client, $group);
}
# merge configuration from default client:
vlog 3, _tr('merging from default client...');
my $defaultClient = $self->fetchClientByID(0);
mergeAttributes($client, $defaultClient);
}
sub aggregatedSystemIDsOfClient
{ # return aggregated list of system-IDs this client should offer
# (as indicated by itself, the default client and the client's groups)
my $self = shift;
my $client = shift;
# add all systems directly linked to client:
my @systemIDs = $self->fetchSystemIDsOfClient($client->{id});
# step over all groups this client belongs to:
my @groupIDs = $self->fetchGroupIDsOfClient($client->{id});
my @groups = $self->fetchGroupByID(\@groupIDs);
foreach my $group (@groups) {
# add all systems that the client inherits from the current group:
push @systemIDs, $self->fetchSystemIDsOfGroup($group->{id});
}
# add all systems inherited from default client
push @systemIDs, $self->fetchSystemIDsOfClient(0);
return _unique(@systemIDs);
}
sub aggregatedClientIDsOfSystem
{ # return aggregated list of client-IDs this system is linked to
# (as indicated by itself, the default system and the system's groups)
my $self = shift;
my $system = shift;
# add all clients directly linked to system:
my @clientIDs = $self->fetchClientIDsOfSystem($system->{id});
# step over all groups this system belongs to:
my @groupIDs = $self->fetchGroupIDsOfSystem($system->{id});
my @groups = $self->fetchGroupByID(\@groupIDs);
foreach my $group (@groups) {
# add all clients that the system inherits from the current group:
push @clientIDs, $self->fetchClientIDsOfGroup($group->{id});
}
# add all clients inherited from default system
push @clientIDs, $self->fetchClientIDsOfSystem(0);
return _unique(@clientIDs);
}
sub aggregatedSystemFileInfoFor
{ # return aggregated information about the kernel and initialramfs
# this system is using
my $self = shift;
my $system = shift;
my $export = $self->fetchExportByID($system->{export_id});
if (!defined $export) {
die _tr("DB-problem: system '%s' references export with id=%s, but that doesn't exist!",
$system->{name}, $system->{export_id});
}
my $vendorOS = $self->fetchVendorOSByID($export->{vendor_os_id});
if (!defined $vendorOS) {
die _tr("DB-problem: export '%s' references vendor-OS with id=%s, but that doesn't exist!",
$export->{name}, $export->{vendor_os_id});
}
my $kernelPath
= "$openslxConfig{'private-path'}/stage1/$vendorOS->{name}/boot";
my $exportURI = $export->{'uri'};
if ($exportURI !~ m[\w]) {
# auto-generate export_uri if none has been given:
my $type = $export->{'type'};
my $serverIpToken = generatePlaceholderFor('serverip');
$exportURI
= "$type://$serverIpToken$openslxConfig{'export-path'}/$type/$vendorOS->{name}";
}
my $info = { %$system };
$info->{'kernel-file'} = "$kernelPath/$system->{kernel}";
$info->{'export-uri'} = $exportURI;
$info->{'export'} = $export;
$info->{'vendor-os'} = $vendorOS;
return $info;
}
################################################################################
### support interface
################################################################################
sub isAttribute
{ # returns whether or not the given key is an exportable attribute
my $key = shift;
return $key =~ m[^attr_];
}
sub mergeAttributes
{ # copies all attributes of source that are unset in target over
my $target = shift;
my $source = shift;
foreach my $key (grep { isAttribute($_) } keys %$source) {
if (length($source->{$key}) > 0 && length($target->{$key}) == 0) {
vlog 3, _tr("merging %s (val=%s)", $key, $source->{$key});
$target->{$key} = $source->{$key};
}
}
}
sub externalIDForSystem
{
my $system = shift;
return "default" if $system->{id} == 0;
my $externalID = $system->{name};
$externalID =~ s[\s+][_]g;
# replace any whitespace in name, such that the external ID can
# be used as a directory name (without complications)
return $externalID;
}
sub externalIDForClient
{
my $client = shift;
return "default" if $client->{id} == 0;
my $mac = lc($client->{mac});
# PXE seems to expect MACs being all lowercase
$mac =~ tr[:][-];
return "01-$mac";
}
sub externalAttrName
{
my $attr = shift;
return substr($attr, 5);
}
sub generatePlaceholderFor
{
my $varName = shift;
return '@@@'.$varName.'@@@';
}
1;