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.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3644f17ca84d47cdb1137cc70206ba1f21e6ff9134cfce63a3e285699da7f209
4
- data.tar.gz: 0ed561ac6371b32ec98b1ff0f4ff8baee4896781eeee7e21ac0af83aeb71e5f0
3
+ metadata.gz: 86eb83fbee1eb9d405515e1112beecf735861a1149aab08468713489f5fa6276
4
+ data.tar.gz: 847652924525076788903cd2e64a0d0fca58760fd57bf934c79d865e538d132f
5
5
  SHA512:
6
- metadata.gz: 230412ae8c751eda2e21459f86c5fd82588c9e4288061fa5137166f92cc82b41045f66a5d643917a6cca0015b61820084e179318ba2730f3f5fefdee985f1923
7
- data.tar.gz: d47c4efedea90cf797e212603da0304250f3a09360e7cde73c7509dd044d91ee36be52ec5f2a86622701e744d85416d8c231c325b0435078c22d2524f9d57688
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 'get_process_mem'
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
- task default: :test
13
+ RuboCop::RakeTask.new(:rubocop)
14
+
15
+ task default: [:rubocop, :test]
data/bin/console CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'bundler/setup'
4
5
  require 'libvirt'
data/exe/libvirt CHANGED
@@ -1,3 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'libvirt'
@@ -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
@@ -6,43 +6,49 @@ module Libvirt
6
6
 
7
7
  STORAGE = DomainCallbackStorage.new
8
8
 
9
- DOMAIN_EVENT_CALLBACKS = FFI::Domain.enum_type(:event_id).symbol_map.map do |name, event_id|
10
- func = FFI::Domain.event_callback(event_id) do |conn_ptr, dom_ptr, *args, op_ptr|
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
- [name, func]
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::Connection.virConnectRef(conn_ptr)
21
- raise Error, "Couldn't retrieve connection reference" if ref_result < 0
22
- new(nil).send(:set_connection, conn_ptr)
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
- cl_result = FFI::Connection.virConnectClose(@conn_ptr)
32
- STDERR.puts "Couldn't close Libvirt::Connection (0x#{obj_id.to_s(16)}) pointer #{@conn_ptr.address}" if cl_result < 0
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::Connection.virConnectOpen(@uri)
39
- raise Error, "Couldn't open connection to #{@uri.inspect}" if @conn_ptr.null?
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::Connection.virConnectClose(@conn_ptr)
45
- raise Error, "Couldn't close connection to #{@uri.inspect}" if result < 0
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::Connection.virConnectGetVersion(@conn_ptr, version_ptr)
63
- raise Error, "Couldn't retrieve connection version" if result < 0
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
- Libvirt::Util::parse_version(version_number)
72
+ Util.parse_version(version_number)
66
73
  end
67
74
 
68
75
  def set_keep_alive(interval, count)
69
- result = FFI::Connection.virConnectSetKeepAlive(@conn_ptr, interval, count)
70
- raise Error, "Couldn't set connection keep_alive" if result < 0
71
- result == 0
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 Error, "Couldn't retrieve domains qty with flags #{flags.to_s(16)}" if result < 0
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 Error, "Couldn't retrieve domains list with flags #{flags.to_s(16)}" if result < 0
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 < 0
164
+ if result.negative?
116
165
  cb_data.pointer.free
117
- raise Error, "Couldn't register domain event callback"
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 Error, "Couldn't deregister domain event callback" if result < 0
183
+ raise Errors::LibError, "Couldn't deregister domain event callback" if result.negative?
135
184
 
136
- STORAGE.remove_struct(connection_pointer: @conn_ptr, callback_id: callback_id)
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::Connection.virConnectGetLibVersion(@conn_ptr, version_ptr)
142
- raise Error, "Couldn't get connection lib version" if result < 0
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
- Libvirt::Util.parse_version(version_number)
196
+ Util.parse_version(version_number)
145
197
  end
146
198
 
147
199
  def hostname
148
- FFI::Connection.virConnectGetHostname(@conn_ptr)
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::Connection.virConnectGetMaxVcpus(@conn_ptr, type)
205
+ FFI::Host.virConnectGetMaxVcpus(@conn_ptr, type)
154
206
  end
155
207
 
156
208
  def capabilities
157
- FFI::Connection.virConnectGetCapabilities(@conn_ptr)
209
+ FFI::Host.virConnectGetCapabilities(@conn_ptr)
158
210
  end
159
211
 
160
212
  def node_info
161
- node_info_ptr = ::FFI::MemoryPointer.new(FFI::NodeInfo::Struct.by_value)
162
- result = FFI::NodeInfo.virNodeGetInfo(@conn_ptr, node_info_ptr)
163
- raise Error, "Couldn't get connection node info" if result < 0
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 Error, "Couldn't create stream" if pointer.null?
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 Error, "Connection to #{@uri.inspect} is not open" if @conn_ptr.null?
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)
@@ -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 Error, "Couldn't retrieve domain reference" if ref_result < 0
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
- STDERR.puts "Couldn't free Libvirt::Domain (0x#{obj_id.to_s(16)}) pointer #{@dom_ptr.address}" if fr_result < 0
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 Error, "Couldn't get domain state" if result < 0
28
- [state.read_int, reason.read_int]
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 Error, "Couldn't get domain uuid" if result < 0
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 Error, "Couldn't attach domain screenshot" if pointer.null?
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 Error, "Couldn't free domain" if result < 0
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 { "#allocate_struct" }
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 = ::FFI::Function.new(:void, [:pointer]) do |pointer|
22
- dbg { "Libvirt::DomainCallbackStorage cb_data_free_func triggered" }
23
- remove_struct(pointer: 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 { "#store_struct" }
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: nil, connection_pointer: nil, callback_id: nil)
37
- dbg { "#remove_struct pointer=#{pointer}, connection_pointer=#{connection_pointer}, callback_id=#{callback_id}," }
37
+ def remove_struct(pointer)
38
+ dbg { "#remove_struct pointer=#{pointer}" }
38
39
 
39
- if pointer
40
- cb_data_struct = CallbackDataStruct.new(pointer)
41
- connection_pointer = cb_data_struct[:connection_pointer]
42
- callback_id = cb_data_struct[:callback_id]
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