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