diff --git a/xCAT-client/share/xcat/rvid/rvid.kvm b/xCAT-client/share/xcat/rvid/rvid.kvm
deleted file mode 120000
index feb274c70..000000000
--- a/xCAT-client/share/xcat/rvid/rvid.kvm
+++ /dev/null
@@ -1 +0,0 @@
-rvid.xen
\ No newline at end of file
diff --git a/xCAT-client/share/xcat/rvid/rvid.kvm b/xCAT-client/share/xcat/rvid/rvid.kvm
new file mode 100755
index 000000000..4aebabe1e
--- /dev/null
+++ b/xCAT-client/share/xcat/rvid/rvid.kvm
@@ -0,0 +1,30 @@
+#!/bin/bash
+# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
+if [ ! -z "$rvid_virturi" -a ! -z "$rvid_virtname" ]; then
+ if [ -x /usr/bin/virt-viewer ]; then
+ exec /usr/bin/virt-viewer -c $rvid_virturi $rvid_virtname
+# else
+# echo "Installation of virt-viewer is recommended, falling back to vncviewer"
+ fi
+fi
+myport=${rvid_vidport#*:}
+if [ $rvid_vidproto = "vnc" ];then
+ let myport=myport-5900;
+ export VNC_VIA_CMD='/usr/bin/ssh -o BatchMode=yes -f -L "$L":"$H":"$R" "$G" sleep 20'
+ (
+ flock 200
+ echo $rvid_password|vncviewer :$myport -via $rvid_server AutoSelect=0 FullColor=1 passwdInput=1 >& /dev/null &
+ sleep 0.2
+ flock -u 200
+) 200> /tmp/xcat/virtvnclock
+elif [ $rvid_vidproto = "spice" ]; then
+ SPICEC="/usr/bin/spicec"
+ if [ ! -x $SPICEC -a -x /usr/libexec/spicec ]; then
+ SPICEC=/usr/libexec/spicec
+ elif [ ! -x $SPICEC ]; then
+ echo "Error: spice client could not be found and needed for this VM" >&2;
+ fi
+ $SPICEC -h $rvid_server -p $myport -w $rvid_password
+fi
+
+
diff --git a/xCAT-server/lib/xcat/plugins/esx.pm b/xCAT-server/lib/xcat/plugins/esx.pm
index ded0c8ba0..4f5a74a82 100644
--- a/xCAT-server/lib/xcat/plugins/esx.pm
+++ b/xCAT-server/lib/xcat/plugins/esx.pm
@@ -560,6 +560,16 @@ sub do_cmd {
generic_hyp_operation(\&migrate,@exargs);
}
wait_for_tasks();
+ if ($command eq 'clonevm') { #TODO: unconditional, remove mkvms hosted copy
+ my @dhcpnodes;
+ foreach (keys %{$tablecfg{dhcpneeded}}) {
+ push @dhcpnodes,$_;
+ delete $tablecfg{dhcpneeded}->{$_};
+ }
+ unless ($::XCATSITEVALS{'dhcpsetup'} and ($::XCATSITEVALS{'dhcpsetup'} =~ /^n/i or $::XCATSITEVALS{'dhcpsetup'} =~ /^d/i or $::XCATSITEVALS{'dhcpsetup'} eq '0')) {
+ $executerequest->({command=>['makedhcp'],node=>\@dhcpnodes});
+ }
+ }
}
#inventory request for esx
@@ -1986,10 +1996,18 @@ sub clonevms {
}
}
} elsif ($target) {
+ if ($url =~ m!/!) {
$url=$target;
- $url =~ s!/([^/]*)\z!!;
+ $url =~ s!/([^/]*)\z!!;
$mastername=$1;
- $newdatastores->{$url}=[$nodes->[0]];
+ } else {
+ $url = $tablecfg{vm}->{$nodes->[0]}->[0]->{storage};
+ $url =~ s/.*\|//;
+ $url =~ s/=(.*)//;
+ $url =~ s/,.*//;
+ $mastername=$target
+ }
+ $newdatastores->{$url}=[$nodes->[0]];
}
if ($hyp) {
unless (validate_datastore_prereqs($nodes,$hyp,$newdatastores)) {
@@ -2087,16 +2105,26 @@ sub clone_vms_from_master {
pool=>$pool,
#diskMoveType=>"createNewChildDiskBacking", #fyi, requires a snapshot, which isn't compatible with templates, moveChildMostDiskBacking would potentially be fine, but either way is ha incopmatible and limited to 8, arbitrary limitations hard to work around...
);
+ unless ($args{detach}) {
+ $relocatespecargs{diskMoveType}="createNewChildDiskBacking";
+ }
if ($hyp) { $relocatespecargs{host}=$hyphash{$hyp}->{hostview} }
my $relocatespec = VirtualMachineRelocateSpec->new(%relocatespecargs);
- my $clonespec = VirtualMachineCloneSpec->new(
+ my %clonespecargs = (
location=>$relocatespec,
template=>0,
powerOn=>0
);
+ unless ($args{detach}) {
+ $clonespecargs{snapshot}=$masterview->snapshot->currentSnapshot;
+ }
+ my $clonespec = VirtualMachineCloneSpec->new(%clonespecargs);
my $vmfolder = $vmhash{$node}->{vmfolder};
my $task = $masterview->CloneVM_Task(folder=>$vmfolder,name=>$node,spec=>$clonespec);
- $running_tasks{$task}->{data} = { node => $node, successtext => 'Successfully cloned from '.$args{mastername}, mastername=>$args{mastername}, nodetypeent=>$nodetypeent,vment=>$vment };
+ $running_tasks{$task}->{data} = { node => $node, conn=>$conn, successtext => 'Successfully cloned from '.$args{mastername},
+ mastername=>$args{mastername}, nodetypeent=>$nodetypeent,vment=>$vment,
+ hyp=>$args{hyp},
+ };
$running_tasks{$task}->{task} = $task;
$running_tasks{$task}->{callback} = \&clone_task_callback;
$running_tasks{$task}->{hyp} = $args{hyp}; #$hyp_conns->{$hyp};
@@ -2135,13 +2163,47 @@ sub clone_task_callback {
my $parms = shift;
my $state = $task->info->state->val;
my $node = $parms->{node};
+ my $conn = $parms->{conn};
my $intent = $parms->{successtext};
if ($state eq 'success') {
- xCAT::SvrUtils::sendmsg($intent, $output_handler,$node);
+ #xCAT::SvrUtils::sendmsg($intent, $output_handler,$node);
my $nodetype=xCAT::Table->new('nodetype',-create=>1);
my $vm=xCAT::Table->new('vm',-create=>1);
$vm->setAttribs({node=>$node},$parms->{vment});
+
$nodetype->setAttribs({node=>$node},$parms->{nodetypeent});
+ foreach (keys %{$parms->{vment}}) {
+ $tablecfg{vm}->{$node}->[0]->{$_}=$parms->{vment}->{$_};
+ }
+
+ my @networks = split /,/,$tablecfg{vm}->{$node}->[0]->{nics};
+ my @macs = xCAT::VMCommon::getMacAddresses(\%tablecfg,$node,scalar @networks);
+ #now with macs, change all macs in the vm to match our generated macs
+ my $regex = qr/^$node(\z|\.)/;
+ #have to do an expensive pull of the vm view, since it is brand new
+ my $nodeviews = $conn->find_entity_views(view_type => 'VirtualMachine',filter=>{'config.name'=>$regex});
+ unless (scalar @$nodeviews == 1) { die "this should be impossible"; }
+ my $vpdtab=xCAT::Table->new('vpd',-create=>1);
+ $vpdtab->setAttribs({node=>$node},{uuid=>$nodeviews->[0]->config->uuid});
+ my $ndev;
+ my @devstochange;
+ foreach $ndev (@{$nodeviews->[0]->config->hardware->device}) {
+ unless ($ndev->{macAddress}) { next; } #not an ndev
+ $ndev->{macAddress}=shift @macs;
+ push @devstochange, VirtualDeviceConfigSpec->new(
+ device => $ndev,
+ operation => VirtualDeviceConfigSpecOperation->new('edit'));
+ }
+ if (@devstochange) {
+ my $reconfigspec = VirtualMachineConfigSpec->new(deviceChange=>\@devstochange);
+ my $task = $nodeviews->[0]->ReconfigVM_Task(spec=>$reconfigspec);
+ $running_tasks{$task}->{task} = $task;
+ $running_tasks{$task}->{callback} = \&generic_task_callback;
+ $running_tasks{$task}->{hyp} = $parms->{hyp};
+ $running_tasks{$task}->{data} = { node => $node, successtext => $intent};
+ }
+
+
} elsif ($state eq 'error') {
relay_vmware_err($task,"",$node);
}
@@ -2176,25 +2238,52 @@ sub promote_vm_to_master {
);
my $clonespec = VirtualMachineCloneSpec->new(
location=>$relocatespec,
- template=>1,
+ template=>0, #can't go straight to template, need to clone, then snap, then templatify
powerOn=>0
);
my $vmfolder=$vmhash{$node}->{vmfolder};
my $task = $nodeview->CloneVM_Task(folder=>$vmfolder,name=>$args{mastername},spec=>$clonespec);
- $running_tasks{$task}->{data} = { node => $node, successtext => 'Successfully copied to '.$args{mastername}, mastername=>$args{mastername}, url=>$args{url} };
+ $running_tasks{$task}->{data} = { node => $node, hyp => $args{hyp}, conn => $conn, successtext => 'Successfully copied to '.$args{mastername}, mastername=>$args{mastername}, url=>$args{url} };
$running_tasks{$task}->{task} = $task;
$running_tasks{$task}->{callback} = \&promote_task_callback;
$running_tasks{$task}->{hyp} = $args{hyp}; #$hyp_conns->{$hyp};
$running_tasks{$task}->{vm}=$node;
}
sub promote_task_callback {
+ my $task = shift;
+ my $parms = shift;
+ my $state = $task->info->state->val;
+ my $node = $parms->{node};
+ my $intent = $parms->{successtext};
+ if ($state eq 'success') { #now, we have to make one snapshot for linked clones
+ my $mastername=$parms->{mastername};
+ my $regex=qr/^$mastername\z/;
+ my $masterviews = $parms->{conn}->find_entity_views(view_type => 'VirtualMachine',filter=>{'config.name'=>$regex});
+ unless (scalar @$masterviews == 1) {
+ die "Impossible";
+ }
+ my $masterview = $masterviews->[0];
+ my $task = $masterview->CreateSnapshot_Task(name=>"xcatsnap",memory=>"false",quiesce=>"false");
+ $parms->{masterview}=$masterview;
+ $running_tasks{$task}->{data} = $parms;
+ $running_tasks{$task}->{task} = $task;
+ $running_tasks{$task}->{callback} = \&promotesnap_task_callback;
+ $running_tasks{$task}->{hyp} = $parms->{hyp}; #$hyp_conns->{$hyp};
+ $running_tasks{$task}->{vm}=$parms->{node};
+ #xCAT::SvrUtils::sendmsg($intent, $output_handler,$node);
+ } elsif ($state eq 'error') {
+ relay_vmware_err($task,"",$node);
+ }
+}
+sub promotesnap_task_callback {
my $task = shift;
my $parms = shift;
my $state = $task->info->state->val;
my $node = $parms->{node};
my $intent = $parms->{successtext};
if ($state eq 'success') {
+ $parms->{masterview}->MarkAsTemplate; #time to be a template
xCAT::SvrUtils::sendmsg($intent, $output_handler,$node);
my $mastertabentry = {
originator=>$requester,
@@ -3834,7 +3923,12 @@ sub makecustomizedmod {
open($shadow,">",$tempdir."/etc/shadow");
$password = crypt($password,'$1$'.xCAT::Utils::genpassword(8));
my $dayssince1970 = int(time()/86400); #Be truthful about /etc/shadow
- my @otherusers = qw/nobody nfsnobody dcui daemon vimuser/;
+ my @otherusers = qw/nobody nfsnobody dcui daemon/;
+ if ($osver =~ /esxi4/) {
+ push @otherusers,"vimuser";
+ } elsif ($osver =~ /esxi5/) {
+ push @otherusers,"vpxuser";
+ }
print $shadow "root:$password:$dayssince1970:0:99999:7:::\n";
foreach (@otherusers) {
print $shadow "$_:*:$dayssince1970:0:99999:7:::\n";
diff --git a/xCAT-server/lib/xcat/plugins/kvm.pm b/xCAT-server/lib/xcat/plugins/kvm.pm
index ec60eed8d..0ff57a0e8 100644
--- a/xCAT-server/lib/xcat/plugins/kvm.pm
+++ b/xCAT-server/lib/xcat/plugins/kvm.pm
@@ -16,6 +16,7 @@ use XML::LibXML; #now that we are in the business of modifying xml data, need so
#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);
+use xCAT::Utils qw/genpassword/;
use File::Basename qw/fileparse/;
use File::Path qw/mkpath/;
use IO::Socket;
@@ -404,6 +405,13 @@ sub build_diskstruct {
$cdhash->{readonly};
$cdhash->{target}->{dev}='hdc';
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}='hdc';
+ push @returns,$cdhash;
}
@@ -604,10 +612,25 @@ sub build_xmldesc {
$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
- $xtree{devices}->{video}= [ { 'content'=>'','model'=> {type=>'vga',vram=>8192}}];
+ if (defined ($confdata->{vm}->{$node}->[0]->{vidmodel})) {
+ my $model = $confdata->{vm}->{$node}->[0]->{vidmodel};
+ my $vram = '8192';
+ $xtree{devices}->{video}= [ { 'content'=>'','model'=> {type=>$model,vram=>8192}}];
+ } else {
+ $xtree{devices}->{video}= [ { 'content'=>'','model'=> {type=>'vga',vram=>8192}}];
+ }
$xtree{devices}->{input}->{type}='tablet';
$xtree{devices}->{input}->{bus}='usb';
- $xtree{devices}->{graphics}->{type}='vnc';
+ 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}->{autoport}='yes';
+ $xtree{devices}->{graphics}->{listen}='0.0.0.0';
+ $xtree{devices}->{graphics}->{password}=genpassword(16);
+ $xtree{devices}->{sound}->{model}='ac97';
+
$xtree{devices}->{console}->{type}='pty';
$xtree{devices}->{console}->{target}->{port}='1';
return XMLout(\%xtree,RootName=>"domain");
@@ -619,11 +642,12 @@ sub refresh_vm {
my $newxml=$dom->get_xml_description();
$updatetable->{kvm_nodedata}->{$node}->{xml}=$newxml;
$newxml = XMLin($newxml);
- my $vncport=$newxml->{devices}->{graphics}->{port};
+ 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};
+ #$updatetable->{vm}->{$node}={vncport=>$vncport,textconsole=>$stty};
#$vmtab->setNodeAttribs($node,{vncport=>$vncport,textconsole=>$stty});
- return {vncport=>$vncport,textconsole=>$stty};
+ return {vidport=>$vidport,textconsole=>$stty,vidproto=>$vidproto};
}
sub getcons {
@@ -649,25 +673,36 @@ sub getcons {
$sconsparms->{node}->[0]->{psuedotty}=[$consdata->{textconsole}];
$sconsparms->{node}->[0]->{baudrate}=[$serialspeed];
return (0,$sconsparms);
- } elsif ($type eq "vnc") {
- return (0,'ssh+vnc@'.$hyper.": localhost:".$consdata->{vncport}); #$consdata->{vncport});
+ } elsif ($type eq "vid") {
+ my $domxml = $dom->get_xml_description();
+ my $parseddom = $parser->parse_string($domxml);
+ my ($graphicsnode) = $parseddom->findnodes("//graphics");
+
+ my $tpasswd=genpassword(16);
+ my $validto=POSIX::strftime("%Y-%m-%dT%H:%M:%S",gmtime(time()+300));
+ $graphicsnode->setAttribute("passwd",$tpasswd);
+ $graphicsnode->setAttribute("passwdValidTo",$validto);
+ $dom->update_device($graphicsnode->toString());
+ #$dom->update_device("");
+ $consdata->{password}=$tpasswd;
+ $consdata->{server}=$hyper;
+ return $consdata;
+ #return (0,{$consdata->{vidproto}.'@'.$hyper.":".$consdata->{vidport}); #$consdata->{vncport});
}
}
sub getrvidparms {
my $node=shift;
- my $location = getcons($node,"vnc");
- if ($location =~ /ssh\+vnc@([^:]*):([^:]*):(\d+)/) {
- my @output = (
- "method: kvm",
- "server: $1",
- "vncdisplay: $2:$3",
- "virturi: ".$hypconn->get_uri(),
- "virtname: $node",
- );
- return 0,@output;
- } else {
- return (1,"Error: Unable to determine rvid destination for $node");
+ my $location = getcons($node,"vid");
+ unless ($location) {
+ return (1,"Error: Unable to determine rvid destination for $node");
}
+ my @output = (
+ "method: kvm"
+ );
+ foreach (keys %$location) {
+ push @output,$_.":".$location->{$_};
+ }
+ return 0,@output;
}
sub pick_target {
@@ -974,6 +1009,9 @@ sub makedom {
} elsif (not $xml) {
$xml = build_xmldesc($node,cd=>$cdloc);
}
+ my $parseddom = $parser->parse_string($xml);
+ my ($graphics) = $parseddom->findnodes("//graphics");
+ $graphics->setAttribute("passwd",genpassword(20));
my $errstr;
eval { $dom=$hypconn->create_domain($xml); };
if ($@) { $errstr = $@; }
@@ -1324,12 +1362,16 @@ sub chvm {
my @purge;
my @derefdisks;
my $memory;
+ my $cdrom;
+ my $eject;
@ARGV=@_;
require Getopt::Long;
GetOptions(
"a=s"=>\@addsizes,
"d=s"=>\@derefdisks,
"mem=s"=>\$memory,
+ "cdrom=s"=>\$cdrom,
+ "eject"=>\$eject,
"cpus=s" => \$cpucount,
"p=s"=>\@purge,
"resize=s%" => \%resize,
@@ -1496,6 +1538,57 @@ 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 = "";
+ } elsif ($eject) {
+ $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 ($cpucount or $memory) {
if ($currstate eq 'on') {
xCAT::SvrUtils::sendmsg([1,"Hot add of cpus or memory not supported"],$callback,$node);