diff --git a/docs/source/guides/admin-guides/references/man1/chvm.1.rst b/docs/source/guides/admin-guides/references/man1/chvm.1.rst index 43de354af..335a396d3 100644 --- a/docs/source/guides/admin-guides/references/man1/chvm.1.rst +++ b/docs/source/guides/admin-guides/references/man1/chvm.1.rst @@ -49,12 +49,19 @@ PPC (using Direct FSP Management) specific: [\ **del_physlots=drc_index1,drc_index2...**\ ] [\ **del_vadapter=slotid**\ ] + +KVM specific: +============= + + \ **chvm**\ \ *noderange*\ [\ **--cpupin**\ \ *hostcpuset*\ ] \ **chvm**\ \ *noderange*\ [\ **--membind**\ \ *numanodeset*\ ] \ **chvm**\ \ *noderange*\ [\ **--devpassthru**\ \ *pcidevice*\ ...] +\ **chvm**\ \ *noderange*\ [\ **--devdetach**\ \ *pcidevice*\ ...] + VMware/KVM specific: ==================== @@ -160,13 +167,6 @@ For normal power machine: chvm could be used to modify the resources assigned to partitions. The admin shall specify the attributes with options \ *vmcpus*\ , \ *vmmemory*\ , \ *add_physlots*\ , \ *vmothersetting*\ , \ *add_vmnics*\ and/or \ *add_vmstorage*\ . If nothing specified, nothing will be returned. -VMware/KVM specific: -==================== - - -The chvm command modifes the vm specified in noderange. Calling with deregister or purge options at the same time as the resize option is not recommended. - - zVM specific: ============= @@ -303,34 +303,6 @@ PPC (using Direct FSP Management) specific: -\ **--cpupin hostcpuset**\ - - To pin guest domain virtual CPUs to physical host CPUs specified with \ *hostcpuset*\ . - \ *hostcpuset*\ is a list of physical CPU numbers. Its syntax is a comma separated list and a special - markup using '-' and '^' (ex. '0-4', '0-3,^2') can also be allowed. The '-' denotes the range and - the '^' denotes exclusive. - - Note: The expression is sequentially evaluated, so "0-15,^8" is identical to "9-14,0-7,15" but not - identical to "^8,0-15". - - - -\ **--membind numanodeset**\ - - It is possible to restrict a guest to allocate memory from the specified set of NUMA nodes \ *numanodeset*\ . - If the guest vCPUs are also pinned to a set of cores located on that same set of NUMA nodes, memory - access is local and improves memory access performance. - - - -\ **--devpassthru pcidevice1,pcidevice2...**\ - - The PCI passthrough gives a guest VM direct access to I/O devices \ *pcidevice1,pcidevice2...*\ . - The PCI devices are assigned to a virtual machine, and the virtual machine can use this I/O exclusively. - The devices list are a list of comma separated PCI device names delimited with comma, the PCI device names can be obtained by running \ **virsh nodedev-list**\ on the host. - - - VMware/KVM specific: ==================== @@ -374,6 +346,46 @@ VMware/KVM specific: +KVM specific: +============= + + + +\ **--cpupin hostcpuset**\ + + To pin guest domain virtual CPUs to physical host CPUs specified with \ *hostcpuset*\ . + \ *hostcpuset*\ is a list of physical CPU numbers. Its syntax is a comma separated list and a special + markup using '-' and '^' (ex. '0-4', '0-3,^2') can also be allowed. The '-' denotes the range and + the '^' denotes exclusive. + + Note: The expression is sequentially evaluated, so "0-15,^8" is identical to "9-14,0-7,15" but not + identical to "^8,0-15". + + + +\ **--membind numanodeset**\ + + It is possible to restrict a guest to allocate memory from the specified set of NUMA nodes \ *numanodeset*\ . + If the guest vCPUs are also pinned to a set of cores located on that same set of NUMA nodes, memory + access is local and improves memory access performance. + + + +\ **--devpassthru pcidevice1,pcidevice2...**\ + + The PCI passthrough gives a guest VM direct access to I/O devices \ *pcidevice1,pcidevice2...*\ . + The PCI devices are assigned to a virtual machine, and the virtual machine can use this I/O exclusively. + The devices list are a list of comma separated PCI device names delimited with comma, the PCI device names can be obtained by running \ **virsh nodedev-list**\ on the host. + + + +\ **--devdetach pcidevice1,pcidevice2...**\ + + To detaching the PCI devices which are attached to VM guest via PCI passthrough from the VM guest. The devices list are a list of comma separated PCI device names delimited with comma, the PCI device names can be obtained by running \ **virsh nodedev-list**\ on the host. + + + + zVM specific: ============= diff --git a/perl-xCAT/xCAT/Schema.pm b/perl-xCAT/xCAT/Schema.pm index 4ad0ae990..23e791a5f 100755 --- a/perl-xCAT/xCAT/Schema.pm +++ b/perl-xCAT/xCAT/Schema.pm @@ -222,7 +222,7 @@ vm => { 'vncport' => 'Tracks the current VNC display port (currently not meant to be set', 'textconsole' => 'Tracks the Psuedo-TTY that maps to the serial port or console of a VM', 'powerstate' => "This flag is used by xCAT to track the last known power state of the VM.", - 'othersettings' => "This allows specifying a semicolon delimited list of key->value pairs to include in a vmx file of VMware or KVM. For partitioning on normal power machines, this option is used to specify the hugepage and/or bsr information, the value is like:'hugepage:1,bsr=2'. For KVM cpu pinning, this option is used to specify the physical cpu set on the host, the value is like:'vcpupin:'0-15,^8' ',Its syntax is a comma separated list and a special markup using '-' and '^' (ex. '0-4', '0-3,^2') can also be allowed, the '-' denotes the range and the '^' denotes exclusive. For KVM memory binding, the value is like:'membind:0', restrict a guest to allocate memory from the specified set of NUMA nodes. For PCI passthrough, the value is like:'devpassthrough:pci_0001_01_00_0,pci_0000_03_00_0',the PCI devices are assigned to a virtual machine, and the virtual machine can use this I/O exclusively,the devices list are a list of PCI device names delimited with comma, the PCI device names can be obtained by running B on the host.", + 'othersettings' => "This allows specifying a semicolon delimited list of key->value pairs to include in a vmx file of VMware or KVM. For partitioning on normal power machines, this option is used to specify the hugepage and/or bsr information, the value is like:'hugepage:1,bsr=2'. For KVM cpu pinning, this option is used to specify the physical cpu set on the host, the value is like:\"vcpupin:'0-15,^8'\",Its syntax is a comma separated list and a special markup using '-' and '^' (ex. '0-4', '0-3,^2') can also be allowed, the '-' denotes the range and the '^' denotes exclusive. For KVM memory binding, the value is like:'membind:0', restrict a guest to allocate memory from the specified set of NUMA nodes. For PCI passthrough, the value is like:'devpassthrough:pci_0001_01_00_0,pci_0000_03_00_0',the PCI devices are assigned to a virtual machine, and the virtual machine can use this I/O exclusively,the devices list are a list of PCI device names delimited with comma, the PCI device names can be obtained by running B on the host.", 'guestostype' => "This allows administrator to specify an identifier for OS to pass through to virtualization stack. Normally this should be ignored as xCAT will translate from nodetype.os rather than requiring this field be used\n", 'beacon' => "This flag is used by xCAT to track the state of the identify LED with respect to the VM.", 'datacenter' => "Optionally specify a datacenter for the VM to exist in (only applicable to VMWare)", diff --git a/perl-xCAT/xCAT/Usage.pm b/perl-xCAT/xCAT/Usage.pm index bff2a2ad5..e3ff511ca 100755 --- a/perl-xCAT/xCAT/Usage.pm +++ b/perl-xCAT/xCAT/Usage.pm @@ -250,9 +250,11 @@ my %usage = ( [add_vmnics=vlan1,vlan2] [add_vmstorage=] [--vios] chvm [del_physlots=drc_index1,drc_index2...] chvm [del_vadapter=slotid] + KVM specific: chvm [--cpupin hostcpuset] chvm [--membind numanodeset] chvm [--devpassthru pcidevice1,pcidevice2... ] + chvm [--devdetach pcidevice1,pcidevice2... ] VMware specific: chvm [-a size][-d disk][-p disk][--resize disk=size][--cpus count][--mem memory] zVM specific: diff --git a/xCAT-client/pods/man1/chvm.1.pod b/xCAT-client/pods/man1/chvm.1.pod index 2087a0243..9930773b6 100644 --- a/xCAT-client/pods/man1/chvm.1.pod +++ b/xCAT-client/pods/man1/chvm.1.pod @@ -29,12 +29,16 @@ B I [B] [B] [B] [B] + +=head2 KVM specific: + B I [B<--cpupin> I] B I [B<--membind> I] B I [B<--devpassthru> I...] +B I [B<--devdetach> I...] =head2 VMware/KVM specific: @@ -125,9 +129,6 @@ For normal power machine: chvm could be used to modify the resources assigned to partitions. The admin shall specify the attributes with options I, I, I, I, I and/or I. If nothing specified, nothing will be returned. -=head2 VMware/KVM specific: - -The chvm command modifes the vm specified in noderange. Calling with deregister or purge options at the same time as the resize option is not recommended. =head2 zVM specific: @@ -223,28 +224,6 @@ To delete physical slots which are specified by the I. To delete a virtual adapter specified by the I. -=item B<--cpupin hostcpuset> - -To pin guest domain virtual CPUs to physical host CPUs specified with I. -I is a list of physical CPU numbers. Its syntax is a comma separated list and a special -markup using '-' and '^' (ex. '0-4', '0-3,^2') can also be allowed. The '-' denotes the range and -the '^' denotes exclusive. - -Note: The expression is sequentially evaluated, so "0-15,^8" is identical to "9-14,0-7,15" but not -identical to "^8,0-15". - -=item B<--membind numanodeset> - -It is possible to restrict a guest to allocate memory from the specified set of NUMA nodes I. -If the guest vCPUs are also pinned to a set of cores located on that same set of NUMA nodes, memory -access is local and improves memory access performance. - -=item B<--devpassthru pcidevice1,pcidevice2...> - -The PCI passthrough gives a guest VM direct access to I/O devices I. -The PCI devices are assigned to a virtual machine, and the virtual machine can use this I/O exclusively. -The devices list are a list of comma separated PCI device names delimited with comma, the PCI device names can be obtained by running B on the host. - =back @@ -276,6 +255,40 @@ Purge the Hard disk. Deregisters and deletes the files. Multiple can be done w Change the size of the Hard disk. The disk can never be set to less than it's current size. Multiple disks can be resized to I by using comma separated values on the left side of B<=>. The disks are specified by SCSI id. Size defaults to GB. +=back + + +=head2 KVM specific: + +=over 10 + +=item B<--cpupin hostcpuset> + +To pin guest domain virtual CPUs to physical host CPUs specified with I. +I is a list of physical CPU numbers. Its syntax is a comma separated list and a special +markup using '-' and '^' (ex. '0-4', '0-3,^2') can also be allowed. The '-' denotes the range and +the '^' denotes exclusive. + +Note: The expression is sequentially evaluated, so "0-15,^8" is identical to "9-14,0-7,15" but not +identical to "^8,0-15". + +=item B<--membind numanodeset> + +It is possible to restrict a guest to allocate memory from the specified set of NUMA nodes I. +If the guest vCPUs are also pinned to a set of cores located on that same set of NUMA nodes, memory +access is local and improves memory access performance. + +=item B<--devpassthru pcidevice1,pcidevice2...> + +The PCI passthrough gives a guest VM direct access to I/O devices I. +The PCI devices are assigned to a virtual machine, and the virtual machine can use this I/O exclusively. +The devices list are a list of comma separated PCI device names delimited with comma, the PCI device names can be obtained by running B on the host. + +=item B<--devdetach pcidevice1,pcidevice2...> + +To detaching the PCI devices which are attached to VM guest via PCI passthrough from the VM guest. The devices list are a list of comma separated PCI device names delimited with comma, the PCI device names can be obtained by running B on the host. + + =back =head2 zVM specific: diff --git a/xCAT-server/lib/xcat/plugins/kvm.pm b/xCAT-server/lib/xcat/plugins/kvm.pm index 371e1c2b3..860921f6e 100755 --- a/xCAT-server/lib/xcat/plugins/kvm.pm +++ b/xCAT-server/lib/xcat/plugins/kvm.pm @@ -1,9 +1,10 @@ #!/usr/bin/env perl # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html package xCAT_plugin::kvm; + BEGIN { - $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; + $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; } use lib "$::XCATROOT/lib/perl"; use xCAT::GlobalDef; @@ -13,6 +14,7 @@ use xCAT_monitoring::monitorctrl; use xCAT::Table; use xCAT::Usage; use XML::LibXML; #now that we are in the business of modifying xml data, need something capable of preserving more of the XML structure + #TODO: convert all uses of XML::Simple to LibXML? Using both seems wasteful in a way.. use XML::Simple qw(XMLout); use Thread qw(yield); @@ -24,10 +26,11 @@ use IO::Select; use xCAT::TableUtils; use xCAT::ServiceNodeUtils; use strict; + #use warnings; -my $use_xhrm=0; #xCAT Hypervisor Resource Manager, to satisfy networking and storage prerequisites, default to not using it for the moment -my $imgfmt='raw'; #use raw format by default -my $clonemethod='qemu-img'; #use qemu-img command +my $use_xhrm = 0; #xCAT Hypervisor Resource Manager, to satisfy networking and storage prerequisites, default to not using it for the moment +my $imgfmt = 'raw'; #use raw format by default +my $clonemethod = 'qemu-img'; #use qemu-img command my %vm_comm_pids; my %offlinehyps; my %hypstats; @@ -35,13 +38,15 @@ my %offlinevms; my $parser; my @destblacklist; my $updatetable; #when a function is performing per-node operations, it can queue up a table update by populating parts of this hash -my $confdata; #a reference to serve as a common pointer betweer VMCommon functions and this plugin -require Sys::Virt; +my $confdata; #a reference to serve as a common pointer betweer VMCommon functions and this plugin +require Sys::Virt; + if (Sys::Virt->VERSION =~ /^0\.[10]\./) { - die; + die; } use XML::Simple; -$XML::Simple::PREFERRED_PARSER='XML::Parser'; +$XML::Simple::PREFERRED_PARSER = 'XML::Parser'; + #use Data::Dumper; use POSIX "WNOHANG"; use Storable qw(freeze thaw store_fd fd_retrieve); @@ -53,35 +58,37 @@ use Getopt::Long; use xCAT::SvrUtils; my %runningstates; -my $vmmaxp=64; +my $vmmaxp = 64; my $mactab; my %usedmacs; -my $status_noop="XXXno-opXXX"; +my $status_noop = "XXXno-opXXX"; my $callback; -my $requester; #used to track the user +my $requester; #used to track the user sub handled_commands { - return { - rpower => 'nodehm:power,mgt', - mkvm => 'nodehm:power,mgt', - clonevm => 'nodehm:power,mgt', - chvm => 'nodehm:power,mgt', - rmvm => 'nodehm:power,mgt', - rinv => 'nodehm:power,mgt', - rmigrate => 'nodehm:mgt', - getcons => 'nodehm:mgt', - #rvitals => 'nodehm:mgt', - #rinv => 'nodehm:mgt', - getrvidparms => 'nodehm:mgt', - lsvm => 'hypervisor:type', - rbeacon => 'nodehm:mgt', - revacuate => 'hypervisor:type', - vmstatenotify => 'hypervisor:type', - #rspreset => 'nodehm:mgt', - #rspconfig => 'nodehm:mgt', - #rbootseq => 'nodehm:mgt', - #reventlog => 'nodehm:mgt', - }; + return { + rpower => 'nodehm:power,mgt', + mkvm => 'nodehm:power,mgt', + clonevm => 'nodehm:power,mgt', + chvm => 'nodehm:power,mgt', + rmvm => 'nodehm:power,mgt', + rinv => 'nodehm:power,mgt', + rmigrate => 'nodehm:mgt', + getcons => 'nodehm:mgt', + + #rvitals => 'nodehm:mgt', + #rinv => 'nodehm:mgt', + getrvidparms => 'nodehm:mgt', + lsvm => 'hypervisor:type', + rbeacon => 'nodehm:mgt', + revacuate => 'hypervisor:type', + vmstatenotify => 'hypervisor:type', + + #rspreset => 'nodehm:mgt', + #rspconfig => 'nodehm:mgt', + #rbootseq => 'nodehm:mgt', + #reventlog => 'nodehm:mgt', + }; } my $hypconn; @@ -93,44 +100,45 @@ my $vmtab; sub get_path_for_pool { - my $poolobj =shift; + my $poolobj = shift; my $poolxml = $poolobj->get_xml_description(); my $pooldom = $parser->parse_string($poolxml); - my @paths = $pooldom->findnodes("/pool/target/path/text()"); + my @paths = $pooldom->findnodes("/pool/target/path/text()"); if (scalar @paths != 1) { return undef; } return $paths[0]->data; } + sub build_pool_xml { my $url = shift; my $pooldesc; - my $name=$url; - $name=~ s!nfs://!nfs_!; - $name=~ s!dir://!dir_!; - $name=~ s!lvm://!lvm_!; + my $name = $url; + $name =~ s!nfs://!nfs_!; + $name =~ s!dir://!dir_!; + $name =~ s!lvm://!lvm_!; $name =~ s/\//_/g; #though libvirt considers / kosher, sometimes it wants to create a local xml file using name for filename... - if ($url =~ /^dir:/) { #directory style.. - my $path=$url; - $path =~ s/dir:\/\///g; - return "$name$path"; - } elsif ($url =~ /^lvm:\/\//) { #lvm specified - my $path = $url; - $path =~ s/^lvm:\/\///; - $path =~ s/:(.*)\z//; - my $volumes = $1; - my $mount = "/dev/$path"; - my $xml = ''.$path.''.$mount.''; - if ($volumes) { - $xml .= ""; - my @vols = split /,/,$volumes; - foreach (@vols) { - $xml .= ''; - } - $xml .= ""; - } - $xml .= ""; - return $xml; + if ($url =~ /^dir:/) { #directory style.. + my $path = $url; + $path =~ s/dir:\/\///g; + return "$name$path"; + } elsif ($url =~ /^lvm:\/\//) { #lvm specified + my $path = $url; + $path =~ s/^lvm:\/\///; + $path =~ s/:(.*)\z//; + my $volumes = $1; + my $mount = "/dev/$path"; + my $xml = '' . $path . '' . $mount . ''; + if ($volumes) { + $xml .= ""; + my @vols = split /,/, $volumes; + foreach (@vols) { + $xml .= ''; + } + $xml .= ""; + } + $xml .= ""; + return $xml; } my $mounthost = shift; unless ($mounthost) { $mounthost = $hyp; } @@ -139,158 +147,166 @@ sub build_pool_xml { $host =~ s/.*:\/\///; $host =~ s/(\/.*)//; my $srcpath = $1; - my $uuid = xCAT::Utils::genUUID(url=>$url); -#first, we make a pool desc that won't have slashes in them + my $uuid = xCAT::Utils::genUUID(url => $url); + + #first, we make a pool desc that won't have slashes in them $pooldesc = ''; - $pooldesc .= ''.$name.''; - $pooldesc .= ''.$uuid.'>'; + $pooldesc .= '' . $name . ''; + $pooldesc .= '' . $uuid . '>'; $pooldesc .= ''; - $pooldesc .= ''; - $pooldesc .= ''; + $pooldesc .= ''; + $pooldesc .= ''; $pooldesc .= ''; - $pooldesc .= '/var/lib/xcat/pools/'.$uuid.''; + $pooldesc .= '/var/lib/xcat/pools/' . $uuid . ''; + #turns out we can 'define', then 'build', then 'create' on the poolobj instead of 'create', to get mkdir -p like function #system("ssh $mounthost mkdir -p /var/lib/xcat/pools/$uuid"); #ok, so not *technically* just building XML, but here is the cheapest - #place to know uuid... And yes, we must be allowed to ssh in - #libvirt just isn't capable enough for this sort of usage + #place to know uuid... And yes, we must be allowed to ssh in + #libvirt just isn't capable enough for this sort of usage return $pooldesc; } sub get_storage_pool_by_url { - my $url = shift; - my $virtconn = shift; + my $url = shift; + my $virtconn = shift; my $mounthost = shift; unless ($virtconn) { $virtconn = $hypconn; } my @currpools = $virtconn->list_storage_pools(); - push @currpools,$virtconn->list_defined_storage_pools(); + push @currpools, $virtconn->list_defined_storage_pools(); my $poolobj; my $pool; - my $islvm=0; + my $islvm = 0; + foreach my $poolo (@currpools) { $poolobj = $poolo; $pool = $parser->parse_string($poolobj->get_xml_description()); #XMLin($poolobj->get_xml_description()); if ($url =~ /^nfs:\/\/([^\/]*)(\/.*)$/) { #check the essence of the pool rather than the name - my $host=$1; - my $path=$2; + my $host = $1; + my $path = $2; unless ($pool->findnodes("/pool")->[0]->getAttribute("type") eq "netfs") { - $pool=undef; + $pool = undef; next; } + #ok, it is netfs, now check source.. my $checkhost = $pool->findnodes("/pool/source/host")->[0]->getAttribute("name"); my $checkpath = $pool->findnodes("/pool/source/dir")->[0]->getAttribute("path"); if ($checkhost eq $host and $checkpath eq $path) { #TODO: check name resolution to see if they match really even if not strictly the same last; } - } elsif ($url =~ /^dir:\/\/(.*)\z/) { #a directory, simple enough - my $path = $1; + } elsif ($url =~ /^dir:\/\/(.*)\z/) { #a directory, simple enough + my $path = $1; unless ($path =~ /^\//) { - $path = '/'.$path; - } - $path =~ s/\/\z//; #delete trailing / if table specifies, a perfectly understable 'mistake' - my $checkpath = $pool->findnodes("/pool/target/path/text()")->[0]->data; - if ($checkpath eq $path) { - last; + $path = '/' . $path; + } + $path =~ s/\/\z//; #delete trailing / if table specifies, a perfectly understable 'mistake' + my $checkpath = $pool->findnodes("/pool/target/path/text()")->[0]->data; + if ($checkpath eq $path) { + last; + } + } elsif ($url =~ /^lvm:\/\/([^:]*)/) { #lvm volume group.... + my $vgname = $1; + my $checkname = $pool->findnodes("/pool/name/text()")->[0]->data; + if ($checkname eq $vgname) { + $islvm = 1; + last; } - } elsif ($url =~ /^lvm:\/\/([^:]*)/) { #lvm volume group.... - my $vgname = $1; - my $checkname = $pool->findnodes("/pool/name/text()")->[0]->data; - if ($checkname eq $vgname) { - $islvm=1; - last; - } } elsif ($pool->findnodes('/pool/name/text()')->[0]->data eq $url) { #$pool->{name} eq $url) { last; } $pool = undef; } - if ($pool) { - my $inf=$poolobj->get_info(); + if ($pool) { + my $inf = $poolobj->get_info(); if ($inf->{state} == 0) { #Sys::Virt::StoragePool::STATE_INACTIVE) { #if pool is currently inactive, bring it up unless ($islvm) { $poolobj->build(); } #if lvm and defined, it's almost certainly been built $poolobj->create(); } - eval { #we *try* to do this, but various things may interfere. - #this is basically to make sure the list of contents is up to date - $poolobj->refresh(); - }; - return $poolobj; + eval { #we *try* to do this, but various things may interfere. + #this is basically to make sure the list of contents is up to date + $poolobj->refresh(); + }; + return $poolobj; } - $poolobj = $virtconn->define_storage_pool(build_pool_xml($url,$mounthost)); + $poolobj = $virtconn->define_storage_pool(build_pool_xml($url, $mounthost)); eval { $poolobj->build(); }; - if ($@) { - my $error = $@; - unless ($error =~ /vgcreate.*exit status 3/ or $error =~ /pvcreate.*exit status 5/) { - die $@; - } + if ($@) { + my $error = $@; + unless ($error =~ /vgcreate.*exit status 3/ or $error =~ /pvcreate.*exit status 5/) { + die $@; + } } $poolobj->create(); eval { #wrap in eval, not likely to fail here, but calling it at all may be superfluous anyway - $poolobj->refresh(); + $poolobj->refresh(); }; return $poolobj; } sub get_multiple_paths_by_url { - my %args =@_; - my $url = $args{url}; - my $node = $args{node}; + my %args = @_; + my $url = $args{url}; + my $node = $args{node}; my $poolobj = get_storage_pool_by_url($url); unless ($poolobj) { die "Cound not get storage pool for $url"; } - eval { #refresh() can 'die' if cloning in progress, accept stale data then - $poolobj->refresh(); #if volumes change on nfs storage, libvirt is too dumb to notice + eval { #refresh() can 'die' if cloning in progress, accept stale data then + $poolobj->refresh(); #if volumes change on nfs storage, libvirt is too dumb to notice }; my @volobjs = $poolobj->list_volumes(); my %paths; foreach (@volobjs) { if ($_->get_name() =~ /^$node\.([^\.]*)\.([^\.]*)$/) { - $paths{$_->get_path()} = {device=>$1,format=>$2}; + $paths{ $_->get_path() } = { device => $1, format => $2 }; } elsif ($_->get_name() =~ /^$node\.([^\.]*)$/) { - $paths{$_->get_path()} = {device=>$1,format=>'raw'}; - #this requires any current user of qcow2 to migrate, unfortunate to escape - #a vulnerability where raw user could write malicious qcow2 to header - #and use that to get at files on the hypervisor os with escalated privilege + $paths{ $_->get_path() } = { device => $1, format => 'raw' }; + + #this requires any current user of qcow2 to migrate, unfortunate to escape + #a vulnerability where raw user could write malicious qcow2 to header + #and use that to get at files on the hypervisor os with escalated privilege } } return \%paths; } + sub get_filepath_by_url { #at the end of the day, the libvirt storage api gives the following capability: #mount, limited ls, and qemu-img - #it does not frontend mkdir, and does not abstract away any nitty-gritty detail, you must know: - #the real mountpoint, and the real full path to storage - #in addition to this, subdirectories are not allowed, and certain extra metadata must be created - #not a big fan compared to ssh and run the commands myself, but it's the most straightforward path - #to avoid ssh for users who dislike that style access - my %args = @_; - my $url = $args{url}; - my $dev = $args{dev}; + #it does not frontend mkdir, and does not abstract away any nitty-gritty detail, you must know: + #the real mountpoint, and the real full path to storage + #in addition to this, subdirectories are not allowed, and certain extra metadata must be created + #not a big fan compared to ssh and run the commands myself, but it's the most straightforward path + #to avoid ssh for users who dislike that style access + my %args = @_; + my $url = $args{url}; + my $dev = $args{dev}; my $create = $args{create}; - my $force = $args{force}; + my $force = $args{force}; my $format = $args{format}; my $sparse = 1; if ($url =~ /^lvm:/) { - $sparse = 0; - $format = 'raw'; + $sparse = 0; + $format = 'raw'; } unless ($format) { $format = 'qcow2'; } + #print "url=$url, dev=$dev,create=$create, force=$force, format=$format\n"; #ok, now that we have the pool, we need the storage volume from the pool for the node/dev my $poolobj = get_storage_pool_by_url($url); unless ($poolobj) { die "Could not get storage pool for $url"; } eval { #make a refresh attempt non-fatal to fail, since cloning can block it - $poolobj->refresh(); #if volumes change on nfs storage, libvirt is too dumb to notice + $poolobj->refresh(); #if volumes change on nfs storage, libvirt is too dumb to notice }; - my @volobjs = $poolobj->list_volumes(); - my $desiredname = $node.'.'.$dev.'.'.$format; + my @volobjs = $poolobj->list_volumes(); + my $desiredname = $node . '.' . $dev . '.' . $format; + #print "desiredname=$desiredname, volobjs=@volobjs\n"; foreach (@volobjs) { if ($_->get_name() eq $desiredname) { if ($create) { - if ($force) { #must destroy the storage + if ($force) { #must destroy the storage $_->delete(); } else { die "Path already exists"; @@ -300,14 +316,15 @@ sub get_filepath_by_url { #at the end of the day, the libvirt storage api gives } } } - if ($create) { + if ($create) { if ($create =~ /^clone=(.*)$/) { my $src = $1; - my $fmt='raw'; + my $fmt = 'raw'; if ($src =~ /\.qcow2$/) { - $fmt='qcow2'; + $fmt = 'qcow2'; } - my $vol = $poolobj->create_volume("".$desiredname."100$src"); + my $vol = $poolobj->create_volume("" . $desiredname . "100$src"); + #ok, this is simply hinting, not the real deal, so to speak # 1) sys::virt complains if capacity isn't defined. We say '100', knowing full well it will be promptly ignored down the code. This is aggravating # and warrants recheck with the RHEL6 stack @@ -319,13 +336,13 @@ sub get_filepath_by_url { #at the end of the day, the libvirt storage api gives # 4) qemu-img was so much more transparent and easy to figure out than this # additionally, when mastering a powered down node, we should rebase the node to be a cow clone of the master it just spawned } else { - my $vol; - unless ($sparse) { #skip allocation specification for now - #currently, LV can have reduced allocation, but *cannot* grow..... - $vol = $poolobj->create_volume("".$desiredname."".getUnits($create,"G",1).""); - } else { - $vol = $poolobj->create_volume("".$desiredname."".getUnits($create,"G",1)."0"); - } + my $vol; + unless ($sparse) { #skip allocation specification for now + #currently, LV can have reduced allocation, but *cannot* grow..... + $vol = $poolobj->create_volume("" . $desiredname . "" . getUnits($create, "G", 1) . ""); + } else { + $vol = $poolobj->create_volume("" . $desiredname . "" . getUnits($create, "G", 1) . "0"); + } if ($vol) { return $vol->get_path(); } } } else { @@ -334,99 +351,103 @@ sub get_filepath_by_url { #at the end of the day, the libvirt storage api gives } sub nodesockopen { - my $node = shift; - my $port = shift; - unless ($node) { return 0; } - my $socket; - my $addr = gethostbyname($node); - if (!$addr) { - xCAT::SvrUtils::sendmsg([1,"Cannot not resolve host $node"], $callback); + my $node = shift; + my $port = shift; + unless ($node) { return 0; } + my $socket; + my $addr = gethostbyname($node); + if (!$addr) { + xCAT::SvrUtils::sendmsg([ 1, "Cannot not resolve host $node" ], $callback); return 0; - } - my $sin = sockaddr_in($port,$addr); - my $proto = getprotobyname('tcp'); - socket($socket,PF_INET,SOCK_STREAM,$proto) || return 0; - connect($socket,$sin) || return 0; - return 1; + } + my $sin = sockaddr_in($port, $addr); + my $proto = getprotobyname('tcp'); + socket($socket, PF_INET, SOCK_STREAM, $proto) || return 0; + connect($socket, $sin) || return 0; + return 1; } - + sub waitforack { - my $sock = shift; + 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 + $select->remove($sock); #Block until parent acks data } } } sub reconfigvm { - $node=shift; - my $xml = shift; - my $domdesc = $parser->parse_string($xml); + $node = shift; + my $xml = shift; + my $domdesc = $parser->parse_string($xml); my @bootdevs = $domdesc->findnodes("/domain/os/boot"); my $curroffset = $domdesc->findnodes("/domain/clock")->[0]->getAttribute("offset"); my $newoffset; - my $needfixin=0; - if (defined ($confdata->{vm}->{$node}->[0]->{clockoffset})) { + my $needfixin = 0; + if (defined($confdata->{vm}->{$node}->[0]->{clockoffset})) { + #If user requested a specific behavior, give it - $newoffset = $confdata->{vm}->{$node}->[0]->{clockoffset}; + $newoffset = $confdata->{vm}->{$node}->[0]->{clockoffset}; } else { + #Otherwise, only do local time for things that look MS - if (defined ($confdata->{nodetype}->{$node}->[0]->{os}) and $confdata->{nodetype}->{$node}->[0]->{os} =~ /win.*/) { - $newoffset = 'localtime'; - } else { #For everyone else, utc is preferred generally - $newoffset = 'utc'; + if (defined($confdata->{nodetype}->{$node}->[0]->{os}) and $confdata->{nodetype}->{$node}->[0]->{os} =~ /win.*/) { + $newoffset = 'localtime'; + } else { #For everyone else, utc is preferred generally + $newoffset = 'utc'; } } if ($curroffset ne $newoffset) { - $needfixin=1; - $domdesc->findnodes("/domain/clock")->[0]->setAttribute("offset",$newoffset); + $needfixin = 1; + $domdesc->findnodes("/domain/clock")->[0]->setAttribute("offset", $newoffset); } my @oldbootdevs; if (defined $confdata->{vm}->{$node}->[0]->{memory}) { - $needfixin=1; - $domdesc->findnodes("/domain/memory/text()")->[0]->setData(getUnits($confdata->{vm}->{$node}->[0]->{memory},"M",1024)); - foreach ($domdesc->findnodes("/domain/currentMemory/text()")) { - $_->setData(getUnits($confdata->{vm}->{$node}->[0]->{memory},"M",1024)); - } + $needfixin = 1; + $domdesc->findnodes("/domain/memory/text()")->[0]->setData(getUnits($confdata->{vm}->{$node}->[0]->{memory}, "M", 1024)); + foreach ($domdesc->findnodes("/domain/currentMemory/text()")) { + $_->setData(getUnits($confdata->{vm}->{$node}->[0]->{memory}, "M", 1024)); + } } if (defined $confdata->{vm}->{$node}->[0]->{vcpus}) { - $needfixin=1; - $domdesc->findnodes("/domain/vcpu/text()")->[0]->setData($confdata->{vm}->{$node}->[0]->{vcpus}); + $needfixin = 1; + $domdesc->findnodes("/domain/vcpu/text()")->[0]->setData($confdata->{vm}->{$node}->[0]->{vcpus}); } if (defined $confdata->{vm}->{$node}->[0]->{bootorder}) { - my @expectedorder = split(/[:,]/,$confdata->{vm}->{$node}->[0]->{bootorder}); + my @expectedorder = split(/[:,]/, $confdata->{vm}->{$node}->[0]->{bootorder}); foreach (@expectedorder) { #this loop will check for changes and fix 'n' and 'net' my $currdev = shift @bootdevs; if ("net" eq $_ or "n" eq $_) { $_ = "network"; } unless ($currdev and $currdev->getAttribute("dev") eq $_) { - $needfixin=1; + $needfixin = 1; } if ($currdev) { - push @oldbootdevs,$currdev; + push @oldbootdevs, $currdev; } } if (scalar(@bootdevs)) { - $needfixin=1; - push @oldbootdevs,@bootdevs; + $needfixin = 1; + push @oldbootdevs, @bootdevs; } unless ($needfixin) { return 0; } + #ok, we need to remove all 'boot' nodes from current xml, and put in new ones in the order we like foreach (@oldbootdevs) { $_->parentNode->removeChild($_); } + #now to add what we want... my $osnode = $domdesc->findnodes("/domain/os")->[0]; foreach (@expectedorder) { - my $fragment = $parser->parse_balanced_chunk(''); + my $fragment = $parser->parse_balanced_chunk(''); $osnode->appendChild($fragment); } } @@ -437,89 +458,93 @@ sub reconfigvm { sub build_oshash { my %rethash; - $rethash{type}->{content}='hvm'; - - my $hypcpumodel = $confdata->{$confdata->{vm}->{$node}->[0]->{host}}->{cpumodel}; + $rethash{type}->{content} = 'hvm'; + + my $hypcpumodel = $confdata->{ $confdata->{vm}->{$node}->[0]->{host} }->{cpumodel}; unless (defined($hypcpumodel) and $hypcpumodel eq "ppc64le") { - $rethash{bios}->{useserial}='yes'; + $rethash{bios}->{useserial} = 'yes'; } if (defined $confdata->{vm}->{$node}->[0]->{bootorder}) { my $bootorder = $confdata->{vm}->{$node}->[0]->{bootorder}; - my @bootdevs = split(/[:,]/,$bootorder); - my $bootnum = 0; + my @bootdevs = split(/[:,]/, $bootorder); + my $bootnum = 0; foreach (@bootdevs) { if ("net" eq $_ or "n" eq $_) { - $rethash{boot}->[$bootnum]->{dev}="network"; + $rethash{boot}->[$bootnum]->{dev} = "network"; } else { - $rethash{boot}->[$bootnum]->{dev}=$_; + $rethash{boot}->[$bootnum]->{dev} = $_; } $bootnum++; } } else { - $rethash{boot}->[0]->{dev}='network'; - $rethash{boot}->[1]->{dev}='hd'; + $rethash{boot}->[0]->{dev} = 'network'; + $rethash{boot}->[1]->{dev} = 'hd'; } return \%rethash; } sub build_diskstruct { print "build_diskstruct called\n"; - my $cdloc=shift; - my @returns=(); + my $cdloc = shift; + my @returns = (); my $currdev; - my @suffixes=('a','b','d'..'zzz'); - my $suffidx=0; + my @suffixes = ('a', 'b', 'd' .. 'zzz'); + my $suffidx = 0; my $storagemodel = $confdata->{vm}->{$node}->[0]->{storagemodel}; - my $cachemethod = "none"; - if ( $confdata->{vm}->{$node}->[0]->{storagecache}) { + my $cachemethod = "none"; + if ($confdata->{vm}->{$node}->[0]->{storagecache}) { $cachemethod = $confdata->{vm}->{$node}->[0]->{storagecache}; } if (defined $confdata->{vm}->{$node}->[0]->{storage}) { - my $disklocs=$confdata->{vm}->{$node}->[0]->{storage}; - my @locations=split /\|/,$disklocs; + my $disklocs = $confdata->{vm}->{$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; $disk =~ s/=(.*)//; my $model = $1; unless ($model) { + #if not defined, model will stay undefined like above $model = $storagemodel; - unless ($model) { $model = 'ide'; } #if still not defined, ide + unless ($model) { $model = 'ide'; } #if still not defined, ide } - my $prefix='hd'; + my $prefix = 'hd'; if ($model eq 'virtio') { - $prefix='vd'; + $prefix = 'vd'; } elsif ($model eq 'scsi') { - $prefix='sd'; + $prefix = 'sd'; } - $diskhash->{type} = 'file'; - $diskhash->{device} = 'disk'; - $diskhash->{target}->{dev} = $prefix.$suffixes[$suffidx]; + $diskhash->{type} = 'file'; + $diskhash->{device} = 'disk'; + $diskhash->{target}->{dev} = $prefix . $suffixes[$suffidx]; $diskhash->{target}->{bus} = $model; 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->{type} = 'block'; $diskhash->{source}->{dev} = substr($disk_parts[0], 4); } elsif ($disk_parts[0] =~ m/^nfs:\/\/(.*)$/ or $disk_parts[0] =~ m/^dir:\/\/(.*)$/ or $disk_parts[0] =~ m/^lvm:\/\/(.*)$/) { - my %disks = %{get_multiple_paths_by_url(url=>$disk_parts[0],node=>$node)}; + my %disks = %{ get_multiple_paths_by_url(url => $disk_parts[0], node => $node) }; unless (keys %disks) { - die "Unable to find any persistent disks at ".$disk_parts[0]; + die "Unable to find any persistent disks at " . $disk_parts[0]; } foreach (keys %disks) { my $tdiskhash; $tdiskhash->{type}; - $tdiskhash->{device}='disk'; - $tdiskhash->{driver}->{name}='qemu'; - $tdiskhash->{driver}->{type}=$disks{$_}->{format}; - $tdiskhash->{driver}->{cache}=$cachemethod; - $tdiskhash->{source}->{file}=$_; - $tdiskhash->{target}->{dev} = $disks{$_}->{device}; + $tdiskhash->{device} = 'disk'; + $tdiskhash->{driver}->{name} = 'qemu'; + $tdiskhash->{driver}->{type} = $disks{$_}->{format}; + $tdiskhash->{driver}->{cache} = $cachemethod; + $tdiskhash->{source}->{file} = $_; + $tdiskhash->{target}->{dev} = $disks{$_}->{device}; + if ($disks{$_} =~ /^vd/) { $tdiskhash->{target}->{bus} = 'virtio'; } elsif ($disks{$_} =~ /^hd/) { @@ -527,12 +552,12 @@ sub build_diskstruct { } elsif ($disks{$_} =~ /^sd/) { $tdiskhash->{target}->{bus} = 'scsi'; } - push @returns,$tdiskhash; + push @returns, $tdiskhash; } - next; #nfs:// skips the other stuff - #$diskhash->{source}->{file} = get_filepath_by_url(url=>$disk_parts[0],dev=>$diskhash->{target}->{dev}); #"/var/lib/xcat/vmnt/nfs_".$1."/$node/".$diskhash->{target}->{dev}; - } else { #currently, this would be a bare file to slap in as a disk + next; #nfs:// skips the other stuff + #$diskhash->{source}->{file} = get_filepath_by_url(url=>$disk_parts[0],dev=>$diskhash->{target}->{dev}); #"/var/lib/xcat/vmnt/nfs_".$1."/$node/".$diskhash->{target}->{dev}; + } else { #currently, this would be a bare file to slap in as a disk $diskhash->{source}->{file} = $disk_parts[0]; } @@ -546,231 +571,236 @@ sub build_diskstruct { $suffidx++; } if ($disk_opts[1] eq 'cdrom') { - $diskhash->{device}='cdrom'; + $diskhash->{device} = 'cdrom'; } } else { $suffidx++; } - push @returns,$diskhash; + push @returns, $diskhash; } } - my $cdprefix='hd'; + my $cdprefix = 'hd'; + # device name vd* doesn't work for CDROM, so delete it. #if ($storagemodel eq 'virtio') { # $cdprefix='vd'; #} els if ($storagemodel eq 'scsi') { - $cdprefix='sd'; + $cdprefix = 'sd'; } $suffidx += 1; if ($cdloc) { my $cdhash; - $cdhash->{device}='cdrom'; + $cdhash->{device} = 'cdrom'; if ($cdloc =~ /^\/dev/) { - $cdhash->{type}='block'; + $cdhash->{type} = 'block'; } else { - $cdhash->{type}='file'; + $cdhash->{type} = 'file'; } - $cdhash->{source}->{file}=$cdloc; + $cdhash->{source}->{file} = $cdloc; $cdhash->{readonly}; - $cdhash->{target}->{dev}=$cdprefix.$suffixes[$suffidx]; - push @returns,$cdhash; + $cdhash->{target}->{dev} = $cdprefix . $suffixes[$suffidx]; + push @returns, $cdhash; } else { #give the VM an empty optical drive, to allow chvm live attach/remove - my $cdhash; - $cdhash->{device}='cdrom'; - $cdhash->{type}='file'; - $cdhash->{readonly}; - $cdhash->{target}->{dev}=$cdprefix.$suffixes[$suffidx]; - push @returns,$cdhash; + my $cdhash; + $cdhash->{device} = 'cdrom'; + $cdhash->{type} = 'file'; + $cdhash->{readonly}; + $cdhash->{target}->{dev} = $cdprefix . $suffixes[$suffidx]; + push @returns, $cdhash; } return \@returns; } + sub getNodeUUID { my $node = shift; my $uuid; if ($confdata->{vpd}->{$node}->[0] and $confdata->{vpd}->{$node}->[0]->{uuid}) { - $uuid = $confdata->{vpd}->{$node}->[0]->{uuid}; - $uuid =~ s/^(..)(..)(..)(..)-(..)(..)-(..)(..)/$4$3$2$1-$6$5-$8$7/; + $uuid = $confdata->{vpd}->{$node}->[0]->{uuid}; + $uuid =~ s/^(..)(..)(..)(..)-(..)(..)-(..)(..)/$4$3$2$1-$6$5-$8$7/; return $uuid; } if ($confdata->{mac}->{$node}->[0]->{mac}) { #a uuidv1 is possible, generate that for absolute uniqueness guarantee my $mac = $confdata->{mac}->{$node}->[0]->{mac}; $mac =~ s/\|.*//; $mac =~ s/!.*//; - $updatetable->{vpd}->{$node}={uuid=>xCAT::Utils::genUUID(mac=>$mac)}; + $updatetable->{vpd}->{$node} = { uuid => xCAT::Utils::genUUID(mac => $mac) }; } else { - $updatetable->{vpd}->{$node}={uuid=>xCAT::Utils::genUUID()}; + $updatetable->{vpd}->{$node} = { uuid => xCAT::Utils::genUUID() }; } $uuid = $updatetable->{vpd}->{$node}->{uuid}; $uuid =~ s/^(..)(..)(..)(..)-(..)(..)-(..)(..)/$4$3$2$1-$6$5-$8$7/; return $uuid; } + sub build_nicstruct { my $rethash; my $node = shift; - my @nics=(); + my @nics = (); if ($confdata->{vm}->{$node}->[0]->{nics}) { - @nics = split /,/,$confdata->{vm}->{$node}->[0]->{nics}; + @nics = split /,/, $confdata->{vm}->{$node}->[0]->{nics}; } - my @macs=xCAT::VMCommon::getMacAddresses($confdata,$node,scalar @nics); + my @macs = xCAT::VMCommon::getMacAddresses($confdata, $node, scalar @nics); my @rethashes; foreach (@macs) { my $rethash; my $nic = shift @nics; my $type = 'virtio'; #better default fake nic than rtl8139, relevant to most unless ($nic) { - last; #Don't want to have multiple vnics tied to the same switch + last; #Don't want to have multiple vnics tied to the same switch } - $nic =~ s/.*://; #the detail of how the bridge was built is of no - #interest to this segment of code + $nic =~ s/.*://; #the detail of how the bridge was built is of no + #interest to this segment of code if ($confdata->{vm}->{$node}->[0]->{nicmodel}) { $type = $confdata->{vm}->{$node}->[0]->{nicmodel}; } if ($nic =~ /=/) { - ($nic,$type) = split /=/,$nic,2; + ($nic, $type) = split /=/, $nic, 2; } - $rethash->{type}='bridge'; - $rethash->{mac}->{address}=$_; - $rethash->{source}->{bridge}=$nic; - $rethash->{model}->{type}=$type; - push @rethashes,$rethash; + $rethash->{type} = 'bridge'; + $rethash->{mac}->{address} = $_; + $rethash->{source}->{bridge} = $nic; + $rethash->{model}->{type} = $type; + push @rethashes, $rethash; } return \@rethashes; } + sub getUnits { - my $amount = shift; + my $amount = shift; my $defunit = shift; - my $divisor=shift; + my $divisor = shift; unless ($divisor) { $divisor = 1; } - if ($amount =~ /(\D)$/) { #If unitless, add unit - $defunit=$1; + if ($amount =~ /(\D)$/) { #If unitless, add unit + $defunit = $1; chop $amount; } if ($defunit =~ /k/i) { - return $amount*1024/$divisor; + return $amount * 1024 / $divisor; } elsif ($defunit =~ /m/i) { - return $amount*1048576/$divisor; + return $amount * 1048576 / $divisor; } elsif ($defunit =~ /g/i) { - return $amount*1073741824/$divisor; - } + return $amount * 1073741824 / $divisor; + } } sub build_xmldesc { - my $node = shift; - my %args=@_; - my $cdloc=$args{cd}; - my %xtree=(); - my $hypcpumodel = $confdata->{$confdata->{vm}->{$node}->[0]->{host}}->{cpumodel}; - my $hypcputype = $confdata->{$confdata->{vm}->{$node}->[0]->{host}}->{cputype}; - my $hypcputhreads = $confdata->{$confdata->{vm}->{$node}->[0]->{host}}->{cpu_thread}; + my $node = shift; + my %args = @_; + my $cdloc = $args{cd}; + my %xtree = (); + my $hypcpumodel = $confdata->{ $confdata->{vm}->{$node}->[0]->{host} }->{cpumodel}; + my $hypcputype = $confdata->{ $confdata->{vm}->{$node}->[0]->{host} }->{cputype}; + my $hypcputhreads = $confdata->{ $confdata->{vm}->{$node}->[0]->{host} }->{cpu_thread}; unless ($hypcputhreads) { $hypcputhreads = "1"; } - $xtree{type}='kvm'; - $xtree{name}->{content}=$node; - $xtree{uuid}->{content}=getNodeUUID($node); - $xtree{os} = build_oshash(); + $xtree{type} = 'kvm'; + $xtree{name}->{content} = $node; + $xtree{uuid}->{content} = getNodeUUID($node); + $xtree{os} = build_oshash(); if (defined($hypcpumodel) and $hypcpumodel eq "ppc64") { - $xtree{os}->{type}->{arch} = "ppc64"; + $xtree{os}->{type}->{arch} = "ppc64"; $xtree{os}->{type}->{machine} = "pseries"; delete $xtree{os}->{bios}; } if ($args{memory}) { - $xtree{memory}->{content}=getUnits($args{memory},"M",1024); + $xtree{memory}->{content} = getUnits($args{memory}, "M", 1024); if ($confdata->{vm}->{$node}->[0]->{memory}) { - $updatetable->{vm}->{$node}->{memory}=$args{memory}; + $updatetable->{vm}->{$node}->{memory} = $args{memory}; } } elsif (defined $confdata->{vm}->{$node}->[0]->{memory}) { - $xtree{memory}->{content}=getUnits($confdata->{vm}->{$node}->[0]->{memory},"M",1024); + $xtree{memory}->{content} = getUnits($confdata->{vm}->{$node}->[0]->{memory}, "M", 1024); } else { - $xtree{memory}->{content}=524288; + $xtree{memory}->{content} = 524288; } - + my %cpupinhash; my @passthrudevices; my $memnumanodes; - my $advsettings=undef; + my $advsettings = undef; if (defined $confdata->{vm}->{$node}->[0]->{othersettings}) { - $advsettings=$confdata->{vm}->{$node}->[0]->{othersettings}; + $advsettings = $confdata->{vm}->{$node}->[0]->{othersettings}; } - - #parse the additional settings in attrubute vm.othersettings + + #parse the additional settings in attrubute vm.othersettings #the settings are semicolon delimited, the format of each setting is: #cpu pining: "vcpupin:" #pci passthrough: "devpassthrough:,..." #memory binding: "membind:" - if($advsettings){ - my @tmp_array=split ";",$advsettings; - foreach(@tmp_array){ - if(/vcpupin:['"]?([^:'"]*)['"]?:?['"]?([^:'"]*)['"]?/){ - if($2){ - #this is for cpu pining in the vcpu level,which is not currently supported - #reserved for future use - $cpupinhash{$1}=$2; - }else{ - $cpupinhash{ALL}=$1; - } - } - - if(/devpassthrough:(.*)/){ - @passthrudevices=split ",",$1; - } - - if(/membind:(.*)/){ - $memnumanodes=$1; - } - + if ($advsettings) { + my @tmp_array = split ";", $advsettings; + foreach (@tmp_array) { + if (/vcpupin:['"]?([^:'"]*)['"]?:?['"]?([^:'"]*)['"]?/) { + if ($2) { + + #this is for cpu pining in the vcpu level,which is not currently supported + #reserved for future use + $cpupinhash{$1} = $2; + } else { + $cpupinhash{ALL} = $1; + } + } + + if (/devpassthrough:(.*)/) { + @passthrudevices = split ",", $1; + } + + if (/membind:(.*)/) { + $memnumanodes = $1; + } + } - } + } #prepare the xml hash for memory binding - if(defined $memnumanodes){ - my %numatunehash; - $numatunehash{memory}=[{nodeset=>"$memnumanodes"}]; - $xtree{numatune}=\%numatunehash; + if (defined $memnumanodes) { + my %numatunehash; + $numatunehash{memory} = [ { nodeset => "$memnumanodes" } ]; + $xtree{numatune} = \%numatunehash; } #prepare the xml hash for cpu pining - if(exists $cpupinhash{ALL}){ - $xtree{vcpu}->{placement}='static'; - $xtree{vcpu}->{cpuset}="$cpupinhash{ALL}"; - $xtree{vcpu}->{cpuset}=~s/\"\'//g; + if (exists $cpupinhash{ALL}) { + $xtree{vcpu}->{placement} = 'static'; + $xtree{vcpu}->{cpuset} = "$cpupinhash{ALL}"; + $xtree{vcpu}->{cpuset} =~ s/\"\'//g; } - + #prepare the xml hash for pci passthrough my @prdevarray; - foreach my $devname(@passthrudevices){ - my $devobj=$hypconn->get_node_device_by_name($devname); - unless($devobj){ - return -1; - } + foreach my $devname (@passthrudevices) { + my $devobj = $hypconn->get_node_device_by_name($devname); + unless ($devobj) { + return -1; + } - #get the xml description of the pci device - my $devxml=$devobj->get_xml_description(); - unless($devxml){ - return -1; - } - - my $devhash=XMLin($devxml); - if(defined $devhash->{capability}->{type} and $devhash->{capability}->{type} =~ /pci/i ){ - my %tmphash; - $tmphash{mode}='subsystem'; - $tmphash{type}=$devhash->{capability}->{type}; - $tmphash{managed}="yes"; - $tmphash{driver}->{name}="vfio"; - $tmphash{source}->{address}->[0]=\%{$devhash->{'capability'}->{'iommuGroup'}->{'address'}}; - push(@prdevarray,\%tmphash); - - } + #get the xml description of the pci device + my $devxml = $devobj->get_xml_description(); + unless ($devxml) { + return -1; + } + + my $devhash = XMLin($devxml); + if (defined $devhash->{capability}->{type} and $devhash->{capability}->{type} =~ /pci/i) { + my %tmphash; + $tmphash{mode} = 'subsystem'; + $tmphash{type} = $devhash->{capability}->{type}; + $tmphash{managed} = "yes"; + $tmphash{driver}->{name} = "vfio"; + $tmphash{source}->{address}->[0] = \%{ $devhash->{'capability'}->{'iommuGroup'}->{'address'} }; + push(@prdevarray, \%tmphash); + + } } - - $xtree{devices}->{hostdev}=\@prdevarray; + + $xtree{devices}->{hostdev} = \@prdevarray; if ($hypcpumodel eq "ppc64" or $hypcpumodel eq "ppc64le") { @@ -779,101 +809,105 @@ sub build_xmldesc { $cpuhash{model} = $hypcputype; } if ($args{cpus}) { - $xtree{vcpu}->{content}=$args{cpus} * $hypcputhreads; - $cpuhash{topology}->{sockets} = 1; - $cpuhash{topology}->{cores} = $args{cpus}; - $cpuhash{topology}->{threads} = $hypcputhreads; - $updatetable->{vm}->{$node}->{cpus}=$args{cpus}; + $xtree{vcpu}->{content} = $args{cpus} * $hypcputhreads; + $cpuhash{topology}->{sockets} = 1; + $cpuhash{topology}->{cores} = $args{cpus}; + $cpuhash{topology}->{threads} = $hypcputhreads; + $updatetable->{vm}->{$node}->{cpus} = $args{cpus}; } elsif (defined $confdata->{vm}->{$node}->[0]->{cpus}) { - $xtree{vcpu}->{content}=$confdata->{vm}->{$node}->[0]->{cpus} * $hypcputhreads; + $xtree{vcpu}->{content} = $confdata->{vm}->{$node}->[0]->{cpus} * $hypcputhreads; $cpuhash{topology}->{sockets} = 1; $cpuhash{topology}->{cores} = $confdata->{vm}->{$node}->[0]->{cpus}; $cpuhash{topology}->{threads} = $hypcputhreads; } else { - $xtree{vcpu}->{content}=1 * $hypcputhreads; + $xtree{vcpu}->{content} = 1 * $hypcputhreads; $cpuhash{topology}->{sockets} = 1; - $cpuhash{topology}->{cores} = 1; + $cpuhash{topology}->{cores} = 1; $cpuhash{topology}->{threads} = $hypcputhreads; } $xtree{cpu} = \%cpuhash; } else { if ($args{cpus}) { - $xtree{vcpu}->{content}=$args{cpus}; + $xtree{vcpu}->{content} = $args{cpus}; if ($confdata->{vm}->{$node}->[0]->{cpus}) { - $updatetable->{vm}->{$node}->{cpus}=$args{cpus}; + $updatetable->{vm}->{$node}->{cpus} = $args{cpus}; } } elsif (defined $confdata->{vm}->{$node}->[0]->{cpus}) { - $xtree{vcpu}->{content}=$confdata->{vm}->{$node}->[0]->{cpus}; + $xtree{vcpu}->{content} = $confdata->{vm}->{$node}->[0]->{cpus}; } else { - $xtree{vcpu}->{content}=1; + $xtree{vcpu}->{content} = 1; } } - if (defined ($confdata->{vm}->{$node}->[0]->{clockoffset})) { + if (defined($confdata->{vm}->{$node}->[0]->{clockoffset})) { + #If user requested a specific behavior, give it - $xtree{clock}->{offset}=$confdata->{vm}->{$node}->[0]->{clockoffset}; + $xtree{clock}->{offset} = $confdata->{vm}->{$node}->[0]->{clockoffset}; } else { + #Otherwise, only do local time for things that look MS - if (defined ($confdata->{nodetype}->{$node}->[0]->{os}) and $confdata->{nodetype}->{$node}->[0]->{os} =~ /win.*/) { - $xtree{clock}->{offset}='localtime'; - } else { #For everyone else, utc is preferred generally - $xtree{clock}->{offset}='utc'; + if (defined($confdata->{nodetype}->{$node}->[0]->{os}) and $confdata->{nodetype}->{$node}->[0]->{os} =~ /win.*/) { + $xtree{clock}->{offset} = 'localtime'; + } else { #For everyone else, utc is preferred generally + $xtree{clock}->{offset} = 'utc'; } } - $xtree{features}->{pae}={}; - $xtree{features}->{acpi}={}; - $xtree{features}->{apic}={}; - $xtree{features}->{content}="\n"; - $xtree{devices}->{disk}=build_diskstruct($cdloc); - $xtree{devices}->{interface}=build_nicstruct($node); + $xtree{features}->{pae} = {}; + $xtree{features}->{acpi} = {}; + $xtree{features}->{apic} = {}; + $xtree{features}->{content} = "\n"; + $xtree{devices}->{disk} = build_diskstruct($cdloc); + $xtree{devices}->{interface} = build_nicstruct($node); + #use content to force xml simple to not make model the 'name' of video - if (defined ($confdata->{vm}->{$node}->[0]->{vidmodel})) { - my $model = $confdata->{vm}->{$node}->[0]->{vidmodel}; - my $vram = '8192'; - if ($model eq 'qxl') { - $xtree{devices}->{channel}->{type}='spicevmc'; - $xtree{devices}->{channel}->{target}->{type}='virtio'; - $xtree{devices}->{channel}->{target}->{name}='com.redhat.spice.0'; - $vram = 65536; } #surprise, spice blows up with less vram than this after version 0.6 and up - $xtree{devices}->{video}= [ { 'content'=>'','model'=> {type=>$model,vram=>$vram}}]; + if (defined($confdata->{vm}->{$node}->[0]->{vidmodel})) { + my $model = $confdata->{vm}->{$node}->[0]->{vidmodel}; + my $vram = '8192'; + if ($model eq 'qxl') { + $xtree{devices}->{channel}->{type} = 'spicevmc'; + $xtree{devices}->{channel}->{target}->{type} = 'virtio'; + $xtree{devices}->{channel}->{target}->{name} = 'com.redhat.spice.0'; + $vram = 65536; } #surprise, spice blows up with less vram than this after version 0.6 and up + $xtree{devices}->{video} = [ { 'content' => '', 'model' => { type => $model, vram => $vram } } ]; } else { - $xtree{devices}->{video}= [ { 'content'=>'','model'=> {type=>'vga',vram=>8192}}]; + $xtree{devices}->{video} = [ { 'content' => '', 'model' => { type => 'vga', vram => 8192 } } ]; } - $xtree{devices}->{input}->{type}='tablet'; - $xtree{devices}->{input}->{bus}='usb'; - if (defined ($confdata->{vm}->{$node}->[0]->{vidproto})) { - $xtree{devices}->{graphics}->{type}=$confdata->{vm}->{$node}->[0]->{vidproto}; + $xtree{devices}->{input}->{type} = 'tablet'; + $xtree{devices}->{input}->{bus} = 'usb'; + if (defined($confdata->{vm}->{$node}->[0]->{vidproto})) { + $xtree{devices}->{graphics}->{type} = $confdata->{vm}->{$node}->[0]->{vidproto}; } else { - $xtree{devices}->{graphics}->{type}='vnc'; + $xtree{devices}->{graphics}->{type} = 'vnc'; } - $xtree{devices}->{graphics}->{autoport}='yes'; - $xtree{devices}->{graphics}->{listen}='0.0.0.0'; + $xtree{devices}->{graphics}->{autoport} = 'yes'; + $xtree{devices}->{graphics}->{listen} = '0.0.0.0'; if ($confdata->{vm}->{$node}->[0]->{vidpassword}) { - $xtree{devices}->{graphics}->{password}=$confdata->{vm}->{$node}->[0]->{vidpassword}; + $xtree{devices}->{graphics}->{password} = $confdata->{vm}->{$node}->[0]->{vidpassword}; } else { - $xtree{devices}->{graphics}->{password}=genpassword(20); + $xtree{devices}->{graphics}->{password} = genpassword(20); } if (defined($hypcpumodel) and $hypcpumodel eq 'ppc64') { $xtree{devices}->{emulator}->{content} = "/usr/bin/qemu-system-ppc64"; - } + } - $xtree{devices}->{console}->{type}='pty'; - $xtree{devices}->{console}->{target}->{port}='1'; - return XMLout(\%xtree,RootName=>"domain"); + $xtree{devices}->{console}->{type} = 'pty'; + $xtree{devices}->{console}->{target}->{port} = '1'; + return XMLout(\%xtree, RootName => "domain"); } sub refresh_vm { my $dom = shift; - my $newxml=$dom->get_xml_description(); - $updatetable->{kvm_nodedata}->{$node}->{xml}=$newxml; + my $newxml = $dom->get_xml_description(); + $updatetable->{kvm_nodedata}->{$node}->{xml} = $newxml; $newxml = XMLin($newxml); - my $vidport=$newxml->{devices}->{graphics}->{port}; - my $vidproto=$newxml->{devices}->{graphics}->{type}; - my $stty=$newxml->{devices}->{console}->{tty}; + my $vidport = $newxml->{devices}->{graphics}->{port}; + my $vidproto = $newxml->{devices}->{graphics}->{type}; + my $stty = $newxml->{devices}->{console}->{tty}; + #$updatetable->{vm}->{$node}={vncport=>$vncport,textconsole=>$stty}; #$vmtab->setNodeAttribs($node,{vncport=>$vncport,textconsole=>$stty}); - return {vidport=>$vidport,textconsole=>$stty,vidproto=>$vidproto}; + return { vidport => $vidport, textconsole => $stty, vidproto => $vidproto }; } sub getcons { @@ -881,117 +915,122 @@ sub getcons { my $type = shift(); my $dom; eval { - $dom = $hypconn->get_domain_by_name($node); + $dom = $hypconn->get_domain_by_name($node); }; unless ($dom) { - return 1,"Unable to query running VM"; + return 1, "Unable to query running VM"; } - my $consdata=refresh_vm($dom); - my $hyper=$confdata->{vm}->{$node}->[0]->{host}; + my $consdata = refresh_vm($dom); + my $hyper = $confdata->{vm}->{$node}->[0]->{host}; if ($type eq "text") { my $serialspeed; if ($confdata->{nodehm}) { - $serialspeed=$confdata->{nodehm}->{$node}->[0]->{serialspeed}; + $serialspeed = $confdata->{nodehm}->{$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); + 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 "vid") { - $consdata->{server}=$hyper; - my $domxml = $dom->get_xml_description(); - my $parseddom = $parser->parse_string($domxml); - my ($graphicsnode) = $parseddom->findnodes("//graphics"); - my $tpasswd; - if ($confdata->{vm}->{$node}->[0]->{vidpassword}) { - $tpasswd=$confdata->{vm}->{$node}->[0]->{vidpassword}; - $graphicsnode->removeAttribute("passwdValidTo"); - } else { - $tpasswd=genpassword(16); - my $validto=POSIX::strftime("%Y-%m-%dT%H:%M:%S",gmtime(time()+60)); - $graphicsnode->setAttribute("passwdValidTo",$validto); - } - $graphicsnode->setAttribute("passwd",$tpasswd); - $dom->update_device($graphicsnode->toString()); - #$dom->update_device(""); - $consdata->{password}=$tpasswd; - return $consdata; + $consdata->{server} = $hyper; + my $domxml = $dom->get_xml_description(); + my $parseddom = $parser->parse_string($domxml); + my ($graphicsnode) = $parseddom->findnodes("//graphics"); + my $tpasswd; + if ($confdata->{vm}->{$node}->[0]->{vidpassword}) { + $tpasswd = $confdata->{vm}->{$node}->[0]->{vidpassword}; + $graphicsnode->removeAttribute("passwdValidTo"); + } else { + $tpasswd = genpassword(16); + my $validto = POSIX::strftime("%Y-%m-%dT%H:%M:%S", gmtime(time() + 60)); + $graphicsnode->setAttribute("passwdValidTo", $validto); + } + $graphicsnode->setAttribute("passwd", $tpasswd); + $dom->update_device($graphicsnode->toString()); + + #$dom->update_device(""); + $consdata->{password} = $tpasswd; + return $consdata; + #return (0,{$consdata->{vidproto}.'@'.$hyper.":".$consdata->{vidport}); #$consdata->{vncport}); } } + sub getrvidparms { - my $node=shift; - my $location = getcons($node,"vid"); + my $node = shift; + my $location = getcons($node, "vid"); unless (ref $location) { - return (1,"Error: Unable to determine rvid destination for $node (appears VM is off)"); + return (1, "Error: Unable to determine rvid destination for $node (appears VM is off)"); } - my @output = ( - "method: kvm" - ); + my @output = ( + "method: kvm" + ); foreach (keys %$location) { - push @output,$_.":".$location->{$_}; + push @output, $_ . ":" . $location->{$_}; } - return 0,@output; + return 0, @output; } my %cached_noderanges; + sub pick_target { - my $node = shift; + my $node = shift; my $addmemory = shift; my $target; - my $mostfreememory=undef; + my $mostfreememory = undef; my $currentfreememory; - my $candidates= $confdata->{vm}->{$node}->[0]->{migrationdest}; - my $currhyp=$confdata->{vm}->{$node}->[0]->{host}; -#caching strategy is implicit on whether $addmemory is passed. + my $candidates = $confdata->{vm}->{$node}->[0]->{migrationdest}; + my $currhyp = $confdata->{vm}->{$node}->[0]->{host}; + + #caching strategy is implicit on whether $addmemory is passed. unless ($candidates) { return undef; } my @fosterhyps; #noderange is relatively expensive, and generally we only will have a few distinct noderange descriptions to contend with in a mass adoption, so cache eache one for reuse across pick_target() calls if (defined $cached_noderanges{$candidates}) { - @fosterhyps = @{$cached_noderanges{$candidates}}; + @fosterhyps = @{ $cached_noderanges{$candidates} }; } else { - @fosterhyps = noderange($candidates); + @fosterhyps = noderange($candidates); $cached_noderanges{$candidates} = \@fosterhyps; } foreach (@fosterhyps) { my $targconn; - my $cand=$_; - if ($_ eq $currhyp) { next; } #skip current node - if ($offlinehyps{$_}) { next }; #skip already offlined nodes + my $cand = $_; + if ($_ eq $currhyp) { next; } #skip current node + if ($offlinehyps{$_}) { next }; #skip already offlined nodes if (grep { "$_" eq $cand } @destblacklist) { next; } #skip blacklisted destinations if ($addmemory and defined $hypstats{$_}->{freememory}) { #only used cache results when addmemory suggests caching can make sense - $currentfreememory=$hypstats{$_}->{freememory} + $currentfreememory = $hypstats{$_}->{freememory} } else { - if (not nodesockopen($_,22)) { $offlinehyps{$_}=1; next; } #skip unusable destinations - eval { #Sys::Virt has bugs that cause it to die out in weird ways some times, contain it here - $targconn = Sys::Virt->new(uri=>"qemu+ssh://root@".$_."/system?no_tty=1&netcat=nc"); + if (not nodesockopen($_, 22)) { $offlinehyps{$_} = 1; next; } #skip unusable destinations + eval { #Sys::Virt has bugs that cause it to die out in weird ways some times, contain it here + $targconn = Sys::Virt->new(uri => "qemu+ssh://root@" . $_ . "/system?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 => "qemu+ssh://root@" . $_ . "/system?no_tty=1"); }; - 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=>"qemu+ssh://root@".$_."/system?no_tty=1"); - }; - } - unless ($targconn) { next; } #skip unreachable destinations - $currentfreememory=$targconn->get_node_info()->{memory}; + } + unless ($targconn) { next; } #skip unreachable destinations + $currentfreememory = $targconn->get_node_info()->{memory}; 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 - + $currentfreememory -= $_->get_info()->{memory}; } - $hypstats{$cand}->{freememory}=$currentfreememory; + $hypstats{$cand}->{freememory} = $currentfreememory; } if ($addmemory and $addmemory->{$_}) { $currentfreememory -= $addmemory->{$_}; } - if (not defined ($mostfreememory)) { - $mostfreememory=$currentfreememory; - $target=$_; + if (not defined($mostfreememory)) { + $mostfreememory = $currentfreememory; + $target = $_; } elsif ($currentfreememory > $mostfreememory) { - $mostfreememory=$currentfreememory; - $target=$_; + $mostfreememory = $currentfreememory; + $target = $_; } } return $target; @@ -1000,157 +1039,165 @@ sub pick_target { sub migrate { $node = shift(); - my @args=@_; + my @args = @_; my $targ; foreach (@args) { - if (/^-/) { next; } - $targ = $_; + if (/^-/) { next; } + $targ = $_; } if ($offlinevms{$node}) { return power("on"); } -#TODO: currently, we completely serialize migration events. Some IO fabrics can facilitate concurrent migrations -#One trivial example is an ethernet port aggregation where a single conversation may likely be unable to utilize all the links -#because traffic is balanced by a mac address hashing algorithim, but talking to several hypervisors would have -#distinct peers that can be balanced more effectively. -#The downside is that migration is sufficiently slow that a lot can change in the intervening time on a target hypervisor, but -#this should not be an issue if: -#xCAT is the only path a configuration is using to make changes in the virtualization stack -#xCAT implements a global semaphore mechanism that this plugin can use to assure migration targets do not change by our own hand.. -#failing that.. flock. + + #TODO: currently, we completely serialize migration events. Some IO fabrics can facilitate concurrent migrations + #One trivial example is an ethernet port aggregation where a single conversation may likely be unable to utilize all the links + #because traffic is balanced by a mac address hashing algorithim, but talking to several hypervisors would have + #distinct peers that can be balanced more effectively. + #The downside is that migration is sufficiently slow that a lot can change in the intervening time on a target hypervisor, but + #this should not be an issue if: + #xCAT is the only path a configuration is using to make changes in the virtualization stack + #xCAT implements a global semaphore mechanism that this plugin can use to assure migration targets do not change by our own hand.. + #failing that.. flock. unless ($targ) { $targ = pick_target($node); } unless ($targ) { - return (1,"Unable to identify a suitable target host for guest $node"); + return (1, "Unable to identify a suitable target host for guest $node"); } if ($use_xhrm) { - xhrm_satisfy($node,$targ); + xhrm_satisfy($node, $targ); } my $prevhyp; - my $target = "qemu+ssh://root@".$targ."/system?no_tty=1"; - my $currhyp="qemu+ssh://root@"; + my $target = "qemu+ssh://root@" . $targ . "/system?no_tty=1"; + my $currhyp = "qemu+ssh://root@"; if ($confdata->{vm}->{$node}->[0]->{host}) { - $prevhyp=$confdata->{vm}->{$node}->[0]->{host}; - $currhyp.=$prevhyp; + $prevhyp = $confdata->{vm}->{$node}->[0]->{host}; + $currhyp .= $prevhyp; } else { - return (1,"Unable to find current location of $node"); + return (1, "Unable to find current location of $node"); } - $currhyp.="/system?no_tty=1"; + $currhyp .= "/system?no_tty=1"; if ($currhyp eq $target) { - return (0,"Guest is already on host $targ"); + return (0, "Guest is already on host $targ"); } my $srchypconn; my $desthypconn; - unless ($offlinehyps{$prevhyp} or nodesockopen($prevhyp,22)) { - $offlinehyps{$prevhyp}=1; + unless ($offlinehyps{$prevhyp} or nodesockopen($prevhyp, 22)) { + $offlinehyps{$prevhyp} = 1; } - my $srcnetcatadd="&netcat=nc"; + my $srcnetcatadd = "&netcat=nc"; unless ($offlinehyps{$prevhyp}) { - eval {#Contain Sys::Virt bugs - $srchypconn= Sys::Virt->new(uri=>"qemu+ssh://root@".$prevhyp."/system?no_tty=1$srcnetcatadd"); + eval { #Contain Sys::Virt bugs + $srchypconn = Sys::Virt->new(uri => "qemu+ssh://root@" . $prevhyp . "/system?no_tty=1$srcnetcatadd"); }; unless ($srchypconn) { - $srcnetcatadd=""; - eval {#Contain Sys::Virt bugs - $srchypconn= Sys::Virt->new(uri=>"qemu+ssh://root@".$prevhyp."/system?no_tty=1"); + $srcnetcatadd = ""; + eval { #Contain Sys::Virt bugs + $srchypconn = Sys::Virt->new(uri => "qemu+ssh://root@" . $prevhyp . "/system?no_tty=1"); }; } } unless ($srchypconn) { if (grep { $_ eq '-f' } @args) { - unless ($vmtab) { $vmtab = new xCAT::Table('vm',-create=>1); } - $vmtab->setNodeAttribs($node,{host=>$targ}); - return (0,"migrated to $targ"); + unless ($vmtab) { $vmtab = new xCAT::Table('vm', -create => 1); } + $vmtab->setNodeAttribs($node, { host => $targ }); + return (0, "migrated to $targ"); } else { - 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 (or use -f on rmigrate)"); - } + 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 (or use -f on rmigrate)"); + } } - unless ($offlinehyps{$targ} or nodesockopen($targ,22)) { - $offlinehyps{$targ}=1; + unless ($offlinehyps{$targ} or nodesockopen($targ, 22)) { + $offlinehyps{$targ} = 1; } - my $destnetcatadd="&netcat=nc"; + my $destnetcatadd = "&netcat=nc"; unless ($offlinehyps{$targ}) { - eval {#Contain Sys::Virt bugs - $desthypconn= Sys::Virt->new(uri=>$target.$destnetcatadd); + eval { #Contain Sys::Virt bugs + $desthypconn = Sys::Virt->new(uri => $target . $destnetcatadd); }; unless ($desthypconn) { - $destnetcatadd=""; - eval {#Contain Sys::Virt bugs - $desthypconn= Sys::Virt->new(uri=>$target); + $destnetcatadd = ""; + eval { #Contain Sys::Virt bugs + $desthypconn = Sys::Virt->new(uri => $target); }; } } unless ($desthypconn) { - return (1,"Unable to reach $targ to perform operation of $node, destination unusable."); + return (1, "Unable to reach $targ to perform operation of $node, destination unusable."); } if (defined $confdata->{vm}->{$node}->[0]->{storage} and $confdata->{vm}->{$node}->[0]->{storage} =~ /^nfs:/) { + #first, assure master is in place if ($confdata->{vm}->{$node}->[0]->{master}) { - my $vmmastertab=xCAT::Table->new('vmmaster',-create=>0); + my $vmmastertab = xCAT::Table->new('vmmaster', -create => 0); my $masterent; if ($vmmastertab) { - $masterent=$vmmastertab->getAttribs({name=>$confdata->{vm}->{$node}->[0]->{master}},['storage']); + $masterent = $vmmastertab->getAttribs({ name => $confdata->{vm}->{$node}->[0]->{master} }, ['storage']); } if ($masterent and $masterent->{storage}) { - foreach (split /,/,$masterent->{storage}) { - s/=.*//; - get_storage_pool_by_url($_,$desthypconn,$targ); - } + foreach (split /,/, $masterent->{storage}) { + s/=.*//; + get_storage_pool_by_url($_, $desthypconn, $targ); + } } } - my $urls = $confdata->{vm}->{$node}->[0]->{storage} and $confdata->{vm}->{$node}->[0]->{storage}; - foreach (split /,/,$urls) { + my $urls = $confdata->{vm}->{$node}->[0]->{storage} and $confdata->{vm}->{$node}->[0]->{storage}; + foreach (split /,/, $urls) { s/=.*//; - get_storage_pool_by_url($_,$desthypconn,$targ); + get_storage_pool_by_url($_, $desthypconn, $targ); } } - my $sock = IO::Socket::INET->new(Proto=>'udp'); - my $ipa=inet_aton($node); + my $sock = IO::Socket::INET->new(Proto => 'udp'); + my $ipa = inet_aton($node); my $pa; if ($ipa) { - $pa=sockaddr_in(7,$ipa); #UDP echo service, not needed to be actually + $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 $nomadomain; - eval { + eval { $nomadomain = $srchypconn->get_domain_by_name($node); }; unless ($nomadomain) { - unless ($vmtab) { $vmtab = new xCAT::Table('vm',-create=>1); } - $vmtab->setNodeAttribs($node,{host=>$targ}); - return (0,"migrated to $targ"); + unless ($vmtab) { $vmtab = new xCAT::Table('vm', -create => 1); } + $vmtab->setNodeAttribs($node, { host => $targ }); + return (0, "migrated to $targ"); + #return (1,"Unable to find $node on $prevhyp, vm.host may be incorrect or a split-brain condition, such as libvirt forgetting a guest due to restart or bug."); - + } my $newdom; my $errstr; eval { - $newdom=$nomadomain->migrate($desthypconn,&Sys::Virt::Domain::MIGRATE_LIVE,undef,undef,0); + $newdom = $nomadomain->migrate($desthypconn, &Sys::Virt::Domain::MIGRATE_LIVE, undef, undef, 0); }; if ($@) { $errstr = $@; } -#TODO: If it looks like it failed to migrate, ensure the guest exists only in one place - if ($errstr) { - return (1,"Failed migration of $node from $prevhyp to $targ: $errstr"); + + #TODO: If it looks like it failed to migrate, ensure the guest exists only in one place + if ($errstr) { + return (1, "Failed migration of $node from $prevhyp to $targ: $errstr"); } unless ($newdom) { - return (1,"Failed migration from $prevhyp to $targ"); + return (1, "Failed migration from $prevhyp to $targ"); } if ($ipa) { - 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... + 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... } + #BTW, this should all be moot since the underlying kvm seems good about gratuitous traffic, but it shouldn't hurt anything refresh_vm($newdom); + #The migration seems tohave suceeded, but to be sure... close($sock); if ($desthypconn->get_domain_by_name($node)) { + #$updatetable->{vm}->{$node}->{host} = $targ; - unless ($vmtab) { $vmtab = new xCAT::Table('vm',-create=>1); } - $vmtab->setNodeAttribs($node,{host=>$targ}); - return (0,"migrated to $targ"); - } else { #This *should* not be possible - return (1,"Failed migration from $prevhyp to $targ, despite normal looking run..."); + unless ($vmtab) { $vmtab = new xCAT::Table('vm', -create => 1); } + $vmtab->setNodeAttribs($node, { host => $targ }); + return (0, "migrated to $targ"); + } else { #This *should* not be possible + return (1, "Failed migration from $prevhyp to $targ, despite normal looking run..."); } } @@ -1161,7 +1208,7 @@ sub getpowstate { if ($dom) { $vmstat = $dom->get_info; } - if ($vmstat and $runningstates{$vmstat->{state}}) { + if ($vmstat and $runningstates{ $vmstat->{state} }) { return "on"; } else { return "off"; @@ -1169,34 +1216,34 @@ sub getpowstate { } sub xhrm_satisfy { - my $node = shift; - my $hyp = shift; - my $rc=0; - my @nics=(); - my @storage=(); + my $node = shift; + my $hyp = shift; + my $rc = 0; + my @nics = (); + my @storage = (); if ($confdata->{vm}->{$node}->[0]->{nics}) { - @nics = split /,/,$confdata->{vm}->{$node}->[0]->{nics}; + @nics = split /,/, $confdata->{vm}->{$node}->[0]->{nics}; } - - $rc |=system("scp $::XCATROOT/share/xcat/scripts/xHRM $hyp:/usr/bin"); + + $rc |= system("scp $::XCATROOT/share/xcat/scripts/xHRM $hyp:/usr/bin"); foreach (@nics) { - s/=.*//; #this code cares not about the model of virtual nic - my $nic=$_; + s/=.*//; #this code cares not about the model of virtual nic + my $nic = $_; my $vlanip; my $netmask; my $subnet; my $vlan; my $interface; if ($nic =~ /^vl([\d]+)$/) { - $vlan=$1; - my $nwtab=xCAT::Table->new("networks", -create =>0); + $vlan = $1; + my $nwtab = xCAT::Table->new("networks", -create => 0); if ($nwtab) { - my $sent = $nwtab->getAttribs({vlanid=>"$vlan"},'net','mask'); + my $sent = $nwtab->getAttribs({ vlanid => "$vlan" }, 'net', 'mask'); if ($sent and ($sent->{net})) { - $subnet=$sent->{net}; - $netmask=$sent->{mask}; - } + $subnet = $sent->{net}; + $netmask = $sent->{mask}; + } if (($subnet) && ($netmask)) { my $hoststab = xCAT::Table->new("hosts", -create => 0); if ($hoststab) { @@ -1204,22 +1251,23 @@ sub xhrm_satisfy { if (defined($tmp) && ($tmp) && $tmp->{otherinterfaces}) { my $otherinterfaces = $tmp->{otherinterfaces}; - my @itf_pairs=split(/,/, $otherinterfaces); + my @itf_pairs = split(/,/, $otherinterfaces); foreach (@itf_pairs) { - my ($name,$vip)=split(/:/, $_); - if(xCAT::NetworkUtils->ishostinsubnet($vip, $netmask, $subnet)) { - $vlanip=$vip; + my ($name, $vip) = split(/:/, $_); + if (xCAT::NetworkUtils->ishostinsubnet($vip, $netmask, $subnet)) { + $vlanip = $vip; last; } } } } + #get the vlan ip from nics table unless ($vlanip) { my $nicstable = xCAT::Table->new("nics", -create => 0); if ($nicstable) { my $tmp = $nicstable->getNodeAttribs($hyp, ['nicips']); - if ($tmp && $tmp->{nicips}){ + if ($tmp && $tmp->{nicips}) { $tmp =~ /vl${vlan}nic!([^,]*)/; $vlanip = $1; } @@ -1229,36 +1277,37 @@ sub xhrm_satisfy { } } - #get the nic that vlan tagged + #get the nic that vlan tagged my $swtab = xCAT::Table->new("switch", -create => 0); if ($swtab) { - my $tmp_switch = $swtab->getNodesAttribs([$hyp], ['vlan','interface']); - if (defined($tmp_switch) && (exists($tmp_switch->{$hyp}))) { - my $tmp_node_array=$tmp_switch->{$hyp}; + my $tmp_switch = $swtab->getNodesAttribs([$hyp], [ 'vlan', 'interface' ]); + if (defined($tmp_switch) && (exists($tmp_switch->{$hyp}))) { + my $tmp_node_array = $tmp_switch->{$hyp}; foreach my $tmp (@$tmp_node_array) { if (exists($tmp->{vlan})) { my $vlans = $tmp->{vlan}; - foreach my $vlan_tmp (split(',',$vlans)) { + foreach my $vlan_tmp (split(',', $vlans)) { if ($vlan_tmp == $vlan) { if (exists($tmp->{interface})) { - $interface=$tmp->{interface}; + $interface = $tmp->{interface}; } - last; + last; } } } } } } - + if (($interface) || ($interface =~ /primary/)) { $interface =~ s/primary(:)?//g; } + #print "interface=$interface nic=$nic vlanip=$vlanip netmask=$netmask\n"; if ($interface) { - $rc |=system("ssh $hyp xHRM bridgeprereq $interface:$nic $vlanip $netmask"); + $rc |= system("ssh $hyp xHRM bridgeprereq $interface:$nic $vlanip $netmask"); } else { - $rc |=system("ssh $hyp xHRM bridgeprereq $nic $vlanip $netmask"); + $rc |= system("ssh $hyp xHRM bridgeprereq $nic $vlanip $netmask"); } #TODO: surprise! there is relatively undocumented libvirt capability for this... @@ -1271,87 +1320,91 @@ sub xhrm_satisfy { } return $rc; } + sub makedom { - my $node=shift; + my $node = shift; my $cdloc = shift; - my $xml = shift; + my $xml = shift; my $dom; if (not $xml and $confdata->{kvmnodedata}->{$node} and $confdata->{kvmnodedata}->{$node}->[0] and $confdata->{kvmnodedata}->{$node}->[0]->{xml}) { + #we do this to trigger storage prereq fixup if (defined $confdata->{vm}->{$node}->[0]->{storage} and ($confdata->{vm}->{$node}->[0]->{storage} =~ /^nfs:/ or $confdata->{vm}->{$node}->[0]->{storage} =~ /^dir:/ or $confdata->{vm}->{$node}->[0]->{storage} =~ /^lvm:/)) { if ($confdata->{vm}->{$node}->[0]->{master}) { - my $vmmastertab=xCAT::Table->new('vmmaster',-create=>0); + my $vmmastertab = xCAT::Table->new('vmmaster', -create => 0); my $masterent; if ($vmmastertab) { - $masterent=$vmmastertab->getAttribs({name=>$confdata->{vm}->{$node}->[0]->{master}},['storage']); + $masterent = $vmmastertab->getAttribs({ name => $confdata->{vm}->{$node}->[0]->{master} }, ['storage']); } if ($masterent and $masterent->{storage}) { - foreach (split /,/,$masterent->{storage}) { - s/=.*//; - get_storage_pool_by_url($_); - } + foreach (split /,/, $masterent->{storage}) { + s/=.*//; + get_storage_pool_by_url($_); + } } } - my $urls = $confdata->{vm}->{$node}->[0]->{storage} and $confdata->{vm}->{$node}->[0]->{storage}; - foreach (split /,/,$urls) { + my $urls = $confdata->{vm}->{$node}->[0]->{storage} and $confdata->{vm}->{$node}->[0]->{storage}; + foreach (split /,/, $urls) { s/=.*//; get_storage_pool_by_url($_); } } $xml = $confdata->{kvmnodedata}->{$node}->[0]->{xml}; - my $newxml = reconfigvm($node,$xml); + my $newxml = reconfigvm($node, $xml); if ($newxml) { - $xml=$newxml; + $xml = $newxml; } } elsif (not $xml) { - $xml = build_xmldesc($node,cd=>$cdloc); + $xml = build_xmldesc($node, cd => $cdloc); } my $parseddom = $parser->parse_string($xml); my ($graphics) = $parseddom->findnodes("//graphics"); if ($confdata->{vm}->{$node}->[0]->{vidpassword}) { - $graphics->setAttribute("passwd",$confdata->{vm}->{$node}->[0]->{vidpassword}); + $graphics->setAttribute("passwd", $confdata->{vm}->{$node}->[0]->{vidpassword}); } else { - $graphics->setAttribute("passwd",genpassword(20)); + $graphics->setAttribute("passwd", genpassword(20)); } - $graphics->setAttribute("listen",'0.0.0.0'); + $graphics->setAttribute("listen", '0.0.0.0'); $xml = $parseddom->toString(); my $errstr; - eval { - if ($::XCATSITEVALS{persistkvmguests}) { - $dom=$hypconn->define_domain($xml); - $dom->create() - } else { - $dom=$hypconn->create_domain($xml); - } + eval { + if ($::XCATSITEVALS{persistkvmguests}) { + $dom = $hypconn->define_domain($xml); + $dom->create() + } else { + $dom = $hypconn->create_domain($xml); + } }; if ($@) { $errstr = $@; } if (ref $errstr) { - $errstr = ":".$errstr->{message}; + $errstr = ":" . $errstr->{message}; } - if ($errstr) { return (undef,$errstr); } + if ($errstr) { return (undef, $errstr); } if ($dom) { - refresh_vm($dom); + refresh_vm($dom); } - return $dom,undef; + return $dom, undef; } sub createstorage { -#svn rev 6638 held the older vintage of createstorage + + #svn rev 6638 held the older vintage of createstorage #print "createstorage called\n"; - my $filename=shift; - my $mastername=shift; - my $size=shift; - my $cfginfo = shift; - my $force = shift; + my $filename = shift; + my $mastername = shift; + my $size = shift; + my $cfginfo = shift; + my $force = shift; + #my $diskstruct = shift; my $node = $cfginfo->{node}; - my @flags = split /,/,$cfginfo->{virtflags}; + my @flags = split /,/, $cfginfo->{virtflags}; my $format; foreach (@flags) { if (/^imageformat=(.*)\z/) { - $format=$1; + $format = $1; } elsif (/^clonemethod=(.*)\z/) { - $clonemethod=$1; + $clonemethod = $1; } } if ($cfginfo->{storageformat}) { @@ -1365,127 +1418,130 @@ sub createstorage { my $basename; my $dirname; if ($mastername and $size) { - return 1,"Can not specify both a master to clone and size(s)"; + return 1, "Can not specify both a master to clone and size(s)"; } - $filename=~s/=(.*)//; - my $model=$1; + $filename =~ s/=(.*)//; + my $model = $1; unless ($model) { + #if not defined, model will stay undefined like above $model = $cfginfo->{storagemodel}; } - my $prefix='hd'; + my $prefix = 'hd'; if ($model eq 'scsi') { - $prefix='sd'; + $prefix = 'sd'; } elsif ($model eq 'virtio') { - $prefix='vd'; + $prefix = 'vd'; } - my @suffixes=('a','b','d'..'zzz'); + my @suffixes = ('a', 'b', 'd' .. 'zzz'); if ($filename =~ /^nfs:/ or $filename =~ /^dir:/ or $filename =~ /^lvm:/) { #libvirt storage pool to be used for this - my @sizes = split /,/,$size; + my @sizes = split /,/, $size; foreach (@sizes) { - get_filepath_by_url(url=>$filename,dev=>$prefix.shift(@suffixes),create=>$_, force=>$force, format=>$format); + get_filepath_by_url(url => $filename, dev => $prefix . shift(@suffixes), create => $_, force => $force, format => $format); } - }else{ + } else { oldCreateStorage($filename, $mastername, $size, $cfginfo, $force); - } - my $masterserver; - if ($mastername) { #cloning } - if ($size) {#new volume + my $masterserver; + if ($mastername) { #cloning + } + if ($size) { #new volume } } sub oldCreateStorage { - my $filename=shift; - my $mastername=shift; - my $size=shift; - my $cfginfo = shift; - my $force = shift; - my $node = $cfginfo->{node}; - my @flags = split /,/,$cfginfo->{virtflags}; + my $filename = shift; + my $mastername = shift; + my $size = shift; + my $cfginfo = shift; + my $force = shift; + my $node = $cfginfo->{node}; + my @flags = split /,/, $cfginfo->{virtflags}; foreach (@flags) { + if (/^imageformat=(.*)\z/) { - $imgfmt=$1; + $imgfmt = $1; } elsif (/^clonemethod=(.*)\z/) { - $clonemethod=$1; + $clonemethod = $1; } } my $mountpath; my $pathappend; my $storageserver; + #for nfs paths and qemu-img, we do the magic locally only for now my $basename; my $dirname; - ($basename,$dirname) = fileparse($filename); + ($basename, $dirname) = fileparse($filename); unless ($storageserver) { if (-f $filename) { unless ($force) { - return 1,"Storage already exists, delete manually or use --force"; + return 1, "Storage already exists, delete manually or use --force"; } unlink $filename; } } if ($storageserver and $mastername and $clonemethod eq 'reflink') { - my $rc=system("ssh $storageserver mkdir -p $dirname"); + my $rc = system("ssh $storageserver mkdir -p $dirname"); if ($rc) { - return 1,"Unable to manage storage on remote server $storageserver"; + return 1, "Unable to manage storage on remote server $storageserver"; } } elsif ($storageserver) { my @mounts = `mount`; my $foundmount; foreach (@mounts) { - if (/^$storageserver:$mountpath/) { - chomp; - s/^.* on (\S*) type nfs.*$/$1/; - $dirname = $_; - mkpath($dirname.$pathappend); - $foundmount=1; - last; - } + if (/^$storageserver:$mountpath/) { + chomp; + s/^.* on (\S*) type nfs.*$/$1/; + $dirname = $_; + mkpath($dirname . $pathappend); + $foundmount = 1; + last; + } } unless ($foundmount) { - return 1,"qemu-img cloning requires that the management server have the directory $mountpath from $storageserver mounted"; + return 1, "qemu-img cloning requires that the management server have the directory $mountpath from $storageserver mounted"; } } else { mkpath($dirname); } if ($mastername and $size) { - return 1,"Can not specify both a master to clone and a size"; + return 1, "Can not specify both a master to clone and a size"; } my $masterserver; if ($mastername) { unless ($mastername =~ /^\// or $mastername =~ /^nfs:/) { - $mastername = $xCAT_plugin::kvm::masterdir.'/'.$mastername; + $mastername = $xCAT_plugin::kvm::masterdir . '/' . $mastername; } if ($mastername =~ m!nfs://([^/]*)(/.*\z)!) { - $mastername = $2; + $mastername = $2; $masterserver = $1; } if ($masterserver ne $storageserver) { - return 1,"Not supporting cloning between $masterserver and $storageserver at this time, for now ensure master images and target VM images are on the same server"; + return 1, "Not supporting cloning between $masterserver and $storageserver at this time, for now ensure master images and target VM images are on the same server"; } my $rc; if ($clonemethod eq 'qemu-img') { my $dirn; my $filn; - ($filn,$dirn) = fileparse($filename); + ($filn, $dirn) = fileparse($filename); chdir($dirn); - $rc=system("qemu-img create -f qcow2 -b $mastername $filename"); + $rc = system("qemu-img create -f qcow2 -b $mastername $filename"); } elsif ($clonemethod eq 'reflink') { if ($storageserver) { - $rc=system("ssh $storageserver cp --reflink $mastername $filename"); + $rc = system("ssh $storageserver cp --reflink $mastername $filename"); } else { - $rc=system("cp --reflink $mastername $filename"); + $rc = system("cp --reflink $mastername $filename"); } } if ($rc) { - return $rc,"Failure creating image $filename from $mastername"; + return $rc, "Failure creating image $filename from $mastername"; } } if ($size) { - my $rc = system("qemu-img create -f $imgfmt $filename ".getUnits($size,"g",1024)); + my $rc = system("qemu-img create -f $imgfmt $filename " . getUnits($size, "g", 1024)); if ($rc) { - return $rc,"Failure creating image $filename of size $size\n"; + return $rc, "Failure creating image $filename of size $size\n"; } } } @@ -1494,56 +1550,60 @@ sub rinv { shift; my $dom; eval { - $dom = $hypconn->get_domain_by_name($node); + $dom = $hypconn->get_domain_by_name($node); }; - my $currstate=getpowstate($dom); + my $currstate = getpowstate($dom); my $currxml; if ($currstate eq 'on') { - $currxml=$dom->get_xml_description(); + $currxml = $dom->get_xml_description(); } else { - $currxml =$confdata->{kvmnodedata}->{$node}->[0]->{xml}; + $currxml = $confdata->{kvmnodedata}->{$node}->[0]->{xml}; } unless ($currxml) { - xCAT::SvrUtils::sendmsg([1,"VM does not appear to exist"], $callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "VM does not appear to exist" ], $callback, $node); return; } - my $domain=$parser->parse_string($currxml); - my $uuid = $domain->findnodes('/domain/uuid')->[0]->to_literal; + my $domain = $parser->parse_string($currxml); + my $uuid = $domain->findnodes('/domain/uuid')->[0]->to_literal; $uuid =~ s/^(..)(..)(..)(..)-(..)(..)-(..)(..)/$4$3$2$1-$6$5-$8$7/; - xCAT::SvrUtils::sendmsg("UUID/GUID: $uuid", $callback,$node); + xCAT::SvrUtils::sendmsg("UUID/GUID: $uuid", $callback, $node); my $cpus = $domain->findnodes('/domain/vcpu')->[0]->to_literal; - xCAT::SvrUtils::sendmsg("CPUs: $cpus", $callback,$node); - my $memnode = $domain->findnodes('/domain/currentMemory')->[0]; + xCAT::SvrUtils::sendmsg("CPUs: $cpus", $callback, $node); + my $memnode = $domain->findnodes('/domain/currentMemory')->[0]; my $maxmemnode = $domain->findnodes('/domain/memory')->[0]; + unless ($memnode) { $memnode = $maxmemnode; } if ($memnode) { my $mem = $memnode->to_literal; - $mem=$mem/1024; - xCAT::SvrUtils::sendmsg("Memory: $mem MB", $callback,$node); + $mem = $mem / 1024; + xCAT::SvrUtils::sendmsg("Memory: $mem MB", $callback, $node); } if ($maxmemnode) { - my $maxmem = $maxmemnode->to_literal; - $maxmem=$maxmem/1024; - xCAT::SvrUtils::sendmsg("Maximum Memory: $maxmem MB", $callback,$node); - + my $maxmem = $maxmemnode->to_literal; + $maxmem = $maxmem / 1024; + xCAT::SvrUtils::sendmsg("Maximum Memory: $maxmem MB", $callback, $node); + } invstorage($domain, $dom); invnics($domain); } + sub get_storage_pool_by_volume { - my $vol = shift; - my $path=$vol->get_path(); + my $vol = shift; + my $path = $vol->get_path(); return get_storage_pool_by_path($path); } + sub get_storage_pool_by_path { -#attempts to get pool for a volume, returns false on failure + + #attempts to get pool for a volume, returns false on failure my $file = shift; my $pool; return eval { my @currpools = $hypconn->list_storage_pools(); - push @currpools,$hypconn->list_defined_storage_pools(); + push @currpools, $hypconn->list_defined_storage_pools(); foreach $pool (@currpools) { my $parsedpool = $parser->parse_string($pool->get_xml_description()); my $currpath = $parsedpool->findnodes("/pool/target/path/text()")->[0]->data; @@ -1552,83 +1612,86 @@ sub get_storage_pool_by_path { } } return undef; + # $pool = $hypconn->get_storage_pool_by_uuid($pooluuid); }; } sub invstorage { - my $domain = shift; # the dom obj of XML - my $dom = shift; # the real domain obj + my $domain = shift; # the dom obj of XML + my $dom = shift; # the real domain obj my @disks = $domain->findnodes('/domain/devices/disk'); my $disk; foreach $disk (@disks) { my $name = $disk->findnodes('./target')->[0]->getAttribute("dev"); my $xref = ""; - my $addr = $disk->findnodes('./address')->[0]; + my $addr = $disk->findnodes('./address')->[0]; if ($addr) { if ($name =~ /^vd/) { - $xref = " (v".$addr->getAttribute("bus").":".$addr->getAttribute('slot').".".$addr->getAttribute("function").")"; + $xref = " (v" . $addr->getAttribute("bus") . ":" . $addr->getAttribute('slot') . "." . $addr->getAttribute("function") . ")"; $xref =~ s/0x//g; } else { - $xref = " (d".$addr->getAttribute("controller").":".$addr->getAttribute("bus").":".$addr->getAttribute("unit").")"; + $xref = " (d" . $addr->getAttribute("controller") . ":" . $addr->getAttribute("bus") . ":" . $addr->getAttribute("unit") . ")"; } } - my @candidatenodes = $disk->findnodes('./source'); - unless (scalar @candidatenodes) { - next; - } + my @candidatenodes = $disk->findnodes('./source'); + unless (scalar @candidatenodes) { + next; + } my $file = $candidatenodes[0]->getAttribute('file'); - my $dev = $candidatenodes[0]->getAttribute('dev'); + my $dev = $candidatenodes[0]->getAttribute('dev'); my $vollocation; my $size; my %info; - if ($file) { # for the volumn is a file in storage poll - #we'll attempt to map file path to pool name and volume name - #fallback to just reporting filename if not feasible - #libvirt lacks a way to lookup a storage pool by path, so we'll only do so if using the 'default' xCAT scheme with uuid in the path + if ($file) { # for the volumn is a file in storage poll + #we'll attempt to map file path to pool name and volume name + #fallback to just reporting filename if not feasible + #libvirt lacks a way to lookup a storage pool by path, so we'll only do so if using the 'default' xCAT scheme with uuid in the path $file =~ m!/([^/]*)/($node\..*)\z!; - my $volname=$2; - $vollocation=$file; + my $volname = $2; + $vollocation = $file; eval { - my $pool = get_storage_pool_by_path($file); + my $pool = get_storage_pool_by_path($file); my $poolname = $pool->get_name(); $vollocation = "[$poolname] $volname"; }; + #at least I get to skip the whole pool mess here my $vol = $hypconn->get_storage_volume_by_path($file); if ($vol) { - %info = %{$vol->get_info()}; + %info = %{ $vol->get_info() }; } - } elsif ($dev) { # for the volumn is a block device + } elsif ($dev) { # for the volumn is a block device $vollocation = $dev; - %info = %{$dom->get_block_info($dev)}; + %info = %{ $dom->get_block_info($dev) }; } - + if ($info{allocation} and $info{capacity}) { $size = $info{allocation}; - $size = $size/1048576; #convert to MB - $size = sprintf("%.3f",$size); - $size .= "/".($info{capacity}/1048576); + $size = $size / 1048576; #convert to MB + $size = sprintf("%.3f", $size); + $size .= "/" . ($info{capacity} / 1048576); } - + $callback->({ - node=>{ - name=>$node, - data=>{ - desc=>"Disk $name$xref", - contents=>"$size MB @ $vollocation", - } - } + node => { + name => $node, + data => { + desc => "Disk $name$xref", + contents => "$size MB @ $vollocation", + } + } }); } } + sub invnics { - my $domain=shift; - my @nics = $domain->findnodes('/domain/devices/interface'); + my $domain = shift; + my @nics = $domain->findnodes('/domain/devices/interface'); my $nic; foreach $nic (@nics) { - my $mac = $nic->findnodes('./mac')->[0]->getAttribute('address'); + my $mac = $nic->findnodes('./mac')->[0]->getAttribute('address'); my $addr = $nic->findnodes('./address')->[0]; my $loc; if ($addr) { @@ -1638,22 +1701,23 @@ sub invnics { $slot =~ s/^0x//; my $function = $addr->getAttribute('function'); $function =~ s/^0x//; - $loc=" at $bus:$slot.$function"; + $loc = " at $bus:$slot.$function"; } $callback->({ - node=>{ - name=>$node, - data=>{ - desc=>"Network adapter$loc", - contents=>$mac, - } - } + node => { + name => $node, + data => { + desc => "Network adapter$loc", + contents => $mac, + } + } }); } } + sub rmvm { shift; - @ARGV=@_; + @ARGV = @_; my $force; my $purge; GetOptions( @@ -1662,42 +1726,43 @@ sub rmvm { ); my $dom; eval { - $dom = $hypconn->get_domain_by_name($node); + $dom = $hypconn->get_domain_by_name($node); }; - my $currstate=getpowstate($dom); + my $currstate = getpowstate($dom); my $currxml; if ($currstate eq 'on') { if ($force) { - $currxml=$dom->get_xml_description(); + $currxml = $dom->get_xml_description(); $dom->destroy(); } else { - xCAT::SvrUtils::sendmsg([1,"Cannot rmvm active guest (use -f argument to force)"], $callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "Cannot rmvm active guest (use -f argument to force)" ], $callback, $node); return; } } else { - $currxml =$confdata->{kvmnodedata}->{$node}->[0]->{xml}; + $currxml = $confdata->{kvmnodedata}->{$node}->[0]->{xml}; } if ($purge and $currxml) { - my $deadman = $parser->parse_string($currxml); + my $deadman = $parser->parse_string($currxml); my @purgedisks = $deadman->findnodes("/domain/devices/disk/source"); my $disk; foreach $disk (@purgedisks) { - my $disktype = $disk->parentNode()->getAttribute("device"); - if ($disktype eq "cdrom") { next; } + my $disktype = $disk->parentNode()->getAttribute("device"); + if ($disktype eq "cdrom") { next; } my $file = $disk->getAttribute("file"); - my $vol = $hypconn->get_storage_volume_by_path($file); + my $vol = $hypconn->get_storage_volume_by_path($file); if ($vol) { $vol->delete(); } } } eval { #try to fetch the domain by name even after it has been destroyed, if it is still there it needs an 'undefine' - $dom = $hypconn->get_domain_by_name($node); - $dom->undefine(); + $dom = $hypconn->get_domain_by_name($node); + $dom->undefine(); }; - - $updatetable->{kvm_nodedata}->{'!*XCATNODESTODELETE*!'}->{$node}=1; + + $updatetable->{kvm_nodedata}->{'!*XCATNODESTODELETE*!'}->{$node} = 1; } + sub chvm { shift; my @addsizes; @@ -1711,45 +1776,56 @@ sub chvm { my $pcpuset; my $numanodeset; my $passthrudevices; - @ARGV=@_; + my $devicestodetach; + @ARGV = @_; require Getopt::Long; - GetOptions( - "a=s"=>\@addsizes, - "d=s"=>\@derefdisks, - "mem|memory=s"=>\$memory, - "optical|dvd|cdrom=s"=>\$cdrom, - "eject"=>\$eject, - "cpus|cpu=s" => \$cpucount, - "p=s"=>\@purge, - "resize=s%" => \%resize, - "cpupin=s" => \$pcpuset, - "membind=s" => \$numanodeset, - "devpassthru=s"=> \$passthrudevices, - ); + Getopt::Long::Configure("bundling"); + Getopt::Long::Configure("no_pass_through"); + + if (!GetOptions( + "a=s" => \@addsizes, + "d=s" => \@derefdisks, + "mem|memory=s" => \$memory, + "optical|dvd|cdrom=s" => \$cdrom, + "eject" => \$eject, + "cpus|cpu=s" => \$cpucount, + "p=s" => \@purge, + "resize=s%" => \%resize, + "cpupin=s" => \$pcpuset, + "membind=s" => \$numanodeset, + "devpassthru=s" => \$passthrudevices, + "devdetach=s" => \$devicestodetach, + )) { + my $usage_string = xCAT::Usage->getUsage("chvm"); + my $rsp; + push @{ $rsp->{data} }, "$usage_string"; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } if (@derefdisks) { - xCAT::SvrUtils::sendmsg([1,"Detach without purge TODO for kvm"],$callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "Detach without purge TODO for kvm" ], $callback, $node); return; } if (@addsizes and @purge) { - xCAT::SvrUtils::sendmsg([1,"Currently adding and purging concurrently is not supported"],$callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "Currently adding and purging concurrently is not supported" ], $callback, $node); return; } - - if(defined $pcpuset){ - $pcpuset=~s/["']//g; - if("###$pcpuset" eq "###" or $pcpuset =~ /[^\d\,\^\-]/ ){ - xCAT::SvrUtils::sendmsg([1,"cpu pining: invalid cpuset"],$callback,$node); - return; - } + + if (defined $pcpuset) { + $pcpuset =~ s/["']//g; + if ("###$pcpuset" eq "###" or $pcpuset =~ /[^\d\,\^\-]/) { + xCAT::SvrUtils::sendmsg([ 1, "cpu pining: invalid cpuset" ], $callback, $node); + return; + } } - if(defined $numanodeset){ - $numanodeset=~s/["']//g; - if("###$numanodeset" eq "###" or $numanodeset =~ /[^\d\,\^\-]/ ){ - xCAT::SvrUtils::sendmsg([1,"memory binding: invalid NUMA nodeset"],$callback,$node); - return; - } + if (defined $numanodeset) { + $numanodeset =~ s/["']//g; + if ("###$numanodeset" eq "###" or $numanodeset =~ /[^\d\,\^\-]/) { + xCAT::SvrUtils::sendmsg([ 1, "memory binding: invalid NUMA nodeset" ], $callback, $node); + return; + } } my %useddisks; @@ -1759,119 +1835,124 @@ sub chvm { }; my $vmxml; if ($dom) { - $vmxml=$dom->get_xml_description(); + $vmxml = $dom->get_xml_description(); } else { - $vmxml=$confdata->{kvmnodedata}->{$node}->[0]->{xml}; + $vmxml = $confdata->{kvmnodedata}->{$node}->[0]->{xml}; } - my $currstate=getpowstate($dom); + my $currstate = getpowstate($dom); if (defined $confdata->{vm}->{$node}->[0]->{storage}) { my $store; foreach $store (split /\|/, $confdata->{vm}->{$node}->[0]->{storage}) { $store =~ s/,.*//; $store =~ s/=.*//; if (($store =~ /^nfs:\/\//) || ($store =~ /^dir:\/\//) || ($store =~ /^lvm:/)) { - my %disks = %{get_multiple_paths_by_url(url=>$store,node=>$node)}; + my %disks = %{ get_multiple_paths_by_url(url => $store, node => $node) }; foreach (keys %disks) { - $useddisks{$disks{$_}->{device}}=1; + $useddisks{ $disks{$_}->{device} } = 1; } } } } - if (@addsizes) { #need to add disks, first identify used devnames + if (@addsizes) { #need to add disks, first identify used devnames my @diskstoadd; my $location = $confdata->{vm}->{$node}->[0]->{storage}; $location =~ s/.*\|//; #use the rightmost location for making new devices - $location =~ s/,.*//; #no comma specified parameters are valid - $location =~ s/=(.*)//; #store model if specified here + $location =~ s/,.*//; #no comma specified parameters are valid + $location =~ s/=(.*)//; #store model if specified here my $model = $1; unless ($model) { + #if not defined, model will stay undefined like above $model = $confdata->{vm}->{$node}->[0]->{storagemodel} } - my $prefix='hd'; + my $prefix = 'hd'; if ($model eq 'scsi') { - $prefix='sd'; + $prefix = 'sd'; } elsif ($model eq 'virtio') { - $prefix='vd'; + $prefix = 'vd'; } if ($prefix eq 'hd' and $currstate eq 'on') { - xCAT::SvrUtils::sendmsg("VM must be powered off to add IDE drives",$callback,$node); + xCAT::SvrUtils::sendmsg("VM must be powered off to add IDE drives", $callback, $node); next; } my @suffixes; - if ($prefix eq 'hd') { - @suffixes=('a','b','d'..'zzz'); + if ($prefix eq 'hd') { + @suffixes = ('a', 'b', 'd' .. 'zzz'); } else { - @suffixes=('a'..'zzz'); + @suffixes = ('a' .. 'zzz'); } my @newsizes; foreach (@addsizes) { - push @newsizes,split /,/,$_; + push @newsizes, split /,/, $_; } foreach (@newsizes) { my $dev; do { - $dev = $prefix.shift(@suffixes); + $dev = $prefix . shift(@suffixes); } while ($useddisks{$dev}); + #ok, now I need a volume created to attach - push @diskstoadd,get_filepath_by_url(url=>$location,dev=>$dev,create=>$_); + push @diskstoadd, get_filepath_by_url(url => $location, dev => $dev, create => $_); } + #now that the volumes are made, must build xml for each and attempt attach if and only if the VM is live foreach (@diskstoadd) { my $suffix; my $format; if (/^[^\.]*\.([^\.]*)\.([^\.]*)/) { - $suffix=$1; - $format=$2; + $suffix = $1; + $format = $2; } elsif (/^[^\.]*\.([^\.]*)/) { - $suffix=$1; - $format='raw'; + $suffix = $1; + $format = 'raw'; } if ($confdata->{vm}->{$node}->[0]->{storageformat}) { $format = $confdata->{vm}->{$node}->[0]->{storageformat}; } - #when creating a new disk not cloned from anything, disable cache as copy on write content similarity is a lost cause... + + #when creating a new disk not cloned from anything, disable cache as copy on write content similarity is a lost cause... my $cachemode = 'none'; + #unless user knows better if ($confdata->{vm}->{$node}->[0]->{storagecache}) { $cachemode = $confdata->{vm}->{$node}->[0]->{storagecache}; } my $bus; if ($suffix =~ /^sd/) { - $bus='scsi'; + $bus = 'scsi'; } elsif ($suffix =~ /hd/) { - $bus='ide'; + $bus = 'ide'; } elsif ($suffix =~ /vd/) { - $bus='virtio'; + $bus = 'virtio'; } my $xml = ""; - if ($currstate eq 'on') { #attempt live attach - eval { - $dom->attach_device($xml); - }; - if ($@) { - my $err=$@; - if ($err =~ /No more available PCI addresses/) { - xCAT::SvrUtils::sendmsg([1,"Exhausted Virtio limits trying to add $_"],$callback,$node); - } else { - xCAT::SvrUtils::sendmsg([1,"Unable to attach $_ because of ".$err],$callback,$node); + if ($currstate eq 'on') { #attempt live attach + eval { + $dom->attach_device($xml); + }; + if ($@) { + my $err = $@; + if ($err =~ /No more available PCI addresses/) { + xCAT::SvrUtils::sendmsg([ 1, "Exhausted Virtio limits trying to add $_" ], $callback, $node); + } else { + xCAT::SvrUtils::sendmsg([ 1, "Unable to attach $_ because of " . $err ], $callback, $node); + } + my $file = $_; + my $vol = $hypconn->get_storage_volume_by_path($file); + if ($vol) { + $vol->delete(); + } } - my $file = $_; - my $vol = $hypconn->get_storage_volume_by_path($file); - if ($vol) { - $vol->delete(); - } - } - $vmxml=$dom->get_xml_description(); - } elsif ($confdata->{kvmnodedata}->{$node}->[0]->{xml}) { - $vmxml=$confdata->{kvmnodedata}->{$node}->[0]->{xml}; - my $disknode = $parser->parse_balanced_chunk($xml); - my $vmdoc = $parser->parse_string($vmxml); + $vmxml = $dom->get_xml_description(); + } elsif ($confdata->{kvmnodedata}->{$node}->[0]->{xml}) { + $vmxml = $confdata->{kvmnodedata}->{$node}->[0]->{xml}; + my $disknode = $parser->parse_balanced_chunk($xml); + my $vmdoc = $parser->parse_string($vmxml); my $devicesnode = $vmdoc->findnodes("/domain/devices")->[0]; $devicesnode->appendChild($disknode); - $vmxml=$vmdoc->toString(); + $vmxml = $vmdoc->toString(); } - $updatetable->{kvm_nodedata}->{$node}->{xml}=$vmxml; + $updatetable->{kvm_nodedata}->{$node}->{xml} = $vmxml; } } elsif (@purge) { my $dom; @@ -1880,30 +1961,32 @@ sub chvm { }; my $vmxml; if ($dom) { - $vmxml=$dom->get_xml_description(); + $vmxml = $dom->get_xml_description(); } else { - $vmxml=$confdata->{kvmnodedata}->{$node}->[0]->{xml}; + $vmxml = $confdata->{kvmnodedata}->{$node}->[0]->{xml}; } - my $currstate=getpowstate($dom); - my @disklist=get_disks_by_userspecs(\@purge,$vmxml,'returnmoddedxml'); + my $currstate = getpowstate($dom); + my @disklist = get_disks_by_userspecs(\@purge, $vmxml, 'returnmoddedxml'); my $moddedxml = shift @disklist; foreach (@disklist) { - my $devxml=$_->[0]; - my $file=$_->[1]; + my $devxml = $_->[0]; + my $file = $_->[1]; $file =~ m!/([^/]*)/($node\..*)\z!; + #first, detach the device. eval { - if ($currstate eq 'on') { - $dom->detach_device($devxml); - $vmxml=$dom->get_xml_description(); - } else { - $vmxml=$moddedxml; - } - $updatetable->{kvm_nodedata}->{$node}->{xml}=$vmxml; + if ($currstate eq 'on') { + $dom->detach_device($devxml); + $vmxml = $dom->get_xml_description(); + } else { + $vmxml = $moddedxml; + } + $updatetable->{kvm_nodedata}->{$node}->{xml} = $vmxml; }; if ($@) { - xCAT::SvrUtils::sendmsg([1,"Unable to remove device"],$callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "Unable to remove device" ], $callback, $node); } else { + #if that worked, remove the disk.. my $vol = $hypconn->get_storage_volume_by_path($file); if ($vol) { @@ -1912,457 +1995,612 @@ sub chvm { } } - } + } my $newcdxml; if ($cdrom) { - my $cdpath; - if ($cdrom =~ m!://!) { - my $url = $cdrom; - $url =~ s!([^/]+)\z!!; - my $imagename=$1; - my $poolobj = get_storage_pool_by_url($url); - unless ($poolobj) { die "Cound not get storage pool for $url"; } - my $poolxml = $poolobj->get_xml_description(); #yes, I have to XML parse for even this... - my $parsedpool = $parser->parse_string($poolxml); - $cdpath = $parsedpool->findnodes("/pool/target/path/text()")->[0]->data; - $cdpath .= "/".$imagename; - } else { - if ($cdrom =~ m!^/dev/!) { - die "TODO: device pass through if anyone cares"; - } elsif ($cdrom =~ m!^/!) { #full path... I guess - $cdpath=$cdrom; - } else { - die "TODO: relative paths, use client cwd as hint?"; - } - } - unless ($cdpath) { die "unable to understand cd path specification"; } - $newcdxml = ""; + my $cdpath; + if ($cdrom =~ m!://!) { + my $url = $cdrom; + $url =~ s!([^/]+)\z!!; + my $imagename = $1; + my $poolobj = get_storage_pool_by_url($url); + unless ($poolobj) { die "Cound not get storage pool for $url"; } + my $poolxml = $poolobj->get_xml_description(); #yes, I have to XML parse for even this... + my $parsedpool = $parser->parse_string($poolxml); + $cdpath = $parsedpool->findnodes("/pool/target/path/text()")->[0]->data; + $cdpath .= "/" . $imagename; + } else { + if ($cdrom =~ m!^/dev/!) { + die "TODO: device pass through if anyone cares"; + } elsif ($cdrom =~ m!^/!) { #full path... I guess + $cdpath = $cdrom; + } else { + die "TODO: relative paths, use client cwd as hint?"; + } + } + unless ($cdpath) { die "unable to understand cd path specification"; } + $newcdxml = ""; } elsif ($eject) { - $newcdxml = ""; + $newcdxml = ""; } if ($newcdxml) { - if ($currstate eq 'on') { - $dom->attach_device($newcdxml); - $vmxml=$dom->get_xml_description(); - } else { - unless ($vmxml) { - $vmxml=$confdata->{kvmnodedata}->{$node}->[0]->{xml}; - } - my $domparsed = $parser->parse_string($vmxml); - my $candidatenodes=$domparsed->findnodes("//disk[\@device='cdrom']"); - if (scalar (@$candidatenodes) != 1) { - die "shouldn't be possible, should only have one cdrom"; - } - my $newcd=$parser->parse_balanced_chunk($newcdxml); - $candidatenodes->[0]->replaceNode($newcd); - my $moddedxml=$domparsed->toString; - if ($moddedxml) { - $vmxml=$moddedxml; - } - } - if ($vmxml) { - $updatetable->{kvm_nodedata}->{$node}->{xml}=$vmxml; - } + if ($currstate eq 'on') { + $dom->attach_device($newcdxml); + $vmxml = $dom->get_xml_description(); + } else { + unless ($vmxml) { + $vmxml = $confdata->{kvmnodedata}->{$node}->[0]->{xml}; + } + my $domparsed = $parser->parse_string($vmxml); + my $candidatenodes = $domparsed->findnodes("//disk[\@device='cdrom']"); + if (scalar(@$candidatenodes) != 1) { + die "shouldn't be possible, should only have one cdrom"; + } + my $newcd = $parser->parse_balanced_chunk($newcdxml); + $candidatenodes->[0]->replaceNode($newcd); + my $moddedxml = $domparsed->toString; + if ($moddedxml) { + $vmxml = $moddedxml; + } + } + if ($vmxml) { + $updatetable->{kvm_nodedata}->{$node}->{xml} = $vmxml; + } } if ($cpucount or $memory) { if ($currstate eq 'on') { - if ($cpucount) {xCAT::SvrUtils::sendmsg([1,"Hot add of cpus not supported (VM must be powered down to successfuly change)"],$callback,$node); } + if ($cpucount) { xCAT::SvrUtils::sendmsg([ 1, "Hot add of cpus not supported (VM must be powered down to successfuly change)" ], $callback, $node); } if ($cpucount) { + #$dom->set_vcpus($cpucount); this didn't work out as well as I hoped.. #xCAT::SvrUtils::sendmsg([1,"Hot add of cpus not supported"],$callback,$node); } if ($memory) { - eval { - $dom->set_memory(getUnits($memory,"M",1024)); - }; - if ($@) { - if ($@ =~ /cannot set memory higher/) { - xCAT::SvrUtils::sendmsg([1,"Unable to increase memory beyond current capacity (requires VM to be powered down to change)"],$callback,$node); - } - } else { - if ($confdata->{vm}->{$node}->[0]->{memory} and $confdata->{vm}->{$node}->[0]->{memory} != $memory) { - $updatetable->{vm}->{$node}->{memory}=$memory; - } - } - $vmxml=$dom->get_xml_description(); - if ($vmxml) { - $updatetable->{kvm_nodedata}->{$node}->{xml}=$vmxml; - } + eval { + $dom->set_memory(getUnits($memory, "M", 1024)); + }; + if ($@) { + if ($@ =~ /cannot set memory higher/) { + xCAT::SvrUtils::sendmsg([ 1, "Unable to increase memory beyond current capacity (requires VM to be powered down to change)" ], $callback, $node); + } + } else { + if ($confdata->{vm}->{$node}->[0]->{memory} and $confdata->{vm}->{$node}->[0]->{memory} != $memory) { + $updatetable->{vm}->{$node}->{memory} = $memory; + } + } + $vmxml = $dom->get_xml_description(); + if ($vmxml) { + $updatetable->{kvm_nodedata}->{$node}->{xml} = $vmxml; + } } - } else { #offline xml edits - my $parsed=$parser->parse_string($vmxml); #TODO: should only do this once, oh well + } else { #offline xml edits + my $parsed = $parser->parse_string($vmxml); #TODO: should only do this once, oh well if ($cpucount) { - my $hypcpumodel = $confdata->{$confdata->{vm}->{$node}->[0]->{host}}->{cpumodel}; + my $hypcpumodel = $confdata->{ $confdata->{vm}->{$node}->[0]->{host} }->{cpumodel}; if ($hypcpumodel eq "ppc64") { my $cputhreads = $parsed->findnodes("/domain/cpu/topology")->[0]->getAttribute('threads'); unless ($cputhreads) { $cputhreads = "1"; } - $parsed->findnodes("/domain/cpu/topology")->[0]->setAttribute('cores'=>$cpucount); + $parsed->findnodes("/domain/cpu/topology")->[0]->setAttribute('cores' => $cpucount); $cpucount *= $cputhreads; } $parsed->findnodes("/domain/vcpu/text()")->[0]->setData($cpucount); } if ($memory) { - $parsed->findnodes("/domain/memory/text()")->[0]->setData(getUnits($memory,"M",1024)); + $parsed->findnodes("/domain/memory/text()")->[0]->setData(getUnits($memory, "M", 1024)); my @currmem = $parsed->findnodes("/domain/currentMemory/text()"); foreach (@currmem) { - $_->setData(getUnits($memory,"M",1024)); + $_->setData(getUnits($memory, "M", 1024)); + } + if ($confdata->{vm}->{$node}->[0]->{memory} and $confdata->{vm}->{$node}->[0]->{memory} != $memory) { + $updatetable->{vm}->{$node}->{memory} = $memory; } - if ($confdata->{vm}->{$node}->[0]->{memory} and $confdata->{vm}->{$node}->[0]->{memory} != $memory) { - $updatetable->{vm}->{$node}->{memory}=$memory; - } } - $vmxml=$parsed->toString; - $updatetable->{kvm_nodedata}->{$node}->{xml}=$vmxml; + $vmxml = $parsed->toString; + $updatetable->{kvm_nodedata}->{$node}->{xml} = $vmxml; } } - if(defined $pcpuset){ - if ($currstate eq 'on') { - #get the vcpuinfo of the domain - my @vcpuinfo; - eval { @vcpuinfo = $dom->get_vcpu_info(); }; - if ($@) { - xCAT::SvrUtils::sendmsg([1,"$@"],$callback,$node); - return; - } + if (defined $pcpuset) { + if ($currstate eq 'on') { - #get the cpuinfo of the host - my ($totcpus, $onlinemap, $totonline); - eval { ($totcpus, $onlinemap, $totonline) = $hypconn->get_node_cpu_map(); }; - if ($@) { - xCAT::SvrUtils::sendmsg([1,"$@"],$callback,$node); - return; - } + #get the vcpuinfo of the domain + my @vcpuinfo; + eval { @vcpuinfo = $dom->get_vcpu_info(); }; + if ($@) { + xCAT::SvrUtils::sendmsg([ 1, "$@" ], $callback, $node); + return; + } - #convert the cpuset to bitmap,which is required by pin_vcpu() - my @pcpumaparr=(0) x $totcpus; - my @cpurange=split ",",$pcpuset; - foreach my $rangeslice (@cpurange){ - if( $rangeslice =~ /^(\d*)-(\d*)$/ ){ - my ($left,$right)=($1,$2); - @pcpumaparr[$left .. $right]=(1) x ($right-$left+1); - }elsif($rangeslice =~ /^\^(\d*)$/){ - if($pcpumaparr[$1]==0){ - xCAT::SvrUtils::sendmsg([1,"\'$rangeslice\' is ignored, please make sure the specified cpu set is correct"],$callback,$node); - return; - }else{ - $pcpumaparr[$1]=0; - } + #get the cpuinfo of the host + my ($totcpus, $onlinemap, $totonline); + eval { ($totcpus, $onlinemap, $totonline) = $hypconn->get_node_cpu_map(); }; + if ($@) { + xCAT::SvrUtils::sendmsg([ 1, "$@" ], $callback, $node); + return; + } - }elsif($rangeslice =~ /^(\d*)$/){ - $pcpumaparr[$1]=1; - } - } - - my $pcpumap=join //,@pcpumaparr; - my $mask=pack("b*",$pcpumap); - - #Pin the virtual CPU given to physical CPUs given - foreach(@vcpuinfo){ - eval { - $dom->pin_vcpu($_->{number}, $mask); - }; - if ($@) { - xCAT::SvrUtils::sendmsg([1,"$@"],$callback,$node); - } - } + #convert the cpuset to bitmap,which is required by pin_vcpu() + my @pcpumaparr = (0) x $totcpus; + my @cpurange = split ",", $pcpuset; + foreach my $rangeslice (@cpurange) { + if ($rangeslice =~ /^(\d*)-(\d*)$/) { + my ($left, $right) = ($1, $2); + @pcpumaparr[ $left .. $right ] = (1) x ($right - $left + 1); + } elsif ($rangeslice =~ /^\^(\d*)$/) { + if ($pcpumaparr[$1] == 0) { + xCAT::SvrUtils::sendmsg([ 1, "\'$rangeslice\' is ignored, please make sure the specified cpu set is correct" ], $callback, $node); + return; + } else { + $pcpumaparr[$1] = 0; + } - $vmxml=$dom->get_xml_description(); - my $parsed=$parser->parse_string($vmxml); - my $ref=$parsed->findnodes("/domain/vcpu"); - $ref->[0]->removeAttribute("cpuset"); - $vmxml=$parsed->toString; - if ($vmxml) { - $updatetable->{kvm_nodedata}->{$node}->{xml}=$vmxml; - } - }else{ - my $parsed=$parser->parse_string($vmxml); - my $ref=$parsed->findnodes("/domain/vcpu"); - $ref->[0]->setAttribute("cpuset",$pcpuset); + } elsif ($rangeslice =~ /^(\d*)$/) { + $pcpumaparr[$1] = 1; + } + } - #for virtual CPUs which have the vcpupin specified, - # the cpuset specified by/domain/vcpu/cpuset will be ignored - my $ref=$parsed->findnodes("/domain"); - my $cputuneref=$parsed->findnodes("/domain/cputune"); - $cputuneref->[0]->parentNode->removeChild($cputuneref->[0]); - $vmxml=$parsed->toString; - $updatetable->{kvm_nodedata}->{$node}->{xml}=$vmxml; - } - } + my $pcpumap = join //, @pcpumaparr; + my $mask = pack("b*", $pcpumap); - if(defined $numanodeset){ - if ($currstate eq 'on') { - eval { - my %tmphash=(Sys::Virt::Domain->NUMA_NODESET => "$numanodeset"); - $dom->set_numa_parameters(\%tmphash); - }; - if ($@) { - xCAT::SvrUtils::sendmsg([1,"$@"],$callback,$node); - return; - } - $vmxml=$dom->get_xml_description(); - if ($vmxml) { - $updatetable->{kvm_nodedata}->{$node}->{xml}=$vmxml; - } - }else{ - my %numatunehash; - $numatunehash{memory}=[{nodeset=>"$numanodeset"}]; - my $numatunexml=XMLout(\%numatunehash,RootName=>"numatune"); - - - my $parsed=$parser->parse_string($vmxml); - my $numatuneref=$parsed->findnodes("/domain/numatune"); - - if($numatuneref) - { - #/domain/numatune exist,modify the numatune/nodeset - my $ref=$parsed->findnodes("/domain/numatune/memory"); - $ref->[0]->setAttribute("nodeset",$numanodeset); - }else{ - #/domain/numatune does not exist,create one - my $ref=$parsed->findnodes("/domain"); - my $numatunenode = $parser->parse_balanced_chunk($numatunexml); - $ref->[0]->appendChild($numatunenode); - } - $vmxml=$parsed->toString; - $updatetable->{kvm_nodedata}->{$node}->{xml}=$vmxml; - } - } - - - - $passthrudevices=~s/["']//g; - if(defined $passthrudevices){ - my @prdevarr=split ",",$passthrudevices; - unless(scalar @prdevarr){ - xCAT::SvrUtils::sendmsg([1,"device passthrough: no device specified"],$callback,$node); - return; - } - - foreach my $devname (@prdevarr){ - my $devobj; - eval { - $devobj=$hypconn->get_node_device_by_name($devname); - }; - if ($@) { - xCAT::SvrUtils::sendmsg([1,"$@"],$callback,$node); - next; - } - - my $devxml=$devobj->get_xml_description(); - unless($devxml){ - next; - } - my $devhash=XMLin($devxml); - if(defined $devhash->{capability}->{type} and $devhash->{capability}->{type} =~ /pci/i ){ - my %tmphash; - $tmphash{mode}='subsystem'; - $tmphash{type}=$devhash->{capability}->{type}; - $tmphash{managed}="yes"; - $tmphash{driver}->{name}="vfio"; - $tmphash{source}->{address}->[0]=\%{$devhash->{'capability'}->{'iommuGroup'}->{'address'}}; - - my $newxml=XMLout(\%tmphash,RootName=>"hostdev"); - - if ($currstate eq 'on') { + #Pin the virtual CPU given to physical CPUs given + foreach (@vcpuinfo) { eval { - $dom->attach_device($newxml); + $dom->pin_vcpu($_->{number}, $mask); }; if ($@) { - xCAT::SvrUtils::sendmsg([1,"$@"],$callback,$node); - next; - }else{ - $vmxml=$dom->get_xml_description(); - if ($vmxml) { - $updatetable->{kvm_nodedata}->{$node}->{xml}=$vmxml; - } - xCAT::SvrUtils::sendmsg([0,"passthrough: $devname attached to guest successfully "],$callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "$@" ], $callback, $node); } + } - }else{ + $vmxml = $dom->get_xml_description(); + my $parsed = $parser->parse_string($vmxml); + my $ref = $parsed->findnodes("/domain/vcpu"); + $ref->[0]->removeAttribute("cpuset"); + $vmxml = $parsed->toString; + if ($vmxml) { + $updatetable->{kvm_nodedata}->{$node}->{xml} = $vmxml; + } + } else { + my $parsed = $parser->parse_string($vmxml); + my $ref = $parsed->findnodes("/domain/vcpu"); + $ref->[0]->setAttribute("cpuset", $pcpuset); - my $hostdevfound; - $hostdevfound=0; - my $parsed=$parser->parse_string($vmxml); - my $ref=$parsed->findnodes("/domain/devices"); + #for virtual CPUs which have the vcpupin specified, + # the cpuset specified by/domain/vcpu/cpuset will be ignored + my $ref = $parsed->findnodes("/domain"); + my $cputuneref = $parsed->findnodes("/domain/cputune"); + $cputuneref->[0]->parentNode->removeChild($cputuneref->[0]); + $vmxml = $parsed->toString; + $updatetable->{kvm_nodedata}->{$node}->{xml} = $vmxml; + } + } - my @hostdevlist=$ref->[0]->findnodes("./hostdev"); - #check whether the hostdev existed in guest xml - foreach my $hostdevref (@hostdevlist){ - my $devaddrref=$hostdevref->findnodes("./source/address"); + if (defined $numanodeset) { + if ($currstate eq 'on') { + eval { + my %tmphash = (Sys::Virt::Domain->NUMA_NODESET => "$numanodeset"); + $dom->set_numa_parameters(\%tmphash); + }; + if ($@) { + xCAT::SvrUtils::sendmsg([ 1, "$@" ], $callback, $node); + return; + } + $vmxml = $dom->get_xml_description(); + if ($vmxml) { + $updatetable->{kvm_nodedata}->{$node}->{xml} = $vmxml; + } + } else { + my %numatunehash; + $numatunehash{memory} = [ { nodeset => "$numanodeset" } ]; + my $numatunexml = XMLout(\%numatunehash, RootName => "numatune"); - my $domain=$devaddrref->[0]->getAttribute("domain"); - my $bus=$devaddrref->[0]->getAttribute("bus"); - my $slot=$devaddrref->[0]->getAttribute("slot"); - my $function=$devaddrref->[0]->getAttribute("function"); - my $curdevaddr=\%{$devhash->{'capability'}->{'iommuGroup'}->{'address'}}; + my $parsed = $parser->parse_string($vmxml); + my $numatuneref = $parsed->findnodes("/domain/numatune"); + + if ($numatuneref) + { + #/domain/numatune exist,modify the numatune/nodeset + my $ref = $parsed->findnodes("/domain/numatune/memory"); + $ref->[0]->setAttribute("nodeset", $numanodeset); + } else { + + #/domain/numatune does not exist,create one + my $ref = $parsed->findnodes("/domain"); + my $numatunenode = $parser->parse_balanced_chunk($numatunexml); + $ref->[0]->appendChild($numatunenode); + } + $vmxml = $parsed->toString; + $updatetable->{kvm_nodedata}->{$node}->{xml} = $vmxml; + } + } - if(("$curdevaddr->{domain}" eq "$domain") and - ("$curdevaddr->{bus}" eq "$bus") and - ("$curdevaddr->{slot}" eq "$slot") and - ("$curdevaddr->{function}" eq "$function")){ + $passthrudevices =~ s/["']//g; + if (defined $passthrudevices) { + my @prdevarr = split ",", $passthrudevices; + unless (scalar @prdevarr) { + xCAT::SvrUtils::sendmsg([ 1, "device passthrough: no device specified" ], $callback, $node); + return; + } - #hostdev existed in guest xml - $hostdevfound=1; - goto PROCESS_HOSTDEV_XML; - } - } + foreach my $devname (@prdevarr) { + my $devobj; + eval { + $devobj = $hypconn->get_node_device_by_name($devname); + }; + if ($@) { + xCAT::SvrUtils::sendmsg([ 1, "$@" ], $callback, $node); + next; + } + + my $devxml = $devobj->get_xml_description(); + unless ($devxml) { + next; + } - PROCESS_HOSTDEV_XML: - unless($hostdevfound){ - #hostdev does not exist,add into guest xml - my $hostdevnode = $parser->parse_balanced_chunk($newxml); - $ref->[0]->appendChild($hostdevnode); - $vmxml=$parsed->toString; - $updatetable->{kvm_nodedata}->{$node}->{xml}=$vmxml; + my $devhash = XMLin($devxml); + if (defined $devhash->{capability}->{type} and $devhash->{capability}->{type} =~ /pci/i) { + my %tmphash; + $tmphash{mode} = 'subsystem'; + $tmphash{type} = $devhash->{capability}->{type}; + $tmphash{managed} = "yes"; + $tmphash{driver}->{name} = "vfio"; + $tmphash{source}->{address}->[0] = \%{ $devhash->{'capability'}->{'iommuGroup'}->{'address'} }; + + my $newxml = XMLout(\%tmphash, RootName => "hostdev"); + + if ($currstate eq 'on') { + + #for a running KVM guest, first unbind the device from the existing driver, + #reset the device, and bind it + #If the description of a PCI device includes the attribute managed='yes', + #and the hypervisor driver supports it, then the device is in managed mode, and attempts to + #use that passthrough device in an active guest will automatically behave as if nodedev-detach + #(guest start, device hot-plug) and nodedev-reattach (guest stop, device hot-unplug) + #were called at the right points. + #in case the hypervisor driver does not support managed mode, do this explicitly here + eval { + $devobj->dettach(undef, 0); + }; + if ($@) { + xCAT::SvrUtils::sendmsg([ 0, "detaching $devname from host:$@" ], $callback, $node); + } + + eval { + $devobj->reset(); + }; + if ($@) { + xCAT::SvrUtils::sendmsg([ 0, "resetting $devname:$@" ], $callback, $node); + } + + my $flag = 0; + if ($dom->is_persistent()) { + $flag = &Sys::Virt::Domain::DEVICE_MODIFY_LIVE | &Sys::Virt::Domain::DEVICE_MODIFY_CONFIG; + } else { + $flag = &Sys::Virt::Domain::DEVICE_MODIFY_LIVE; + } + + eval { + $dom->attach_device($newxml, $flag); + }; + if ($@) { + xCAT::SvrUtils::sendmsg([ 1, "attaching device to guest:$@" ], $callback, $node); + next; + } else { + $vmxml = $dom->get_xml_description(); + if ($vmxml) { + $updatetable->{kvm_nodedata}->{$node}->{xml} = $vmxml; + } + xCAT::SvrUtils::sendmsg([ 0, "passthrough: $devname attached to guest successfully " ], $callback, $node); + } + + } else { + + my $hostdevfound; + $hostdevfound = 0; + my $parsed = $parser->parse_string($vmxml); + my $ref = $parsed->findnodes("/domain/devices"); + + my @hostdevlist = $ref->[0]->findnodes("./hostdev"); + + #check whether the hostdev existed in guest xml + foreach my $hostdevref (@hostdevlist) { + my $devaddrref = $hostdevref->findnodes("./source/address"); + + my $domain = $devaddrref->[0]->getAttribute("domain"); + my $bus = $devaddrref->[0]->getAttribute("bus"); + my $slot = $devaddrref->[0]->getAttribute("slot"); + my $function = $devaddrref->[0]->getAttribute("function"); + + my $curdevaddr = \%{ $devhash->{'capability'}->{'iommuGroup'}->{'address'} }; + + + + if (("$curdevaddr->{domain}" eq "$domain") and + ("$curdevaddr->{bus}" eq "$bus") and + ("$curdevaddr->{slot}" eq "$slot") and + ("$curdevaddr->{function}" eq "$function")) { + + #hostdev existed in guest xml + $hostdevfound = 1; + goto PROCESS_HOSTDEV_XML_ATTATCH; + } + } + + PROCESS_HOSTDEV_XML_ATTATCH: + unless ($hostdevfound) { + + #hostdev does not exist,add into guest xml + my $hostdevnode = $parser->parse_balanced_chunk($newxml); + $ref->[0]->appendChild($hostdevnode); + $vmxml = $parsed->toString; + $updatetable->{kvm_nodedata}->{$node}->{xml} = $vmxml; + + } + } + } - - } - } - - } - } + } + } + + $devicestodetach =~ s/["']//g; + if (defined $devicestodetach) { + my @devarr = split ",", $devicestodetach; + unless (scalar @devarr) { + xCAT::SvrUtils::sendmsg([ 1, "device detaching: no device specified" ], $callback, $node); + return; + } + + foreach my $devname (@devarr) { + my $devobj; + eval { + $devobj = $hypconn->get_node_device_by_name($devname); + }; + if ($@) { + xCAT::SvrUtils::sendmsg([ 1, "$@" ], $callback, $node); + next; + } + my $devxml = $devobj->get_xml_description(); + unless ($devxml) { + next; + } + + my $devhash = XMLin($devxml); + if (defined $devhash->{capability}->{type} and $devhash->{capability}->{type} =~ /pci/i) { + my %tmphash; + $tmphash{mode} = 'subsystem'; + $tmphash{type} = $devhash->{capability}->{type}; + $tmphash{managed} = "yes"; + $tmphash{driver}->{name} = "vfio"; + $tmphash{source}->{address}->[0] = \%{ $devhash->{'capability'}->{'iommuGroup'}->{'address'} }; + + my $newxml = XMLout(\%tmphash, RootName => "hostdev"); + if ($currstate eq 'on') { + my $flag = 0; + if ($dom->is_persistent()) { + $flag = &Sys::Virt::Domain::DEVICE_MODIFY_LIVE | &Sys::Virt::Domain::DEVICE_MODIFY_CONFIG; + } else { + $flag = &Sys::Virt::Domain::DEVICE_MODIFY_LIVE; + } + eval { + $dom->detach_device($newxml, $flag); + }; + if ($@) { + xCAT::SvrUtils::sendmsg([ 1, "detaching device from guest:$@" ], $callback, $node); + next; + } else { + $vmxml = $dom->get_xml_description(); + if ($vmxml) { + $updatetable->{kvm_nodedata}->{$node}->{xml} = $vmxml; + } + + eval { + $devobj->reattach(); + }; + if ($@) { + xCAT::SvrUtils::sendmsg([ 0, "reattaching device to host:$@" ], $callback, $node); + } + + xCAT::SvrUtils::sendmsg([ 0, "devdetach: $devname detached from guest successfully " ], $callback, $node); + } + + } else { + + my $hostdevfound; + $hostdevfound = 0; + my $hostdevobj; + my $parsed = $parser->parse_string($vmxml); + my $ref = $parsed->findnodes("/domain/devices"); + + my @hostdevlist = $ref->[0]->findnodes("./hostdev"); + + #check whether the hostdev existed in guest xml + foreach my $hostdevref (@hostdevlist) { + my $devaddrref = $hostdevref->findnodes("./source/address"); + + my $domain = $devaddrref->[0]->getAttribute("domain"); + my $bus = $devaddrref->[0]->getAttribute("bus"); + my $slot = $devaddrref->[0]->getAttribute("slot"); + my $function = $devaddrref->[0]->getAttribute("function"); + + my $curdevaddr = \%{ $devhash->{'capability'}->{'iommuGroup'}->{'address'} }; + + + if (("$curdevaddr->{domain}" eq "$domain") and + ("$curdevaddr->{bus}" eq "$bus") and + ("$curdevaddr->{slot}" eq "$slot") and + ("$curdevaddr->{function}" eq "$function")) { + + #hostdev existed in guest xml + $hostdevfound = 1; + $hostdevobj = $hostdevref; + goto PROCESS_HOSTDEV_XML_DETATCH; + } + } + + + + PROCESS_HOSTDEV_XML_DETATCH: + if ($hostdevfound) { + + #hostdev exist,remove it from guest xml + my $hostdevnode = $parser->parse_balanced_chunk($newxml); + $hostdevobj->parentNode()->removeChild($hostdevobj); + $vmxml = $parsed->toString; + $updatetable->{kvm_nodedata}->{$node}->{xml} = $vmxml; + xCAT::SvrUtils::sendmsg([ 0, "devdetach: $devname detached from guest $node successfully " ], $callback, $node); + + } else { + xCAT::SvrUtils::sendmsg([ 1, "device detaching: the specified device $devname is not attached to $node yet" ], $callback, $node); + return; + } + } + + } + } + } } + sub get_disks_by_userspecs { - my $specs = shift; - my $xml = shift; - my $returnmoddedxml=shift; - my $struct = XMLin($xml,forcearray=>1); - my $dominf = $parser->parse_string($xml); - my @disknodes = $dominf->findnodes('/domain/devices/disk'); + my $specs = shift; + my $xml = shift; + my $returnmoddedxml = shift; + my $struct = XMLin($xml, forcearray => 1); + my $dominf = $parser->parse_string($xml); + my @disknodes = $dominf->findnodes('/domain/devices/disk'); my @returnxmls; foreach my $spec (@$specs) { my $disknode; foreach $disknode (@disknodes) { if ($spec =~ /^.d./) { #vda, hdb, sdc, etc, match be equality to target->{dev} if ($disknode->findnodes('./target')->[0]->getAttribute("dev") eq $spec) { - push @returnxmls,[$disknode->toString(),$disknode->findnodes('./source')->[0]->getAttribute('file')]; - if ($returnmoddedxml) { + push @returnxmls, [ $disknode->toString(), $disknode->findnodes('./source')->[0]->getAttribute('file') ]; + if ($returnmoddedxml) { $disknode->parentNode->removeChild($disknode); } } - } elsif ($spec =~ /^d(.*)/) { #delete by scsi unit number.. - my $loc=$1; + } elsif ($spec =~ /^d(.*)/) { #delete by scsi unit number.. + my $loc = $1; my $addr = $disknode->findnodes('./address')->[0]; - if ($loc =~ /:/) { #controller, bus, unit + if ($loc =~ /:/) { #controller, bus, unit my $controller; my $bus; my $unit; - ($controller,$bus,$unit) = split /:/,$loc; + ($controller, $bus, $unit) = split /:/, $loc; if (hex($addr->getAttribute('controller')) == hex($controller) and ($addr->getAttribute('bus')) == hex($bus) and ($addr->getAttribute('unit')) == hex($unit)) { - push @returnxmls,[$disknode->toString(),$disknode->findnodes('./source')->[0]->getAttribute('file')]; - if ($returnmoddedxml) { + push @returnxmls, [ $disknode->toString(), $disknode->findnodes('./source')->[0]->getAttribute('file') ]; + if ($returnmoddedxml) { $disknode->parentNode->removeChild($disknode); } } - + } else { #match just on unit number, not helpful on ide as much generally, but whatever if (hex($addr->getAttribute('unit')) == hex($loc)) { - push @returnxmls,[$disknode->toString(),$disknode->findnodes('./source')->[0]->getAttribute('file')]; - if ($returnmoddedxml) { + push @returnxmls, [ $disknode->toString(), $disknode->findnodes('./source')->[0]->getAttribute('file') ]; + if ($returnmoddedxml) { $disknode->parentNode->removeChild($disknode); } } } - } elsif ($spec =~ /^v(.*)/) { #virtio pci selector - my $slot=$1; - $slot =~ s/^(.*)://; #remove pci bus number (not used for now) - my $bus=$1; + } elsif ($spec =~ /^v(.*)/) { #virtio pci selector + my $slot = $1; + $slot =~ s/^(.*)://; #remove pci bus number (not used for now) + my $bus = $1; $slot =~ s/\.0$//; - my $addr=$disknode->findnodes('./address')->[0]; + my $addr = $disknode->findnodes('./address')->[0]; if (hex($addr->getAttribute('slot')) == hex($slot) and hex($addr->getAttribute('bus') == hex($bus))) { - push @returnxmls,[$disknode->toString(),$disknode->findnodes('./source')->[0]->getAttribute('file')]; - if ($returnmoddedxml) { + push @returnxmls, [ $disknode->toString(), $disknode->findnodes('./source')->[0]->getAttribute('file') ]; + if ($returnmoddedxml) { $disknode->parentNode->removeChild($disknode); } } - } #other formats TBD + } #other formats TBD } } - if ($returnmoddedxml) {#there are list entries to delete - unshift @returnxmls,$dominf->toString(); + if ($returnmoddedxml) { #there are list entries to delete + unshift @returnxmls, $dominf->toString(); } return @returnxmls; } sub promote_vm_to_master { - my %args=@_; - my $target=$args{target}; - my $force=$args{force}; - my $detach=$args{detach}; - if ($target !~ m!://!) { #if not a url, use same place as source + my %args = @_; + my $target = $args{target}; + my $force = $args{force}; + my $detach = $args{detach}; + if ($target !~ m!://!) { #if not a url, use same place as source my $sourcedir = $confdata->{vm}->{$node}->[0]->{storage}; $sourcedir =~ s/=.*//; $sourcedir =~ s/,.*//; $sourcedir =~ s!/\z!!; - $target = $sourcedir."/".$target; + $target = $sourcedir . "/" . $target; } unless ($target =~ /^nfs:\/\//) { - xCAT::SvrUtils::sendmsg([1,"KVM plugin only has nfs://// support for cloning at this moment"], $callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "KVM plugin only has nfs://// support for cloning at this moment" ], $callback, $node); return; } my $dom; eval { - $dom = $hypconn->get_domain_by_name($node); + $dom = $hypconn->get_domain_by_name($node); }; if ($dom and not $force) { - xCAT::SvrUtils::sendmsg([1,"VM shut be shut down before attempting to clone (-f to copy unclean disks)"], $callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "VM shut be shut down before attempting to clone (-f to copy unclean disks)" ], $callback, $node); return; } my $xml; if ($dom) { - $xml = $dom->get_xml_description(); - $detach=1; #can't rebase if vm is on + $xml = $dom->get_xml_description(); + $detach = 1; #can't rebase if vm is on } else { $xml = $confdata->{kvmnodedata}->{$node}->[0]->{xml}; } unless ($xml) { - xCAT::SvrUtils::sendmsg([1,"VM must be created before it can be cloned"], $callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "VM must be created before it can be cloned" ], $callback, $node); return; } my $parsedxml = $parser->parse_string($xml); - my($tmpnod) = $parsedxml->findnodes('/domain/uuid/text()'); + my ($tmpnod) = $parsedxml->findnodes('/domain/uuid/text()'); if ($tmpnod) { - $tmpnod->setData("none"); #get rid of the VM specific uuid + $tmpnod->setData("none"); #get rid of the VM specific uuid } $target =~ m!^(.*)/([^/]*)\z!; - my $directory=$1; - my $mastername=$2; + my $directory = $1; + my $mastername = $2; ($tmpnod) = $parsedxml->findnodes('/domain/name/text()'); if ($tmpnod) { $tmpnod->setData($mastername); #name the xml whatever the master name is to be } - foreach ($parsedxml->findnodes("/domain/devices/interface/mac")) { #clear all mac addresses - if ($_->hasAttribute("address")) { $_->setAttribute("address"=>''); } + foreach ($parsedxml->findnodes("/domain/devices/interface/mac")) { #clear all mac addresses + if ($_->hasAttribute("address")) { $_->setAttribute("address" => ''); } } my $poolobj = get_storage_pool_by_url($directory); unless ($poolobj) { - xCAT::SvrUtils::sendmsg([1,"Unable to reach $directory from hypervisor"], $callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "Unable to reach $directory from hypervisor" ], $callback, $node); return; } + #arguments validated, on with our lives -#firrder of business, calculate all the image names to be created and ensure none will conflict. + #firrder of business, calculate all the image names to be created and ensure none will conflict. my @disks = $parsedxml->findnodes('/domain/devices/disk/source'); my %volclonemap; foreach (@disks) { my $filename = $_->getAttribute('file'); - my $volname = $filename; - $volname =~ s!.*/!!; #perl is greedy by default + my $volname = $filename; + $volname =~ s!.*/!!; #perl is greedy by default $volname =~ s/^$node/$mastername/; my $novol; eval { #use two evals, there is a chance the pool has a task blocking refresh like long-running clone.... libvirt should do better IMO, oh well @@ -2372,7 +2610,7 @@ sub promote_vm_to_master { $novol = $poolobj->get_volume_by_name($volname); }; if ($novol) { - xCAT::SvrUtils::sendmsg([1,"$volname already exists in target storage pool"], $callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "$volname already exists in target storage pool" ], $callback, $node); return; } my $sourcevol; @@ -2380,131 +2618,133 @@ sub promote_vm_to_master { $sourcevol = $hypconn->get_storage_volume_by_path($filename); }; unless ($sourcevol) { - xCAT::SvrUtils::sendmsg([1,"Unable to access $filename to clone"], $callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "Unable to access $filename to clone" ], $callback, $node); return; } - $volclonemap{$filename}=[$sourcevol,$volname]; + $volclonemap{$filename} = [ $sourcevol, $volname ]; $filename = get_path_for_pool($poolobj); $filename =~ s!/\z!!; - $filename .= '/'.$volname; - $_->setAttribute(file=>$filename); + $filename .= '/' . $volname; + $_->setAttribute(file => $filename); } foreach (keys %volclonemap) { my $sourcevol = $volclonemap{$_}->[0]; - my $targname = $volclonemap{$_}->[1]; + my $targname = $volclonemap{$_}->[1]; my $format; $targname =~ /([^\.]*)$/; - $format=$1; + $format = $1; my $newvol; - my %sourceinfo = %{$sourcevol->get_info()}; - my $targxml = "$targname".$sourceinfo{capacity}.""; - xCAT::SvrUtils::sendmsg("Cloning ".$sourcevol->get_name()." (currently is ".($sourceinfo{allocation}/1048576)." MB and has a capacity of ".($sourceinfo{capacity}/1048576)."MB)",$callback,$node); + my %sourceinfo = %{ $sourcevol->get_info() }; + my $targxml = "$targname" . $sourceinfo{capacity} . ""; + xCAT::SvrUtils::sendmsg("Cloning " . $sourcevol->get_name() . " (currently is " . ($sourceinfo{allocation} / 1048576) . " MB and has a capacity of " . ($sourceinfo{capacity} / 1048576) . "MB)", $callback, $node); eval { - $newvol =$poolobj->clone_volume($targxml,$sourcevol); + $newvol = $poolobj->clone_volume($targxml, $sourcevol); }; if ($newvol) { - %sourceinfo = %{$newvol->get_info()}; - xCAT::SvrUtils::sendmsg("Cloning of ".$sourcevol->get_name()." complete (clone uses ".($sourceinfo{allocation}/1048576)." for a disk size of ".($sourceinfo{capacity}/1048576)."MB)",$callback,$node); + %sourceinfo = %{ $newvol->get_info() }; + xCAT::SvrUtils::sendmsg("Cloning of " . $sourcevol->get_name() . " complete (clone uses " . ($sourceinfo{allocation} / 1048576) . " for a disk size of " . ($sourceinfo{capacity} / 1048576) . "MB)", $callback, $node); unless ($detach) { my $rebasepath = $sourcevol->get_path(); my $rebasename = $sourcevol->get_name(); my $rebasepool = get_storage_pool_by_volume($sourcevol); unless ($rebasepool) { - xCAT::SvrUtils::sendmsg([1,"Skipping rebase of $rebasename, unable to find correct storage pool"],$callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "Skipping rebase of $rebasename, unable to find correct storage pool" ], $callback, $node); next; } - xCAT::SvrUtils::sendmsg("Rebasing $rebasename from master",$callback,$node); + xCAT::SvrUtils::sendmsg("Rebasing $rebasename from master", $callback, $node); $sourcevol->delete(); - my $newbasexml="$rebasename".$sourceinfo{capacity}."".$newvol->get_path().""; + my $newbasexml = "$rebasename" . $sourceinfo{capacity} . "" . $newvol->get_path() . ""; my $newbasevol; eval { $newbasevol = $rebasepool->create_volume($newbasexml); }; if ($newbasevol) { - xCAT::SvrUtils::sendmsg("Rebased $rebasename from master",$callback,$node); + xCAT::SvrUtils::sendmsg("Rebased $rebasename from master", $callback, $node); } else { - xCAT::SvrUtils::sendmsg([1,"Critical failure, rebasing process failed halfway through, source VM trashed"],$callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "Critical failure, rebasing process failed halfway through, source VM trashed" ], $callback, $node); } } } else { - xCAT::SvrUtils::sendmsg([1,"Cloning of ".$sourcevol->get_name()." failed due to ". $@],$callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "Cloning of " . $sourcevol->get_name() . " failed due to " . $@ ], $callback, $node); return; } } - my $mastertabentry={}; + my $mastertabentry = {}; foreach (qw/os arch profile/) { - if (defined ($confdata->{nodetype}->{$node}->[0]->{$_})) { - $mastertabentry->{$_}=$confdata->{nodetype}->{$node}->[0]->{$_}; + if (defined($confdata->{nodetype}->{$node}->[0]->{$_})) { + $mastertabentry->{$_} = $confdata->{nodetype}->{$node}->[0]->{$_}; } } foreach (qw/storagemodel nics/) { - if (defined ($confdata->{vm}->{$node}->[0]->{$_})) { - $mastertabentry->{$_}=$confdata->{vm}->{$node}->[0]->{$_}; + if (defined($confdata->{vm}->{$node}->[0]->{$_})) { + $mastertabentry->{$_} = $confdata->{vm}->{$node}->[0]->{$_}; } } - $mastertabentry->{storage}=$directory; - $mastertabentry->{vintage}=localtime; - $mastertabentry->{originator}=$requester; + $mastertabentry->{storage} = $directory; + $mastertabentry->{vintage} = localtime; + $mastertabentry->{originator} = $requester; unless ($detach) { - $updatetable->{vm}->{$node}->{master}=$mastername; + $updatetable->{vm}->{$node}->{master} = $mastername; } - $updatetable->{vmmaster}->{$mastername}=$mastertabentry; + $updatetable->{vmmaster}->{$mastername} = $mastertabentry; $updatetable->{kvm_masterdata}->{$mastername}->{xml} = $parsedxml->toString(); } + sub clonevm { - shift; #throw away node - @ARGV=@_; + shift; #throw away node + @ARGV = @_; my $target; my $base; my $detach; my $force; GetOptions( - 'f' => \$force, + 'f' => \$force, 'b=s' => \$base, 't=s' => \$target, - 'd' => \$detach, + 'd' => \$detach, ); if ($base and $target) { - xCAT::SvrUtils::sendmsg([1,"Cannot specify both base (-b) and target (-t)"], $callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "Cannot specify both base (-b) and target (-t)" ], $callback, $node); return; } - if ($target) { #we need to take a single vm and create a master out of it - return promote_vm_to_master(target=>$target,force=>$force,detach=>$detach); + if ($target) { #we need to take a single vm and create a master out of it + return promote_vm_to_master(target => $target, force => $force, detach => $detach); } elsif ($base) { - return clone_vm_from_master(base=>$base,detach=>$detach); + return clone_vm_from_master(base => $base, detach => $detach); } } sub clone_vm_from_master { - my %args = @_; - my $base=$args{base}; - my $detach=$args{detach}; - my $vmmastertab=xCAT::Table->new('vmmaster',-create=>0); - my $kvmmastertab = xCAT::Table->new('kvm_masterdata',-create=>0); + my %args = @_; + my $base = $args{base}; + my $detach = $args{detach}; + my $vmmastertab = xCAT::Table->new('vmmaster', -create => 0); + my $kvmmastertab = xCAT::Table->new('kvm_masterdata', -create => 0); unless ($vmmastertab and $kvmmastertab) { - xCAT::SvrUtils::sendmsg([1,"No KVM master images in tables"], $callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "No KVM master images in tables" ], $callback, $node); return; } - my $mastername=$base; - $mastername=~s!.*/!!; #shouldn't be needed, as storage is in there, but just in case - my $masteref=$vmmastertab->getAttribs({name=>$mastername},[qw/os arch profile storage storagemodel nics/]); - my $kvmmasteref=$kvmmastertab->getAttribs({name=>$mastername},['xml']); + my $mastername = $base; + $mastername =~ s!.*/!!; #shouldn't be needed, as storage is in there, but just in case + my $masteref = $vmmastertab->getAttribs({ name => $mastername }, [qw/os arch profile storage storagemodel nics/]); + my $kvmmasteref = $kvmmastertab->getAttribs({ name => $mastername }, ['xml']); unless ($masteref and $kvmmasteref) { - xCAT::SvrUtils::sendmsg([1,"KVM master $mastername not found in tables"], $callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "KVM master $mastername not found in tables" ], $callback, $node); return; } my $newnodexml = $parser->parse_string($kvmmasteref->{xml}); $newnodexml->findnodes("/domain/name/text()")->[0]->setData($node); #set name correctly - my $uuid=getNodeUUID($node); + my $uuid = getNodeUUID($node); $newnodexml->findnodes("/domain/uuid/text()")->[0]->setData($uuid); #put in correct uuid - #set up mac addresses and such right... - fixup_clone_network(mastername=>$mastername,mastertableentry=>$masteref,kvmmastertableentry=>$kvmmasteref,xmlinprogress=>$newnodexml); + #set up mac addresses and such right... + fixup_clone_network(mastername => $mastername, mastertableentry => $masteref, kvmmastertableentry => $kvmmasteref, xmlinprogress => $newnodexml); + #ok, now the fun part, storage... my $disk; if ($masteref->{storage}) { - foreach (split /,/,$masteref->{storage}) { - s/=.*//; - get_storage_pool_by_url($_); + foreach (split /,/, $masteref->{storage}) { + s/=.*//; + get_storage_pool_by_url($_); } } my $url; @@ -2515,259 +2755,265 @@ sub clone_vm_from_master { $url = $confdata->{vm}->{$node}->[0]->{storage}; } else { $url = $masteref->{storage}; - $updatetable->{vm}->{$node}->{storage}=$url; + $updatetable->{vm}->{$node}->{storage} = $url; } if ($masteref->{storagemodel} and not $confdata->{vm}->{$node}->[0]->{storagemodel}) { - $updatetable->{vm}->{$node}->{storagemodel}=$masteref->{storagemodel}; + $updatetable->{vm}->{$node}->{storagemodel} = $masteref->{storagemodel}; } $url =~ s/,.*//; my $destinationpool = get_storage_pool_by_url($url); foreach $disk ($newnodexml->findnodes("/domain/devices/disk")) { - my ($source) = ($disk->findnodes("./source")); - unless ($source) { next; } #most likely an empty cdrom + my ($source) = ($disk->findnodes("./source")); + unless ($source) { next; } #most likely an empty cdrom my $srcfilename = $source->getAttribute("file"); - my $filename = $srcfilename; + my $filename = $srcfilename; $filename =~ s/^.*$mastername/$node/; $filename =~ m!\.([^\.]*)\z!; - my $format=$1; + my $format = $1; my $newvol; + if ($detach) { - my $sourcevol = $hypconn->get_storage_volume_by_path($srcfilename); - my %sourceinfo = %{$sourcevol->get_info()}; - my $targxml = "$filename".$sourceinfo{capacity}.""; - xCAT::SvrUtils::sendmsg("Cloning ".$sourcevol->get_name()." (currently is ".($sourceinfo{allocation}/1048576)." MB and has a capacity of ".($sourceinfo{capacity}/1048576)."MB)",$callback,$node); + my $sourcevol = $hypconn->get_storage_volume_by_path($srcfilename); + my %sourceinfo = %{ $sourcevol->get_info() }; + my $targxml = "$filename" . $sourceinfo{capacity} . ""; + xCAT::SvrUtils::sendmsg("Cloning " . $sourcevol->get_name() . " (currently is " . ($sourceinfo{allocation} / 1048576) . " MB and has a capacity of " . ($sourceinfo{capacity} / 1048576) . "MB)", $callback, $node); eval { - $newvol =$destinationpool->clone_volume($targxml,$sourcevol); + $newvol = $destinationpool->clone_volume($targxml, $sourcevol); }; - if ($@) { - if ($@ =~ /already exists/) { - return 1,"Storage creation request conflicts with existing file(s)"; - } else { - return 1,"Unknown issue $@"; - } - } + if ($@) { + if ($@ =~ /already exists/) { + return 1, "Storage creation request conflicts with existing file(s)"; + } else { + return 1, "Unknown issue $@"; + } + } } else { - my $sourcevol = $hypconn->get_storage_volume_by_path($srcfilename); - my %sourceinfo = %{$sourcevol->get_info()}; - my $newbasexml="$filename".$sourceinfo{capacity}."$srcfilename"; - eval { - $newvol = $destinationpool->create_volume($newbasexml); - }; - if ($@) { - if ($@ =~ /already in use/) { - return 1,"Storage creation request conflicts with existing file(s)"; - } else { - return 1,"Unknown issue $@"; - } - } - $updatetable->{vm}->{$node}->{master}=$mastername; + my $sourcevol = $hypconn->get_storage_volume_by_path($srcfilename); + my %sourceinfo = %{ $sourcevol->get_info() }; + my $newbasexml = "$filename" . $sourceinfo{capacity} . "$srcfilename"; + eval { + $newvol = $destinationpool->create_volume($newbasexml); + }; + if ($@) { + if ($@ =~ /already in use/) { + return 1, "Storage creation request conflicts with existing file(s)"; + } else { + return 1, "Unknown issue $@"; + } + } + $updatetable->{vm}->{$node}->{master} = $mastername; } - my $newfilename=$newvol->get_path(); - $disk->findnodes("./source")->[0]->setAttribute("file"=>$newfilename); - if (not $detach) { #if we are a copied image, enable writethrough cache in order to reduce trips out to disk - #but if the format is not qcow2, still leave it at 'none' - my $type = $disk->findnodes("./driver")->[0]->getAttribute("type"); - if ($type eq "qcow2") { $disk->findnodes("./driver")->[0]->setAttribute("cache"=>"writethrough"); } - } - + my $newfilename = $newvol->get_path(); + $disk->findnodes("./source")->[0]->setAttribute("file" => $newfilename); + if (not $detach) { #if we are a copied image, enable writethrough cache in order to reduce trips out to disk + #but if the format is not qcow2, still leave it at 'none' + my $type = $disk->findnodes("./driver")->[0]->getAttribute("type"); + if ($type eq "qcow2") { $disk->findnodes("./driver")->[0]->setAttribute("cache" => "writethrough"); } + } + } - my $textxml=$newnodexml->toString(); - $updatetable->{kvm_nodedata}->{$node}->{xml}=$textxml; + my $textxml = $newnodexml->toString(); + $updatetable->{kvm_nodedata}->{$node}->{xml} = $textxml; } sub fixup_clone_network { - my %args = @_; - my $newnodexml = $args{xmlinprogress}; - my $mastername=$args{mastername}; - my $masteref=$args{mastertableentry}; - my $kvmmasteref=$args{kvmmastertableentry}; - unless (ref ($confdata->{vm}->{$node})) { - $confdata->{vm}->{$node}=[{nics=>$masteref->{nics}}]; - $updatetable->{vm}->{$node}->{nics}=$masteref->{nics}; + my %args = @_; + my $newnodexml = $args{xmlinprogress}; + my $mastername = $args{mastername}; + my $masteref = $args{mastertableentry}; + my $kvmmasteref = $args{kvmmastertableentry}; + unless (ref($confdata->{vm}->{$node})) { + $confdata->{vm}->{$node} = [ { nics => $masteref->{nics} } ]; + $updatetable->{vm}->{$node}->{nics} = $masteref->{nics}; } unless ($confdata->{vm}->{$node}->[0]->{nics}) { #if no nic configuration yet, take the one stored in the master - $confdata->{vm}->{$node}->[0]->{nics}=$masteref->{nics}; - $updatetable->{vm}->{$node}->{nics}=$masteref->{nics}; + $confdata->{vm}->{$node}->[0]->{nics} = $masteref->{nics}; + $updatetable->{vm}->{$node}->{nics} = $masteref->{nics}; } my @nics; if ($confdata->{vm}->{$node}->[0]->{nics}) { #could still be empty if it came from master that way - @nics = split /,/,$confdata->{vm}->{$node}->[0]->{nics}; + @nics = split /,/, $confdata->{vm}->{$node}->[0]->{nics}; } my @nicsinmaster = $newnodexml->findnodes("/domain/devices/interface"); if (scalar @nicsinmaster > scalar @nics) { #we don't have enough places to attach nics to.. - xCAT::SvrUtils::sendmsg([1,"KVM master $mastername has ".scalar @nicsinmaster." but this vm only has ".scalar @nics." defined"], $callback,$node); + xCAT::SvrUtils::sendmsg([ 1, "KVM master $mastername has " . scalar @nicsinmaster . " but this vm only has " . scalar @nics . " defined" ], $callback, $node); return; } my $nicstruct; - my @macs=xCAT::VMCommon::getMacAddresses($confdata,$node,scalar @nics); + my @macs = xCAT::VMCommon::getMacAddresses($confdata, $node, scalar @nics); foreach $nicstruct (@nicsinmaster) { my $bridge = shift @nics; $bridge =~ s/.*://; $bridge =~ s/=.*//; - $nicstruct->findnodes("./mac")->[0]->setAttribute("address"=>shift @macs); - $nicstruct->findnodes("./source")->[0]->setAttribute("bridge"=>$bridge); + $nicstruct->findnodes("./mac")->[0]->setAttribute("address" => shift @macs); + $nicstruct->findnodes("./source")->[0]->setAttribute("bridge" => $bridge); } my $nic; - my $deviceroot=$newnodexml->findnodes("/domain/devices")->[0]; - foreach $nic (@nics) { #need more xml to throw at it.. + my $deviceroot = $newnodexml->findnodes("/domain/devices")->[0]; + foreach $nic (@nics) { #need more xml to throw at it.. my $type = 'virtio'; #better default fake nic than rtl8139, relevant to most - $nic =~ s/.*://; #the detail of how the bridge was built is of no - #interest to this segment of code + $nic =~ s/.*://; #the detail of how the bridge was built is of no + #interest to this segment of code if ($confdata->{vm}->{$node}->[0]->{nicmodel}) { $type = $confdata->{vm}->{$node}->[0]->{nicmodel}; } if ($nic =~ /=/) { - ($nic,$type) = split /=/,$nic,2; + ($nic, $type) = split /=/, $nic, 2; } - my $xmlsnippet = ""; + my $xmlsnippet = ""; my $chunk = $parser->parse_balanced_chunk($xmlsnippet); $deviceroot->appendChild($chunk); } } + sub mkvm { - shift; #Throuw away first argument - @ARGV=@_; - my $disksize; - my $mastername; - my $force=0; - require Getopt::Long; - my $memory; - my $cpucount; - GetOptions( - 'master|m=s'=>\$mastername, - 'size|s=s'=>\$disksize, - "mem=s"=>\$memory, - "cpus=s" => \$cpucount, - 'force|f'=>\$force - ); - if (defined $confdata->{vm}->{$node}->[0]->{storage}) { - my $diskname=$confdata->{vm}->{$node}->[0]->{storage}; - if ($diskname =~ /^phy:/) { #in this case, mkvm should have no argumens + shift; #Throuw away first argument + @ARGV = @_; + my $disksize; + my $mastername; + my $force = 0; + require Getopt::Long; + my $memory; + my $cpucount; + GetOptions( + 'master|m=s' => \$mastername, + 'size|s=s' => \$disksize, + "mem=s" => \$memory, + "cpus=s" => \$cpucount, + 'force|f' => \$force + ); + if (defined $confdata->{vm}->{$node}->[0]->{storage}) { + my $diskname = $confdata->{vm}->{$node}->[0]->{storage}; + if ($diskname =~ /^phy:/) { #in this case, mkvm should have no argumens + if ($mastername or $disksize) { + return 1, "mkvm management of block device storage not implemented"; + } + } + + #print "force=$force\n"; + my @return; if ($mastername or $disksize) { - return 1,"mkvm management of block device storage not implemented"; + eval { + @return = createstorage($diskname, $mastername, $disksize, $confdata->{vm}->{$node}->[0], $force); + }; + if ($@) { + if ($@ =~ /ath already exists/) { + return 1, "Storage creation request conflicts with existing file(s)"; + } else { + return 1, "Unknown issue $@"; + } + } + + unless ($confdata->{kvmnodedata}->{$node} and $confdata->{kvmnodedata}->{$node}->[0] and $confdata->{kvmnodedata}->{$node}->[0]->{xml}) { + my $xml; + $xml = build_xmldesc($node, cpus => $cpucount, memory => $memory); + $updatetable->{kvm_nodedata}->{$node}->{xml} = $xml; + } + } + my $xml; + if ($confdata->{kvmnodedata}->{$node} and $confdata->{kvmnodedata}->{$node}->[0] and $confdata->{kvmnodedata}->{$node}->[0]->{xml}) { + $xml = $confdata->{kvmnodedata}->{$node}->[0]->{xml}; + } else { # ($confdata->{kvmnodedata}->{$node} and $confdata->{kvmnodedata}->{$node}->[0] and $confdata->{kvmnodedata}->{$node}->[0]->{xml}) { + $xml = build_xmldesc($node, cpus => $cpucount, memory => $memory); + $updatetable->{kvm_nodedata}->{$node}->{xml} = $xml; + } + if ($::XCATSITEVALS{persistkvmguests}) { + $hypconn->define_domain($xml); + } + return @return; + } else { + if ($mastername or $disksize) { + return 1, "Requested initialization of storage, but vm.storage has no value for node"; } } - #print "force=$force\n"; - my @return; - if ($mastername or $disksize) { - eval { - @return = createstorage($diskname,$mastername,$disksize,$confdata->{vm}->{$node}->[0],$force); - }; - if ($@) { - if ($@ =~ /ath already exists/) { - return 1,"Storage creation request conflicts with existing file(s)"; - } else { - return 1,"Unknown issue $@"; - } - } - - unless ($confdata->{kvmnodedata}->{$node} and $confdata->{kvmnodedata}->{$node}->[0] and $confdata->{kvmnodedata}->{$node}->[0]->{xml}) { - my $xml; - $xml = build_xmldesc($node,cpus=>$cpucount,memory=>$memory); - $updatetable->{kvm_nodedata}->{$node}->{xml}=$xml; - } - } - my $xml; - if ($confdata->{kvmnodedata}->{$node} and $confdata->{kvmnodedata}->{$node}->[0] and $confdata->{kvmnodedata}->{$node}->[0]->{xml}) { - $xml = $confdata->{kvmnodedata}->{$node}->[0]->{xml}; - } else { # ($confdata->{kvmnodedata}->{$node} and $confdata->{kvmnodedata}->{$node}->[0] and $confdata->{kvmnodedata}->{$node}->[0]->{xml}) { - $xml = build_xmldesc($node,cpus=>$cpucount,memory=>$memory); - $updatetable->{kvm_nodedata}->{$node}->{xml}=$xml; - } - if ($::XCATSITEVALS{persistkvmguests}) { - $hypconn->define_domain($xml); - } - return @return; - } else { - if ($mastername or $disksize) { - return 1,"Requested initialization of storage, but vm.storage has no value for node"; - } - } } + sub power { - @ARGV=@_; + @ARGV = @_; require Getopt::Long; my $cdloc; - GetOptions('cdrom|iso|c|i=s'=>\$cdloc); + GetOptions('cdrom|iso|c|i=s' => \$cdloc); my $subcommand = shift @ARGV; my $retstring; my $dom; eval { - $dom = $hypconn->get_domain_by_name($node); + $dom = $hypconn->get_domain_by_name($node); }; if ($subcommand eq "boot") { - my $currstate=getpowstate($dom); - $retstring=$currstate." "; + my $currstate = getpowstate($dom); + $retstring = $currstate . " "; if ($currstate eq "off") { - $subcommand="on"; + $subcommand = "on"; } elsif ($currstate eq "on") { - $subcommand="reset"; + $subcommand = "reset"; } } my $errstr; if ($subcommand eq 'on') { unless ($dom) { if ($use_xhrm) { - if (xhrm_satisfy($node,$hyp)) { - return (1,"Failure satisfying networking and storage requirements on $hyp for $node"); - } + if (xhrm_satisfy($node, $hyp)) { + return (1, "Failure satisfying networking and storage requirements on $hyp for $node"); + } } -#TODO: here, storage validation is not necessarily performed, consequently, must explicitly do storage validation -#this worked before I started doing the offline xml store because every rpower on tried to rebuild - ($dom,$errstr) = makedom($node,$cdloc); - if ($errstr) { return (1,$errstr); } + + #TODO: here, storage validation is not necessarily performed, consequently, must explicitly do storage validation + #this worked before I started doing the offline xml store because every rpower on tried to rebuild + ($dom, $errstr) = makedom($node, $cdloc); + if ($errstr) { return (1, $errstr); } } elsif (not $dom->is_active()) { - $dom->create(); + $dom->create(); } else { - $retstring .= "$status_noop"; + $retstring .= "$status_noop"; } } elsif ($subcommand eq 'off') { if ($dom) { - my $newxml=$dom->get_xml_description(); - $updatetable->{kvm_nodedata}->{$node}->{xml}=$newxml; - if ($dom->is_active()) { - $dom->destroy(); - } + my $newxml = $dom->get_xml_description(); + $updatetable->{kvm_nodedata}->{$node}->{xml} = $newxml; + if ($dom->is_active()) { + $dom->destroy(); + } undef $dom; } else { $retstring .= "$status_noop"; } } elsif ($subcommand eq 'softoff') { if ($dom) { - my $newxml=$dom->get_xml_description(); - $updatetable->{kvm_nodedata}->{$node}->{xml}=$newxml; + my $newxml = $dom->get_xml_description(); + $updatetable->{kvm_nodedata}->{$node}->{xml} = $newxml; $dom->shutdown(); - } else { $retstring .= "$status_noop"; } + } else { $retstring .= "$status_noop"; } } elsif ($subcommand eq 'reset') { if ($dom && $dom->is_active()) { - my $oldxml=$dom->get_xml_description(); - my $newxml=reconfigvm($node,$oldxml); + my $oldxml = $dom->get_xml_description(); + my $newxml = reconfigvm($node, $oldxml); + #This *was* to be clever, but libvirt doesn't even frontend the capability, great... - unless ($newxml) { $newxml=$oldxml; } #TODO: remove this when the 'else' line can be sanely filled out - if ($newxml) { #need to destroy and repower.. - $updatetable->{kvm_nodedata}->{$node}->{xml}=$newxml; - my $persist = $dom->is_persistent(); + unless ($newxml) { $newxml = $oldxml; } #TODO: remove this when the 'else' line can be sanely filled out + if ($newxml) { #need to destroy and repower.. + $updatetable->{kvm_nodedata}->{$node}->{xml} = $newxml; + my $persist = $dom->is_persistent(); $dom->destroy(); - if ($persist) { $dom->undefine(); } + if ($persist) { $dom->undefine(); } undef $dom; if ($use_xhrm) { - xhrm_satisfy($node,$hyp); + xhrm_satisfy($node, $hyp); } - ($dom,$errstr) = makedom($node,$cdloc,$newxml); - if ($errstr) { return (1,$errstr); } + ($dom, $errstr) = makedom($node, $cdloc, $newxml); + if ($errstr) { return (1, $errstr); } } else { #no changes, just restart the domain TODO when possible, stupid lack of feature... } - $retstring.="reset"; - } else { $retstring .= "$status_noop"; } - } else { + $retstring .= "reset"; + } else { $retstring .= "$status_noop"; } + } else { unless ($subcommand =~ /^stat/) { - return (1,"Unsupported power directive '$subcommand'"); + return (1, "Unsupported power directive '$subcommand'"); } } unless ($retstring =~ /reset/) { - $retstring=$retstring.getpowstate($dom); + $retstring = $retstring . getpowstate($dom); } - return (0,$retstring); + return (0, $retstring); } sub lsvm { @@ -2775,39 +3021,40 @@ sub lsvm { my @doms = $hypconn->list_domains(); my @vms; foreach (@doms) { - push @vms,$_->get_name(); + push @vms, $_->get_name(); } - return (0,@vms); + return (0, @vms); } 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($node,@args); - } elsif ($command eq "clonevm") { - return clonevm($node,@args); - } elsif ($command eq "chvm") { - return chvm($node,@args); - } elsif ($command eq "rmvm") { - return rmvm($node,@args); - } elsif ($command eq "rinv") { - return rinv($node,@args); - } elsif ($command eq "rmigrate") { - return migrate($node,@args); - } elsif ($command eq "getrvidparms") { - return getrvidparms($node,@args); - } elsif ($command eq "getcons") { - return getcons($node,@args); - } elsif ($command eq "lsvm") { - return lsvm($node,@args); - } + $hyp = shift; + $node = shift; + my $command = shift; + my @args = @_; + my $error; + if ($command eq "rpower") { + return power(@args); + } elsif ($command eq "mkvm") { + return mkvm($node, @args); + } elsif ($command eq "clonevm") { + return clonevm($node, @args); + } elsif ($command eq "chvm") { + return chvm($node, @args); + } elsif ($command eq "rmvm") { + return rmvm($node, @args); + } elsif ($command eq "rinv") { + return rinv($node, @args); + } elsif ($command eq "rmigrate") { + return migrate($node, @args); + } elsif ($command eq "getrvidparms") { + return getrvidparms($node, @args); + } elsif ($command eq "getcons") { + return getcons($node, @args); + } elsif ($command eq "lsvm") { + return lsvm($node, @args); + } + =cut } elsif ($command eq "rvitals") { return vitals(@args); @@ -2830,631 +3077,648 @@ sub guestcmd { } =cut - return (1,"$command not a supported command by kvm method"); + + return (1, "$command not a supported command by kvm method"); } -sub preprocess_request { - my $request = shift; - if ($request->{_xcatpreprocessed}->[0] == 1) { return [$request]; } - my $callback=shift; - my @requests; +sub preprocess_request { + my $request = shift; + 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 $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; - } + 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"; + if (!$noderange) { + $usage_string = xCAT::Usage->getUsage($command); + $callback->({ data => $usage_string }); + $request = {}; + return; + } - # 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"); + #print "noderange=@$noderange\n"; - # build each request for each service node + # 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"); - 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; + # 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 { - my $orphash = shift; - my $hyphash = shift; + my $orphash = shift; + my $hyphash = shift; my %addmemory = (); my $node; my $target; my $vmupdates; foreach $node (keys %{$orphash}) { - $target=pick_target($node,\%addmemory); + $target = pick_target($node, \%addmemory); unless ($target) { next; } if ($confdata->{vm}->{$node}->[0]->{memory}) { - $addmemory{$target}+=getUnits($confdata->{vm}->{$node}->[0]->{memory},"M",1024); + $addmemory{$target} += getUnits($confdata->{vm}->{$node}->[0]->{memory}, "M", 1024); } else { - $addmemory{$target}+=getUnits("4096","M",1024); + $addmemory{$target} += getUnits("4096", "M", 1024); } - $hyphash{$target}->{nodes}->{$node}=1; + $hyphash{$target}->{nodes}->{$node} = 1; delete $orphash->{$node}; - $vmupdates->{$node}->{host}=$target; + $vmupdates->{$node}->{host} = $target; } - unless ($vmtab) { $vmtab = new xCAT::Table('vm',-create=>1); } + unless ($vmtab) { $vmtab = new xCAT::Table('vm', -create => 1); } $vmtab->setNodesAttribs($vmupdates); if (keys %{$orphash}) { return 0; - } else { + } else { return 1; } } -sub process_request { - $SIG{INT} = $SIG{TERM} = sub { - foreach (keys %vm_comm_pids) { - kill 2, $_; - } - exit 0; - }; - unless ($parser) { - $parser = XML::LibXML->new(); - } - %offlinehyps=(); - %hypstats=(); - %offlinevms=(); - my $request = shift; - if ($request->{_xcat_authname}->[0]) { - $requester=$request->{_xcat_authname}->[0]; - } - $callback = shift; - require Sys::Virt; - if ( xCAT::Utils::version_cmp(Sys::Virt->VERSION, "0.2.0") < 0 ) { - die; - } - 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}); - } - my $forcemode = 0; - my %orphans=(); - if ($command eq 'vmstatenotify') { - unless ($vmtab) { $vmtab = new xCAT::Table('vm',-create=>1); } - my $state = $exargs[0]; - if ($state eq 'vmoff') { - $vmtab->setNodeAttribs($exargs[1],{powerstate=>'off'}); - return; - } elsif ($state eq 'vmon') { - $vmtab->setNodeAttribs($exargs[1],{powerstate=>'on'}); - return; - } elsif ($state eq 'hypshutdown') { #turn this into an evacuate - my $nodelisttab = xCAT::Table->new('nodelist'); - my $appstatus = $nodelisttab->getNodeAttribs($noderange->[0],['appstatus']); - my @apps =split /,/,$appstatus->{'appstatus'}; - my @newapps; - foreach (@apps) { - if ($_ eq 'virtualization') { next; } - push @newapps,$_; - } - $nodelisttab->setNodeAttribs($noderange->[0],{appstatus=>join(',',@newapps)}); - $command="revacuate"; - @exargs=(); - } elsif ($state eq 'hypstartup') { #if starting up, check for nodes on this hypervisor and start them up - my $nodelisttab = xCAT::Table->new('nodelist'); - my $appstatus = $nodelisttab->getNodeAttribs($noderange->[0],['appstatus']); - my @apps =split /,/,$appstatus->{appstatus}; - unless (grep {$_ eq 'virtualization'} @apps) { - push @apps,'virtualization'; - $nodelisttab->setNodeAttribs($noderange->[0],{appstatus=>join(',',@apps)}); - } - my @tents = $vmtab->getAttribs({host=>$noderange->[0],power=>'on'},['node']); - $noderange=[]; - foreach (@tents) { - push @$noderange,noderange($_->{node}); - } - $command="rpower"; - @exargs=("on"); - } - - } - if ($command eq 'revacuate') { - my $newnoderange; - if (grep { $_ eq '-f' } @exargs) { - $forcemode=1; - } - foreach (@$noderange) { - my $hyp = $_; #I used $_ too much here... sorry - $hypconn=undef; - push @destblacklist,$_; - if ((not $offlinehyps{$_}) and nodesockopen($_,22)) { - eval { #Contain bugs that won't be in $@ - $hypconn= Sys::Virt->new(uri=>"qemu+ssh://root@".$_."/system?no_tty=1&netcat=nc"); - }; - unless ($hypconn) { #retry for socat - eval { #Contain bugs that won't be in $@ - $hypconn= Sys::Virt->new(uri=>"qemu+ssh://root@".$_."/system?no_tty=1"); - }; - } +sub process_request { + $SIG{INT} = $SIG{TERM} = sub { + foreach (keys %vm_comm_pids) { + kill 2, $_; } - unless ($hypconn) { - $offlinehyps{$hyp}=1; - if ($forcemode) { #forcemode indicates the hypervisor is probably already dead, and to clear vm.host of all the nodes, and adopt the ones that are supposed to be 'on', power them on - unless ($vmtab) { $vmtab = new xCAT::Table('vm',-create=>0); } - unless ($vmtab) { next; } - my @vents = $vmtab->getAttribs({host=>$hyp},['node','powerstate']); - my $vent; - my $nodestozap; - foreach $vent (@vents) { - my @nodes = noderange($vent->{node}); - if ($vent->{powerstate} eq 'on') { - foreach (@nodes) { - $offlinevms{$_}=1; - $orphans{$_}=1; - push @$newnoderange,$_; - } - } - push @$nodestozap,@nodes; - } - $vmtab->setNodesAttribs($nodestozap,{host=>'|^.*$||'}); - } else { - $callback->({node=>[{name=>[$_],error=>["Cannot communicate via libvirt to node"]}]}); - } - next; - } - if ($hypconn) { - foreach ($hypconn->list_domains()) { - my $guestname = $_->get_name(); - if ($guestname eq 'Domain-0') { - next; - } - push @$newnoderange,$guestname; - } - } - } - $hypconn=undef; - $noderange = $newnoderange; - $command = 'rmigrate'; - } + exit 0; + }; + unless ($parser) { + $parser = XML::LibXML->new(); + } + %offlinehyps = (); + %hypstats = (); + %offlinevms = (); + my $request = shift; + if ($request->{_xcat_authname}->[0]) { + $requester = $request->{_xcat_authname}->[0]; + } + $callback = shift; + require Sys::Virt; + if (xCAT::Utils::version_cmp(Sys::Virt->VERSION, "0.2.0") < 0) { + die; + } + require Sys::Virt::Domain; + %runningstates = (&Sys::Virt::Domain::STATE_NOSTATE => 1, &Sys::Virt::Domain::STATE_RUNNING => 1, &Sys::Virt::Domain::STATE_BLOCKED => 1); - if ($::XCATSITEVALS{usexhrm}) { $use_xhrm=1; } - $vmtab = xCAT::Table->new("vm"); - $confdata={}; - unless ($command eq 'lsvm') { - xCAT::VMCommon::grab_table_data($noderange,$confdata,$callback); - my $kvmdatatab = xCAT::Table->new("kvm_nodedata",-create=>0); #grab any pertinent pre-existing xml - if ($kvmdatatab) { - $confdata->{kvmnodedata} = $kvmdatatab->getNodesAttribs($noderange,[qw/xml/]); - } else { - $confdata->{kvmnodedata} = {}; - } - } - if ($command eq 'mkvm' or ($command eq 'clonevm' and (grep { "$_" eq '-b' } @exargs)) or ($command eq 'rpower' and (grep { "$_" eq "on" or $_ eq "boot" or $_ eq "reset" } @exargs))) { - xCAT::VMCommon::requestMacAddresses($confdata,$noderange); - my @dhcpnodes; - foreach (keys %{$confdata->{dhcpneeded}}) { - push @dhcpnodes,$_; - delete $confdata->{dhcpneeded}->{$_}; - } - unless ($::XCATSITEVALS{'dhcpsetup'} and ($::XCATSITEVALS{'dhcpsetup'} =~ /^n/i or $::XCATSITEVALS{'dhcpsetup'} =~ /^d/i or $::XCATSITEVALS{'dhcpsetup'} eq '0')) { - $doreq->({command=>['makedhcp'],node=>\@dhcpnodes}); - } - } - - if ($command eq 'revacuate' or $command eq 'rmigrate') { - $vmmaxp=1; #for now throttle concurrent migrations, requires more sophisticated heuristics to ensure sanity - } else { - my $tmp; - if ($::XCATSITEVALS{vmmaxp}) { $vmmaxp=$::XCATSITEVALS{vmmaxp}; } - } - - 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=(); - if ($command eq 'lsvm') { #command intended for hypervisors, not guests - foreach(@$noderange) { $hyphash{$_}->{nodes}->{$_}=1; } - } else { - foreach (keys %{$confdata->{vm}}) { - if ($confdata->{vm}->{$_}->[0]->{host}) { - $hyphash{$confdata->{vm}->{$_}->[0]->{host}}->{nodes}->{$_}=1; - } else { - $orphans{$_}=1; - } - } - } - if (keys %orphans) { - if ($command eq "rpower") { - if (grep /^on$/,@exargs or grep /^boot$/,@exargs) { - unless (adopt(\%orphans,\%hyphash)) { - $callback->({error=>"Can't find ".join(",",keys %orphans),errorcode=>[1]}); - return 1; - } - } else { - foreach (keys %orphans) { - $callback->({node=>[{name=>[$_],data=>[{contents=>['off']}]}]}); - } - } - } elsif ($command eq "rmigrate") { - if ($forcemode) { - unless (adopt(\%orphans,\%hyphash)) { - $callback->({error=>"Can't find ".join(",",keys %orphans),errorcode=>[1]}); - return 1; - } - } else { - $callback->({error=>"Can't find ".join(",",keys %orphans),errorcode=>[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}); + } + my $forcemode = 0; + my %orphans = (); + if ($command eq 'vmstatenotify') { + unless ($vmtab) { $vmtab = new xCAT::Table('vm', -create => 1); } + my $state = $exargs[0]; + if ($state eq 'vmoff') { + $vmtab->setNodeAttribs($exargs[1], { powerstate => 'off' }); return; - } - } elsif ($command eq "mkvm" or $command eq "clonevm") { #must adopt to create - unless (adopt(\%orphans,\%hyphash)) { - $callback->({error=>"Can't find ".join(",",keys %orphans),errorcode=>[1]}); - return 1; - } - #mkvm used to be able to happen devoid of any hypervisor, make a fake hypervisor entry to allow this to occur - #commenting that out for now -# foreach (keys %orphans) { -# $hyphash{'!@!XCATDUMMYHYPERVISOR!@!'}->{nodes}->{$_}=1; -# } - } 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; - if ($::XCATSITEVALS{nodestatus} =~ /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}); - } + } elsif ($state eq 'vmon') { + $vmtab->setNodeAttribs($exargs[1], { powerstate => 'on' }); + return; + } elsif ($state eq 'hypshutdown') { #turn this into an evacuate + my $nodelisttab = xCAT::Table->new('nodelist'); + my $appstatus = $nodelisttab->getNodeAttribs($noderange->[0], ['appstatus']); + my @apps = split /,/, $appstatus->{'appstatus'}; + my @newapps; + foreach (@apps) { + if ($_ eq 'virtualization') { next; } + push @newapps, $_; + } + $nodelisttab->setNodeAttribs($noderange->[0], { appstatus => join(',', @newapps) }); + $command = "revacuate"; + @exargs = (); + } elsif ($state eq 'hypstartup') { #if starting up, check for nodes on this hypervisor and start them up + my $nodelisttab = xCAT::Table->new('nodelist'); + my $appstatus = $nodelisttab->getNodeAttribs($noderange->[0], ['appstatus']); + my @apps = split /,/, $appstatus->{appstatus}; + unless (grep { $_ eq 'virtualization' } @apps) { + push @apps, 'virtualization'; + $nodelisttab->setNodeAttribs($noderange->[0], { appstatus => join(',', @apps) }); + } + my @tents = $vmtab->getAttribs({ host => $noderange->[0], power => 'on' }, ['node']); + $noderange = []; + foreach (@tents) { + push @$noderange, noderange($_->{node}); + } + $command = "rpower"; + @exargs = ("on"); } - } - #donot update node provision status (installing or netbooting) here - xCAT::Utils->filter_nostatusupdate(\%newnodestatus); - #print "newstatus" . Dumper(\%newnodestatus); - xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%newnodestatus, 1); + } - } - - if ($::XCATSITEVALS{masterimgdir}) { $xCAT_plugin::kvm::masterdir=$::XCATSITEVALS{masterimgdir} } - - - - 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); - } + if ($command eq 'revacuate') { + my $newnoderange; + if (grep { $_ eq '-f' } @exargs) { + $forcemode = 1; + } + foreach (@$noderange) { + my $hyp = $_; #I used $_ too much here... sorry + $hypconn = undef; + push @destblacklist, $_; + if ((not $offlinehyps{$_}) and nodesockopen($_, 22)) { + eval { #Contain bugs that won't be in $@ + $hypconn = Sys::Virt->new(uri => "qemu+ssh://root@" . $_ . "/system?no_tty=1&netcat=nc"); + }; + unless ($hypconn) { #retry for socat + eval { #Contain bugs that won't be in $@ + $hypconn = Sys::Virt->new(uri => "qemu+ssh://root@" . $_ . "/system?no_tty=1"); + }; + } + } + unless ($hypconn) { + $offlinehyps{$hyp} = 1; + if ($forcemode) { #forcemode indicates the hypervisor is probably already dead, and to clear vm.host of all the nodes, and adopt the ones that are supposed to be 'on', power them on + unless ($vmtab) { $vmtab = new xCAT::Table('vm', -create => 0); } + unless ($vmtab) { next; } + my @vents = $vmtab->getAttribs({ host => $hyp }, [ 'node', 'powerstate' ]); + my $vent; + my $nodestozap; + foreach $vent (@vents) { + my @nodes = noderange($vent->{node}); + if ($vent->{powerstate} eq 'on') { + foreach (@nodes) { + $offlinevms{$_} = 1; + $orphans{$_} = 1; + push @$newnoderange, $_; + } + } + push @$nodestozap, @nodes; + } + $vmtab->setNodesAttribs($nodestozap, { host => '|^.*$||' }); + } else { + $callback->({ node => [ { name => [$_], error => ["Cannot communicate via libvirt to node"] } ] }); + } + next; + } + if ($hypconn) { + foreach ($hypconn->list_domains()) { + my $guestname = $_->get_name(); + if ($guestname eq 'Domain-0') { + next; + } + push @$newnoderange, $guestname; + } + } + } + $hypconn = undef; + $noderange = $newnoderange; + $command = 'rmigrate'; } - $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); + + if ($::XCATSITEVALS{usexhrm}) { $use_xhrm = 1; } + $vmtab = xCAT::Table->new("vm"); + $confdata = {}; + unless ($command eq 'lsvm') { + xCAT::VMCommon::grab_table_data($noderange, $confdata, $callback); + my $kvmdatatab = xCAT::Table->new("kvm_nodedata", -create => 0); #grab any pertinent pre-existing xml + if ($kvmdatatab) { + $confdata->{kvmnodedata} = $kvmdatatab->getNodesAttribs($noderange, [qw/xml/]); + } else { + $confdata->{kvmnodedata} = {}; + } } - $vm_comm_pids{$cpid} = 1; - close ($pfd); - $sub_fds->add($cfd); - } - while ($sub_fds->count > 0) { # or $children > 0) { #if count is zero, even if we have live children, we can't possibly get data from them - my $handlednodes={}; - forward_data($callback,$sub_fds,$handlednodes); - #update the node status to the nodelist.status table + if ($command eq 'mkvm' or ($command eq 'clonevm' and (grep { "$_" eq '-b' } @exargs)) or ($command eq 'rpower' and (grep { "$_" eq "on" or $_ eq "boot" or $_ eq "reset" } @exargs))) { + xCAT::VMCommon::requestMacAddresses($confdata, $noderange); + my @dhcpnodes; + foreach (keys %{ $confdata->{dhcpneeded} }) { + push @dhcpnodes, $_; + delete $confdata->{dhcpneeded}->{$_}; + } + unless ($::XCATSITEVALS{'dhcpsetup'} and ($::XCATSITEVALS{'dhcpsetup'} =~ /^n/i or $::XCATSITEVALS{'dhcpsetup'} =~ /^d/i or $::XCATSITEVALS{'dhcpsetup'} eq '0')) { + $doreq->({ command => ['makedhcp'], node => \@dhcpnodes }); + } + } + + if ($command eq 'revacuate' or $command eq 'rmigrate') { + $vmmaxp = 1; #for now throttle concurrent migrations, requires more sophisticated heuristics to ensure sanity + } else { + my $tmp; + if ($::XCATSITEVALS{vmmaxp}) { $vmmaxp = $::XCATSITEVALS{vmmaxp}; } + } + + 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 = (); + if ($command eq 'lsvm') { #command intended for hypervisors, not guests + foreach (@$noderange) { $hyphash{$_}->{nodes}->{$_} = 1; } + } else { + foreach (keys %{ $confdata->{vm} }) { + if ($confdata->{vm}->{$_}->[0]->{host}) { + $hyphash{ $confdata->{vm}->{$_}->[0]->{host} }->{nodes}->{$_} = 1; + } else { + $orphans{$_} = 1; + } + } + } + if (keys %orphans) { + if ($command eq "rpower") { + if (grep /^on$/, @exargs or grep /^boot$/, @exargs) { + unless (adopt(\%orphans, \%hyphash)) { + $callback->({ error => "Can't find " . join(",", keys %orphans), errorcode => [1] }); + return 1; + } + } else { + foreach (keys %orphans) { + $callback->({ node => [ { name => [$_], data => [ { contents => ['off'] } ] } ] }); + } + } + } elsif ($command eq "rmigrate") { + if ($forcemode) { + unless (adopt(\%orphans, \%hyphash)) { + $callback->({ error => "Can't find " . join(",", keys %orphans), errorcode => [1] }); + return 1; + } + } else { + $callback->({ error => "Can't find " . join(",", keys %orphans), errorcode => [1] }); + return; + } + } elsif ($command eq "mkvm" or $command eq "clonevm") { #must adopt to create + unless (adopt(\%orphans, \%hyphash)) { + $callback->({ error => "Can't find " . join(",", keys %orphans), errorcode => [1] }); + return 1; + } + + #mkvm used to be able to happen devoid of any hypervisor, make a fake hypervisor entry to allow this to occur + #commenting that out for now + # foreach (keys %orphans) { + # $hyphash{'!@!XCATDUMMYHYPERVISOR!@!'}->{nodes}->{$_}=1; + # } + } 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; + if ($::XCATSITEVALS{nodestatus} =~ /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); + } + } + + if ($::XCATSITEVALS{masterimgdir}) { $xCAT_plugin::kvm::masterdir = $::XCATSITEVALS{masterimgdir} } + + + + 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) { #if count is zero, even if we have live children, we can't possibly get data from them + my $handlednodes = {}; + forward_data($callback, $sub_fds, $handlednodes); + + #update the node status to the nodelist.status table + if ($check) { + updateNodeStatus($handlednodes, \@allerrornodes); + } + } + + #while (wait() > -1) { } #keep around just in case we find the absolute need to wait for children to be gone + + #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) { - updateNodeStatus($handlednodes, \@allerrornodes); + + #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); } - } - #while (wait() > -1) { } #keep around just in case we find the absolute need to wait for children to be gone - - #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); } - } + 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; - my $responses; - eval { - $responses = fd_retrieve($rfh); - }; - if ($@ and $@ =~ /^Magic number checking on storable file/) { #this most likely means we ran over the end of available input - $fds->remove($rfh); - close($rfh); - } else { - eval { print $rfh "ACK\n"; }; #ignore failures to send inter-process ack - 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->($_); - } + 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; + my $responses; + eval { + $responses = fd_retrieve($rfh); + }; + if ($@ and $@ =~ /^Magic number checking on storable file/) { #this most likely means we ran over the end of available input + $fds->remove($rfh); + close($rfh); + } else { + eval { print $rfh "ACK\n"; }; #ignore failures to send inter-process ack + 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->($_); + } + } } - } - yield(); #Try to avoid useless iterations as much as possible - return $rc; + yield(); #Try to avoid useless iterations as much as possible + 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"); - $vmtab = undef; - unless ($offlinehyps{$hyp} or ($hyp eq '!@!XCATDUMMYHYPERVISOR!@!') or nodesockopen($hyp,22)) { - $offlinehyps{$hyp}=1; - } + my $out = shift; + $hyp = shift; + my $command = shift; + my %namedargs = @_; + my @exargs = @{ $namedargs{-args} }; + my $node; + my $args = \@exargs; - - eval { #Contain Sys::Virt bugs that make $@ useless - if ($hyp eq '!@!XCATDUMMYHYPERVISOR!@!') { #Fake connection for commands that have a fake hypervisor key - $hypconn = 1; - } elsif (not $offlinehyps{$hyp}) { - $hypconn= Sys::Virt->new(uri=>"qemu+ssh://root@".$hyp."/system?no_tty=1&netcat=nc"); - } - }; - unless ($hypconn or $offlinehyps{$hyp}) { - eval { #Contain Sys::Virt bugs that make $@ useless - $hypconn= Sys::Virt->new(uri=>"qemu+ssh://root@".$hyp."/system?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]}); - } - store_fd([\%err],$out); - yield(); - waitforack($out); - %err=(node=>[]); - if ($command eq 'rmigrate' and grep { $_ eq '-f' } @$args) { - foreach (keys %{$hyphash{$hyp}->{nodes}}) { - push (@{$err{node}},{name=>[$_],error=>["Forcibly relocating VM from $hyp"],errorcode=>[1]}); - } - store_fd([\%err],$out); - } else { - return 1,"General error establishing libvirt communication"; - } - } - if (($command eq 'mkvm' or $command eq 'chvm') and $hypconn) { - my $nodeinfo = $hypconn->get_node_info(); - if (exists($nodeinfo->{model})) { - $confdata->{$hyp}->{cpumodel} = $nodeinfo->{model}; - if ($nodeinfo->{model} eq "ppc64") { - my $sysinfo = $hypconn->get_sysinfo(); - if ($sysinfo) { - my $syshash = XMLin($sysinfo); - my $processor_content = $syshash->{processor}->[0]->{entry}->{type}->{content}; - if ($processor_content =~ /POWER8/i) { - $confdata->{$hyp}->{cputype} = "power8"; - $confdata->{$hyp}->{cpu_thread} = "8"; - } elsif ($processor_content =~ /POWER7/i) { - $confdata->{$hyp}->{cputype} = "power7"; - $confdata->{$hyp}->{cpu_thread} = "4"; - } elsif ($processor_content =~ /POWER6/i) { - $confdata->{$hyp}->{cputype} = "power6"; - $confdata->{$hyp}->{cpu_thread} = "2"; - } - } - } - } - } - - foreach $node (sort (keys %{$hyphash{$hyp}->{nodes}})) { - if ($confdata->{$hyp}->{cpumodel} and $confdata->{$hyp}->{cpumodel} =~ /ppc64/i) { - $confdata->{vm}->{$node}->[0]->{storagemodel} = "scsi"; - } - if ($confdata->{$hyp}->{cpu_thread}) { - $confdata->{vm}->{$node}->[0]->{cpu_thread} = $confdata->{$hyp}->{cpu_thread}; - } - if ($confdata->{$hyp}->{cputype}) { - $confdata->{vm}->{$node}->[0]->{cputype} = $confdata->{$hyp}->{cputype}; + #$vmtab = xCAT::Table->new("vm"); + $vmtab = undef; + unless ($offlinehyps{$hyp} or ($hyp eq '!@!XCATDUMMYHYPERVISOR!@!') or nodesockopen($hyp, 22)) { + $offlinehyps{$hyp} = 1; } - my ($rc,@output) = guestcmd($hyp,$node,$command,@$args); - foreach(@output) { - my %output; - if (ref($_)) { - store_fd([$_],$out); - 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; + eval { #Contain Sys::Virt bugs that make $@ useless + if ($hyp eq '!@!XCATDUMMYHYPERVISOR!@!') { #Fake connection for commands that have a fake hypervisor key + $hypconn = 1; + } elsif (not $offlinehyps{$hyp}) { + $hypconn = Sys::Virt->new(uri => "qemu+ssh://root@" . $hyp . "/system?no_tty=1&netcat=nc"); } - } - $text =~ s/^\s+//; - $text =~ s/\s+$//; - $output{node}->[0]->{errorcode} = $rc; - $output{node}->[0]->{name}->[0]=$node; - if ($rc == 0) { - $output{node}->[0]->{data}->[0]->{contents}->[0]=$text; - } else { - $output{node}->[0]->{error} = $text; - } - store_fd([\%output],$out); - yield(); - waitforack($out); + }; + unless ($hypconn or $offlinehyps{$hyp}) { + eval { #Contain Sys::Virt bugs that make $@ useless + $hypconn = Sys::Virt->new(uri => "qemu+ssh://root@" . $hyp . "/system?no_tty=1"); + }; } - yield(); - } - foreach (keys %$updatetable) { - my $tabhandle = xCAT::Table->new($_,-create=>1); - my $updates = $updatetable->{$_}; - if ($updates->{'!*XCATNODESTODELETE*!'}) { - my @delkeys; - foreach (keys %{$updates->{'!*XCATNODESTODELETE*!'}}) { - if ($_) { push @delkeys, {node=>$_}; } - } - if (@delkeys) { $tabhandle->delEntries(\@delkeys); } - delete $updates->{'!*XCATNODESTODELETE*!'}; - } - $tabhandle->setNodesAttribs($updatetable->{$_}); - } - #my $msgtoparent=freeze(\@outhashes); # = XMLout(\%output,RootName => 'xcatresponse'); - #print $out $msgtoparent; #$node.": $_\n"; + unless ($hypconn) { + my %err = (node => []); + foreach (keys %{ $hyphash{$hyp}->{nodes} }) { + push(@{ $err{node} }, { name => [$_], error => ["Cannot communicate via libvirt to $hyp"], errorcode => [1] }); + } + store_fd([ \%err ], $out); + yield(); + waitforack($out); + %err = (node => []); + if ($command eq 'rmigrate' and grep { $_ eq '-f' } @$args) { + foreach (keys %{ $hyphash{$hyp}->{nodes} }) { + push(@{ $err{node} }, { name => [$_], error => ["Forcibly relocating VM from $hyp"], errorcode => [1] }); + } + store_fd([ \%err ], $out); + } else { + return 1, "General error establishing libvirt communication"; + } + } + if (($command eq 'mkvm' or $command eq 'chvm') and $hypconn) { + my $nodeinfo = $hypconn->get_node_info(); + if (exists($nodeinfo->{model})) { + $confdata->{$hyp}->{cpumodel} = $nodeinfo->{model}; + if ($nodeinfo->{model} eq "ppc64") { + my $sysinfo = $hypconn->get_sysinfo(); + if ($sysinfo) { + my $syshash = XMLin($sysinfo); + my $processor_content = $syshash->{processor}->[0]->{entry}->{type}->{content}; + if ($processor_content =~ /POWER8/i) { + $confdata->{$hyp}->{cputype} = "power8"; + $confdata->{$hyp}->{cpu_thread} = "8"; + } elsif ($processor_content =~ /POWER7/i) { + $confdata->{$hyp}->{cputype} = "power7"; + $confdata->{$hyp}->{cpu_thread} = "4"; + } elsif ($processor_content =~ /POWER6/i) { + $confdata->{$hyp}->{cputype} = "power6"; + $confdata->{$hyp}->{cpu_thread} = "2"; + } + } + } + } + } + + foreach $node (sort (keys %{ $hyphash{$hyp}->{nodes} })) { + if ($confdata->{$hyp}->{cpumodel} and $confdata->{$hyp}->{cpumodel} =~ /ppc64/i) { + $confdata->{vm}->{$node}->[0]->{storagemodel} = "scsi"; + } + if ($confdata->{$hyp}->{cpu_thread}) { + $confdata->{vm}->{$node}->[0]->{cpu_thread} = $confdata->{$hyp}->{cpu_thread}; + } + if ($confdata->{$hyp}->{cputype}) { + $confdata->{vm}->{$node}->[0]->{cputype} = $confdata->{$hyp}->{cputype}; + } + + my ($rc, @output) = guestcmd($hyp, $node, $command, @$args); + + foreach (@output) { + my %output; + if (ref($_)) { + store_fd([$_], $out); + 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; + if ($rc == 0) { + $output{node}->[0]->{data}->[0]->{contents}->[0] = $text; + } else { + $output{node}->[0]->{error} = $text; + } + store_fd([ \%output ], $out); + yield(); + waitforack($out); + } + yield(); + } + foreach (keys %$updatetable) { + my $tabhandle = xCAT::Table->new($_, -create => 1); + my $updates = $updatetable->{$_}; + if ($updates->{'!*XCATNODESTODELETE*!'}) { + my @delkeys; + foreach (keys %{ $updates->{'!*XCATNODESTODELETE*!'} }) { + if ($_) { push @delkeys, { node => $_ }; } + } + if (@delkeys) { $tabhandle->delEntries(\@delkeys); } + delete $updates->{'!*XCATNODESTODELETE*!'}; + } + $tabhandle->setNodesAttribs($updatetable->{$_}); + } + + #my $msgtoparent=freeze(\@outhashes); # = XMLout(\%output,RootName => 'xcatresponse'); + #print $out $msgtoparent; #$node.": $_\n"; } - + 1;