Darryl L. Pierce
2008-Sep-24 21:09 UTC
[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
Also added a few helper methods to Vm to contain the knowledge
of how Cobbler integration is containined.
This patch is not complete. I'm going to be away for a few days and am
putting this out there if someone else has time to pick it up. Otherwise,
I'll continue with it on Monday.
Signed-off-by: Darryl L. Pierce <dpierce at redhat.com>
---
src/app/controllers/vm_controller.rb | 10 +++++-
src/app/models/vm.rb | 31 +++++++++++++++++-
src/task-omatic/task_vm.rb | 22 ++++++++++---
src/test/unit/vm_test.rb | 57 ++++++++++++++++++++++++++++++++--
4 files changed, 108 insertions(+), 12 deletions(-)
diff --git a/src/app/controllers/vm_controller.rb
b/src/app/controllers/vm_controller.rb
index b6192e2..bf50980 100644
--- a/src/app/controllers/vm_controller.rb
+++ b/src/app/controllers/vm_controller.rb
@@ -224,10 +224,16 @@ 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, profile.name]
+ [profile.name + Vm::COBBLER_PROFILE_SUFFIX,
+
"#{Vm::PROFILE_PREFIX}@#{Vm::COBBLER_PREFIX}:#{profile.name}"]
end
rescue
diff --git a/src/app/models/vm.rb b/src/app/models/vm.rb
index ace6fb1..a5def2b 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
@@ -241,6 +246,28 @@ class Vm < ActiveRecord::Base
def search_users
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
diff --git a/src/task-omatic/task_vm.rb b/src/task-omatic/task_vm.rb
index 3588224..11b7b8a 100644
--- a/src/task-omatic/task_vm.rb
+++ b/src/task-omatic/task_vm.rb
@@ -154,15 +154,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
@@ -267,6 +264,21 @@ def start_vm(task)
host = findHostSLA(vm)
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
+
+ # if the file's an .ISO then we'll have to mount as a CDrom,
otherwise
+ # we'll mount as a hard drive
+ if details.file[/.iso/]
+ # TODO mount details.file as a CDROM
+ else
+ # TODO mount details.file as a hard disk
+ end
+ end
storagedevs = connect_storage_pools(conn, vm)
diff --git a/src/test/unit/vm_test.rb b/src/test/unit/vm_test.rb
index 4a5e353..2fbdf59 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
Darryl L. Pierce
2008-Oct-01 14:17 UTC
[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
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 URL for where the virtual
image will go to mount it; i.e., nfs://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.
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
Darryl L. Pierce
2008-Oct-02 13:24 UTC
[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
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
Darryl L. Pierce
2008-Oct-03 20:46 UTC
[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 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
Darryl L. Pierce
2008-Oct-08 19:04 UTC
[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
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 | 50 +++++++++++++---------
src/app/models/vm.rb | 34 ++++++++++++++-
src/task-omatic/task_vm.rb | 78 ++++++++++++++++++++++++++++-----
src/test/unit/vm_test.rb | 56 +++++++++++++++++++++++-
4 files changed, 181 insertions(+), 37 deletions(-)
diff --git a/src/app/controllers/vm_controller.rb
b/src/app/controllers/vm_controller.rb
index f5c0845..7f911e1 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
@@ -239,24 +244,29 @@ 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 =~ /Vm::IMAGE_PREFIX at
Vm::COBBLER_PREFIX/
+ provisioning_type = :system_type if provision =~ /Vm::PROFILE_PREFIX at
Vm::COBBLER_PREFIX/
+
+ unless provisioning_type == :pxe_or_hd_type
+ cobbler_name =
provision.gsub(/(Vm::IMAGE_PREFIX|Vm::PROFILE_PREFIX)@Vm::COBBLER_PREFIX/,
'')
+
+ system = Cobbler::System.find_one(name)
+
+ unless system
+ nic = Cobbler::NetworkInterface.new({'mac_address' => mac})
+
+ case provisioning_type
+ when :image_type:
+ system = Cobbler::System.create("name" => name,
"image" => cobbler_name)
+ when :system_type:
+ system = Cobbler::System.create("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..e37b88e 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 =~ /#{COBBLER_IMAGE}\@#{COBBLER_PREFIX}/
+ self[:boot_device] = BOOT_DEV_CDROM
+ self[:provisiong] = settings
+ elsif settings =~ /#{COBBLER_PROFILE}\@#{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..afac66d 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
@@ -231,6 +238,14 @@ 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)
+ StoragePool.find(:first,
+ :conditions =>
+ ['ip_addr = ? and export_path = ?',ip_addr, export_path])
+end
+
def start_vm(task)
puts "start_vm"
@@ -266,6 +281,45 @@ 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 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
+ # 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 = 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
+ end
+ end
+
conn = Libvirt::open("qemu+tcp://" + host.hostname +
"/system")
storagedevs = connect_storage_pools(conn, vm)
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
Darryl L. Pierce
2008-Oct-08 19:53 UTC
[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
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 | 50 +++++++++++++---------
src/app/models/vm.rb | 34 ++++++++++++++-
src/task-omatic/task_vm.rb | 78 ++++++++++++++++++++++++++++-----
src/test/unit/vm_test.rb | 56 +++++++++++++++++++++++-
4 files changed, 181 insertions(+), 37 deletions(-)
diff --git a/src/app/controllers/vm_controller.rb
b/src/app/controllers/vm_controller.rb
index f5c0845..0ec2837 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
@@ -239,24 +244,29 @@ 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 =~ /Vm::IMAGE_PREFIX at
Vm::COBBLER_PREFIX/
+ provisioning_type = :system_type if provision =~ /Vm::PROFILE_PREFIX at
Vm::COBBLER_PREFIX/
+
+ unless provisioning_type == :pxe_or_hd_type
+ cobbler_name =
provision.gsub(/(Vm::IMAGE_PREFIX|Vm::PROFILE_PREFIX)@Vm::COBBLER_PREFIX/,
'')
+
+ system = Cobbler::System.find_one(name)
+
+ unless system
+ nic = Cobbler::NetworkInterface.new({'mac_address' => mac})
+
+ case provisioning_type
+ when :image_type:
+ system = Cobbler::System.create("name" => name,
"image" => cobbler_name)
+ when :system_type:
+ system = Cobbler::System.create("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..e37b88e 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 =~ /#{COBBLER_IMAGE}\@#{COBBLER_PREFIX}/
+ self[:boot_device] = BOOT_DEV_CDROM
+ self[:provisiong] = settings
+ elsif settings =~ /#{COBBLER_PROFILE}\@#{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..6adda3b 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
@@ -231,6 +238,14 @@ 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)
+ StoragePool.find(:first,
+ :conditions =>
+ ['ip_addr = ? and export_path = ?',ip_addr, export_path])
+end
+
def start_vm(task)
puts "start_vm"
@@ -266,6 +281,45 @@ 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 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
+ # 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 = 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
+ end
+ end
+
conn = Libvirt::open("qemu+tcp://" + host.hostname +
"/system")
storagedevs = connect_storage_pools(conn, vm)
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
Darryl L. Pierce
2008-Oct-09 15:12 UTC
[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
The NFS export for Cobbler needs to be added as an NFS storage pool.
Otherwise, taskomatic will not be able to locate it.
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 | 50 ++++++++++++--------
src/app/models/vm.rb | 34 +++++++++++++-
src/task-omatic/task_vm.rb | 83 +++++++++++++++++++++++++++++-----
src/test/unit/vm_test.rb | 56 +++++++++++++++++++++-
4 files changed, 186 insertions(+), 37 deletions(-)
diff --git a/src/app/controllers/vm_controller.rb
b/src/app/controllers/vm_controller.rb
index f5c0845..0ec2837 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
@@ -239,24 +244,29 @@ 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 =~ /Vm::IMAGE_PREFIX at
Vm::COBBLER_PREFIX/
+ provisioning_type = :system_type if provision =~ /Vm::PROFILE_PREFIX at
Vm::COBBLER_PREFIX/
+
+ unless provisioning_type == :pxe_or_hd_type
+ cobbler_name =
provision.gsub(/(Vm::IMAGE_PREFIX|Vm::PROFILE_PREFIX)@Vm::COBBLER_PREFIX/,
'')
+
+ system = Cobbler::System.find_one(name)
+
+ unless system
+ nic = Cobbler::NetworkInterface.new({'mac_address' => mac})
+
+ case provisioning_type
+ when :image_type:
+ system = Cobbler::System.create("name" => name,
"image" => cobbler_name)
+ when :system_type:
+ system = Cobbler::System.create("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..982613f 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
@@ -231,6 +238,14 @@ 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)
+ StoragePool.find(:first,
+ :conditions =>
+ ['ip_addr = ? and export_path = ?',ip_addr, export_path])
+end
+
def start_vm(task)
puts "start_vm"
@@ -266,6 +281,50 @@ 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 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
+ # 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 = find_storage_pool(ip_addr, export_path)
+
+ raise Exception.new("Unable to find Cobbler storage pool")
unless image_pool
+
+ if image_pool
+ image_pool.storage_volumes << image_volume
+ image_pool.save!
+ end
+ vm.storage_volumes << image_volume
+ vm.save!
+ end
+ end
+
conn = Libvirt::open("qemu+tcp://" + host.hostname +
"/system")
storagedevs = connect_storage_pools(conn, vm)
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
Darryl L. Pierce
2008-Oct-09 15:17 UTC
[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
The NFS export for Cobbler needs to be added as an NFS storage pool.
Otherwise, taskomatic will not be able to locate it.
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 | 50 ++++++++++++--------
src/app/models/vm.rb | 34 +++++++++++++-
src/task-omatic/task_vm.rb | 81 +++++++++++++++++++++++++++++-----
src/test/unit/vm_test.rb | 56 ++++++++++++++++++++++-
4 files changed, 184 insertions(+), 37 deletions(-)
diff --git a/src/app/controllers/vm_controller.rb
b/src/app/controllers/vm_controller.rb
index f5c0845..0ec2837 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
@@ -239,24 +244,29 @@ 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 =~ /Vm::IMAGE_PREFIX at
Vm::COBBLER_PREFIX/
+ provisioning_type = :system_type if provision =~ /Vm::PROFILE_PREFIX at
Vm::COBBLER_PREFIX/
+
+ unless provisioning_type == :pxe_or_hd_type
+ cobbler_name =
provision.gsub(/(Vm::IMAGE_PREFIX|Vm::PROFILE_PREFIX)@Vm::COBBLER_PREFIX/,
'')
+
+ system = Cobbler::System.find_one(name)
+
+ unless system
+ nic = Cobbler::NetworkInterface.new({'mac_address' => mac})
+
+ case provisioning_type
+ when :image_type:
+ system = Cobbler::System.create("name" => name,
"image" => cobbler_name)
+ when :system_type:
+ system = Cobbler::System.create("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..ef0f8e2 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
@@ -231,6 +238,14 @@ 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)
+ StoragePool.find(:first,
+ :conditions =>
+ ['ip_addr = ? and export_path = ?',ip_addr, export_path])
+end
+
def start_vm(task)
puts "start_vm"
@@ -266,6 +281,48 @@ 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 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
+ # 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 = find_storage_pool(ip_addr, export_path)
+
+ raise Exception.new("Unable to find Cobbler storage pool")
unless image_pool
+
+ if image_pool
+ image_pool.storage_volumes << image_volume
+ end
+ vm.storage_volumes << image_volume
+ end
+ end
+
conn = Libvirt::open("qemu+tcp://" + host.hostname +
"/system")
storagedevs = connect_storage_pools(conn, vm)
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
Darryl L. Pierce
2008-Oct-10 14:43 UTC
[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
The NFS export for Cobbler needs to be added as an NFS storage pool.
Otherwise, taskomatic will not be able to locate it.
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 | 81 +++++++++++++++++++++++++++++-----
src/test/unit/vm_test.rb | 56 ++++++++++++++++++++++-
4 files changed, 185 insertions(+), 37 deletions(-)
diff --git a/src/app/controllers/vm_controller.rb
b/src/app/controllers/vm_controller.rb
index f5c0845..5e9da06 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,
@@ -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,29 @@ 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.gsub(/(Vm::IMAGE_PREFIX|Vm::PROFILE_PREFIX)@Vm::COBBLER_PREFIX/,
'')
+
+ system = Cobbler::System.find_one(name)
+
+ unless system
+ nic = Cobbler::NetworkInterface.new({'mac_address' => mac})
+
+ case provisioning_type
+ when :image_type:
+ system = Cobbler::System.create("name" => name,
"image" => cobbler_name)
+ when :system_type:
+ system = Cobbler::System.create("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..6c4ace6 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
@@ -231,6 +238,14 @@ 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)
+ StoragePool.find(:first,
+ :conditions =>
+ ['ip_addr = ? and export_path = ?',ip_addr, export_path])
+end
+
def start_vm(task)
puts "start_vm"
@@ -266,6 +281,48 @@ 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 = find_storage_pool(ip_addr, export_path)
+
+ raise Exception.new("Unable to find Cobbler storage pool")
unless image_pool
+
+ if image_pool
+ image_pool.storage_volumes << image_volume
+ end
+ vm.storage_volumes << image_volume
+ end
+ end
+
conn = Libvirt::open("qemu+tcp://" + host.hostname +
"/system")
storagedevs = connect_storage_pools(conn, vm)
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
Darryl L. Pierce
2008-Oct-10 19:55 UTC
[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
The NFS export for Cobbler needs to be added as an NFS storage pool.
Otherwise, taskomatic will not be able to locate it.
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 | 81 +++++++++++++++++++++++++++++-----
src/test/unit/vm_test.rb | 56 ++++++++++++++++++++++-
4 files changed, 184 insertions(+), 38 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..6c4ace6 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
@@ -231,6 +238,14 @@ 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)
+ StoragePool.find(:first,
+ :conditions =>
+ ['ip_addr = ? and export_path = ?',ip_addr, export_path])
+end
+
def start_vm(task)
puts "start_vm"
@@ -266,6 +281,48 @@ 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 = find_storage_pool(ip_addr, export_path)
+
+ raise Exception.new("Unable to find Cobbler storage pool")
unless image_pool
+
+ if image_pool
+ image_pool.storage_volumes << image_volume
+ end
+ vm.storage_volumes << image_volume
+ end
+ end
+
conn = Libvirt::open("qemu+tcp://" + host.hostname +
"/system")
storagedevs = connect_storage_pools(conn, vm)
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
Darryl L. Pierce
2008-Oct-11 11:29 UTC
[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
The NFS export for Cobbler needs to be added as an NFS storage pool.
Otherwise, taskomatic will not be able to locate it.
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 | 84 +++++++++++++++++++++++++++++-----
src/test/unit/vm_test.rb | 56 +++++++++++++++++++++-
4 files changed, 187 insertions(+), 38 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..72ad9d8 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
@@ -231,6 +238,14 @@ 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)
+ StoragePool.find(:first,
+ :conditions =>
+ ['ip_addr = ? and export_path = ?',ip_addr, export_path])
+end
+
def start_vm(task)
puts "start_vm"
@@ -266,6 +281,48 @@ 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 = find_storage_pool(ip_addr, export_path)
+
+ raise Exception.new("Unable to find Cobbler storage pool")
unless image_pool
+
+ if image_pool
+ image_pool.storage_volumes << image_volume
+ end
+ vm.storage_volumes << image_volume
+ end
+ end
+
conn = Libvirt::open("qemu+tcp://" + host.hostname +
"/system")
storagedevs = connect_storage_pools(conn, vm)
@@ -277,6 +334,9 @@ def start_vm(task)
dom = conn.define_domain_xml(xml.to_s)
dom.create
+
+ # Remove the transient storage volume so that it's not persisted
+ vm.storage_volumes.delete image_volume defined? image_volume
setVmVncPort(vm, dom)
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
Darryl L. Pierce
2008-Oct-13 15:30 UTC
[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
The NFS export for Cobbler needs to be added as an NFS storage pool.
Otherwise, taskomatic will not be able to locate it.
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 | 91 +++++++++++++++++++++++++++++-----
src/test/unit/vm_test.rb | 56 ++++++++++++++++++++-
4 files changed, 194 insertions(+), 38 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..40e0a1d 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
@@ -231,6 +238,21 @@ 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)
+ pool = StoragePool.factory(StoragePool::NFS)
+
+ pool.ip_addr = ip_addr
+ pool.export_path = export_path
+
+ return pool
+
+ #StoragePool.find(:first,
+ # :conditions =>
+ # ['ip_addr = ? and export_path = ?',ip_addr, export_path])
+end
+
def start_vm(task)
puts "start_vm"
@@ -266,6 +288,48 @@ 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 = find_storage_pool(ip_addr, export_path)
+
+ raise Exception.new("Unable to find Cobbler storage pool")
unless image_pool
+
+ if image_pool
+ image_pool.storage_volumes << image_volume
+ end
+ vm.storage_volumes << image_volume
+ end
+ end
+
conn = Libvirt::open("qemu+tcp://" + host.hostname +
"/system")
storagedevs = connect_storage_pools(conn, vm)
@@ -278,6 +342,9 @@ def start_vm(task)
dom = conn.define_domain_xml(xml.to_s)
dom.create
+ # Remove the transient storage volume so that it's not persisted
+ vm.storage_volumes.delete image_volume defined? image_volume
+
setVmVncPort(vm, dom)
conn.close
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
Darryl L. Pierce
2008-Oct-13 16:45 UTC
[Ovirt-devel] [PATCH server] Added support for booting a VM from an ISO image.
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