[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.

Darryl L. Pierce dpierce at redhat.com
Thu Oct 2 13:24:15 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 |   13 +++++--
 src/app/models/vm.rb                 |   31 +++++++++++++++-
 src/task-omatic/task_vm.rb           |   63 ++++++++++++++++++++++++++++------
 src/test/unit/vm_test.rb             |   57 +++++++++++++++++++++++++++++--
 4 files changed, 144 insertions(+), 20 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..2e2ddb5 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.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..101878b 100644
--- a/src/task-omatic/task_vm.rb
+++ b/src/task-omatic/task_vm.rb
@@ -25,7 +25,7 @@ gem 'cobbler'
 require 'cobbler'
 
 def create_vm_xml(name, uuid, memAllocated, memUsed, vcpus, bootDevice,
-                  macAddr, bridge, diskDevices)
+                  macAddr, bridge, diskDevices, bootdrive)
   doc = Document.new
 
   doc.add_element("domain", {"type" => "kvm"})
@@ -65,16 +65,28 @@ 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', 'hde', 'hdf' ]
+  which_device = 0
   diskDevices.each do |disk|
     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_element("target", {"dev" => devs[which_device]})
     doc.root.elements["devices"] << diskdev
-    i += 1
+    which_device += 1
+  end
+
+  if bootdrive
+    bootdisk = Element.new("disk")
+
+    bootdisk.add_attribute("type", bootdisk[:type])
+    bootdisk.add_attribute("device", bootdisk[:device])
+    bootdisk.add_element("source", {"file" => bootdrive[:source]})
+    bootdisk.add_element("target", {"dev" => devs[which_device], "bus" => "ide"})
+    bootdisk.add_element("readonly") if bootdrive[:readonly]
+
+    doc.root.elements["devices"] << bootdisk
   end
 
   doc.root.elements["devices"].add_element("interface", {"type" => "bridge"})
@@ -154,15 +166,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
@@ -268,12 +277,44 @@ def start_vm(task)
 
     conn = Libvirt::open("qemu+tcp://" + host.hostname + "/system")
 
+    # 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, protocol, sharepath, filename = details.filename(/(.*):\/\/(.*)\/(.*)/)
+
+        raise Exception.new("Only NFS mounts are supported currently") unless protocol == 'nfs'
+
+        # if the file's an .ISO then we'll have to mount as a CDrom, otherwise
+        # we'll mount as a hard drive
+        bootdrive[:type]     = 'file'
+        bootdrive[:source]   = filename
+        bootdrive[:path]     = sharepath
+        if details.file[/.iso/]
+          bootdrive[:readonly] = true
+          bootdrive[:device]   = 'cdrom'
+        else
+          bootdrive[:readonly] = false
+          bootdrive[:device]   = 'disk'
+        end
+
+        # mount the filesystem
+        mountpoint = `mktemp -d`
+        bootdrive[:mountpoint] = mountpoint
+
+        unless system("mount #{sharepath} #{mountpoint}")
+          raise Exception.new "Unable to mount #{details.file}"
+        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,
-                        vm.vnic_mac_addr, "ovirtbr0", storagedevs)
+                        vm.vnic_mac_addr, "ovirtbr0", storagedevs, bootdrive)
 
     dom = conn.define_domain_xml(xml.to_s)
     dom.create
diff --git a/src/test/unit/vm_test.rb b/src/test/unit/vm_test.rb
index 4a5e353..c226c63 100644
--- a/src/test/unit/vm_test.rb
+++ b/src/test/unit/vm_test.rb
@@ -22,8 +22,59 @@ 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