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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86eb83fbee1eb9d405515e1112beecf735861a1149aab08468713489f5fa6276
|
4
|
+
data.tar.gz: 847652924525076788903cd2e64a0d0fca58760fd57bf934c79d865e538d132f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d54fdb1f3131d4a23f90126773a40f8829b5a9282bae0cc22c81613dd846970d4a1f32ae899856d0993f5fc61d833d6fc1f84896d9bc2e5dab23c964326b62cb
|
7
|
+
data.tar.gz: f092fa8e8a0c3f220a4c8fff8ad530f4b5b740f5f92c4b21bca9c1157e93ae0f572c9547b6d922c61f6629348f6087ac37c2541551713f5ab3934e5a26e4442f
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
AllCops:
|
2
|
+
DisplayCopNames: true
|
3
|
+
TargetRubyVersion: 2.5
|
4
|
+
Exclude:
|
5
|
+
- vendor/**/*
|
6
|
+
- tmp/**/*
|
7
|
+
- pkg/*
|
8
|
+
- test_usage/**/*.rb
|
9
|
+
|
10
|
+
Layout/LineLength:
|
11
|
+
Max: 180
|
12
|
+
|
13
|
+
Style/SymbolArray:
|
14
|
+
EnforcedStyle: brackets
|
15
|
+
|
16
|
+
Style/Lambda:
|
17
|
+
EnforcedStyle: literal
|
18
|
+
|
19
|
+
Naming/MethodParameterName:
|
20
|
+
AllowedNames:
|
21
|
+
- id
|
22
|
+
- cb
|
23
|
+
- fd
|
24
|
+
|
25
|
+
Naming/AccessorMethodName:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Style/HashEachMethods:
|
29
|
+
Enabled: true
|
30
|
+
|
31
|
+
Style/HashTransformKeys:
|
32
|
+
Enabled: true
|
33
|
+
|
34
|
+
Style/HashTransformValues:
|
35
|
+
Enabled: true
|
36
|
+
|
37
|
+
Style/Documentation:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Metrics/PerceivedComplexity:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
Metrics/MethodLength:
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
Metrics/AbcSize:
|
47
|
+
Enabled: false
|
48
|
+
|
49
|
+
Metrics/ModuleLength:
|
50
|
+
Enabled: false
|
51
|
+
|
52
|
+
Metrics/BlockLength:
|
53
|
+
Enabled: false
|
54
|
+
|
55
|
+
Metrics/ClassLength:
|
56
|
+
Enabled: false
|
57
|
+
|
58
|
+
Metrics/CyclomaticComplexity:
|
59
|
+
Enabled: false
|
60
|
+
|
61
|
+
Layout/MultilineOperationIndentation:
|
62
|
+
Enabled: false
|
63
|
+
|
64
|
+
Layout/FirstHashElementIndentation:
|
65
|
+
Enabled: false
|
66
|
+
|
67
|
+
Layout/FirstArrayElementIndentation:
|
68
|
+
Enabled: false
|
69
|
+
|
70
|
+
Layout/FirstArgumentIndentation:
|
71
|
+
Enabled: false
|
72
|
+
|
73
|
+
Layout/ClosingParenthesisIndentation:
|
74
|
+
Enabled: false
|
75
|
+
|
76
|
+
Layout/ArgumentAlignment:
|
77
|
+
Enabled: false
|
78
|
+
|
data/Gemfile
CHANGED
@@ -1,14 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source 'https://rubygems.org'
|
2
4
|
|
3
5
|
# Specify your gem's dependencies in libvirt.gemspec
|
4
6
|
gemspec
|
5
7
|
|
6
|
-
gem 'rake', '~> 12.0'
|
7
8
|
gem 'minitest', '~> 5.0'
|
9
|
+
gem 'rake', '~> 12.0'
|
10
|
+
|
11
|
+
gem 'nokogiri'
|
8
12
|
|
9
13
|
group :development do
|
10
|
-
gem 'async', '~> 1.24'
|
11
14
|
gem 'activesupport'
|
12
|
-
gem '
|
15
|
+
gem 'async', '~> 1.24'
|
13
16
|
gem 'gc_tracer'
|
17
|
+
gem 'get_process_mem'
|
14
18
|
end
|
data/Rakefile
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler/gem_tasks'
|
2
4
|
require 'rake/testtask'
|
5
|
+
require 'rubocop/rake_task'
|
3
6
|
|
4
7
|
Rake::TestTask.new(:test) do |t|
|
5
8
|
t.libs << 'test'
|
@@ -7,4 +10,6 @@ Rake::TestTask.new(:test) do |t|
|
|
7
10
|
t.test_files = FileList['test/**/*_test.rb']
|
8
11
|
end
|
9
12
|
|
10
|
-
|
13
|
+
RuboCop::RakeTask.new(:rubocop)
|
14
|
+
|
15
|
+
task default: [:rubocop, :test]
|
data/bin/console
CHANGED
data/exe/libvirt
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Libvirt
|
4
|
+
class BaseInfo
|
5
|
+
# Abstract Base class for info
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :_struct_class
|
9
|
+
|
10
|
+
# @param [Class<FFI::Struct>]
|
11
|
+
def struct_class(klass)
|
12
|
+
self._struct_class = klass
|
13
|
+
klass.members.each do |attr|
|
14
|
+
define_method(attr) { @struct[attr] }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(pointer)
|
20
|
+
raise ArgumentError, "Can't initialize base class #{self.class}" if self.class == BaseInfo
|
21
|
+
|
22
|
+
@ptr = pointer
|
23
|
+
@struct = self.class._struct_class.new(pointer)
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](attr)
|
27
|
+
@struct[attr]
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_h
|
31
|
+
@struct.members.map { |attr| [attr, @struct[attr]] }.to_h
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/libvirt/connection.rb
CHANGED
@@ -6,43 +6,49 @@ module Libvirt
|
|
6
6
|
|
7
7
|
STORAGE = DomainCallbackStorage.new
|
8
8
|
|
9
|
-
DOMAIN_EVENT_CALLBACKS =
|
10
|
-
func = FFI::Domain.
|
9
|
+
DOMAIN_EVENT_CALLBACKS = DOMAIN_EVENT_IDS.map do |event_id_sym|
|
10
|
+
func = FFI::Domain.event_callback_for(event_id_sym) do |conn_ptr, dom_ptr, *args, op_ptr|
|
11
11
|
connection = Connection.load_ref(conn_ptr)
|
12
12
|
domain = Domain.load_ref(dom_ptr)
|
13
13
|
block, opaque = STORAGE.retrieve_from_pointer(op_ptr)
|
14
14
|
block.call(connection, domain, *args, opaque)
|
15
15
|
end
|
16
|
-
[
|
17
|
-
end.to_h
|
16
|
+
[event_id_sym, func]
|
17
|
+
end.to_h.freeze
|
18
18
|
|
19
19
|
def self.load_ref(conn_ptr)
|
20
|
-
ref_result = FFI::
|
21
|
-
raise
|
22
|
-
|
20
|
+
ref_result = FFI::Host.virConnectRef(conn_ptr)
|
21
|
+
raise Errors::LibError, "Couldn't retrieve connection reference" if ref_result.negative?
|
22
|
+
|
23
|
+
new(nil).tap { |r| r.send(:set_connection, conn_ptr) }
|
23
24
|
end
|
24
25
|
|
25
26
|
def initialize(uri)
|
26
27
|
@uri = uri
|
27
28
|
@conn_ptr = ::FFI::Pointer.new(0)
|
29
|
+
@close_data = nil
|
28
30
|
|
29
31
|
free = ->(obj_id) do
|
32
|
+
Util.log(:debug) { "Finalize Libvirt::Connection 0x#{obj_id.to_s(16)} @conn_ptr=#{@conn_ptr}," }
|
30
33
|
return if @conn_ptr.null?
|
31
|
-
|
32
|
-
|
34
|
+
|
35
|
+
cl_result = FFI::Host.virConnectClose(@conn_ptr)
|
36
|
+
warn "Couldn't close Libvirt::Connection (0x#{obj_id.to_s(16)}) pointer #{@conn_ptr.address}" if cl_result.negative?
|
33
37
|
end
|
34
38
|
ObjectSpace.define_finalizer(self, free)
|
35
39
|
end
|
36
40
|
|
37
41
|
def open
|
38
|
-
@conn_ptr = FFI::
|
39
|
-
raise
|
42
|
+
@conn_ptr = FFI::Host.virConnectOpen(@uri)
|
43
|
+
raise Errors::LibError, "Couldn't open connection to #{@uri.inspect}" if @conn_ptr.null?
|
44
|
+
|
40
45
|
true
|
41
46
|
end
|
42
47
|
|
43
48
|
def close
|
44
|
-
result = FFI::
|
45
|
-
raise
|
49
|
+
result = FFI::Host.virConnectClose(@conn_ptr)
|
50
|
+
raise Errors::LibError, "Couldn't close connection to #{@uri.inspect}" if result.negative?
|
51
|
+
|
46
52
|
@conn_ptr = ::FFI::Pointer.new(0)
|
47
53
|
true
|
48
54
|
end
|
@@ -59,16 +65,18 @@ module Libvirt
|
|
59
65
|
check_open!
|
60
66
|
|
61
67
|
version_ptr = ::FFI::MemoryPointer.new(:ulong)
|
62
|
-
result = FFI::
|
63
|
-
raise
|
68
|
+
result = FFI::Host.virConnectGetVersion(@conn_ptr, version_ptr)
|
69
|
+
raise Errors::LibError, "Couldn't retrieve connection version" if result.negative?
|
70
|
+
|
64
71
|
version_number = version_ptr.get_ulong(0)
|
65
|
-
|
72
|
+
Util.parse_version(version_number)
|
66
73
|
end
|
67
74
|
|
68
75
|
def set_keep_alive(interval, count)
|
69
|
-
result = FFI::
|
70
|
-
raise
|
71
|
-
|
76
|
+
result = FFI::Host.virConnectSetKeepAlive(@conn_ptr, interval, count)
|
77
|
+
raise Errors::LibError, "Couldn't set connection keep_alive" if result.negative?
|
78
|
+
|
79
|
+
result.zero?
|
72
80
|
end
|
73
81
|
|
74
82
|
def to_s
|
@@ -81,19 +89,60 @@ module Libvirt
|
|
81
89
|
|
82
90
|
def list_all_domains_qty(flags = 0)
|
83
91
|
result = FFI::Domain.virConnectListAllDomains(@conn_ptr, nil, flags)
|
84
|
-
raise
|
92
|
+
raise Errors::LibError, "Couldn't retrieve domains qty with flags #{flags.to_s(16)}" if result.negative?
|
93
|
+
|
85
94
|
result
|
86
95
|
end
|
87
96
|
|
88
97
|
def list_all_domains(flags = 0)
|
89
98
|
size = list_all_domains_qty(flags)
|
99
|
+
return [] if size.zero?
|
100
|
+
|
90
101
|
domains_ptr = ::FFI::MemoryPointer.new(:pointer, size)
|
91
102
|
result = FFI::Domain.virConnectListAllDomains(@conn_ptr, domains_ptr, flags)
|
92
|
-
raise
|
103
|
+
raise Errors::LibError, "Couldn't retrieve domains list with flags #{flags.to_s(16)}" if result.negative?
|
104
|
+
|
93
105
|
ptr = domains_ptr.read_pointer
|
94
106
|
ptr.get_array_of_pointer(0, size).map { |dom_ptr| Libvirt::Domain.new(dom_ptr) }
|
95
107
|
end
|
96
108
|
|
109
|
+
def list_all_storage_pools_qty(options_or_flags = nil)
|
110
|
+
flags = Util.parse_flags options_or_flags, FFI::Storage.enum_type(:list_all_pools_flags)
|
111
|
+
result = FFI::Storage.virConnectListAllStoragePools(@conn_ptr, nil, flags)
|
112
|
+
raise Errors::LibError, "Couldn't retrieve storage pools qty with flags #{flags.to_s(16)}" if result.negative?
|
113
|
+
|
114
|
+
result
|
115
|
+
end
|
116
|
+
|
117
|
+
def list_all_storage_pools(options_or_flags = nil)
|
118
|
+
flags = Util.parse_flags options_or_flags, FFI::Storage.enum_type(:list_all_pools_flags)
|
119
|
+
size = list_all_storage_pools_qty(flags)
|
120
|
+
return [] if size.zero?
|
121
|
+
|
122
|
+
storage_pools_ptr = ::FFI::MemoryPointer.new(:pointer, size)
|
123
|
+
result = FFI::Storage.virConnectListAllStoragePools(@conn_ptr, storage_pools_ptr, flags)
|
124
|
+
raise Errors::LibError, "Couldn't retrieve storage pools list with flags #{flags.to_s(16)}" if result.negative?
|
125
|
+
|
126
|
+
ptr = storage_pools_ptr.read_pointer
|
127
|
+
ptr.get_array_of_pointer(0, size).map { |stp_ptr| StoragePool.new(stp_ptr) }
|
128
|
+
end
|
129
|
+
|
130
|
+
def register_close_callback(opaque = nil, &block)
|
131
|
+
dbg { "#register_close_callback opaque=#{opaque}" }
|
132
|
+
raise ArgumentError, 'close function already registered' if @close_data
|
133
|
+
|
134
|
+
@close_data = { opaque: opaque, block: block }
|
135
|
+
@close_cb = FFI::Host.callback_function(:virConnectCloseFunc) do |_conn, reason, _op|
|
136
|
+
dbg { "CONNECTION CLOSED @conn_ptr=#{@conn_ptr} reason=#{reason}" }
|
137
|
+
@close_data[:block].call(self, reason, @close_data[:opaque])
|
138
|
+
end
|
139
|
+
@close_free_func = FFI::Common.free_function do
|
140
|
+
dbg { "CONNECTION CLOSED FREE FUNC @conn_ptr=#{@conn_ptr}" }
|
141
|
+
@close_cb = @close_free_func = @close_data = nil
|
142
|
+
end
|
143
|
+
FFI::Host.virConnectRegisterCloseCallback(@conn_ptr, @close_cb, nil, @close_free_func)
|
144
|
+
end
|
145
|
+
|
97
146
|
# @yield conn, dom, *args
|
98
147
|
def register_domain_event_callback(event_id, domain = nil, opaque = nil, &block)
|
99
148
|
dbg { "#register_domain_event_callback event_id=#{event_id}" }
|
@@ -112,9 +161,9 @@ module Libvirt
|
|
112
161
|
cb_data.pointer,
|
113
162
|
cb_data_free_func
|
114
163
|
)
|
115
|
-
if result
|
164
|
+
if result.negative?
|
116
165
|
cb_data.pointer.free
|
117
|
-
raise
|
166
|
+
raise Errors::LibError, "Couldn't register domain event callback"
|
118
167
|
end
|
119
168
|
|
120
169
|
STORAGE.store_struct(
|
@@ -131,45 +180,58 @@ module Libvirt
|
|
131
180
|
dbg { "#deregister_domain_event_callback callback_id=#{callback_id}" }
|
132
181
|
|
133
182
|
result = FFI::Domain.virConnectDomainEventDeregisterAny(@conn_ptr, callback_id)
|
134
|
-
raise
|
183
|
+
raise Errors::LibError, "Couldn't deregister domain event callback" if result.negative?
|
135
184
|
|
136
|
-
|
185
|
+
# virConnectDomainEventDeregisterAny will call free func
|
186
|
+
# So we don't need to remove object from STORAGE here.
|
187
|
+
true
|
137
188
|
end
|
138
189
|
|
139
190
|
def lib_version
|
140
191
|
version_ptr = ::FFI::MemoryPointer.new(:ulong)
|
141
|
-
result = FFI::
|
142
|
-
raise
|
192
|
+
result = FFI::Host.virConnectGetLibVersion(@conn_ptr, version_ptr)
|
193
|
+
raise Errors::LibError, "Couldn't get connection lib version" if result.negative?
|
194
|
+
|
143
195
|
version_number = version_ptr.get_ulong(0)
|
144
|
-
|
196
|
+
Util.parse_version(version_number)
|
145
197
|
end
|
146
198
|
|
147
199
|
def hostname
|
148
|
-
FFI::
|
200
|
+
FFI::Host.virConnectGetHostname(@conn_ptr)
|
149
201
|
end
|
150
202
|
|
151
203
|
# @param type [String,NilClass]
|
152
204
|
def max_vcpus(type = nil)
|
153
|
-
FFI::
|
205
|
+
FFI::Host.virConnectGetMaxVcpus(@conn_ptr, type)
|
154
206
|
end
|
155
207
|
|
156
208
|
def capabilities
|
157
|
-
FFI::
|
209
|
+
FFI::Host.virConnectGetCapabilities(@conn_ptr)
|
158
210
|
end
|
159
211
|
|
160
212
|
def node_info
|
161
|
-
node_info_ptr = ::FFI::MemoryPointer.new(FFI::
|
162
|
-
result = FFI::
|
163
|
-
raise
|
213
|
+
node_info_ptr = ::FFI::MemoryPointer.new(FFI::Host::NodeInfoStruct.by_value)
|
214
|
+
result = FFI::Host.virNodeGetInfo(@conn_ptr, node_info_ptr)
|
215
|
+
raise Errors::LibError, "Couldn't get connection node info" if result.negative?
|
216
|
+
|
164
217
|
NodeInfo.new(node_info_ptr)
|
165
218
|
end
|
166
219
|
|
167
220
|
def stream(flags = 0)
|
168
221
|
pointer = FFI::Stream.virStreamNew(@conn_ptr, flags)
|
169
|
-
raise
|
222
|
+
raise Errors::LibError, "Couldn't create stream" if pointer.null?
|
223
|
+
|
170
224
|
Stream.new(pointer)
|
171
225
|
end
|
172
226
|
|
227
|
+
def define_domain(xml, options_or_flags = nil)
|
228
|
+
flags = Util.parse_flags options_or_flags, FFI::Domain.enum_type(:define_flags)
|
229
|
+
pointer = FFI::Domain.virDomainDefineXMLFlags(@conn_ptr, xml, flags)
|
230
|
+
raise Errors::LibError, "Couldn't define domain" if pointer.null?
|
231
|
+
|
232
|
+
Domain.new(pointer)
|
233
|
+
end
|
234
|
+
|
173
235
|
private
|
174
236
|
|
175
237
|
def set_connection(conn_ptr)
|
@@ -177,7 +239,7 @@ module Libvirt
|
|
177
239
|
end
|
178
240
|
|
179
241
|
def check_open!
|
180
|
-
raise
|
242
|
+
raise Errors::LibError, "Connection to #{@uri.inspect} is not open" if @conn_ptr.null?
|
181
243
|
end
|
182
244
|
|
183
245
|
def dbg(&block)
|
data/lib/libvirt/domain.rb
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
module Libvirt
|
4
4
|
class Domain
|
5
|
-
|
6
5
|
def self.load_ref(dom_ptr)
|
7
6
|
ref_result = FFI::Domain.virDomainRef(dom_ptr)
|
8
|
-
raise
|
7
|
+
raise Errors::LibError, "Couldn't retrieve domain reference" if ref_result.negative?
|
8
|
+
|
9
9
|
new(dom_ptr)
|
10
10
|
end
|
11
11
|
|
@@ -13,9 +13,11 @@ module Libvirt
|
|
13
13
|
@dom_ptr = dom_ptr
|
14
14
|
|
15
15
|
free = ->(obj_id) do
|
16
|
+
Util.log(:debug) { "Finalize Libvirt::Domain 0x#{obj_id.to_s(16)} @dom_ptr=#{@dom_ptr}," }
|
16
17
|
return unless @dom_ptr
|
18
|
+
|
17
19
|
fr_result = FFI::Domain.virDomainFree(@dom_ptr)
|
18
|
-
|
20
|
+
warn "Couldn't free Libvirt::Domain (0x#{obj_id.to_s(16)}) pointer #{@dom_ptr.address}" if fr_result.negative?
|
19
21
|
end
|
20
22
|
ObjectSpace.define_finalizer(self, free)
|
21
23
|
end
|
@@ -24,8 +26,11 @@ module Libvirt
|
|
24
26
|
state = ::FFI::MemoryPointer.new(:int)
|
25
27
|
reason = ::FFI::MemoryPointer.new(:int)
|
26
28
|
result = FFI::Domain.virDomainGetState(@dom_ptr, state, reason, 0)
|
27
|
-
raise
|
28
|
-
|
29
|
+
raise Errors::LibError, "Couldn't get domain state" if result.negative?
|
30
|
+
|
31
|
+
state_sym = FFI::Domain.enum_type(:state)[state.read_int]
|
32
|
+
reason_sym = FFI::Domain.state_reason(state_sym, reason.read_int)
|
33
|
+
[state_sym, reason_sym]
|
29
34
|
end
|
30
35
|
|
31
36
|
def to_ptr
|
@@ -35,7 +40,8 @@ module Libvirt
|
|
35
40
|
def uuid
|
36
41
|
buff = ::FFI::MemoryPointer.new(:char, FFI::Domain::UUID_STRING_BUFLEN)
|
37
42
|
result = FFI::Domain.virDomainGetUUIDString(@dom_ptr, buff)
|
38
|
-
raise
|
43
|
+
raise Errors::LibError, "Couldn't get domain uuid" if result.negative?
|
44
|
+
|
39
45
|
buff.read_string
|
40
46
|
end
|
41
47
|
|
@@ -71,17 +77,108 @@ module Libvirt
|
|
71
77
|
dbg { "#screenshot stream=#{stream}, display=#{display}," }
|
72
78
|
|
73
79
|
mime_type, pointer = FFI::Domain.virDomainScreenshot(@dom_ptr, stream.to_ptr, display, 0)
|
74
|
-
raise
|
80
|
+
raise Errors::LibError, "Couldn't attach domain screenshot" if pointer.null?
|
81
|
+
|
75
82
|
# free pointer required
|
76
83
|
mime_type
|
77
84
|
end
|
78
85
|
|
79
86
|
def free_domain
|
80
87
|
result = FFI::Domain.virDomainFree(@dom_ptr)
|
81
|
-
raise
|
88
|
+
raise Errors::LibError, "Couldn't free domain" if result.negative?
|
89
|
+
|
82
90
|
@dom_ptr = nil
|
83
91
|
end
|
84
92
|
|
93
|
+
def start(flags = 0)
|
94
|
+
result = FFI::Domain.virDomainCreateWithFlags(@dom_ptr, flags)
|
95
|
+
raise Errors::LibError, "Couldn't start domain" if result.negative?
|
96
|
+
end
|
97
|
+
|
98
|
+
def reboot(flags = 0)
|
99
|
+
result = FFI::Domain.virDomainReboot(@dom_ptr, flags)
|
100
|
+
raise Errors::LibError, "Couldn't reboot domain" if result.negative?
|
101
|
+
end
|
102
|
+
|
103
|
+
def shutdown(flags = :ACPI_POWER_BTN)
|
104
|
+
result = FFI::Domain.virDomainShutdownFlags(@dom_ptr, flags)
|
105
|
+
raise Errors::LibError, "Couldn't shutdown domain" if result.negative?
|
106
|
+
end
|
107
|
+
|
108
|
+
def power_off(flags = 0)
|
109
|
+
result = FFI::Domain.virDomainDestroyFlags(@dom_ptr, flags)
|
110
|
+
raise Errors::LibError, "Couldn't power off domain" if result.negative?
|
111
|
+
end
|
112
|
+
|
113
|
+
def reset(flags = 0)
|
114
|
+
result = FFI::Domain.virDomainReset(@dom_ptr, flags)
|
115
|
+
raise Errors::LibError, "Couldn't reset domain" if result.negative?
|
116
|
+
end
|
117
|
+
|
118
|
+
def suspend
|
119
|
+
result = FFI::Domain.virDomainSuspend(@dom_ptr)
|
120
|
+
raise Errors::LibError, "Couldn't suspend domain" if result.negative?
|
121
|
+
end
|
122
|
+
|
123
|
+
def resume
|
124
|
+
result = FFI::Domain.virDomainResume(@dom_ptr)
|
125
|
+
raise Errors::LibError, "Couldn't resume domain" if result.negative?
|
126
|
+
end
|
127
|
+
|
128
|
+
# Undefine a domain.
|
129
|
+
# If the domain is running, it's converted to transient domain, without stopping it.
|
130
|
+
# If the domain is inactive, the domain configuration is removed.
|
131
|
+
# @param options_or_flags [Array<Symbol>,Hash{Symbol=>Boolean},Integer,Symbol,nil]
|
132
|
+
# @see Libvirt::FFI::Domain enum :undefine_flags_values for acceptable keys
|
133
|
+
# @see Libvirt::Util.parse_flags for possible argument values
|
134
|
+
# @raise [Libvirt::Errors::LibError] if operation failed
|
135
|
+
def undefine(options_or_flags = nil)
|
136
|
+
flags = Util.parse_flags options_or_flags, FFI::Domain.enum_type(:undefine_flags_values)
|
137
|
+
result = FFI::Domain.virDomainUndefineFlags(@dom_ptr, flags)
|
138
|
+
raise Errors::LibError, "Couldn't resume domain" if result.negative?
|
139
|
+
end
|
140
|
+
|
141
|
+
# After save_memory(:PAUSED) you need to call #start and #resume
|
142
|
+
# to move domain to the running state.
|
143
|
+
def save_memory(flags = :PAUSED)
|
144
|
+
result = FFI::Domain.virDomainManagedSave(@dom_ptr, flags)
|
145
|
+
raise Errors::LibError, "Couldn't save domain memory" if result.negative?
|
146
|
+
end
|
147
|
+
|
148
|
+
# Sets metadata
|
149
|
+
# @param metadata [String] xml node for element type, text for other types
|
150
|
+
# DESCRIPTION 0x0 - Operate on <description>
|
151
|
+
# TITLE 0x1 - Operate on <title>
|
152
|
+
# ELEMENT 0x2 - Operate on <metadata>
|
153
|
+
# @param type [Integer,Symbol] one of :ELEMENT, :TITLE, :DESCRIPTION
|
154
|
+
# @param key [String] xml key (required for type element)
|
155
|
+
# @param uri [String] xml namespace (required for type element)
|
156
|
+
# @param flags [Integer,Symbol] one off AFFECT_CURRENT, AFFECT_CONFIG, AFFECT_LIVE
|
157
|
+
# AFFECT_CURRENT 0x0 - Affect current domain state.
|
158
|
+
# AFFECT_LIVE 0x1 - Affect running domain state.
|
159
|
+
# AFFECT_CONFIG 0x2 - Affect persistent domain state.
|
160
|
+
# @raise [Libvirt::Errors::LibError] if operation failed
|
161
|
+
def set_metadata(metadata, type: :ELEMENT, key: nil, uri: nil, flags: :AFFECT_CURRENT)
|
162
|
+
result = FFI::Domain.virDomainSetMetadata(@dom_ptr, type, metadata, key, uri, flags)
|
163
|
+
raise Errors::LibError, "Couldn't set domain metadata" if result.negative?
|
164
|
+
end
|
165
|
+
|
166
|
+
# Retrieves metadata
|
167
|
+
# @param type [Integer,Symbol] one of :ELEMENT, :TITLE, :DESCRIPTION
|
168
|
+
# @param uri [String] xml namespace (required for type element)
|
169
|
+
# @param flags [Integer,Symbol] one off AFFECT_CURRENT, AFFECT_CONFIG, AFFECT_LIVE
|
170
|
+
# AFFECT_CURRENT 0x0 - Affect current domain state.
|
171
|
+
# AFFECT_LIVE 0x1 - Affect running domain state.
|
172
|
+
# AFFECT_CONFIG 0x2 - Affect persistent domain state.
|
173
|
+
# @raise [Libvirt::Errors::LibError] if operation failed
|
174
|
+
# @return [String] xml node, title, or description.
|
175
|
+
def get_metadata(type: :ELEMENT, uri: nil, flags: :AFFECT_CURRENT)
|
176
|
+
result = FFI::Domain.virDomainGetMetadata(@dom_ptr, type, uri, flags)
|
177
|
+
raise Errors::LibError, "Couldn't get domain metadata" if result.nil?
|
178
|
+
|
179
|
+
result
|
180
|
+
end
|
181
|
+
|
85
182
|
private
|
86
183
|
|
87
184
|
def dbg(&block)
|
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Libvirt
|
2
4
|
class DomainCallbackStorage
|
3
|
-
|
4
5
|
class CallbackDataStruct < ::FFI::Struct
|
5
6
|
layout :connection_pointer, :pointer,
|
6
7
|
:callback_id, :int
|
@@ -14,39 +15,36 @@ module Libvirt
|
|
14
15
|
# cb_data [Libvirt::DomainCallbackStorage::CallbackDataStruct],
|
15
16
|
# cb_data_free_func [FFI::Function]
|
16
17
|
def allocate_struct
|
17
|
-
dbg {
|
18
|
+
dbg { '#allocate_struct' }
|
18
19
|
|
19
20
|
cb_data_ptr = ::FFI::MemoryPointer.new(:char, CallbackDataStruct.size, false)
|
20
21
|
cb_data = CallbackDataStruct.new(cb_data_ptr)
|
21
|
-
cb_data_free_func =
|
22
|
-
dbg {
|
23
|
-
remove_struct(pointer
|
22
|
+
cb_data_free_func = FFI::Common.free_function do |pointer|
|
23
|
+
dbg { 'Libvirt::DomainCallbackStorage cb_data_free_func triggered' }
|
24
|
+
remove_struct(pointer)
|
24
25
|
end
|
25
26
|
[cb_data, cb_data_free_func]
|
26
27
|
end
|
27
28
|
|
28
29
|
def store_struct(cb_data, connection_pointer:, callback_id:, cb:, opaque:)
|
29
|
-
dbg {
|
30
|
+
dbg { '#store_struct' }
|
30
31
|
|
31
32
|
cb_data[:connection_pointer] = connection_pointer
|
32
33
|
cb_data[:callback_id] = callback_id
|
33
34
|
@inner_storage[connection_pointer.address][callback_id] = { cb: cb, opaque: opaque, pointer: cb_data.pointer }
|
34
35
|
end
|
35
36
|
|
36
|
-
def remove_struct(pointer
|
37
|
-
dbg { "#remove_struct pointer=#{pointer}
|
37
|
+
def remove_struct(pointer)
|
38
|
+
dbg { "#remove_struct pointer=#{pointer}" }
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
40
|
+
cb_data_struct = CallbackDataStruct.new(pointer)
|
41
|
+
connection_pointer = cb_data_struct[:connection_pointer]
|
42
|
+
callback_id = cb_data_struct[:callback_id]
|
43
|
+
dbg { "#remove_struct pointer=#{pointer}, connection_pointer=#{connection_pointer}, callback_id=#{callback_id}," }
|
44
44
|
|
45
45
|
cb_data = @inner_storage[connection_pointer.address].delete(callback_id)
|
46
|
-
pointer ||= cb_data[:pointer]
|
47
46
|
@inner_storage.delete(connection_pointer.address) if @inner_storage[connection_pointer.address].empty?
|
48
47
|
|
49
|
-
#pointer.free
|
50
48
|
cb_data[:opaque]
|
51
49
|
end
|
52
50
|
|