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
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