libvirt_ffi 0.6.1 → 0.8.1

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.
@@ -1,536 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module LibvirtAsync
4
- class << self
5
- attr_writer :logger
6
-
7
- attr_reader :logger
8
- end
9
-
10
- module WithDbg
11
- extend ActiveSupport::Concern
12
-
13
- class_methods do
14
- def dbg(progname = nil, &block)
15
- LibvirtAsync.logger&.debug(progname || "#{name}.:0x#{object_id.to_s(16)}", &block)
16
- end
17
- end
18
-
19
- private
20
-
21
- def dbg(progname = nil, &block)
22
- LibvirtAsync.logger&.debug(progname || "#{self.class}#:0x#{object_id.to_s(16)}", &block)
23
- end
24
- end
25
-
26
- module Util
27
- def self.create_task(parent = nil, reactor = nil, &block)
28
- parent = Async::Task.current? if parent == :current
29
- reactor ||= Async::Task.current.reactor
30
- Async::Task.new(reactor, parent, &block)
31
- end
32
- end
33
-
34
- class Handle
35
- # Represents an event handle (usually a file descriptor). When an event
36
- # happens to the handle, we dispatch the event to libvirt via
37
- # Libvirt::event_invoke_handle_callback (feeding it the handle_id we returned
38
- # from add_handle, the file descriptor, the new events, and the opaque
39
- # data that libvirt gave us earlier).
40
-
41
- class Monitor < Async::Wrapper
42
- def close
43
- cancel_monitor
44
- end
45
-
46
- def readiness
47
- monitor&.readiness
48
- end
49
-
50
- def to_s
51
- "#<#{self.class}:0x#{object_id.to_s(16)} readable=#{@readable&.object_id&.to_s(16)} writable=#{@writable&.object_id&.to_s(16)} alive=#{@monitor && !@monitor.closed?}>"
52
- end
53
-
54
- def inspect
55
- to_s
56
- end
57
- end
58
-
59
- include WithDbg
60
-
61
- attr_reader :handle_id, :fd, :opaque, :monitor
62
- attr_accessor :events
63
-
64
- def initialize(handle_id, fd, events, opaque)
65
- dbg { "#{self.class}#initialize handle_id=#{handle_id}, fd=#{fd}, events=#{events}" }
66
-
67
- @handle_id = handle_id
68
- @fd = fd
69
- @events = events
70
- @opaque = opaque
71
- @monitor = nil
72
- end
73
-
74
- def register
75
- dbg { "#{self.class}#register handle_id=#{handle_id}, fd=#{fd}" }
76
-
77
- dbg { "#{self.class}#register skip EVENT_HANDLE_ERROR handle_id=#{handle_id}, fd=#{fd}" } if (events & Libvirt::EVENT_HANDLE_ERROR) != 0
78
- dbg { "#{self.class}#register skip EVENT_HANDLE_HANGUP handle_id=#{handle_id}, fd=#{fd}" } if (events & Libvirt::EVENT_HANDLE_HANGUP) != 0
79
-
80
- interest = events_to_interest(events)
81
- dbg { "#{self.class}#register parse handle_id=#{handle_id}, fd=#{fd}, events=#{events}, interest=#{interest}" }
82
-
83
- if interest.nil?
84
- dbg { "#{self.class}#register no interest handle_id=#{handle_id}, fd=#{fd}" }
85
- return
86
- end
87
-
88
- task = Util.create_task do
89
- dbg { "#{self.class}#register_handle Async start handle_id=#{handle_id}, fd=#{fd}" }
90
- io_mode = interest_to_io_mode(interest)
91
-
92
- io = IO.new(fd, io_mode, autoclose: false)
93
- @monitor = Monitor.new(io)
94
-
95
- while @monitor.readiness.nil?
96
- cancelled = wait_io(interest)
97
-
98
- if cancelled
99
- dbg { "#{self.class}#register_handle async cancel handle_id=#{handle_id}, fd=#{fd}" }
100
- break
101
- end
102
-
103
- dbg { "#{self.class}#register_handle async resumes readiness=#{@monitor.readiness}, handle_id=#{handle_id}, fd=#{fd}" }
104
- events = readiness_to_events(@monitor.readiness)
105
-
106
- unless events.nil?
107
- dispatch(events)
108
- break
109
- end
110
-
111
- dbg { "#{self.class}#register_handle async not ready readiness=#{@monitor.readiness}, handle_id=#{handle_id}, fd=#{fd}" }
112
- end
113
- end
114
-
115
- dbg { "#{self.class}#register_handle invokes fiber=0x#{task.fiber.object_id.to_s(16)} handle_id=#{handle_id}, fd=#{fd}" }
116
- task.run
117
- dbg { "#{self.class}#register_handle ends handle_id=#{handle_id}, fd=#{fd}" }
118
- end
119
-
120
- def unregister
121
- dbg { "#{self.class}#unregister handle_id=#{handle_id}, fd=#{fd}" }
122
-
123
- if @monitor.nil?
124
- dbg { "#{self.class}#unregister already unregistered handle_id=#{handle_id}, fd=#{fd}" }
125
- return
126
- end
127
-
128
- @monitor.close
129
- @monitor = nil
130
- end
131
-
132
- def to_s
133
- "#<#{self.class}:0x#{object_id.to_s(16)} handle_id=#{handle_id} fd=#{fd} events=#{events} monitor=#{monitor}>"
134
- end
135
-
136
- def inspect
137
- to_s
138
- end
139
-
140
- private
141
-
142
- def dispatch(events)
143
- dbg { "#{self.class}#dispatch starts handle_id=#{handle_id}, events=#{events}, fd=#{fd}" }
144
-
145
- task = Util.create_task do
146
- dbg { "#{self.class}#dispatch async starts handle_id=#{handle_id} events=#{events}, fd=#{fd}" }
147
- # Libvirt::event_invoke_handle_callback(handle_id, fd, events, opaque)
148
- # opaque.call_cb(handle_id, fd, events)
149
- Libvirt::Event.invoke_handle_callback(handle_id, fd, events, opaque)
150
- dbg { "#{self.class}#dispatch async ends handle_id=#{handle_id} received_events=#{events}, fd=#{fd}" }
151
- end
152
- # dbg { "#{self.class}#dispatch invokes fiber=0x#{task.fiber.object_id.to_s(16)} handle_id=#{handle_id}, events=#{events}, fd=#{fd}" }
153
- # task.run
154
- # dbg { "#{self.class}#dispatch ends handle_id=#{handle_id}, events=#{events}, fd=#{fd}" }
155
- dbg { "#{self.class}#dispatch schedules fiber=0x#{task.fiber.object_id.to_s(16)} handle_id=#{handle_id}, events=#{events}, fd=#{fd}" }
156
- task.reactor << task.fiber
157
- end
158
-
159
- def wait_io(interest)
160
- meth = interest_to_monitor_method(interest)
161
- begin
162
- @monitor.public_send(meth)
163
- false
164
- rescue Monitor::Cancelled => e
165
- dbg { "#{self.class}#wait_io cancelled #{e.class} #{e.message}" }
166
- true
167
- end
168
- end
169
-
170
- def interest_to_monitor_method(interest)
171
- case interest
172
- when :r
173
- :wait_readable
174
- when :w
175
- :wait_writable
176
- when :rw
177
- :wait_any
178
- else
179
- raise ArgumentError, "invalid interest #{interest}"
180
- end
181
- end
182
-
183
- def events_to_interest(events)
184
- readable = (events & Libvirt::EVENT_HANDLE_READABLE) != 0
185
- writable = (events & Libvirt::EVENT_HANDLE_WRITABLE) != 0
186
- if readable && writable
187
- :rw
188
- elsif readable
189
- :r
190
- elsif writable
191
- :w
192
- end
193
- end
194
-
195
- def interest_to_io_mode(interest)
196
- case interest
197
- when :rw
198
- 'a+'
199
- when :r
200
- 'r'
201
- when :w
202
- 'w'
203
- else
204
- raise ArgumentError, "invalid interest #{interest}"
205
- end
206
- end
207
-
208
- def readiness_to_events(readiness)
209
- case readiness&.to_sym
210
- when :rw
211
- Libvirt::EVENT_HANDLE_READABLE | Libvirt::EVENT_HANDLE_WRITABLE
212
- when :r
213
- Libvirt::EVENT_HANDLE_READABLE
214
- when :w
215
- Libvirt::EVENT_HANDLE_WRITABLE
216
- end
217
- end
218
- end
219
-
220
- class Timer
221
- # Represents a When a timer expires, we dispatch the event to
222
- # libvirt via Libvirt::event_invoke_timeout_callback (feeding it the timer_id
223
- # we returned from add_timer and the opaque data that libvirt gave us
224
- # earlier).
225
-
226
- class Monitor
227
- class Cancelled < StandardError
228
- def initialize
229
- super('was cancelled')
230
- end
231
- end
232
-
233
- attr_reader :fiber
234
-
235
- def initialize
236
- @fiber = nil
237
- end
238
-
239
- def wait(timeout)
240
- @fiber = Async::Task.current.fiber
241
- Async::Task.current.sleep(timeout)
242
- @fiber = nil
243
- end
244
-
245
- def close
246
- @fiber.resume(Cancelled.new) if @fiber&.alive?
247
- @fiber = nil
248
- end
249
-
250
- def to_s
251
- "#<#{self.class}:0x#{object_id.to_s(16)} fiber=#{@fiber&.object_id&.to_s(16)} alive=#{@fiber&.alive?}>"
252
- end
253
-
254
- def inspect
255
- to_s
256
- end
257
- end
258
-
259
- include WithDbg
260
-
261
- attr_reader :timer_id, :opaque, :monitor
262
- attr_accessor :last_fired, :interval
263
-
264
- def initialize(timer_id, interval, opaque)
265
- dbg { "#{self.class}#initialize timer_id=#{timer_id}, interval=#{interval}" }
266
-
267
- @timer_id = timer_id
268
- @interval = interval / 1000.0
269
- @opaque = opaque
270
- @last_fired = Time.now.to_f
271
- @monitor = nil
272
- end
273
-
274
- def wait_time
275
- return if interval.negative?
276
-
277
- last_fired + interval
278
- end
279
-
280
- def register
281
- dbg { "#{self.class}#register starts timer_id=#{timer_id}, interval=#{interval}" }
282
-
283
- if wait_time.nil?
284
- dbg { "#{self.class}#register no wait time timer_id=#{timer_id}, interval=#{interval}" }
285
- return
286
- end
287
-
288
- task = Util.create_task do
289
- dbg { "#{self.class}#register async starts timer_id=#{timer_id}, interval=#{interval}" }
290
- now_time = Time.now.to_f
291
- timeout = wait_time > now_time ? wait_time - now_time : 0
292
- @monitor = Monitor.new
293
- cancelled = wait_timer(timeout)
294
-
295
- if cancelled
296
- dbg { "#{self.class}#register async cancel timer_id=#{timer_id}, interval=#{interval}" }
297
- else
298
- dbg { "#{self.class}#register async ready timer_id=#{timer_id}, interval=#{interval}" }
299
- self.last_fired = Time.now.to_f
300
- dispatch
301
- end
302
- end
303
-
304
- dbg { "#{self.class}#register invokes fiber=0x#{task.fiber.object_id.to_s(16)} timer_id=#{timer_id}, interval=#{interval}" }
305
- task.run
306
- dbg { "#{self.class}#register ends timer_id=#{timer_id}, interval=#{interval}" }
307
- end
308
-
309
- def unregister
310
- dbg { "#{self.class}#unregister_timer timer_id=#{timer_id}, interval=#{interval}" }
311
-
312
- if @monitor.nil?
313
- dbg { "#{self.class}#unregister_timer already unregistered timer_id=#{timer_id}, interval=#{interval}" }
314
- return
315
- end
316
-
317
- @monitor.close
318
- @monitor = nil
319
- end
320
-
321
- def to_s
322
- "#<#{self.class}:0x#{object_id.to_s(16)} timer_id=#{timer_id} interval=#{interval} last_fired=#{last_fired} monitor=#{monitor}>"
323
- end
324
-
325
- def inspect
326
- to_s
327
- end
328
-
329
- private
330
-
331
- def dispatch
332
- dbg { "#{self.class}#dispatch starts timer_id=#{timer_id}, interval=#{interval}" }
333
-
334
- task = Util.create_task do
335
- dbg { "#{self.class}#dispatch async starts timer_id=#{timer_id}, interval=#{interval}" }
336
- # Libvirt::event_invoke_timeout_callback(timer_id, opaque)
337
- # opaque.call_cb(timer_id)
338
- Libvirt::Event.invoke_timeout_callback(timer_id, opaque)
339
- dbg { "#{self.class}#dispatch async async ends timer_id=#{timer_id}, interval=#{interval}" }
340
- end
341
-
342
- # dbg { "#{self.class}#dispatch invokes fiber=0x#{task.fiber.object_id.to_s(16)} timer_id=#{timer_id}, interval=#{interval}" }
343
- # task.run
344
- # dbg { "#{self.class}#dispatch ends timer_id=#{timer_id}, interval=#{interval}" }
345
- dbg { "#{self.class}#dispatch schedules fiber=0x#{task.fiber.object_id.to_s(16)} timer_id=#{timer_id}, interval=#{interval}" }
346
- task.reactor << task.fiber
347
- end
348
-
349
- def wait_timer(timeout)
350
- @monitor.wait(timeout)
351
- false
352
- rescue Monitor::Cancelled => e
353
- dbg { "#{self.class}#wait_timer cancelled #{e.class} #{e.message}" }
354
- true
355
- end
356
- end
357
-
358
- class Implementations
359
- include WithDbg
360
-
361
- def initialize
362
- dbg { "#{self.class}#initialize" }
363
-
364
- default_variables
365
- end
366
-
367
- def start
368
- dbg { "#{self.class}#start" }
369
-
370
- register_implementations
371
- end
372
-
373
- def stop
374
- dbg { "#{self.class}#stop" }
375
-
376
- @handles.each(&:unregister)
377
- @timers.each(&:unregister)
378
-
379
- default_variables
380
- end
381
-
382
- def print_debug_info
383
- str = [
384
- "#{self.class}:0x#{object_id.to_s(16)}",
385
- 'handles = [',
386
- @handles.map(&:to_s).join("\n"),
387
- ']',
388
- 'timers = [',
389
- @timers.map(&:to_s).join("\n"),
390
- ']'
391
- ].join("\n")
392
- Libvirt.logger&.debug { str }
393
- end
394
-
395
- def to_s
396
- "#<#{self.class}:0x#{object_id.to_s(16)} handles=#{@handles} timers=#{@timers}>"
397
- end
398
-
399
- def inspect
400
- to_s
401
- end
402
-
403
- private
404
-
405
- def default_variables
406
- @next_handle_id = 1
407
- @next_timer_id = 1
408
- @handles = []
409
- @timers = []
410
- end
411
-
412
- def register_implementations
413
- dbg { "#{self.class}#register_implementations" }
414
-
415
- Libvirt::Event.register(
416
- add_handle: method(:add_handle).to_proc,
417
- update_handle: method(:update_handle).to_proc,
418
- remove_handle: method(:remove_handle).to_proc,
419
- add_timer: method(:add_timer).to_proc,
420
- update_timer: method(:update_timer).to_proc,
421
- remove_timer: method(:remove_timer).to_proc,
422
- schedule: method(:schedule).to_proc
423
- )
424
- end
425
-
426
- def schedule(&block)
427
- task = Async::Task.new(Async::Task.current.reactor, nil, &block)
428
- task.reactor << task.fiber
429
- end
430
-
431
- def add_handle(fd, events, opaque)
432
- # add a handle to be tracked by this object. The application is
433
- # expected to maintain a list of internal handle IDs (integers); this
434
- # callback *must* return the current handle_id. This handle_id is used
435
- # both by libvirt to identify the handle (during an update or remove
436
- # callback), and is also passed by the application into libvirt when
437
- # dispatching an event. The application *must* also store the opaque
438
- # data given by libvirt, and return it back to libvirt later
439
- # (see remove_handle)
440
- dbg { "#{self.class}#add_handle starts fd=#{fd}, events=#{events}" }
441
-
442
- @next_handle_id += 1
443
- handle_id = @next_handle_id
444
- handle = LibvirtAsync::Handle.new(handle_id, fd, events, opaque)
445
- @handles << handle
446
- handle.register
447
-
448
- dbg { "#{self.class}#add_handle ends fd=#{fd}, events=#{events}" }
449
- handle_id
450
- end
451
-
452
- def update_handle(handle_id, events)
453
- # update a previously registered handle. Libvirt tells us the handle_id
454
- # (which was returned to libvirt via add_handle), and the new events. It
455
- # is our responsibility to find the correct handle and update the events
456
- # it cares about
457
- dbg { "#{self.class}#update_handle starts handle_id=#{handle_id}, events=#{events}" }
458
-
459
- handle = @handles.detect { |h| h.handle_id == handle_id }
460
- handle.events = events
461
- handle.unregister
462
- handle.register
463
-
464
- dbg { "#{self.class}#update_handle ends handle_id=#{handle_id}, events=#{events}" }
465
- nil
466
- end
467
-
468
- def remove_handle(handle_id)
469
- # remove a previously registered handle. Libvirt tells us the handle_id
470
- # (which was returned to libvirt via add_handle), and it is our
471
- # responsibility to "forget" the handle. We must return the opaque data
472
- # that libvirt handed us in "add_handle", otherwise we will leak memory
473
- dbg { "#{self.class}#remove_handle starts handle_id=#{handle_id}" }
474
-
475
- idx = @handles.index { |h| h.handle_id == handle_id }
476
- handle = @handles.delete_at(idx)
477
- handle.unregister
478
-
479
- dbg { "#{self.class}#remove_handle starts handle_id=#{handle_id}" }
480
- handle.opaque
481
- end
482
-
483
- def add_timer(interval, opaque)
484
- # add a timeout to be tracked by this object. The application is
485
- # expected to maintain a list of internal timer IDs (integers); this
486
- # callback *must* return the current timer_id. This timer_id is used
487
- # both by libvirt to identify the timeout (during an update or remove
488
- # callback), and is also passed by the application into libvirt when
489
- # dispatching an event. The application *must* also store the opaque
490
- # data given by libvirt, and return it back to libvirt later
491
- # (see remove_timer)
492
- dbg { "#{self.class}#add_timer starts interval=#{interval}" }
493
-
494
- @next_timer_id += 1
495
- timer_id = @next_timer_id
496
- timer = LibvirtAsync::Timer.new(timer_id, interval, opaque)
497
- @timers << timer
498
- timer.register
499
-
500
- dbg { "#{self.class}#add_timer ends interval=#{interval}" }
501
- timer_id
502
- end
503
-
504
- def update_timer(timer_id, interval)
505
- # update a previously registered timer. Libvirt tells us the timer_id
506
- # (which was returned to libvirt via add_timer), and the new interval. It
507
- # is our responsibility to find the correct timer and update the timers
508
- # it cares about
509
- dbg { "#{self.class}#update_timer starts timer_id=#{timer_id}, interval=#{interval}" }
510
-
511
- timer = @timers.detect { |t| t.timer_id == timer_id }
512
- dbg { "#{self.class}#update_timer updating timer_id=#{timer.timer_id}" }
513
- timer.interval = interval
514
- timer.unregister
515
- timer.register
516
-
517
- dbg { "#{self.class}#update_timer ends timer_id=#{timer_id}, interval=#{interval}" }
518
- nil
519
- end
520
-
521
- def remove_timer(timer_id)
522
- # remove a previously registered timeout. Libvirt tells us the timer_id
523
- # (which was returned to libvirt via add_timer), and it is our
524
- # responsibility to "forget" the timer. We must return the opaque data
525
- # that libvirt handed us in "add_timer", otherwise we will leak memory
526
- dbg { "#{self.class}#remove_timer starts timer_id=#{timer_id}" }
527
-
528
- idx = @timers.index { |t| t.timer_id == timer_id }
529
- timer = @timers.delete_at(idx)
530
- timer.unregister
531
-
532
- dbg { "#{self.class}#remove_timer ends timer_id=#{timer_id}" }
533
- timer.opaque
534
- end
535
- end
536
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class LogFormatter
4
- LOG_FORMAT = "%s, %s [%s/%s/%s] %s\n"
5
- DEFAULT_DATETIME_FORMAT = '%F %T.%N'
6
-
7
- attr_accessor :datetime_format
8
-
9
- def initialize
10
- @datetime_format = nil
11
- end
12
-
13
- def call(severity, time, progname, message)
14
- format(LOG_FORMAT, severity[0..0], format_datetime(time), "0x#{Async::Task.current?&.object_id&.to_s(16)}", "0x#{Fiber.current.object_id.to_s(16)}", progname, format_message(message))
15
- end
16
-
17
- private
18
-
19
- def format_datetime(time)
20
- time.strftime(@datetime_format || DEFAULT_DATETIME_FORMAT)
21
- end
22
-
23
- def format_message(message)
24
- case message
25
- when ::String
26
- message
27
- when ::Exception
28
- "<#{message.class}>:#{message.message}\n#{(message.backtrace || []).join("\n")}"
29
- else
30
- message.inspect
31
- end
32
- end
33
- end
@@ -1,43 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'libvirt'
6
- require 'logger'
7
- require 'active_support/all'
8
- require 'async'
9
-
10
- require_relative 'support/libvirt_async'
11
- require_relative 'support/log_formatter'
12
-
13
- require 'libvirt/xml'
14
-
15
- Libvirt.logger = Logger.new(STDOUT, formatter: LogFormatter.new)
16
- Libvirt.logger.level = ENV['DEBUG'] ? :debug : :info
17
-
18
- IMPL = LibvirtAsync::Implementations.new
19
-
20
- Async do
21
- ASYNC_REACTOR = Async::Task.current.reactor
22
-
23
- puts "Lib version #{Libvirt.lib_version}"
24
- puts "Gem version #{Libvirt::VERSION}"
25
-
26
- IMPL.start
27
-
28
- conn = Libvirt::Connection.new('qemu+tcp://localhost:16510/system')
29
- conn.open
30
-
31
- puts "Connection version #{conn.version.inspect}"
32
- puts "Connection lib_version #{conn.lib_version.inspect}"
33
- puts "Connection hostname #{conn.hostname.inspect}"
34
-
35
- doms = conn.list_all_domains
36
- puts "Connection domains qty #{doms.size}"
37
-
38
- doms.each.with_index do |dom, i|
39
- puts "Domain #{i} xml", dom.xml_desc
40
- puts "Domain #{i} xml object", Libvirt::Xml::Domain.load(dom.xml_desc).to_h
41
- end
42
-
43
- end