libvirt_ffi 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/lib/libvirt.rb +1 -2
- data/lib/libvirt/connection.rb +59 -24
- data/lib/libvirt/domain.rb +16 -1
- data/lib/libvirt/domain_callback_storage.rb +65 -0
- data/lib/libvirt/ffi/connection.rb +10 -0
- data/lib/libvirt/ffi/domain.rb +422 -37
- data/lib/libvirt/stream.rb +16 -8
- data/lib/libvirt/util.rb +14 -0
- data/lib/libvirt/version.rb +1 -1
- data/test_usage/test_event_loop.rb +39 -14
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79ea563057bb675ad519b6dfafca175b69afe8f9b0dc9b3ab76c32175372eab1
|
4
|
+
data.tar.gz: 86420a7e48c60ff3a4cf3bbd5377d8070facd1e453494b16225a78c7c3e4c898
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e5c81de1008c0a2c55bcacbbd0a50611d492eba8ee7232fb007f861e011f71053e75434563374c60037a89330b10da5f2873b5fd7359a8ba7bd9a88c011da01d
|
7
|
+
data.tar.gz: b803fae7ff7a85fa744784c89e6566acd43bad33f3229af2f97404e852021005084ca7513afea67f28c9e6e0bc9d2309731f25899bb5b9b5fcb2d8568c46c7d3
|
data/Gemfile
CHANGED
data/lib/libvirt.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'ffi'
|
4
4
|
require 'objspace'
|
5
|
+
require 'libvirt/domain_callback_storage'
|
5
6
|
require 'libvirt/util'
|
6
7
|
require 'libvirt/error'
|
7
8
|
require 'libvirt/ffi/common'
|
@@ -24,8 +25,6 @@ module Libvirt
|
|
24
25
|
EVENT_HANDLE_ERROR = 4
|
25
26
|
EVENT_HANDLE_HANGUP = 8
|
26
27
|
|
27
|
-
DOMAIN_EVENT_ID_LIFECYCLE = 0
|
28
|
-
|
29
28
|
class << self
|
30
29
|
def lib_version
|
31
30
|
version_ptr = ::FFI::MemoryPointer.new(:ulong)
|
data/lib/libvirt/connection.rb
CHANGED
@@ -2,20 +2,48 @@
|
|
2
2
|
|
3
3
|
module Libvirt
|
4
4
|
class Connection
|
5
|
+
DOMAIN_EVENT_IDS = FFI::Domain.enum_type(:event_id).symbols.dup.freeze
|
6
|
+
|
7
|
+
STORAGE = DomainCallbackStorage.new
|
8
|
+
|
9
|
+
DOMAIN_EVENT_CALLBACKS = FFI::Domain.enum_type(:event_id).symbol_map.map do |name, event_id|
|
10
|
+
func = FFI::Domain.event_callback(event_id) do |conn_ptr, dom_ptr, *args, op_ptr|
|
11
|
+
connection = Connection.load_ref(conn_ptr)
|
12
|
+
domain = Domain.load_ref(dom_ptr)
|
13
|
+
block, opaque = STORAGE.retrieve_from_pointer(op_ptr)
|
14
|
+
block.call(connection, domain, *args, opaque)
|
15
|
+
end
|
16
|
+
[name, func]
|
17
|
+
end.to_h
|
18
|
+
|
19
|
+
def self.load_ref(conn_ptr)
|
20
|
+
ref_result = FFI::Connection.virConnectRef(conn_ptr)
|
21
|
+
raise Error, "Couldn't retrieve connection reference" if ref_result < 0
|
22
|
+
new(nil).send(:set_connection, conn_ptr)
|
23
|
+
end
|
24
|
+
|
5
25
|
def initialize(uri)
|
6
26
|
@uri = uri
|
7
27
|
@conn_ptr = ::FFI::Pointer.new(0)
|
8
|
-
@cb_data = {}
|
9
28
|
|
10
29
|
free = ->(obj_id) do
|
11
|
-
|
30
|
+
return if @conn_ptr.null?
|
31
|
+
cl_result = FFI::Connection.virConnectClose(@conn_ptr)
|
32
|
+
STDERR.puts "Couldn't close Libvirt::Connection (0x#{obj_id.to_s(16)}) pointer #{@conn_ptr.address}" if cl_result < 0
|
12
33
|
end
|
13
34
|
ObjectSpace.define_finalizer(self, free)
|
14
35
|
end
|
15
36
|
|
16
37
|
def open
|
17
38
|
@conn_ptr = FFI::Connection.virConnectOpen(@uri)
|
18
|
-
raise Error, "Couldn't
|
39
|
+
raise Error, "Couldn't open connection to #{@uri.inspect}" if @conn_ptr.null?
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def close
|
44
|
+
result = FFI::Connection.virConnectClose(@conn_ptr)
|
45
|
+
raise Error, "Couldn't close connection to #{@uri.inspect}" if result < 0
|
46
|
+
@conn_ptr = ::FFI::Pointer.new(0)
|
19
47
|
true
|
20
48
|
end
|
21
49
|
|
@@ -66,42 +94,46 @@ module Libvirt
|
|
66
94
|
ptr.get_array_of_pointer(0, size).map { |dom_ptr| Libvirt::Domain.new(dom_ptr) }
|
67
95
|
end
|
68
96
|
|
69
|
-
# @yield conn, dom
|
97
|
+
# @yield conn, dom, *args
|
70
98
|
def register_domain_event_callback(event_id, domain = nil, opaque = nil, &block)
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
99
|
+
Util.logger.debug { "Libvirt::Connection#register_domain_event_callback event_id=#{event_id}" }
|
100
|
+
|
101
|
+
enum = FFI::Domain.enum_type(:event_id)
|
102
|
+
event_id, event_id_sym = Util.parse_enum(enum, event_id)
|
103
|
+
cb = DOMAIN_EVENT_CALLBACKS.fetch(event_id_sym)
|
104
|
+
|
105
|
+
cb_data, cb_data_free_func = STORAGE.allocate_struct
|
76
106
|
|
77
107
|
result = FFI::Domain.virConnectDomainEventRegisterAny(
|
78
108
|
@conn_ptr,
|
79
109
|
domain&.to_ptr,
|
80
110
|
event_id,
|
81
111
|
cb,
|
82
|
-
|
83
|
-
|
112
|
+
cb_data.pointer,
|
113
|
+
cb_data_free_func
|
84
114
|
)
|
85
|
-
|
115
|
+
if result < 0
|
116
|
+
cb_data.pointer.free
|
117
|
+
raise Error, "Couldn't register domain event callback"
|
118
|
+
end
|
86
119
|
|
87
|
-
|
120
|
+
STORAGE.store_struct(
|
121
|
+
cb_data,
|
122
|
+
connection_pointer: @conn_ptr,
|
123
|
+
callback_id: result,
|
124
|
+
cb: block,
|
125
|
+
opaque: opaque
|
126
|
+
)
|
88
127
|
result
|
89
128
|
end
|
90
129
|
|
91
130
|
def deregister_domain_event_callback(callback_id)
|
92
|
-
|
131
|
+
Util.logger.debug { "Libvirt::Connection#deregister_domain_event_callback callback_id=#{callback_id}" }
|
132
|
+
|
93
133
|
result = FFI::Domain.virConnectDomainEventDeregisterAny(@conn_ptr, callback_id)
|
94
134
|
raise Error, "Couldn't deregister domain event callback" if result < 0
|
95
|
-
true
|
96
|
-
end
|
97
135
|
|
98
|
-
|
99
|
-
version_ptr = ::FFI::MemoryPointer.new(:ulong)
|
100
|
-
result = FFI::Connection.virConnectGetVersion(@conn_ptr, version_ptr)
|
101
|
-
raise Error, "Couldn't get connection version" if result < 0
|
102
|
-
version_number = version_ptr.get_ulong(0)
|
103
|
-
# version_number = FFI::Connection.virConnectGetVersion(@conn_ptr)
|
104
|
-
Libvirt::Util.parse_version(version_number)
|
136
|
+
STORAGE.remove_struct(connection_pointer: @conn_ptr, callback_id: callback_id)
|
105
137
|
end
|
106
138
|
|
107
139
|
def lib_version
|
@@ -109,7 +141,6 @@ module Libvirt
|
|
109
141
|
result = FFI::Connection.virConnectGetLibVersion(@conn_ptr, version_ptr)
|
110
142
|
raise Error, "Couldn't get connection lib version" if result < 0
|
111
143
|
version_number = version_ptr.get_ulong(0)
|
112
|
-
# version_number = FFI::Connection.virConnectGetLibVersion(@conn_ptr)
|
113
144
|
Libvirt::Util.parse_version(version_number)
|
114
145
|
end
|
115
146
|
|
@@ -141,6 +172,10 @@ module Libvirt
|
|
141
172
|
|
142
173
|
private
|
143
174
|
|
175
|
+
def set_connection(conn_ptr)
|
176
|
+
@conn_ptr = conn_ptr
|
177
|
+
end
|
178
|
+
|
144
179
|
def check_open!
|
145
180
|
raise Error, "Connection to #{@uri.inspect} is not open" if @conn_ptr.null?
|
146
181
|
end
|
data/lib/libvirt/domain.rb
CHANGED
@@ -2,11 +2,20 @@
|
|
2
2
|
|
3
3
|
module Libvirt
|
4
4
|
class Domain
|
5
|
+
|
6
|
+
def self.load_ref(dom_ptr)
|
7
|
+
ref_result = FFI::Domain.virDomainRef(dom_ptr)
|
8
|
+
raise Error, "Couldn't retrieve domain reference" if ref_result < 0
|
9
|
+
new(dom_ptr)
|
10
|
+
end
|
11
|
+
|
5
12
|
def initialize(dom_ptr)
|
6
13
|
@dom_ptr = dom_ptr
|
7
14
|
|
8
15
|
free = ->(obj_id) do
|
9
|
-
|
16
|
+
return unless @dom_ptr
|
17
|
+
fr_result = FFI::Domain.virDomainFree(@dom_ptr)
|
18
|
+
STDERR.puts "Couldn't free Libvirt::Domain (0x#{obj_id.to_s(16)}) pointer #{@dom_ptr.address}" if fr_result < 0
|
10
19
|
end
|
11
20
|
ObjectSpace.define_finalizer(self, free)
|
12
21
|
end
|
@@ -64,5 +73,11 @@ module Libvirt
|
|
64
73
|
# free pointer required
|
65
74
|
mime_type
|
66
75
|
end
|
76
|
+
|
77
|
+
def free_domain
|
78
|
+
result = FFI::Domain.virDomainFree(@dom_ptr)
|
79
|
+
raise Error, "Couldn't free domain" if result < 0
|
80
|
+
@dom_ptr = nil
|
81
|
+
end
|
67
82
|
end
|
68
83
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Libvirt
|
2
|
+
class DomainCallbackStorage
|
3
|
+
|
4
|
+
class CallbackDataStruct < ::FFI::Struct
|
5
|
+
layout :connection_pointer, :pointer,
|
6
|
+
:callback_id, :int
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@inner_storage = Hash.new { |h, key| h[key] = {} }
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Array<2>]
|
14
|
+
# cb_data [Libvirt::DomainCallbackStorage::CallbackDataStruct],
|
15
|
+
# cb_data_free_func [FFI::Function]
|
16
|
+
def allocate_struct
|
17
|
+
Util.logger.debug { "Libvirt::DomainCallbackStorage#allocate_struct" }
|
18
|
+
|
19
|
+
cb_data_ptr = ::FFI::MemoryPointer.new(:char, CallbackDataStruct.size, false)
|
20
|
+
cb_data = CallbackDataStruct.new(cb_data_ptr)
|
21
|
+
cb_data_free_func = ::FFI::Function.new(:void, [:pointer]) do |pointer|
|
22
|
+
Util.logger.debug { "Libvirt::DomainCallbackStorage cb_data_free_func triggered" }
|
23
|
+
remove_struct(pointer: pointer)
|
24
|
+
end
|
25
|
+
[cb_data, cb_data_free_func]
|
26
|
+
end
|
27
|
+
|
28
|
+
def store_struct(cb_data, connection_pointer:, callback_id:, cb:, opaque:)
|
29
|
+
Util.logger.debug { "Libvirt::DomainCallbackStorage#store_struct" }
|
30
|
+
|
31
|
+
cb_data[:connection_pointer] = connection_pointer
|
32
|
+
cb_data[:callback_id] = callback_id
|
33
|
+
@inner_storage[connection_pointer.address][callback_id] = { cb: cb, opaque: opaque, pointer: cb_data.pointer }
|
34
|
+
end
|
35
|
+
|
36
|
+
def remove_struct(pointer: nil, connection_pointer: nil, callback_id: nil)
|
37
|
+
Util.logger.debug { "Libvirt::DomainCallbackStorage#remove_struct pointer=#{pointer}, connection_pointer=#{connection_pointer}, callback_id=#{callback_id}," }
|
38
|
+
|
39
|
+
if pointer
|
40
|
+
cb_data_struct = CallbackDataStruct.new(pointer)
|
41
|
+
connection_pointer = cb_data_struct[:connection_pointer]
|
42
|
+
callback_id = cb_data_struct[:callback_id]
|
43
|
+
end
|
44
|
+
|
45
|
+
cb_data = @inner_storage[connection_pointer.address].delete(callback_id)
|
46
|
+
pointer ||= cb_data[:pointer]
|
47
|
+
@inner_storage.delete(connection_pointer.address) if @inner_storage[connection_pointer.address].empty?
|
48
|
+
|
49
|
+
#pointer.free
|
50
|
+
cb_data[:opaque]
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param [::FFI::Pointer]
|
54
|
+
# @return [Array<2>] cb [Proc], opaque [Object]
|
55
|
+
def retrieve_from_pointer(pointer)
|
56
|
+
Util.logger.debug { "Libvirt::DomainCallbackStorage#retrieve_from_pointer pointer=#{pointer}," }
|
57
|
+
|
58
|
+
cb_data_struct = CallbackDataStruct.new(pointer)
|
59
|
+
connection_pointer = cb_data_struct[:connection_pointer]
|
60
|
+
callback_id = cb_data_struct[:callback_id]
|
61
|
+
cb_data = @inner_storage[connection_pointer.address][callback_id]
|
62
|
+
[cb_data[:cb], cb_data[:opaque]]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -79,6 +79,16 @@ module Libvirt
|
|
79
79
|
# virConnectPtr conn
|
80
80
|
# )
|
81
81
|
attach_function :virConnectGetCapabilities, [:pointer], :string # strptr ?
|
82
|
+
|
83
|
+
# int virConnectClose (
|
84
|
+
# virConnectPtr conn
|
85
|
+
# )
|
86
|
+
attach_function :virConnectClose, [:pointer], :int
|
87
|
+
|
88
|
+
# int virConnectRef (
|
89
|
+
# virConnectPtr conn
|
90
|
+
# )
|
91
|
+
attach_function :virConnectRef, [:pointer], :int
|
82
92
|
end
|
83
93
|
end
|
84
94
|
end
|
data/lib/libvirt/ffi/domain.rb
CHANGED
@@ -8,6 +8,85 @@ module Libvirt
|
|
8
8
|
|
9
9
|
UUID_STRING_BUFLEN = 0x80 # RFC4122
|
10
10
|
|
11
|
+
# enum virDomainEventID
|
12
|
+
enum :event_id, [
|
13
|
+
:LIFECYCLE, 0, # (0x0) virConnectDomainEventCallback
|
14
|
+
:REBOOT, 1, # (0x1) virConnectDomainEventGenericCallback
|
15
|
+
:RTC_CHANGE, 2, # (0x2) virConnectDomainEventRTCChangeCallback
|
16
|
+
:WATCHDOG, 3, # (0x3) virConnectDomainEventWatchdogCallback
|
17
|
+
:IO_ERROR, 4, # (0x4) virConnectDomainEventIOErrorCallback
|
18
|
+
:GRAPHICS, 5, # (0x5) virConnectDomainEventGraphicsCallback
|
19
|
+
:IO_ERROR_REASON, 6, # (0x6) virConnectDomainEventIOErrorReasonCallback
|
20
|
+
:CONTROL_ERROR, 7, # (0x7) virConnectDomainEventGenericCallback
|
21
|
+
:BLOCK_JOB, 8, # (0x8) virConnectDomainEventBlockJobCallback
|
22
|
+
:DISK_CHANGE, 9, # (0x9) virConnectDomainEventDiskChangeCallback
|
23
|
+
:TRAY_CHANGE, 10, # (0xa) virConnectDomainEventTrayChangeCallback
|
24
|
+
:PMWAKEUP, 11, # (0xb) virConnectDomainEventPMWakeupCallback
|
25
|
+
:PMSUSPEND, 12, # (0xc) virConnectDomainEventPMSuspendCallback
|
26
|
+
:BALLOON_CHANGE, 13, # (0xd) virConnectDomainEventBalloonChangeCallback
|
27
|
+
:PMSUSPEND_DISK, 14, # (0xe) virConnectDomainEventPMSuspendDiskCallback
|
28
|
+
:DEVICE_REMOVED, 15, # (0xf) virConnectDomainEventDeviceRemovedCallback
|
29
|
+
:BLOCK_JOB_2, 16, # (0x10) virConnectDomainEventBlockJobCallback
|
30
|
+
:TUNABLE, 17, # (0x11) virConnectDomainEventTunableCallback
|
31
|
+
:AGENT_LIFECYCLE, 18, # (0x12) virConnectDomainEventAgentLifecycleCallback
|
32
|
+
:DEVICE_ADDED, 19, # (0x13) virConnectDomainEventDeviceAddedCallback
|
33
|
+
:MIGRATION_ITERATION, 20, # (0x14) virConnectDomainEventMigrationIterationCallback
|
34
|
+
:JOB_COMPLETED, 21, # (0x15) virConnectDomainEventJobCompletedCallback
|
35
|
+
:DEVICE_REMOVAL_FAILED, 22, # (0x16) virConnectDomainEventDeviceRemovalFailedCallback
|
36
|
+
:METADATA_CHANGE, 23, # (0x17) virConnectDomainEventMetadataChangeCallback
|
37
|
+
:BLOCK_THRESHOLD, 24 # (0x18) virConnectDomainEventBlockThresholdCallback
|
38
|
+
]
|
39
|
+
|
40
|
+
EVENT_ID_TO_CALLBACK = {
|
41
|
+
LIFECYCLE: :virConnectDomainEventCallback,
|
42
|
+
REBOOT: :virConnectDomainEventGenericCallback,
|
43
|
+
RTC_CHANGE: :virConnectDomainEventRTCChangeCallback,
|
44
|
+
WATCHDOG: :virConnectDomainEventWatchdogCallback,
|
45
|
+
IO_ERROR: :virConnectDomainEventIOErrorCallback,
|
46
|
+
GRAPHICS: :virConnectDomainEventGraphicsCallback,
|
47
|
+
IO_ERROR_REASON: :virConnectDomainEventIOErrorReasonCallback,
|
48
|
+
CONTROL_ERROR: :virConnectDomainEventGenericCallback,
|
49
|
+
BLOCK_JOB: :virConnectDomainEventBlockJobCallback,
|
50
|
+
DISK_CHANGE: :virConnectDomainEventDiskChangeCallback,
|
51
|
+
TRAY_CHANGE: :virConnectDomainEventTrayChangeCallback,
|
52
|
+
PMWAKEUP: :virConnectDomainEventPMWakeupCallback,
|
53
|
+
PMSUSPEND: :virConnectDomainEventPMSuspendCallback,
|
54
|
+
BALLOON_CHANGE: :virConnectDomainEventBalloonChangeCallback,
|
55
|
+
PMSUSPEND_DISK: :virConnectDomainEventPMSuspendDiskCallback,
|
56
|
+
DEVICE_REMOVED: :virConnectDomainEventDeviceRemovedCallback,
|
57
|
+
BLOCK_JOB_2: :virConnectDomainEventBlockJobCallback,
|
58
|
+
TUNABLE: :virConnectDomainEventTunableCallback,
|
59
|
+
AGENT_LIFECYCLE: :virConnectDomainEventAgentLifecycleCallback,
|
60
|
+
DEVICE_ADDED: :virConnectDomainEventDeviceAddedCallback,
|
61
|
+
MIGRATION_ITERATION: :virConnectDomainEventMigrationIterationCallback,
|
62
|
+
JOB_COMPLETED: :virConnectDomainEventJobCompletedCallback,
|
63
|
+
DEVICE_REMOVAL_FAILED: :virConnectDomainEventDeviceRemovalFailedCallback,
|
64
|
+
METADATA_CHANGE: :virConnectDomainEventMetadataChangeCallback,
|
65
|
+
BLOCK_THRESHOLD: :virConnectDomainEventBlockThresholdCallback
|
66
|
+
}
|
67
|
+
|
68
|
+
# enum virDomainState
|
69
|
+
enum :state, [
|
70
|
+
:NOSTATE, 0x0, # no state
|
71
|
+
:RUNNING, 0x1, # the domain is running
|
72
|
+
:BLOCKED, 0x2, # the domain is blocked on resource
|
73
|
+
:PAUSED, 0x3, # the domain is paused by user
|
74
|
+
:SHUTDOWN, 0x4, # the domain is being shut down
|
75
|
+
:SHUTOFF, 0x5, # the domain is shut off
|
76
|
+
:CRASHED, 0x6, # the domain is crashed
|
77
|
+
:PMSUSPENDED, 0x7 # the domain is suspended by guest power management
|
78
|
+
]
|
79
|
+
|
80
|
+
# int virDomainFree (
|
81
|
+
# virDomainPtr domain
|
82
|
+
# )
|
83
|
+
attach_function :virDomainFree, [:pointer], :int
|
84
|
+
|
85
|
+
# int virDomainRef (
|
86
|
+
# virDomainPtr domain
|
87
|
+
# )
|
88
|
+
attach_function :virDomainRef, [:pointer], :int
|
89
|
+
|
11
90
|
# int virConnectDomainEventRegisterAny(
|
12
91
|
# virConnectPtr conn,
|
13
92
|
# virDomainPtr dom,
|
@@ -25,6 +104,12 @@ module Libvirt
|
|
25
104
|
:pointer
|
26
105
|
], :int
|
27
106
|
|
107
|
+
# int virConnectDomainEventDeregisterAny (
|
108
|
+
# virConnectPtr conn,
|
109
|
+
# int callbackID
|
110
|
+
# )
|
111
|
+
attach_function :virConnectDomainEventDeregisterAny, [:pointer, :int], :int
|
112
|
+
|
28
113
|
# int virConnectListAllDomains (
|
29
114
|
# virConnectPtr conn,
|
30
115
|
# virDomainPtr **domains,
|
@@ -32,39 +117,6 @@ module Libvirt
|
|
32
117
|
# )
|
33
118
|
attach_function :virConnectListAllDomains, [:pointer, :pointer, :uint], :int
|
34
119
|
|
35
|
-
# enum virDomainState {
|
36
|
-
# VIR_DOMAIN_NOSTATE = 0 (0x0)
|
37
|
-
# no state
|
38
|
-
# VIR_DOMAIN_RUNNING = 1 (0x1)
|
39
|
-
# the domain is running
|
40
|
-
# VIR_DOMAIN_BLOCKED = 2 (0x2)
|
41
|
-
# the domain is blocked on resource
|
42
|
-
# VIR_DOMAIN_PAUSED = 3 (0x3)
|
43
|
-
# the domain is paused by user
|
44
|
-
# VIR_DOMAIN_SHUTDOWN = 4 (0x4)
|
45
|
-
# the domain is being shut down
|
46
|
-
# VIR_DOMAIN_SHUTOFF = 5 (0x5)
|
47
|
-
# the domain is shut off
|
48
|
-
# VIR_DOMAIN_CRASHED = 6 (0x6)
|
49
|
-
# the domain is crashed
|
50
|
-
# VIR_DOMAIN_PMSUSPENDED = 7 (0x7)
|
51
|
-
# the domain is suspended by guest power management
|
52
|
-
# VIR_DOMAIN_LAST = 8 (0x8)
|
53
|
-
# NB: this enum value will increase over time as new events are added to the libvirt API.
|
54
|
-
# It reflects the last state supported by this version of the libvirt API.
|
55
|
-
# }
|
56
|
-
enum :states, [
|
57
|
-
:no_state, 0x0,
|
58
|
-
:running, 0x1,
|
59
|
-
:blocked, 0x2,
|
60
|
-
:paused, 0x3,
|
61
|
-
:shutdown, 0x4,
|
62
|
-
:shutoff, 0x5,
|
63
|
-
:crashed, 0x6,
|
64
|
-
:pm_suspended, 0x7,
|
65
|
-
:last, 0x8
|
66
|
-
]
|
67
|
-
|
68
120
|
# int virDomainGetState (
|
69
121
|
# virDomainPtr domain,
|
70
122
|
# int *state,
|
@@ -124,10 +176,343 @@ module Libvirt
|
|
124
176
|
# int detail,
|
125
177
|
# void * opaque
|
126
178
|
# )
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
179
|
+
callback :virConnectDomainEventCallback, [:pointer, :pointer, :int, :int, :pointer], :int
|
180
|
+
|
181
|
+
# typedef void (*virConnectDomainEventGenericCallback) (
|
182
|
+
# virConnectPtr conn,
|
183
|
+
# virDomainPtr dom,
|
184
|
+
# void * opaque
|
185
|
+
# )
|
186
|
+
callback :virConnectDomainEventGenericCallback, [:pointer, :pointer, :pointer], :void
|
187
|
+
|
188
|
+
# typedef void (*virConnectDomainEventRTCChangeCallback) (
|
189
|
+
# virConnectPtr conn,
|
190
|
+
# virDomainPtr dom,
|
191
|
+
# long long utcoffset,
|
192
|
+
# void * opaque
|
193
|
+
# )
|
194
|
+
callback :virConnectDomainEventRTCChangeCallback, [
|
195
|
+
:pointer,
|
196
|
+
:pointer,
|
197
|
+
:long_long,
|
198
|
+
:pointer
|
199
|
+
], :void
|
200
|
+
|
201
|
+
# typedef void (*virConnectDomainEventWatchdogCallback) (
|
202
|
+
# virConnectPtr conn,
|
203
|
+
# virDomainPtr dom,
|
204
|
+
# int action,
|
205
|
+
# void * opaque
|
206
|
+
# )
|
207
|
+
callback :virConnectDomainEventWatchdogCallback, [
|
208
|
+
:pointer,
|
209
|
+
:pointer,
|
210
|
+
:int,
|
211
|
+
:pointer
|
212
|
+
], :void
|
213
|
+
|
214
|
+
# typedef void (*virConnectDomainEventIOErrorCallback) (
|
215
|
+
# virConnectPtr conn,
|
216
|
+
# virDomainPtr dom,
|
217
|
+
# const char * srcPath,
|
218
|
+
# const char * devAlias,
|
219
|
+
# int action,
|
220
|
+
# void * opaque
|
221
|
+
# )
|
222
|
+
callback :virConnectDomainEventIOErrorCallback, [
|
223
|
+
:pointer,
|
224
|
+
:pointer,
|
225
|
+
:string,
|
226
|
+
:string,
|
227
|
+
:int,
|
228
|
+
:pointer
|
229
|
+
], :void
|
230
|
+
|
231
|
+
# typedef void (*virConnectDomainEventGraphicsCallback) (
|
232
|
+
# virConnectPtr conn,
|
233
|
+
# virDomainPtr dom,
|
234
|
+
# int phase,
|
235
|
+
# const virDomainEventGraphicsAddress * local,
|
236
|
+
# const virDomainEventGraphicsAddress * remote,
|
237
|
+
# const char * authScheme,
|
238
|
+
# const virDomainEventGraphicsSubject * subject,
|
239
|
+
# void * opaque
|
240
|
+
# )
|
241
|
+
callback :virConnectDomainEventGraphicsCallback, [
|
242
|
+
:pointer,
|
243
|
+
:pointer,
|
244
|
+
:int,
|
245
|
+
# virDomainEventGraphicsAddress
|
246
|
+
# virDomainEventGraphicsAddress
|
247
|
+
:string,
|
248
|
+
# virDomainEventGraphicsSubject
|
249
|
+
:pointer
|
250
|
+
], :void
|
251
|
+
|
252
|
+
# typedef void (*virConnectDomainEventIOErrorReasonCallback) (
|
253
|
+
# virConnectPtr conn,
|
254
|
+
# virDomainPtr dom,
|
255
|
+
# const char * srcPath,
|
256
|
+
# const char * devAlias,
|
257
|
+
# int action,
|
258
|
+
# const char * reason,
|
259
|
+
# void * opaque
|
260
|
+
# )
|
261
|
+
callback :virConnectDomainEventIOErrorReasonCallback, [
|
262
|
+
:pointer,
|
263
|
+
:pointer,
|
264
|
+
:string,
|
265
|
+
:string,
|
266
|
+
:int,
|
267
|
+
:string,
|
268
|
+
:pointer
|
269
|
+
], :void
|
270
|
+
|
271
|
+
# typedef void (*virConnectDomainEventBlockJobCallback) (
|
272
|
+
# virConnectPtr conn,
|
273
|
+
# virDomainPtr dom,
|
274
|
+
# const char * disk,
|
275
|
+
# int type,
|
276
|
+
# int status,
|
277
|
+
# void * opaque
|
278
|
+
# )
|
279
|
+
callback :virConnectDomainEventBlockJobCallback, [
|
280
|
+
:pointer,
|
281
|
+
:pointer,
|
282
|
+
:string,
|
283
|
+
:int,
|
284
|
+
:int,
|
285
|
+
:pointer
|
286
|
+
], :void
|
287
|
+
|
288
|
+
# typedef void (*virConnectDomainEventDiskChangeCallback) (
|
289
|
+
# virConnectPtr conn,
|
290
|
+
# virDomainPtr dom,
|
291
|
+
# const char * oldSrcPath,
|
292
|
+
# const char * newSrcPath,
|
293
|
+
# const char * devAlias,
|
294
|
+
# int reason,
|
295
|
+
# void * opaque
|
296
|
+
# )
|
297
|
+
callback :virConnectDomainEventDiskChangeCallback, [
|
298
|
+
:pointer,
|
299
|
+
:pointer,
|
300
|
+
:string,
|
301
|
+
:string,
|
302
|
+
:string,
|
303
|
+
:int,
|
304
|
+
:pointer
|
305
|
+
], :void
|
306
|
+
|
307
|
+
# typedef void (*virConnectDomainEventTrayChangeCallback) (
|
308
|
+
# virConnectPtr conn,
|
309
|
+
# virDomainPtr dom,
|
310
|
+
# const char * devAlias,
|
311
|
+
# int reason,
|
312
|
+
# void * opaque
|
313
|
+
# )
|
314
|
+
callback :virConnectDomainEventTrayChangeCallback, [
|
315
|
+
:pointer,
|
316
|
+
:pointer,
|
317
|
+
:string,
|
318
|
+
:int,
|
319
|
+
:pointer
|
320
|
+
], :void
|
321
|
+
|
322
|
+
# typedef void (*virConnectDomainEventPMWakeupCallback) (
|
323
|
+
# virConnectPtr conn,
|
324
|
+
# virDomainPtr dom,
|
325
|
+
# int reason,
|
326
|
+
# void * opaque
|
327
|
+
# )
|
328
|
+
callback :virConnectDomainEventPMWakeupCallback, [
|
329
|
+
:pointer,
|
330
|
+
:pointer,
|
331
|
+
:int,
|
332
|
+
:pointer
|
333
|
+
], :void
|
334
|
+
|
335
|
+
# typedef void (*virConnectDomainEventPMSuspendCallback) (
|
336
|
+
# virConnectPtr conn,
|
337
|
+
# virDomainPtr dom,
|
338
|
+
# int reason,
|
339
|
+
# void * opaque
|
340
|
+
# )
|
341
|
+
callback :virConnectDomainEventPMSuspendCallback, [
|
342
|
+
:pointer,
|
343
|
+
:pointer,
|
344
|
+
:int,
|
345
|
+
:pointer
|
346
|
+
], :void
|
347
|
+
|
348
|
+
# typedef void (*virConnectDomainEventBalloonChangeCallback) (
|
349
|
+
# virConnectPtr conn,
|
350
|
+
# virDomainPtr dom,
|
351
|
+
# unsigned long long actual,
|
352
|
+
# void * opaque
|
353
|
+
# )
|
354
|
+
callback :virConnectDomainEventBalloonChangeCallback, [
|
355
|
+
:pointer,
|
356
|
+
:pointer,
|
357
|
+
:ulong_long,
|
358
|
+
:pointer
|
359
|
+
], :void
|
360
|
+
|
361
|
+
# typedef void (*virConnectDomainEventPMSuspendDiskCallback) (
|
362
|
+
# virConnectPtr conn,
|
363
|
+
# virDomainPtr dom,
|
364
|
+
# int reason,
|
365
|
+
# void * opaque
|
366
|
+
# )
|
367
|
+
callback :virConnectDomainEventPMSuspendDiskCallback, [
|
368
|
+
:pointer,
|
369
|
+
:pointer,
|
370
|
+
:int,
|
371
|
+
:pointer
|
372
|
+
], :void
|
373
|
+
|
374
|
+
# typedef void (*virConnectDomainEventDeviceRemovedCallback) (
|
375
|
+
# virConnectPtr conn,
|
376
|
+
# virDomainPtr dom,
|
377
|
+
# const char * devAlias,
|
378
|
+
# void * opaque
|
379
|
+
# )
|
380
|
+
callback :virConnectDomainEventDeviceRemovedCallback, [
|
381
|
+
:pointer,
|
382
|
+
:pointer,
|
383
|
+
:string,
|
384
|
+
:pointer
|
385
|
+
], :void
|
386
|
+
|
387
|
+
# typedef void (*virConnectDomainEventTunableCallback) (
|
388
|
+
# virConnectPtr conn,
|
389
|
+
# virDomainPtr dom,
|
390
|
+
# virTypedParameterPtr params,
|
391
|
+
# int nparams,
|
392
|
+
# void * opaque
|
393
|
+
# )
|
394
|
+
callback :virConnectDomainEventTunableCallback, [
|
395
|
+
:pointer,
|
396
|
+
:pointer,
|
397
|
+
# virTypedParameterPtr
|
398
|
+
:int,
|
399
|
+
:pointer
|
400
|
+
], :void
|
401
|
+
|
402
|
+
# typedef void (*virConnectDomainEventAgentLifecycleCallback) (
|
403
|
+
# virConnectPtr conn,
|
404
|
+
# virDomainPtr dom,
|
405
|
+
# int state,
|
406
|
+
# int reason,
|
407
|
+
# void * opaque
|
408
|
+
# )
|
409
|
+
callback :virConnectDomainEventAgentLifecycleCallback, [
|
410
|
+
:pointer,
|
411
|
+
:pointer,
|
412
|
+
:int,
|
413
|
+
:int,
|
414
|
+
:pointer
|
415
|
+
], :void
|
416
|
+
|
417
|
+
# typedef void (*virConnectDomainEventDeviceAddedCallback) (
|
418
|
+
# virConnectPtr conn,
|
419
|
+
# virDomainPtr dom,
|
420
|
+
# const char * devAlias,
|
421
|
+
# void * opaque
|
422
|
+
# )
|
423
|
+
callback :virConnectDomainEventDeviceAddedCallback, [
|
424
|
+
:pointer,
|
425
|
+
:pointer,
|
426
|
+
:string,
|
427
|
+
:pointer
|
428
|
+
], :void
|
429
|
+
|
430
|
+
# typedef void (*virConnectDomainEventMigrationIterationCallback) (
|
431
|
+
# virConnectPtr conn,
|
432
|
+
# virDomainPtr dom,
|
433
|
+
# int iteration,
|
434
|
+
# void * opaque
|
435
|
+
# )
|
436
|
+
callback :virConnectDomainEventMigrationIterationCallback, [
|
437
|
+
:pointer,
|
438
|
+
:pointer,
|
439
|
+
:int,
|
440
|
+
:pointer
|
441
|
+
], :void
|
442
|
+
|
443
|
+
# typedef void (*virConnectDomainEventJobCompletedCallback) (
|
444
|
+
# virConnectPtr conn,
|
445
|
+
# virDomainPtr dom,
|
446
|
+
# virTypedParameterPtr params,
|
447
|
+
# int nparams,
|
448
|
+
# void * opaque
|
449
|
+
# )
|
450
|
+
callback :virConnectDomainEventJobCompletedCallback, [
|
451
|
+
:pointer,
|
452
|
+
:pointer,
|
453
|
+
# virTypedParameterPtr
|
454
|
+
:int,
|
455
|
+
:pointer
|
456
|
+
], :void
|
457
|
+
|
458
|
+
# typedef void (*virConnectDomainEventDeviceRemovalFailedCallback) (
|
459
|
+
# virConnectPtr conn,
|
460
|
+
# virDomainPtr dom,
|
461
|
+
# const char * devAlias,
|
462
|
+
# void * opaque
|
463
|
+
# )
|
464
|
+
callback :virConnectDomainEventDeviceRemovalFailedCallback, [
|
465
|
+
:pointer,
|
466
|
+
:pointer,
|
467
|
+
:string,
|
468
|
+
:pointer
|
469
|
+
], :void
|
470
|
+
|
471
|
+
# typedef void (*virConnectDomainEventMetadataChangeCallback) (
|
472
|
+
# virConnectPtr conn,
|
473
|
+
# virDomainPtr dom,
|
474
|
+
# int type,
|
475
|
+
# const char * nsuri,
|
476
|
+
# void * opaque
|
477
|
+
# )
|
478
|
+
callback :virConnectDomainEventMetadataChangeCallback, [
|
479
|
+
:pointer,
|
480
|
+
:pointer,
|
481
|
+
:string,
|
482
|
+
:pointer
|
483
|
+
], :void
|
484
|
+
|
485
|
+
# typedef void (*virConnectDomainEventBlockThresholdCallback) (
|
486
|
+
# virConnectPtr conn,
|
487
|
+
# virDomainPtr dom,
|
488
|
+
# const char * dev,
|
489
|
+
# const char * path,
|
490
|
+
# unsigned long long threshold,
|
491
|
+
# unsigned long long excess,
|
492
|
+
# void * opaque
|
493
|
+
# )
|
494
|
+
callback :virConnectDomainEventBlockThresholdCallback, [
|
495
|
+
:pointer,
|
496
|
+
:pointer,
|
497
|
+
:string,
|
498
|
+
:string,
|
499
|
+
:ulong_long,
|
500
|
+
:ulong_long,
|
501
|
+
:pointer
|
502
|
+
], :void
|
503
|
+
|
504
|
+
# @param event_id [Integer]
|
505
|
+
# @yield connect_ptr, domain_ptr, *args, opaque_ptr
|
506
|
+
def self.event_callback(event_id, &block)
|
507
|
+
event_id_sym = enum_type(:event_id)[event_id]
|
508
|
+
callback_name = EVENT_ID_TO_CALLBACK.fetch(event_id_sym)
|
509
|
+
callback_info = find_type(callback_name)
|
510
|
+
::FFI::Function.new(callback_info.result_type, callback_info.param_types) do |*args|
|
511
|
+
Util.log(:debug) { "Libvirt::Domain #{event_id_sym} CALLBACK #{args.map(&:to_s).join(', ')}," }
|
512
|
+
block.call(*args)
|
513
|
+
# Only callback for lifecycle must return 0.
|
514
|
+
# Return value of other callbacks are ignored.
|
515
|
+
# So we just pass zero everywhere.
|
131
516
|
0
|
132
517
|
end
|
133
518
|
end
|
data/lib/libvirt/stream.rb
CHANGED
@@ -12,15 +12,15 @@ module Libvirt
|
|
12
12
|
@opaque = nil
|
13
13
|
|
14
14
|
free = ->(obj_id) do
|
15
|
-
|
16
|
-
if @
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
return unless @stream_ptr
|
16
|
+
if @cb
|
17
|
+
rcb_result = FFI::Stream.virStreamEventRemoveCallback(@stream_ptr)
|
18
|
+
STDERR.puts("Couldn't remove callback Libvirt::Stream (0x#{obj_id.to_s(16)}) pointer #{@stream_ptr.address}") if rcb_result < 0
|
19
|
+
ab_result = FFI::Stream.virStreamAbort(@stream_ptr)
|
20
|
+
STDERR.puts("Couldn't abort Libvirt::Stream (0x#{obj_id.to_s(16)}) pointer #{@stream_ptr.address}") if ab_result < 0
|
21
21
|
end
|
22
|
-
|
23
|
-
|
22
|
+
fr_result = FFI::Stream.virStreamFree(@stream_ptr)
|
23
|
+
STDERR.puts("Couldn't free Libvirt::Stream (0x#{obj_id.to_s(16)}) pointer #{@stream_ptr.address}") if fr_result < 0
|
24
24
|
end
|
25
25
|
ObjectSpace.define_finalizer(self, free)
|
26
26
|
end
|
@@ -77,6 +77,14 @@ module Libvirt
|
|
77
77
|
@opaque = nil
|
78
78
|
end
|
79
79
|
|
80
|
+
def free_stream
|
81
|
+
result = FFI::Stream.virStreamFree(@stream_ptr)
|
82
|
+
raise Error, "Couldn't free stream event callback" if result < 0
|
83
|
+
@cb = nil
|
84
|
+
@opaque = nil
|
85
|
+
@stream_ptr = nil
|
86
|
+
end
|
87
|
+
|
80
88
|
def recv(bytes)
|
81
89
|
buffer = ::FFI::MemoryPointer.new(:char, bytes)
|
82
90
|
result = FFI::Stream.virStreamRecv(@stream_ptr, buffer, bytes)
|
data/lib/libvirt/util.rb
CHANGED
@@ -29,6 +29,20 @@ module Libvirt
|
|
29
29
|
"#{major}.#{minor}.#{release}"
|
30
30
|
end
|
31
31
|
|
32
|
+
# @param enum [FFI::Enum]
|
33
|
+
# @param value [Symbol, Integer]
|
34
|
+
# @return [Array] event_id, event_id_sym
|
35
|
+
# @raise ArgumentError
|
36
|
+
def parse_enum(enum, value)
|
37
|
+
if value.is_a?(Symbol)
|
38
|
+
raise ArgumentError, 'invalid enum value' unless enum.symbols.include?(value)
|
39
|
+
return [enum.find(value), value]
|
40
|
+
end
|
41
|
+
|
42
|
+
raise ArgumentError, 'invalid enum value' unless enum.symbol_map.values.include?(value)
|
43
|
+
[value, enum.symbol_map[value]]
|
44
|
+
end
|
45
|
+
|
32
46
|
end
|
33
47
|
end
|
34
48
|
end
|
data/lib/libvirt/version.rb
CHANGED
@@ -6,16 +6,26 @@ require 'logger'
|
|
6
6
|
require 'active_support/all'
|
7
7
|
require 'async'
|
8
8
|
require 'get_process_mem'
|
9
|
+
require 'gc_tracer'
|
9
10
|
|
10
11
|
require_relative 'support/libvirt_async'
|
11
12
|
require_relative 'support/log_formatter'
|
12
13
|
|
14
|
+
GC::Tracer.start_logging(
|
15
|
+
nil,
|
16
|
+
gc_stat: false,
|
17
|
+
gc_latest_gc_info: false,
|
18
|
+
rusage: false,
|
19
|
+
events: %i[end_mark end_sweep]
|
20
|
+
)
|
21
|
+
|
13
22
|
Libvirt.logger = Logger.new(STDOUT, formatter: LogFormatter.new)
|
14
23
|
Libvirt.logger.level = ENV['DEBUG'] ? :debug : :info
|
15
24
|
|
16
25
|
IMPL = LibvirtAsync::Implementations.new
|
17
26
|
CONNS = []
|
18
27
|
DOMS = []
|
28
|
+
CB_IDS = []
|
19
29
|
|
20
30
|
Async do
|
21
31
|
ASYNC_REACTOR = Async::Task.current.reactor
|
@@ -47,9 +57,13 @@ Async do
|
|
47
57
|
puts "NodeInfo threads #{node_info.threads.inspect}"
|
48
58
|
puts "NodeInfo memory #{node_info.memory.inspect}"
|
49
59
|
|
50
|
-
|
51
|
-
|
60
|
+
cb_ids = Libvirt::Connection::DOMAIN_EVENT_IDS.map do |event_id|
|
61
|
+
op = OpenStruct.new(a: 'b', event_id: event_id)
|
62
|
+
c.register_domain_event_callback(event_id, nil, op) do |conn, dom, *args, opaque|
|
63
|
+
Libvirt.logger.info { "DOMAIN EVENT #{event_id} conn=#{conn}, dom=#{dom}, args=#{args}, opaque=#{opaque}" }
|
64
|
+
end
|
52
65
|
end
|
66
|
+
CB_IDS.concat(cb_ids)
|
53
67
|
|
54
68
|
puts "domains qty #{c.list_all_domains_qty}"
|
55
69
|
|
@@ -57,12 +71,6 @@ Async do
|
|
57
71
|
DOMS.concat(domains)
|
58
72
|
puts "Domains (#{domains.size}): #{domains}"
|
59
73
|
|
60
|
-
domains.each_with_index do |domain, index|
|
61
|
-
c.register_domain_event_callback(Libvirt::DOMAIN_EVENT_ID_LIFECYCLE, domain) do |dom, event, detail, opaque|
|
62
|
-
Libvirt.logger.info { "DOMAIN_EVENT_CALLBACK LIFECYCLE user##{index} dom=#{dom}, event=#{event}, detail=#{detail}, opaque=#{opaque}" }
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
74
|
d = domains.first
|
67
75
|
puts "Domain uuid #{d.uuid.inspect}"
|
68
76
|
puts "Domain name #{d.name.inspect}"
|
@@ -75,10 +83,27 @@ Async do
|
|
75
83
|
# LibvirtAsync::Util.create_task(nil, ASYNC_REACTOR) { IMPL.print_debug_info }.run
|
76
84
|
# end
|
77
85
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
86
|
+
ASYNC_REACTOR.every(5) do
|
87
|
+
Libvirt.logger.info { "MEM USAGE: #{GetProcessMem.new.mb} MB" }
|
88
|
+
# Libvirt.logger.info { "GC.start" }
|
89
|
+
# GC.start
|
90
|
+
# Libvirt.logger.info { "MEM USAGE: #{GetProcessMem.new.mb} MB" }
|
91
|
+
end
|
92
|
+
|
93
|
+
ASYNC_REACTOR.after(20) do
|
94
|
+
LibvirtAsync::Util.create_task(nil, ASYNC_REACTOR) do
|
95
|
+
|
96
|
+
c = CONNS.first
|
97
|
+
CB_IDS.each do |callback_id|
|
98
|
+
opaque = c.deregister_domain_event_callback(callback_id)
|
99
|
+
puts "Retrieved opaque #{opaque}"
|
100
|
+
end
|
101
|
+
Libvirt.logger.info { 'Cleaning up!' }
|
102
|
+
CONNS = []
|
103
|
+
DOMS = []
|
104
|
+
CB_IDS = []
|
105
|
+
GC.start
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
84
109
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: libvirt_ffi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Talakevich
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -45,6 +45,7 @@ files:
|
|
45
45
|
- lib/libvirt.rb
|
46
46
|
- lib/libvirt/connection.rb
|
47
47
|
- lib/libvirt/domain.rb
|
48
|
+
- lib/libvirt/domain_callback_storage.rb
|
48
49
|
- lib/libvirt/error.rb
|
49
50
|
- lib/libvirt/event.rb
|
50
51
|
- lib/libvirt/ffi/common.rb
|