[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
Darryl L. Pierce
dpierce at redhat.com
Fri Oct 3 20:46:38 UTC 2008
*** NOTE ***
This is a work in progress. I'm at the point now where the NFS image is properly
being mounted and the ISO being booted. But, for whatever reason, the VM is
refusing to actually boot the CDROM. I'm sure it's something simple but I
haven't figured out how to get past it.
As it stands now, I can boot from an ISO on a cobbler server if I manually
intervene and tell the VM to boot the CDROM.
A pointer would be nice if someone can apply the patch and see what's going on.
*** NOTE ***
Also added a few helper methods to Vm to contain the knowledge
of how Cobbler integration is contained.
When a user adds an ISO image to the Cobbler server on the appliance,
they will need to do so using the full NFS path for where the virtual
image will go to mount it; i.e., hostname:/path/to/filename.iso
If the filename ends in ".iso" then the virtual machine will mount the
file as a CDROM device and boot it. Otherwise, it mounts it as a hard
disk device.
To add an image to Cobbler, do the following:
1. Download an ISO image, such as the KDE LiveImage from Fedora.
2. Copy it to the NFS directory on the server:
cp *.iso /ovirtnfs/kde-live-cd.iso
3. Add that image to your Cobbler instance:
cobbler image add --name=KDE-LiveCD --file=management.priv.ovirt.org:/ovirtnfs/kde-live-cd.iso
4. Create a new VM in your server.
5. Select "KDE-LiveCD" from the list of operating systems.
6. Save the VM.
7. Start the VM.
It should run the selected ISO.
Signed-off-by: Darryl L. Pierce <dpierce at redhat.com>
---
src/app/controllers/vm_controller.rb | 13 ++++--
src/app/models/vm.rb | 31 ++++++++++++-
src/task-omatic/task_vm.rb | 85 ++++++++++++++++++++++++++++-----
src/test/unit/vm_test.rb | 56 +++++++++++++++++++++-
4 files changed, 163 insertions(+), 22 deletions(-)
diff --git a/src/app/controllers/vm_controller.rb b/src/app/controllers/vm_controller.rb
index f5c0845..8b16c94 100644
--- a/src/app/controllers/vm_controller.rb
+++ b/src/app/controllers/vm_controller.rb
@@ -223,13 +223,18 @@ class VmController < ApplicationController
def _setup_provisioning_options
@provisioning_options = [[Vm::PXE_OPTION_LABEL, Vm::PXE_OPTION_VALUE],
[Vm::HD_OPTION_LABEL, Vm::HD_OPTION_VALUE]]
- # FIXME add cobbler images too
+
begin
+ @provisioning_options += Cobbler::Image.find.collect do |image|
+ [image.name + Vm::COBBLER_IMAGE_SUFFIX,
+ "#{Vm::IMAGE_PREFIX}@#{Vm::COBBLER_PREFIX}#{Vm::PROVISIONING_DELIMITER}#{image.name}"]
+ end
+
@provisioning_options += Cobbler::Profile.find.collect do |profile|
[profile.name + Vm::COBBLER_PROFILE_SUFFIX,
- Vm::COBBLER_PREFIX + Vm::PROVISIONING_DELIMITER +
- Vm::PROFILE_PREFIX + Vm::PROVISIONING_DELIMITER + profile.name]
- end
+ "#{Vm::PROFILE_PREFIX}@#{Vm::COBBLER_PREFIX}#{Vm::PROVISIONING_DELIMITER}#{profile.name}"]
+
+ end
rescue
#if cobbler doesn't respond/is misconfigured/etc just don't add profiles
end
diff --git a/src/app/models/vm.rb b/src/app/models/vm.rb
index ace6fb1..963da84 100644
--- a/src/app/models/vm.rb
+++ b/src/app/models/vm.rb
@@ -49,7 +49,7 @@ class Vm < ActiveRecord::Base
PROFILE_PREFIX = "profile"
IMAGE_PREFIX = "image"
COBBLER_PROFILE_SUFFIX = " (Cobbler Profile)"
- COBBLER_IMAGE_SUFFIX = " (Cobbler Profile)"
+ COBBLER_IMAGE_SUFFIX = " (Cobbler Image)"
PXE_OPTION_LABEL = "PXE Boot"
PXE_OPTION_VALUE = "pxe"
@@ -139,7 +139,12 @@ class Vm < ActiveRecord::Base
end
def provisioning_and_boot_settings=(settings)
- if settings==PXE_OPTION_VALUE
+ # if the settings have a prefix that matches cobber settings, then process
+ # those details
+ if settings =~ /\@#{COBBLER_PREFIX}/
+ self[:boot_device] = BOOT_DEV_NETWORK
+ self[:provisioning] = settings
+ elsif settings==PXE_OPTION_VALUE
self[:boot_device]= BOOT_DEV_NETWORK
self[:provisioning]= nil
elsif settings==HD_OPTION_VALUE
@@ -242,6 +247,28 @@ class Vm < ActiveRecord::Base
vm_resource_pool.search_users
end
+ # Reports whether the VM is uses Cobbler for booting.
+ #
+ def uses_cobbler?
+ (self.provisioning != nil) && (self.provisioning.include? COBBLER_PREFIX)
+ end
+
+ # Returns the cobbler type.
+ #
+ def cobbler_type
+ if self.uses_cobbler?
+ self.provisioning[/^(.*)@/,1]
+ end
+ end
+
+ # Returns the cobbler provisioning name.
+ #
+ def cobbler_name
+ if self.uses_cobbler?
+ self.provisioning[/^.*@.*:(.*)/,1]
+ end
+ end
+
protected
def validate
resources = vm_resource_pool.max_resources_for_vm(self)
diff --git a/src/task-omatic/task_vm.rb b/src/task-omatic/task_vm.rb
index 3588224..0c1be3a 100644
--- a/src/task-omatic/task_vm.rb
+++ b/src/task-omatic/task_vm.rb
@@ -65,16 +65,26 @@ def create_vm_xml(name, uuid, memAllocated, memUsed, vcpus, bootDevice,
doc.root.elements["devices"].add_element("emulator")
doc.root.elements["devices"].elements["emulator"].text = "/usr/bin/qemu-kvm"
- devs = [ 'hda', 'hdb', 'hdc', 'hdd' ]
- i = 0
+ devs = [ 'hda', 'hdb', 'hdd', 'hde', 'hdf' ]
+ which_device = 0
diskDevices.each do |disk|
+ is_cdrom = (disk =~ /\.iso/) ? true : false
+
diskdev = Element.new("disk")
- diskdev.add_attribute("type", "block")
- diskdev.add_attribute("device", "disk")
- diskdev.add_element("source", {"dev" => disk})
- diskdev.add_element("target", {"dev" => devs[i]})
+ diskdev.add_attribute("type", is_cdrom ? "file" : "block")
+ diskdev.add_attribute("device", is_cdrom ? "cdrom" : "disk")
+
+ if is_cdrom
+ diskdev.add_element("readonly")
+ diskdev.add_element("source", {"file" => disk})
+ diskdev.add_element("target", {"dev" => "hdc", "bus" => "ide"})
+ else
+ diskdev.add_element("source", {"dev" => disk})
+ diskdev.add_element("target", {"dev" => devs[which_device]})
+ end
+
doc.root.elements["devices"] << diskdev
- i += 1
+ which_device += 1 unless is_cdrom
end
doc.root.elements["devices"].add_element("interface", {"type" => "bridge"})
@@ -154,15 +164,12 @@ def create_vm(task)
# create cobbler system profile
begin
if vm.provisioning and !vm.provisioning.empty?
- provisioning_arr = vm.provisioning.split(Vm::PROVISIONING_DELIMITER)
- if provisioning_arr[0]==Vm::COBBLER_PREFIX
- if provisioning_arr[1]==Vm::PROFILE_PREFIX
+ if vm.uses_cobbler?
+ if vm.cobbler_type == Vm::PROFILE_PREFIX:
system = Cobbler::System.new('name' => vm.uuid,
'profile' => provisioning_arr[2])
system.interfaces=[Cobbler::NetworkInterface.new({'mac_address' => vm.vnic_mac_addr})]
system.save
- elsif provisioning_arr[1]==Vm::IMAGE_PREFIX
- #FIXME handle cobbler images
end
end
end
@@ -231,6 +238,22 @@ def shutdown_vm(task)
setVmShutdown(vm)
end
+# Find thes storage pool with the given ip address and export path.
+#
+def find_storage_pool(ip_addr, export_path)
+ result = StoragePool.find(:first,
+ :conditions =>
+ ['ip_addr = ? and export_path = ?',ip_addr, export_path])
+
+ if result
+ puts "Found #{ip_addr}:#{export_path}"
+ else
+ puts "FUCK!"
+ end
+
+ return result
+end
+
def start_vm(task)
puts "start_vm"
@@ -268,8 +291,44 @@ def start_vm(task)
conn = Libvirt::open("qemu+tcp://" + host.hostname + "/system")
- storagedevs = connect_storage_pools(conn, vm)
+ # if the VM is an image, then prepare things
+ if vm.uses_cobbler? && (vm.cobbler_type == Vm::IMAGE_PREFIX)
+ details = Cobbler::Image.find_one(vm.cobbler_name)
+ raise Exception.new("Image #{vm.cobbler_name} not found in Cobbler server") unless details
+
+ ignored, ip_addr, export_path, filename =
+ details.file.split(/(.*):(.*)\/(.*)/)
+ found = false
+
+ vm.storage_volumes.each do |volume|
+ if volume.filename == filename
+ if (volume.storage_pool.ip_addr == ip_addr) &&
+ (volume.storage_pool.export_path == export_path)
+ found = true
+ end
+ end
+ end
+
+ unless found
+ puts "Creating a new NFS storage volume"
+ image_volume = StorageVolume.factory("NFS",
+ :filename => filename
+ )
+
+ image_volume.storage_pool
+ image_pool = find_storage_pool(ip_addr, export_path)
+ if image_pool
+ image_pool.storage_volumes << image_volume
+ image_pool.save!
+ end
+ vm.storage_volumes << image_volume
+ vm.save!
+ end
+ end
+
+ storagedevs = connect_storage_pools(conn, vm)
+
# FIXME: get rid of the hardcoded bridge
xml = create_vm_xml(vm.description, vm.uuid, vm.memory_allocated,
vm.memory_used, vm.num_vcpus_allocated, vm.boot_device,
diff --git a/src/test/unit/vm_test.rb b/src/test/unit/vm_test.rb
index 4a5e353..0186c5f 100644
--- a/src/test/unit/vm_test.rb
+++ b/src/test/unit/vm_test.rb
@@ -22,8 +22,58 @@ require File.dirname(__FILE__) + '/../test_helper'
class VmTest < Test::Unit::TestCase
fixtures :vms
- # Replace this with your real tests.
- def test_truth
- assert true
+ def setup
+ @vm_name = "Test"
+ @no_cobbler_provisioning = "#{@vm_name}"
+ @cobbler_image_provisioning =
+ "#{Vm::IMAGE_PREFIX}@#{Vm::COBBLER_PREFIX}#{Vm::PROVISIONING_DELIMITER}#{@vm_name}"
+ @cobbler_profile_provisioning =
+ "#{Vm::PROFILE_PREFIX}@#{Vm::COBBLER_PREFIX}#{Vm::PROVISIONING_DELIMITER}#{@vm_name}"
+ end
+
+ # Ensures that, if the VM does not contain the Cobbler prefix, that it
+ # does not claim to be a Cobbler VM.
+ #
+ def test_uses_cobbler_without_cobbler_prefix
+ vm = Vm.new
+
+ vm.provisioning_and_boot_settings=@no_cobbler_provisioning
+
+ flunk "VM is not a Cobbler provisioned one." if vm.uses_cobbler?
+ assert_equal @vm_name, vm.provisioning, "Wrong name reported."
+ end
+
+ # Ensures that the VM reports that it uses Cobbler if the provisioning
+ # is for a Cobbler profile.
+ #
+ def test_uses_cobbler_with_cobbler_profile
+ vm = Vm.new
+
+ vm.provisioning_and_boot_settings = @cobbler_profile_provisioning
+
+ flunk "VM did not report that it's Cobbler provisioned." unless vm.uses_cobbler?
+ assert_equal Vm::PROFILE_PREFIX,
+ vm.cobbler_type,
+ "Wrong cobbler type reported."
+ assert_equal @vm_name,
+ vm.cobbler_name,
+ "Wrong name reported."
+ end
+
+ # Ensures that the VM reports that it uses Cobbler if the provisioning
+ # is for a Cobbler image.
+ #
+ def test_uses_cobbler_with_cobbler_image
+ vm = Vm.new
+
+ vm.provisioning_and_boot_settings = @cobbler_image_provisioning
+
+ flunk "VM did not report that it's Cobbler provisioned." unless vm.uses_cobbler?
+ assert_equal Vm::IMAGE_PREFIX,
+ vm.cobbler_type,
+ "Wrong cobbler type reported."
+ assert_equal @vm_name,
+ vm.cobbler_name,
+ "Wrong name reported."
end
end
--
1.5.5.1
More information about the ovirt-devel
mailing list