libvirt 0.1.0

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