Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add support for virNodeDevice

  • Loading branch information...
commit f451e32d26391f051871be7202544ca36c5780ec 1 parent f54bdbe
Michael Santos authored
41 README.md
View
@@ -8,22 +8,19 @@ An (almost) pure Erlang libvirt binding is available here:
<https://github.com/msantos/erlang-libvirt-remote>
-libvirt-remote uses the (unsupported) libvirtd remote procotol over a
-Unix socket.
+libvirt-remote uses the libvirtd remote procotol over a socket (by
+default, a Unix socket).
## WARNING
- The libvirt API is massive, inconsistent and error prone. So consider
- this language binding to be incomplete, buggy and erratic.
+ The current implementation prevents the Erlang VM from being blocked
+ by calling all libvirt functions in a thread. If libvirt blocks,
+ the caller will receive an error immediately ({error, eagain}).
+ Only one call from Erlang into libvirt can be running at a time.
- The current implementation calls all libvirt functions in a thread so
- that the Erlang VM will not block. If libvirt blocks, the caller will
- receive an error immediately ({error, eagain}). This means only one
- call from a single Erlang VM into libvirt can be running at any time.
-
- These bindings have not been heavily tested, are still under
- development and will undergo many changes.
+ These bindings have only been lightly tested. It's still possible
+ that some of the functions may segfault.
## HOW TO BUILD IT
@@ -85,7 +82,7 @@ To call the same functions in Erlang:
This example dumps the XML of a defined (not running) domain.
- 1> {ok, Connect} = vert:virConnectOpen("qemu:///system"),
+ 1> {ok, Connect} = vert:virConnectOpen("qemu:///system").
{ok,{resource,connect,#Ref<0.0.0.30>,<<>>}}
2> {ok, [Host|_]} = vert:virConnectListDefinedDomains(Connect).
@@ -166,22 +163,6 @@ running domains. The example is taken from:
## TODO
-* review the Erlang interface
- * find examples of code in other languages using libvirt and discover
- how awkward the Erlang interface is by porting them
- * then of course document the interface
-
-* Carefully check the code. There are many copy/paste defects.
-
-* Allow the caller to kill the background thread if it is blocked
-
-* Figure out how to deal with backwards compatibility (functions
- unsupported in older versions of libvirt)
-
-* Generate as much of the code as possible automatically from the libvirt
- XML documentation file (another rewrite!)
+* allow caller to optionally run a libvirt operations in a new thread
-* Reduce macro usage, many of them obscure the logic (returning from
- functions, setting variables, ...). Rename macros to show intent, e.g.,
- VERTERR() should be something like RETURN\_ERR\_IF\_FALSE(), ISNULL()
- should be RETURN\_ERR\_IF\_NULL().
+* add remaining libvirt API functions
45 bin/vert
View
@@ -576,6 +576,43 @@ call(["secret-undefine", Name | Arg]) ->
{ok, Secret} = secret(Connect, Name),
rp(vert:virSecretUndefine(Secret));
+call(["nodedev-create", Name, File | Arg]) ->
+ Opt = getopt(Arg),
+ {ok, Connect} = connect(Opt),
+ {ok, Dev} = vert:lookup(Connect, {nodedevice, Name}),
+ {ok, XML} = file:read_file(File),
+ rp(vert:virNodeDeviceCreateXML(Dev, XML));
+
+call(["nodedev-destroy", Name | Arg]) ->
+ Opt = getopt(Arg),
+ {ok, Connect} = connect(Opt),
+ {ok, Dev} = vert:lookup(Connect, {nodedevice, Name}),
+ rp(vert:virNodeDeviceDestroy(Dev));
+
+call(["nodedev-dumpxml", Name | Arg]) ->
+ Opt = getopt(Arg),
+ Flags = proplists:get_value("flags", Opt, 0),
+ {ok, Connect} = connect(Opt),
+ {ok, Dev} = vert:lookup(Connect, {nodedevice, Name}),
+ rp(vert:virNodeDeviceGetXMLDesc(Dev, Flags));
+
+call(["nodedev-list" | Arg]) ->
+ Opt = getopt(Arg),
+ {ok, Connect} = connect(Opt),
+ rp(vert:virNodeListDevices(Connect));
+
+call(["nodedev-reattach", Name | Arg]) ->
+ Opt = getopt(Arg),
+ {ok, Connect} = connect(Opt),
+ {ok, Dev} = vert:lookup(Connect, {nodedevice, Name}),
+ rp(vert:virNodeDeviceReattach(Dev));
+
+call(["nodedev-reset", Name | Arg]) ->
+ Opt = getopt(Arg),
+ {ok, Connect} = connect(Opt),
+ {ok, Dev} = vert:lookup(Connect, {nodedevice, Name}),
+ rp(vert:virNodeDeviceReset(Dev));
+
call(_) ->
Help = [
"autostart",
@@ -609,6 +646,14 @@ call(_) ->
"net-undefine",
"net-uuid",
+ "nodedev-create",
+ "nodedev-destroy",
+ "nodedev-dettach",
+ "nodedev-dumpxml",
+ "nodedev-list",
+ "nodedev-reattach",
+ "nodedev-reset",
+
"nwfilter-define",
"nwfilter-dumpxml",
"nwfilter-list",
1  c_src/vert.c
View
@@ -85,6 +85,7 @@ load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info)
atom_domain = enif_make_atom(env, "domain");
atom_interface = enif_make_atom(env, "interface");
atom_network = enif_make_atom(env, "network");
+ atom_nodedevice = enif_make_atom(env, "nodedevice");
atom_nwfilter = enif_make_atom(env, "nwfilter");
atom_secret = enif_make_atom(env, "secret");
atom_storagepool = enif_make_atom(env, "storagepool");
2  c_src/vert.h
View
@@ -50,6 +50,7 @@ ERL_NIF_TERM atom_connect;
ERL_NIF_TERM atom_domain;
ERL_NIF_TERM atom_interface;
ERL_NIF_TERM atom_network;
+ERL_NIF_TERM atom_nodedevice;
ERL_NIF_TERM atom_nwfilter;
ERL_NIF_TERM atom_secret;
ERL_NIF_TERM atom_storagepool;
@@ -92,6 +93,7 @@ enum {
VERT_RES_FILTER,
VERT_RES_INTERFACE,
VERT_RES_NETWORK,
+ VERT_RES_NODEDEVICE,
VERT_RES_NWFILTER,
VERT_RES_SECRET,
VERT_RES_STORAGEPOOL,
15 c_src/vert_func.h
View
@@ -135,6 +135,21 @@ ErlNifFunc vert_funcs[] = {
{"virNetworkSetAutostart", 2, vert_virNetworkSetAutostart},
{"virNetworkUndefine", 1, vert_virNetworkUndefine},
+ /* nodedevice */
+ {"virNodeDeviceCreateXML", 3, vert_virNodeDeviceCreateXML},
+ {"virNodeDeviceDestroy", 1, vert_virNodeDeviceDestroy},
+ {"virNodeDeviceDettach", 1, vert_virNodeDeviceDettach},
+ {"virNodeDeviceGetName", 1, vert_virNodeDeviceGetName},
+ {"virNodeDeviceGetParent", 1, vert_virNodeDeviceGetParent},
+ {"virNodeDeviceGetXMLDesc", 2, vert_virNodeDeviceGetXMLDesc},
+ {"virNodeDeviceListCaps", 2, vert_virNodeDeviceListCaps},
+ {"virNodeDeviceLookupByName", 2, vert_virNodeDeviceLookupByName},
+ {"virNodeDeviceNumOfCaps", 1, vert_virNodeDeviceNumOfCaps},
+ {"virNodeDeviceReAttach", 1, vert_virNodeDeviceReAttach},
+ {"virNodeDeviceReset", 1, vert_virNodeDeviceReset},
+ {"virNodeListDevices", 4, vert_virNodeListDevices},
+ {"virNodeNumOfDevices", 3, vert_virNodeNumOfDevices},
+
/* nwfilter */
{"virNWFilterDefineXML", 2, vert_virNWFilterDefineXML},
{"virNWFilterGetName", 1, vert_virNWFilterGetName},
168 c_src/vert_nodedevice.c
View
@@ -0,0 +1,168 @@
+/* Copyright (c) 2012, Michael Santos <michael.santos@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "vert.h"
+#include "vert_util.h"
+
+
+VERT_FUN_CREATEXML(virNodeDeviceCreateXML, VERT_RES_NODEDEVICE, atom_nodedevice)
+VERT_FUN_INT_RES(virNodeDeviceDestroy, VERT_RES_NODEDEVICE)
+VERT_FUN_INT_RES(virNodeDeviceDettach, VERT_RES_NODEDEVICE)
+VERT_FUN_INT_RES(virNodeDeviceNumOfCaps, VERT_RES_NODEDEVICE)
+VERT_FUN_INT_RES(virNodeDeviceReAttach, VERT_RES_NODEDEVICE)
+VERT_FUN_INT_RES(virNodeDeviceReset, VERT_RES_NODEDEVICE)
+VERT_FUN_GETNAME(virNodeDeviceGetName, VERT_RES_NODEDEVICE)
+VERT_FUN_GETNAME(virNodeDeviceGetParent, VERT_RES_NODEDEVICE)
+VERT_FUN_GETXMLDESC(virNodeDeviceGetXMLDesc, VERT_RES_NODEDEVICE)
+VERT_FUN_LOOKUPBYNAME(virNodeDeviceLookupByName, VERT_RES_NODEDEVICE, atom_nodedevice)
+
+ ERL_NIF_TERM
+vert_virNodeNumOfDevices(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ VERT_RESOURCE *vp = NULL;
+ ErlNifBinary cap = {0};
+ u_int32_t flags = 0;
+
+ int n = 0;
+
+
+ VERT_GET_RESOURCE(0, vp, VERT_RES_CONNECT);
+ VERT_GET_IOLIST(1, cap);
+ VERT_GET_UINT(2, flags);
+
+ if (cap.size > 0)
+ VERT_BIN_APPEND_NULL(cap);
+
+ n = virNodeNumOfDevices(vp->res,
+ (cap.size == 0 ? NULL : (char *)cap.data),
+ flags);
+
+ VERTERR(n < 0);
+
+ return enif_make_tuple2(env,
+ atom_ok,
+ enif_make_int(env, n));
+}
+
+ ERL_NIF_TERM
+vert_virNodeListDevices(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ VERT_RESOURCE *vp = NULL;
+ ErlNifBinary cap = {0};
+ char **names = NULL;
+ int maxnames = 0;
+ u_int32_t flags = 0;
+
+ int n = 0;
+ int rv = 0;
+ ERL_NIF_TERM dev = {0};
+
+
+ VERT_GET_RESOURCE(0, vp, VERT_RES_CONNECT);
+ VERT_GET_IOLIST(1, cap);
+ VERT_GET_INT(2, maxnames);
+ VERT_GET_UINT(3, flags);
+
+ if (cap.size > 0)
+ VERT_BIN_APPEND_NULL(cap);
+
+ names = calloc(maxnames, sizeof(char *));
+
+ if (names == NULL)
+ return error_tuple(env, atom_enomem);
+
+ rv = virNodeListDevices(vp->res,
+ (cap.size == 0 ? NULL : (char *)cap.data),
+ names, maxnames, flags);
+
+ if (rv < 0)
+ goto ERR;
+
+ dev = enif_make_list(env, 0);
+ for (n = 0; n < rv; n++) {
+ dev = enif_make_list_cell(env,
+ enif_make_string(env, names[n], ERL_NIF_LATIN1),
+ dev);
+ }
+
+ free(names);
+
+ return enif_make_tuple2(env,
+ atom_ok,
+ dev);
+
+ERR:
+ free(names);
+ return verterr(env);
+}
+
+ ERL_NIF_TERM
+vert_virNodeDeviceListCaps(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
+{
+ VERT_RESOURCE *dp = NULL;
+ char **names = NULL;
+ int maxnames = 0;
+
+ int n = 0;
+ int rv = 0;
+ ERL_NIF_TERM cap = {0};
+
+
+ VERT_GET_RESOURCE(0, dp, VERT_RES_NODEDEVICE);
+ VERT_GET_INT(1, maxnames);
+
+ names = calloc(maxnames, sizeof(char *));
+
+ if (names == NULL)
+ return error_tuple(env, atom_enomem);
+
+ rv = virNodeDeviceListCaps(dp->res, names, maxnames);
+
+ if (rv < 0)
+ goto ERR;
+
+ cap = enif_make_list(env, 0);
+ for (n = 0; n < rv; n++) {
+ cap = enif_make_list_cell(env,
+ enif_make_string(env, names[n], ERL_NIF_LATIN1),
+ cap);
+ }
+
+ free(names);
+
+ return enif_make_tuple2(env,
+ atom_ok,
+ cap);
+
+ERR:
+ free(names);
+ return verterr(env);
+}
3  c_src/vert_util.c
View
@@ -133,6 +133,9 @@ vert_cleanup(ErlNifEnv *env, void *obj)
case VERT_RES_NETWORK:
(void)virNetworkFree(vp->res);
break;
+ case VERT_RES_NODEDEVICE:
+ (void)virNodeDeviceFree(vp->res);
+ break;
#if HAVE_NWFILTER
case VERT_RES_NWFILTER:
(void)virNWFilterFree(vp->res);
8 include/vert.hrl
View
@@ -54,6 +54,12 @@
res
}).
+%% virNodeDevicePtr resource
+-record(nodedevice, {
+ ref,
+ res
+ }).
+
%% virStoragePoolPtr resource
-record(storagepool, {
ref,
@@ -67,7 +73,7 @@
}).
%% virNWFilterPtr resource
--record(filter, {
+-record(nwfilter, {
ref,
res
}).
87 src/vert.erl
View
@@ -48,6 +48,20 @@
virNodeGetInfo/1,
virNodeGetSecurityModel/1,
+ virNodeDeviceCreateXML/2, virNodeDeviceCreateXML/3,
+ virNodeDeviceDestroy/1,
+ virNodeDeviceDettach/1,
+ virNodeDeviceGetName/1,
+ virNodeDeviceGetParent/1,
+ virNodeDeviceGetXMLDesc/1, virNodeDeviceGetXMLDesc/2,
+ virNodeDeviceListCaps/1, virNodeDeviceListCaps/2,
+ virNodeDeviceLookupByName/2,
+ virNodeDeviceNumOfCaps/1,
+ virNodeDeviceReAttach/1,
+ virNodeDeviceReset/1,
+ virNodeListDevices/1, virNodeListDevices/2, virNodeListDevices/4,
+ virNodeNumOfDevices/2, virNodeNumOfDevices/3,
+
virNetworkCreate/1,
virNetworkDefineXML/2,
virNetworkDestroy/1,
@@ -504,8 +518,20 @@ virSecretDefineXML(#resource{type = connect, res = Res}, XML, Flags) ->
%%% Node
%%-------------------------------------------------------------------------
-%virNodeNumOfDevices(Conn, Cap, Flags) ->
-%virNodeListDevices(Conn, Cap, Names, Maxnames, Flags) ->
+virNodeNumOfDevices(Conn, Cap) ->
+ virNodeNumOfDevices(Conn, Cap, 0).
+virNodeNumOfDevices(#resource{type = connect, res = Res}, Cap, Flags) ->
+ call(virNodeNumOfDevices, [Res, Cap, Flags]).
+
+virNodeListDevices(Res) ->
+ virNodeListDevices(Res, <<>>).
+virNodeListDevices(Res, Cap) ->
+ {ok, Maxnames} = virNodeNumOfDevices(Res, Cap),
+ virNodeListDevices(Res, Cap, Maxnames, 0).
+virNodeListDevices(_Res, _Cap, 0, _Flags) ->
+ {ok, []};
+virNodeListDevices(#resource{type = connect, res = Res}, Cap, Maxnames, Flags) ->
+ call(virNodeListDevices, [Res, Cap, Maxnames, Flags]).
%% struct _virSecurityModel {
%% char model[VIR_SECURITY_MODEL_BUFLEN]; /* security model string */
@@ -573,18 +599,48 @@ virNodeGetCellsFreeMemory(#resource{type = connect, res = Res}, MaxCells) ->
%%% Node Device
%%-------------------------------------------------------------------------
-%virNodeDeviceReset(Dev) ->
-%virNodeDeviceReAttach(Dev) ->
-%virNodeDeviceNumOfCaps(Dev) ->
-%virNodeDeviceLookupByName(Conn, Name) ->
-%virNodeDeviceListCaps(Dev, Names, Maxnames) ->
-%virNodeDeviceGetXMLDesc(Dev, Flags) ->
-%virNodeDeviceGetParent(Dev) ->
-%virNodeDeviceGetName(Dev) ->
-%virNodeDeviceFree(Dev) ->
-%virNodeDeviceDettach(Dev) ->
-%virNodeDeviceDestroy(Dev) ->
-%virNodeDeviceCreateXML(Conn, XmlDesc, Flags) ->
+virNodeDeviceReset(#resource{type = nodedevice, res = Res}) ->
+ call(virNodeDeviceReset, [Res]).
+
+virNodeDeviceReAttach(#resource{type = nodedevice, res = Res}) ->
+ call(virNodeDeviceReAttach, [Res]).
+
+virNodeDeviceNumOfCaps(#resource{type = nodedevice, res = Res}) ->
+ call(virNodeDeviceNumOfCaps, [Res]).
+
+virNodeDeviceLookupByName(#resource{type = connect, res = Res}, Name) ->
+ call(virNodeDeviceLookupByName, [Res, Name]).
+
+virNodeDeviceListCaps(Dev) ->
+ {ok, Maxnames} = virNodeDeviceNumOfCaps(Dev),
+ virNodeDeviceListCaps(Dev, Maxnames).
+virNodeDeviceListCaps(_Dev, 0) ->
+ {ok, []};
+virNodeDeviceListCaps(#resource{type = nodedevice, res = Res}, Maxnames) ->
+ call(virNodeDeviceListCaps, [Res, Maxnames]).
+
+virNodeDeviceGetXMLDesc(Res) ->
+ virNodeDeviceGetXMLDesc(Res, 0).
+virNodeDeviceGetXMLDesc(#resource{type = nodedevice, res = Res}, Flags) ->
+ call(virNodeDeviceGetXMLDesc, [Res, Flags]).
+
+virNodeDeviceGetParent(#resource{type = nodedevice, res = Res}) ->
+ call(virNodeDeviceGetParent, [Res]).
+
+virNodeDeviceGetName(#resource{type = nodedevice, res = Res}) ->
+ call(virNodeDeviceGetName, [Res]).
+
+% Yes, it's spelt "dettach"
+virNodeDeviceDettach(#resource{type = nodedevice, res = Res}) ->
+ call(virNodeDeviceDettach, [Res]).
+
+virNodeDeviceDestroy(#resource{type = nodedevice, res = Res}) ->
+ call(virNodeDeviceDestroy, [Res]).
+
+virNodeDeviceCreateXML(Conn, XML) ->
+ virNodeDeviceCreateXML(Conn, XML, 0).
+virNodeDeviceCreateXML(#resource{type = nodedevice, res = Res}, XML, Flags) ->
+ call(virNodeDeviceCreateXML, [Res, XML, Flags]).
%%-------------------------------------------------------------------------
@@ -1139,6 +1195,9 @@ lookup(Connect, {domain, Name}) ->
fun() -> vert:virDomainLookupByUUIDString(Connect, Name) end,
fun() -> vert:virDomainLookupByName(Connect, Name) end ],
lookup_1(Fun);
+lookup(Connect, {nodedevice, Name}) ->
+ Fun = [ fun() -> vert:virNodeDeviceLookupByName(Connect, Name) end ],
+ lookup_1(Fun);
lookup(Connect, {network, Name}) ->
Fun = [ fun() -> vert:virNetworkLookupByUUIDString(Connect, Name) end,
fun() -> vert:virNetworkLookupByName(Connect, Name) end ],
Please sign in to comment.
Something went wrong with that request. Please try again.