Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 259 lines (208 sloc) 7.28 KB
#!/usr/bin/env escript
%%%
%%% Command line interface using the libvirt remote protocol
%%%
-include_lib("kernel/include/file.hrl").
-include("$PWD/include/verx.hrl").
main(Opt) ->
% load verx
true = code:add_pathz(filename:dirname(escript:script_name())
++ "/../ebin"),
true = code:add_pathz(filename:dirname(escript:script_name())
++ "/../deps/procket/ebin"),
true = code:add_pathz(filename:dirname(escript:script_name())
++ "/../deps/uuid/ebin"),
% load srly for tcsetattr (optional)
code:add_pathz(filename:dirname(escript:script_name())
++ "/../deps/srly/ebin"),
call(Opt).
call(["capabilities" | Arg]) ->
Opt = getopt(Arg),
{ok, Ref} = connect(Opt),
rp(verx:get_capabilities(Ref));
call(["ctl-alt-del", Name | Arg]) ->
call(["send-key", Name, "29 56 111" | Arg]);
call(["create", Name | Arg]) ->
Opt = getopt(Arg),
{ok, Ref} = connect(Opt),
{ok, [Domain]} = case file:read_file(Name) of
{ok, XML} ->
verx:domain_define_xml(Ref, [XML]);
{error, enoent} ->
lookup(Ref, {domain, Name})
end,
rp(verx:domain_create(Ref, [Domain]));
call(["define", File | Arg]) ->
Opt = getopt(Arg),
{ok, Ref} = connect(Opt),
{ok, XML} = file:read_file(File),
rp(verx:domain_define_xml(Ref, [XML]));
call(["destroy", Name | Arg]) ->
Opt = getopt(Arg),
{ok, Ref} = connect(Opt),
{ok, [Domain]} = lookup(Ref, {domain, Name}),
rp(verx:domain_destroy(Ref, [Domain]));
call(["dumpxml", Name | Arg]) ->
Opt = getopt(Arg),
Flags = proplists:get_value("flags", Opt, 0),
{ok, Ref} = connect(Opt),
{ok, [Domain]} = lookup(Ref, {domain, Name}),
rp(verx:domain_get_xml_desc(Ref, [Domain, Flags]));
call(["list", "--all" | Arg]) ->
Opt = getopt(Arg),
{ok, Ref} = connect(Opt),
{ok, [NumDef]} = verx:num_of_defined_domains(Ref),
{ok, [NumRun]} = verx:num_of_domains(Ref),
{ok, [Shutoff]} = verx:list_defined_domains(Ref, [NumDef]),
{ok, [Running]} = verx:list_domains(Ref, [NumRun]),
rp({ok, [{running, domains(Ref, Running)},
{shutoff, domains(Ref, Shutoff)}]});
call(["list" | Arg]) ->
Opt = getopt(Arg),
{ok, Ref} = connect(Opt),
{ok, [N]} = verx:num_of_domains(Ref),
{ok, [Domains]} = verx:list_domains(Ref, [N]),
rp(domains(Ref, Domains));
call(["screenshot", Name | Arg]) ->
Opt = getopt(Arg),
Screen = proplists:get_value("screen", Opt, 0),
Flags = proplists:get_value("flags", Opt, 0),
File = proplists:get_value("file", Opt),
{ok, Ref} = connect(Opt),
{ok, [Domain]} = lookup(Ref, {domain, Name}),
{ok, [Mime]} = verx:domain_screenshot(Ref, [Domain, Screen, Flags]),
{ok, Buf} = verx_client:recvall(Ref),
File1 = case File of
undefined ->
Name ++ "_" ++ os:getpid() ++ mime_ext(Mime);
_ ->
File
end,
ok = file:write_file(File1, Buf),
rp({ok, Mime, File1});
call(["send", Name | Arg]) ->
{Opt, Cmd} = getopt(Arg),
{ok, Ref} = connect(Opt),
{ok, [Domain]} = lookup(Ref, {domain, Name}),
ok = verx:domain_open_console(Ref, [Domain, void, 0]),
rp(verx_client:send(Ref, [ list_to_binary([C, "\n"]) || C <- Cmd ]));
call(["send-key", Name, Keycode | Arg]) ->
Opt = getopt(Arg),
Codeset = list_to_integer(proplists:get_value("codeset", Opt, "0")),
Holdtime = list_to_integer(proplists:get_value("holdtime", Opt, "0")),
Flags = list_to_integer(proplists:get_value("flags", Opt, "0")),
{ok, Ref} = connect(Opt),
{ok, [Domain]} = lookup(Ref, {domain, Name}),
rp(verx:domain_send_key(Ref, [
Domain,
Codeset,
Holdtime,
[ list_to_integer(N) ||
N <- string:tokens(Keycode, " ") ],
Flags]
));
call(["shutdown", Name | Arg]) ->
Opt = getopt(Arg),
{ok, Ref} = connect(Opt),
{ok, [Domain]} = lookup(Ref, {domain, Name}),
rp(verx:domain_shutdown(Ref, [Domain]));
call(["undefine", Name | Arg]) ->
Opt = getopt(Arg),
{ok, Ref} = connect(Opt),
{ok, [Domain]} = lookup(Ref, {domain, Name}),
rp(verx:domain_undefine(Ref, [Domain]));
call(_) ->
Help = [
"capabilities",
"create",
"ctl-alt-del",
"define",
"destroy",
"dumpxml",
"list",
"list --all",
"screenshot",
"send",
"send-key",
"shutdown",
"undefine"
],
rp(Help),
halt(1).
%%-------------------------------------------------------------------------
%%% Internal functions
%%-------------------------------------------------------------------------
rp(Term) ->
io:format("~p~n", [Term]).
connect(Opt) ->
URI = proplists:get_value("uri", Opt, "qemu:///system"),
Transport = list_to_atom(proplists:get_value("transport", Opt,
"verx_client_unix")),
% Unix socket
Path = proplists:get_value("path", Opt, ?LIBVIRT_SOCK_PATH),
% TCP and TLS
Host = proplists:get_value("host", Opt, "127.0.0.1"),
Port = maybe_integer(proplists:get_value("port", Opt, default_port(Transport))),
% TLS
CACert = proplists:get_value("cacert", Opt, "/etc/pki/CA/cacert.pem"),
Cert = proplists:get_value("cert", Opt, "/etc/pki/libvirt/clientcert.pem"),
Key = proplists:get_value("key", Opt, "/etc/pki/libvirt/private/clientkey.pem"),
Depth = maybe_integer(proplists:get_value("depth", Opt, "1")),
{ok, Ref} = verx_client:start_link([
{transport, Transport},
{path, Path},
{host, Host},
{port, Port},
{cacert, CACert},
{cert, Cert},
{key, Key},
{depth, Depth}
]),
ok = verx:open(Ref, [URI, 0]),
{ok, Ref}.
getopt(Opts) ->
getopt(Opts, []).
getopt([], Acc) ->
lists:reverse(Acc);
getopt(["--" ++ Key, "--" ++ _ = Val | Rest], Acc) ->
getopt([Val|Rest], [{Key, true}|Acc]);
getopt(["--" ++ Key], Acc) ->
getopt([], [{Key, true}|Acc]);
getopt(["--" ++ Key, Val | Rest], Acc) ->
getopt(Rest, [{Key, Val}|Acc]);
getopt(Rest, Acc) ->
{lists:reverse(Acc), Rest}.
domains(Ref, Domains) ->
[ begin
{ok, [{Name, UUID, Id}]} = if
is_integer(N) -> verx:domain_lookup_by_id(Ref, [N]);
is_binary(N) -> verx:domain_lookup_by_name(Ref, [N])
end,
{Name, [{uuid, uuid:uuid_to_string(UUID)}, {id, Id}]}
end || N <- Domains ].
lookup(Ref, {domain, Name}) ->
Fun = [ fun() -> verx:domain_lookup_by_id(Ref, [list_to_integer(Name)]) end,
fun() -> verx:domain_lookup_by_name(Ref, [list_to_binary(Name)]) end,
fun() -> verx:domain_lookup_by_uuid(Ref, [uuid:string_to_uuid(Name)]) end ],
lookup_1(Fun).
lookup_1(Fun) ->
lookup_1(Fun, []).
lookup_1([], [{error, Error}|_]) ->
{error, Error};
lookup_1([Fun|Tail], Acc) ->
try Fun() of
{ok, Res} ->
{ok, Res};
{error, Error} ->
lookup_1(Tail, [{error, Error}|Acc])
catch
_:_ ->
lookup_1(Tail, Acc)
end.
maybe_integer(N) when is_integer(N) -> N;
maybe_integer(N) when is_list(N) -> list_to_integer(N).
default_port(verx_client_tcp) -> ?LIBVIRT_TCP_PORT;
default_port(verx_client_tls) -> ?LIBVIRT_TLS_PORT;
default_port(_) -> 0.
mime_ext(<<"image/x-portable-pixmap">>) -> ".ppm";
mime_ext(_) -> ".screen".
Something went wrong with that request. Please try again.