[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

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



*** 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 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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]