[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
Darryl L. Pierce
dpierce at redhat.com
Mon Oct 13 16:45:02 UTC 2008
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 | 51 +++++++++++++---------
src/app/models/vm.rb | 34 ++++++++++++++-
src/task-omatic/task_vm.rb | 76 ++++++++++++++++++++++++++++------
src/task-omatic/utils.rb | 4 +-
src/test/unit/vm_test.rb | 56 +++++++++++++++++++++++-
5 files changed, 180 insertions(+), 41 deletions(-)
diff --git a/src/app/controllers/vm_controller.rb b/src/app/controllers/vm_controller.rb
index f5c0845..bc88760 100644
--- a/src/app/controllers/vm_controller.rb
+++ b/src/app/controllers/vm_controller.rb
@@ -48,6 +48,7 @@ class VmController < ApplicationController
begin
Vm.transaction do
@vm.save!
+ _setup_vm_provision(params)
@task = VmTask.new({ :user => @user,
:vm_id => @vm.id,
:action => VmTask::ACTION_CREATE_VM,
@@ -70,7 +71,7 @@ class VmController < ApplicationController
alert = "VM was successfully created."
end
render :json => { :object => "vm", :success => true, :alert => alert }
- rescue
+ rescue Exception => error
# FIXME: need to distinguish vm vs. task save errors (but should mostly be vm)
render :json => { :object => "vm", :success => false,
:errors => @vm.errors.localize_error_messages.to_a }
@@ -223,13 +224,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
@@ -239,24 +245,27 @@ class VmController < ApplicationController
def _setup_vm_provision(params)
# spaces are invalid in the cobbler name
name = params[:vm][:uuid]
- provision = params[:vm][:provisioning_and_boot_settings].gsub(
- Vm::COBBLER_PREFIX + Vm::PROVISIONING_DELIMITER +
- Vm::PROFILE_PREFIX + Vm::PROVISIONING_DELIMITER, "")
mac = params[:vm][:vnic_mac_addr]
- unless provision == Vm::PXE_OPTION_VALUE or
- provision == Vm::HD_OPTION_VALUE
- found = false
- Cobbler::System.find.each{ |system|
- if system.name == name
- system.profile = provision
- system.save
- found = true
+ provision = params[:vm][:provisioning_and_boot_settings]
+ # determine what type of provisioning was selected for the VM
+ provisioning_type = :pxe_or_hd_type
+ provisioning_type = :image_type if provision.index "#{Vm::IMAGE_PREFIX}@#{Vm::COBBLER_PREFIX}"
+ provisioning_type = :system_type if provision.index "#{Vm::PROFILE_PREFIX}@#{Vm::COBBLER_PREFIX}"
+
+ unless provisioning_type == :pxe_or_hd_type
+ cobbler_name = provision.slice(/(.*):(.*)/, 2)
+ system = Cobbler::System.find_one(name)
+ unless system
+ nic = Cobbler::NetworkInterface.new({'mac_address' => mac})
+
+ case provisioning_type
+ when :image_type:
+ system = Cobbler::System.new("name" => name, "image" => cobbler_name)
+ when :system_type:
+ system = Cobbler::System.new("name" => name, "profile" => cobbler_name)
end
- }
- unless found
- system = Cobbler::System.create("name" => name,
- "profile" => provision)
- system.interfaces=[Cobbler::NetworkInterface.new({'mac_address' => mac})]
+
+ system.interfaces = [nic]
system.save
end
end
diff --git a/src/app/models/vm.rb b/src/app/models/vm.rb
index ace6fb1..d7beacf 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,15 @@ 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 =~ /#{IMAGE_PREFIX}@#{COBBLER_PREFIX}/
+ self[:boot_device] = BOOT_DEV_CDROM
+ self[:provisioning] = settings
+ elsif settings =~ /#{PROFILE_PREFIX}@#{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 +250,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..8398e83 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', 'hdc', 'hdd']
+ 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" => devs[which_device], "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
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
@@ -266,9 +273,52 @@ def start_vm(task)
# hosts to see if there is a host that will fit these constraints
host = findHostSLA(vm)
+ # if we're booting from a CDROM the VM is an image,
+ # then we need to add the NFS mount as a storage volume for this
+ # boot
+ #
+ if (vm.boot_device == Vm::BOOT_DEV_CDROM) && vm.uses_cobbler? && (vm.cobbler_type == Vm::IMAGE_PREFIX)
+ details = Cobbler::Image.find_one(vm.cobbler_name)
+
+ raise "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
+ # Create a new transient NFS storage volume
+ # This volume is *not* persisted.
+ image_volume = StorageVolume.factory("NFS",
+ :filename => filename
+ )
+
+ image_volume.storage_pool
+ image_pool = StoragePool.factory(StoragePool::NFS)
+
+ image_pool.ip_addr = ip_addr
+ image_pool.export_path = export_path
+ image_pool.storage_volumes << image_volume
+ image_volume.storage_pool = image_pool
+ end
+ end
+
conn = Libvirt::open("qemu+tcp://" + host.hostname + "/system")
- storagedevs = connect_storage_pools(conn, vm)
+ volumes = []
+ volumes += vm.storage_volumes
+ volumes << image_volume if image_volume
+ storagedevs = connect_storage_pools(conn, volumes)
# FIXME: get rid of the hardcoded bridge
xml = create_vm_xml(vm.description, vm.uuid, vm.memory_allocated,
diff --git a/src/task-omatic/utils.rb b/src/task-omatic/utils.rb
index 9e60122..47a4543 100644
--- a/src/task-omatic/utils.rb
+++ b/src/task-omatic/utils.rb
@@ -67,7 +67,7 @@ def teardown_storage_pools(conn)
end
end
-def connect_storage_pools(conn, vm)
+def connect_storage_pools(conn, storage_volumes)
# here, build up a list of already defined pools. We'll use it
# later to see if we need to define new pools for the storage or just
# keep using existing ones
@@ -78,7 +78,7 @@ def connect_storage_pools(conn, vm)
end
storagedevs = []
- vm.storage_volumes.each do |volume|
+ storage_volumes.each do |volume|
# here, we need to iterate through each volume and possibly attach it
# to the host we are going to be using
storage_pool = volume.storage_pool
diff --git a/src/test/unit/vm_test.rb b/src/test/unit/vm_test.rb
index 4a5e353..22164e8 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