libvirt 0.1.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.
Files changed (74) hide show
  1. data/.gitignore +10 -0
  2. data/.yardopts +3 -0
  3. data/CHANGELOG.md +3 -0
  4. data/Gemfile +12 -0
  5. data/Gemfile.lock +36 -0
  6. data/README.md +83 -0
  7. data/Rakefile +20 -0
  8. data/docs/user_guide.md +259 -0
  9. data/examples/README.md +10 -0
  10. data/examples/create_domain.rb +46 -0
  11. data/examples/print_domain_info.rb +46 -0
  12. data/examples/print_hypervisor_info.rb +40 -0
  13. data/lib/ffi/libvirt/domain_info.rb +12 -0
  14. data/lib/ffi/libvirt/error.rb +25 -0
  15. data/lib/ffi/libvirt/error_functions.rb +21 -0
  16. data/lib/ffi/libvirt/error_types.rb +125 -0
  17. data/lib/ffi/libvirt/functions.rb +363 -0
  18. data/lib/ffi/libvirt/node_info.rb +27 -0
  19. data/lib/ffi/libvirt/storage_pool_info.rb +11 -0
  20. data/lib/ffi/libvirt/storage_volume_info.rb +14 -0
  21. data/lib/ffi/libvirt/types.rb +61 -0
  22. data/lib/ffi/libvirt/util.rb +19 -0
  23. data/lib/ffi/libvirt/version.rb +46 -0
  24. data/lib/ffi/libvirt.rb +29 -0
  25. data/lib/libvirt/collection/abstract_collection.rb +49 -0
  26. data/lib/libvirt/collection/domain_collection.rb +98 -0
  27. data/lib/libvirt/collection/interface_collection.rb +16 -0
  28. data/lib/libvirt/collection/network_collection.rb +58 -0
  29. data/lib/libvirt/collection/node_device_collection.rb +29 -0
  30. data/lib/libvirt/collection/nwfilter_collection.rb +15 -0
  31. data/lib/libvirt/collection/storage_pool_collection.rb +58 -0
  32. data/lib/libvirt/collection/storage_volume_collection.rb +57 -0
  33. data/lib/libvirt/collection.rb +12 -0
  34. data/lib/libvirt/connection.rb +225 -0
  35. data/lib/libvirt/domain.rb +241 -0
  36. data/lib/libvirt/error.rb +91 -0
  37. data/lib/libvirt/exception.rb +19 -0
  38. data/lib/libvirt/network.rb +118 -0
  39. data/lib/libvirt/node.rb +118 -0
  40. data/lib/libvirt/node_device.rb +75 -0
  41. data/lib/libvirt/spec/device/disk.rb +58 -0
  42. data/lib/libvirt/spec/device/emulator.rb +23 -0
  43. data/lib/libvirt/spec/device.rb +8 -0
  44. data/lib/libvirt/spec/domain/os_booting.rb +69 -0
  45. data/lib/libvirt/spec/domain.rb +71 -0
  46. data/lib/libvirt/spec.rb +12 -0
  47. data/lib/libvirt/storage_pool.rb +164 -0
  48. data/lib/libvirt/storage_volume.rb +109 -0
  49. data/lib/libvirt/version.rb +3 -0
  50. data/lib/libvirt.rb +53 -0
  51. data/libvirt.gemspec +27 -0
  52. data/test/libvirt/collection/abstract_collection_test.rb +14 -0
  53. data/test/libvirt/collection/domain_collection_test.rb +122 -0
  54. data/test/libvirt/collection/interface_collection_test.rb +9 -0
  55. data/test/libvirt/collection/network_collection_test.rb +41 -0
  56. data/test/libvirt/collection/node_device_collection_test.rb +24 -0
  57. data/test/libvirt/collection/nwfilter_collection_test.rb +7 -0
  58. data/test/libvirt/collection/storage_pool_collection_test.rb +41 -0
  59. data/test/libvirt/collection/storage_volume_collection_test.rb +86 -0
  60. data/test/libvirt/connection_test.rb +133 -0
  61. data/test/libvirt/domain_test.rb +221 -0
  62. data/test/libvirt/error_test.rb +93 -0
  63. data/test/libvirt/libvirt_test.rb +16 -0
  64. data/test/libvirt/network_test.rb +76 -0
  65. data/test/libvirt/node_device_test.rb +16 -0
  66. data/test/libvirt/node_test.rb +24 -0
  67. data/test/libvirt/spec/devices/disk_test.rb +107 -0
  68. data/test/libvirt/spec/devices/emulator_test.rb +20 -0
  69. data/test/libvirt/spec/domain_test.rb +17 -0
  70. data/test/libvirt/storage_pool_test.rb +133 -0
  71. data/test/libvirt/storage_volume_test.rb +63 -0
  72. data/test/support/xml_assertions.rb +29 -0
  73. data/test/test_helper.rb +23 -0
  74. metadata +219 -0
@@ -0,0 +1,93 @@
1
+ require "test_helper"
2
+
3
+ Protest.describe("error") do
4
+ setup do
5
+ @klass = Libvirt::Error
6
+ end
7
+
8
+ context "getting the last error" do
9
+ setup do
10
+ # Reset the error manually so we're clear.
11
+ FFI::Libvirt.virResetLastError
12
+ end
13
+
14
+ should "be fine if there is no last error" do
15
+ result = nil
16
+ assert_nothing_raised { result = @klass.last_error }
17
+ assert result.nil?
18
+ end
19
+
20
+ should "report the last error" do
21
+ begin
22
+ FFI::Libvirt.virConnectOpen("NSEpicFailure")
23
+ rescue Libvirt::Exception::LibvirtError; end
24
+
25
+ # And now we verify the error is accessible
26
+ error = @klass.last_error
27
+ assert error
28
+ end
29
+ end
30
+
31
+ context "setting an error handling callback" do
32
+ teardown do
33
+ # Reset the error handler to nil
34
+ @klass.on_error
35
+ end
36
+
37
+ should "call the callback when an error occurs" do
38
+ called = false
39
+ @klass.on_error do |error|
40
+ called = true
41
+ end
42
+
43
+ FFI::Libvirt.virConnectOpen("foo") rescue nil
44
+ assert called
45
+ end
46
+ end
47
+
48
+ context "disable error raising" do
49
+ teardown do
50
+ # Make sure this gets reset
51
+ @klass.raise_errors = true
52
+ end
53
+
54
+ should "raise errors by default" do
55
+ assert @klass.raise_errors
56
+ assert_raise(Libvirt::Exception::LibvirtError) { FFI::Libvirt.virConnectOpen("fail") }
57
+ end
58
+
59
+ should "not raise errors if it is disabled" do
60
+ @klass.raise_errors = false
61
+ assert !@klass.raise_errors
62
+ assert_nothing_raised { FFI::Libvirt.virConnectOpen("fail") }
63
+ end
64
+ end
65
+
66
+ context "with an error instance" do
67
+ setup do
68
+ # Get an error instance...
69
+ @error = nil
70
+ begin
71
+ FFI::Libvirt.virConnectOpen("FailHard")
72
+ rescue Libvirt::Exception::LibvirtError => e
73
+ @error = e.error
74
+ end
75
+ end
76
+
77
+ should "allow access to the raw struct" do
78
+ assert @error.interface.is_a?(FFI::Libvirt::Error)
79
+ end
80
+
81
+ should "allow access to the error code" do
82
+ assert_equal :system_error, @error.code
83
+ end
84
+
85
+ should "allow access to the error domain" do
86
+ assert_equal :remote, @error.domain
87
+ end
88
+
89
+ should "allow access to the error message" do
90
+ assert @error.message
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,16 @@
1
+ require "test_helper"
2
+
3
+ Protest.describe("libvirt") do
4
+ setup do
5
+ @klass = Libvirt
6
+ end
7
+
8
+ should "provide the version" do
9
+ assert @klass.version.is_a?(Array)
10
+ end
11
+
12
+ should "provide a shortcut to connecting" do
13
+ Libvirt::Connection.expects(:new).with(1,2,3)
14
+ @klass.connect(1,2,3)
15
+ end
16
+ end
@@ -0,0 +1,76 @@
1
+ require "test_helper"
2
+
3
+ Protest.describe("network") do
4
+ setup do
5
+ @klass = Libvirt::Network
6
+
7
+ @data = {
8
+ :name => "vboxnet0",
9
+ :uuid => "786f6276-656e-4074-8000-0a0027000000"
10
+ }
11
+
12
+ @conn = Libvirt.connect("test:///default")
13
+ @instance = @klass.new(FFI::Libvirt.virNetworkDefineXML(@conn, <<-XML))
14
+ <network>
15
+ <name>#{@data[:name]}</name>
16
+ <uuid>#{@data[:uuid]}</uuid>
17
+ </network>
18
+ XML
19
+ end
20
+
21
+ should "provide the name of the network" do
22
+ result = @instance.name
23
+ assert_equal @data[:name], result
24
+ end
25
+
26
+ should "provide the UUID of the network" do
27
+ result = @instance.uuid
28
+ assert result
29
+ assert_equal @data[:uuid], result
30
+ end
31
+
32
+ should "provide the bridge of the network" do
33
+ assert_raise(Libvirt::Exception::LibvirtError) { @instance.bridge }
34
+ end
35
+
36
+ should "control autostart" do
37
+ assert !@instance.autostart?
38
+ @instance.autostart = true
39
+ assert @instance.autostart?
40
+ end
41
+
42
+ should "provide an active check" do
43
+ assert !@instance.active?
44
+ @instance.start
45
+ assert @instance.active?
46
+ end
47
+
48
+ should "provide a persistence check" do
49
+ assert @instance.persistent?
50
+ # TODO: Check state change. Not sure how to trigger this at the moment.
51
+ end
52
+
53
+ should "provide ability to stop network" do
54
+ @instance.start
55
+ assert @instance.active?
56
+ @instance.stop
57
+ assert !@instance.active?
58
+ end
59
+
60
+ should "be able to undefine the network" do
61
+ assert @conn.networks.include?(@instance)
62
+ @instance.undefine
63
+ assert !@conn.networks.include?(@instance)
64
+ end
65
+
66
+ should "provide an equality comparison based on UUID" do
67
+ # TODO: Create multiple networks with uuids to compare
68
+ end
69
+
70
+ should "provide a `to_ptr` method to get the `virNetworkPtr`" do
71
+ result = nil
72
+ assert_nothing_raised { result = @instance.to_ptr }
73
+ assert result.is_a?(FFI::Pointer)
74
+ assert !result.null?
75
+ end
76
+ end
@@ -0,0 +1,16 @@
1
+ require "test_helper"
2
+
3
+ Protest.describe("node device") do
4
+ setup do
5
+ @klass = Libvirt::NodeDevice
6
+ @instance = Libvirt.connect("test:///default").node.devices.first
7
+ end
8
+
9
+ should "provide the name" do
10
+ assert_equal "computer", @instance.name
11
+ end
12
+
13
+ should "provide the XML description" do
14
+ assert @instance.xml
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ require "test_helper"
2
+
3
+ Protest.describe("node") do
4
+ setup do
5
+ @klass = Libvirt::Node
6
+
7
+ @connection = Libvirt.connect("test:///default")
8
+ @instance = @connection.node
9
+ end
10
+
11
+ should "provide access to the connection" do
12
+ assert_equal @connection, @instance.connection
13
+ end
14
+
15
+ should "be able to retrieve devices" do
16
+ result = nil
17
+ assert_nothing_raised { result = @instance.devices }
18
+ assert result
19
+ assert result.is_a?(Libvirt::Collection::NodeDeviceCollection)
20
+ end
21
+
22
+ # Note: This class is extremely hard to test since the results actually
23
+ # change computer to computer. Mocking/stubbing probably required.
24
+ end
@@ -0,0 +1,107 @@
1
+ require "test_helper"
2
+
3
+ Protest.describe("Disk device spec") do
4
+ setup do
5
+ @klass = Libvirt::Spec::Device::Disk
6
+ end
7
+
8
+ context "outputting XML" do
9
+ setup do
10
+ @instance = @klass.new(:file)
11
+ end
12
+
13
+ should "output given type for type in XML" do
14
+ assert_xpath @instance.type.to_s, @instance.to_xml, "//disk/@type"
15
+ end
16
+
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
58
+ end
59
+
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
82
+ end
83
+
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
93
+ end
94
+
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
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,20 @@
1
+ require "test_helper"
2
+
3
+ Protest.describe("Emulator device spec") do
4
+ setup do
5
+ @klass = Libvirt::Spec::Device::Emulator
6
+ end
7
+
8
+ should "have the given path when initialized" do
9
+ path = "foo"
10
+ instance = @klass.new(path)
11
+ assert_equal path, instance.path
12
+ end
13
+
14
+ should "output XML with the given path" do
15
+ path = "foo"
16
+ instance = @klass.new(path)
17
+
18
+ assert_xpath path, instance.to_xml, "//emulator"
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+
3
+ Protest.describe("Domain spec") do
4
+ setup do
5
+ @klass = Libvirt::Spec::Domain
6
+ end
7
+
8
+ context "initialization" do
9
+ setup do
10
+ @instance= @klass.new
11
+ end
12
+
13
+ should "not have any devices" do
14
+ assert @instance.devices.empty?
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,133 @@
1
+ require "test_helper"
2
+
3
+ Protest.describe("storage pool") do
4
+ setup do
5
+ @klass = Libvirt::StoragePool
6
+
7
+ @data = {
8
+ :name => "default-pool",
9
+ :uuid => "786f6276-656e-4074-8000-0a0027000000",
10
+ :capacity => "107374182400"
11
+ }
12
+
13
+ @conn = Libvirt.connect("test:///default")
14
+ @instance = @klass.new(FFI::Libvirt.virStoragePoolDefineXML(@conn, <<-XML, 0))
15
+ <pool type='dir'>
16
+ <name>#{@data[:name]}</name>
17
+ <uuid>#{@data[:uuid]}</uuid>
18
+ <capacity>#{@data[:capacity]}</capacity>
19
+ <allocation>0</allocation>
20
+ <available>107374182400</available>
21
+ <source>
22
+ </source>
23
+ <target>
24
+ <path>/default-pool</path>
25
+ <permissions>
26
+ <mode>0700</mode>
27
+ <owner>-1</owner>
28
+ <group>-1</group>
29
+ </permissions>
30
+ </target>
31
+ </pool>
32
+ XML
33
+ end
34
+
35
+ should "be able to retrieve the connection" do
36
+ result = @instance.connection
37
+ assert result.is_a?(Libvirt::Connection)
38
+ end
39
+
40
+ should "provide the name" do
41
+ result = @instance.name
42
+ assert_equal @data[:name], result
43
+ end
44
+
45
+ should "provide the UUID" do
46
+ result = @instance.uuid
47
+ assert result
48
+ assert_equal 36, result.length
49
+ end
50
+
51
+ should "provide the state" do
52
+ assert_equal :running, @instance.state
53
+ @instance.stop
54
+ assert_equal :inactive, @instance.state
55
+ end
56
+
57
+ should "provide the capacity" do
58
+ assert_equal @data[:capacity].to_i, @instance.capacity
59
+ end
60
+
61
+ should "provide the current allocation" do
62
+ assert_equal 0, @instance.allocation
63
+ end
64
+
65
+ should "provide the available bytes remaining" do
66
+ assert_equal 107374182400, @instance.available
67
+ end
68
+
69
+ should "dump the XML" do
70
+ result = @instance.xml
71
+ assert result
72
+ end
73
+
74
+ should "provide an active check" do
75
+ @instance.start
76
+ assert @instance.active?
77
+ end
78
+
79
+ should "provide a persistence check" do
80
+ assert @instance.persistent?
81
+ # TODO: Check state change. Not sure how to trigger this at the moment.
82
+ end
83
+
84
+ should "not raise an exception if instance is already active and start is called" do
85
+ assert @instance.active?
86
+ assert_nothing_raised { @instance.start }
87
+ end
88
+
89
+ should "be able to build storage pool" do
90
+ @instance.stop
91
+ assert @instance.build
92
+ end
93
+
94
+ context "deleting underlying resources" do
95
+ setup do
96
+ @instance.stop
97
+ end
98
+
99
+ should "be able to delete underlying resources" do
100
+ assert @instance.delete
101
+ end
102
+
103
+ should "be able to specify type of delete" do
104
+ assert @instance.delete(:zeroed)
105
+ end
106
+ end
107
+
108
+ should "be able to be stopped" do
109
+ assert @instance.active?
110
+ @instance.stop
111
+ assert !@instance.active?
112
+ end
113
+
114
+ should "be able to undefine a storage pool" do
115
+ @instance.stop
116
+ assert @conn.storage_pools.include?(@instance)
117
+ @instance.undefine
118
+ assert !@conn.storage_pools.include?(@instance)
119
+ end
120
+
121
+ should "provide a collection of volumes" do
122
+ result = nil
123
+ assert_nothing_raised { result = @instance.volumes }
124
+ assert result.is_a?(Libvirt::Collection::StorageVolumeCollection)
125
+ end
126
+
127
+ should "provide a `to_ptr` method to get the pointer" do
128
+ result = nil
129
+ assert_nothing_raised { result = @instance.to_ptr }
130
+ assert result.is_a?(FFI::Pointer)
131
+ assert !result.null?
132
+ end
133
+ end
@@ -0,0 +1,63 @@
1
+ require "test_helper"
2
+
3
+ Protest.describe("storage volume") do
4
+ setup do
5
+ @klass = Libvirt::StorageVolume
6
+
7
+ @data = {
8
+ :name => "test_vm_A.vdi",
9
+ :key => "f9eba311-ea76-4e4a-ad7b-401fa81e38c8"
10
+ }
11
+
12
+ @connection = Libvirt.connect("test:///default")
13
+ @pool = @connection.storage_pools.first
14
+ @instance = @pool.volumes.create(<<-XML)
15
+ <volume>
16
+ <name>#{@data[:name]}</name>
17
+ <key>#{@data[:key]}</key>
18
+ <source>
19
+ </source>
20
+ <capacity>8589934592</capacity>
21
+ <allocation>33280</allocation>
22
+ <target>
23
+ <format type='raw'/>
24
+ <permissions>
25
+ <mode>00</mode>
26
+ <owner>0</owner>
27
+ <group>0</group>
28
+ </permissions>
29
+ </target>
30
+ </volume>
31
+ XML
32
+ end
33
+
34
+ should "be able to retrieve the key" do
35
+ assert @instance.uuid
36
+ end
37
+
38
+ should "be able to retrieve the name" do
39
+ assert_equal @data[:name], @instance.name
40
+ end
41
+
42
+ should "be able to retrieve the path" do
43
+ assert_equal "/default-pool/test_vm_A.vdi", @instance.path
44
+ end
45
+
46
+ should "be able to retrieve the type of this volume" do
47
+ assert_equal :file, @instance.type
48
+ end
49
+
50
+ should "be able to retrieve the capacity" do
51
+ assert_equal 8589934592, @instance.capacity
52
+ end
53
+
54
+ should "be able to retrieve the allocation" do
55
+ assert_equal 33280, @instance.allocation
56
+ end
57
+
58
+ should "be able to delete the storage volume" do
59
+ assert @pool.volumes.include?(@instance)
60
+ @instance.delete
61
+ assert !@pool.volumes.include?(@instance)
62
+ end
63
+ end
@@ -0,0 +1,29 @@
1
+ class Protest::TestCase
2
+ # Assert the value of an xpath expression is valid
3
+ def assert_xpath(expected, xml, xpath, msg = nil)
4
+ full_message = build_message(msg, '? at ? should be ?', xpath, xml, expected)
5
+ assert_block full_message do
6
+ value = Nokogiri::XML.parse(xml).at_xpath(xpath)
7
+ value && value.text == expected
8
+ end
9
+ end
10
+
11
+ # Assert an element exists
12
+ def assert_element(xml, xpath, msg = nil)
13
+ full_message = build_message(msg, 'element ? at ? not found', xpath, xml)
14
+ assert_block full_message do
15
+ element = Nokogiri::XML.parse(xml).at_xpath(xpath)
16
+ !element.nil? && element.element?
17
+ end
18
+ end
19
+
20
+ # Assert an element doesn't exist
21
+ def assert_not_element(xml, xpath)
22
+ assert_nil Nokogiri::XML.parse(xml).at_xpath(xpath)
23
+ end
24
+
25
+ # Assert a valid number of elements
26
+ def assert_elements(expected_size, xml, xpath, msg = nil)
27
+ assert_equal expected_size, Nokogiri::XML.parse(xml).xpath(xpath).size
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ require "test/unit/assertions"
2
+ require "protest"
3
+ require "mocha"
4
+ require "libvirt"
5
+
6
+ # Test helpers which monkey-patch Protest::TestCase directly
7
+ require File.expand_path("../support/xml_assertions", __FILE__)
8
+
9
+ class Protest::TestCase
10
+ include Test::Unit::Assertions
11
+ include Mocha::API
12
+
13
+ # Get Mocha integrated properly into the tests
14
+ alias :original_run :run
15
+ def run(report)
16
+ original_run(report)
17
+ mocha_verify
18
+ ensure
19
+ mocha_teardown
20
+ end
21
+ end
22
+
23
+ Protest.report_with(:progress)