libvirt_ffi 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +78 -0
  3. data/Gemfile +7 -3
  4. data/Rakefile +6 -1
  5. data/bin/console +1 -0
  6. data/exe/libvirt +1 -0
  7. data/lib/libvirt/base_info.rb +34 -0
  8. data/lib/libvirt/connection.rb +98 -36
  9. data/lib/libvirt/domain.rb +105 -8
  10. data/lib/libvirt/domain_callback_storage.rb +13 -15
  11. data/lib/libvirt/errors.rb +65 -0
  12. data/lib/libvirt/event.rb +29 -19
  13. data/lib/libvirt/ffi/common.rb +8 -1
  14. data/lib/libvirt/ffi/domain.rb +529 -196
  15. data/lib/libvirt/ffi/error.rb +243 -0
  16. data/lib/libvirt/ffi/event.rb +30 -36
  17. data/lib/libvirt/ffi/helpers.rb +17 -0
  18. data/lib/libvirt/ffi/host.rb +122 -0
  19. data/lib/libvirt/ffi/storage.rb +149 -0
  20. data/lib/libvirt/ffi/stream.rb +19 -17
  21. data/lib/libvirt/ffi.rb +17 -0
  22. data/lib/libvirt/node_info.rb +2 -41
  23. data/lib/libvirt/storage_pool.rb +70 -0
  24. data/lib/libvirt/storage_pool_info.rb +7 -0
  25. data/lib/libvirt/storage_volume.rb +51 -0
  26. data/lib/libvirt/storage_volume_info.rb +7 -0
  27. data/lib/libvirt/stream.rb +21 -14
  28. data/lib/libvirt/util.rb +61 -8
  29. data/lib/libvirt/version.rb +1 -1
  30. data/lib/libvirt/xml/disk.rb +59 -0
  31. data/lib/libvirt/xml/domain.rb +76 -0
  32. data/lib/libvirt/xml/generic.rb +252 -0
  33. data/lib/libvirt/xml/graphics.rb +14 -0
  34. data/lib/libvirt/xml/max_vcpu.rb +12 -0
  35. data/lib/libvirt/xml/memory.rb +14 -0
  36. data/lib/libvirt/xml/storage_pool.rb +24 -0
  37. data/lib/libvirt/xml/storage_volume.rb +32 -0
  38. data/lib/libvirt/xml/vcpu.rb +12 -0
  39. data/lib/libvirt/xml.rb +23 -0
  40. data/lib/libvirt.rb +12 -12
  41. data/lib/libvirt_ffi.rb +2 -0
  42. data/libvirt.gemspec +5 -1
  43. data/test_usage/support/libvirt_async.rb +27 -35
  44. data/test_usage/support/log_formatter.rb +5 -10
  45. data/test_usage/test_domain.rb +43 -0
  46. data/test_usage/test_event_loop.rb +115 -39
  47. data/test_usage/test_libvirtd_restart.rb +63 -0
  48. data/test_usage/test_metadata.rb +104 -0
  49. data/test_usage/test_screenshot.rb +14 -13
  50. data/test_usage/test_storage.rb +52 -0
  51. metadata +42 -6
  52. data/lib/libvirt/error.rb +0 -6
  53. data/lib/libvirt/ffi/connection.rb +0 -94
  54. data/lib/libvirt/ffi/libvirt.rb +0 -17
  55. data/lib/libvirt/ffi/node_info.rb +0 -37
@@ -0,0 +1,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Libvirt
4
+ module Xml
5
+ class Generic
6
+ class_attribute :_root_path, instance_writer: false, default: '.'
7
+ class_attribute :_attributes_opts, instance_writer: false, default: {}
8
+
9
+ def self.inherited(subclass)
10
+ subclass._root_path = '.'
11
+ subclass._attributes_opts = _attributes_opts.dup
12
+ end
13
+
14
+ def self.root_path(path)
15
+ self._root_path = path
16
+ end
17
+
18
+ def self.attributes(*names)
19
+ options = names.extract_options!
20
+ names.each do |name|
21
+ _attributes_opts.merge!(name.to_sym => options.dup)
22
+ end
23
+ attr_accessor(*names)
24
+ end
25
+
26
+ def self.attribute(name, options = {})
27
+ _attributes_opts.merge!(name.to_sym => options.dup)
28
+ attr_accessor name
29
+ end
30
+
31
+ # @param xml [String]
32
+ # @return [Class<LibvirtXml::Generic>]
33
+ def self.load(xml)
34
+ xml_node = Nokogiri::XML(xml).xpath(_root_path).first
35
+ new(xml_node)
36
+ end
37
+
38
+ # Build xml object with attributes.
39
+ # @param attrs [Hash]
40
+ # @return [Xml::Base]
41
+ def self.build(attrs = {})
42
+ xml_node = Nokogiri::XML(nil)
43
+ obj = new(xml_node)
44
+ attrs.each { |key, val| obj.public_send("#{key}=", val) }
45
+ obj
46
+ end
47
+
48
+ # @param xml_node [Nokogiri::XML::Element]
49
+ def initialize(xml_node)
50
+ @xml_node = xml_node
51
+ parse_xml_node
52
+ end
53
+
54
+ # @param attr [Symbol,String]
55
+ # @return [Object,nil]
56
+ def [](attr)
57
+ read_attribute(attr)
58
+ end
59
+
60
+ # @param attr [Symbol,String]
61
+ # @param value [Object,nil]
62
+ # @return [Object,nil]
63
+ def []=(attr, value)
64
+ write_attribute(attr, value)
65
+ end
66
+
67
+ # @return [Hash{Symbol=>(Object,nil)}]
68
+ def to_h
69
+ _attributes_opts.map do |name, _opts|
70
+ value = public_send(name)
71
+ [name, serialize_for_hash(value)]
72
+ end.to_h
73
+ end
74
+
75
+ # @return [String]
76
+ def to_xml
77
+ @xml_node.to_xml
78
+ end
79
+
80
+ delegate :as_json, :to_json, to: :to_h
81
+
82
+ private
83
+
84
+ def parse_xml_node
85
+ _attributes_opts.each do |name, opts|
86
+ value = parse_node(name, opts)
87
+ value = decode(value, opts)
88
+ write_attribute name, value
89
+ end
90
+ end
91
+
92
+ # Parse node value using "parse_node_#{type}" method.
93
+ # @param name [Symbol]
94
+ # @param opts [Hash{Symbol=>Object}]
95
+ # @return [Object, nil]
96
+ def parse_node(name, opts)
97
+ type = opts[:type] || :text
98
+ meth = "parse_node_#{type}"
99
+
100
+ if opts[:apply]
101
+ opts[:apply].call(@xml_node, opts)
102
+ elsif respond_to?(meth, true)
103
+ send(meth, name, opts)
104
+ else
105
+ raise ArgumentError, "Invalid :type option #{type.inspect} for attribute #{name}"
106
+ end
107
+ end
108
+
109
+ # Cast value using "decode_#{type}" method.
110
+ # @param value [String]
111
+ # @param opts [Hash{Symbol=>Object}]
112
+ # @return [Object, nil]
113
+ def decode(value, opts)
114
+ return if value.nil?
115
+
116
+ cast = opts[:cast]
117
+ return value if cast.nil?
118
+
119
+ meth = "decode_#{cast}"
120
+
121
+ if opts[:array]
122
+ value.map do |val|
123
+ if cast.is_a?(Proc)
124
+ cast.call(val, opts)
125
+ elsif respond_to?(meth, true)
126
+ send(meth, val, opts)
127
+ else
128
+ raise ArgumentError, "invalid :cast option #{cast.inspect}"
129
+ end
130
+ end
131
+ end
132
+
133
+ if cast.is_a?(Proc)
134
+ cast.call(value, opts)
135
+ elsif respond_to?(meth, true)
136
+ send(meth, value, opts)
137
+ else
138
+ raise ArgumentError, "invalid :cast option #{cast.inspect}"
139
+ end
140
+ end
141
+
142
+ # @param value [String, Boolean]
143
+ # @return [Boolean]
144
+ def decode_bool(value, _opts)
145
+ return value if value.is_a?(TrueClass) || value.is_a?(FalseClass)
146
+
147
+ return true if value == 'yes'
148
+
149
+ return false if value == 'no'
150
+
151
+ nil
152
+ end
153
+
154
+ # @param value [String, Integer]
155
+ # @return [Integer]
156
+ # @raise [ArgumentError]
157
+ def decode_int(value, _opts)
158
+ Integer(value)
159
+ end
160
+
161
+ def find_nodes(name, opts)
162
+ value_name = opts[:name]&.to_sym || name
163
+ path = opts[:path] || "./#{value_name}"
164
+ path == :root ? [@xml_node] : @xml_node.xpath(path)
165
+ end
166
+
167
+ def parse_node_text(name, opts)
168
+ nodes = find_nodes(name, opts)
169
+
170
+ nodes.map(&:text) if opts[:array]
171
+
172
+ node = nodes.first
173
+ return if node.nil?
174
+
175
+ node.text
176
+ end
177
+
178
+ def parse_node_attr(name, opts)
179
+ nodes = find_nodes name, { path: :root }.merge(opts)
180
+ value_name = opts[:name]&.to_sym || name
181
+
182
+ nodes.map { |node| node[value_name.to_s] } if opts[:array]
183
+
184
+ node = nodes.first
185
+ return if node.nil?
186
+
187
+ node[value_name.to_s]
188
+ end
189
+
190
+ def parse_node_struct(name, opts)
191
+ klass = opts[:class]
192
+ raise ArgumentError, "Invalid :class option nil for attribute #{name}" if klass.nil?
193
+
194
+ nodes = find_nodes(name, opts)
195
+
196
+ nodes.map { |node| klass.new(node) } if opts[:array]
197
+
198
+ node = nodes.first
199
+ return if node.nil?
200
+
201
+ klass.new(node)
202
+ end
203
+
204
+ def parse_node_raw(name, opts)
205
+ nodes = find_nodes(name, opts)
206
+
207
+ nodes.map(&:to_xml) if opts[:array]
208
+
209
+ node = nodes.first
210
+ return if node.nil?
211
+
212
+ node.to_xml
213
+ end
214
+
215
+ def parse_node_memory(name, opts)
216
+ nodes = find_nodes(name, opts)
217
+
218
+ if opts[:array]
219
+ return [] if nodes.empty?
220
+
221
+ nodes.map { |node| Util.parse_memory node.text, node['unit'] }
222
+ end
223
+
224
+ node = nodes.first
225
+ return if node.nil?
226
+
227
+ Util.parse_memory node.text, node['unit']
228
+ end
229
+
230
+ def read_attribute(attr)
231
+ attr = attr.to_sym
232
+ raise ArgumentError, "can't find attribute #{attr}" unless _attributes_opts.key?(attr)
233
+
234
+ instance_variable_get :"@#{attr}"
235
+ end
236
+
237
+ def write_attribute(attr, value)
238
+ attr = attr.to_sym
239
+ raise ArgumentError, "can't find attribute #{attr}" unless _attributes_opts.key?(attr)
240
+
241
+ instance_variable_set :"@#{attr}", value
242
+ end
243
+
244
+ def serialize_for_hash(value)
245
+ return value.to_h if value.is_a?(Generic)
246
+ return value.map { |val| serialize_for_hash(val) } if value.is_a?(Array)
247
+
248
+ value
249
+ end
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Libvirt
4
+ module Xml
5
+ class Graphics < Generic
6
+ # https://libvirt.org/formatdomain.html#elementsGraphics
7
+
8
+ attribute :type, type: :attr
9
+ attribute :listen, type: :attr
10
+ attribute :port, type: :attr
11
+ attribute :auto_port, type: :attr, name: :autoport, cast: :bool
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Libvirt
4
+ module Xml
5
+ class MaxVcpu < Generic
6
+ attribute :value, path: :root, cast: :int
7
+ attribute :cpu_set, type: :attr, name: :cpuset
8
+ attribute :current, type: :attr, cast: :int
9
+ attribute :placement, type: :attr
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Libvirt
4
+ module Xml
5
+ class Memory < Generic
6
+ attribute :dump_core, type: :attr, name: :dumpCore
7
+ attribute :slots, type: :attr
8
+
9
+ attribute :bytes, apply: ->(node, _opts) do
10
+ Util.parse_memory node.text, node['unit']
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Libvirt
4
+ module Xml
5
+ class StoragePool < Generic
6
+ # https://libvirt.org/formatstorage.html
7
+
8
+ root_path './pool'
9
+
10
+ attribute :type, type: :attr
11
+ attribute :name
12
+ attribute :uuid
13
+ attribute :capacity, type: :memory
14
+ attribute :allocation, type: :memory
15
+ attribute :available, type: :memory
16
+ attribute :target_path, path: './target/path'
17
+ attribute :target_perm_mode, path: './target/permissions/mode'
18
+ attribute :target_perm_owner, path: './target/permissions/owner'
19
+ attribute :target_perm_group, path: './target/permissions/group'
20
+ attribute :target_perm_label, path: './target/permissions/label'
21
+ # TODO: source
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Libvirt
4
+ module Xml
5
+ class StorageVolume < Generic
6
+ # https://libvirt.org/formatstorage.html
7
+
8
+ root_path './volume'
9
+
10
+ attribute :type, type: :attr
11
+ attribute :name
12
+ attribute :key
13
+ attribute :allocation, type: :memory
14
+ attribute :capacity, type: :memory
15
+ attribute :physical, type: :memory
16
+ attribute :target_path, path: './target/path'
17
+ attribute :target_format, type: :attr, name: :type, path: './target/format'
18
+ attribute :target_perm_mode, path: './target/permissions/mode'
19
+ attribute :target_perm_owner, path: './target/permissions/owner'
20
+ attribute :target_perm_group, path: './target/permissions/group'
21
+ attribute :target_perm_label, path: './target/permissions/label'
22
+ attribute :timestamp_atime, path: './timestamp/atime'
23
+ attribute :timestamp_btime, path: './timestamp/btime'
24
+ attribute :timestamp_ctime, path: './timestamp/ctime'
25
+ attribute :timestamp_mtime, path: './timestamp/mtime'
26
+ attribute :compat
27
+ # TODO: target/encryption target/nocow target/features
28
+ # todo source
29
+ # todo backingStore
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Libvirt
4
+ module Xml
5
+ class Vcpu < Generic
6
+ attribute :id, type: :attr, cast: :int
7
+ attribute :enabled, type: :attr, cast: :bool
8
+ attribute :hot_pluggable, type: :attr, cast: :bool, name: :hotpluggable
9
+ attribute :order, type: :attr, cast: :int
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+ require 'libvirt/util'
5
+
6
+ module Libvirt
7
+ module Xml
8
+ # https://libvirt.org/format.html
9
+ # namespace for libvirt xml objects.
10
+ # does not loaded by default.
11
+ # requires nokogiri.
12
+
13
+ require 'libvirt/xml/generic'
14
+ require 'libvirt/xml/storage_pool'
15
+ require 'libvirt/xml/storage_volume'
16
+ require 'libvirt/xml/memory'
17
+ require 'libvirt/xml/graphics'
18
+ require 'libvirt/xml/disk'
19
+ require 'libvirt/xml/max_vcpu'
20
+ require 'libvirt/xml/vcpu'
21
+ require 'libvirt/xml/domain'
22
+ end
23
+ end
data/lib/libvirt.rb CHANGED
@@ -4,19 +4,18 @@ require 'ffi'
4
4
  require 'objspace'
5
5
  require 'libvirt/domain_callback_storage'
6
6
  require 'libvirt/util'
7
- require 'libvirt/error'
8
- require 'libvirt/ffi/common'
9
- require 'libvirt/ffi/libvirt'
10
- require 'libvirt/ffi/connection'
11
- require 'libvirt/ffi/domain'
12
- require 'libvirt/ffi/event'
13
- require 'libvirt/ffi/node_info'
14
- require 'libvirt/ffi/stream'
7
+ require 'libvirt/errors'
8
+ require 'libvirt/ffi'
9
+ require 'libvirt/base_info'
10
+ require 'libvirt/node_info'
11
+ require 'libvirt/storage_pool_info'
12
+ require 'libvirt/storage_volume_info'
15
13
  require 'libvirt/event'
16
14
  require 'libvirt/connection'
17
15
  require 'libvirt/domain'
18
- require 'libvirt/node_info'
19
16
  require 'libvirt/stream'
17
+ require 'libvirt/storage_pool'
18
+ require 'libvirt/storage_volume'
20
19
  require 'libvirt/version'
21
20
 
22
21
  module Libvirt
@@ -28,10 +27,11 @@ module Libvirt
28
27
  class << self
29
28
  def lib_version
30
29
  version_ptr = ::FFI::MemoryPointer.new(:ulong)
31
- code = FFI::Libvirt.virGetVersion(version_ptr, nil, nil)
32
- raise Error, 'failed to get version' if code < 0
30
+ code = FFI::Host.virGetVersion(version_ptr, nil, nil)
31
+ raise Errors::LibError, 'failed to get version' if code.negative?
32
+
33
33
  version_number = version_ptr.get_ulong(0)
34
- Libvirt::Util.parse_version(version_number)
34
+ Util.parse_version(version_number)
35
35
  end
36
36
 
37
37
  def logger
data/lib/libvirt_ffi.rb CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'libvirt'
data/libvirt.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'lib/libvirt/version'
2
4
 
3
5
  Gem::Specification.new do |spec|
@@ -17,7 +19,7 @@ Gem::Specification.new do |spec|
17
19
 
18
20
  # Specify which files should be added to the gem when it is released.
19
21
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
21
23
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
24
  end
23
25
  spec.bindir = 'exe'
@@ -25,4 +27,6 @@ Gem::Specification.new do |spec|
25
27
  spec.require_paths = ['lib']
26
28
 
27
29
  spec.add_dependency 'ffi', '>= 1.0'
30
+
31
+ spec.add_development_dependency 'rubocop', '~> 0.80.1'
28
32
  end
@@ -1,12 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module LibvirtAsync
2
4
  class << self
3
- def logger=(logger)
4
- @logger = logger
5
- end
5
+ attr_writer :logger
6
6
 
7
- def logger
8
- @logger
9
- end
7
+ attr_reader :logger
10
8
  end
11
9
 
12
10
  module WithDbg
@@ -26,12 +24,11 @@ module LibvirtAsync
26
24
  end
27
25
 
28
26
  module Util
29
- def create_task(parent = nil, reactor = nil, &block)
27
+ def self.create_task(parent = nil, reactor = nil, &block)
30
28
  parent = Async::Task.current? if parent == :current
31
29
  reactor ||= Async::Task.current.reactor
32
30
  Async::Task.new(reactor, parent, &block)
33
31
  end
34
- module_function :create_task
35
32
  end
36
33
 
37
34
  class Handle
@@ -77,12 +74,8 @@ module LibvirtAsync
77
74
  def register
78
75
  dbg { "#{self.class}#register handle_id=#{handle_id}, fd=#{fd}" }
79
76
 
80
- if (events & Libvirt::EVENT_HANDLE_ERROR) != 0
81
- dbg { "#{self.class}#register skip EVENT_HANDLE_ERROR handle_id=#{handle_id}, fd=#{fd}" }
82
- end
83
- if (events & Libvirt::EVENT_HANDLE_HANGUP) != 0
84
- dbg { "#{self.class}#register skip EVENT_HANDLE_HANGUP handle_id=#{handle_id}, fd=#{fd}" }
85
- end
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
86
79
 
87
80
  interest = events_to_interest(events)
88
81
  dbg { "#{self.class}#register parse handle_id=#{handle_id}, fd=#{fd}, events=#{events}, interest=#{interest}" }
@@ -99,7 +92,7 @@ module LibvirtAsync
99
92
  io = IO.new(fd, io_mode, autoclose: false)
100
93
  @monitor = Monitor.new(io)
101
94
 
102
- while @monitor.readiness == nil
95
+ while @monitor.readiness.nil?
103
96
  cancelled = wait_io(interest)
104
97
 
105
98
  if cancelled
@@ -117,7 +110,6 @@ module LibvirtAsync
117
110
 
118
111
  dbg { "#{self.class}#register_handle async not ready readiness=#{@monitor.readiness}, handle_id=#{handle_id}, fd=#{fd}" }
119
112
  end
120
-
121
113
  end
122
114
 
123
115
  dbg { "#{self.class}#register_handle invokes fiber=0x#{task.fiber.object_id.to_s(16)} handle_id=#{handle_id}, fd=#{fd}" }
@@ -197,8 +189,6 @@ module LibvirtAsync
197
189
  :r
198
190
  elsif writable
199
191
  :w
200
- else
201
- nil
202
192
  end
203
193
  end
204
194
 
@@ -223,8 +213,6 @@ module LibvirtAsync
223
213
  Libvirt::EVENT_HANDLE_READABLE
224
214
  when :w
225
215
  Libvirt::EVENT_HANDLE_WRITABLE
226
- else
227
- nil
228
216
  end
229
217
  end
230
218
  end
@@ -277,14 +265,15 @@ module LibvirtAsync
277
265
  dbg { "#{self.class}#initialize timer_id=#{timer_id}, interval=#{interval}" }
278
266
 
279
267
  @timer_id = timer_id
280
- @interval = interval.to_f / 1000.to_f
268
+ @interval = interval / 1000.0
281
269
  @opaque = opaque
282
270
  @last_fired = Time.now.to_f
283
271
  @monitor = nil
284
272
  end
285
273
 
286
274
  def wait_time
287
- return if interval < 0
275
+ return if interval.negative?
276
+
288
277
  last_fired + interval
289
278
  end
290
279
 
@@ -358,15 +347,12 @@ module LibvirtAsync
358
347
  end
359
348
 
360
349
  def wait_timer(timeout)
361
- begin
362
- @monitor.wait(timeout)
363
- false
364
- rescue Monitor::Cancelled => e
365
- dbg { "#{self.class}#wait_timer cancelled #{e.class} #{e.message}" }
366
- true
367
- end
350
+ @monitor.wait(timeout)
351
+ false
352
+ rescue Monitor::Cancelled => e
353
+ dbg { "#{self.class}#wait_timer cancelled #{e.class} #{e.message}" }
354
+ true
368
355
  end
369
-
370
356
  end
371
357
 
372
358
  class Implementations
@@ -396,12 +382,12 @@ module LibvirtAsync
396
382
  def print_debug_info
397
383
  str = [
398
384
  "#{self.class}:0x#{object_id.to_s(16)}",
399
- "handles = [",
385
+ 'handles = [',
400
386
  @handles.map(&:to_s).join("\n"),
401
- "]",
402
- "timers = [",
387
+ ']',
388
+ 'timers = [',
403
389
  @timers.map(&:to_s).join("\n"),
404
- "]"
390
+ ']'
405
391
  ].join("\n")
406
392
  Libvirt.logger&.debug { str }
407
393
  end
@@ -432,10 +418,16 @@ module LibvirtAsync
432
418
  remove_handle: method(:remove_handle).to_proc,
433
419
  add_timer: method(:add_timer).to_proc,
434
420
  update_timer: method(:update_timer).to_proc,
435
- remove_timer: method(:remove_timer).to_proc
421
+ remove_timer: method(:remove_timer).to_proc,
422
+ schedule: method(:schedule).to_proc
436
423
  )
437
424
  end
438
425
 
426
+ def schedule(&block)
427
+ task = Async::Task.new(Async::Task.current.reactor, nil, &block)
428
+ task.reactor << task.fiber
429
+ end
430
+
439
431
  def add_handle(fd, events, opaque)
440
432
  # add a handle to be tracked by this object. The application is
441
433
  # expected to maintain a list of internal handle IDs (integers); this
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class LogFormatter
2
- LOG_FORMAT = "%s, %s [%s/%s/%s] %s\n".freeze
3
- DEFAULT_DATETIME_FORMAT = "%F %T.%N".freeze
4
+ LOG_FORMAT = "%s, %s [%s/%s/%s] %s\n"
5
+ DEFAULT_DATETIME_FORMAT = '%F %T.%N'
4
6
 
5
7
  attr_accessor :datetime_format
6
8
 
@@ -9,14 +11,7 @@ class LogFormatter
9
11
  end
10
12
 
11
13
  def call(severity, time, progname, message)
12
- LOG_FORMAT % [
13
- severity[0..0],
14
- format_datetime(time),
15
- "0x#{Async::Task.current?&.object_id&.to_s(16)}",
16
- "0x#{Fiber.current.object_id.to_s(16)}",
17
- progname,
18
- format_message(message)
19
- ]
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))
20
15
  end
21
16
 
22
17
  private
@@ -0,0 +1,43 @@
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