2009-05-31 18:52:35 +00:00
package xCAT::VMCommon ;
2009-08-19 15:14:38 +00:00
use Socket ;
2009-05-31 18:52:35 +00:00
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" ) ;
2010-05-16 23:03:33 +00:00
my $ vpdtab = xCAT::Table - > new ( "vpd" ) ;
2009-05-31 18:52:35 +00:00
my $ hmtab = xCAT::Table - > new ( "nodehm" ) ;
my $ nttab = xCAT::Table - > new ( "nodetype" ) ;
2009-08-19 15:14:38 +00:00
my $ sitetab = xCAT::Table - > new ( "site" ) ;
$ cfghash - > { site } - > { genmacprefix } = xCAT::Utils - > get_site_attribute ( 'genmacprefix' ) ;
2009-05-31 18:52:35 +00:00
if ( $ hmtab ) {
$ cfghash - > { nodehm } = $ hmtab - > getNodesAttribs ( $ noderange , [ 'serialspeed' ] ) ;
}
if ( $ nttab ) {
2010-08-27 23:46:06 +00:00
$ cfghash - > { nodetype } = $ nttab - > getNodesAttribs ( $ noderange , [ 'os' , 'arch' , 'profile' ] ) ; #allow us to guess RTC config
#also needed for vmware guestid and also data for vmmaster
2009-05-31 18:52:35 +00:00
}
unless ( $ vmtab ) {
$ callback - > ( { data = > [ "Cannot open vm table" ] } ) ;
return ;
}
2010-05-16 23:03:33 +00:00
if ( $ vpdtab ) {
$ cfghash - > { vpd } = $ vpdtab - > getNodesAttribs ( $ noderange , [ 'uuid' ] ) ;
}
2011-02-08 21:23:39 +00:00
$ cfghash - > { vm } = $ vmtab - > getNodesAttribs ( $ noderange , [ 'node' , 'host' , 'migrationdest' , 'cfgstore' , 'storage' , 'storagemodel' , 'memory' , 'cpus' , 'nics' , 'nicmodel' , 'bootorder' , 'virtflags' , 'datacenter' , 'guestostype' , 'othersettings' , 'master' ] ) ;
2009-05-31 18:52:35 +00:00
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/\!.*// ;
2009-10-06 16:13:58 +00:00
if ( $ cfghash - > { usedmacs } - > { lc ( $ mac ) } ) {
$ cfghash - > { usedmacs } - > { lc ( $ mac ) } += 1 ;
} else {
$ cfghash - > { usedmacs } - > { lc ( $ mac ) } = 1 ;
}
2009-05-31 18:52:35 +00:00
}
}
}
2009-10-06 16:13:58 +00:00
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 ;
}
2009-11-27 15:14:20 +00:00
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 ;
2011-03-09 20:22:57 +00:00
my $ vpdupdates ;
2009-11-27 15:14:20 +00:00
srand ( ) ; #Re-seed the rng. This will make the mac address generation less deterministic
2009-12-02 00:37:04 +00:00
while ( not $ complete and scalar @$ neededmacs ) {
2009-11-27 15:14:20 +00:00
foreach $ node ( @$ neededmacs ) {
2009-11-28 00:50:14 +00:00
my $ nicdata = $ tablecfg - > { vm } - > { $ node } - > [ 0 ] - > { nics } ;
2009-11-27 15:14:20 +00:00
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 ) {
2009-11-28 00:50:14 +00:00
$ updatesneeded - > { $ node } - > { mac } = 1 ;
2009-11-27 15:14:20 +00:00
}
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 {
2011-02-11 14:29:41 +00:00
$ macdata . = "|" . $ macaddr . "!*NOIP*" ;
2009-11-27 15:14:20 +00:00
}
push @ macs , $ macaddr ;
}
if ( defined $ updatesneeded - > { $ node } ) {
$ updatesneeded - > { $ node } - > { mac } = $ macdata ;
$ tablecfg - > { dhcpneeded } - > { $ node } = 1 ; #at our leisure, this dhcp binding should be updated
}
2011-03-09 20:22:57 +00:00
#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
}
2009-11-27 15:14:20 +00:00
#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
}
2011-03-09 20:22:57 +00:00
if ( defined $ vpdupdates ) {
my $ vpdtab = xCAT::Table - > new ( 'vpd' , - create = > 1 ) ;
$ vpdtab - > setNodesAttribs ( $ vpdupdates ) ;
}
2009-11-27 15:14:20 +00:00
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}};
}
2009-08-19 15:14:38 +00:00
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 ;
}
2009-10-02 18:04:43 +00:00
srand ( ) ; #Re-seed the rng. This will make the mac address generation less deterministic
2009-10-06 16:13:58 +00:00
while ( $ count > 0 ) {
2009-08-19 15:14:38 +00:00
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 {
2010-07-30 12:59:46 +00:00
$ macdata . = "|" . $ macaddr . "*NOIP*" ;
2009-08-19 15:14:38 +00:00
}
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
}
2009-10-06 16:13:58 +00:00
#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 = "" ;
}
}
2009-08-19 15:14:38 +00:00
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;
2010-05-12 17:12:36 +00:00
if ( $ prefix eq '00:50:56' ) { #vmware reserves certain addresses in their scheme if this prefix used
2010-05-14 12:29:05 +00:00
$ tail = $ tail & 0x3fffff ; #mask out the two bits in question
2010-05-12 17:12:36 +00:00
}
2009-08-19 15:14:38 +00:00
$ tail = sprintf ( "%06x" , $ tail ) ;
$ tail =~ s/(..)(..)(..)/:$1:$2:$3/ ;
return $ prefix . $ tail ;
}
2010-09-27 18:49:50 +00:00
#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 ) ) ;
2010-09-27 19:04:38 +00:00
$ leading = $ leading | 0x4200 ;
2009-08-19 15:14:38 +00:00
#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 ;
}
2009-05-31 18:52:35 +00:00
1 ;