Backport a few features from trunk:

KVM:
	-Remote video enhancements for password use, remote listen, and SPICE
	-chvm allows attaching/removing an iso as optical 
ESX:
	-Support linked clones
	-Presume a clone target is in the same datastore as source when not specified


git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/branches/2.6@9499 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
This commit is contained in:
jbjohnso
2011-05-06 12:52:10 +00:00
parent 6153c74606
commit 053fd1611f
3 changed files with 244 additions and 28 deletions
-1
View File
@@ -1 +0,0 @@
rvid.xen
+30
View File
@@ -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
+102 -8
View File
@@ -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";
+112 -19
View File
@@ -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("<graphics type='".$consdata->{vidproto}."' passwd='$tpasswd' passwdValidTo='$validto' autoport='yes'/>");
$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 = "<disk type='file' device='cdrom'><source file='$cdpath'/><target dev='hdc'/><readonly/></disk>";
} elsif ($eject) {
$newcdxml = "<disk type='file' device='cdrom'><target dev='hdc'/><readonly/></disk>";
}
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);