libvirt_ffi 0.3.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32cd6d96cd065507a105cffe398c5d6bfa2afd2cd472888f831dd2ad382a48de
4
- data.tar.gz: 06ae43ce6d0d65c8d6a5daa0914df5cfa01a3102f467bc7bef3bafaa19e2208a
3
+ metadata.gz: 79ea563057bb675ad519b6dfafca175b69afe8f9b0dc9b3ab76c32175372eab1
4
+ data.tar.gz: 86420a7e48c60ff3a4cf3bbd5377d8070facd1e453494b16225a78c7c3e4c898
5
5
  SHA512:
6
- metadata.gz: 205e7bc1253381af45bcf354b784476acf35f862ba212f6a9b7cdeba3673ed2ae5b73b54ec0a894632e753dd8f1e7ba90cec6a40187c1a0c33df6900f4208c71
7
- data.tar.gz: d5754a19f39a9c508722b1cb8d29227d38c62eb1c9f0a7503d94aba33f815180217d4b346c32a1e4638a8b1e2526910ebf6dc6ccffe6ab8e3c415870b629b430
6
+ metadata.gz: e5c81de1008c0a2c55bcacbbd0a50611d492eba8ee7232fb007f861e011f71053e75434563374c60037a89330b10da5f2873b5fd7359a8ba7bd9a88c011da01d
7
+ data.tar.gz: b803fae7ff7a85fa744784c89e6566acd43bad33f3229af2f97404e852021005084ca7513afea67f28c9e6e0bc9d2309731f25899bb5b9b5fcb2d8568c46c7d3
data/Gemfile CHANGED
@@ -10,4 +10,5 @@ group :development do
10
10
  gem 'async', '~> 1.24'
11
11
  gem 'activesupport'
12
12
  gem 'get_process_mem'
13
+ gem 'gc_tracer'
13
14
  end
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)
@@ -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
- STDOUT.puts("finalized Libvirt::Connection obj_id=0x#{obj_id.to_s(16)}, @conn_ptr=#{@conn_ptr}, @uri=#{@uri}, @cb_data=#{@cb_data}")
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 connect to #{@uri.inspect}" if @conn_ptr.null?
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
- if event_id == Libvirt::DOMAIN_EVENT_ID_LIFECYCLE
72
- cb = FFI::Domain::domain_event_id_lifecycle_callback(&block)
73
- else
74
- raise Error, "not supported event_id #{event_id.inspect}"
75
- end
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
- opaque&.to_ptr,
83
- nil # free_opaque
112
+ cb_data.pointer,
113
+ cb_data_free_func
84
114
  )
85
- raise Error, "Couldn't register domain event callback" if result < 0
115
+ if result < 0
116
+ cb_data.pointer.free
117
+ raise Error, "Couldn't register domain event callback"
118
+ end
86
119
 
87
- @cb_data[result] = { event_id: event_id, cb: cb, opaque: opaque }
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
- @cb_data.delete(callback_id)
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
- def version
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
@@ -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
- STDOUT.puts("finalized Libvirt::Domain obj_id=0x#{obj_id.to_s(16)}, @dom_ptr=#{@dom_ptr},")
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
@@ -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
- def self.domain_event_id_lifecycle_callback(&block)
128
- ::FFI::Function.new(:int, [:pointer, :pointer, :int, :int]) do |_conn, dom, event, detail, op|
129
- Util.log(:debug) { "DOMAIN_EVENT_CALLBACK LIFECYCLE dom=#{dom}, event=#{event}, detail=#{detail}, op=#{op}" }
130
- block.call(dom, event, detail, op)
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
@@ -12,15 +12,15 @@ module Libvirt
12
12
  @opaque = nil
13
13
 
14
14
  free = ->(obj_id) do
15
- STDOUT.puts("finalized Libvirt::Stream obj_id=0x#{obj_id.to_s(16)}, @stream_ptr=#{@stream_ptr}, @cb=#{@cb},")
16
- if @stream_ptr && @cb
17
- rm = FFI::Stream.virStreamEventRemoveCallback(@stream_ptr)
18
- STDOUT.puts("finalized Libvirt::Stream obj_id=0x#{obj_id.to_s(16)} rm=#{rm}")
19
- ab = FFI::Stream.virStreamAbort(@stream_ptr)
20
- STDOUT.puts("finalized Libvirt::Stream obj_id=0x#{obj_id.to_s(16)} ab=#{ab}")
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
- fr = FFI::Stream.virStreamFree(@stream_ptr) if @stream_ptr
23
- STDOUT.puts("finalized Libvirt::Stream obj_id=0x#{obj_id.to_s(16)} fr=#{fr}")
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Libvirt
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
@@ -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
- c.register_domain_event_callback(Libvirt::DOMAIN_EVENT_ID_LIFECYCLE, nil) do |dom, event, detail, opaque|
51
- Libvirt.logger.info { "DOMAIN_EVENT_ID_LIFECYCLE user dom=#{dom}, event=#{event}, detail=#{detail}, opaque=#{opaque}" }
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
- # ASYNC_REACTOR.every(5) do
79
- # Libvirt.logger.info { "MEM USAGE: #{GetProcessMem.new.mb} MB" }
80
- # Libvirt.logger.info { "GC.start" }
81
- # GC.start
82
- # Libvirt.logger.info { "MEM USAGE: #{GetProcessMem.new.mb} MB" }
83
- # end
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.3.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-28 00:00:00.000000000 Z
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