Skip to content

Commit 36bba07

Browse files
committed
Add bootdisk support
1 parent 40bd584 commit 36bba07

File tree

13 files changed

+695
-5
lines changed

13 files changed

+695
-5
lines changed

lib/fog/libvirt/compute.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ class Compute < Fog::Service
4545
request :get_node_info
4646
request :update_autostart
4747
request :update_display
48+
request :upload_iso
49+
request :attach_iso
50+
request :detach_iso
51+
request :destroy_iso
4852
request :libversion
4953

5054
module Shared

lib/fog/libvirt/models/compute/server.rb

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,33 @@ def initialize(attributes={} )
6161
@user_data = attributes.delete(:user_data)
6262
end
6363

64+
def upload_iso(file_path, volume_name = nil, pool_name = nil)
65+
raise ArgumentError, "file_path is a required parameter" if file_path.nil?
66+
67+
volume_name ||= File.basename(file_path)
68+
pool_name ||= default_iso_pool_name
69+
service.upload_iso(pool_name, volume_name, file_path)
70+
end
71+
72+
def attach_iso(path, options = {})
73+
raise ArgumentError, "path is a required parameter" if path.nil?
74+
75+
iso_path = path.start_with?("/") ? path : File.join(iso_dir || default_iso_dir, path)
76+
service.attach_iso(uuid, iso_path, options)
77+
end
78+
79+
def detach_iso(options = {})
80+
service.detach_iso(uuid, options)
81+
end
82+
83+
def destroy_iso(volume_name, pool_name = nil)
84+
raise ArgumentError, "volume_name is a required parameter" if volume_name.nil?
85+
86+
iso_name = File.basename(volume_name)
87+
pool_name ||= service.volumes.all(:name => iso_name).first&.pool_name || default_iso_pool_name
88+
service.destroy_iso(pool_name, iso_name)
89+
end
90+
6491
def new?
6592
uuid.nil?
6693
end
@@ -531,14 +558,15 @@ def create_or_clone_volume
531558
@volumes.nil? ? @volumes = [volume] : @volumes << volume
532559
end
533560

534-
def default_iso_dir
535-
"/var/lib/libvirt/images"
536-
end
537-
538561
def default_volume_name
539562
"#{name}.#{volume_format_type || 'img'}"
540563
end
541564

565+
def default_iso_pool_name
566+
volume = volumes&.first
567+
volume&.pool_name || "default"
568+
end
569+
542570
def defaults
543571
{
544572
:persistent => true,

lib/fog/libvirt/models/compute/util/util.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ def xml_elements(xml, path, attribute=nil)
1717
def randomized_name
1818
"fog-#{(SecureRandom.random_number*10E14).to_i.round}"
1919
end
20+
21+
def default_iso_dir
22+
"/var/lib/libvirt/images"
23+
end
2024
end
2125
end
2226
end
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
require "nokogiri"
2+
3+
module Fog
4+
module Libvirt
5+
class Compute
6+
module Shared
7+
DEFAULT_CDROM_TARGET_DEV = "sdc".freeze
8+
DEFAULT_CDROM_BUS = "sata".freeze
9+
10+
def attach_iso(uuid, iso_path, options = {})
11+
raise ArgumentError, "uuid is a required parameter" if uuid.nil?
12+
raise ArgumentError, "iso_path is a required parameter" if iso_path.nil?
13+
14+
options ||= {}
15+
raise ArgumentError, "options must be a hash" unless options.is_a?(Hash)
16+
17+
target_dev = options.fetch(:target_dev, DEFAULT_CDROM_TARGET_DEV)
18+
bus = options.fetch(:bus, DEFAULT_CDROM_BUS)
19+
flags = options.fetch(:flags, ::Libvirt::Domain::AFFECT_CONFIG)
20+
21+
resolved_iso_path = iso_path.start_with?("/") ? iso_path : File.join(default_iso_dir, iso_path)
22+
xml = attach_cdrom_xml(resolved_iso_path, target_dev, bus)
23+
24+
domain = client.lookup_domain_by_uuid(uuid)
25+
begin
26+
domain.attach_device(xml, flags)
27+
rescue ::Libvirt::Error => e
28+
begin
29+
domain.update_device(xml, flags)
30+
rescue ::Libvirt::Error
31+
raise e
32+
end
33+
end
34+
35+
# if we get no exception, we assume the operation was successful
36+
true
37+
end
38+
39+
private
40+
41+
def attach_cdrom_xml(iso_path, target_dev, bus)
42+
Nokogiri::XML::Builder.new do |x|
43+
x.disk(:type => "file", :device => "cdrom") do
44+
x.driver(:name => "qemu", :type => "raw")
45+
x.source(:file => iso_path)
46+
x.target(:dev => target_dev, :bus => bus)
47+
x.readonly
48+
end
49+
end.to_xml
50+
end
51+
end
52+
53+
class Real
54+
include Shared
55+
end
56+
57+
class Mock
58+
include Shared
59+
end
60+
end
61+
end
62+
end
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
module Fog
2+
module Libvirt
3+
class Compute
4+
module Shared
5+
def destroy_iso(pool_name, volume_name)
6+
raise ArgumentError, "pool_name is a required parameter" if pool_name.nil?
7+
raise ArgumentError, "volume_name is a required parameter" if volume_name.nil?
8+
9+
pool = client.lookup_storage_pool_by_name(pool_name)
10+
begin
11+
pool.lookup_volume_by_name(volume_name).delete
12+
rescue ::Libvirt::RetrieveError
13+
# already absent, treat as success if not present afterwards
14+
end
15+
16+
# if the ISO is absent, then we are good
17+
volume_absent?(pool, volume_name)
18+
end
19+
20+
private
21+
22+
def volume_absent?(pool, volume_name)
23+
pool.lookup_volume_by_name(volume_name)
24+
false
25+
rescue ::Libvirt::RetrieveError
26+
true
27+
end
28+
end
29+
30+
class Real
31+
include Shared
32+
end
33+
34+
class Mock
35+
include Shared
36+
end
37+
end
38+
end
39+
end
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
require "nokogiri"
2+
3+
module Fog
4+
module Libvirt
5+
class Compute
6+
module Shared
7+
def detach_iso(uuid, options = {})
8+
raise ArgumentError, "uuid is a required parameter" if uuid.nil?
9+
10+
options ||= {}
11+
raise ArgumentError, "options must be a hash" unless options.is_a?(Hash)
12+
13+
target_dev = options.fetch(:target_dev, "sdc")
14+
bus = options.fetch(:bus, "sata")
15+
flags = options.fetch(:flags, ::Libvirt::Domain::AFFECT_CONFIG)
16+
domain = client.lookup_domain_by_uuid(uuid)
17+
domain_active = domain.active?
18+
flags = effective_detach_iso_flags(flags, domain_active)
19+
20+
if domain_active
21+
domain.update_device(eject_cdrom_xml(target_dev, bus), flags)
22+
begin
23+
domain.detach_device(detach_cdrom_xml(target_dev, bus), flags)
24+
rescue ::Libvirt::Error
25+
# Some backends don't allow to detach the cdrom if the host is running.
26+
# In this case, we just eject the cdrom and leave it attached to the VM.
27+
# Return true that maybe the ISO file can be removed in further steps.
28+
true
29+
end
30+
else
31+
begin
32+
domain.detach_device(detach_cdrom_xml(target_dev, bus), flags)
33+
true
34+
rescue ::Libvirt::Error
35+
false
36+
end
37+
end
38+
end
39+
40+
private
41+
42+
def detach_cdrom_xml(target_dev, bus)
43+
Nokogiri::XML::Builder.new do |x|
44+
x.disk(:type => "file", :device => "cdrom") do
45+
x.target(:dev => target_dev, :bus => bus)
46+
end
47+
end.to_xml
48+
end
49+
50+
def eject_cdrom_xml(target_dev, bus)
51+
Nokogiri::XML::Builder.new do |x|
52+
x.disk(:type => "file", :device => "cdrom", :tray => "open") do
53+
x.driver(:name => "qemu", :type => "raw")
54+
x.target(:dev => target_dev, :bus => bus)
55+
x.readonly
56+
end
57+
end.to_xml
58+
end
59+
60+
def effective_detach_iso_flags(flags, domain_active)
61+
return flags unless flags == ::Libvirt::Domain::AFFECT_CONFIG
62+
return flags unless domain_active
63+
64+
flags | ::Libvirt::Domain::AFFECT_LIVE
65+
rescue ::Libvirt::Error
66+
flags
67+
end
68+
end
69+
70+
class Real
71+
include Shared
72+
end
73+
74+
class Mock
75+
include Shared
76+
end
77+
end
78+
end
79+
end
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
require "nokogiri"
2+
3+
module Fog
4+
module Libvirt
5+
class Compute
6+
module Shared
7+
def upload_iso(pool_name, volume_name, file_path)
8+
raise ArgumentError, "pool_name is a required parameter" if pool_name.nil?
9+
raise ArgumentError, "volume_name is a required parameter" if volume_name.nil?
10+
raise ArgumentError, "file_path is a required parameter" if file_path.nil?
11+
12+
pool = client.lookup_storage_pool_by_name(pool_name)
13+
pool.lookup_volume_by_name(volume_name).delete if pool.list_volumes.include?(volume_name)
14+
15+
create_volume(pool_name, iso_volume_xml(volume_name, file_path))
16+
upload_volume(pool_name, volume_name, file_path)
17+
18+
volume = pool.lookup_volume_by_name(volume_name)
19+
{
20+
:pool_name => pool_name,
21+
:name => volume_name,
22+
:key => volume.key,
23+
:path => volume.path
24+
}
25+
end
26+
27+
private
28+
29+
def iso_volume_xml(volume_name, file_path)
30+
iso_size = File.size(file_path)
31+
32+
Nokogiri::XML::Builder.new do |x|
33+
x.volume do
34+
x.name(volume_name)
35+
x.allocation(0, :unit => "B")
36+
x.capacity(iso_size, :unit => "B")
37+
x.target do
38+
x.format(:type => "raw")
39+
end
40+
end
41+
end.to_xml
42+
end
43+
end
44+
45+
class Real
46+
include Shared
47+
end
48+
49+
class Mock
50+
include Shared
51+
end
52+
end
53+
end
54+
end

tests/libvirt/compute_tests.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
%w{ create_domain create_volume define_domain define_pool destroy_interface destroy_network get_node_info
1313
update_autostart list_domains
1414
list_interfaces list_networks list_pools list_pool_volumes list_volumes pool_action vm_action volume_action
15-
dhcp_leases }.each do |request|
15+
dhcp_leases upload_iso attach_iso detach_iso destroy_iso }.each do |request|
1616
test("it should respond to #{request}") { compute.respond_to? request }
1717
end
1818
end

tests/libvirt/models/compute/server_tests.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
%w{ start stop destroy reboot suspend }.each do |action|
1414
test(action) { server.respond_to? action }
1515
end
16+
%w{ upload_iso attach_iso detach_iso destroy_iso }.each do |action|
17+
test(action) { server.respond_to? action }
18+
end
1619
%w{ start reboot suspend stop }.each do |action|
1720
test("#{action} returns successfully") {
1821
begin

0 commit comments

Comments
 (0)