git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@9897 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
		
			
				
	
	
		
			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','vidmodel','vidproto','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;
 |