Class: VagrantPlugins::ProviderZone::QGA::Client
- Inherits:
-
Object
- Object
- VagrantPlugins::ProviderZone::QGA::Client
- Includes:
- ClientExec
- Defined in:
- lib/vagrant-zones/qga/client.rb
Overview
JSON-RPC client speaking the QEMU Guest Agent protocol over a UNIX socket. One socket per request to avoid stale buffers and partial reads. TODO(qga-abort): wire guest-exec-cancel on SIGINT mid-exec.
Constant Summary collapse
- DEFAULT_READ_TIMEOUT =
30- PING_READ_TIMEOUT =
5
Instance Attribute Summary collapse
-
#socket_path ⇒ Object
readonly
Returns the value of attribute socket_path.
Instance Method Summary collapse
- #hostname ⇒ Object
- #info ⇒ Object
-
#initialize(socket_path) ⇒ Client
constructor
A new instance of Client.
- #network_interfaces ⇒ Object
- #osinfo ⇒ Object
- #ping ⇒ Object
- #request(execute, arguments = nil, read_timeout: DEFAULT_READ_TIMEOUT) ⇒ Object
- #set_user_password(username, password, crypted: false) ⇒ Object
- #shutdown(mode = 'powerdown') ⇒ Object
- #socket_present? ⇒ Boolean
- #wait_for_ready(timeout) ⇒ Object
- #wait_for_socket(timeout) ⇒ Object
Methods included from ClientExec
Constructor Details
#initialize(socket_path) ⇒ Client
Returns a new instance of Client.
22 23 24 |
# File 'lib/vagrant-zones/qga/client.rb', line 22 def initialize(socket_path) @socket_path = socket_path end |
Instance Attribute Details
#socket_path ⇒ Object (readonly)
Returns the value of attribute socket_path.
20 21 22 |
# File 'lib/vagrant-zones/qga/client.rb', line 20 def socket_path @socket_path end |
Instance Method Details
#hostname ⇒ Object
89 90 91 |
# File 'lib/vagrant-zones/qga/client.rb', line 89 def hostname request('guest-get-host-name') end |
#info ⇒ Object
81 82 83 |
# File 'lib/vagrant-zones/qga/client.rb', line 81 def info request('guest-info') end |
#network_interfaces ⇒ Object
93 94 95 |
# File 'lib/vagrant-zones/qga/client.rb', line 93 def network_interfaces request('guest-network-get-interfaces') end |
#osinfo ⇒ Object
85 86 87 |
# File 'lib/vagrant-zones/qga/client.rb', line 85 def osinfo request('guest-get-osinfo') end |
#ping ⇒ Object
74 75 76 77 78 79 |
# File 'lib/vagrant-zones/qga/client.rb', line 74 def ping request('guest-ping', nil, read_timeout: PING_READ_TIMEOUT) true rescue StandardError false end |
#request(execute, arguments = nil, read_timeout: DEFAULT_READ_TIMEOUT) ⇒ Object
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/vagrant-zones/qga/client.rb', line 50 def request(execute, arguments = nil, read_timeout: DEFAULT_READ_TIMEOUT) payload = { execute: execute } payload[:arguments] = arguments if arguments sock = UNIXSocket.new(@socket_path) begin sock.write("#{JSON.generate(payload)}\n") sock.flush line = nil Timeout.timeout(read_timeout) { line = sock.gets } raise Errors::QGAError, message: "empty response from qga (#{execute})" if line.nil? || line.strip.empty? response = JSON.parse(line) raise Errors::QGAError, message: "#{execute}: #{response['error']}" if response['error'] response['return'] ensure sock.close end rescue Errno::ENOENT, Errno::ECONNREFUSED => e raise Errors::QGAError, message: "qga socket unavailable: #{e.}" rescue Timeout::Error raise Errors::QGATimeout, message: "qga read timeout for #{execute}" end |
#set_user_password(username, password, crypted: false) ⇒ Object
97 98 99 100 101 102 103 |
# File 'lib/vagrant-zones/qga/client.rb', line 97 def set_user_password(username, password, crypted: false) request('guest-set-user-password', { username: username, password: Base64.strict_encode64(password), crypted: crypted }) end |
#shutdown(mode = 'powerdown') ⇒ Object
105 106 107 108 109 110 |
# File 'lib/vagrant-zones/qga/client.rb', line 105 def shutdown(mode = 'powerdown') request('guest-shutdown', { mode: mode }) rescue Errors::QGAError, Errors::QGATimeout # Guest disconnects mid-shutdown; treat as success. nil end |
#socket_present? ⇒ Boolean
26 27 28 |
# File 'lib/vagrant-zones/qga/client.rb', line 26 def socket_present? File.exist?(@socket_path) && File.socket?(@socket_path) end |
#wait_for_ready(timeout) ⇒ Object
40 41 42 43 44 45 46 47 48 |
# File 'lib/vagrant-zones/qga/client.rb', line 40 def wait_for_ready(timeout) deadline = Time.now + timeout loop do return if ping raise Errors::QGATimeout, message: "guest-ping did not respond within #{timeout}s" if Time.now > deadline sleep 2 end end |
#wait_for_socket(timeout) ⇒ Object
30 31 32 33 34 35 36 37 38 |
# File 'lib/vagrant-zones/qga/client.rb', line 30 def wait_for_socket(timeout) deadline = Time.now + timeout loop do return if socket_present? raise Errors::QGATimeout, message: "qga.sock not present after #{timeout}s at #{@socket_path}" if Time.now > deadline sleep 1 end end |