You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vcl.apache.org by ar...@apache.org on 2011/12/14 18:15:17 UTC

svn commit: r1214348 [2/2] - in /incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning: libvirt.pm libvirt/ libvirt/KVM.pm

Added: incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/libvirt/KVM.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/libvirt/KVM.pm?rev=1214348&view=auto
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/libvirt/KVM.pm (added)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/libvirt/KVM.pm Wed Dec 14 17:15:17 2011
@@ -0,0 +1,887 @@
+#!/usr/bin/perl -w
+###############################################################################
+# $Id$
+###############################################################################
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###############################################################################
+
+=head1 NAME
+
+VCL::Provisioning::libvirt::KVM - Libvirt hypervisor driver module to allow
+support for the KVM hypervisor
+
+=head1 DESCRIPTION
+
+ This is a driver module to allow the main libvirt.pm provisioning module to
+ support KVM hosts. It performs the KVM-specific tasks not handled by libvirt
+ itself.
+
+=cut
+
+##############################################################################
+package VCL::Module::Provisioning::libvirt::KVM;
+
+# Specify the lib path using FindBin
+use FindBin;
+use lib "$FindBin::Bin/../../../..";
+
+# Configure inheritance
+use base qw(VCL::Module::Provisioning::libvirt);
+
+# Specify the version of this module
+our $VERSION = '2.2.1';
+
+# Specify the version of Perl to use
+use 5.008000;
+
+use strict;
+use warnings;
+use diagnostics;
+use English qw( -no_match_vars );
+use File::Basename;
+
+use VCL::utils;
+	
+##############################################################################
+
+=head1 OBJECT METHODS
+
+=cut
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 initialize
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Checks if the node has KVM installed by checking if /usr/bin/qemu
+               exists. Returns true if the file exists, false otherwise.
+
+=cut
+
+sub initialize {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	my $node_name = $self->data->get_vmhost_short_name();
+	my ($driver_name) = ref($self) =~ /::([^:]+)$/;
+	
+	# Check to see if qemu exists on the VM host
+	my $test_file_path = '/usr/bin/qemu';
+	if ($self->vmhost_os->file_exists($test_file_path)) {
+		notify($ERRORS{'DEBUG'}, 0, "$driver_name driver module successfully initialized, verified '$test_file_path' exists on $node_name");
+		return 1;
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "$driver_name driver module not initialized, '$test_file_path' does NOT exist on $node_name");
+		return;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_domain_type
+
+ Parameters  : none
+ Returns     : string
+ Description : Returns 'kvm'. This is specified in the domain XML definition:
+                  <domain type='kvm'>
+
+
+=cut
+
+sub get_domain_type {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	return 'kvm';
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_disk_driver_name
+
+ Parameters  : none
+ Returns     : string
+ Description : Returns 'qemu'. The disk driver name is specified in the domain
+               XML definition:
+                  <domain ...>
+                     <devices>
+                        <disk ...>
+                           <driver name='qemu' ...>
+
+=cut
+
+sub get_disk_driver_name {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	return 'qemu';
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_disk_format
+
+ Parameters  : none
+ Returns     : string
+ Description : Returns 'qcow2'. The disk format is specified in the domain XML
+               definition:
+                  <domain ...>
+                     <devices>
+                        <disk ...>
+                           <driver type='qcow2' ...>
+
+=cut
+
+sub get_disk_format {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	return 'qcow2';
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_disk_file_extension
+
+ Parameters  : none
+ Returns     : string
+ Description : Returns 'qcow2'. This is used by libvirt.pm as the file extension
+               of the virtual disk file paths.
+
+=cut
+
+sub get_disk_format {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	return 'qcow2';
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 pre_define
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Performs the KVM-specific steps prior to defining a domain:
+               * Checks if the master image file exists on the node, If it does
+                 not exist, attempts to copy image from repository to the node
+               * Creates a copy on write image which will be used by the domain
+                 being loaded
+
+=cut
+
+sub pre_define {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	my $image_name = $self->data->get_image_name();
+	my $node_name = $self->data->get_vmhost_short_name();
+	my $copy_on_write_file_path = $self->get_copy_on_write_file_path();
+	my $master_image_file_path = $self->get_master_image_file_path();
+
+	if ($self->vmhost_os->file_exists($master_image_file_path)) {
+		notify($ERRORS{'DEBUG'}, 0, "master image file exists in the datastore on $node_name: $master_image_file_path");
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "master image file does NOT exist in the datastore on $node_name: $master_image_file_path");
+		
+		# Check the files found in the repository
+		# Attempt to determine which files are actual virtual disk files
+		my @repository_image_file_paths = $self->find_repository_image_file_paths();
+		if (@repository_image_file_paths) {
+			# Attempt to copy the virtual disk from the repository to the datastore
+			if ($self->copy_virtual_disk(\@repository_image_file_paths, $master_image_file_path)) {
+				notify($ERRORS{'DEBUG'}, 0, "copied master image from repository to datastore");
+			}
+			else {
+				notify($ERRORS{'WARNING'}, 0, "unable to prepare virtual disk, failed to copy master image from repository to datastore");
+				return;
+			}
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "unable to prepare virtual disk, failed to locate virtual disk file in the repository");
+			return;
+		}
+	}
+	
+	if (!$self->create_copy_on_write_image($master_image_file_path, $copy_on_write_file_path)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to prepare virtual disk, unable to create copy on write image");
+		return;
+	}
+	
+	return 1;
+}
+
+##############################################################################
+
+=head1 PRIVATE METHODS
+
+=cut
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_virtual_disk_file_info
+
+ Parameters  : $virtual_disk_file_path
+ Returns     : hash reference
+ Description : Calls 'qemu-img info' to retrieve the virtual disk information.
+               Builds a hash based on the output. Example:
+                  "backing_file" => "/var/lib/libvirt/images/vmwarewinxp-base234-v23.qcow2 (actual path: /var/lib/libvirt/images/vmwarewinxp-base234-v23.qcow2)",
+                  "backing_file_actual_path" => "/var/lib/libvirt/images/vmwarewinxp-base234-v23.qcow2",
+                  "cluster_size" => 65536,
+                  "disk_size" => "423M",
+                  "disk_size_bytes" => 443547648,
+                  "file_format" => "qcow2",
+                  "image" => "/var/lib/libvirt/images/vclv99-37_234-v23.qcow2",
+                  "snapshot" => {
+                    1 => {
+                      "date" => "2011-12-07 14:43:12",
+                      "tag" => "snap1",
+                      "vm_clock" => "00:00:00.000",
+                      "vm_size" => 0
+                    }
+                  },
+                  "virtual_size" => "20G (21474836480 bytes)",
+                  "virtual_size_bytes" => "21474836480"
+
+=cut
+
+sub get_virtual_disk_file_info {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	my $virtual_disk_file_path = shift;
+	if (!$virtual_disk_file_path) {
+		notify($ERRORS{'WARNING'}, 0, "unable to retrieve image info, file path argument was not supplied");
+		return;
+	}
+	
+	# Return cached copy of virtual disk file info if it exists
+	return $self->{virtual_disk_file_info}{$virtual_disk_file_path} if defined($self->{virtual_disk_file_info}{$virtual_disk_file_path});
+	
+	my $node_name = $self->data->get_vmhost_short_name();
+	
+	my $command = "qemu-img info \"$virtual_disk_file_path\"";
+	my ($exit_status, $output) = $self->vmhost_os->execute($command);
+	if (!defined($exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command to retrieve image info on $node_name");
+		return;
+	}
+	elsif ($exit_status) {
+		notify($ERRORS{'DEBUG'}, 0, "unable to retrieve image info on $node_name, output:\n" . join("\n", @$output));
+		return;
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "retrieved image info, output:\n" . join("\n", @$output));
+		
+		my $virtual_disk_file_info;
+		for my $line (@$output) {
+			# Output example:
+			#    image: vclv99-37_234-v23.qcow2
+			#    file format: qcow2
+			#    virtual size: 20G (21474836480 bytes)
+			#    disk size: 423M
+			#    cluster_size: 65536
+			#    backing file: /var/lib/libvirt/images/vmwarewinxp-base234-v23.qcow2 (actual path: /var/lib/libvirt/images/vmwarewinxp-base234-v23.qcow2)
+			#    Snapshot list:
+			#    ID        TAG                 VM SIZE                DATE       VM CLOCK
+			#    1         snap1                     0 2011-12-07 14:43:12   00:00:00.000
+			
+			# Skip the 'Snapshot list:' and snapshot header lines
+			if ($line =~ /^(Snapshot list|ID)/i) {
+				next;
+			}
+			#                  ID      TAG    SIZE    DATE                  CLOCK
+			elsif ($line =~ /^(\d+)\s+(.+)\s+(\d+)\s+([\d\-:\.]+ [\d:]+)\s+([\d:\.]+)/g) {
+				my $id = $1;
+				my $tag = $2;
+				my $vm_size = $3;
+				my $date = $4;
+				my $vm_clock = $5;
+				
+				# Remove trailing spaces from the tag
+				$tag =~ s/\s+$//;
+				
+				$virtual_disk_file_info->{snapshot}{$id} = {
+					'tag' => $tag,
+					'vm_size' => $vm_size,
+					'date' => $date,
+					'vm_clock' => $vm_clock,
+				};
+			}
+			elsif ($line =~ /([\w_ ]+):\s*(.+)/) {
+				my $property = $1;
+				my $value = $2;
+				
+				if ($property =~ /disk size/i) {
+					# Calculate the number of bytes from the "disk size" line:
+					# "disk_size" => "16K",
+					# "disk_size" => "2.7M",
+					
+					my $disk_size_bytes;
+					my ($disk_size, $units) = $value =~ /([\d\.]+)(\w)/;
+					
+					if ($units =~ /K/) {
+						$disk_size_bytes = ($disk_size * 1024 ** 1);
+					}
+					elsif ($units =~ /M/) {
+						$disk_size_bytes = ($disk_size * 1024 ** 2);
+					}
+					elsif ($units =~ /G/) {
+						$disk_size_bytes = ($disk_size * 1024 ** 3);
+					}
+					elsif ($units =~ /T/) {
+						$disk_size_bytes = ($disk_size * 1024 ** 4);
+					}
+					else {
+						$disk_size_bytes = $disk_size;
+					}
+					
+					$virtual_disk_file_info->{disk_size_bytes} = int($disk_size_bytes);
+				}
+				elsif ($property =~ /virtual size/i) {
+					# Extract the number of bytes from the "virtual size" line:
+					# "virtual_size" => "15M (15728640 bytes)"
+					my ($virtual_size_bytes) = $value =~ /(\d+) bytes/;
+					$virtual_disk_file_info->{virtual_size_bytes} = $virtual_size_bytes;
+				}
+				elsif ($property =~ /backing file/i) {
+					# Extract the actual path from the "backing file" line:
+					my ($actual_path) = $value =~ /actual path: ([^\)]+)/;
+					$virtual_disk_file_info->{backing_file_actual_path} = $actual_path;
+				}
+				
+				$property = lc($property);
+				$property =~ s/\s+/_/g;
+				$virtual_disk_file_info->{$property} = $value;
+			}
+		}
+		
+		notify($ERRORS{'DEBUG'}, 0, "retrieved virtual disk file info:\n" . format_data($virtual_disk_file_info));
+		$self->{virtual_disk_file_info}{$virtual_disk_file_path} = $virtual_disk_file_info;
+		return $virtual_disk_file_info;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2  get_virtual_disk_size_bytes
+
+ Parameters  : $image_name (optional)
+ Returns     : integer
+ Description : Returns the size of the virtual disk in bytes.
+
+=cut
+
+sub get_virtual_disk_size_bytes {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	# Attempt to get the image name argument
+	my $image_name = shift;
+	if (!$image_name) {
+		$image_name = $self->data->get_image_name() || return;
+	}
+	
+	my $node_name = $self->data->get_vmhost_short_name();
+	
+	# Check if the virtual disk image files reside in the repository
+	my @virtual_disk_file_paths;
+	
+	# Check if the virtual disk image resides in the datastore
+	my $master_image_file_path = $self->get_master_image_file_path();
+
+	# Check if the virtual disk exists on the VM host
+	if ($self->vmhost_os->file_exists($master_image_file_path)) {
+		@virtual_disk_file_paths = ($master_image_file_path);
+	}
+	else {
+		@virtual_disk_file_paths = $self->find_repository_image_file_paths();
+		
+		if (!@virtual_disk_file_paths) {
+			notify($ERRORS{'WARNING'}, 0, "virtual disk for image $image_name does not exist in repository or datastore on $node_name");
+			return;
+		}
+	}
+	
+	my $total_used_bytes;
+	my $total_reserved_bytes;
+	my $total_virtual_bytes;
+	
+	for my $virtual_disk_file_path (@virtual_disk_file_paths) {
+		# Get the bytes used from the VM host OS's 'du' command
+		my ($used_bytes, $reserved_bytes) = $self->vmhost_os->get_file_size($virtual_disk_file_path);
+		$total_used_bytes += $used_bytes;
+		$total_reserved_bytes += $reserved_bytes;
+		
+		# Attempt to retrieve the virtual disk file info
+		# get_virtual_disk_file_info will return false if it is unable to retrieve info for the file
+		my $virtual_disk_file_info = $self->get_virtual_disk_file_info($virtual_disk_file_path);
+		
+		if ($virtual_disk_file_info) {
+			my $virtual_bytes = $virtual_disk_file_info->{virtual_size_bytes};
+			$total_virtual_bytes += $virtual_bytes;
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "unable to determine virtual disk size, information could not be retrieved for virtual disk file: $virtual_disk_file_path");
+			return;
+		}
+	}
+	
+	notify($ERRORS{'DEBUG'}, 0, "size of $image_name image:\n" .
+			 "used: " . get_file_size_info_string($total_used_bytes) . "\n" .
+			 "reserved: " . get_file_size_info_string($total_reserved_bytes) . "\n" .
+			 "virtual: " . get_file_size_info_string($total_virtual_bytes)
+			 );
+	
+	if (wantarray) {
+		return ($total_used_bytes, $total_reserved_bytes, $total_virtual_bytes);
+	}
+	else {
+		return $total_reserved_bytes;
+	}
+	
+} ## end sub get_virtual_disk_size_bytes
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 copy_virtual_disk
+
+ Parameters  : $source_file_paths, $destination_file_path, $disk_format (optional)
+ Returns     : boolean
+ Description : Calls qemu-img to copy a virtual disk image. The destination disk
+               format can be specified as an argument. If omitted, qcow2 is
+               used.
+
+=cut
+
+sub copy_virtual_disk {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	my ($source_file_paths, $destination_file_path, $disk_format) = @_;
+	
+	if (!$source_file_paths || !$destination_file_path) {
+		notify($ERRORS{'WARNING'}, 0, "unable to copy virtual disk, source and destination file path arguments were not passed");
+		return;
+	}
+	elsif (ref($source_file_paths)) {
+		if (ref($source_file_paths) ne 'ARRAY') {
+			notify($ERRORS{'WARNING'}, 0, "unable to copy virtual disk, source file path argument was passed as a reference by reference type is not ARRAY");
+			return;
+		}
+		else {
+			# Join the array of file paths into a string
+			$source_file_paths = join('" "', @$source_file_paths);
+		}
+	}
+	
+	if (!$disk_format) {
+		$disk_format = $self->data->get_vmhost_profile_vmdisk_format() || 'qcow2';
+	}
+	
+	my $node_name = $self->data->get_vmhost_short_name();
+	my $image_os_type = $self->data->get_image_os_type();
+	
+	my $source_size_bytes = $self->get_image_size_bytes() || 0;
+	
+	# Get a semaphore so that multiple processes don't try to copy the image at the same time
+	my $semaphore_id = "$node_name:$destination_file_path";
+	my $semaphore_timeout_minutes = 60;
+	my $semaphore = $self->get_semaphore($semaphore_id, (60 * $semaphore_timeout_minutes), 5) || return;
+	
+	my $start_time = time;
+	my $command = "qemu-img convert -O $disk_format -o preallocation=metadata \"$source_file_paths\" \"$destination_file_path\"";
+	notify($ERRORS{'DEBUG'}, 0, "attempting to copy/convert virtual disk:\nsource file path(s):\n" . join("\n", split(/" "/, $source_file_paths)) . "\ndestination file path: $destination_file_path\ndestination disk format: $disk_format\nsource image size: " . get_file_size_info_string($source_size_bytes));
+	my ($exit_status, $output) = $self->vmhost_os->execute($command, 0, 1800);
+	if (!defined($exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command to convert image $node_name: '$command'");
+		return;
+	}
+	elsif ($exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "unable to copy/convert virtual disk on $node_name\ncommand: '$command'\noutput:\n" . join("\n", @$output));
+		return;
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "copied/converted virtual disk on $node_name, command: '$command', output:\n" . join("\n", @$output));
+	}
+	
+	# Calculate how long it took to copy
+	my $duration_seconds = (time - $start_time);
+	my $minutes = ($duration_seconds / 60);
+	$minutes =~ s/\..*//g;
+	my $seconds = ($duration_seconds - ($minutes * 60));
+	if (length($seconds) == 0) {
+		$seconds = "00";
+	}
+	elsif (length($seconds) == 1) {
+		$seconds = "0$seconds";
+	}
+	
+	my $image_size_bytes = $self->vmhost_os->get_file_size($destination_file_path);
+	
+	# Get a string which displays various copy rate information
+	my $copy_speed_info_string = get_copy_speed_info_string($image_size_bytes, $duration_seconds);
+	notify($ERRORS{'OK'}, 0, "copied image on $node_name: $destination_file_path'\n$copy_speed_info_string");
+	
+	# Update the registry if this is a Windows image
+	if ($image_os_type =~ /windows/i && !$self->update_windows_image($destination_file_path)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to make Windows-specific changes to $destination_file_path after it was copied/converted");
+		return;
+	}
+	
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 create_copy_on_write_image
+
+ Parameters  : $master_image_file_path, $copy_on_write_file_path
+ Returns     : boolean
+ Description : Calls qemu-img to create a copy on write virtual disk image based
+               on the master image. The resulting image is written to by the VM
+               when it makes changes to its hard disk. Multiple VMs may utilize
+               the master image file. Each writes to its own copy on write image
+               file. The master image file is not altered.
+
+=cut
+
+sub create_copy_on_write_image {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	my ($master_image_file_path, $copy_on_write_file_path, $disk_format) = @_;
+	
+	if (!$master_image_file_path || !$copy_on_write_file_path) {
+		notify($ERRORS{'WARNING'}, 0, "unable to create copy on write image, master and copy on write image file path arguments were not passed");
+		return;
+	}
+	
+	my $node_name = $self->data->get_vmhost_short_name();
+	
+	if (!$disk_format) {
+		$disk_format = $self->data->get_vmhost_profile_vmdisk_format() || 'qcow2';
+	}
+	
+	notify($ERRORS{'DEBUG'}, 0, "creating copy on write image on $node_name\nmaster disk image: $master_image_file_path\ncopy on write image: $copy_on_write_file_path\nformat: $disk_format");
+	my $command = "qemu-img create -f $disk_format -b \"$master_image_file_path\" \"$copy_on_write_file_path\"";
+	my ($exit_status, $output) = $self->vmhost_os->execute($command);
+	if (!defined($exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command to create copy on write image on $node_name: '$command'");
+		return;
+	}
+	elsif ($exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to create copy on write image on $node_name, command: '$command', output:\n" . join("\n", @$output));
+		return;
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "created copy on write image: $copy_on_write_file_path, output:\n" . join("\n", @$output));
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 update_windows_image
+
+ Parameters  : $virtual_disk_file_path
+ Returns     : boolean
+ Description : Runs virt-win-reg to update the registry of the image specified
+               by the $virtual_disk_file_path argument. The virt-win-reg utility
+               is provided by libguestfs-tools. This subroutine returns true if
+               virt-win-reg isn't installed.
+               
+               Adds registry keys to disable VMware services. If the image is
+               Windows 5.x, registry keys are added to enable the builtin IDE
+               drivers. This allows Windows images converted from VMware using a
+               SCSI virtual disk to be loaded on KVM.
+
+=cut
+
+sub update_windows_image {
+	my $self = shift;
+	unless (ref($self) && $self->isa('VCL::Module')) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be called as a VCL::Module module object method");
+		return;	
+	}
+	
+	my $virtual_disk_file_path = shift;
+	if (!$virtual_disk_file_path) {
+		notify($ERRORS{'WARNING'}, 0, "virtual disk file path argument was not supplied");
+		return;	
+	}
+	
+	my $node_name = $self->data->get_vmhost_short_name();
+	
+	# Construct a string containing .reg file contents
+	# Add keys to disable VMware services if they are installed
+	my $registry_contents .= <<'EOF';
+Windows Registry Editor Version 5.00
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\VClone]
+"Start"=dword:00000004
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\vmci]
+"Start"=dword:00000004
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\vmmouse]
+"Start"=dword:00000004
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\vmscsi]
+"Start"=dword:00000004
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\VMTools]
+"Start"=dword:00000004
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\vmx_svga]
+"Start"=dword:00000004
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\vmxnet]
+"Start"=dword:00000004
+EOF
+	
+	# Check if the guest OS module is for Windows 5.x
+	# Add registry entries to enable the Windows IDE drivers
+	if ($self->os->isa('VCL::Module::OS::Windows::Version_5')) {
+		notify($ERRORS{'DEBUG'}, 0, "guest OS is Windows 5.x, adding registry keys to enable IDE drivers");
+		$registry_contents .= <<'EOF';
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\primary_ide_channel]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="atapi"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\secondary_ide_channel]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="atapi"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\*pnp0600]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="atapi"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\gendisk]
+"ClassGUID"="{4D36E967-E325-11CE-BFC1-08002BE10318}"
+"Service"="disk"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#cc_0101]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_0e11&dev_ae33]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_1039&dev_0601]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_1039&dev_5513]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_1042&dev_1000]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_105a&dev_4d33]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_1095&dev_0640]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_1095&dev_0646]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_1095&dev_0646&REV_05]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_1095&dev_0646&REV_07]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_1095&dev_0648]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_1095&dev_0649]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_1097&dev_0038]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_10ad&dev_0001]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_10ad&dev_0150]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_10b9&dev_5215]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_10b9&dev_5219]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_10b9&dev_5229]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="pciide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_1106&dev_0571]
+"Service"="pciide"
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_8086&dev_1222]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="intelide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_8086&dev_1230]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="intelide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_8086&dev_2411]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="intelide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_8086&dev_2421]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="intelide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_8086&dev_7010]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="intelide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_8086&dev_7111]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="intelide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\CriticalDeviceDatabase\pci#ven_8086&dev_7199]
+"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
+"Service"="intelide"
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\atapi]
+"ErrorControl"=dword:00000001
+"Group"="SCSI miniport"
+"Start"=dword:00000000
+"Tag"=dword:00000019
+"Type"=dword:00000001
+"DisplayName"="Standard IDE/ESDI Hard Disk Controller"
+"ImagePath"=hex(2):53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,44,00,\ 
+  52,00,49,00,56,00,45,00,52,00,53,00,5c,00,61,00,74,00,61,00,70,00,69,00,2e,\ 
+  00,73,00,79,00,73,00,00,00
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\IntelIde]
+"ErrorControl"=dword:00000001
+"Group"="System Bus Extender"
+"Start"=dword:00000000
+"Tag"=dword:00000004
+"Type"=dword:00000001
+"ImagePath"=hex(2):53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,44,00,\ 
+  52,00,49,00,56,00,45,00,52,00,53,00,5c,00,69,00,6e,00,74,00,65,00,6c,00,69,\ 
+  00,64,00,65,00,2e,00,73,00,79,00,73,00,00,00
+
+[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\PCIIde]
+"ErrorControl"=dword:00000001
+"Group"="System Bus Extender"
+"Start"=dword:00000000
+"Tag"=dword:00000003
+"Type"=dword:00000001
+"ImagePath"=hex(2):53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,44,00,\ 
+  52,00,49,00,56,00,45,00,52,00,53,00,5c,00,70,00,63,00,69,00,69,00,64,00,65,\ 
+  00,2e,00,73,00,79,00,73,00,00,00
+EOF
+	}
+	
+	# Create a text file on the VM host containing the registry contents
+	my $virtual_disk_file_base_name = fileparse($virtual_disk_file_path, qr/\.[^\.]*$/i);
+	my $temp_reg_file_path = "/tmp/$virtual_disk_file_base_name.reg";
+	if (!$self->vmhost_os->create_text_file($temp_reg_file_path, $registry_contents)) {
+		return;
+	}
+	
+	# Attempt to run virt-win-reg to merge the registry contents into the registry on the virtual disk
+	notify($ERRORS{'DEBUG'}, 0, "attempting to merge $temp_reg_file_path into $virtual_disk_file_path");
+	my ($exit_status, $output) = $self->vmhost_os->execute("virt-win-reg --merge $virtual_disk_file_path $temp_reg_file_path");
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command to merge $temp_reg_file_path into $virtual_disk_file_path");
+		return;
+	}
+	elsif (grep(/command not found/i, @$output)) {
+		notify($ERRORS{'OK'}, 0, "unable to merge $temp_reg_file_path into $virtual_disk_file_path, virt-win-reg is not installed on $node_name");
+		return 1;
+	}
+	elsif ($exit_status ne '0') {
+		notify($ERRORS{'WARNING'}, 0, "failed to merge $temp_reg_file_path into $virtual_disk_file_path, exit status: $exit_status, output:\n" . join("\n", @$output));
+		return;
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "merged $temp_reg_file_path into $virtual_disk_file_path");
+	}
+	
+	# Delete the temporary registry file on the VM host
+	$self->vmhost_os->delete_file($temp_reg_file_path);
+	
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+1;
+__END__
+
+=head1 SEE ALSO
+
+L<http://cwiki.apache.org/VCL/>
+
+=cut

Propchange: incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/libvirt/KVM.pm
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/libvirt/KVM.pm
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id