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,58 @@
1
+ module Libvirt
2
+ module Spec
3
+ module Device
4
+ # Any device that looks like a disk, be it a floppy, harddisk,
5
+ # cdrom, or paravirtualized driver is specified via the disk
6
+ # element.
7
+ class Disk
8
+ attr_accessor :type
9
+ attr_accessor :source
10
+ attr_accessor :target_dev
11
+ attr_accessor :target_bus
12
+ attr_accessor :driver
13
+ attr_accessor :driver_type
14
+ attr_accessor :driver_cache
15
+ attr_accessor :shareable
16
+ attr_accessor :serial
17
+
18
+ # Initialize a new disk element with the given type. Examples
19
+ # of valid `type`s are "disk," "floppy," and "cdrom."
20
+ def initialize(type)
21
+ @type = type
22
+ @shareable = false
23
+ end
24
+
25
+ # Returns the XML representation of this device.
26
+ def to_xml(xml=Nokogiri::XML::Builder.new)
27
+ xml.disk(:type => type) do |d|
28
+ if source
29
+ # Source tag, the attribute depends on the type.
30
+ attribute = type == :block ? :dev : :file
31
+ d.source(attribute => source)
32
+ end
33
+
34
+ if target_dev
35
+ # Target tag has optional "bus" parameter
36
+ options = { :dev => target_dev }
37
+ options[:bus] = target_bus if target_bus
38
+ d.target(options)
39
+ end
40
+
41
+ if driver
42
+ # Driver tag has a couple optional parameters
43
+ options = { :name => driver }
44
+ options[:type] = driver_type if driver_type
45
+ options[:cache] = driver_cache if driver_cache
46
+ d.driver(options)
47
+ end
48
+
49
+ d.shareable if shareable
50
+ d.serial serial if serial
51
+ end
52
+
53
+ xml.to_xml
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,23 @@
1
+ module Libvirt
2
+ module Spec
3
+ module Device
4
+ class Emulator
5
+ attr_accessor :path
6
+
7
+ # Initialize an emulator device with the given path. The capabilities
8
+ # XML from {Connection} describes what emulators are available.
9
+ def initialize(path)
10
+ @path = path
11
+ end
12
+
13
+ # Returns the XML for this device.
14
+ #
15
+ # @return [String]
16
+ def to_xml(xml=Nokogiri::XML::Builder.new)
17
+ xml.emulator path
18
+ xml.to_xml
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,8 @@
1
+ module Libvirt
2
+ module Spec
3
+ module Device
4
+ autoload :Disk, 'libvirt/spec/device/disk'
5
+ autoload :Emulator, 'libvirt/spec/device/emulator'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,69 @@
1
+ module Libvirt
2
+ module Spec
3
+ class Domain
4
+ # The OS booting section of a domain specification indicates to libvirt
5
+ # how to configure the virtual machine to boot. There are three main ways
6
+ # to configure booting. Each hypervisor supports one or more of the following:
7
+ #
8
+ # 1. BIOS bootloader
9
+ # 2. Host bootloader
10
+ # 3. Direct kernel boot
11
+ #
12
+ # TODO: Host bootloader and direct kernel bootloader options.
13
+ class OSBooting
14
+ attr_accessor :type
15
+ attr_accessor :arch
16
+ attr_accessor :loader # Part of the BIOS bootloader
17
+ attr_accessor :boot
18
+ attr_accessor :bootmenu_enabled
19
+
20
+ # Host bootloader configuration options
21
+ attr_accessor :bootloader
22
+ attr_accessor :bootloader_args
23
+
24
+ # Direct kernel boot
25
+ attr_accessor :kernel
26
+ attr_accessor :initrd
27
+ attr_accessor :cmdline
28
+
29
+ def initialize
30
+ @boot = []
31
+ end
32
+
33
+ # Enables or disables the interactive boot menu prompt on guest startup.
34
+ def bootmenu_enabled=(value)
35
+ # Force a boolean value
36
+ @bootmenu_enabled = !!value
37
+ end
38
+
39
+ # Convert just the OS booting section to its XML representation.
40
+ def to_xml(parent=Nokogiri::XML::Builder.new)
41
+ parent.os do |os|
42
+ # Build the arguments for the OS booting type
43
+ type_args = [type]
44
+ type_args << { :arch => arch } if arch
45
+
46
+ # Setup the specification
47
+ os.type_ *type_args
48
+ os.loader loader if loader
49
+ os.bootmenu(:enable => bootmenu_enabled ? 'yes' : 'no') if !bootmenu_enabled.nil?
50
+
51
+ # Boot order for BIOS booting
52
+ boot.each { |dev| os.boot :dev => dev } if boot.is_a?(Array)
53
+
54
+ # Host bootloader configuration options
55
+ os.bootloader bootloader if bootloader
56
+ os.bootloader_args bootloader_args if bootloader_args
57
+
58
+ # Direct kernel boot options
59
+ os.kernel kernel if kernel
60
+ os.initrd initrd if initrd
61
+ os.cmdline cmdline if cmdline
62
+ end
63
+
64
+ parent.to_xml
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,71 @@
1
+ require 'libvirt/spec/domain/os_booting'
2
+
3
+ module Libvirt
4
+ module Spec
5
+ # A specification of a domain. This translates directly down to XML
6
+ # 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
+ class Domain
12
+ attr_accessor :hypervisor
13
+ attr_accessor :name
14
+ attr_accessor :uuid
15
+ attr_accessor :description
16
+ attr_accessor :os
17
+ attr_accessor :memory
18
+ attr_accessor :current_memory
19
+ attr_accessor :vcpu
20
+
21
+ attr_accessor :on_poweroff
22
+ attr_accessor :on_reboot
23
+ attr_accessor :on_crash
24
+
25
+ attr_accessor :devices
26
+
27
+ def initialize
28
+ @os = OSBooting.new
29
+ @devices = []
30
+ end
31
+
32
+ # Returns the XML for this specification. This XML may be passed
33
+ # into libvirt to create a domain. This is actually the method which
34
+ # should be used for validation of this XML, since libvirt has
35
+ # great validation built in. If you define a domain and an error occurs,
36
+ # then it will notify you what is missing or wrong with the specification.
37
+ #
38
+ # @return [String]
39
+ def to_xml
40
+ Nokogiri::XML::Builder.new do |xml|
41
+ xml.domain(:type => hypervisor) do
42
+ # Name and description
43
+ xml.name name if name
44
+ xml.uuid uuid if uuid
45
+ xml.description description if description
46
+
47
+ # Operating system boot information
48
+ os.to_xml(xml)
49
+
50
+ # Basic resources
51
+ xml.memory memory if memory
52
+ xml.currentMemory current_memory if current_memory
53
+ xml.vcpu vcpu if vcpu
54
+
55
+ # Lifecycle control
56
+ xml.on_poweroff on_poweroff if on_poweroff
57
+ xml.on_reboot on_reboot if on_reboot
58
+ xml.on_crash on_crash if on_crash
59
+
60
+ # Devices
61
+ if !devices.empty?
62
+ xml.devices do
63
+ devices.map { |d| d.to_xml(xml) }
64
+ end
65
+ end
66
+ end
67
+ end.to_xml
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,12 @@
1
+ module Libvirt
2
+ # This is the namespace which contains all the classes which define
3
+ # specifications for domains, networks, storage pools, etc. These
4
+ # specifications are used to define and launch them via libvirt.
5
+ # The specifications are converted down to XML for consumption by
6
+ # libvirt. Please view the example files for examples on how to use
7
+ # the specs.
8
+ module Spec
9
+ autoload :Device, 'libvirt/spec/device'
10
+ autoload :Domain, 'libvirt/spec/domain'
11
+ end
12
+ end
@@ -0,0 +1,164 @@
1
+ module Libvirt
2
+ # Represents a single storage pool.
3
+ class StoragePool
4
+ # Initializes a new {StoragePool} object given a `virStoragePoolPtr`.
5
+ # Do not call this directly. Instead, use the {Connection#storage_pools}
6
+ # object to retrieve a specific {StoragePool}.
7
+ def initialize(pointer)
8
+ @pointer = pointer
9
+ ObjectSpace.define_finalizer(self, method(:finalize))
10
+ end
11
+
12
+ # Returns the connection of this storage pool.
13
+ #
14
+ # @return [Connection]
15
+ def connection
16
+ Connection.new(FFI::Libvirt.virStoragePoolGetConnect(self))
17
+ end
18
+
19
+ # Returns the name of the network as a string.
20
+ #
21
+ # @return [String]
22
+ def name
23
+ FFI::Libvirt.virStoragePoolGetName(self)
24
+ end
25
+
26
+ # Returns the UUID of the storage pool as a string.
27
+ #
28
+ # @return [String]
29
+ def uuid
30
+ output_ptr = FFI::MemoryPointer.new(:char, 36)
31
+ FFI::Libvirt.virStoragePoolGetUUIDString(self, output_ptr)
32
+ output_ptr.read_string
33
+ end
34
+
35
+ # Returns the XML description of this storage pool.
36
+ #
37
+ # @return [String]
38
+ def xml
39
+ FFI::Libvirt.virStoragePoolGetXMLDesc(self, 0)
40
+ end
41
+
42
+ # Deterine if the storage pool is active or not.
43
+ #
44
+ # @return [Boolean]
45
+ def active?
46
+ FFI::Libvirt.virStoragePoolIsActive(self) == 1
47
+ end
48
+
49
+ # Determine if the storage pool is persistent or not.
50
+ #
51
+ # @return [Boolean]
52
+ def persistent?
53
+ FFI::Libvirt.virStoragePoolIsPersistent(self) == 1
54
+ end
55
+
56
+ # Build the underlying storage pool.
57
+ #
58
+ # @return [Boolean]
59
+ def build
60
+ FFI::Libvirt.virStoragePoolBuild(self, 0) == 0
61
+ end
62
+
63
+ # Starts an inactive storage pool.
64
+ #
65
+ # @return [Boolean]
66
+ def create
67
+ return true if active?
68
+ FFI::Libvirt.virStoragePoolCreate(self, 0) == 0
69
+ end
70
+ alias :start :create
71
+
72
+ # Delete the underlying pool resources. This is a non-recoverable
73
+ # operation. This won't undefine the pool.
74
+ #
75
+ # @return [Boolean]
76
+ def delete(type=nil)
77
+ type ||= :normal
78
+ FFI::Libvirt.virStoragePoolDelete(self, type) == 0
79
+ end
80
+
81
+ # Stops an active storage pool.
82
+ #
83
+ # @return [Boolean]
84
+ def destroy
85
+ FFI::Libvirt.virStoragePoolDestroy(self) == 0
86
+ end
87
+ alias :stop :destroy
88
+
89
+ # Undefines the storage pool. This requires that the storage pool be
90
+ # inactive. This won't delete the underlying resources of the storage pool,
91
+ # however. Run {#delete} for that.
92
+ #
93
+ # @return [Boolean]
94
+ def undefine
95
+ FFI::Libvirt.virStoragePoolUndefine(self) == 0
96
+ end
97
+
98
+ # Returns the state of the storage pool as a symbol.
99
+ #
100
+ # @return [Symbol]
101
+ def state
102
+ info[:state]
103
+ end
104
+
105
+ # Returns the capacity in bytes.
106
+ #
107
+ # @return [Fixnum]
108
+ def capacity
109
+ info[:capacity]
110
+ end
111
+
112
+ # Returns the current allocation in bytes.
113
+ #
114
+ # @return [Fixnum]
115
+ def allocation
116
+ info[:allocation]
117
+ end
118
+
119
+ # Returns the available free space in bytes.
120
+ #
121
+ # @return [Fixnum]
122
+ def available
123
+ info[:available]
124
+ end
125
+
126
+ # Returns the volumes associated with this storage pool.
127
+ #
128
+ # @return [Collection::StorageVolumeCollection]
129
+ def volumes
130
+ Collection::StorageVolumeCollection.new(self)
131
+ end
132
+
133
+ # Provide a meaningful equality check for two storage pools by comparing
134
+ # UUID.
135
+ #
136
+ # @return [Boolean]
137
+ def ==(other)
138
+ other.is_a?(StoragePool) && other.uuid == uuid
139
+ end
140
+
141
+ # Returns the actual `virStoragePoolPtr` underlying this structure.
142
+ #
143
+ # @return [FFI::Pointer]
144
+ def to_ptr
145
+ @pointer
146
+ end
147
+
148
+ protected
149
+
150
+ # Gets the virStoragePoolInfo structure for this storage pool.
151
+ #
152
+ # @return [Hash]
153
+ def info
154
+ result = FFI::Libvirt::StoragePoolInfo.new
155
+ FFI::Libvirt.virStoragePoolGetInfo(self, result.to_ptr)
156
+ result
157
+ end
158
+
159
+ # Frees the underlying memory associated with this object.
160
+ def finalize(*args)
161
+ FFI::Libvirt.virStoragePoolFree(self)
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,109 @@
1
+ module Libvirt
2
+ # Represents a single storage volume.
3
+ class StorageVolume
4
+ # Initializes a new {StorageVolume} object given a `virStorageVolPtr`.
5
+ # Do not call this directly. Instead, use the {StoragePool#volumes} collection
6
+ # object to retrieve a specific {StorageVolume}.
7
+ def initialize(pointer)
8
+ @pointer = pointer
9
+ ObjectSpace.define_finalizer(self, method(:finalize))
10
+ end
11
+
12
+ # Returns a key for this storage volume. The key is a unique identifier.
13
+ #
14
+ # @return [String]
15
+ def key
16
+ FFI::Libvirt.virStorageVolGetKey(self)
17
+ end
18
+ alias :uuid :key
19
+
20
+ # Returns the name of this storage volume.
21
+ #
22
+ # @return [String]
23
+ def name
24
+ FFI::Libvirt.virStorageVolGetName(self)
25
+ end
26
+
27
+ # Returns the path of this storage volume.
28
+ #
29
+ # @return [String]
30
+ def path
31
+ FFI::Libvirt.virStorageVolGetPath(self)
32
+ end
33
+
34
+ # Returns the type of this storage volume.
35
+ #
36
+ # @return [Symbol]
37
+ def type
38
+ info[:type]
39
+ end
40
+
41
+ # Returns the capacity of this volume in bytes.
42
+ #
43
+ # @return [Integer]
44
+ def capacity
45
+ info[:capacity]
46
+ end
47
+
48
+ # Returns the currently allocated number of bytes.
49
+ #
50
+ # @return [Integer]
51
+ def allocation
52
+ info[:allocation]
53
+ end
54
+
55
+ # Wipe the contents of a volume so it is not readable in the future.
56
+ #
57
+ # @return [Boolean]
58
+ def wipe
59
+ FFI::Libvirt.virStorageVolWipe(self, 0) == 0
60
+ end
61
+
62
+ # Delete the storage volume rom the pool.
63
+ #
64
+ # @return [Boolean]
65
+ def delete
66
+ FFI::Libvirt.virStorageVolDelete(self, 0) == 0
67
+ end
68
+
69
+ # Returns the XML description of this storage volume.
70
+ #
71
+ # @return [String]
72
+ def xml
73
+ FFI::Libvirt.virStorageVolGetXMLDesc(self, 0)
74
+ end
75
+
76
+ # Returns the actual `virStorageVolPtr` underlying this structure.
77
+ #
78
+ # @return [FFI::Pointer]
79
+ def to_ptr
80
+ @pointer
81
+ end
82
+
83
+ # Provide a meaningful equality check so that two storage volumes
84
+ # can eaisly be checked for equality.
85
+ #
86
+ # @return [Boolean]
87
+ def ==(other)
88
+ other.is_a?(StorageVolume) && other.uuid == uuid
89
+ end
90
+
91
+ protected
92
+
93
+ # Returns the {FFI::Libvirt::StorageVolumeInfo} object for this volume.
94
+ # The various fields of the info struct are accessible via other getters.
95
+ #
96
+ # @return [FFI::Libvirt::StorageVolumeInfo]
97
+ def info
98
+ result = FFI::Libvirt::StorageVolumeInfo.new
99
+ FFI::Libvirt.virStorageVolGetInfo(self, result.to_ptr)
100
+ result
101
+ end
102
+
103
+ # Release the resources underlying this storage volume. This is called
104
+ # automatically when this volume is garbage collected.
105
+ def finalize(*args)
106
+ FFI::Libvirt.virStorageVolFree(self)
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,3 @@
1
+ module Libvirt
2
+ VERSION = "0.1.0"
3
+ end
data/lib/libvirt.rb ADDED
@@ -0,0 +1,53 @@
1
+ require 'nokogiri'
2
+ require 'ffi/libvirt'
3
+
4
+ # This module is the namespace for the higher level library on top of
5
+ # libvirt, in typicaly Ruby library style. In contrast with {FFI::Libvirt}
6
+ # which is a direct layer on top of FFI, this namespace abstracts much of
7
+ # the manual work away (especially pointer handling) and exposes aspects
8
+ # of the API through nice Ruby objects.
9
+ #
10
+ # Due to the nature of this side of the project, there may be certain features
11
+ # not readily available which are supported by the API. If this is the case,
12
+ # you can use the {FFI::Libvirt} library alongside this side.
13
+ module Libvirt
14
+ autoload :Collection, 'libvirt/collection'
15
+ autoload :Connection, 'libvirt/connection'
16
+ autoload :Domain, 'libvirt/domain'
17
+ autoload :Error, 'libvirt/error'
18
+ autoload :Exception, 'libvirt/exception'
19
+ autoload :Network, 'libvirt/network'
20
+ autoload :Node, 'libvirt/node'
21
+ autoload :NodeDevice, 'libvirt/node_device'
22
+ autoload :Spec, 'libvirt/spec'
23
+ autoload :StoragePool, 'libvirt/storage_pool'
24
+ autoload :StorageVolume, 'libvirt/storage_volume'
25
+
26
+ # Initializes the library by calling `virInitialize`. Most methods
27
+ # in libvirt actually call this themselves, so its not strictly
28
+ # necessary. However, it is good practice and is **highly** recommended
29
+ # that this is called at a same place in a multi-threaded environment.
30
+ def self.initialize!
31
+ FFI::Libvirt.virInitialize == 0
32
+ end
33
+
34
+ # Returns the version of `libvirt` on the client machine. **This is
35
+ # not the version of the `libvirt` ruby library.** The result is
36
+ # return as an array of `[major, minor, patch]`.
37
+ #
38
+ # @return [Array]
39
+ def self.version
40
+ FFI::Libvirt.version
41
+ end
42
+
43
+ # Connect to a hypervisor using libvirt. This is a shortcut to
44
+ # instantiating a {Connection} object, therefore for documentation on
45
+ # the arguments and return value for this method, please consult
46
+ # {Connection#initialize}.
47
+ def self.connect(*args)
48
+ Connection.new(*args)
49
+ end
50
+ end
51
+
52
+ # Disable the stderr output which libvirt defaults to.
53
+ Libvirt::Error.on_error
data/libvirt.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "libvirt/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "libvirt"
7
+ s.version = Libvirt::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Mitchell Hashimoto"]
10
+ s.email = ["mitchell.hashimoto@gmail.com"]
11
+ 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."
14
+
15
+ s.rubyforge_project = "libvirt"
16
+
17
+ s.add_dependency "ffi", "~> 0.6.3"
18
+ s.add_dependency "nokogiri", "~> 1.4.3"
19
+
20
+ s.add_development_dependency "protest", "~> 0.4.0"
21
+ s.add_development_dependency "mocha", "~> 0.9.8"
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+ end
@@ -0,0 +1,14 @@
1
+ require "test_helper"
2
+
3
+ Protest.describe("abstract collection") do
4
+ setup do
5
+ @klass = Libvirt::Collection::AbstractCollection
6
+
7
+ @connection = mock("connection")
8
+ @instance = @klass.new(@connection)
9
+ end
10
+
11
+ should "have interface be available as an attribute" do
12
+ assert_equal @connection, @instance.interface
13
+ end
14
+ end