Define hello API function and data types

This is the third post in the series “HOWTO: Add libvirt support for a qemu command”

When designing an API for libvirt, the first step is to think about how your API will be used and establish its calling conventions. Our new API interacts with a domain (by issuing a monitor command) so we will follow libvirt convention and name it virDomainHello. The hello qemu monitor command returns a string so let’s do the same in libvirt. The first parameter should be a pointer to the domain (qemu instance). The exclaim and language parameters could be passed individually (as an integer and string). I chose to pass them in a structure to show how structures are handled by libvirt.

Once the API is defined, we add an entry into the driver structure so that individual hypervisors (including qemu) can register an implementation of it. The driver structure is statically initialized by each hypervisor driver so this patch updates all drivers to set the new field to NULL.

Finally, virDomainHello breaks the Python bindings generator and causes compilation to fail. We can fix this by telling the generator to skip our function for the time being.

Let’s walk through the patch.

diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 1cf9273..9fe724f 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -2507,6 +2507,15 @@ int virDomainOpenConsole(virDomainPtr dom,
                          virStreamPtr st,
                          unsigned int flags);
 
+typedef struct _virHelloParams virHelloParams;
+struct _virHelloParams {
+    char *lang;
+    int exclaim;
+};
+typedef virHelloParams *virHelloParamsPtr;
+char *                   virDomainHello          (virDomainPtr dom,
+                                                  virHelloParamsPtr params);
+
 #ifdef __cplusplus
 }
 #endif

This first part of the patch defines the top-level data types. We create a new structure type virHelloParams to wrap the arguments. Libvirt coding style requires typedefs for the structure and structure pointers. Then we define the API function itself. It takes a domain pointer and a pointer to our new parameters structure as arguments and returns a string.

diff --git a/python/generator.py b/python/generator.py
index 4fa4f65..569f4a8 100755
--- a/python/generator.py
+++ b/python/generator.py
@@ -166,6 +166,7 @@ def enum(type, name, value):
 functions_failed = []
 functions_skipped = [
     "virConnectListDomains",
+    "virDomainHello",
 ]
 
 skipped_modules = {
@@ -180,6 +181,7 @@ skipped_types = {
      'virConnectDomainEventIOErrorCallback': "No function types in python",
      'virConnectDomainEventGraphicsCallback': "No function types in python",
      'virEventAddHandleFunc': "No function types in python",
+     'virHelloParamsPtr': "Not implemented yet",
 }
 
 #######################################################################

Here we tell the Python API generator to ignore our function and parameters structure. This is a compile fix. We’ll implement the Python bindings in a later patch.

diff --git a/src/driver.h b/src/driver.h
index 286130a..b2fc0e5 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -515,6 +515,8 @@ typedef int
                                virStreamPtr st,
                                unsigned int flags);
 
+typedef char *
+    (*virDrvDomainHello)(virDomainPtr dom, virHelloParamsPtr params);
 
 /**
  * _virDriver:
@@ -639,6 +641,7 @@ struct _virDriver {
     virDrvDomainSnapshotDelete domainSnapshotDelete;
     virDrvQemuDomainMonitorCommand qemuDomainMonitorCommand;
     virDrvDomainOpenConsole domainOpenConsole;
+    virDrvDomainHello domainHello;
 };
 
 typedef int

Add our new API to the driver structure.

diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c
index deda372..28cc0e4 100644
--- a/src/esx/esx_driver.c
+++ b/src/esx/esx_driver.c
@@ -4675,6 +4675,7 @@ static virDriver esxDriver = {
     esxDomainSnapshotDelete,         /* domainSnapshotDelete */
     NULL,                            /* qemuDomainMonitorCommand */
     NULL,                            /* domainOpenConsole */
+    NULL,                            /* domainHello */
 };
 
 
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
index 3159e1b..1b3408f 100644
--- a/src/lxc/lxc_driver.c
+++ b/src/lxc/lxc_driver.c
@@ -2904,6 +2904,7 @@ static virDriver lxcDriver = {
     NULL, /* domainSnapshotDelete */
     NULL, /* qemuDomainMonitorCommand */
     lxcDomainOpenConsole, /* domainOpenConsole */
+    NULL, /* domainHello */
 };
 
 static virStateDriver lxcStateDriver = {
diff --git a/src/opennebula/one_driver.c b/src/opennebula/one_driver.c
index 3146589..6e1867f 100644
--- a/src/opennebula/one_driver.c
+++ b/src/opennebula/one_driver.c
@@ -832,6 +832,7 @@ static virDriver oneDriver = {
     NULL, /* domainSnapshotDelete */
     NULL, /* qemuDomainMonitorCommand */
     NULL, /* domainOpenConsole */
+    NULL, /* domainHello */
 };
 
 static virStateDriver oneStateDriver = {
diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c
index fb30c37..0b9c270 100644
--- a/src/openvz/openvz_driver.c
+++ b/src/openvz/openvz_driver.c
@@ -1654,6 +1654,7 @@ static virDriver openvzDriver = {
     NULL, /* domainSnapshotDelete */
     NULL, /* qemuDomainMonitorCommand */
     NULL, /* domainOpenConsole */
+    NULL, /* domainHello */
 };
 
 int openvzRegister(void) {
diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c
index 51f9ff6..dcb848e 100644
--- a/src/phyp/phyp_driver.c
+++ b/src/phyp/phyp_driver.c
@@ -4055,6 +4055,7 @@ static virDriver phypDriver = {
     NULL,                       /* domainSnapshotDelete */
     NULL,                       /* qemuMonitorCommand */
     NULL, /* domainOpenConsole */
+    NULL, /* domainHello */
 };
 
 static virStorageDriver phypStorageDriver = {
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index af897ad..40bb4c0 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -7191,6 +7191,7 @@ static virDriver qemuDriver = {
     qemuDomainSnapshotDelete, /* domainSnapshotDelete */
     qemuDomainMonitorCommand, /* qemuDomainMonitorCommand */
     qemuDomainOpenConsole, /* domainOpenConsole */
+    NULL, /* domainHello */
 };
 
 
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index b05bbcb..87a68a6 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -11202,6 +11202,7 @@ static virDriver remote_driver = {
     remoteDomainSnapshotDelete, /* domainSnapshotDelete */
     remoteQemuDomainMonitorCommand, /* qemuDomainMonitorCommand */
     remoteDomainOpenConsole, /* domainOpenConsole */
+    NULL, /* domainHello */
 };
 
 static virNetworkDriver network_driver = {
diff --git a/src/test/test_driver.c b/src/test/test_driver.c
index 17f5ad9..57edda0 100644
--- a/src/test/test_driver.c
+++ b/src/test/test_driver.c
@@ -5447,6 +5447,7 @@ static virDriver testDriver = {
     NULL, /* domainSnapshotDelete */
     NULL, /* qemuDomainMonitorCommand */
     NULL, /* domainOpenConsole */
+    NULL, /* domainHello */
 };
 
 static virNetworkDriver testNetworkDriver = {
diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c
index 5514487..99d2f7a 100644
--- a/src/uml/uml_driver.c
+++ b/src/uml/uml_driver.c
@@ -2249,6 +2249,7 @@ static virDriver umlDriver = {
     NULL, /* domainSnapshotDelete */
     NULL, /* qemuDomainMonitorCommand */
     umlDomainOpenConsole, /* domainOpenConsole */
+    NULL, /* domainHello */
 };
 
 static int
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index 8bd27dd..f532923 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -8647,6 +8647,7 @@ virDriver NAME(Driver) = {
     vboxDomainSnapshotDelete, /* domainSnapshotDelete */
     NULL, /* qemuDomainMonitorCommand */
     NULL, /* domainOpenConsole */
+    NULL, /* domainHello */
 };
 
 virNetworkDriver NAME(NetworkDriver) = {
diff --git a/src/vmware/vmware_driver.c b/src/vmware/vmware_driver.c
index b5e416b..3c14fbb 100644
--- a/src/vmware/vmware_driver.c
+++ b/src/vmware/vmware_driver.c
@@ -1007,6 +1007,7 @@ static virDriver vmwareDriver = {
     NULL,                       /* domainSnapshotDelete */
     NULL,                       /* qemuDomainMonitorCommand */
     NULL,                       /* domainOpenConsole */
+    NULL,                       /* domainHello */
 };
 
 int
diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c
index bf422e6..ff2b172 100644
--- a/src/xen/xen_driver.c
+++ b/src/xen/xen_driver.c
@@ -2141,6 +2141,7 @@ static virDriver xenUnifiedDriver = {
     NULL, /* domainSnapshotDelete */
     NULL, /* qemuDomainMonitorCommand */
     xenUnifiedDomainOpenConsole, /* domainOpenConsole */
+    NULL, /* domainHello */
 };
 
 /**

It’s a bit tedious, but due to static initialization, we must add a placeholder to every driver.

Up next… Add hello to the public libvirt API

Advertisements
Posted in Uncategorized | Leave a comment

Hello World in Qemu

This is the second post in the series “HOWTO: Add libvirt support for a qemu command”

This first patch shows the implementation of the ‘hello’ monitor command in qemu. The command is simple — merely printing a greeting in English or Spanish and with punctuation if requested. The command synopsis looks like this:

hello [-e] [language]

  • if -e is specified, the greeting will be punctuated with exclamation marks
  • language may be specified as either ‘english or ‘spanish’. If ommitted, english is used.

Next we will walk through the patch to explain the implementation. You can view the patch here.

commit 468da14f29552f415413864ea848310c9ad6efe9
Author: Adam Litke <agl@us.ibm.com>
Date:   Thu Apr 7 08:25:58 2011 -0500

    hello: Add a simple hello world monitor command to qemu
    
    Signed-off-by: Adam Litke <agl@us.ibm.com>

diff --git a/hmp-commands.hx b/hmp-commands.hx
index 372bef4..dd2ad7d 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -10,6 +10,21 @@ STEXI
 ETEXI
 
     {
+        .name       = "hello",
+        .args_type  = "exclaim:-e,lang:s?",
+        .params     = "[-e] [language]",
+        .help       = "Say hello",
+        .user_print = monitor_print_hello,
+        .mhandler.cmd_new = do_hello,
+    },
+
+STEXI
+@item hello
+@findex hello
+Say hello to QEMU.
+ETEXI
+
+    {
         .name       = "help|?",
         .args_type  = "name:s?",
         .params     = "[cmd]",

This first part of the patch declares the command for the human monitor. hmp_commands.hx contains a list of these command declarations. Here you set the command name, argument synopsis, help text, command handler, and a function that will be called to print the results.

diff --git a/monitor.c b/monitor.c
index 7fc311d..957ea01 100644
--- a/monitor.c
+++ b/monitor.c
@@ -360,6 +360,7 @@ static void monitor_json_emitter(Monitor *mon, const QObject *data)
     qstring_append_chr(json, '\n');
     monitor_puts(mon, qstring_get_str(json));
 
+    printf("-> %s\n", qstring_get_str(json));
     QDECREF(json);
 }
 

I included this bit of debugging code for convenience. This will echo all JSON responses from the qmp monitor to standard out. I found this very helpful when debugging libvirt/qemu interactions.

@@ -1004,6 +1005,47 @@ static void do_info_trace_events(Monitor *mon)
 }
 #endif
 
+static void monitor_print_hello(Monitor *mon, const QObject *data)
+{
+    QDict *qdict;
+
+    qdict = qobject_to_qdict(data);
+    if (!qdict_haskey(qdict, "response"))
+        return;
+
+    monitor_printf(mon, "%s\n", qdict_get_str(qdict, "response"));
+}
+
+/**
+ * do_hello(): Implement the sample hello command
+ */
+static int do_hello(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+    int exclaim = qdict_get_try_bool(qdict, "exclaim", 0);
+    const char *lang = qdict_get_try_str(qdict, "lang");
+    char *resp;
+    int rc;
+
+    if (!lang || !strcmp(lang, "english"))
+	    rc = asprintf(&resp, "%s%s", "Hello world", exclaim ? "!" : "");
+    else if (!strcmp(lang, "spanish"))
+        rc = asprintf(&resp, "%s%s%s", exclaim ? "¡" : "",
+                 "Hola mundo", exclaim ? "!" : "");
+    else {
+        qerror_report(QERR_INVALID_PARAMETER, "lang");
+        return -1;
+    }
+
+    if (rc == -1) {
+        qerror_report(QERR_UNDEFINED_ERROR);
+        return -1;
+    }
+
+    *ret_data = qobject_from_jsonf("{ 'response': %s }", resp);
+    free(resp);
+    return 0;
+}
+
 /**
  * do_quit(): Quit QEMU execution
  */

Shown above are the print handler and command handler. The print handler takes the command result (as a QObject) and emits it as a human readable string to the monitor. The command handler is where the command itself is implemented. Most commands will do something interesting and virtualization-related here. Our command simply parses its arguments and builds a return object based on them.

@@ -4396,6 +4438,8 @@ static void handle_user_command(Monitor *mon, const char *cmdline)
 
     qdict = qdict_new();
 
+    printf("Got command: %s\n", cmdline);
+
     cmd = monitor_parse_command(mon, cmdline, qdict);
     if (!cmd)
         goto out;

This is more debugging code. The line above causes all commands received via the ‘hmp’ monitor to be echoed to standard out.

@@ -5024,6 +5068,7 @@ static void monitor_control_read(void *opaque, const uint8_t *buf, int size)
 
     cur_mon = opaque;
 
+    printf("%*c", size, (char)*buf);
     json_message_parser_feed(&cur_mon->mc->parser, (const char *) buf, size);
 
     cur_mon = old_mon;

Debug statememt similar to the one above, but commands received via the ‘qmp’ monitor are echoed.

diff --git a/qmp-commands.hx b/qmp-commands.hx
index df40a3d..e60aec8 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -61,6 +61,37 @@ refer to the QMP specification for more details on error responses.
 EQMP
 
     {
+        .name       = "hello",
+        .args_type  = "exclaim:-e,lang:s?",
+        .params     = "[-e] [language]",
+        .help       = "Say hello",
+        .mhandler.cmd_new = do_hello,
+    },
+
+SQMP
+hello
+-----
+
+Say hello.
+
+Arguments:
+
+- exclaim: Greet with more enthusiasm (json-bool, optional)
+- lang: Select greeting language (json-string, optional)
+
+Note: The lang argument defaults to english
+
+Returns a JSON object containing:
+- response: A greeting/response (json-string)
+
+Example:
+
+-> { "execute": "hello", "arguments": { "lang": "spanish" } }
+<- { "return": { "response": "Hola Mundo" } }
+
+EQMP
+
+    {
         .name       = "quit",
         .args_type  = "",
         .params     = "",

This block of code declares the hello command in the qmp monitor. Similar to the definition in hmp-commands.hx, this specifies the command name, arguments synopsis, documentation, and the actual command handler. Notice that both the hmp and qmp version use the same handler. This is good.

This concludes the code walkthrough for the ‘hello’ qemu monitor command. In the next installment, we will begin extending libvirt to support this command.

Up next… Define hello API function and data types

Posted in Uncategorized | Leave a comment

HOWTO: Add libvirt support for a qemu command

If you are a qemu developer, one task you’ll inevitably do is add a new command to qemu. If you want your command to be usable by tools such as virt-manager, you will also need to add support for your command to libvirt. The first time I did this, I found the process quite daunting (due to the number of abstraction layers). In this series of posts, I will guide you through the process of supporting a fictitious hello qemu command in libvirt.

Continue reading

Posted in libvirt | Leave a comment

A crazy idea to improve QEMU/Libvirt interoperability

On two separate occasions I have had the opportunity to enable a new QEMU API in libvirt. The first API is a memory statistics reporting interface that can be useful for managing guest memory ballooning. (For the curious, this is virDomainMemoryStats in libvirt and ‘info balloon’ in qemu). The second, a work-in-progress, is a mechanism for locally populating a disk image from a backing image. (See the libvirt list). These experiences have shown me that there is definitely room for improvement in the way we approach libvirt-qemu interoperability.

I believe the problem stems from differences in the way libvirt and qemu view the virtualization ecosystem. (Disclaimer: I do not pretend to speak for everyone, these are my observations). Qemu/kvm developers are narrowly focused on solving problems with and adding features to qemu. They do not think about libvirt or other hypervisors too much and therefore APIs get added to qemu that are not hypervisor agnostic and that do not easily mesh with the libvirt model.

The libvirt community has developed an API that rides above the hypervisor level and aims to be as neutral as possible by creating abstractions where necessary. The primary focus is on end-user consumability of the libvirt interfaces with less regard to the underlying hypervisor implementations.

This dynamic has created two separate communities that are intently focused on their specific domains with a neglected gap between the two. As a consequence, the process for adding a new function to qemu and libvirt is something like this:

  1. Work in the qemu community to get a new feature and monitor commands upstream.
  2. Present the qemu API to the libvirt community with a strawman implementation.
  3. Negotiate changes to the API to make it fit into the libvirt model.
  4. Work those changes back into qemu.
  5. Repeat 3 & 4 until differences are resolved.
  6. Work in the libvirt community on a set of patches to implement the approved API.

The back and forth nature of the current situation is the fault of neither libvirt or qemu. It just means we are not talking soon enough. To solve this problem as a joint community we should try to build early communication into our processes. Before code is committed into either qemu or libvirt we ask: “Will this code require changes or extensions to the API between qemu and libvirt?” If so, a discussion on the matter should be had first and an agreement on the API reached prior to adding the feature. The agreed-upon API should be kept up to date in a specification document similar to that used for the virtio specification.

By talking about APIs earlier in the development process, we can bring the libvirt and qemu communities closer together, avoid wasting time on reimplementing features, spend less time reviewing multiple patch submissions, and provide the best experience for our users.

Posted in libvirt, qemu | Leave a comment

Don’t miss the KVM-Autotest Install Fest on April 14th

The KVM community will be hosting a KVM-Autotest Install Fest on Thursday, April 14. This is an opportunity for KVM/qemu developers to learn how to install KVM-Autotest, run some tests, and contribute new tests back to the community. The qemu project is increasing its focus on testing as a way to improve overall code quality and speed up the patch merge process. Developers will be on-hand in #qemu on irc.oftc.net to answer questions you may have as you begin to use the test suite.

You can find more information about KVM-Autotest here. and about the install fest here.

Posted in KVM | 2 Comments

Automatic Memory Ballooning with MOM

In this post, I will describe how to use Memory Overcommitment Manager (MOM) to automatically manage memory ballooning on a KVM host.

Memory ballooning is a virtualization feature that allows youto apply a “soft” memory limit to your virtual machines.  For example, you can start a virtual machine with 2GB of assigned memory.  If, at a later time, you want to limit this virtual machine to 1GB you could use libvirt (virsh setmem <domain>1048576) or the qemu monitor (qemu> balloon 1024) to activate the memory balloon.

Memory ballooning can also be used if you want to over-commit memory.  This means that you assign more memory to virtual machines than you have available.  Memory overcommitment depends on an assumption that all virtual machines do not need 100% of their memory at all times.  Unused memory can be shifted around based on the actual memory requirements of each virtual machine by dynamically adjusting the memory balloons of each guest.  Obviously we need some automation here.  One emerging solution for KVM/libvirt hosts is MOM (Memory Overcommitment Manager).  MOM is a Python program that can (among other things) dynamically manage memory ballooning according to a user-defined policy.  Many more details about MOM can be found in the wiki.

How to set up MOM

At the time of this writing, the recommended configuration requires the MOM daemon running on the host to connect to a small daemon running in each guest.  As you will see, this is a bit tricky to set up.  Rest assured that we are working diligently to make it easier in the future!

The first step is to download MOM to your host computer.  You may get the latest version from git or download a release tarball.

Install MOM on the host:

  • Change to the directory containing the MOM distribution and install it python setup.py install.
  • Copy the sample configuration file /usr/share/doc/mom/examples/mom-balloon.conf to /etc/mom.conf.
  • Copy the ballooning policy file /usr/share/doc/mom/examples/balloon.rules to /etc/mom.rules.

Perform the following steps to install the mom guest daemon in each virtual machine:

  • Install MOM in the same manner as for the host:  python setup.py install
  • Copy mom-guestd to /usr/local/bin.
  • Arrange for the daemon to be started at boot: /usr/local/bin/mom-guestd & in /etc/rc.local

Configure MOM network access: The MOM host daemon talks to each guest daemon over the network to gather statistics about guest memory usage.  These statistics help MOM to make better memory ballooning decisions.  In order to connect to the guests, MOM relies on a helper script to determine each guest’s IP address.  A sample name-to-ip helper script is provided in the doc/ directory of the MOM source code.   Follow these steps to configure MOM guest network connectivity:

  • Copy doc/name-to-ip to /usr/local/bin/name-to-ip
  • Edit the name-to-ip helper script to suit your environment.  MOM will pass the script a libvirt domain name and expect the script to print an IP address.
  • Modify /etc/mom.conf
    • Set name-to-ip-helper to /usr/local/bin/name-to-ip
    • Add GuestNetworkDaemon to the [guest]->collectors list

Putting it all together

Now that you have MOM and the guests set up you should be ready to start.

  • Start MOM: momd -c /etc/mom.conf -r /etc/mom.rules
  • Start guests

Pay attention to the MOM output.  If everything is working you should see messages like ‘Guest <name> is ready’.  This means that MOM is communicating with the guests and is ready to manage memory ballooning.  If you do not see the ‘ready’ messages, double-check your mom.conf and make sure you added GuestNetworkDaemon to the guest collectors and that name-to-ip points to an executable script that returns the proper IP addresses of your guests.  Also make sure that mom-guestd is configured to start automatically when your guests boot.

The MOM Ballooning Policy

If you’ve gotten this far, you’re probably wondering about the memory ballooning policy that MOM uses by default.  The principle goal of the default policy is to allow the guests as much memory as possible without adversely affecting the host.  MOM watches the host and guests for signs of memory pressure.  As long as host memory pressure is low, the guests will not be ballooned.  When the amount of claimable memory on the host drops below 20% of total RAM, then MOM will start proactively ballooning guests.  The amount of pressure applied to the guests is proportional to the amount of memory pressure seen in the host.  Guests that are not using as much memory are ballooned more aggressively.  When host memory pressure subsides, MOM restores the guests to their full memory allocation.

MOM is designed to be flexible.  If you would prefer a different auto-ballooning algorithm on your host, feel free to edit the balloon.rules file to suit your needs.  For a start, try modifying the variables at the top.  For example, to start ballooning when there is less host memory pressure, increase pressure_threshold,  To alter the balloon change rate, modify max_balloon_change_percent.

Hopefully this tutorial has helped you to set up an auto-balloon environment with KVM and libvirt virtualization.  I look forward to hearing how it works out for you.  We are working hard to better integrate this technology into the core tools and make it easier to use in the future.

Posted in libvirt, MOM | 24 Comments

How to hack on a local copy of libvirt

So you want to run a version of libvirt other than packaged version on your system.  You might want to do this to see if a bug you found has been fixed in a later release, or to develop your own new feature.  Since libvirt can be very tightly integrated with the rest of the distribution, there are a few things to look out for when building your own version.  Here’s how I do it:

Step 0: Install your distribution’s libvirt package
The first thing you should do is install libvirt and its dependencies using your distribution’s usual method.  This ensures that the directories, config files, and security policies that are required for your system get installed.  Rather than starting from scratch, you’ll want to make use of these things.

Step 1: Get the source code
In order to hack on libvirt you first need to get the source.  You can use git or grab a release tarball.  Find the details here.  Once you have the source, unpack or checkout a copy and change to that directory.

Step 2: Build it
Our goal is to run our own copy of libvirt directly from the build tree (without overwriting the system version).  To do this we execute these simple commands:

./autogen.sh --system
make

The --system argument to autogen.sh causes libvirt to use the regular system paths for configuration files, sockets, logs, etc.  Note that we do not install (as that would overwrite the system installed version).

Step 3: Start the daemon
It is easy to start the daemon from the build root:

sudo ./daemon/libvirtd -d

This will create a system-level daemon listening at the libvirt URI qemu:///system which you can connect to using virsh:

sudo tools/virsh -c qemu:///system

Using GDB
Both libvirtd and virsh use libtool wrapper scripts around the actual binaries.  This means that:

gdb ./daemon/libvirtd

Will not work as expected.  The easiest way to work around this problem is to launch the daemon as suggested in Step 3 and then attach to the running process:

gdb -p `pidof lt-libvirtd`

If you cannot do this (because you want to place a breakpoint in initialization code), you can use this workaround.  First, run libvirtd once as normal and stop it.  This will create a binary which can then be directly executed:

gdb ./daemon/.libs/lt-libvirtd

This same trick can be used with virsh.  Run it once normally, then:

gdb ./tools/.libs/lt-virsh

I hope this gets you off to a good start with libvirt development.  Happy hacking!

Posted in libvirt | 5 Comments