libvirt_async 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b24efe4c5438e07c1fbc9fac06b82493e242deb7
4
- data.tar.gz: c49f812826b3e2275065f52f587a29ef3dec95ae
2
+ SHA256:
3
+ metadata.gz: e2b8dda8105b9fe625c1d8bf71634ad66d1dca7507315c09e340eab1b9cfbde5
4
+ data.tar.gz: 5dc290b2014113e4ad84f3ed36dc4afbe177c95a1124948c2efd47a689062078
5
5
  SHA512:
6
- metadata.gz: cbb0387eabbed0bd926268e62e7995fead38c42e5ee7b01f663e24d03f41c165cf54c219d17102842d4d6a98d6ba18d0f0c91ea4f43b61b57fe31877c80e4af4
7
- data.tar.gz: deb1c6f89322e867a97da6b4d3e7e694b515b9f5e9c7edaced04865cf18b386ef946aafae8eaf502fd89ad84329449c79195dcfef06a90a248edcfb6942abdef
6
+ metadata.gz: 645fdff4cfe5c25e405ee02c49fa3c15c1a4ae6ad58922cd4ded7a14215ede30559480a6f2712d384d1268d49ff8eda4dd916ce2332d749a46a3f49d7c0907d3
7
+ data.tar.gz: 5cf5d9a3b65a5e019e420298f98d8acd2c6480b2eb9b29867a4c42e8111987ed67c7abeef27f4cbaa2786384da97ae746043c3a5c69c95651d844f6ae0995cdc
@@ -1,11 +1,13 @@
1
1
  # Changelog
2
2
 
3
3
  ## Unreleased
4
+ - Add StreamRead for working with non-block streams in read mode
5
+ - Improve README
4
6
 
5
7
  ## 0.1.1
6
- - fix async tasks hierarchy
8
+ - Fix async tasks hierarchy
7
9
 
8
10
  ## 0.1.0
9
11
 
10
- - register implementations
11
- - debug logging
12
+ - Register implementations
13
+ - Debug logging
data/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # LibvirtAsync
2
2
 
3
- Libvirt event async implementation.
4
- Libvirt event api implementation on Fibers based on [libvirt-ruby](https://github.com/qwe/libvirt-ruby) and [async](https://github.com/socketry/async)
3
+ Libvirt event loop asynchronous implementation on Fibers.
4
+ Based on [libvirt-ruby](https://github.com/qwe/libvirt-ruby) and [async](https://github.com/socketry/async).
5
+ Allows to receive domain events.
6
+ Allows to work with streams in asynchronous mode.
5
7
 
6
8
  ## Installation
7
9
 
@@ -21,21 +23,133 @@ Or install it yourself as:
21
23
 
22
24
  ## Usage
23
25
 
26
+ `LibvirtAsync.register_implementations!` must be called once per process before connecting to hypervisor.
27
+
28
+ ### Receiving domain events
29
+
30
+ We can subscribe for event from all hypervisor's domains
24
31
  ```ruby
25
32
  require 'libvirt_async'
26
33
 
27
- LibvirtAsync.use_logger!
28
- LibvirtAsync.logger.level = Logger::Severity::DEBUG # optional for debugging
29
34
  LibvirtAsync.register_implementations!
35
+
36
+ connection = Libvirt::open('qemu+tcp://127.0.0.1:16509')
37
+
38
+ some_object = Object.new
39
+
40
+ connection.domain_event_register_any(
41
+ Libvirt::Connect::DOMAIN_EVENT_ID_LIFECYCLE,
42
+ ->(connection, domain, event, detail, opaque) {
43
+ puts "LIFECYCLE event #{domain.uuid} #{event} #{detail}"
44
+ },
45
+ nil, # optional domain, can be omitted
46
+ some_object # will be an opaque in callback, can be omitted
47
+ )
30
48
  ```
31
49
 
32
- ## Development
50
+ Or we can subscribe on particular domain
51
+
52
+ ```ruby
53
+ require 'libvirt_async'
54
+
55
+ LibvirtAsync.register_implementations!
56
+
57
+ connection = Libvirt::open('qemu+tcp://127.0.0.1:16509')
58
+ domain = connection.list_all_domains.first
33
59
 
34
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests.
35
- You can also run `bin/console` for an interactive prompt that will allow you to experiment.
60
+ some_object = Object.new
61
+
62
+ connection.domain_event_register_any(
63
+ Libvirt::Connect::DOMAIN_EVENT_ID_LIFECYCLE,
64
+ ->(connection, domain, event, detail, opaque) {
65
+ puts "LIFECYCLE event #{domain.uuid} #{event} #{detail}"
66
+ },
67
+ domain,
68
+ some_object # will be an opaque in callback, can be omitted
69
+ )
70
+ ```
71
+
72
+ All available domain events:
73
+ ```ruby
74
+ connection.domain_event_register_any(
75
+ Libvirt::Connect::DOMAIN_EVENT_ID_REBOOT,
76
+ ->(connection, domain, opaque) {
77
+ puts "REBOOT event #{domain.uuid}"
78
+ }
79
+ )
80
+
81
+ connection.domain_event_register_any(
82
+ Libvirt::Connect::DOMAIN_EVENT_ID_RTC_CHANGE,
83
+ ->(connection, domain, utc_offset, opaque) {
84
+ puts "RTC_CHANGE event #{domain.uuid} #{utc_offset}"
85
+ }
86
+ )
87
+
88
+ connection.domain_event_register_any(
89
+ Libvirt::Connect::DOMAIN_EVENT_ID_WATCHDOG,
90
+ ->(connection, domain, action, opaque) {
91
+ puts "WATCHDOG event #{domain.uuid} #{action}"
92
+ }
93
+ )
94
+
95
+ connection.domain_event_register_any(
96
+ Libvirt::Connect::DOMAIN_EVENT_ID_IO_ERROR,
97
+ ->(connection, domain, src_path, dev_alias, action, opaque) {
98
+ puts "IO_ERROR event #{domain.uuid} #{src_path} #{dev_alias} #{action}"
99
+ }
100
+ )
101
+
102
+ connection.domain_event_register_any(
103
+ Libvirt::Connect::DOMAIN_EVENT_ID_IO_ERROR_REASON,
104
+ ->(connection, domain, src_path, dev_alias, action, opaque) {
105
+ puts "IO_ERROR_REASON event #{domain.uuid} #{src_path} #{dev_alias} #{action}"
106
+ }
107
+ )
108
+
109
+ connection.domain_event_register_any(
110
+ Libvirt::Connect::DOMAIN_EVENT_ID_GRAPHICS,
111
+ ->(connection, domain, phase, local, remote, auth_scheme, subject, opaque) {
112
+ puts "GRAPHICS event #{domain.uuid} #{phase} #{local} #{remote} #{auth_scheme} #{subject}"
113
+ }
114
+ )
115
+ ```
116
+
117
+ ### Taking screenshot
118
+
119
+ ```ruby
120
+ require 'libvirt_async'
121
+
122
+ LibvirtAsync.register_implementations!
123
+
124
+ connection = Libvirt::open('qemu+tcp://127.0.0.1:16509')
125
+ domain = connection.list_all_domains.first
126
+
127
+ file = File.new("/screenshots/#{domain.uuid}.pnm", 'wb')
128
+ stream = LibvirtAsync::StreamRead.new(connection, file)
129
+ mime_type = domain.screenshot(stream.stream, 0)
130
+ puts "screenshot saving initiated mime_type=#{mime_type}"
131
+
132
+ # will start screenshot saving
133
+ stream.call do |success, reason, io|
134
+ # this block will be called asynchronously on complete or error
135
+ io.close
136
+ if success
137
+ puts "screenshot saved at #{io.path}"
138
+ else
139
+ puts "screenshot was not saved: #{reason}"
140
+ end
141
+ end
142
+ ```
143
+
144
+ Logging.
145
+ ```ruby
146
+ require 'libvirt_async'
147
+
148
+ LibvirtAsync.use_logger!
149
+ LibvirtAsync.logger.level = Logger::Severity::DEBUG # for debugging
150
+ ```
36
151
 
37
- To install this gem onto your local machine, run `bundle exec rake install`.
38
- To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
152
+ Look at [ruby-libvirt Documenation](https://libvirt.org/ruby/documentation.html) for further details.
39
153
 
40
154
  ## Contributing
41
155
 
@@ -2,6 +2,7 @@ require 'libvirt_async/version'
2
2
  require 'libvirt_async/error'
3
3
  require 'libvirt_async/log_formatter'
4
4
  require 'libvirt_async/implementations'
5
+ require 'libvirt_async/stream_read'
5
6
 
6
7
  module LibvirtAsync
7
8
  def register_implementations!
@@ -0,0 +1,144 @@
1
+ module LibvirtAsync
2
+ class StreamRead
3
+ # StreamRead allows to work with stream in non-block read mode.
4
+
5
+ STATE_COMPLETED = 'completed'.freeze
6
+ STATE_CANCELLED = 'cancelled'.freeze
7
+ STATE_FAILED = 'failed'.freeze
8
+ STATE_PENDING = 'pending'.freeze
9
+
10
+ class RecvError < StandardError
11
+ end
12
+
13
+ include WithDbg
14
+
15
+ attr_reader :state, :stream, :io
16
+
17
+ # @param connection [Libvirt::Connection]
18
+ # @param io [IO]
19
+ # @return [LibvirtAsync::Stream]
20
+ def initialize(connection, io)
21
+ @connection = connection
22
+ @io = io
23
+ @callback = nil
24
+ @state = STATE_PENDING
25
+ @stream = @connection.stream(Libvirt::Stream::NONBLOCK)
26
+ end
27
+
28
+ # @yield asynchronously on complete or error
29
+ # @yieldparam success [Boolean]
30
+ # @yieldparam reason [String,NilClass]
31
+ # @yieldparam io [IO]
32
+ def call(&block)
33
+ add_callback(block) if block_given?
34
+
35
+ run
36
+ end
37
+
38
+ def add_callback(block)
39
+ raise ArgumentError, 'block must be a Proc' unless @callback.is_a?(Proc)
40
+ @callback = block
41
+ end
42
+
43
+ def run
44
+ raise ArgumentError, 'block must be given' if @callback.nil?
45
+
46
+ dbg { "#{to_s}#call event_add_callback calling" }
47
+ @cb_opaque = stream.event_add_callback(
48
+ Libvirt::Stream::EVENT_READABLE,
49
+ -> (_stream, events, _opaque) { stream_callback(events) },
50
+ self
51
+ )
52
+ dbg { "#{to_s}#call event_add_callback called" }
53
+
54
+ nil
55
+ rescue Libvirt::Error => e
56
+ dbg { "#{to_s}#call error occurred\n<#{e.class}>: #{e.message}\n#{e.backtrace.join("\n")}" }
57
+ @state = STATE_FAILED
58
+ stream&.finish rescue nil
59
+ on_error(e)
60
+ @cb_opaque = nil
61
+ end
62
+
63
+ def cancel
64
+ dbg { "#{to_s}#cancel" }
65
+ return if stream.nil?
66
+
67
+ @state = STATE_CANCELLED
68
+ stream.event_remove_callback
69
+ stream.finish
70
+ @stream = nil
71
+ rescue Libvirt::Error => e
72
+ dbg { "#{to_s}#cancel error occurred\n<#{e.class}>: #{e.message}\n#{e.backtrace.join("\n")}" }
73
+ @stream = nil
74
+ ensure
75
+ @cb_opaque = nil
76
+ end
77
+
78
+ def to_s
79
+ "#<#{self.class}:0x#{object_id.to_s(16)} @state=#{@state}>"
80
+ end
81
+
82
+ def inspect
83
+ to_s
84
+ end
85
+
86
+ private
87
+
88
+ def on_error(error)
89
+ @callback.call(false, "#{error.class}: #{error.message}", io)
90
+ end
91
+
92
+ def on_receive(data)
93
+ io.write(data)
94
+ end
95
+
96
+ def on_complete
97
+ @callback.call(true, nil, io)
98
+ end
99
+
100
+ # @param events [Integer]
101
+ def stream_callback(events)
102
+ dbg { "#{to_s}#stream_callback events=#{events}" }
103
+ return unless (Libvirt::Stream::EVENT_READABLE & events) != 0
104
+ # `stream.finish` will be executed asynchronously
105
+ # so callback will be triggered even after we complete data transfer.
106
+ if state != STATE_PENDING
107
+ dbg { "#{to_s}#stream_callback called for #{state} stream. Skipping." }
108
+ return
109
+ end
110
+
111
+ process_read
112
+
113
+ rescue RecvError, Libvirt::Error => e
114
+ dbg { "#{to_s}#stream_callback error occurred\n<#{e.class}>: #{e.message}\n#{e.backtrace.join("\n")}" }
115
+ @state = STATE_FAILED
116
+ stream.finish rescue nil
117
+ on_error(e)
118
+ @cb_opaque = nil
119
+ end
120
+
121
+ def process_read
122
+ code, data = stream.recv(1024)
123
+ dbg { "#{to_s}#stream_callback recv code=#{code}, size=#{data&.size}" }
124
+
125
+ case code
126
+ when 0
127
+ dbg { "#{to_s}#stream_callback finished" }
128
+ @state = STATE_COMPLETED
129
+ stream.finish
130
+ on_complete
131
+ @cb_opaque = nil
132
+ when -1
133
+ dbg { "#{to_s}#stream_callback code -1" }
134
+ raise RecvError, 'error code -1 received'
135
+ when -2
136
+ dbg { "#{to_s}#stream_callback is not ready" }
137
+ else
138
+ dbg { "#{to_s}#stream_callback ready code=#{code}" }
139
+ on_receive(data)
140
+ end
141
+ end
142
+
143
+ end
144
+ end
@@ -1,3 +1,3 @@
1
1
  module LibvirtAsync
2
- VERSION = '0.1.1'
2
+ VERSION = '0.2.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libvirt_async
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Talakevich
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-12-29 00:00:00.000000000 Z
11
+ date: 2020-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-libvirt
@@ -75,6 +75,7 @@ files:
75
75
  - lib/libvirt_async/handle.rb
76
76
  - lib/libvirt_async/implementations.rb
77
77
  - lib/libvirt_async/log_formatter.rb
78
+ - lib/libvirt_async/stream_read.rb
78
79
  - lib/libvirt_async/timer.rb
79
80
  - lib/libvirt_async/util.rb
80
81
  - lib/libvirt_async/version.rb
@@ -102,8 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
103
  - !ruby/object:Gem::Version
103
104
  version: '0'
104
105
  requirements: []
105
- rubyforge_project:
106
- rubygems_version: 2.4.8
106
+ rubygems_version: 3.0.6
107
107
  signing_key:
108
108
  specification_version: 4
109
109
  summary: Libvirt event async implementation.