libvirt_ffi 0.6.1 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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