{"id":13570945,"url":"https://github.com/msantos/verx","last_synced_at":"2025-04-15T17:47:40.433Z","repository":{"id":1378994,"uuid":"1332634","full_name":"msantos/verx","owner":"msantos","description":"Erlang implementation of the libvirtd remote protocol","archived":false,"fork":false,"pushed_at":"2023-02-01T13:26:50.000Z","size":466,"stargazers_count":59,"open_issues_count":1,"forks_count":11,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-03-28T23:11:19.768Z","etag":null,"topics":["libvirt"],"latest_commit_sha":null,"homepage":"","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/msantos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-02-05T21:22:17.000Z","updated_at":"2024-08-20T09:46:51.000Z","dependencies_parsed_at":"2023-02-17T05:45:41.412Z","dependency_job_id":null,"html_url":"https://github.com/msantos/verx","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msantos%2Fverx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msantos%2Fverx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msantos%2Fverx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msantos%2Fverx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/msantos","download_url":"https://codeload.github.com/msantos/verx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249124591,"owners_count":21216690,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["libvirt"],"created_at":"2024-08-01T14:00:56.767Z","updated_at":"2025-04-15T17:47:40.401Z","avatar_url":"https://github.com/msantos.png","language":"Erlang","readme":"Erlang implementation of the libvirtd remote protocol.\n\nFor an Erlang binding to the C libvirt interface, see:\n\n\u003chttps://github.com/msantos/erlang-libvirt\u003e\n\n\n## WARNING\n\nremote\\_protocol.x contains this warning:\n\n    (1) The protocol is internal and may change at any time, without\n    notice.  Do not use it.  Instead link to libvirt and use the remote\n    driver.\n\n\u003chttp://libvirt.org/git/?p=libvirt.git;a=blob_plain;f=src/remote/remote_protocol.x;hb=HEAD\u003e\n\nHowever, see the section _GENERATING THE REMOTE PROTOCOL MODULE_ below for\ninstructions on recompiling the XDR protocol spec if any changes occur.\n\nThe RPC protocol is documented here:\n\n\u003chttp://libvirt.org/internals/rpc.html\u003e\n\nFor the remote support documentation:\n\n\u003chttp://libvirt.org/remote.html\u003e\n\nThe version of remote\\_protocol.x used was taken from libvirt master\nat 1.3.1 (commit 8fd68675e2b5eed5b2aae636544a0a80f9fc70e9).\n\n## HOW TO BUILD IT\n\n    make\n\nSee _GENERATING THE REMOTE PROTOCOL MODULE_ to rebuild the XDR protocol\nparser.\n\n## TESTING EVERYTHING WORKS\n\nTo quickly test everything works, the libvirtd test driver can be used\nwith `bin/verx`, an escript that provides a simple command line interface\nto the verx library.\n\nYou'll have to set up the ERL\\_LIBS environment variable first, e.g.,\nif verx is checked out in ~/src:\n\n    export ERL_LIBS=$ERL_LIBS:~/src\n\nThen run:\n\n    bin/verx\n\nTo list the test VMs:\n\n    bin/verx list --uri test:///default\n\nTo retrieve the test virtual machine configuration:\n\n    bin/verx dumpxml test --uri test:///default\n\nTo create the example VM (a no-op with the test driver):\n\n    bin/verx create priv/example.xml --uri test:///default\n\nTo see all the VMs (if you have TLS set up):\n\n    bin/verx list --all --transport verx_client_tls --uri test:///default\n\nTo screenshot the test VM:\n\n    bin/verx screenshot test --uri test:///default\n\nThe test driver will return an error if console access is requested. To\nconnect to an actual VM's console using the Unix transport:\n\n    bin/verx console localvm # control-C to exit\n\n## HOW TO USE IT\n\n## libvirt documentation\n\nSee \u003chttp://libvirt.org/html/libvirt-libvirt.html\u003e\n\n### DATA TYPES\n\n    verx_transport()\n\n        Reference to the underlying transport and transport handler.\n\n    unix_socket() = string() | binary()\n\n        Path to Unix socket.\n\n### verx\n\n\n    verx:Call(Ref) -\u003e ok | {ok, Payload} | {error, Error}\n    verx:Call(Ref, Arg) -\u003e ok | {ok, Payload} | {error, Error}\n\n        Types   Call = [connect_open, connect_close, connect_list_domain, ...]\n                Ref = verx_transport()\n                Arg = [remote_protocol_args()]\n                Payload = [remote_protocol_ret()]\n                Error = [ posix() | libvirt() ]\n\n    verx has a large number of functions (283). See verx.erl or the\n    exports in verx:module_info() for a list.\n\n    Understanding the arguments for a remote protocol call takes some\n    work.  For example, for verx:domain_define_xml/2, here are some\n    places to look at:\n\n        * check verx.erl for the arity\n\n        * check remote_protocol_xdr.erl for the argument format. The\n          parsing function is prefaced with \"enc_remote_\" and ends with\n          \"_args\":\n\n            enc_remote_domain_define_xml_args/1\n\n        * check the XDR protocol file, remote_protocol.x:\n\n            struct remote_domain_define_xml_args {\n                remote_nonnull_string xml;\n            };\n\n        * look at the libvirt documentation. Generally the libvirt\n          counterpart is camelcased and prefaced with \"vir\":\n\n            virDomainDefineXML\n\n    Similarly, for the call return values, search for the\n    suffix \"_ret\", e.g., dec_remote_domain_define_xml_ret and\n    remote_domain_define_xml_ret.\n\n### verx\\_client\n\n    verx_client:start(Opt) -\u003e {ok, Ref} | {error, posix()}\n\n        Types   Opt = [ Options ]\n                Options = {transport, Transport}\n\n                      % Unix socket\n                    | {path, unix_socket()}\n\n                      % TCP and TLS\n                    | {host, ip_address()}\n                    | {port, uint16()}\n\n                      % TLS\n                    | {cacert, path()}\n                    | {cert, path()}\n                    | {key, path()}\n                    | {depth, integer()}\n                    | {password, string()}\n                    | {ciphers, ciphers()}\n\n                Transport = verx_client_unix\n                    | verx_client_tcp\n                    | verx_client_tls\n\n        RPC transport layer, supports Unix sockets, TCP and TLS (IPv4\n        and IPV6).\n\n        Options depend on the underlying transport mechanism.\n\n    verx_client:stop(Ref) -\u003e ok\n\n        Closes the transport socket.\n\n    verx_client:recv(Ref) -\u003e {ok, Buf} | {error, posix()}\n\n        Types   Ref = verx_transport()\n                Buf = [binary()]\n\n        Returns streamed data. The stream must first be prepared\n        by making the appropriate remote protocol call, e.g.,\n        verx:domain_snapshot/2.\n\n### verx\\_client\\_unix\n\n### verx\\_client\\_tcp\n\n### verx\\_client\\_tls\n\n### verx\\_rpc\n\n## EXAMPLES\n\n### OPEN A CONNECTION TO LIBVIRTD\n\n    % Connect to the libvirtd socket\n    {ok, Ref} = verx_client:start(),\n\n    % libvirt remote protocol open message\n    % by default to qemu:///system\n    ok = verx:connect_open(Ref),\n\n    % send a close message\n    ok = verx:connect_close(Ref),\n\n    % send a remote protocol open message\n    %  connecting to lxc containers\n    ok = verx:connect_open(Ref, [\"lxc:///\", 0]),\n\n    % close and stop the transport\n    ok = verx:connect_close(Ref),\n    ok = verx_client:stop(Ref).\n\n    % open a TLS connection on the default port\n    CACert = \"/tmp/cert/cacert.pem\",\n    Cert = \"/tmp/cert/clientcert.pem\",\n    Key = \"/tmp/cert/clientkey.pem\",\n\n    {ok, Ref} = verx_client:start([\n            {transport, verx_client_tls},\n            {cacert, CACert},\n            {cert, Cert},\n            {key, Key}\n            ]).\n\n### CREATING A DOMAIN\n\n    -module(crvm).\n    -export([file/0]).\n\n    file() -\u003e\n        file(\"priv/example.xml\").\n    file(Path) -\u003e\n        % Connect to the libvirtd socket\n        {ok, Ref} = verx_client:start(),\n\n        % libvirt remote protocol open message\n        ok = verx:connect_open(Ref),\n\n        {ok, XML} = file:read_file(Path),\n\n        % Domain is defined but not running\n        {ok, [Domain]} = verx:domain_define_xml(Ref, [XML]),\n\n        % Start the VM\n        ok = verx:domain_create(Ref, [Domain]),\n\n        {ok, [Active] = verx:connect_num_of_domains(Ref),\n        io:format(\"Active Domains: ~p~n\", [Active]),\n\n        % Send a protocol close message\n        ok = verx:connect_close(R),\n\n        % Close the socket\n        ok = verx_client:stop(R),\n\n        {ok, Domain}.\n\nTo list the VMs:\n\n    -module(lsvm).\n    -export([ls/0]).\n\n    ls() -\u003e\n        {ok, Ref} = verx_client:start(),\n        ok = verx:connect_open(Ref),\n\n        {ok, [NumDef]} = verx:connect_num_of_defined_domains(Ref),\n\n        {ok, [NumRun]} = verx:connect_num_of_domains(Ref),\n\n        {ok, [Shutoff]} = verx:connect_list_defined_domains(Ref, [NumDef]),\n        {ok, [Running]} = verx:connect_list_domains(Ref, [NumRun]),\n\n        {ok, [{running, info(Ref, Running)},\n                 {shutoff, info(Ref, Shutoff)}]}.\n\n    info(Ref, Domains) -\u003e\n        [ begin\n            {ok, [{Name, UUID, Id}]} = verx:domain_lookup_by_id(Ref, [N]),\n            {Name, [{uuid, UUID}, {id, Id}]}\n          end || N \u003c- Domains ].\n\nTo shutdown the VM:\n\n    % Get the domain resource\n    lookup(Ref, Id) when is_integer(Id) -\u003e\n        {ok, [Domain]} = verx:domain_lookup_by_id(Ref, [Id]),\n        {ok, Domain};\n\n    lookup(Ref, Name) when is_binary(Name) -\u003e\n        {ok, [Domain]} = verx:domain_lookup_by_name(Ref, [Name]),\n        {ok, Domain}.\n\n    halt(Ref, Domain) -\u003e\n        % shutdown only works if acpid is installed in the VM\n        ok = verx:domain_shutdown(R, [Domain]),\n        verx:domain_destroy(Ref, [Domain]).\n\nTo remove the VM, undefine it:\n\n        verx:domain_undefine(Ref, [Domain])\n\n### SUSPENDING AND RESUMING A DOMAIN\n\nThis example provides the Erlang equivalent of a Python script to\nmanipulate a running domain. The example was taken from:\n\n\u003chttp://www.ibm.com/developerworks/linux/library/l-libvirt/\u003e\n\n    -module(ex6).\n\n    -export([start/0, states/2]).\n\n    start() -\u003e\n        {ok, Ref} = verx_client:start(),\n        ok = verx:connect_open(Ref),\n\n        {ok, [Num]} = verx:connect_num_of_domains(Ref),\n        {ok, [Ids]} = verx:connect_list_domains(Ref, [Num]),\n\n        [ states(Ref, Id) || Id \u003c- Ids ],\n        ok.\n\n    states(Ref, Id) -\u003e\n        {ok, [Domain]} = verx:domain_lookup_by_id(Ref, [Id]),\n\n        % return value of domain_get_info from remote_protocol.x:\n        %\n        % struct remote_domain_get_info_ret {\n        %   unsigned char state;\n        %   unsigned hyper maxMem;\n        %   unsigned hyper memory;\n        %   unsigned short nrVirtCpu;\n        %   unsigned hyper cpuTime;\n        % };\n\n        io:format(\"running: ~p~n\", [verx:domain_get_info(Ref, [Domain])]),\n\n        ok = verx:domain_suspend(Ref, [Domain]),\n        io:format(\"suspended: ~p~n\", [verx:domain_get_info(Ref, [Domain])]),\n\n        ok = verx:domain_resume(Ref, [Domain]),\n        io:format(\"resumed: ~p~n\", [verx:domain_get_info(Ref, [Domain])]),\n\n        ok = verx:domain_shutdown(Ref, [Domain]),\n        io:format(\"shutdown: ~p~n\", [verx:domain_get_info(Ref, [Domain])]),\n\n        ok = verx:domain_destroy(Ref, [Domain]),\n        io:format(\"destroyed: ~p~n\", [verx:domain_get_info(Ref, [Domain])]).\n\n\n### RETRIEVING HYPERVISOR INFORMATION\n\nHere is some code to retrieve information about the hypervisor,\nsimilar to the example in the Ruby libvirt documentation\n(\u003chttp://libvirt.org/ruby/examples/node_info.rb\u003e):\n\n    -module(node_info).\n    -export([start/0]).\n\n    start() -\u003e\n        {ok, Ref} = verx_client:start(),\n        ok = verx:connect_open(Ref),\n\n        [ begin\n                Reply = case Proc of\n                    {Call, Arg} -\u003e verx:Call(Ref, Arg);\n                    Call -\u003e verx:Call(Ref)\n                end,\n                result(Proc, Reply)\n          end || Proc \u003c- [\n                    node_get_info,\n                    {node_get_cells_free_memory, [0, 100]},\n                    connect_get_version,\n                    connect_get_lib_version,\n                    connect_get_hostname,\n                    connect_get_uri,\n                    node_get_free_memory,\n                    node_get_security_model,\n                    connect_is_secure,\n                    connect_get_capabilities\n                    ] ],\n\n        ok = verx:connect_close(Ref),\n        verx_client:stop(Ref).\n\n    result(Call, {ok, N}) -\u003e\n        error_logger:info_report([{call, Call}] ++ N);\n    result(Call, {error, _Error} = N) -\u003e\n        error_logger:error_report([{call, Call}] ++ N).\n\n### SYSTEM CONSOLE\n\nThe VM system console can be accessed using any of the transports.\n\n    % Connect to libvirtd using the Unix socket\n    1\u003e {ok, Ref} = verx_client:start().\n    {ok,\u003c0.43.0\u003e}\n\n    % Open a remote protocol session to the Linux containers hypervisor\n    2\u003e verx:connect_open(Ref, [\"lxc:///\", 0]).\n    ok\n\n    % Get a domain reference\n    3\u003e {ok, [Domain]} = verx:domain_lookup_by_name(Ref, [\u003c\u003c\"lxc-1\"\u003e\u003e]).\n    {ok,[{\u003c\u003c\"lxc-1\"\u003e\u003e,\n         \u003c\u003c150,162,91,134,54,66,203,130,29,224,244,242,121,45,5,118\u003e\u003e,\n           19586}]}\n\n    % Open the console. The arguments are:\n    %   Domain\n    %   Device name : string() or void (NULL)\n    %   Flags : integer()\n    4\u003e verx:domain_open_console(Ref, [Domain, void, 0]).\n\n    % Send a message to the console, check the results with\n    % flush()\n\n    % Start up Erlang ...\n    5\u003e verx_client:send(Ref, [\u003c\u003c\"erl\\n\"\u003e\u003e]).\n\n    6\u003e verx_client:send(Ref, [\u003c\u003c\"spawn(fun() -\u003e io:format(\\\"Erlang process in an Erlang VM in a Linux VM in an Erlang process!\\\") end).\\n\"\u003e\u003e]).\n\n    % Receive the message back from the console\n    8\u003e verx_client:recv(Ref).\n    {ok,\u003c\u003c\"Erlang process in an Erlang VM in a Linux VM in an Erlang process!\"\u003e\u003e}\n\n### TAKING A SCREENSHOT\n\nAn example of using the libvirt stream interface to capture an image of\nthe VM console:\n\n    -module(ss).\n    -export([host/1]).\n\n    host(Name) when is_list(Name) -\u003e\n        host(list_to_binary(Name));\n    host(Name) when is_binary(Name) -\u003e\n        {ok, Ref} = verx_client:start(),\n        ok = verx:connect_open(Ref),\n\n        {ok, [Domain]} = verx:domain_lookup_by_name(Ref, [Name]),\n\n        Screen = 0,\n        Flags = 0,\n\n        {ok, [Mime]} = verx:domain_screenshot(Ref, [Domain, Screen, Flags]),\n\n        Ext = case Mime of\n            \u003c\u003c\"image/x-portable-pixmap\"\u003e\u003e -\u003e \u003c\u003c\".ppm\"\u003e\u003e;\n            _ -\u003e \u003c\u003c\".screen\"\u003e\u003e\n        end,\n\n        {ok, Buf} = verx_client:recvall(Ref),\n\n        File = \u003c\u003cName/binary, Ext/binary\u003e\u003e,\n        ok = file:write_file(File, Buf),\n\n        {ok, Mime, File}.\n\n## CREATING LINUX CONTAINERS\n\nThis example will generate many Linux containers (LXC) attached to a\nbridge (br0).\n\n    -module(clxc).\n    -export([start/2, start/3, create/2, template/2]).\n\n    start(Prefix, Num) -\u003e\n        {ok, Ref} = verx_client:start(),\n        ok = verx:connect_open(Ref, [\"lxc:///\", 0]),\n        start(Ref, Prefix, Num).\n\n    start(_Ref, _Prefix, 0) -\u003e\n        ok;\n\n    start(Ref, Prefix, Num) -\u003e\n        Name = Prefix ++ integer_to_list(Num),\n\n        \u003c\u003cBytes:3/bytes, _/binary\u003e\u003e = erlang:md5(Name),\n        Macaddr = \"52:54:00:\" ++ string:join([ httpd_util:integer_to_hexlist(N)\n            || \u003c\u003cN:8\u003e\u003e \u003c= Bytes ], \":\"),\n\n        XML = template(Name, Macaddr),\n        ok = create(Ref, XML),\n\n        start(Ref, Prefix, Num-1).\n\n    create(Ref, XML) -\u003e\n        {ok, [Domain]} = verx:domain_define_xml(Ref, [XML]),\n        verx:domain_create(Ref, [Domain]).\n\n    template(Name, Macaddr) -\u003e\n    \"\u003cdomain type='lxc'\u003e\n        \u003cname\u003e\" ++ Name ++ \"\u003c/name\u003e\n        \u003cmemory\u003e102400\u003c/memory\u003e\n        \u003cos\u003e\n            \u003ctype\u003eexe\u003c/type\u003e\n            \u003cinit\u003e/bin/sh\u003c/init\u003e\n        \u003c/os\u003e\n        \u003cdevices\u003e\n            \u003cconsole type='pty'/\u003e\n            \u003cinterface type='bridge'\u003e\n                \u003cmac address='\" ++ Macaddr ++ \"'/\u003e\n                \u003csource bridge='br0'/\u003e\n            \u003c/interface\u003e\n        \u003c/devices\u003e\n    \u003c/domain\u003e\".\n\n## GENERATING THE REMOTE PROTOCOL MODULE\n\nTo create the remote\\_protocol\\_xdr.erl from a remote\\_protocol.x file:\n\n1. Copy remote\\_protocol.x to priv\n\n2. Run:\n\n        make clean; make\n\nIf there are any errors, read through `bin/mk_remote_protocol.escript`.\n\n## VERX CLIENT\n\n`verx` is a simple command line client similar to `virsh`. To use\n`verx`, the ERL\\_LIBS environment variable must point to the directory\n_containing_ the verx repository:\n\n    export ERL_LIBS=$ERL_LIBS:~/src\n    export PATH=$PATH:~/src/verx/bin\n\nRunning `verx` without any options will return the list of commands.\n\nAll `verx` commands can take some options:\n\n    --uri : URI supported by libvirt (default: qemu:///system)\n    --transport : (default: verx_client_unix)\n        verx_client_unix\n        verx_client_tcp\n        verx_client_tls\n\nFor TCP and TLS transports:\n\n    --host : hostname\n    --port : port\n\nFor the TLS transport:\n\n    --cacert : path to CA cert (default: /etc/pki/CA/cacert.pem)\n    --cert : path to client cert (default: /etc/pki/libvirt/clientcert.pem)\n    --depth : cert validation depth (default: 1)\n\nExamples:\n\n    # List all defined Qemu/KVM instances through the libvirtd Unix socket\n    verx list --all\n\n    # List running LXC instances\n    verx list --uri=lxc:///\n\n    # Dump the configuration of a KVM using the TLS transport over IPv6\n    verx dumpxml myvm --transport verx_client_tls --host ::1\n\n    # Access the console of a container over TLS/IPv6\n    # Use ctl-C to exit\n    verx console mylxc --uri lxc:/// --transport verx_client_tls --host ::1\n\n\n## TODO\n\n* verx\\_client\\_tls\n    * single byte received before packet (works if thrown away)\n","funding_links":[],"categories":["Erlang"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmsantos%2Fverx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmsantos%2Fverx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmsantos%2Fverx/lists"}