in managementnode/lib/VCL/Module/Provisioning/vbox.pm [75:442]
sub load {
my $self = shift;
if (ref($self) !~ /vbox/i) {
notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
return 0;
}
my $request_data = shift;
my ($package, $filename, $line, $sub) = caller(0);
# Store some hash variables into local variables
my $request_id = $self->data->get_request_id;
my $reservation_id = $self->data->get_reservation_id;
my $persistent = $self->data->get_request_forimaging;
my $management_node_keys = $self->data->get_management_node_keys();
my $image_os_type = $self->data->get_image_os_type;
my $vmclient_computerid = $self->data->get_computer_id;
my $computer_shortname = $self->data->get_computer_short_name;
my $computer_nodename = $computer_shortname;
my $computer_hostname = $self->data->get_computer_hostname;
my $vmprofile_vmdisk = $self->data->get_vmhost_profile_vmdisk;
my $datastorepath = $self->data->get_vmhost_profile_datastore_path;
my $virtualswitch0 = $self->data->get_vmhost_profile_virtualswitch0;
my $virtualswitch1 = $self->data->get_vmhost_profile_virtualswitch1;
my $requestedimagename = $self->data->get_image_name;
my $shortname = $computer_shortname;
my $vmhost_imagename = $self->data->get_vmhost_image_name;
my $vmhost_vmpath = $self->data->get_vmhost_profile_vmpath;
my $vmhost_hostname = $self->data->get_vmhost_hostname;
my $vmclient_eth0MAC = $self->data->get_computer_eth0_mac_address;
my $vmclient_eth1MAC = $self->data->get_computer_eth1_mac_address;
my $vmclient_imageminram = $self->data->get_image_minram;
my $vmhost_RAM = $self->data->get_vmhost_ram;
my $vmclient_drivetype = $self->data->get_computer_drive_type;
my $vmclient_privateIPaddress = $self->data->get_computer_public_ip_address;
my $vmclient_publicIPaddress = $self->data->get_computer_private_ip_address;
my $vmclient_OSname = $self->data->get_image_os_name;
my $image_repository_path = $self->_get_image_repository_path();
# Assemble a consistent prefix for notify messages
my $notify_prefix = "req=$request_id, res=$reservation_id:";
my $vm_name;
my @sshcmd;
insertloadlog($reservation_id, $vmclient_computerid, "startload", "$computer_shortname $requestedimagename");
my $starttime = convert_to_epoch_seconds;
my ($hostnode);
$hostnode = $vmhost_hostname;
if (!(defined($management_node_keys))) {
notify($ERRORS{'CRITICAL'}, 0, "identity variable not defined, setting to blade identity file vmhost variable to $vmhost_imagename");
}
notify($ERRORS{'OK'}, 0, "identity file set $management_node_keys vmhost imagename $vmhost_imagename bladekey ");
#setup flags
my $baseisregistered = 0;
my $baseexists = 0;
my $dirstructure = 0;
#for convienence
my ($myimagename, $myvmx, $myvmdir, $mybasedirname, $requestedimagenamebase);
# preform cleanup
if ($self->control_vm("remove")) {
notify($ERRORS{'OK'}, 0, "removed node $shortname from vmhost $vmhost_hostname");
}
### FIX-ME: I have no freakin clue how to approach this (imaging mode) at the moment
### For VBox, this would require changing the disk mode from immuatable to normal
### which itself would be easy, the challenge for me is handeling the hypervisors that have this image registered
### where any VMs that are using it will have associated snapshots that will have to be delt with before the image
### could be un-registered and re-registered. so for now, @#$% it... (david.hutchins)
if ($persistent) {
$vm_name = "$requestedimagename\_IMAGING\_$shortname";
} ## end if ($persistent)
else {
$vm_name = "$requestedimagename\_$shortname";
}
$myimagename = $requestedimagename;
notify($ERRORS{'DEBUG'}, 0, "persistent= NOT IMPLEMENTED");
notify($ERRORS{'DEBUG'}, 0, "myimagename= $myimagename");
notify($ERRORS{'OK'}, 0, "vmpath= $vmhost_vmpath");
#does the requested base vbox image files already existed on the vmhost
notify($ERRORS{'OK'}, 0, "checking for base image $requestedimagename on $hostnode ");
insertloadlog($reservation_id, $vmclient_computerid, "vmround1", "checking host for requested image files");
#check for lock file - another process might be copying the same image files to the same host server
my $tmplockfile = "/tmp/$hostnode" . "$requestedimagename" . "lock";
notify($ERRORS{'OK'}, 0, "trying to create exclusive lock on $tmplockfile while checking if image files exist on host");
if (sysopen(TMPLOCK, $tmplockfile, O_RDONLY | O_CREAT)) {
if (flock(TMPLOCK, LOCK_EX)) {
notify($ERRORS{'OK'}, 0, "owning exclusive lock on $tmplockfile");
notify($ERRORS{'OK'}, 0, "listing datestore $datastorepath ");
# Check to see if the baseimage is registered with VirtualBox on this host.
undef @sshcmd;
#@sshcmd = run_ssh_command($hostnode, $management_node_keys, "ls -1 $datastorepath", "root");
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q list hdds", "root");
notify($ERRORS{'OK'}, 0, "Hdds in VirtualBox database on vm host:\n@{ $sshcmd[1] }");
foreach my $l (@{$sshcmd[1]}) {
if ($l =~ /(\s*?)$datastorepath\/$myimagename/) {
# The base is registered, so we will assume it is also present (This may not be the best approach, but for now it will do).
notify($ERRORS{'OK'}, 0, "base image exists");
$baseisregistered = 1;
$baseexists = 1;
}
} ## end foreach my $l (@{$sshcmd[1]})
# If the base is not registered, we will check to see if it exists
if (!($baseisregistered)) {
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "ls -1 $datastorepath", "root");
foreach my $l (@{$sshcmd[1]}) {
if ($l =~ /(\s*?)$myimagename/) {
# The base exists so we just need to register it with VirtualBox.
notify($ERRORS{'OK'}, 0, "base image exists, registering it with VirtualBox");
$baseexists = 1;
}
}
}
if (!($baseexists)) {
#check available disk space -- clean up if needed
#copy vm files from local repository to vmhost
#this could take a few minutes
#get size of vmdl files
insertloadlog($reservation_id, $vmclient_computerid, "info", "image files do not exist on host server, preparing to copy");
my $myvmdkfilesize = 0;
if (open(SIZE, "du -k $image_repository_path\/$requestedimagename 2>&1 |")) {
my @du = <SIZE>;
close(SIZE);
foreach my $d (@du) {
if ($d =~ /No such file or directory/) {
insertloadlog($reservation_id, $vmclient_computerid, "failed", "could not collect size of local image files");
notify($ERRORS{'CRITICAL'}, 0, "problem checking local vm file size on $image_repository_path\/$requestedimagename");
close(TMPLOCK);
unlink($tmplockfile);
return 0;
}
if ($d =~ /^([0-9]*)/) {
$myvmdkfilesize += $1;
}
} ## end foreach my $d (@du)
} ## end if (open(SIZE, "du -k $image_repository_path/$requestedimagename 2>&1 |"...
notify($ERRORS{'DEBUG'}, 0, "file size $myvmdkfilesize of $requestedimagename");
if ($vmprofile_vmdisk =~ /(local|dedicated)/) {
notify($ERRORS{'OK'}, 0, "copying base image files $requestedimagename to $hostnode");
if (run_scp_command("$image_repository_path\/$requestedimagename", "$hostnode:\"$datastorepath\/\"", $management_node_keys)) {
#recheck host server for files - the scp output is not being captured
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "ls -1 $datastorepath", "root");
foreach my $l (@{$sshcmd[1]}) {
if ($l =~ /denied|No such/) {
notify($ERRORS{'CRITICAL'}, 0, "node $hostnode output @{ $sshcmd[1] }");
insertloadlog($reservation_id, $vmclient_computerid, "failed", "could not log into vmhost $hostnode @{ $sshcmd[1] }");
close(TMPLOCK);
unlink($tmplockfile);
return 0;
}
if ($l =~ /(\s*?)$requestedimagename$/) {
notify($ERRORS{'OK'}, 0, "base image exists");
$baseexists = 1;
insertloadlog($reservation_id, $vmclient_computerid, "transfervm", "copying base image files");
}
} ## end foreach my $l (@{$sshcmd[1]})
} ## end if (run_scp_command("$image_repository_path/$requestedimagename"...
else {
notify($ERRORS{'CRITICAL'}, 0, "problems scp vm files to $hostnode $!");
close(TMPLOCK);
unlink($tmplockfile);
return 0;
}
} ## end if ($vmprofile_vmdisk =~ /(local|dedicated)/)
notify($ERRORS{'OK'}, 0, "confirm image exist process complete removing lock on $tmplockfile");
close(TMPLOCK);
unlink($tmplockfile);
} # start if base not exists
# If the base exists but was not registered we just need to register it
if ((!($baseisregistered)) && ($baseexists)) {
undef @sshcmd;
# So Oracle removed the method for registering an image with the server. Registration is now automated when media is attached to a VM. But a "read lock" error is given if you attempt to specify "-mtype multiattach" after the first attachment to a vm if the first vm is running. In order to avoid extra logic to determine if it is the first attachment during VM creation, a non-running VM is registered named "STORAGE_HOLDER" with a scsi controller named "STORAGE_HOLDER_SCSI". An image can be attached to port 0 in multiattach mode and any further attachments will default to multiattach when no mtype is specified, without the mtype arg no error is thrown. This feels more like a VBox bug to me, and I opened a bug report with Oracle.
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage storageattach STORAGE_HOLDER --storagectl STORAGE_HOLDER_SCSI --medium $datastorepath\/$myimagename --mtype multiattach --type hdd --port 0", "root");
foreach my $l (@{$sshcmd[1]}) {
if ($l =~ /(\s*?)ERROR:/) {
# Registeration failed, manual intervention is probably required, send warning and die.
notify($ERRORS{'CRITICAL'}, 0, "Registeration of image failed, output is: \n@{ $sshcmd[1] }");
close(TMPLOCK);
unlink($tmplockfile);
return 0;
}
else {
# Registeration success.
notify($ERRORS{'OK'}, 0, "IMG REGISTRATION-> $l");
$baseisregistered = 1;
}
}
} else {
#base exists
notify($ERRORS{'OK'}, 0, "confirm image exist process complete removing lock on $tmplockfile");
close(TMPLOCK);
unlink($tmplockfile);
}
} #flock
} #sysopen
#ok good base vm files exist on hostnode
#if guest dirstructure exists check state of vm, else create sturcture and new vmx file
#check for dependent settings ethX
if (!(defined($vmclient_eth0MAC))) {
#complain
notify($ERRORS{'CRITICAL'}, 0, "eth0MAC is not defined for $computer_shortname can not continue");
insertloadlog($reservation_id, $vmclient_computerid, "failed", "eth0MAC address is not defined");
return 0;
}
#check for memory settings
my $dynamicmemvalue = "128";
if (defined($vmclient_imageminram)) {
#preform some sanity check
if (($dynamicmemvalue < $vmclient_imageminram) && ($vmclient_imageminram < $vmhost_RAM)) {
$dynamicmemvalue = $vmclient_imageminram;
notify($ERRORS{'OK'}, 0, "setting memory to $dynamicmemvalue");
}
else {
notify($ERRORS{'WARNING'}, 0, "image memory value $vmclient_imageminram out of the expected range in host machine $vmhost_RAM setting to 128");
}
} ## end if (defined($vmclient_imageminram))
VBOXCREATE:
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q setproperty machinefolder $vmhost_vmpath", "root");
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q createvm --name $vm_name --register", "root");
$vmclient_eth0MAC =~ tr/://d;
$vmclient_eth1MAC =~ tr/://d;
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q modifyvm $vm_name --memory $dynamicmemvalue", "root");
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q modifyvm $vm_name --ioapic on", "root");
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q modifyvm $vm_name --nic1 bridged --bridgeadapter1 $virtualswitch0 --macaddress1 $vmclient_eth0MAC --nictype1 82540EM", "root");
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q modifyvm $vm_name --nic2 bridged --bridgeadapter2 $virtualswitch1 --macaddress2 $vmclient_eth1MAC --nictype2 82540EM", "root");
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q storagectl $vm_name --name $shortname\_stor --add ide", "root");
if ($persistent) {
notify($ERRORS{'OK'}, 0, "Cloning image, this could take a while.");
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q clonehd $datastorepath\/$requestedimagename $datastorepath\/$requestedimagename\_IMAGING\_$shortname ", "root");
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q storageattach $vm_name --storagectl $shortname\_stor --port 0 --device 0 --type hdd --medium $datastorepath\/$requestedimagename\_IMAGING\_$shortname", "root");
} ## end if ($persistent)
else {
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q storageattach $vm_name --storagectl $shortname\_stor --port 0 --device 0 --type hdd --medium $datastorepath\/$requestedimagename", "root");
}
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q modifyvm $vm_name --pae on", "root");
#turn on vm
#set loop control
my $vbox_starts = 0;
VBOXSTART:
$vbox_starts++;
notify($ERRORS{'OK'}, 0, "starting vm $vm_name - pass $vbox_starts");
if ($vbox_starts > 2) {
notify($ERRORS{'CRITICAL'}, 0, "VirtualBox starts exceeded limit vbox_starts= $vbox_starts hostnode= $hostnode vm= $computer_shortname");
insertloadlog($reservation_id, $vmclient_computerid, "failed", "could not load machine on $hostnode exceeded attempts");
return 0;
}
undef @sshcmd;
@sshcmd = run_ssh_command($hostnode, $management_node_keys, "VBoxManage -q startvm $vm_name --type headless", "root");
for my $l (@{$sshcmd[1]}) {
next if ($l =~ /Waiting/);
#if successful -- this cmd does not appear to return any ouput so anything could be a failure
if ($l =~ /successfully started/) {
notify($ERRORS{'OK'}, 0, "started $vm_name on $hostnode: $l");
}
else {
notify($ERRORS{'OK'}, 0, "Unknown output when trying to start $vm_name on $hostnode \n@{ $sshcmd[1] }");
}
} ## end for my $l (@{$sshcmd[1]})
insertloadlog($reservation_id, $vmclient_computerid, "startvm", "started vm on $hostnode");
my $sshd_attempts = 0;
VBOXROUND2:
insertloadlog($reservation_id, $vmclient_computerid, "vmround2", "waiting for ssh to become active");
if ($self->os->can("post_load")) {
notify($ERRORS{'DEBUG'}, 0, "calling " . ref($self->os) . "->post_load()");
if ($self->os->post_load()) {
notify($ERRORS{'DEBUG'}, 0, "successfully ran OS post_load subroutine");
}
else {
my $vm_state = $self->power_status() || 'unknown';
notify($ERRORS{'WARNING'}, 0, "failed to run OS post_load subroutine, VM state: $vm_state");
return;
}
}
else {
notify($ERRORS{'DEBUG'}, 0, ref($self->os) . "::post_load() has not been implemented");
}
insertloadlog($reservation_id, $vmclient_computerid, "info", "starting post configurations on node");
#clear ssh public keys from /root/.ssh/known_hosts
my $known_hosts = "/root/.ssh/known_hosts";
my $ssh_keyscan = "/usr/bin/ssh-keyscan";
my $port = "22";
my @file;
if (open(FILE, $known_hosts)) {
@file = <FILE>;
close FILE;
foreach my $line (@file) {
if ($line =~ s/$computer_shortname.*\n//) {
notify($ERRORS{'OK'}, 0, "removing $computer_shortname ssh public key from $known_hosts");
}
if ($line =~ s/$vmclient_privateIPaddress}.*\n//) {
notify($ERRORS{'OK'}, 0, "removing $vmclient_privateIPaddress ssh public key from $known_hosts");
}
}
if (open(FILE, ">$known_hosts")) {
print FILE @file;
close FILE;
}
#sync new keys
if (open(KEYSCAN, "$ssh_keyscan -t rsa -p $port $computer_shortname >> $known_hosts 2>&1 |")) {
my @ret = <KEYSCAN>;
close(KEYSCAN);
foreach my $r (@ret) {
notify($ERRORS{'OK'}, 0, "$r");
}
}
} ## end if (open(FILE, $known_hosts))
else {
notify($ERRORS{'OK'}, 0, "could not open $known_hosts for editing the $computer_shortname public ssh key");
}
insertloadlog($reservation_id, $vmclient_computerid, "vboxready", "preformed post config on node");
return 1;
} ## end sub load