mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-11-04 05:12:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1092 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			1092 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
#!/usr/bin/env perl
 | 
						|
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
 | 
						|
package xCAT_plugin::xen;
 | 
						|
 | 
						|
BEGIN
 | 
						|
{
 | 
						|
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
 | 
						|
}
 | 
						|
use lib "$::XCATROOT/lib/perl";
 | 
						|
use xCAT::GlobalDef;
 | 
						|
use xCAT::NodeRange;
 | 
						|
use xCAT_monitoring::monitorctrl;
 | 
						|
 | 
						|
use xCAT::Table;
 | 
						|
use XML::Simple qw(XMLout);
 | 
						|
use IO::Socket;
 | 
						|
use IO::Select;
 | 
						|
use xCAT::Usage;
 | 
						|
use strict;
 | 
						|
 | 
						|
#use warnings;
 | 
						|
my %vm_comm_pids;
 | 
						|
my @destblacklist;
 | 
						|
my $vmhash;
 | 
						|
my $hmhash;
 | 
						|
my $bptab;
 | 
						|
my $bphash;
 | 
						|
 | 
						|
use XML::Simple;
 | 
						|
$XML::Simple::PREFERRED_PARSER = 'XML::Parser';
 | 
						|
use Data::Dumper;
 | 
						|
use POSIX "WNOHANG";
 | 
						|
use Storable qw(freeze thaw);
 | 
						|
use IO::Select;
 | 
						|
use IO::Handle;
 | 
						|
use Time::HiRes qw(gettimeofday sleep usleep);
 | 
						|
use xCAT::DBobjUtils;
 | 
						|
use Getopt::Long;
 | 
						|
use xCAT::SvrUtils;
 | 
						|
use xCAT::TableUtils;
 | 
						|
use xCAT::ServiceNodeUtils;
 | 
						|
 | 
						|
my %runningstates;
 | 
						|
my $vmmaxp = 64;
 | 
						|
my $mactab;
 | 
						|
my $nrtab;
 | 
						|
my $machash;
 | 
						|
my $status_noop = "XXXno-opXXX";
 | 
						|
 | 
						|
sub handled_commands {
 | 
						|
 | 
						|
    #unless ($libvirtsupport) {
 | 
						|
    #    return {};
 | 
						|
    #}
 | 
						|
    return {
 | 
						|
        rpower     => 'nodehm:power,mgt',
 | 
						|
        mkvm       => 'nodehm:power,mgt',
 | 
						|
        rmigrate   => 'nodehm:mgt',
 | 
						|
        getxencons => 'nodehm:mgt',
 | 
						|
 | 
						|
        #rvitals => 'nodehm:mgt',
 | 
						|
        #rinv => 'nodehm:mgt',
 | 
						|
        getrvidparms => 'nodehm:mgt',
 | 
						|
        rbeacon      => 'nodehm:mgt',
 | 
						|
        revacuate    => 'hypervisor:type',
 | 
						|
 | 
						|
        #rspreset => 'nodehm:mgt',
 | 
						|
        #rspconfig => 'nodehm:mgt',
 | 
						|
        #rbootseq => 'nodehm:mgt',
 | 
						|
        #reventlog => 'nodehm:mgt',
 | 
						|
        mkinstall => 'nodehm:mgt=(xen)',
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
my $virsh;
 | 
						|
my $vmhash;
 | 
						|
my $hypconn;
 | 
						|
my $hyp;
 | 
						|
my $doreq;
 | 
						|
my %hyphash;
 | 
						|
my $node;
 | 
						|
my $hmtab;
 | 
						|
my $vmtab;
 | 
						|
my $chaintab;
 | 
						|
my $chainhash;
 | 
						|
 | 
						|
sub waitforack {
 | 
						|
    my $sock   = shift;
 | 
						|
    my $select = new IO::Select;
 | 
						|
    $select->add($sock);
 | 
						|
    my $str;
 | 
						|
    if ($select->can_read(60)) { # Continue after 10 seconds, even if not acked...
 | 
						|
        if ($str = <$sock>) {
 | 
						|
        } else {
 | 
						|
            $select->remove($sock);    #Block until parent acks data
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
sub build_oshash {
 | 
						|
    my $node = shift;
 | 
						|
    my %rethash;
 | 
						|
    my $is_pv = $vmhash->{$node}->[0]->{'virtflags'} =~ 'paravirt' ? 1 : 0;
 | 
						|
    if ($is_pv) {
 | 
						|
        $rethash{type}->{content} = 'linux';
 | 
						|
    } else {
 | 
						|
        $rethash{type}->{content}   = 'hvm';
 | 
						|
        $rethash{loader}->{content} = '/usr/lib/xen/boot/hvmloader';
 | 
						|
        if (defined $vmhash->{$node}->[0]->{bootorder}) {
 | 
						|
            my $bootorder = $vmhash->{$node}->[0]->{bootorder};
 | 
						|
            my @bootdevs  = split(/[:,]/, $bootorder);
 | 
						|
            my $bootnum   = 0;
 | 
						|
            foreach (@bootdevs) {
 | 
						|
                $rethash{boot}->[$bootnum]->{dev} = $_;
 | 
						|
                $bootnum++;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            $rethash{boot}->[0]->{dev} = 'network';
 | 
						|
            $rethash{boot}->[1]->{dev} = 'hd';
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return \%rethash;
 | 
						|
}
 | 
						|
 | 
						|
sub build_diskstruct {
 | 
						|
    my $node    = shift;
 | 
						|
    my @returns = ();
 | 
						|
    my $currdev;
 | 
						|
    my @suffixes = ('a' .. 'z');
 | 
						|
    my $suffidx  = 0;
 | 
						|
    my $is_pv    = $vmhash->{$node}->[0]->{'virtflags'} =~ 'paravirt' ? 1 : 0;
 | 
						|
    if (defined $vmhash->{$node}->[0]->{storage}) {
 | 
						|
        my $disklocs = $vmhash->{$node}->[0]->{storage};
 | 
						|
        my @locations = split /\|/, $disklocs;
 | 
						|
        foreach my $disk (@locations) {
 | 
						|
 | 
						|
            #Setting default values of a virtual disk backed by a file at hd*.
 | 
						|
            my $diskhash;
 | 
						|
            $diskhash->{type}   = 'file';
 | 
						|
            $diskhash->{device} = 'disk';
 | 
						|
            if ($is_pv) {
 | 
						|
                $diskhash->{target}->{dev} = 'xvd' . $suffixes[$suffidx];
 | 
						|
            } else {
 | 
						|
                $diskhash->{target}->{dev} = 'hd' . $suffixes[$suffidx];
 | 
						|
            }
 | 
						|
 | 
						|
            my @disk_parts = split(/,/, $disk);
 | 
						|
 | 
						|
            #Find host file and determine if it is a file or a block device.
 | 
						|
            if (substr($disk_parts[0], 0, 4) eq 'phy:') {
 | 
						|
                $diskhash->{type} = 'block';
 | 
						|
                $diskhash->{source}->{dev} = substr($disk_parts[0], 4);
 | 
						|
            } else {
 | 
						|
                $diskhash->{source}->{file} = $disk_parts[0];
 | 
						|
            }
 | 
						|
 | 
						|
            #See if there are any other options. If not, increment suffidx because the already determined device node was used.
 | 
						|
            if (@disk_parts gt 1) {
 | 
						|
                my @disk_opts = split(/:/, $disk_parts[1]);
 | 
						|
                if ($disk_opts[0] ne '') {
 | 
						|
                    $diskhash->{target}->{dev} = $disk_opts[0];
 | 
						|
                } else {
 | 
						|
                    $suffidx++;
 | 
						|
                }
 | 
						|
                if ($disk_opts[1] eq 'cdrom') {
 | 
						|
                    $diskhash->{device} = 'cdrom';
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                $suffidx++;
 | 
						|
            }
 | 
						|
 | 
						|
            push @returns, $diskhash;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return \@returns;
 | 
						|
}
 | 
						|
 | 
						|
sub getNodeUUID {
 | 
						|
    my $node = shift;
 | 
						|
    return xCAT::Utils::genUUID();
 | 
						|
}
 | 
						|
 | 
						|
sub build_nicstruct {
 | 
						|
    my $rethash;
 | 
						|
    my $node = shift;
 | 
						|
    my @macs = ();
 | 
						|
    if ($machash->{$node}->[0]->{mac}) {
 | 
						|
        my $macdata = $machash->{$node}->[0]->{mac};
 | 
						|
        foreach my $macaddr (split /\|/, $macdata) {
 | 
						|
            $macaddr =~ s/\!.*//;
 | 
						|
            push @macs, $macaddr;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    unless (scalar(@macs)) {
 | 
						|
        my $allbutmult = 65279; # & mask for bitwise clearing of the multicast bit of mac
 | 
						|
        my $localad    = 512;   # | to set the bit for locally admnistered mac address
 | 
						|
        my $leading = int(rand(65535));
 | 
						|
        $leading = $leading | 512;
 | 
						|
        $leading = $leading & 65279;
 | 
						|
        my $n = inet_aton($node);
 | 
						|
        my $tail;
 | 
						|
        if ($n) {
 | 
						|
            $tail = unpack("N", $n);
 | 
						|
        }
 | 
						|
        unless ($tail) {
 | 
						|
            $tail = int(rand(4294967295));
 | 
						|
        }
 | 
						|
        my $macstr = sprintf("%04x%08x", $leading, $tail);
 | 
						|
        $macstr =~ s/(..)(..)(..)(..)(..)(..)/$1:$2:$3:$4:$5:$6/;
 | 
						|
        $mactab->setNodeAttribs($node, { mac => $macstr });
 | 
						|
        $nrtab->setNodeAttribs($node, { netboot => 'pxe' });
 | 
						|
        $doreq->({ command => ['makedhcp'], node => [$node] });
 | 
						|
        push @macs, $macstr;
 | 
						|
    }
 | 
						|
    my @rethashes;
 | 
						|
    foreach (@macs) {
 | 
						|
        my $rethash;
 | 
						|
        $rethash->{type}             = 'bridge';
 | 
						|
        $rethash->{mac}->{address}   = $_;
 | 
						|
        $rethash->{source}->{bridge} = 'xenbr0';
 | 
						|
        push @rethashes, $rethash;
 | 
						|
    }
 | 
						|
    return \@rethashes;
 | 
						|
}
 | 
						|
 | 
						|
sub getUnits {
 | 
						|
    my $amount  = shift;
 | 
						|
    my $defunit = shift;
 | 
						|
    my $divisor = shift;
 | 
						|
    unless ($divisor) {
 | 
						|
        $divisor = 1;
 | 
						|
    }
 | 
						|
    if ($amount =~ /(\D)$/) {    #If unitless, add unit
 | 
						|
        $defunit = $1;
 | 
						|
        chop $amount;
 | 
						|
    }
 | 
						|
    if ($defunit =~ /k/i) {
 | 
						|
        return $amount * 1024 / $divisor;
 | 
						|
    } elsif ($defunit =~ /m/i) {
 | 
						|
        return $amount * 1048576 / $divisor;
 | 
						|
    } elsif ($defunit =~ /g/i) {
 | 
						|
        return $amount * 1073741824 / $divisor;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
sub build_xmldesc {
 | 
						|
    my $node  = shift;
 | 
						|
    my %xtree = ();
 | 
						|
    my $is_pv = $vmhash->{$node}->[0]->{'virtflags'} =~ 'paravirt' ? 1 : 0;
 | 
						|
    $xtree{type} = 'xen';
 | 
						|
    if (!$is_pv) {
 | 
						|
        $xtree{image} = 'hvm';
 | 
						|
    }
 | 
						|
    $xtree{name}->{content} = $node;
 | 
						|
    $xtree{uuid}->{content} = getNodeUUID($node);
 | 
						|
    $xtree{os}              = build_oshash($node);
 | 
						|
    if (defined $vmhash->{$node}->[0]->{memory}) {
 | 
						|
        $xtree{memory}->{content} = getUnits($vmhash->{$node}->[0]->{memory}, "M", 1024);
 | 
						|
    } else {
 | 
						|
        $xtree{memory}->{content} = 524288;
 | 
						|
    }
 | 
						|
    $xtree{vcpu}->{content}     = 1;
 | 
						|
    $xtree{features}->{pae}     = {};
 | 
						|
    $xtree{features}->{acpi}    = {};
 | 
						|
    $xtree{features}->{apic}    = {};
 | 
						|
    $xtree{features}->{content} = "\n";
 | 
						|
    unless ($is_pv) {
 | 
						|
        $xtree{devices}->{emulator}->{content} = '/usr/lib64/xen/bin/qemu-dm';
 | 
						|
    }
 | 
						|
    $xtree{devices}->{disk}                      = build_diskstruct($node);
 | 
						|
    $xtree{devices}->{interface}                 = build_nicstruct($node);
 | 
						|
    $xtree{devices}->{graphics}->{type}          = 'vnc';
 | 
						|
    $xtree{devices}->{graphics}->{'listen'}      = '0.0.0.0';
 | 
						|
    $xtree{devices}->{console}->{type}           = 'pty';
 | 
						|
    $xtree{devices}->{console}->{target}->{port} = '1';
 | 
						|
    if ($is_pv) {
 | 
						|
        $xtree{bootloader}{content} = '/usr/bin/pypxeboot';
 | 
						|
        $xtree{bootloader_args}{content} = 'mac=' . $xtree{devices}{interface}[0]{mac}{address};
 | 
						|
    }
 | 
						|
    $xtree{on_poweroff}{content} = 'destroy';
 | 
						|
    $xtree{on_reboot}{content}   = 'restart';
 | 
						|
    return XMLout(\%xtree, RootName => "domain", KeyAttr => {});
 | 
						|
}
 | 
						|
 | 
						|
sub refresh_vm {
 | 
						|
    my $dom = shift;
 | 
						|
 | 
						|
    my $newxml  = XMLin($dom->get_xml_description());
 | 
						|
    my $vncport = $newxml->{devices}->{graphics}->{port};
 | 
						|
    my $stty    = $newxml->{devices}->{console}->{tty};
 | 
						|
    $vmtab->setNodeAttribs($node, { vncport => $vncport, textconsole => $stty });
 | 
						|
    return { vncport => $vncport, textconsole => $stty };
 | 
						|
}
 | 
						|
 | 
						|
sub getvmcons {
 | 
						|
    my $node = shift();
 | 
						|
    my $type = shift();
 | 
						|
    my $dom;
 | 
						|
    eval {
 | 
						|
        $dom = $hypconn->get_domain_by_name($node);
 | 
						|
    };
 | 
						|
    unless ($dom) {
 | 
						|
        return 1, "Unable to query running VM";
 | 
						|
    }
 | 
						|
    my $consdata = refresh_vm($dom);
 | 
						|
    my $hyper    = $vmhash->{$node}->[0]->{host};
 | 
						|
 | 
						|
    if ($type eq "text") {
 | 
						|
        my $serialspeed;
 | 
						|
        if ($hmhash) {
 | 
						|
            $serialspeed = $hmhash->{$node}->[0]->{serialspeed};
 | 
						|
        }
 | 
						|
        my $sconsparms = { node => [ { name => [$node] } ] };
 | 
						|
        $sconsparms->{node}->[0]->{sshhost}   = [$hyper];
 | 
						|
        $sconsparms->{node}->[0]->{psuedotty} = [ $consdata->{textconsole} ];
 | 
						|
        $sconsparms->{node}->[0]->{baudrate}  = [$serialspeed];
 | 
						|
        return (0, $sconsparms);
 | 
						|
    } elsif ($type eq "vnc") {
 | 
						|
        my $domdata  = `ssh $hyper xm list $node -l`;
 | 
						|
        my @domlines = split /\n/, $domdata;
 | 
						|
        my $foundvfb = 0;
 | 
						|
        my $vnclocation;
 | 
						|
        foreach (@domlines) {
 | 
						|
            if (/\(vfb/) {
 | 
						|
                $foundvfb = 1;
 | 
						|
            }
 | 
						|
            if ($foundvfb and /location\s+([^\)]+)/) {
 | 
						|
                $vnclocation = $1;
 | 
						|
                $foundvfb    = 0;
 | 
						|
                last;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return (0, 'ssh+vnc@' . $hyper . ": " . $vnclocation); #$consdata->{vncport});
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
sub getrvidparms {
 | 
						|
    my $node = shift;
 | 
						|
    my $location = getvmcons($node, "vnc");
 | 
						|
    if ($location =~ /ssh\+vnc@([^:]*):([^:]*):(\d+)/) {
 | 
						|
        my @output = (
 | 
						|
            "method: xen",
 | 
						|
            "server: $1",
 | 
						|
            "vncdisplay: $2:$3",
 | 
						|
        );
 | 
						|
        return 0, @output;
 | 
						|
    } else {
 | 
						|
        return (1, "Error: Unable to determine rvid destination for $node");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
sub pick_target {
 | 
						|
    my $node = shift;
 | 
						|
    my $target;
 | 
						|
    my $leastusedmemory = undef;
 | 
						|
    my $currentusedmemory;
 | 
						|
    my $candidates = $vmhash->{$node}->[0]->{migrationdest};
 | 
						|
    my $currhyp    = $vmhash->{$node}->[0]->{host};
 | 
						|
    unless ($candidates) {
 | 
						|
        return undef;
 | 
						|
    }
 | 
						|
    print "$node with $candidates\n";
 | 
						|
    foreach (noderange($candidates)) {
 | 
						|
        my $targconn;
 | 
						|
        my $cand = $_;
 | 
						|
        $currentusedmemory = 0;
 | 
						|
        if ($_ eq $currhyp) { next; }    #skip current node
 | 
						|
        if (grep { "$_" eq $cand } @destblacklist) { print "$_ was blacklisted\n"; next; } #skip blacklisted destinations
 | 
						|
        print "maybe $_\n";
 | 
						|
        eval { #Sys::Virt has bugs that cause it to die out in weird ways some times, contain it here
 | 
						|
            $targconn = Sys::Virt->new(uri => "xen+ssh://" . $_ . "?no_tty=1&netcat=nc");
 | 
						|
        };
 | 
						|
        unless ($targconn) {
 | 
						|
            eval { #Sys::Virt has bugs that cause it to die out in weird ways some times, contain it here
 | 
						|
                $targconn = Sys::Virt->new(uri => "xen+ssh://" . $_ . "?no_tty=1");
 | 
						|
            };
 | 
						|
        }
 | 
						|
        unless ($targconn) { next; }    #skip unreachable destinations
 | 
						|
        foreach ($targconn->list_domains()) {
 | 
						|
            if ($_->get_name() eq 'Domain-0') { next; } #Dom0 memory usage is elastic, we are interested in HVM DomU memory, which is inelastic
 | 
						|
 | 
						|
            $currentusedmemory += $_->get_info()->{memory};
 | 
						|
        }
 | 
						|
        if (not defined($leastusedmemory)) {
 | 
						|
            $leastusedmemory = $currentusedmemory;
 | 
						|
            $target          = $_;
 | 
						|
        } elsif ($currentusedmemory < $leastusedmemory) {
 | 
						|
            $leastusedmemory = $currentusedmemory;
 | 
						|
            $target          = $_;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return $target;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
sub migrate {
 | 
						|
    my $node = shift();
 | 
						|
    my $targ = shift();
 | 
						|
    unless ($targ) {
 | 
						|
        $targ = pick_target($node);
 | 
						|
    }
 | 
						|
    unless ($targ) {
 | 
						|
        return (1, "Unable to identify a suitable target host for guest $node");
 | 
						|
    }
 | 
						|
    my $prevhyp;
 | 
						|
    my $target  = "xen+ssh://" . $targ . "?no_tty=1";
 | 
						|
    my $currhyp = "xen+ssh://";
 | 
						|
    if ($vmhash->{$node}->[0]->{host}) {
 | 
						|
        $prevhyp = $vmhash->{$node}->[0]->{host};
 | 
						|
        $currhyp .= $prevhyp;
 | 
						|
    } else {
 | 
						|
        return (1, "Unable to find current location of $node");
 | 
						|
    }
 | 
						|
    $currhyp .= "?no_tty=1";
 | 
						|
    if ($currhyp eq $target) {
 | 
						|
        return (0, "Guest is already on host $targ");
 | 
						|
    }
 | 
						|
    my $testhypconn;
 | 
						|
    my $srcnetcatadd = "&netcat=nc";
 | 
						|
    eval {    #Contain Sys::Virt bugs
 | 
						|
        $testhypconn = Sys::Virt->new(uri => "xen+ssh://" . $prevhyp . "?no_tty=1$srcnetcatadd");
 | 
						|
    };
 | 
						|
    unless ($testhypconn) {
 | 
						|
        $srcnetcatadd = "";
 | 
						|
        eval {    #Contain Sys::Virt bugs
 | 
						|
            $testhypconn = Sys::Virt->new(uri => "xen+ssh://" . $prevhyp . "?no_tty=1");
 | 
						|
        };
 | 
						|
    }
 | 
						|
    unless ($testhypconn) {
 | 
						|
        return (1, "Unable to reach $prevhyp to perform operation of $node, use nodech to change vm.host if certain of no split-brain possibility exists");
 | 
						|
    }
 | 
						|
    undef $testhypconn;
 | 
						|
    my $destnetcatadd = "&netcat=nc";
 | 
						|
    eval {        #Contain Sys::Virt bugs
 | 
						|
        $testhypconn = Sys::Virt->new(uri => $target . $destnetcatadd);
 | 
						|
    };
 | 
						|
    unless ($testhypconn) {
 | 
						|
        $destnetcatadd = "";
 | 
						|
        eval {    #Contain Sys::Virt bugs
 | 
						|
            $testhypconn = Sys::Virt->new(uri => $target);
 | 
						|
        };
 | 
						|
    }
 | 
						|
    unless ($testhypconn) {
 | 
						|
        return (1, "Unable to reach $targ to perform operation of $node, destination unusable.");
 | 
						|
    }
 | 
						|
    my $sock = IO::Socket::INET->new(Proto => 'udp');
 | 
						|
    my $ipa = inet_aton($node);
 | 
						|
    my $pa = sockaddr_in(7, $ipa);  #UDP echo service, not needed to be actually
 | 
						|
        #serviced, we just want to trigger MAC move in the switch forwarding dbs
 | 
						|
    my $rc = system("virsh -c '$currhyp" . $srcnetcatadd . "' migrate --live $node '$target" . "$destnetcatadd'");
 | 
						|
    system("arp -d $node");    #Make ethernet fabric take note of change
 | 
						|
    send($sock, "dummy", 0, $pa); #UDP packet to force forwarding table update in switches, ideally a garp happened, but just in case...
 | 
						|
 | 
						|
    if ($rc) {
 | 
						|
        return (1, "Failed migration from $prevhyp to $targ");
 | 
						|
    } else {
 | 
						|
        $vmtab->setNodeAttribs($node, { host => $targ });
 | 
						|
        my $newhypconn;
 | 
						|
        eval {                    #Contain Sys::Virt bugs
 | 
						|
            $newhypconn = Sys::Virt->new(uri => "xen+ssh://" . $targ . "?no_tty=1&netcat=nc");
 | 
						|
        };
 | 
						|
        unless ($newhypconn) {
 | 
						|
            eval {                #Contain Sys::Virt bugs
 | 
						|
                $newhypconn = Sys::Virt->new(uri => "xen+ssh://" . $targ . "?no_tty=1");
 | 
						|
            };
 | 
						|
        }
 | 
						|
        if ($newhypconn) {
 | 
						|
            my $dom;
 | 
						|
            eval {
 | 
						|
                $dom = $newhypconn->get_domain_by_name($node);
 | 
						|
            };
 | 
						|
            if ($dom) {
 | 
						|
                refresh_vm($dom);
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            return (0, "migrated to $targ");
 | 
						|
        }
 | 
						|
        return (0, "migrated to $targ");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
sub getpowstate {
 | 
						|
    my $dom = shift;
 | 
						|
    my $vmstat;
 | 
						|
    if ($dom) {
 | 
						|
        $vmstat = $dom->get_info;
 | 
						|
    }
 | 
						|
    if ($vmstat and $runningstates{ $vmstat->{state} }) {
 | 
						|
        return "on";
 | 
						|
    } else {
 | 
						|
        return "off";
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
sub makedom {
 | 
						|
    my $node = shift;
 | 
						|
    my $dom;
 | 
						|
    my $xml = build_xmldesc($node);
 | 
						|
    my $errstr;
 | 
						|
    eval { $dom = $hypconn->create_domain($xml); };
 | 
						|
    if ($@) { $errstr = $@; }
 | 
						|
    if (ref $errstr) {
 | 
						|
        $errstr = ":" . $errstr->{message};
 | 
						|
    }
 | 
						|
    if ($errstr) { return (undef, $errstr); }
 | 
						|
    if ($dom) {
 | 
						|
        refresh_vm($dom);
 | 
						|
    }
 | 
						|
    return $dom, undef;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
sub mkvm {
 | 
						|
    build_xmldesc($node);
 | 
						|
}
 | 
						|
 | 
						|
sub power {
 | 
						|
    my $subcommand = shift;
 | 
						|
    my $retstring;
 | 
						|
    my $dom;
 | 
						|
    eval {
 | 
						|
        $dom = $hypconn->get_domain_by_name($node);
 | 
						|
    };
 | 
						|
    if ($subcommand eq "boot") {
 | 
						|
        my $currstate = getpowstate($dom);
 | 
						|
        $retstring = $currstate . " ";
 | 
						|
        if ($currstate eq "off") {
 | 
						|
            $subcommand = "on";
 | 
						|
        } elsif ($currstate eq "on") {
 | 
						|
            $subcommand = "reset";
 | 
						|
        }
 | 
						|
    }
 | 
						|
    my $errstr;
 | 
						|
    if ($subcommand eq 'on') {
 | 
						|
        unless ($dom) {
 | 
						|
            ($dom, $errstr) = makedom($node);
 | 
						|
            if ($errstr) { return (1, $errstr); }
 | 
						|
        } else {
 | 
						|
            $retstring .= " $status_noop";
 | 
						|
        }
 | 
						|
    } elsif ($subcommand eq 'off') {
 | 
						|
        if ($dom) {
 | 
						|
            $dom->destroy();
 | 
						|
            undef $dom;
 | 
						|
        } else { $retstring .= " $status_noop"; }
 | 
						|
    } elsif ($subcommand eq 'softoff') {
 | 
						|
        if ($dom) {
 | 
						|
            $dom->shutdown();
 | 
						|
        } else { $retstring .= " $status_noop"; }
 | 
						|
    } elsif ($subcommand eq 'reset') {
 | 
						|
        if ($dom) {
 | 
						|
            $dom->destroy();
 | 
						|
            ($dom, $errstr) = makedom($node);
 | 
						|
            if ($errstr) { return (1, $errstr); }
 | 
						|
            $retstring .= "reset";
 | 
						|
        } else { $retstring .= " $status_noop"; }
 | 
						|
    } else {
 | 
						|
        unless ($subcommand =~ /^stat/) {
 | 
						|
            return (1, "Unsupported power directive '$subcommand'");
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    unless ($retstring =~ /reset/) {
 | 
						|
        $retstring = getpowstate($dom) . $retstring;
 | 
						|
    }
 | 
						|
    return (0, $retstring);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
sub guestcmd {
 | 
						|
    $hyp  = shift;
 | 
						|
    $node = shift;
 | 
						|
    my $command = shift;
 | 
						|
    my @args    = @_;
 | 
						|
    my $error;
 | 
						|
    if ($command eq "rpower") {
 | 
						|
        return power(@args);
 | 
						|
    } elsif ($command eq "mkvm") {
 | 
						|
        return mkvm();
 | 
						|
    } elsif ($command eq "rmigrate") {
 | 
						|
        return migrate($node, @args);
 | 
						|
    } elsif ($command eq "getrvidparms") {
 | 
						|
        return getrvidparms($node, @args);
 | 
						|
    } elsif ($command eq "getxencons") {
 | 
						|
        return getvmcons($node, @args);
 | 
						|
    }
 | 
						|
 | 
						|
    return (1, "$command not a supported command by xen method");
 | 
						|
}
 | 
						|
 | 
						|
sub preprocess_request {
 | 
						|
    my $request = shift;
 | 
						|
 | 
						|
    #if already preprocessed, go straight to request
 | 
						|
    if ($request->{_xcatpreprocessed}->[0] == 1) { return [$request]; }
 | 
						|
 | 
						|
    my $callback = shift;
 | 
						|
    my @requests;
 | 
						|
 | 
						|
    my $noderange = $request->{node};           #Should be arrayref
 | 
						|
    my $command   = $request->{command}->[0];
 | 
						|
    my $extrargs  = $request->{arg};
 | 
						|
    my @exargs    = ($request->{arg});
 | 
						|
    if (ref($extrargs)) {
 | 
						|
        @exargs = @$extrargs;
 | 
						|
    }
 | 
						|
 | 
						|
    my $usage_string = xCAT::Usage->parseCommand($command, @exargs);
 | 
						|
    if ($usage_string) {
 | 
						|
        $callback->({ data => $usage_string });
 | 
						|
        $request = {};
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!$noderange) {
 | 
						|
        $usage_string = xCAT::Usage->getUsage($command);
 | 
						|
        $callback->({ data => $usage_string });
 | 
						|
        $request = {};
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    #print "noderange=@$noderange\n";
 | 
						|
 | 
						|
    # find service nodes for requested nodes
 | 
						|
    # build an individual request for each service node
 | 
						|
    my $service = "xcat";
 | 
						|
    my $sn = xCAT::ServiceNodeUtils->get_ServiceNode($noderange, $service, "MN");
 | 
						|
 | 
						|
    # build each request for each service node
 | 
						|
 | 
						|
    foreach my $snkey (keys %$sn)
 | 
						|
    {
 | 
						|
        #print "snkey=$snkey\n";
 | 
						|
        my $reqcopy = {%$request};
 | 
						|
        $reqcopy->{node}                   = $sn->{$snkey};
 | 
						|
        $reqcopy->{'_xcatdest'}            = $snkey;
 | 
						|
        $reqcopy->{_xcatpreprocessed}->[0] = 1;
 | 
						|
 | 
						|
        push @requests, $reqcopy;
 | 
						|
    }
 | 
						|
    return \@requests;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
sub adopt {
 | 
						|
 | 
						|
    #TODO: adopt orphans into suitable homes if possible
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
sub grab_table_data {    #grab table data relevent to VM guest nodes
 | 
						|
    my $noderange = shift;
 | 
						|
    my $callback  = shift;
 | 
						|
    $vmtab = xCAT::Table->new("vm");
 | 
						|
    $hmtab = xCAT::Table->new("nodehm");
 | 
						|
    if ($hmtab) {
 | 
						|
        $hmhash = $hmtab->getNodesAttribs($noderange, ['serialspeed']);
 | 
						|
    }
 | 
						|
    unless ($vmtab) {
 | 
						|
        $callback->({ data => ["Cannot open vm table"] });
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    $vmhash = $vmtab->getNodesAttribs($noderange, [ 'node', 'host', 'migrationdest', 'storage', 'memory', 'cpu', 'nics', 'bootorder', 'virtflags' ]);
 | 
						|
    $mactab = xCAT::Table->new("mac",     -create => 1);
 | 
						|
    $nrtab  = xCAT::Table->new("noderes", -create => 1);
 | 
						|
    $machash = $mactab->getNodesAttribs($noderange, ['mac']);
 | 
						|
    $chaintab = xCAT::Table->new("chain", -create => 1);
 | 
						|
    $chainhash = $chaintab->getNodesAttribs($noderange, ['currstate']);
 | 
						|
    $bptab = xCAT::Table->new("bootparams", -create => 1);
 | 
						|
    $bphash = $bptab->getNodesAttribs($noderange, [ 'kernel', 'initrd' ]);
 | 
						|
}
 | 
						|
 | 
						|
sub process_request {
 | 
						|
    $SIG{INT} = $SIG{TERM} = sub {
 | 
						|
        foreach (keys %vm_comm_pids) {
 | 
						|
            kill 2, $_;
 | 
						|
        }
 | 
						|
        exit 0;
 | 
						|
    };
 | 
						|
 | 
						|
    #makes sense to check it here anyway, this way we avoid the main process
 | 
						|
    #sucking up ram with Sys::Virt
 | 
						|
    my $libvirtsupport = eval { require Sys::Virt; };
 | 
						|
    my $request        = shift;
 | 
						|
    my $callback       = shift;
 | 
						|
    unless ($libvirtsupport) {    #Still no Sys::Virt module
 | 
						|
        $callback->({ error => "Sys::Virt perl module missing, unable to fulfill Xen plugin requirements", errorcode => [42] });
 | 
						|
        return [];
 | 
						|
    }
 | 
						|
    require Sys::Virt::Domain;
 | 
						|
    %runningstates = (&Sys::Virt::Domain::STATE_NOSTATE => 1, &Sys::Virt::Domain::STATE_RUNNING => 1, &Sys::Virt::Domain::STATE_BLOCKED => 1);
 | 
						|
 | 
						|
    $doreq = shift;
 | 
						|
    my $level     = shift;
 | 
						|
    my $noderange = $request->{node};
 | 
						|
    my $command   = $request->{command}->[0];
 | 
						|
    my @exargs;
 | 
						|
    unless ($command) {
 | 
						|
        return;    #Empty request
 | 
						|
    }
 | 
						|
    if (ref($request->{arg})) {
 | 
						|
        @exargs = @{ $request->{arg} };
 | 
						|
    } else {
 | 
						|
        @exargs = ($request->{arg});
 | 
						|
    }
 | 
						|
 | 
						|
     #pdu commands will be handled in the pdu plugin
 | 
						|
     if ($command eq "rpower" and grep(/^pduon|pduoff|pdustat$/, @exargs)) {
 | 
						|
         return;
 | 
						|
     }
 | 
						|
     
 | 
						|
    if ($command eq 'revacuate') {
 | 
						|
        my $newnoderange;
 | 
						|
        foreach (@$noderange) {
 | 
						|
            $hypconn = undef;
 | 
						|
            push @destblacklist, $_;
 | 
						|
            eval {    #Contain bugs that won't be in $@
 | 
						|
                $hypconn = Sys::Virt->new(uri => "xen+ssh://" . $_ . "?no_tty=1&netcat=nc");
 | 
						|
            };
 | 
						|
            unless ($hypconn) {    #retry for socat
 | 
						|
                eval {             #Contain bugs that won't be in $@
 | 
						|
                    $hypconn = Sys::Virt->new(uri => "xen+ssh://" . $_ . "?no_tty=1");
 | 
						|
                };
 | 
						|
            }
 | 
						|
            unless ($hypconn) {
 | 
						|
                $callback->({ node => [ { name => [$_], error => ["Cannot communicate BC via libvirt to node"] } ] });
 | 
						|
                next;
 | 
						|
            }
 | 
						|
            foreach ($hypconn->list_domains()) {
 | 
						|
                my $guestname = $_->get_name();
 | 
						|
                if ($guestname eq 'Domain-0') {
 | 
						|
                    next;
 | 
						|
                }
 | 
						|
                push @$newnoderange, $guestname;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        $hypconn   = undef;
 | 
						|
        $noderange = $newnoderange;
 | 
						|
        $command   = 'rmigrate';
 | 
						|
    }
 | 
						|
 | 
						|
    grab_table_data($noderange, $callback);
 | 
						|
    if ($command eq 'mkinstall') {
 | 
						|
        $DB::single = 1;
 | 
						|
        eval {
 | 
						|
            require xCAT_plugin::anaconda;
 | 
						|
            xCAT_plugin::anaconda::mkinstall($request, $callback, $doreq);
 | 
						|
            for my $node (@{ $request->{node} }) {
 | 
						|
                my $is_pv = $vmhash->{$node}->[0]->{'virtflags'} =~ 'paravirt' ? 1 : 0;
 | 
						|
                if ($is_pv) {
 | 
						|
                    my $kernel = $bphash->{$node}[0]{kernel};
 | 
						|
                    my $initrd = $bphash->{$node}[0]{initrd};
 | 
						|
                    $kernel =~ s|vmlinuz|xen/vmlinuz|;
 | 
						|
                    $initrd =~ s|initrd\.img|xen/initrd\.img|;
 | 
						|
                    $bptab->setNodeAttribs($node, { kernel => $kernel, initrd => $initrd });
 | 
						|
                }
 | 
						|
            }
 | 
						|
        };
 | 
						|
 | 
						|
        if ($@) {
 | 
						|
            $callback->({ error => $@, errorcode => [1] });
 | 
						|
        }
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if ($command eq 'revacuate' or $command eq 'rmigrate') {
 | 
						|
        $vmmaxp = 1; #for now throttle concurrent migrations, requires more sophisticated heuristics to ensure sanity
 | 
						|
    } else {
 | 
						|
 | 
						|
        #my $sitetab = xCAT::Table->new('site');
 | 
						|
        #my $tmp;
 | 
						|
        #if ($sitetab) {
 | 
						|
        #($tmp)=$sitetab->getAttribs({'key'=>'vmmaxp'},'value');
 | 
						|
        my @entries = xCAT::TableUtils->get_site_attribute("vmmaxp");
 | 
						|
        my $t_entry = $entries[0];
 | 
						|
        if (defined($t_entry)) { $vmmaxp = $t_entry; }
 | 
						|
 | 
						|
        #}
 | 
						|
    }
 | 
						|
 | 
						|
    my $children = 0;
 | 
						|
    $SIG{CHLD} = sub { my $cpid; while (($cpid = waitpid(-1, WNOHANG)) > 0) { if ($vm_comm_pids{$cpid}) { delete $vm_comm_pids{$cpid}; $children--; } } };
 | 
						|
    my $inputs  = new IO::Select;
 | 
						|
    my $sub_fds = new IO::Select;
 | 
						|
    %hyphash = ();
 | 
						|
    my %orphans = ();
 | 
						|
    foreach (keys %{$vmhash}) {
 | 
						|
        if ($vmhash->{$_}->[0]->{host}) {
 | 
						|
            $hyphash{ $vmhash->{$_}->[0]->{host} }->{nodes}->{$_} = 1;
 | 
						|
        } else {
 | 
						|
            $orphans{$_} = 1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (keys %orphans) {
 | 
						|
        if ($command eq "rpower" and (grep /^on$/, @exargs or grep /^boot$/, @exargs)) {
 | 
						|
            unless (adopt(\%orphans, \%hyphash)) {
 | 
						|
                $callback->({ error => "Can't find " . join(",", keys %orphans), errorcode => [1] });
 | 
						|
                return 1;
 | 
						|
            }
 | 
						|
 | 
						|
        } elsif ($command eq "rmigrate") {
 | 
						|
            $callback->({ error => "Can't find " . join(",", keys %orphans), errorcode => [1] });
 | 
						|
            return;
 | 
						|
        } else {
 | 
						|
            $callback->({ error => "Can't find " . join(",", keys %orphans), errorcode => [1] });
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if ($command eq "rbeacon") {
 | 
						|
        my %req = ();
 | 
						|
        $req{command} = ['rbeacon'];
 | 
						|
        $req{arg}     = \@exargs;
 | 
						|
        $req{node}    = [ keys %hyphash ];
 | 
						|
        $doreq->(\%req, $callback);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    #get new node status
 | 
						|
    my %oldnodestatus = ();    #saves the old node status
 | 
						|
    my @allerrornodes = ();
 | 
						|
    my $check         = 0;
 | 
						|
    my $global_check  = 1;
 | 
						|
 | 
						|
    #my $sitetab = xCAT::Table->new('site');
 | 
						|
    #if ($sitetab) {
 | 
						|
    #(my $ref) = $sitetab->getAttribs({key => 'nodestatus'}, 'value');
 | 
						|
    my @entries = xCAT::TableUtils->get_site_attribute("nodestatus");
 | 
						|
    my $t_entry = $entries[0];
 | 
						|
    if (defined($t_entry)) {
 | 
						|
        if ($t_entry =~ /0|n|N/) { $global_check = 0; }
 | 
						|
    }
 | 
						|
 | 
						|
    #}
 | 
						|
 | 
						|
 | 
						|
    if ($command eq 'rpower') {
 | 
						|
        my $subcommand = $exargs[0];
 | 
						|
        if (($global_check) && ($subcommand ne 'stat') && ($subcommand ne 'status')) {
 | 
						|
            $check = 1;
 | 
						|
            my @allnodes = @$noderange;
 | 
						|
 | 
						|
            #save the old status
 | 
						|
            my $nodelisttab = xCAT::Table->new('nodelist');
 | 
						|
            if ($nodelisttab) {
 | 
						|
                my $tabdata = $nodelisttab->getNodesAttribs(\@allnodes, [ 'node', 'status' ]);
 | 
						|
                foreach my $node (@allnodes)
 | 
						|
                {
 | 
						|
                    my $tmp1 = $tabdata->{$node}->[0];
 | 
						|
                    if ($tmp1) {
 | 
						|
                        if ($tmp1->{status}) { $oldnodestatus{$node} = $tmp1->{status}; }
 | 
						|
                        else                 { $oldnodestatus{$node} = ""; }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            #print "oldstatus:" . Dumper(\%oldnodestatus);
 | 
						|
 | 
						|
            #set the new status to the nodelist.status
 | 
						|
            my %newnodestatus = ();
 | 
						|
            my $newstat;
 | 
						|
            if (($subcommand eq 'off') || ($subcommand eq 'softoff')) {
 | 
						|
                my $newstat = $::STATUS_POWERING_OFF;
 | 
						|
                $newnodestatus{$newstat} = \@allnodes;
 | 
						|
            } else {
 | 
						|
 | 
						|
                #get the current nodeset stat
 | 
						|
                if (@allnodes > 0) {
 | 
						|
                    my $nsh = {};
 | 
						|
                    my ($ret, $msg) = xCAT::SvrUtils->getNodesetStates(\@allnodes, $nsh);
 | 
						|
                    if (!$ret) {
 | 
						|
                        foreach (keys %$nsh) {
 | 
						|
                            my $newstat = xCAT_monitoring::monitorctrl->getNodeStatusFromNodesetState($_, "rpower");
 | 
						|
                            $newnodestatus{$newstat} = $nsh->{$_};
 | 
						|
                        }
 | 
						|
                    } else {
 | 
						|
                        $callback->({ data => $msg });
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            #donot update node provision status (installing or netbooting) here
 | 
						|
            xCAT::Utils->filter_nostatusupdate(\%newnodestatus);
 | 
						|
 | 
						|
            #print "newstatus" . Dumper(\%newnodestatus);
 | 
						|
            xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%newnodestatus, 1);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
    foreach $hyp (sort (keys %hyphash)) {
 | 
						|
        while ($children > $vmmaxp) {
 | 
						|
            my $handlednodes = {};
 | 
						|
            forward_data($callback, $sub_fds, $handlednodes);
 | 
						|
 | 
						|
            #update the node status to the nodelist.status table
 | 
						|
            if ($check) {
 | 
						|
                updateNodeStatus($handlednodes, \@allerrornodes);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        $children++;
 | 
						|
        my $cfd;
 | 
						|
        my $pfd;
 | 
						|
        socketpair($pfd, $cfd, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!";
 | 
						|
        $cfd->autoflush(1);
 | 
						|
        $pfd->autoflush(1);
 | 
						|
        my $cpid = xCAT::Utils->xfork;
 | 
						|
        unless (defined($cpid)) { die "Fork error"; }
 | 
						|
 | 
						|
        unless ($cpid) {
 | 
						|
            close($cfd);
 | 
						|
            dohyp($pfd, $hyp, $command, -args => \@exargs);
 | 
						|
            exit(0);
 | 
						|
        }
 | 
						|
        $vm_comm_pids{$cpid} = 1;
 | 
						|
        close($pfd);
 | 
						|
        $sub_fds->add($cfd);
 | 
						|
    }
 | 
						|
    while ($sub_fds->count > 0 or $children > 0) {
 | 
						|
        my $handlednodes = {};
 | 
						|
        forward_data($callback, $sub_fds, $handlednodes);
 | 
						|
 | 
						|
        #update the node status to the nodelist.status table
 | 
						|
        if ($check) {
 | 
						|
            updateNodeStatus($handlednodes, \@allerrornodes);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    #Make sure they get drained, this probably is overkill but shouldn't hurt
 | 
						|
    my $rc = 1;
 | 
						|
    while ($rc > 0) {
 | 
						|
        my $handlednodes = {};
 | 
						|
        $rc = forward_data($callback, $sub_fds, $handlednodes);
 | 
						|
 | 
						|
        #update the node status to the nodelist.status table
 | 
						|
        if ($check) {
 | 
						|
            updateNodeStatus($handlednodes, \@allerrornodes);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if ($check) {
 | 
						|
 | 
						|
        #print "allerrornodes=@allerrornodes\n";
 | 
						|
        #revert the status back for there is no-op for the nodes
 | 
						|
        my %old = ();
 | 
						|
        foreach my $node (@allerrornodes) {
 | 
						|
            my $stat = $oldnodestatus{$node};
 | 
						|
            if (exists($old{$stat})) {
 | 
						|
                my $pa = $old{$stat};
 | 
						|
                push(@$pa, $node);
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                $old{$stat} = [$node];
 | 
						|
            }
 | 
						|
        }
 | 
						|
        xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%old, 1);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
sub updateNodeStatus {
 | 
						|
    my $handlednodes  = shift;
 | 
						|
    my $allerrornodes = shift;
 | 
						|
    foreach my $node (keys(%$handlednodes)) {
 | 
						|
        if ($handlednodes->{$node} == -1) { push(@$allerrornodes, $node); }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
sub forward_data {
 | 
						|
    my $callback   = shift;
 | 
						|
    my $fds        = shift;
 | 
						|
    my $errornodes = shift;
 | 
						|
    my @ready_fds  = $fds->can_read(1);
 | 
						|
    my $rfh;
 | 
						|
    my $rc = @ready_fds;
 | 
						|
    foreach $rfh (@ready_fds) {
 | 
						|
        my $data;
 | 
						|
        if ($data = <$rfh>) {
 | 
						|
            while ($data !~ /ENDOFFREEZE6sK4ci/) {
 | 
						|
                $data .= <$rfh>;
 | 
						|
            }
 | 
						|
            eval { print $rfh "ACK\n"; };
 | 
						|
            my $responses = thaw($data);
 | 
						|
            foreach (@$responses) {
 | 
						|
 | 
						|
                #save the nodes that has errors and the ones that has no-op for use by the node status monitoring
 | 
						|
                my $no_op = 0;
 | 
						|
                if ($_->{node}->[0]->{errorcode}) { $no_op = 1; }
 | 
						|
                else {
 | 
						|
                    my $text = $_->{node}->[0]->{data}->[0]->{contents}->[0];
 | 
						|
 | 
						|
                    #print "data:$text\n";
 | 
						|
                    if (($text) && ($text =~ /$status_noop/)) {
 | 
						|
                        $no_op = 1;
 | 
						|
 | 
						|
                        #remove the symbols that meant for use by node status
 | 
						|
                        $_->{node}->[0]->{data}->[0]->{contents}->[0] =~ s/ $status_noop//;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                #print "data:". $_->{node}->[0]->{data}->[0]->{contents}->[0] . "\n";
 | 
						|
                if ($no_op) {
 | 
						|
                    if ($errornodes) { $errornodes->{ $_->{node}->[0]->{name}->[0] } = -1; }
 | 
						|
                } else {
 | 
						|
                    if ($errornodes) { $errornodes->{ $_->{node}->[0]->{name}->[0] } = 1; }
 | 
						|
                }
 | 
						|
                $callback->($_);
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            $fds->remove($rfh);
 | 
						|
            close($rfh);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    usleep(0);    # yield
 | 
						|
    return $rc;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
sub dohyp {
 | 
						|
    my $out = shift;
 | 
						|
    $hyp = shift;
 | 
						|
    my $command   = shift;
 | 
						|
    my %namedargs = @_;
 | 
						|
    my @exargs    = @{ $namedargs{-args} };
 | 
						|
    my $node;
 | 
						|
    my $args = \@exargs;
 | 
						|
    $vmtab = xCAT::Table->new("vm");
 | 
						|
 | 
						|
 | 
						|
    eval {    #Contain Sys::Virt bugs that make $@ useless
 | 
						|
        $hypconn = Sys::Virt->new(uri => "xen+ssh://" . $hyp . "?no_tty=1&netcat=nc");
 | 
						|
    };
 | 
						|
    unless ($hypconn) {
 | 
						|
        eval {    #Contain Sys::Virt bugs that make $@ useless
 | 
						|
            $hypconn = Sys::Virt->new(uri => "xen+ssh://" . $hyp . "?no_tty=1");
 | 
						|
        };
 | 
						|
    }
 | 
						|
    unless ($hypconn) {
 | 
						|
        my %err = (node => []);
 | 
						|
        foreach (keys %{ $hyphash{$hyp}->{nodes} }) {
 | 
						|
            push(@{ $err{node} }, { name => [$_], error => ["Cannot communicate via libvirt to $hyp"], errorcode => [1] });
 | 
						|
        }
 | 
						|
        print $out freeze([ \%err ]);
 | 
						|
        print $out "\nENDOFFREEZE6sK4ci\n";
 | 
						|
        usleep(0);    # yield
 | 
						|
        waitforack($out);
 | 
						|
        return 1, "General error establishing libvirt communication";
 | 
						|
    }
 | 
						|
    foreach $node (sort (keys %{ $hyphash{$hyp}->{nodes} })) {
 | 
						|
        my ($rc, @output) = guestcmd($hyp, $node, $command, @$args);
 | 
						|
 | 
						|
        foreach (@output) {
 | 
						|
            my %output;
 | 
						|
            if (ref($_)) {
 | 
						|
                print $out freeze([$_]);
 | 
						|
                print $out "\nENDOFFREEZE6sK4ci\n";
 | 
						|
                usleep(0);    # yield
 | 
						|
                waitforack($out);
 | 
						|
                next;
 | 
						|
            }
 | 
						|
 | 
						|
            (my $desc, my $text) = split(/:/, $_, 2);
 | 
						|
            unless ($text) {
 | 
						|
                $text = $desc;
 | 
						|
            } else {
 | 
						|
                $desc =~ s/^\s+//;
 | 
						|
                $desc =~ s/\s+$//;
 | 
						|
                if ($desc) {
 | 
						|
                    $output{node}->[0]->{data}->[0]->{desc}->[0] = $desc;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            $text =~ s/^\s+//;
 | 
						|
            $text =~ s/\s+$//;
 | 
						|
            $output{node}->[0]->{errorcode}                  = $rc;
 | 
						|
            $output{node}->[0]->{name}->[0]                  = $node;
 | 
						|
            $output{node}->[0]->{data}->[0]->{contents}->[0] = $text;
 | 
						|
            $output{node}->[0]->{error} = $text unless $rc == 0;
 | 
						|
            print $out freeze([ \%output ]);
 | 
						|
            print $out "\nENDOFFREEZE6sK4ci\n";
 | 
						|
            usleep(0);    # yield
 | 
						|
            waitforack($out);
 | 
						|
        }
 | 
						|
        usleep(0);        # yield
 | 
						|
    }
 | 
						|
 | 
						|
    #my $msgtoparent=freeze(\@outhashes); # = XMLout(\%output,RootName => 'xcatresponse');
 | 
						|
    #print $out $msgtoparent; #$node.": $_\n";
 | 
						|
}
 | 
						|
 | 
						|
1;
 |