libvirt_async 0.1.1 → 0.2.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 +5 -5
- data/CHANGELOG.md +5 -3
- data/README.md +123 -9
- data/lib/libvirt_async.rb +1 -0
- data/lib/libvirt_async/stream_read.rb +144 -0
- data/lib/libvirt_async/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e2b8dda8105b9fe625c1d8bf71634ad66d1dca7507315c09e340eab1b9cfbde5
|
4
|
+
data.tar.gz: 5dc290b2014113e4ad84f3ed36dc4afbe177c95a1124948c2efd47a689062078
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 645fdff4cfe5c25e405ee02c49fa3c15c1a4ae6ad58922cd4ded7a14215ede30559480a6f2712d384d1268d49ff8eda4dd916ce2332d749a46a3f49d7c0907d3
|
7
|
+
data.tar.gz: 5cf5d9a3b65a5e019e420298f98d8acd2c6480b2eb9b29867a4c42e8111987ed67c7abeef27f4cbaa2786384da97ae746043c3a5c69c95651d844f6ae0995cdc
|
data/CHANGELOG.md
CHANGED
@@ -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
|
-
-
|
8
|
+
- Fix async tasks hierarchy
|
7
9
|
|
8
10
|
## 0.1.0
|
9
11
|
|
10
|
-
-
|
11
|
-
-
|
12
|
+
- Register implementations
|
13
|
+
- Debug logging
|
data/README.md
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# LibvirtAsync
|
2
2
|
|
3
|
-
Libvirt event
|
4
|
-
|
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
|
-
|
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
|
-
|
35
|
-
|
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
|
-
|
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
|
|
data/lib/libvirt_async.rb
CHANGED
@@ -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
|
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.
|
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:
|
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
|
-
|
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.
|