[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