libvirt 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,14 @@
1
+ require 'libvirt/spec/domain/clock'
2
+ require 'libvirt/spec/domain/memtune'
1
3
  require 'libvirt/spec/domain/os_booting'
2
4
 
3
5
  module Libvirt
4
6
  module Spec
5
7
  # A specification of a domain. This translates directly down to XML
6
8
  # which can be used to define and launch domains on a node by libvirt.
7
- #
8
- # **Note:** This class may only be temporary, and the functionality
9
- # may be merged back into {Domain}. Also, the interface will likely
10
- # change.
11
9
  class Domain
10
+ include Util
11
+
12
12
  attr_accessor :hypervisor
13
13
  attr_accessor :name
14
14
  attr_accessor :uuid
@@ -16,17 +16,72 @@ module Libvirt
16
16
  attr_accessor :os
17
17
  attr_accessor :memory
18
18
  attr_accessor :current_memory
19
+ attr_accessor :memory_backing
20
+ attr_accessor :memtune
19
21
  attr_accessor :vcpu
22
+ attr_accessor :features
20
23
 
21
24
  attr_accessor :on_poweroff
22
25
  attr_accessor :on_reboot
23
26
  attr_accessor :on_crash
24
27
 
28
+ attr_accessor :clock
25
29
  attr_accessor :devices
26
30
 
27
- def initialize
31
+ # Initializes a domain specification. If a valid XML string for a domain
32
+ # is given, the it will attempt to be parsed into the structure. This
33
+ # is still very experimental. As such, if there is something which is
34
+ # found which is not parseable, an {Exception::UnparseableSpec} exception
35
+ # will be raised. Catch this and inspect the message for more information.
36
+ #
37
+ # @param [String] xml XML spec to attempt to parse into the structure.
38
+ def initialize(xml=nil)
28
39
  @os = OSBooting.new
40
+ @memtune = Memtune.new
41
+ @features = []
42
+ @clock = Clock.new
29
43
  @devices = []
44
+
45
+ load!(xml) if xml
46
+ end
47
+
48
+ # Attempts to load the attributes from an XML specification. **Warning:
49
+ # this will overwrite any already set attributes which exist in the XML.**
50
+ #
51
+ # @param [String] xml XML spec to attempt to parse into the structure.
52
+ def load!(xml)
53
+ root = Nokogiri::XML(xml).root
54
+ try(root.xpath("//domain[@type]"), :preserve => true) { |result| self.hypervisor = result["type"].to_sym }
55
+ try(root.xpath("//domain/name")) { |result| self.name = result.text }
56
+ try(root.xpath("//domain/uuid")) { |result| self.uuid = result.text }
57
+ try(root.xpath("//domain/memory")) { |result| self.memory = result.text }
58
+ try(root.xpath("//domain/currentMemory")) { |result| self.current_memory = result.text }
59
+ try(root.xpath("//domain/vcpu")) { |result| self.vcpu = result.text }
60
+
61
+ try(root.xpath("//domain/on_poweroff")) { |result| self.on_poweroff = result.text.to_sym }
62
+ try(root.xpath("//domain/on_reboot")) { |result| self.on_reboot = result.text.to_sym }
63
+ try(root.xpath("//domain/on_crash")) { |result| self.on_crash = result.text.to_sym }
64
+
65
+ try(root.xpath("//domain/clock")) { |result| self.clock = Clock.new(result) }
66
+ try(root.xpath("//domain/os")) { |result| self.os = OSBooting.new(result) }
67
+
68
+ try(root.xpath("//domain/devices")) do |result|
69
+ self.devices = []
70
+
71
+ result.element_children.each do |device|
72
+ self.devices << Device.load!(device)
73
+ end
74
+ end
75
+
76
+ try(root.xpath("//domain/features")) do |result|
77
+ self.features = []
78
+
79
+ result.element_children.each do |feature|
80
+ self.features << feature.name.to_sym
81
+ end
82
+ end
83
+
84
+ raise_if_unparseables(root.xpath("//domain/*"))
30
85
  end
31
86
 
32
87
  # Returns the XML for this specification. This XML may be passed
@@ -52,11 +107,31 @@ module Libvirt
52
107
  xml.currentMemory current_memory if current_memory
53
108
  xml.vcpu vcpu if vcpu
54
109
 
110
+ if memory_backing == :huge_pages
111
+ xml.memoryBacking do
112
+ xml.hugepages
113
+ end
114
+ end
115
+
116
+ # Memtune handles whether or not to render itself
117
+ memtune.to_xml(xml)
118
+
119
+ if !features.empty?
120
+ xml.features do
121
+ features.each do |feature|
122
+ xml.send(feature)
123
+ end
124
+ end
125
+ end
126
+
55
127
  # Lifecycle control
56
128
  xml.on_poweroff on_poweroff if on_poweroff
57
129
  xml.on_reboot on_reboot if on_reboot
58
130
  xml.on_crash on_crash if on_crash
59
131
 
132
+ # Clock
133
+ clock.to_xml(xml)
134
+
60
135
  # Devices
61
136
  if !devices.empty?
62
137
  xml.devices do
@@ -0,0 +1,35 @@
1
+ module Libvirt
2
+ module Spec
3
+ class Domain
4
+ # Time keeping configuration for a domain.
5
+ class Clock
6
+ include Util
7
+
8
+ attr_accessor :offset
9
+
10
+ # Initializes a clock specification. This should never be called
11
+ # directly. Instead, use a {Libvirt::Spec::Domain} spec, which
12
+ # contains a clock attribute.
13
+ def initialize(xml=nil)
14
+ load!(xml) if xml
15
+ end
16
+
17
+ # Loads clock information from the given XML string. This shouldn't
18
+ # be called directly, since the domain spec automatically calls
19
+ # this.
20
+ def load!(root)
21
+ root = Nokogiri::XML(root).root if !root.is_a?(Nokogiri::XML::Element)
22
+ try(root.xpath("//clock[@offset]"), :preserve => true) { |result| self.offset = result["offset"].to_sym }
23
+ raise_if_unparseables(root.xpath("//clock/*"))
24
+ end
25
+
26
+ def to_xml(parent=Nokogiri::XML::Builder.new)
27
+ return if !offset
28
+
29
+ parent.clock :offset => offset
30
+ parent.to_xml
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ module Libvirt
2
+ module Spec
3
+ class Domain
4
+ # Allows the modification of details regarding the memory tuneable
5
+ # parameters for this domain.
6
+ class Memtune
7
+ attr_accessor :hard_limit
8
+ attr_accessor :soft_limit
9
+ attr_accessor :swap_hard_limit
10
+ attr_accessor :min_guarantee
11
+
12
+ def to_xml(parent=Nokogiri::XML::Builder.new)
13
+ # If nothing has been modified, then don't do anything
14
+ return if !hard_limit && !soft_limit &&
15
+ !swap_hard_limit && !min_guarantee
16
+
17
+ parent.memtune do |m|
18
+ m.hard_limit hard_limit if hard_limit
19
+ m.soft_limit soft_limit if soft_limit
20
+ m.swap_hard_limit swap_hard_limit if swap_hard_limit
21
+ m.min_guarantee min_guarantee if min_guarantee
22
+ end
23
+
24
+ parent.to_xml
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -11,6 +11,8 @@ module Libvirt
11
11
  #
12
12
  # TODO: Host bootloader and direct kernel bootloader options.
13
13
  class OSBooting
14
+ include Util
15
+
14
16
  attr_accessor :type
15
17
  attr_accessor :arch
16
18
  attr_accessor :loader # Part of the BIOS bootloader
@@ -26,8 +28,13 @@ module Libvirt
26
28
  attr_accessor :initrd
27
29
  attr_accessor :cmdline
28
30
 
29
- def initialize
31
+ # Initializes an OS booting specification. This shouldn't be
32
+ # called directly since the domain spec automatically loads
33
+ # this.
34
+ def initialize(xml=nil)
30
35
  @boot = []
36
+
37
+ load!(xml) if xml
31
38
  end
32
39
 
33
40
  # Enables or disables the interactive boot menu prompt on guest startup.
@@ -36,6 +43,25 @@ module Libvirt
36
43
  @bootmenu_enabled = !!value
37
44
  end
38
45
 
46
+ # Loads the OS booting information from the given XML string. This
47
+ # shouldn't be called directly, since the domain spec automatically
48
+ # calls this.
49
+ def load!(root)
50
+ root = Nokogiri::XML(root).root if !root.is_a?(Nokogiri::XML::Element)
51
+ try(root.xpath("//os/type[@arch]"), :preserve => true) { |result| self.arch = result["arch"].to_sym }
52
+ try(root.xpath("//os/type")) { |result| self.type = result.text.to_sym }
53
+
54
+ try(root.xpath("//os/boot"), :multi => true) do |results|
55
+ self.boot = []
56
+
57
+ results.each do |result|
58
+ self.boot << result["dev"].to_sym
59
+ end
60
+ end
61
+
62
+ raise_if_unparseables(root.xpath("//os/*"))
63
+ end
64
+
39
65
  # Convert just the OS booting section to its XML representation.
40
66
  def to_xml(parent=Nokogiri::XML::Builder.new)
41
67
  parent.os do |os|
@@ -0,0 +1,43 @@
1
+ module Libvirt
2
+ module Spec
3
+ # Utility methods for the spec classes. This module is typically
4
+ # included for each class.
5
+ module Util
6
+ # Tries the given XML search, running the block if there are any
7
+ # results. This allows a concise syntax for loading data from
8
+ # XML which may or may not exist.
9
+ #
10
+ # **Warning:** By default, the result of the search given will
11
+ # be removed from the XML tree. See the options below for information
12
+ # on how to avoid this.
13
+ #
14
+ # An additional parameter supports options given as a hash. This
15
+ # allows for the following to be set:
16
+ #
17
+ # * `multi` - If true, then all results will be returned, not just
18
+ # the first one.
19
+ # * `preserve` - If true, then the node will not be deleted after
20
+ # yielding to the block.
21
+ #
22
+ # @param [Array] search_result
23
+ # @param [Hash] options Additional options, which are outlined
24
+ # above.
25
+ def try(search_result, options=nil)
26
+ options ||= {}
27
+ return if search_result.empty?
28
+ search_result = search_result.first if !options[:multi]
29
+ yield search_result
30
+ search_result.remove if !options[:preserve]
31
+ end
32
+
33
+ # This will raise an {Exception::UnparseableSpec} exception if there
34
+ # are any search results given. This is meant as a helper to reduce
35
+ # the duplicity of this feature across specs.
36
+ #
37
+ # @param [Array] search_result
38
+ def raise_if_unparseables(search_result)
39
+ raise Exception::UnparseableSpec, search_result if !search_result.empty?
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,3 +1,3 @@
1
1
  module Libvirt
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,6 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "libvirt/version"
2
+ require File.expand_path("../lib/libvirt/version", __FILE__)
4
3
 
5
4
  Gem::Specification.new do |s|
6
5
  s.name = "libvirt"
@@ -9,8 +8,8 @@ Gem::Specification.new do |s|
9
8
  s.authors = ["Mitchell Hashimoto"]
10
9
  s.email = ["mitchell.hashimoto@gmail.com"]
11
10
  s.homepage = "http://rubygems.org/gems/libvirt"
12
- s.summary = "A ruby client library providing the raw interface to libvirt via FFI."
13
- s.description = "A ruby client library providing the raw interface to libvirt via FFI."
11
+ s.summary = "A ruby client library providing an interface to libvirt via FFI."
12
+ s.description = "A ruby client library providing an interface to libvirt via FFI."
14
13
 
15
14
  s.rubyforge_project = "libvirt"
16
15
 
@@ -90,6 +90,12 @@ Protest.describe("domain") do
90
90
  assert !result.empty?
91
91
  end
92
92
 
93
+ should "provide the spec object for the domain" do
94
+ result = nil
95
+ assert_nothing_raised { result = @instance.spec }
96
+ assert result.is_a?(Libvirt::Spec::Domain)
97
+ end
98
+
93
99
  should "return result of active status" do
94
100
  @instance.start
95
101
  assert @instance.active?
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+
3
+ Protest.describe("Domain devices") do
4
+ setup do
5
+ @klass = Libvirt::Spec::Device
6
+ end
7
+
8
+ should "parse from XML" do
9
+ @instance = @klass.load!("<disk type='file'></disk>")
10
+ assert @instance.is_a?(Libvirt::Spec::Device::Disk)
11
+ assert_equal :file, @instance.type
12
+ end
13
+
14
+ should "be able to get defined classes by name" do
15
+ assert_equal Libvirt::Spec::Device::Disk, @klass.get(:disk)
16
+ end
17
+
18
+ should "raise an exception if an unknown device name is given" do
19
+ assert_raises(NameError) { @klass.get(:foo) }
20
+ end
21
+ end
@@ -5,103 +5,37 @@ Protest.describe("Disk device spec") do
5
5
  @klass = Libvirt::Spec::Device::Disk
6
6
  end
7
7
 
8
- context "outputting XML" do
9
- setup do
10
- @instance = @klass.new(:file)
8
+ context "initialization and parsing XML" do
9
+ should "parse the type" do
10
+ @instance = @klass.new("<disk type='file'></disk>")
11
+ assert_equal :file, @instance.type
11
12
  end
12
13
 
13
- should "output given type for type in XML" do
14
- assert_xpath @instance.type.to_s, @instance.to_xml, "//disk/@type"
14
+ should "parse the device attribute" do
15
+ @instance = @klass.new("<disk device='disk'></disk>")
16
+ assert_equal :disk, @instance.device
15
17
  end
16
18
 
17
- context "source" do
18
- should "not output source if not specified" do
19
- @instance.source = nil
20
- assert_not_element @instance.to_xml, "//disk/source"
21
- end
22
-
23
- should "output source attr as dev for block devices" do
24
- @instance.type = :block
25
- @instance.source = "foo"
26
- assert_xpath @instance.source, @instance.to_xml, "//disk/source/@dev"
27
- end
28
-
29
- should "output source attr as file for other devices" do
30
- @instance.type = :file
31
- @instance.source = "foo"
32
- assert_xpath @instance.source, @instance.to_xml, "//disk/source/@file"
33
- end
34
- end
35
-
36
- context "target" do
37
- should "not output target if not specified" do
38
- @instance.target_dev = nil
39
- assert_not_element @instance.to_xml, "//disk/target"
40
- end
41
-
42
- should "not output target if only bus is given" do
43
- @instance.target_dev = nil
44
- @instance.target_bus = "foo"
45
- assert_not_element @instance.to_xml, "//disk/target"
46
- end
47
-
48
- should "output specified dev on target" do
49
- @instance.target_dev = "foo"
50
- assert_xpath @instance.target_dev, @instance.to_xml, "//disk/target/@dev"
51
- end
52
-
53
- should "output specified bus on target" do
54
- @instance.target_dev = "foo"
55
- @instance.target_bus = "bar"
56
- assert_xpath @instance.target_bus, @instance.to_xml, "//disk/target/@bus"
57
- end
19
+ should "parse the file from the source" do
20
+ @instance = @klass.new("<disk type='file'><source file='foo'/></disk>")
21
+ assert_equal 'foo', @instance.source
58
22
  end
59
23
 
60
- context "driver" do
61
- should "not output if no name is given" do
62
- @instance.driver = nil
63
- assert_not_element @instance.to_xml, "//disk/driver"
64
- end
65
-
66
- should "output with specified driver" do
67
- @instance.driver = "foo"
68
- assert_xpath @instance.driver, @instance.to_xml, "//disk/driver/@name"
69
- end
70
-
71
- should "output with specified type if given" do
72
- @instance.driver = "foo"
73
- @instance.driver_type = "bar"
74
- assert_xpath @instance.driver_type, @instance.to_xml, "//disk/driver/@type"
75
- end
76
-
77
- should "output with specified cache if given" do
78
- @instance.driver = "foo"
79
- @instance.driver_cache = "bar"
80
- assert_xpath @instance.driver_cache, @instance.to_xml, "//disk/driver/@cache"
81
- end
24
+ should "parse the dev from the source" do
25
+ @instance = @klass.new("<disk type='file'><source dev='bar'/></disk>")
26
+ assert_equal 'bar', @instance.source
82
27
  end
83
28
 
84
- context "shareable" do
85
- should "not be sharable initially" do
86
- assert_not_element @instance.to_xml, "//disk/shareable"
87
- end
88
-
89
- should "be shareable if specified" do
90
- @instance.shareable = true
91
- assert_element @instance.to_xml, "//disk/shareable"
92
- end
29
+ should "parse the target" do
30
+ @instance = @klass.new("<disk type='file'><target bus='foo' dev='bar'/></disk>")
31
+ assert_equal 'foo', @instance.target_bus
32
+ assert_equal 'bar', @instance.target_dev
93
33
  end
94
34
 
95
- context "serial" do
96
- should "not have serial if not specified" do
97
- @instance.serial = nil
98
- assert_not_element @instance.to_xml, "//disk/serial"
99
- end
100
-
101
- should "have serial if specified" do
102
- @instance.serial = "foobar"
103
- assert_xpath @instance.serial, @instance.to_xml, "//disk/serial"
104
- end
35
+ should "raise an exception if unsupported tags exist" do
36
+ assert_raises(Libvirt::Exception::UnparseableSpec) {
37
+ @klass.new("<disk><foo/></disk>")
38
+ }
105
39
  end
106
40
  end
107
41
  end