Verx
Erlang implementation of the libvirtd remote protocol
Install / Use
/learn @msantos/VerxREADME
Erlang implementation of the libvirtd remote protocol.
For an Erlang binding to the C libvirt interface, see:
https://github.com/msantos/erlang-libvirt
WARNING
remote_protocol.x contains this warning:
(1) The protocol is internal and may change at any time, without
notice. Do not use it. Instead link to libvirt and use the remote
driver.
http://libvirt.org/git/?p=libvirt.git;a=blob_plain;f=src/remote/remote_protocol.x;hb=HEAD
However, see the section GENERATING THE REMOTE PROTOCOL MODULE below for instructions on recompiling the XDR protocol spec if any changes occur.
The RPC protocol is documented here:
http://libvirt.org/internals/rpc.html
For the remote support documentation:
http://libvirt.org/remote.html
The version of remote_protocol.x used was taken from libvirt master at 1.3.1 (commit 8fd68675e2b5eed5b2aae636544a0a80f9fc70e9).
HOW TO BUILD IT
make
See GENERATING THE REMOTE PROTOCOL MODULE to rebuild the XDR protocol parser.
TESTING EVERYTHING WORKS
To quickly test everything works, the libvirtd test driver can be used
with bin/verx, an escript that provides a simple command line interface
to the verx library.
You'll have to set up the ERL_LIBS environment variable first, e.g., if verx is checked out in ~/src:
export ERL_LIBS=$ERL_LIBS:~/src
Then run:
bin/verx
To list the test VMs:
bin/verx list --uri test:///default
To retrieve the test virtual machine configuration:
bin/verx dumpxml test --uri test:///default
To create the example VM (a no-op with the test driver):
bin/verx create priv/example.xml --uri test:///default
To see all the VMs (if you have TLS set up):
bin/verx list --all --transport verx_client_tls --uri test:///default
To screenshot the test VM:
bin/verx screenshot test --uri test:///default
The test driver will return an error if console access is requested. To connect to an actual VM's console using the Unix transport:
bin/verx console localvm # control-C to exit
HOW TO USE IT
libvirt documentation
See http://libvirt.org/html/libvirt-libvirt.html
DATA TYPES
verx_transport()
Reference to the underlying transport and transport handler.
unix_socket() = string() | binary()
Path to Unix socket.
verx
verx:Call(Ref) -> ok | {ok, Payload} | {error, Error}
verx:Call(Ref, Arg) -> ok | {ok, Payload} | {error, Error}
Types Call = [connect_open, connect_close, connect_list_domain, ...]
Ref = verx_transport()
Arg = [remote_protocol_args()]
Payload = [remote_protocol_ret()]
Error = [ posix() | libvirt() ]
verx has a large number of functions (283). See verx.erl or the
exports in verx:module_info() for a list.
Understanding the arguments for a remote protocol call takes some
work. For example, for verx:domain_define_xml/2, here are some
places to look at:
* check verx.erl for the arity
* check remote_protocol_xdr.erl for the argument format. The
parsing function is prefaced with "enc_remote_" and ends with
"_args":
enc_remote_domain_define_xml_args/1
* check the XDR protocol file, remote_protocol.x:
struct remote_domain_define_xml_args {
remote_nonnull_string xml;
};
* look at the libvirt documentation. Generally the libvirt
counterpart is camelcased and prefaced with "vir":
virDomainDefineXML
Similarly, for the call return values, search for the
suffix "_ret", e.g., dec_remote_domain_define_xml_ret and
remote_domain_define_xml_ret.
verx_client
verx_client:start(Opt) -> {ok, Ref} | {error, posix()}
Types Opt = [ Options ]
Options = {transport, Transport}
% Unix socket
| {path, unix_socket()}
% TCP and TLS
| {host, ip_address()}
| {port, uint16()}
% TLS
| {cacert, path()}
| {cert, path()}
| {key, path()}
| {depth, integer()}
| {password, string()}
| {ciphers, ciphers()}
Transport = verx_client_unix
| verx_client_tcp
| verx_client_tls
RPC transport layer, supports Unix sockets, TCP and TLS (IPv4
and IPV6).
Options depend on the underlying transport mechanism.
verx_client:stop(Ref) -> ok
Closes the transport socket.
verx_client:recv(Ref) -> {ok, Buf} | {error, posix()}
Types Ref = verx_transport()
Buf = [binary()]
Returns streamed data. The stream must first be prepared
by making the appropriate remote protocol call, e.g.,
verx:domain_snapshot/2.
verx_client_unix
verx_client_tcp
verx_client_tls
verx_rpc
EXAMPLES
OPEN A CONNECTION TO LIBVIRTD
% Connect to the libvirtd socket
{ok, Ref} = verx_client:start(),
% libvirt remote protocol open message
% by default to qemu:///system
ok = verx:connect_open(Ref),
% send a close message
ok = verx:connect_close(Ref),
% send a remote protocol open message
% connecting to lxc containers
ok = verx:connect_open(Ref, ["lxc:///", 0]),
% close and stop the transport
ok = verx:connect_close(Ref),
ok = verx_client:stop(Ref).
% open a TLS connection on the default port
CACert = "/tmp/cert/cacert.pem",
Cert = "/tmp/cert/clientcert.pem",
Key = "/tmp/cert/clientkey.pem",
{ok, Ref} = verx_client:start([
{transport, verx_client_tls},
{cacert, CACert},
{cert, Cert},
{key, Key}
]).
CREATING A DOMAIN
-module(crvm).
-export([file/0]).
file() ->
file("priv/example.xml").
file(Path) ->
% Connect to the libvirtd socket
{ok, Ref} = verx_client:start(),
% libvirt remote protocol open message
ok = verx:connect_open(Ref),
{ok, XML} = file:read_file(Path),
% Domain is defined but not running
{ok, [Domain]} = verx:domain_define_xml(Ref, [XML]),
% Start the VM
ok = verx:domain_create(Ref, [Domain]),
{ok, [Active] = verx:connect_num_of_domains(Ref),
io:format("Active Domains: ~p~n", [Active]),
% Send a protocol close message
ok = verx:connect_close(R),
% Close the socket
ok = verx_client:stop(R),
{ok, Domain}.
To list the VMs:
-module(lsvm).
-export([ls/0]).
ls() ->
{ok, Ref} = verx_client:start(),
ok = verx:connect_open(Ref),
{ok, [NumDef]} = verx:connect_num_of_defined_domains(Ref),
{ok, [NumRun]} = verx:connect_num_of_domains(Ref),
{ok, [Shutoff]} = verx:connect_list_defined_domains(Ref, [NumDef]),
{ok, [Running]} = verx:connect_list_domains(Ref, [NumRun]),
{ok, [{running, info(Ref, Running)},
{shutoff, info(Ref, Shutoff)}]}.
info(Ref, Domains) ->
[ begin
{ok, [{Name, UUID, Id}]} = verx:domain_lookup_by_id(Ref, [N]),
{Name, [{uuid, UUID}, {id, Id}]}
end || N <- Domains ].
To shutdown the VM:
% Get the domain resource
lookup(Ref, Id) when is_integer(Id) ->
{ok, [Domain]} = verx:domain_lookup_by_id(Ref, [Id]),
{ok, Domain};
lookup(Ref, Name) when is_binary(Name) ->
{ok, [Domain]} = verx:domain_lookup_by_name(Ref, [Name]),
{ok, Domain}.
halt(Ref, Domain) ->
% shutdown only works if acpid is installed in the VM
ok = verx:domain_shutdown(R, [Domain]),
verx:domain_destroy(Ref, [Domain]).
To remove the VM, undefine it:
verx:domain_undefine(Ref, [Domain])
SUSPENDING AND RESUMING A DOMAIN
This example provides the Erlang equivalent of a Python script to manipulate a running domain. The example was taken from:
http://www.ibm.com/developerworks/linux/library/l-libvirt/
-module(ex6).
-export([start/0, states/2]).
start() ->
{ok, Ref} = verx_client:start(),
ok = verx:connect_open(Ref),
{ok, [Num]} = verx:connect_num_of_domains(Ref),
{ok, [Ids]} = verx:connect_list_domains(Ref, [Num]),
[ states(Ref, Id) || Id <- Ids ],
ok.
states(Ref, Id) ->
{ok, [Domain]} = verx:domain_lookup_by_id(Ref, [Id]),
% return value of domain_get_info from remote_protocol.x:
%
% struct remote_domain_get_info_ret {
% unsigned char state;
% unsigned hyper maxMem;
% unsigned hyper memory;
% unsigned short nrVirtCpu;
% unsigned hyper cpuTime;
% };
io:format("running: ~p~n", [verx:domain_get_info(Ref, [Domain])]),
ok = verx:domain_suspend(Ref, [Domain]),
io:format("suspended: ~p~n", [verx:domain_get_info(Ref, [Domain])]),
ok = verx:domain_resume(Ref, [Domain]),
io:format("resumed: ~p~n", [verx:domain_get_info(Ref, [Domain])]),
ok = verx:domain_shutdown(Ref, [Domain]),
io:format("shutdown: ~p~n", [verx:domain_get_info(Ref, [Domain])]),
ok = verx:domain_destroy(Ref, [Domain]),
io:format("destroyed: ~p~n", [verx:domain_get_info(Ref, [Domain])]).
RETRIEVING HYPERVISOR INFORMATION
Here is some code to retrieve information about the hypervisor, similar to the example in the Ruby libvirt documentation (http://libvirt.org/ruby/examples/node_info.rb):
-module(node_info).
-export([start/0]).
start() ->
{ok, Ref} = verx_client:start(),
ok = verx:connect_open(Ref),
[ begin
Reply = case Proc of
{Call, Arg} -> verx:Call(Ref, Arg)
Related Skills
node-connect
347.6kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.6kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.6kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
