[Libvir] 2/4 QEMU driver

Richard W.M. Jones rjones at redhat.com
Thu Feb 21 20:57:58 UTC 2008


This patch implements the QEMU driver part of the change.

We run dnsmasq with the extra --dhcp-hostsfile=... parameter, and then
include functions to read and write this file.

When the file changes we send SIGHUP to dnsmasq.

Rich.

-- 
Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/
Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod
Street, Windsor, Berkshire, SL4 1TE, United Kingdom.  Registered in
England and Wales under Company Registration No. 03798903
-------------- next part --------------
Index: src/driver.h
===================================================================
RCS file: /data/cvs/libvirt/src/driver.h,v
retrieving revision 1.42
diff -u -r1.42 driver.h
--- src/driver.h	20 Feb 2008 15:06:53 -0000	1.42
+++ src/driver.h	21 Feb 2008 20:43:48 -0000
@@ -377,6 +377,22 @@
 	(*virDrvNetworkSetAutostart)	(virNetworkPtr network,
 					 int autostart);
 
+typedef int
+    (*virDrvNetworkNumOfDHCPHostMappings) (virNetworkPtr network);
+typedef int
+    (*virDrvNetworkListDHCPHostMappings) (virNetworkPtr network,
+                     virNetworkDHCPHostMappingPtr *const mappings,
+                     int maxmappings);
+typedef int
+    (*virDrvNetworkAddDHCPHostMapping) (virNetworkPtr network,
+                                         const char *hwaddr,
+                                         const char *ipaddr,
+                                         const char *hostname,
+                                         unsigned int flags);
+typedef int
+    (*virDrvNetworkDeleteDHCPHostMapping) (virNetworkPtr network,
+                                           const char *hwaddr);
+
 
 typedef struct _virNetworkDriver virNetworkDriver;
 typedef virNetworkDriver *virNetworkDriverPtr;
@@ -410,6 +426,10 @@
 	virDrvNetworkGetBridgeName	networkGetBridgeName;
 	virDrvNetworkGetAutostart	networkGetAutostart;
 	virDrvNetworkSetAutostart	networkSetAutostart;
+    virDrvNetworkNumOfDHCPHostMappings	networkNumOfDHCPHostMappings;
+    virDrvNetworkListDHCPHostMappings	networkListDHCPHostMappings;
+    virDrvNetworkAddDHCPHostMapping	    networkAddDHCPHostMapping;
+    virDrvNetworkDeleteDHCPHostMapping	networkDeleteDHCPHostMapping;
 };
 
 
Index: src/qemu_driver.c
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_driver.c,v
retrieving revision 1.52
diff -u -r1.52 qemu_driver.c
--- src/qemu_driver.c	7 Feb 2008 16:50:17 -0000	1.52
+++ src/qemu_driver.c	21 Feb 2008 20:43:50 -0000
@@ -831,6 +831,7 @@
         2 + /* --except-interface lo */
         2 + /* --listen-address 10.0.0.1 */
         1 + /* --dhcp-leasefile=path */
+        1 + /* --dhcp-hostsfile=path */
         (2 * network->def->nranges) + /* --dhcp-range 10.0.0.2,10.0.0.254 */
         1;  /* NULL */
 
@@ -884,6 +885,10 @@
              LOCAL_STATE_DIR, network->def->name);
     APPEND_ARG(*argv, i++, buf);
 
+    snprintf(buf, sizeof(buf),
+             "--dhcp-hostsfile=" DHCP_HOSTS_FORMAT, network->def->name);
+    APPEND_ARG(*argv, i++, buf);
+
     range = network->def->ranges;
     while (range) {
         snprintf(buf, sizeof(buf), "%s,%s",
@@ -914,6 +919,7 @@
 dhcpStartDhcpDaemon(virConnectPtr conn,
                     struct qemud_network *network)
 {
+    char buf[PATH_MAX];
     char **argv;
     int ret, i;
 
@@ -923,6 +929,15 @@
         return -1;
     }
 
+    /* Touch the DHCP hosts file so it exists before dnsmasq starts up. */
+    snprintf (buf, sizeof buf, DHCP_HOSTS_FORMAT, network->def->name);
+    ret = open (buf, O_CREAT|O_WRONLY, 0644);
+    if (ret == -1) {
+        qemudReportError (conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR,
+                          "%s: %s", buf, strerror (errno));
+        return -1;
+    }
+
     argv = NULL;
     if (qemudBuildDnsmasqArgv(conn, network, &argv) < 0)
         return -1;
@@ -2853,6 +2868,302 @@
     return 0;
 }
 
+static void
+qemudFreeDHCPHostMappings (virNetworkDHCPHostMappingPtr *mappings,
+                           int nr_mappings)
+{
+    int i;
+
+    for (i = 0; i < nr_mappings; ++i)
+        if (mappings[i]) {
+            free (mappings[i]->hwaddr);
+            free (mappings[i]->ipaddr);
+            free (mappings[i]->hostname);
+            free (mappings[i]);
+        }
+    free (mappings);
+}
+
+/* This allocates the array and returns the number of mappings in the
+ * array or -1 on error.
+ */
+static int
+qemudParseDHCPHostMappingsFile (virNetworkPtr net,
+                                struct qemud_network *network,
+                                virNetworkDHCPHostMappingPtr **mappings)
+{
+    char buf[PATH_MAX];
+    FILE *fp;
+    int col;
+    int lines = 0;
+    int allocated = 0;
+    char *str;
+    char *token;
+    char *saveptr = NULL;
+    virNetworkDHCPHostMappingPtr *old_mappings;
+
+    if (mappings) *mappings = NULL;
+
+    snprintf (buf, sizeof buf, DHCP_HOSTS_FORMAT, network->def->name);
+    fp = fopen (buf, "r");
+    if (fp == NULL) {
+        qemudReportError (net->conn, NULL, net, VIR_ERR_SYSTEM_ERROR,
+                          "%s: %s", buf, strerror (errno));
+        return -1;
+    }
+
+    while (fgets (buf, sizeof buf, fp) != 0) {
+        char *hwaddr = 0, *ipaddr = 0, *hostname = 0;
+
+        /* Dnsmasq manpage is fuzzy about what is accepted as a
+         * parameter for dhcp-host.  Hopefully only we will be
+         * writing to this file, but be generous in what we
+         * accept anyway.
+         */
+        if (buf[0] == '\0' || buf[0] == '#' || buf[0] == '\n')
+            continue;
+
+        for (str = buf, col = 0;
+             (token = strtok_r (str, ",", &saveptr)) != NULL;
+             str = NULL, col++) {
+            if (!STREQLEN (token, "net:", 4) &&
+                !STREQLEN (token, "id:", 3) &&
+                !STREQ (token, "ignore")) {
+                /* First column is the hardware address. */
+                if (col == 0) hwaddr = token;
+                else {
+                    if (isdigit (token[0])) {
+                        if (!ipaddr) ipaddr = token;
+                    } else {
+                        if (!hostname) hostname = token;
+                    }
+                }
+            }
+        }
+
+        if (hwaddr && ipaddr) {
+            if (mappings) {
+                if (lines >= allocated) {
+                    allocated += 16;
+                    old_mappings = *mappings;
+                    *mappings = realloc (*mappings,
+                                         sizeof (virNetworkDHCPHostMappingPtr) *
+                                         allocated);
+                    if (!*mappings) {
+                        *mappings = old_mappings;
+                        goto mem_error;
+                    }
+                }
+                *mappings[lines] = malloc (sizeof (virNetworkDHCPHostMapping));
+                if (!*mappings[lines]) goto mem_error;
+                (*mappings[lines])->hwaddr = hwaddr;
+                (*mappings[lines])->ipaddr = ipaddr;
+                (*mappings[lines])->hostname = hostname;
+            }
+            lines++;
+        }
+    }
+
+    fclose (fp);
+
+    return lines;
+
+ mem_error:
+    fclose (fp);
+
+    if (mappings) qemudFreeDHCPHostMappings (*mappings, lines);
+
+    qemudReportError (net->conn, NULL, net, VIR_ERR_NO_MEMORY,
+                      __FUNCTION__);
+
+    return -1;
+}
+
+static int
+qemudNetworkNumOfDHCPHostMappings (virNetworkPtr net)
+{
+    struct qemud_driver *driver =
+        (struct qemud_driver *)net->conn->networkPrivateData;
+    struct qemud_network *network =
+        qemudFindNetworkByUUID(driver, net->uuid);
+
+    if (!network) {
+        qemudReportError (net->conn, NULL, net, VIR_ERR_INVALID_DOMAIN,
+                          "no network with matching uuid");
+        return -1;
+    }
+
+    return qemudParseDHCPHostMappingsFile (net, network, NULL);
+}
+
+static int
+qemudNetworkListDHCPHostMappings (virNetworkPtr net,
+                                virNetworkDHCPHostMappingPtr *const mappings_r,
+                                  int maxmappings)
+{
+    virNetworkDHCPHostMappingPtr *mappings;
+    int nr_mappings;
+    struct qemud_driver *driver =
+        (struct qemud_driver *)net->conn->networkPrivateData;
+    struct qemud_network *network =
+        qemudFindNetworkByUUID(driver, net->uuid);
+
+    if (!network) {
+        qemudReportError (net->conn, NULL, net, VIR_ERR_INVALID_DOMAIN,
+                          "no network with matching uuid");
+        return -1;
+    }
+
+    nr_mappings = qemudParseDHCPHostMappingsFile (net, network, &mappings);
+    if (nr_mappings == -1) return -1;
+
+    if (maxmappings >= nr_mappings) {
+        /* The return array is large enough. */
+        memcpy (mappings_r, mappings,
+                nr_mappings *sizeof (virNetworkDHCPHostMappingPtr));
+    } else {
+        /* Partially copy the array, free the unused mappings that
+         * we didn't copy.
+         */
+        memcpy (mappings_r, mappings,
+                maxmappings * sizeof (virNetworkDHCPHostMappingPtr));
+        nr_mappings -= maxmappings;
+        qemudFreeDHCPHostMappings (mappings + maxmappings, nr_mappings);
+    }
+
+    free (mappings);            /* NB: Just free the array itself. */
+    return 0;
+}
+
+static int
+qemudNetworkAddDHCPHostMapping (virNetworkPtr net,
+                                const char *hwaddr,
+                                const char *ipaddr,
+                                const char *hostname,
+                                unsigned int flags ATTRIBUTE_UNUSED)
+{
+    char buf[PATH_MAX];
+    virNetworkDHCPHostMappingPtr *mappings;
+    int nr_mappings, i, written = 0;
+    FILE *fp;
+    struct qemud_driver *driver =
+        (struct qemud_driver *)net->conn->networkPrivateData;
+    struct qemud_network *network =
+        qemudFindNetworkByUUID(driver, net->uuid);
+
+    if (!network) {
+        qemudReportError (net->conn, NULL, net, VIR_ERR_INVALID_DOMAIN,
+                          "no network with matching uuid");
+        return -1;
+    }
+
+    nr_mappings = qemudParseDHCPHostMappingsFile (net, network, &mappings);
+    if (nr_mappings == -1) return -1;
+
+    snprintf (buf, sizeof buf, DHCP_HOSTS_FORMAT, network->def->name);
+    fp = fopen (buf, "w");
+    if (fp == NULL) {
+        qemudReportError (net->conn, NULL, net, VIR_ERR_SYSTEM_ERROR,
+                          "%s: %s", buf, strerror (errno));
+        return -1;
+    }
+
+    for (i = 0; i < nr_mappings; ++i) {
+        if (STREQ (mappings[i]->hwaddr, hwaddr)) {
+            /* Replace this entry. */
+            fputs (hwaddr, fp);
+            fputc (',', fp);
+            fputs (ipaddr, fp);
+            if (hostname) {
+                fputc (',', fp);
+                fputs (hostname, fp);
+            }
+            fputc ('\n', fp);
+            written = 1;
+        } else {
+            /* Write this entry again. */
+            fputs (mappings[i]->hwaddr, fp);
+            fputc (',', fp);
+            fputs (mappings[i]->ipaddr, fp);
+            if (mappings[i]->hostname) {
+                fputc (',', fp);
+                fputs (mappings[i]->hostname, fp);
+            }
+            fputc ('\n', fp);
+        }
+    }
+
+    if (!written) {
+        fputs (hwaddr, fp);
+        fputc (',', fp);
+        fputs (ipaddr, fp);
+        if (hostname) {
+            fputc (',', fp);
+            fputs (hostname, fp);
+        }
+        fputc ('\n', fp);
+    }
+
+    fclose (fp);
+
+    /* Tell dnsmasq to reread the configuration file. */
+    kill (network->dnsmasqPid, SIGHUP);
+
+    return 0;
+}
+
+static int
+qemudNetworkDeleteDHCPHostMapping (virNetworkPtr net,
+                                   const char *hwaddr)
+{
+    char buf[PATH_MAX];
+    virNetworkDHCPHostMappingPtr *mappings;
+    int nr_mappings, i;
+    FILE *fp;
+    struct qemud_driver *driver =
+        (struct qemud_driver *)net->conn->networkPrivateData;
+    struct qemud_network *network =
+        qemudFindNetworkByUUID(driver, net->uuid);
+
+    if (!network) {
+        qemudReportError (net->conn, NULL, net, VIR_ERR_INVALID_DOMAIN,
+                          "no network with matching uuid");
+        return -1;
+    }
+
+    nr_mappings = qemudParseDHCPHostMappingsFile (net, network, &mappings);
+    if (nr_mappings == -1) return -1;
+
+    snprintf (buf, sizeof buf, DHCP_HOSTS_FORMAT, network->def->name);
+    fp = fopen (buf, "w");
+    if (fp == NULL) {
+        qemudReportError (net->conn, NULL, net, VIR_ERR_SYSTEM_ERROR,
+                          "%s: %s", buf, strerror (errno));
+        return -1;
+    }
+
+    for (i = 0; i < nr_mappings; ++i) {
+        if (!STREQ (mappings[i]->hwaddr, hwaddr)) {
+            /* Write this entry again. */
+            fputs (mappings[i]->hwaddr, fp);
+            fputc (',', fp);
+            fputs (mappings[i]->ipaddr, fp);
+            if (mappings[i]->hostname) {
+                fputc (',', fp);
+                fputs (mappings[i]->hostname, fp);
+            }
+            fputc ('\n', fp);
+        }
+    }
+
+    fclose (fp);
+
+    /* Tell dnsmasq to reread the configuration file. */
+    kill (network->dnsmasqPid, SIGHUP);
+
+    return 0;
+}
+
 static virDriver qemuDriver = {
     VIR_DRV_QEMU,
     "QEMU",
@@ -2931,6 +3242,10 @@
     qemudNetworkGetBridgeName, /* networkGetBridgeName */
     qemudNetworkGetAutostart, /* networkGetAutostart */
     qemudNetworkSetAutostart, /* networkSetAutostart */
+    qemudNetworkNumOfDHCPHostMappings, /* networkNumOfDHCPHostMappings */
+    qemudNetworkListDHCPHostMappings, /* networkListDHCPHostMappings */
+    qemudNetworkAddDHCPHostMapping, /* networkAddDHCPHostMapping */
+    qemudNetworkDeleteDHCPHostMapping, /* networkDeleteDHCPHostMapping */
 };
 
 static virStateDriver qemuStateDriver = {
Index: src/qemu_driver.h
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_driver.h,v
retrieving revision 1.4
diff -u -r1.4 qemu_driver.h
--- src/qemu_driver.h	29 Jan 2008 18:15:54 -0000	1.4
+++ src/qemu_driver.h	21 Feb 2008 20:43:50 -0000
@@ -31,6 +31,9 @@
 
 #include "internal.h"
 
+/* %s is replaced with the network name. */
+#define DHCP_HOSTS_FORMAT LOCAL_STATE_DIR "/lib/libvirt/hosts-%s.conf"
+
 int qemudRegister(void);
 
 #endif /* WITH_QEMU */


More information about the libvir-list mailing list