libvirt 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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