020889ad3c
It has been requested to make these tunables available since different storage schemes can benefit greatly from them
272 lines
10 KiB
Perl
272 lines
10 KiB
Perl
package xCAT::VMCommon;
|
|
use Socket;
|
|
use strict;
|
|
#Functions common to virtualization management (KVM, Xen, VMware)
|
|
sub grab_table_data{ #grab table data relevent to VM guest nodes
|
|
my $noderange=shift;
|
|
my $cfghash = shift;
|
|
my $callback=shift;
|
|
my $vmtab = xCAT::Table->new("vm");
|
|
my $vpdtab = xCAT::Table->new("vpd");
|
|
my $hmtab = xCAT::Table->new("nodehm");
|
|
my $nttab = xCAT::Table->new("nodetype");
|
|
#my $sitetab = xCAT::Table->new("site");
|
|
$cfghash->{site}->{genmacprefix} = $::XCATSITEVALS{genmacprefix}; #xCAT::Utils->get_site_attribute('genmacprefix');
|
|
if ($hmtab) {
|
|
$cfghash->{nodehm} = $hmtab->getNodesAttribs($noderange,['serialspeed']);
|
|
}
|
|
if ($nttab) {
|
|
$cfghash->{nodetype} = $nttab->getNodesAttribs($noderange,['os','arch','profile']); #allow us to guess RTC config
|
|
#also needed for vmware guestid and also data for vmmaster
|
|
}
|
|
unless ($vmtab) {
|
|
$callback->({data=>["Cannot open vm table"]});
|
|
return;
|
|
}
|
|
if ($vpdtab) {
|
|
$cfghash->{vpd} = $vpdtab->getNodesAttribs($noderange,['uuid']);
|
|
}
|
|
$cfghash->{vm} = $vmtab->getNodesAttribs($noderange,['node','host','migrationdest','cfgstore','storage','storagecache','storageformat','vidmodel','vidproto','vidpassword','storagemodel','memory','cpus','nics','nicmodel','bootorder','virtflags','datacenter','guestostype','othersettings','master']);
|
|
my $mactab = xCAT::Table->new("mac",-create=>1);
|
|
my $nrtab= xCAT::Table->new("noderes",-create=>1);
|
|
$cfghash->{mac} = $mactab->getAllNodeAttribs(['mac'],1);
|
|
my $macs;
|
|
my $mac;
|
|
foreach (keys %{$cfghash->{mac}}) {
|
|
$macs=$cfghash->{mac}->{$_}->[0]->{mac};
|
|
foreach $mac (split /\|/,$macs) {
|
|
$mac =~ s/\!.*//;
|
|
if ($cfghash->{usedmacs}->{lc($mac)}) {
|
|
$cfghash->{usedmacs}->{lc($mac)} += 1;
|
|
} else {
|
|
$cfghash->{usedmacs}->{lc($mac)}=1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sub macsAreUnique { #internal function, do not call, argument format may change without warning
|
|
#Take a list of macs, ensure that in the table view, they are unique.
|
|
#this should be performed after the macs have been committed to
|
|
#db
|
|
my $cfghash = shift;
|
|
my @macs = @_;
|
|
my $mactab = xCAT::Table->new("mac",-create=>0);
|
|
unless ($mactab) {
|
|
return 1;
|
|
}
|
|
$cfghash->{mac} = $mactab->getAllNodeAttribs(['mac'],1);
|
|
$cfghash->{usedmacs} = {};
|
|
my $macs;
|
|
my $mac;
|
|
foreach (keys %{$cfghash->{mac}}) {
|
|
$macs=$cfghash->{mac}->{$_}->[0]->{mac};
|
|
foreach $mac (split /\|/,$macs) {
|
|
$mac =~ s/\!.*//;
|
|
if ($cfghash->{usedmacs}->{lc($mac)}) {
|
|
$cfghash->{usedmacs}->{lc($mac)} += 1;
|
|
} else {
|
|
$cfghash->{usedmacs}->{lc($mac)}=1;
|
|
}
|
|
}
|
|
}
|
|
foreach $mac (@macs) {
|
|
if ($cfghash->{usedmacs}->{lc($mac)} > 1) {
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
sub requestMacAddresses {
|
|
#This function combs through the list of nodes to assure every vm.nic declared nic has a mac address
|
|
my $tablecfg = shift;
|
|
my $neededmacs = shift;
|
|
my $mactab = xCAT::Table->new("mac",-create=>1);
|
|
my $node;
|
|
my @allmacs;
|
|
my $complete = 0;
|
|
my $updatesneeded;
|
|
my $vpdupdates;
|
|
srand(); #Re-seed the rng. This will make the mac address generation less deterministic
|
|
while (not $complete and scalar @$neededmacs) {
|
|
foreach $node (@$neededmacs) {
|
|
my $nicdata = $tablecfg->{vm}->{$node}->[0]->{nics};
|
|
unless ($nicdata) { $nicdata = "" }
|
|
my @nicsneeded = split /,/,$nicdata;
|
|
my $count = scalar(@nicsneeded);
|
|
|
|
my $macdata = $tablecfg->{mac}->{$node}->[0]->{mac};
|
|
unless ($macdata) { $macdata ="" }
|
|
my @macs;
|
|
my $macaddr;
|
|
foreach $macaddr (split /\|/,$macdata) {
|
|
$macaddr =~ s/\!.*//;
|
|
push @macs,lc($macaddr);
|
|
}
|
|
$count-=scalar(@macs);
|
|
if ($count > 0) {
|
|
$updatesneeded->{$node}->{mac}=1;
|
|
}
|
|
|
|
while ($count > 0) { #still need more, autogen
|
|
$macaddr = "";
|
|
while (not $macaddr) {
|
|
$macaddr = lc(genMac($node,$tablecfg->{site}->{genmacprefix}));
|
|
push @allmacs,$macaddr;
|
|
if ($tablecfg->{usedmacs}->{$macaddr}) {
|
|
$macaddr = "";
|
|
}
|
|
}
|
|
$count--;
|
|
$tablecfg->{usedmacs}->{$macaddr} = 1;
|
|
if (not $macdata) {
|
|
$macdata = $macaddr;
|
|
} else {
|
|
$macdata .= "|".$macaddr."!*NOIP*";
|
|
}
|
|
push @macs,$macaddr;
|
|
}
|
|
if (defined $updatesneeded->{$node}) {
|
|
$updatesneeded->{$node}->{mac}=$macdata;
|
|
$tablecfg->{dhcpneeded}->{$node}=1; #at our leisure, this dhcp binding should be updated
|
|
}
|
|
#now that macs are done, do simple uuid... (done after to benefit from having at least one mac address)
|
|
unless ($tablecfg->{vpd}->{$node}->[0]->{uuid}) {
|
|
my $umac = $macs[0];
|
|
my $uuid;
|
|
if ($umac) {
|
|
$uuid=xCAT::Utils::genUUID(mac=>$umac);
|
|
} else { #shouldn't be possible, but just in case
|
|
$uuid=xCAT::Utils::genUUID();
|
|
}
|
|
$vpdupdates->{$node}->{uuid}=$uuid;
|
|
$tablecfg->{vpd}->{$node}=[{uuid=>$uuid}];
|
|
$tablecfg->{dhcpneeded}->{$node}=1; #at our leisure, this dhcp binding should be updated
|
|
}
|
|
#TODO: LOCK if a distributed lock management structure goes in place, that may be a faster solution than this
|
|
#this code should be safe though as it is, if a tiny bit slower
|
|
#can also be sped up by doing it for a noderange in a sweep instead of once per node
|
|
#but the current architecture has this called at a place that is unaware of the larger context
|
|
#TODO2.4 would be either the lock management or changing this to make large scale mkvm faster
|
|
}
|
|
if (defined $vpdupdates) {
|
|
my $vpdtab = xCAT::Table->new('vpd',-create=>1);
|
|
$vpdtab->setNodesAttribs($vpdupdates);
|
|
}
|
|
if (defined $updatesneeded) {
|
|
my $mactab = xCAT::Table->new('mac',-create=>1);
|
|
$mactab->setNodesAttribs($updatesneeded);
|
|
if(macsAreUnique($tablecfg,@allmacs)) {
|
|
$complete=1;
|
|
} else { #Throw away ALL macs and try again
|
|
#this currently includes manually specified ones
|
|
foreach $node (keys %$updatesneeded) {
|
|
$tablecfg->{mac}->{$node}->[0]->{mac}="";
|
|
}
|
|
$tablecfg->{usedmacs} = {};
|
|
}
|
|
}
|
|
}
|
|
# $cfghash->{usedmacs}-{lc{$mac}};
|
|
}
|
|
sub getMacAddresses {
|
|
my $tablecfg = shift;
|
|
my $node = shift;
|
|
my $count = shift;
|
|
my $mactab = xCAT::Table->new("mac",-create=>1);
|
|
my $macdata = $tablecfg->{mac}->{$node}->[0]->{mac};
|
|
unless ($macdata) { $macdata ="" }
|
|
my @macs;
|
|
my $macaddr;
|
|
foreach $macaddr (split /\|/,$macdata) {
|
|
$macaddr =~ s/\!.*//;
|
|
push @macs,lc($macaddr);
|
|
}
|
|
$count-=scalar(@macs);
|
|
my $updatesneeded=0;
|
|
if ($count > 0) {
|
|
$updatesneeded = 1;
|
|
}
|
|
|
|
srand(); #Re-seed the rng. This will make the mac address generation less deterministic
|
|
while ($count > 0) {
|
|
while ($count > 0) { #still need more, autogen
|
|
$macaddr = "";
|
|
while (not $macaddr) {
|
|
$macaddr = lc(genMac($node,$tablecfg->{site}->{genmacprefix}));
|
|
if ($tablecfg->{usedmacs}->{$macaddr}) {
|
|
$macaddr = "";
|
|
}
|
|
}
|
|
$count--;
|
|
$tablecfg->{usedmacs}->{$macaddr} = 1;
|
|
if (not $macdata) {
|
|
$macdata = $macaddr;
|
|
} else {
|
|
$macdata .= "|".$macaddr."!*NOIP*";
|
|
}
|
|
push @macs,$macaddr;
|
|
}
|
|
if ($updatesneeded) {
|
|
my $mactab = xCAT::Table->new('mac',-create=>1);
|
|
$mactab->setNodeAttribs($node,{mac=>$macdata});
|
|
$tablecfg->{dhcpneeded}->{$node}=1; #at our leisure, this dhcp binding should be updated
|
|
}
|
|
#TODO: LOCK if a distributed lock management structure goes in place, that may be a faster solution than this
|
|
#this code should be safe though as it is, if a tiny bit slower
|
|
#can also be sped up by doing it for a noderange in a sweep instead of once per node
|
|
#but the current architecture has this called at a place that is unaware of the larger context
|
|
#TODO2.4 would be either the lock management or changing this to make large scale mkvm faster
|
|
unless (macsAreUnique($tablecfg,@macs)) { #Throw away ALL macs and try again
|
|
#this currently includes manually specified ones
|
|
$count += scalar(@macs);
|
|
@macs = ();
|
|
$macdata="";
|
|
}
|
|
}
|
|
|
|
return @macs;
|
|
# $cfghash->{usedmacs}-{lc{$mac}};
|
|
|
|
}
|
|
|
|
sub genMac { #Generates a mac address for a node, does NOT assure uniqueness, calling code needs to do that
|
|
my $node=shift;
|
|
my $prefix = shift;
|
|
if ($prefix) { #Specific prefix requested, honor it
|
|
my $tail = int(rand(0xffffff)); #With only 24 bits of space, use random bits;
|
|
if ($prefix eq '00:50:56') { #vmware reserves certain addresses in their scheme if this prefix used
|
|
$tail = $tail&0x3fffff; #mask out the two bits in question
|
|
}
|
|
$tail = sprintf("%06x",$tail);
|
|
$tail =~ s/(..)(..)(..)/:$1:$2:$3/;
|
|
return $prefix.$tail;
|
|
}
|
|
#my $allbutmult = 0xfeff; # to & with a number to ensure multicast bit is *not* set
|
|
#my $locallyadministered = 0x200; # to | with the 16 MSBs to indicate a local mac
|
|
#my $leading = int(rand(0xffff));
|
|
#$leading = $leading & $allbutmult;
|
|
#$leading = $leading | $locallyadministered;
|
|
#for the header, we used to use all 14 possible bits, however, if a guest mac starts with 0xfe then libvirt will construct a bridge that looks identical
|
|
#First thought was to go to 13 bits, but by fixing our generated mac addresses to always start with the same byte and still be unique
|
|
#this induces libvirt to do unique TAP mac addresses
|
|
my $leading = int(rand(0xff));
|
|
$leading = $leading | 0x4200;
|
|
#If this nodename is a resolvable name, we'll use that for the other 32 bits
|
|
my $low32;
|
|
my $n;
|
|
if ($n = inet_aton($node)) {
|
|
$low32= unpack("N",$n);
|
|
}
|
|
unless ($low32) { #If that failed, just do 32 psuedo-random bits
|
|
$low32 = int(rand(0xffffffff));
|
|
}
|
|
my $mac;
|
|
$mac = sprintf("%04x%08x",$leading,$low32);
|
|
$mac =~s/(..)(..)(..)(..)(..)(..)/$1:$2:$3:$4:$5:$6/;
|
|
return $mac;
|
|
|
|
}
|
|
1;
|