libvirt_ffi 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +78 -0
- data/Gemfile +7 -3
- data/Rakefile +6 -1
- data/bin/console +1 -0
- data/exe/libvirt +1 -0
- data/lib/libvirt/base_info.rb +34 -0
- data/lib/libvirt/connection.rb +98 -36
- data/lib/libvirt/domain.rb +105 -8
- data/lib/libvirt/domain_callback_storage.rb +13 -15
- data/lib/libvirt/errors.rb +65 -0
- data/lib/libvirt/event.rb +29 -19
- data/lib/libvirt/ffi/common.rb +8 -1
- data/lib/libvirt/ffi/domain.rb +529 -196
- data/lib/libvirt/ffi/error.rb +243 -0
- data/lib/libvirt/ffi/event.rb +30 -36
- data/lib/libvirt/ffi/helpers.rb +17 -0
- data/lib/libvirt/ffi/host.rb +122 -0
- data/lib/libvirt/ffi/storage.rb +149 -0
- data/lib/libvirt/ffi/stream.rb +19 -17
- data/lib/libvirt/ffi.rb +17 -0
- data/lib/libvirt/node_info.rb +2 -41
- data/lib/libvirt/storage_pool.rb +70 -0
- data/lib/libvirt/storage_pool_info.rb +7 -0
- data/lib/libvirt/storage_volume.rb +51 -0
- data/lib/libvirt/storage_volume_info.rb +7 -0
- data/lib/libvirt/stream.rb +21 -14
- data/lib/libvirt/util.rb +61 -8
- data/lib/libvirt/version.rb +1 -1
- data/lib/libvirt/xml/disk.rb +59 -0
- data/lib/libvirt/xml/domain.rb +76 -0
- data/lib/libvirt/xml/generic.rb +252 -0
- data/lib/libvirt/xml/graphics.rb +14 -0
- data/lib/libvirt/xml/max_vcpu.rb +12 -0
- data/lib/libvirt/xml/memory.rb +14 -0
- data/lib/libvirt/xml/storage_pool.rb +24 -0
- data/lib/libvirt/xml/storage_volume.rb +32 -0
- data/lib/libvirt/xml/vcpu.rb +12 -0
- data/lib/libvirt/xml.rb +23 -0
- data/lib/libvirt.rb +12 -12
- data/lib/libvirt_ffi.rb +2 -0
- data/libvirt.gemspec +5 -1
- data/test_usage/support/libvirt_async.rb +27 -35
- data/test_usage/support/log_formatter.rb +5 -10
- data/test_usage/test_domain.rb +43 -0
- data/test_usage/test_event_loop.rb +115 -39
- data/test_usage/test_libvirtd_restart.rb +63 -0
- data/test_usage/test_metadata.rb +104 -0
- data/test_usage/test_screenshot.rb +14 -13
- data/test_usage/test_storage.rb +52 -0
- metadata +42 -6
- data/lib/libvirt/error.rb +0 -6
- data/lib/libvirt/ffi/connection.rb +0 -94
- data/lib/libvirt/ffi/libvirt.rb +0 -17
- 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
|
data/lib/libvirt/xml.rb
ADDED
@@ -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/
|
8
|
-
require 'libvirt/ffi
|
9
|
-
require 'libvirt/
|
10
|
-
require 'libvirt/
|
11
|
-
require 'libvirt/
|
12
|
-
require 'libvirt/
|
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::
|
32
|
-
raise
|
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
|
-
|
34
|
+
Util.parse_version(version_number)
|
35
35
|
end
|
36
36
|
|
37
37
|
def logger
|
data/lib/libvirt_ffi.rb
CHANGED
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
|
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
|
-
|
4
|
-
@logger = logger
|
5
|
-
end
|
5
|
+
attr_writer :logger
|
6
6
|
|
7
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
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
|
-
|
385
|
+
'handles = [',
|
400
386
|
@handles.map(&:to_s).join("\n"),
|
401
|
-
|
402
|
-
|
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"
|
3
|
-
DEFAULT_DATETIME_FORMAT =
|
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
|