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 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