[Libvir] [PATCH] (for discussion) DHCP host mappings using 3 arrays API

Richard W.M. Jones rjones at redhat.com
Mon Feb 25 16:49:00 UTC 2008


This patch is an evolution of the previous patch for implementing DHCP
host mappings.  The virNetworkListDHCPHostMappings calls has been
changed to take three arrays of strings:

int  virNetworkListDHCPHostMappings
                (virNetworkPtr network,
	         char **const hwaddrs,
		 char **const ipaddrs,
	         char **const hostnames,
	         int maxmappings);

Also I've fixed a number of bugs throughout, particularly in the way
that the hosts file was being parsed.

This patch is for discussion only.  There is a segfault on the client
side of (perhaps ironically) the virNetworkListDHCPHostMappings call.
Been looking for about an hour but I can't see it yet ...

All the other calls appear to work OK.

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
virt-p2v converts physical machines to virtual machines.  Boot with a
live CD or over the network (PXE) and turn machines into Xen guests.
http://et.redhat.com/~rjones/virt-p2v
-------------- next part --------------
Index: include/libvirt/libvirt.h.in
===================================================================
RCS file: /data/cvs/libvirt/include/libvirt/libvirt.h.in,v
retrieving revision 1.42
diff -u -r1.42 libvirt.h.in
--- include/libvirt/libvirt.h.in	20 Feb 2008 14:57:39 -0000	1.42
+++ include/libvirt/libvirt.h.in	25 Feb 2008 16:45:26 -0000
@@ -760,6 +760,27 @@
 int			virNetworkSetAutostart	(virNetworkPtr network,
 						 int autostart);
 
+/*
+ * DHCP host mappings
+ */
+int                     virNetworkNumOfDHCPHostMappings
+                                                (virNetworkPtr network);
+int                     virNetworkListDHCPHostMappings
+                                                (virNetworkPtr network,
+						 char **const hwaddrs,
+						 char **const ipaddrs,
+						 char **const hostnames,
+						 int maxmappings);
+
+int                     virNetworkAddDHCPHostMapping
+                                                (virNetworkPtr network,
+						 const char *hwaddr,
+						 const char *ipaddr,
+						 const char *hostname,
+						 unsigned int flags);
+int                     virNetworkDeleteDHCPHostMapping
+                                                (virNetworkPtr network,
+						 const char *hwaddr);
 
 /**
  * virStoragePool:
Index: qemud/remote.c
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote.c,v
retrieving revision 1.24
diff -u -r1.24 remote.c
--- qemud/remote.c	22 Feb 2008 16:26:13 -0000	1.24
+++ qemud/remote.c	25 Feb 2008 16:45:28 -0000
@@ -2058,6 +2058,173 @@
 
 
 static int
+remoteDispatchNetworkNumOfDhcpHostMappings (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                            struct qemud_client *client,
+                                            remote_message_header *req,
+                                            remote_network_num_of_dhcp_host_mappings_args *args,
+                                            remote_network_num_of_dhcp_host_mappings_ret *ret)
+{
+    virNetworkPtr net;
+    CHECK_CONN(client);
+
+    net = get_nonnull_network (client->conn, args->net);
+    if (net == NULL) {
+        remoteDispatchError (client, req, "network not found");
+        return -2;
+    }
+
+    ret->num = virNetworkNumOfDHCPHostMappings (net);
+    if (ret->num == -1) {
+        virNetworkFree (net);
+        return -1;
+    }
+
+    virNetworkFree (net);
+    return 0;
+}
+
+
+static int
+remoteDispatchNetworkListDhcpHostMappings (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                           struct qemud_client *client,
+                                           remote_message_header *req,
+                                           remote_network_list_dhcp_host_mappings_args *args,
+                                           remote_network_list_dhcp_host_mappings_ret *ret)
+{
+    virNetworkPtr net;
+    char **hwaddrs, **ipaddrs, **hostnames;
+    int nr_mappings, i;
+    CHECK_CONN(client);
+
+    net = get_nonnull_network (client->conn, args->net);
+    if (net == NULL) {
+        remoteDispatchError (client, req, "network not found");
+        return -2;
+    }
+
+    if (args->maxmappings > REMOTE_NETWORK_DHCP_HOST_MAPPINGS_LIST_MAX) {
+        remoteDispatchError (client, req,
+                             "maxmappings > REMOTE_NETWORK_DHCP_HOST_MAPPINGS_LIST_MAX");
+        return -2;
+    }
+
+    hwaddrs = malloc (args->maxmappings * sizeof (char *));
+    if (!hwaddrs) {
+        remoteDispatchSendError (client, req, VIR_ERR_NO_MEMORY, "hwaddrs");
+        return -2;
+    }
+    ipaddrs = malloc (args->maxmappings * sizeof (char *));
+    if (!ipaddrs) {
+        remoteDispatchSendError (client, req, VIR_ERR_NO_MEMORY, "ipaddrs");
+        free (hwaddrs);
+        return -2;
+    }
+    hostnames = malloc (args->maxmappings * sizeof (char *));
+    if (!hostnames) {
+        remoteDispatchSendError (client, req, VIR_ERR_NO_MEMORY, "hostnames");
+        free (hwaddrs);
+        free (ipaddrs);
+        return -2;
+    }
+
+    nr_mappings = virNetworkListDHCPHostMappings (net,
+                                                  hwaddrs, ipaddrs, hostnames,
+                                                  args->maxmappings);
+    if (nr_mappings == -1) {
+        virNetworkFree(net);
+        free (hwaddrs);
+        free (ipaddrs);
+        free (hostnames);
+        return -1;
+    }
+
+    ret->hwaddrs.hwaddrs_len = nr_mappings;
+    ret->ipaddrs.ipaddrs_len = nr_mappings;
+    ret->hostnames.hostnames_len = nr_mappings;
+
+    ret->hwaddrs.hwaddrs_val =
+        calloc (nr_mappings, sizeof (ret->hwaddrs.hwaddrs_val[0]));
+    ret->ipaddrs.ipaddrs_val =
+        calloc (nr_mappings, sizeof (ret->ipaddrs.ipaddrs_val[0]));
+    ret->hostnames.hostnames_val =
+        calloc (nr_mappings, sizeof (ret->hostnames.hostnames_val[0]));
+    for (i = 0; i < nr_mappings; ++i) {
+        ret->hwaddrs.hwaddrs_val[i] = hwaddrs[i];
+        ret->ipaddrs.ipaddrs_val[i] = ipaddrs[i];
+        /* See note in remote_protocol.x */
+        ret->hostnames.hostnames_val[i] =
+            hostnames[i] == NULL ? strdup ("") : hostnames[i];
+    }
+
+    free (hwaddrs); /* NB: Just free the array, caller frees the rest. */
+    free (ipaddrs);
+    free (hostnames);
+
+    virNetworkFree(net);
+    return 0;
+}
+
+
+static int
+remoteDispatchNetworkAddDhcpHostMapping (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                         struct qemud_client *client,
+                                         remote_message_header *req,
+                                         remote_network_add_dhcp_host_mapping_args *args,
+                                         void *ret ATTRIBUTE_UNUSED)
+{
+    virNetworkPtr net;
+    char *hwaddr, *ipaddr, *hostname;
+    CHECK_CONN(client);
+
+    net = get_nonnull_network (client->conn, args->net);
+    if (net == NULL) {
+        remoteDispatchError (client, req, "network not found");
+        return -2;
+    }
+
+    hwaddr = args->hwaddr;
+    ipaddr = args->ipaddr;
+    hostname = args->hostname == NULL ? NULL : *args->hostname;
+
+    if (virNetworkAddDHCPHostMapping (net, hwaddr, ipaddr, hostname, args->flags) == -1) {
+        virNetworkFree (net);
+        return -1;
+    }
+
+    virNetworkFree (net);
+    return 0;
+}
+
+static int
+remoteDispatchNetworkDeleteDhcpHostMapping (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                            struct qemud_client *client,
+                                            remote_message_header *req,
+                                            remote_network_delete_dhcp_host_mapping_args *args,
+                                            void *ret ATTRIBUTE_UNUSED)
+{
+    virNetworkPtr net;
+    char *hwaddr;
+    CHECK_CONN(client);
+
+    net = get_nonnull_network (client->conn, args->net);
+    if (net == NULL) {
+        remoteDispatchError (client, req, "network not found");
+        return -2;
+    }
+
+    hwaddr = args->hwaddr;
+
+    if (virNetworkDeleteDHCPHostMapping (net, hwaddr) == -1) {
+        virNetworkFree (net);
+        return -1;
+    }
+
+    virNetworkFree (net);
+    return 0;
+}
+
+
+static int
 remoteDispatchAuthList (struct qemud_server *server ATTRIBUTE_UNUSED,
                         struct qemud_client *client,
                         remote_message_header *req ATTRIBUTE_UNUSED,
Index: qemud/remote_protocol.x
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote_protocol.x,v
retrieving revision 1.10
diff -u -r1.10 remote_protocol.x
--- qemud/remote_protocol.x	20 Feb 2008 15:22:35 -0000	1.10
+++ qemud/remote_protocol.x	25 Feb 2008 16:45:29 -0000
@@ -78,6 +78,9 @@
 /* Upper limit on lists of network names. */
 const REMOTE_NETWORK_NAME_LIST_MAX = 256;
 
+/* Upper limit on lists of DHCP host mappings. */
+const REMOTE_NETWORK_DHCP_HOST_MAPPINGS_LIST_MAX = 256;
+
 /* Upper limit on lists of storage pool names. */
 const REMOTE_STORAGE_POOL_NAME_LIST_MAX = 256;
 
@@ -647,6 +650,42 @@
     int autostart;
 };
 
+struct remote_network_num_of_dhcp_host_mappings_args {
+    remote_nonnull_network net;
+};
+
+struct remote_network_num_of_dhcp_host_mappings_ret {
+    int num;
+};
+
+struct remote_network_list_dhcp_host_mappings_args {
+    remote_nonnull_network net;
+    int maxmappings;
+};
+
+struct remote_network_list_dhcp_host_mappings_ret {
+    remote_nonnull_string hwaddrs<REMOTE_NETWORK_DHCP_HOST_MAPPINGS_LIST_MAX>;
+    remote_nonnull_string ipaddrs<REMOTE_NETWORK_DHCP_HOST_MAPPINGS_LIST_MAX>;
+    /* Technically these can be NULL, but XDR makes it very hard
+     * to implement remoteDispatchNetworkListDhcpHostMappings without
+     * using an ugly static variable, so instead just assume "" on the
+     * wire is the same as NULL.  Hostnames can't be zero length anyway.
+     */
+    remote_nonnull_string hostnames<REMOTE_NETWORK_DHCP_HOST_MAPPINGS_LIST_MAX>;
+};
+
+struct remote_network_add_dhcp_host_mapping_args {
+    remote_nonnull_network net;
+    remote_nonnull_string hwaddr;
+    remote_nonnull_string ipaddr;
+    remote_string hostname;
+    int flags;
+};
+
+struct remote_network_delete_dhcp_host_mapping_args {
+    remote_nonnull_network net;
+    remote_nonnull_string hwaddr;
+};
 
 struct remote_auth_list_ret {
     remote_auth_type types<REMOTE_AUTH_TYPE_LIST_MAX>;
@@ -1017,7 +1056,12 @@
     REMOTE_PROC_STORAGE_VOL_LOOKUP_BY_PATH = 97,
     REMOTE_PROC_STORAGE_VOL_GET_INFO = 98,
     REMOTE_PROC_STORAGE_VOL_DUMP_XML = 99,
-    REMOTE_PROC_STORAGE_VOL_GET_PATH = 100
+    REMOTE_PROC_STORAGE_VOL_GET_PATH = 100,
+
+    REMOTE_PROC_NETWORK_ADD_DHCP_HOST_MAPPING = 101,
+    REMOTE_PROC_NETWORK_DELETE_DHCP_HOST_MAPPING = 102,
+    REMOTE_PROC_NETWORK_LIST_DHCP_HOST_MAPPINGS = 103,
+    REMOTE_PROC_NETWORK_NUM_OF_DHCP_HOST_MAPPINGS = 104
 };
 
 /* Custom RPC structure. */
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	25 Feb 2008 16:45:29 -0000
@@ -377,6 +377,24 @@
 	(*virDrvNetworkSetAutostart)	(virNetworkPtr network,
 					 int autostart);
 
+typedef int
+    (*virDrvNetworkNumOfDHCPHostMappings) (virNetworkPtr network);
+typedef int
+    (*virDrvNetworkListDHCPHostMappings) (virNetworkPtr network,
+                                          char **const hwaddrs,
+                                          char **const ipaddrs,
+                                          char **const hostnames,
+                                          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 +428,10 @@
 	virDrvNetworkGetBridgeName	networkGetBridgeName;
 	virDrvNetworkGetAutostart	networkGetAutostart;
 	virDrvNetworkSetAutostart	networkSetAutostart;
+    virDrvNetworkNumOfDHCPHostMappings	networkNumOfDHCPHostMappings;
+    virDrvNetworkListDHCPHostMappings	networkListDHCPHostMappings;
+    virDrvNetworkAddDHCPHostMapping	    networkAddDHCPHostMapping;
+    virDrvNetworkDeleteDHCPHostMapping	networkDeleteDHCPHostMapping;
 };
 
 
Index: src/libvirt.c
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt.c,v
retrieving revision 1.123
diff -u -r1.123 libvirt.c
--- src/libvirt.c	20 Feb 2008 16:54:36 -0000	1.123
+++ src/libvirt.c	25 Feb 2008 16:45:32 -0000
@@ -3683,6 +3683,175 @@
     return -1;
 }
 
+/**
+ * virNetworkNumOfDHCPHostMappings:
+ * @network: a network object
+ *
+ * Returns the number of DHCP host mappings, or -1 in case of error.
+ */
+int
+virNetworkNumOfDHCPHostMappings (virNetworkPtr network)
+{
+    virConnectPtr conn;
+    DEBUG("network=%p", network);
+
+    if (!VIR_IS_NETWORK(network)) {
+        virLibNetworkError (NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__);
+        return -1;
+    }
+
+    conn = network->conn;
+
+    if (conn->networkDriver &&
+        conn->networkDriver->networkNumOfDHCPHostMappings)
+        return conn->networkDriver->networkNumOfDHCPHostMappings (network);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+/**
+ * virNetworkListDHCPHostMappings:
+ * @network: a network object
+ * @hwaddrs: an array of hardware addresses
+ * @ipaddrs: an array of IP addresses
+ * @hostnames: an array of hostnames (which may be NULL)
+ * @maxmappings: the length of the arrays
+ *
+ * This can be used to return a list of DHCP host mappings for the
+ * network.
+ *
+ * This function fills in three arrays of corresponding pointers,
+ * thus the first tuple returned is (hwaddrs[0], ipaddrs[0],
+ * hostnames[0]).  The 'hwaddrs' array contains hardware (MAC)
+ * addresses.  The 'ipaddrs' array contains IP addresses as strings
+ * in dotted-quad or IPv6 colon format.  The 'hostnames' array
+ * contains optional hostnames, which may be NULL.
+ *
+ * The caller is responsible for freeing the strings.
+ *
+ * Returns the number of DHCP host mappings, or -1 in case of error.
+ */
+int
+virNetworkListDHCPHostMappings (virNetworkPtr network,
+                                char **const hwaddrs,
+                                char **const ipaddrs,
+                                char **const hostnames,
+                                int maxmappings)
+{
+    virConnectPtr conn;
+    DEBUG("network=%p, hwaddrs=%p, ipaddrs=%p, hostnames=%p, maxmappings=%d",
+          network, hwaddrs, ipaddrs, hostnames, maxmappings);
+
+    if (!VIR_IS_NETWORK(network)) {
+        virLibNetworkError (NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__);
+        return -1;
+    }
+
+    conn = network->conn;
+
+    if (!hwaddrs || !ipaddrs || !hostnames) {
+        virLibNetworkError (network, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return -1;
+    }
+
+    if (maxmappings == 0) return 0;
+
+    memset (hwaddrs, 0, sizeof (char *) * maxmappings);
+    memset (ipaddrs, 0, sizeof (char *) * maxmappings);
+    memset (hostnames, 0, sizeof (char *) * maxmappings);
+
+    if (conn->networkDriver &&
+        conn->networkDriver->networkListDHCPHostMappings)
+        return conn->networkDriver->networkListDHCPHostMappings (network,
+                                                                 hwaddrs,
+                                                                 ipaddrs,
+                                                                 hostnames,
+                                                                 maxmappings);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+/**
+ * virNetworkAddDHCPHostMapping:
+ * @network: a network object
+ * @hwaddr: hardware (MAC) address
+ * @ipaddr: IP address
+ * @hostname: hostname
+ * @flags: unused, pass 0
+ *
+ * Add a DHCP hardware address to IP address mapping, and optionally
+ * a DHCP hostname for the IP address (if hostname is not NULL).
+ *
+ * The mappings are keyed on the hardware (MAC) address.  If the hardware
+ * address already has a mapping, then this call replaces it.
+ *
+ * Return -1 on error or 0 on success.
+ */
+int
+virNetworkAddDHCPHostMapping (virNetworkPtr network,
+                              const char *hwaddr,
+                              const char *ipaddr,
+                              const char *hostname,
+                              unsigned int flags)
+{
+    virConnectPtr conn;
+    DEBUG("network=%p, hwaddr=%s, ipaddr=%s, hostname=%s, flags=%d",
+          network, hwaddr, ipaddr, hostname, flags);
+
+    if (!VIR_IS_NETWORK(network)) {
+        virLibNetworkError (NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__);
+        return -1;
+    }
+
+    conn = network->conn;
+
+    if (flags != 0) {
+        virLibNetworkError (NULL, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return -1;
+    }
+
+    if (conn->networkDriver &&
+        conn->networkDriver->networkAddDHCPHostMapping)
+        return conn->networkDriver->networkAddDHCPHostMapping
+            (network, hwaddr, ipaddr, hostname, flags);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+/**
+ * virNetworkDeleteDHCPHostMapping:
+ * @network: a network object
+ * @hwaddr: hardware (MAC) address
+ *
+ * Delete an existing DHCP host mapping.
+ *
+ * Return -1 on error or 0 on success.
+ */
+int
+virNetworkDeleteDHCPHostMapping (virNetworkPtr network,
+                                 const char *hwaddr)
+{
+    virConnectPtr conn;
+    DEBUG("network=%p, hwaddr=%s", network, hwaddr);
+
+    if (!VIR_IS_NETWORK(network)) {
+        virLibNetworkError (NULL, VIR_ERR_INVALID_NETWORK, __FUNCTION__);
+        return -1;
+    }
+
+    conn = network->conn;
+
+    if (conn->networkDriver &&
+        conn->networkDriver->networkDeleteDHCPHostMapping)
+        return conn->networkDriver->networkDeleteDHCPHostMapping (network,
+                                                                  hwaddr);
+
+    virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
 
 /**
  * virStoragePoolGetConnect:
Index: src/libvirt_sym.version
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt_sym.version,v
retrieving revision 1.36
diff -u -r1.36 libvirt_sym.version
--- src/libvirt_sym.version	21 Feb 2008 03:53:03 -0000	1.36
+++ src/libvirt_sym.version	25 Feb 2008 16:45:32 -0000
@@ -99,6 +99,11 @@
 	virNetworkGetBridgeName;
 	virNetworkGetAutostart;
 	virNetworkSetAutostart;
+	virNetworkNumOfDHCPHostMappings;
+	virNetworkListDHCPHostMappings;
+	virNetworkFreeDHCPHostMappings;
+	virNetworkAddDHCPHostMapping;
+	virNetworkDeleteDHCPHostMapping;
 
         virStoragePoolGetConnect;
 	virConnectNumOfStoragePools;
Index: src/qemu_driver.c
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_driver.c,v
retrieving revision 1.54
diff -u -r1.54 qemu_driver.c
--- src/qemu_driver.c	22 Feb 2008 16:26:13 -0000	1.54
+++ src/qemu_driver.c	25 Feb 2008 16:45:35 -0000
@@ -832,6 +832,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 */
 
@@ -885,6 +886,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",
@@ -915,6 +920,7 @@
 dhcpStartDhcpDaemon(virConnectPtr conn,
                     struct qemud_network *network)
 {
+    char buf[PATH_MAX];
     char **argv;
     int ret, i;
 
@@ -924,6 +930,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;
@@ -2855,6 +2870,382 @@
     return 0;
 }
 
+/* Free the strings but NOT the underlying arrays. */
+static void
+qemudFreeDHCPHostMappings (char **hwaddrs, char **ipaddrs, char **hostnames,
+                           int nr_mappings)
+{
+    int i;
+
+    for (i = 0; i < nr_mappings; ++i) {
+        free (hwaddrs[i]);
+        free (ipaddrs[i]);
+        free (hostnames[i]);
+    }
+}
+
+/* Parse the mappings file.
+ *
+ * If hwaddrs != NULL then this allocates all three arrays and
+ * stores (allocated) pointers to the strings in each one.
+ * Use qemudFreeDHCPHostMappings to free the strings, then free
+ * the arrays by hand.
+ *
+ * Returns the number of entries read from the file.
+ */
+static int
+qemudParseDHCPHostMappingsFile (virNetworkPtr net,
+                                struct qemud_network *network,
+                                char ***hwaddrs,
+                                char ***ipaddrs,
+                                char ***hostnames)
+{
+    char buf[PATH_MAX];
+    FILE *fp;
+    int col, len;
+    int lines = 0;
+    int allocated = 0;
+    char *str;
+    char *token;
+    char *saveptr = NULL;
+
+    if (hwaddrs) {
+        *hwaddrs = NULL;
+        *ipaddrs = NULL;
+        *hostnames = 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.
+         *
+         * NB: A possible improvement here: If we can't parse the
+         * line then just store the line as-is and write it back
+         * into the file.
+         */
+        /* Chomp trailing \n character. */
+        len = strlen (buf);
+        if (len > 0 && buf[len-1] == '\n') {
+            buf[len-1] = '\0';
+            len--;
+        }
+
+        /* Ignore blank lines and comments. */
+        if (len == 0 || buf[0] == '#')
+            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 (hwaddrs) {
+                if (lines >= allocated) {
+                    char **old;
+
+                    allocated += 16;
+
+                    old = *hwaddrs;
+                    *hwaddrs = realloc (*hwaddrs, allocated * sizeof (char *));
+                    if (!*hwaddrs) {
+                        *hwaddrs = old;
+                        goto mem_error;
+                    }
+                    old = *ipaddrs;
+                    *ipaddrs = realloc (*ipaddrs, allocated * sizeof (char *));
+                    if (!*ipaddrs) {
+                        *ipaddrs = old;
+                        goto mem_error;
+                    }
+                    old = *hostnames;
+                    *hostnames = realloc (*hostnames, allocated * sizeof (char *));
+                    if (!*hostnames) {
+                        *hostnames = old;
+                        goto mem_error;
+                    }
+                }
+
+                (*hwaddrs)[lines] = strdup (hwaddr);
+                if (!(*hwaddrs)[lines]) goto mem_error;
+                (*ipaddrs)[lines] = strdup (ipaddr);
+                if (!(*ipaddrs)[lines]) goto mem_error;
+                if (hostname) {
+                    (*hostnames)[lines] = strdup (hostname);
+                    if (!(*hostnames)[lines]) goto mem_error;
+                } else
+                    (*hostnames)[lines] = NULL;
+            }
+
+            lines++;
+        }
+    }
+
+    fclose (fp);
+
+    return lines;
+
+ mem_error:
+    fclose (fp);
+
+    if (hwaddrs) {
+        qemudFreeDHCPHostMappings (*hwaddrs, *ipaddrs, *hostnames, lines);
+        free (*hwaddrs);
+        free (*ipaddrs);
+        free (*hostnames);
+    }
+
+    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, NULL, NULL);
+}
+
+static int
+qemudNetworkListDHCPHostMappings (virNetworkPtr net,
+                                  char **const hwaddrs_rtn,
+                                  char **const ipaddrs_rtn,
+                                  char **const hostnames_rtn,
+                                  int maxmappings)
+{
+    int nr_mappings, ret;
+    char **hwaddrs, **ipaddrs, **hostnames;
+    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,
+                                        &hwaddrs, &ipaddrs, &hostnames);
+    if (nr_mappings == -1) return -1;
+
+    if (maxmappings >= nr_mappings) {
+        /* The return array is large enough. */
+        memcpy (hwaddrs_rtn, hwaddrs, nr_mappings * sizeof (char *));
+        memcpy (ipaddrs_rtn, ipaddrs, nr_mappings * sizeof (char *));
+        memcpy (hostnames_rtn, hostnames, nr_mappings * sizeof (char *));
+        ret = nr_mappings;
+    } else {
+        /* Partially copy the array, free the unused mappings that
+         * we didn't copy.
+         */
+        memcpy (hwaddrs_rtn, hwaddrs, maxmappings * sizeof (char *));
+        memcpy (ipaddrs_rtn, ipaddrs, maxmappings * sizeof (char *));
+        memcpy (hostnames_rtn, hostnames, maxmappings * sizeof (char *));
+        qemudFreeDHCPHostMappings (hwaddrs + maxmappings,
+                                   ipaddrs + maxmappings,
+                                   hostnames + maxmappings,
+                                   nr_mappings - maxmappings);
+        ret = maxmappings;
+    }
+
+    free (hwaddrs);             /* NB: Just free the arrays themselves. */
+    free (ipaddrs);
+    free (hostnames);
+
+    return ret;
+}
+
+static int
+qemudNetworkAddDHCPHostMapping (virNetworkPtr net,
+                                const char *hwaddr,
+                                const char *ipaddr,
+                                const char *hostname,
+                                unsigned int flags ATTRIBUTE_UNUSED)
+{
+    char buf[PATH_MAX];
+    char **hwaddrs, **ipaddrs, **hostnames;
+    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,
+                                        &hwaddrs, &ipaddrs, &hostnames);
+    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 (hwaddrs[i], 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 (hwaddrs[i], fp);
+            fputc (',', fp);
+            fputs (ipaddrs[i], fp);
+            if (hostnames[i]) {
+                fputc (',', fp);
+                fputs (hostnames[i], fp);
+            }
+            fputc ('\n', fp);
+        }
+    }
+
+    if (!written) {
+        fputs (hwaddr, fp);
+        fputc (',', fp);
+        fputs (ipaddr, fp);
+        if (hostname) {
+            fputc (',', fp);
+            fputs (hostname, fp);
+        }
+        fputc ('\n', fp);
+    }
+
+    qemudFreeDHCPHostMappings (hwaddrs, ipaddrs, hostnames, nr_mappings);
+    free (hwaddrs);
+    free (ipaddrs);
+    free (hostnames);
+
+    if (fclose (fp) == EOF) {
+        qemudReportError (net->conn, NULL, net, VIR_ERR_SYSTEM_ERROR,
+                          "%s", strerror (errno));
+        return -1;
+    }
+
+    /* 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];
+    char **hwaddrs, **ipaddrs, **hostnames;
+    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,
+                                        &hwaddrs, &ipaddrs, &hostnames);
+    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 (hwaddrs[i], hwaddr)) {
+            /* Write this entry again. */
+            fputs (hwaddrs[i], fp);
+            fputc (',', fp);
+            fputs (ipaddrs[i], fp);
+            if (hostnames[i]) {
+                fputc (',', fp);
+                fputs (hostnames[i], fp);
+            }
+            fputc ('\n', fp);
+        }
+    }
+
+    qemudFreeDHCPHostMappings (hwaddrs, ipaddrs, hostnames, nr_mappings);
+    free (hwaddrs);
+    free (ipaddrs);
+    free (hostnames);
+
+    if (fclose (fp) == EOF) {
+        qemudReportError (net->conn, NULL, net, VIR_ERR_SYSTEM_ERROR,
+                          "%s", strerror (errno));
+        return -1;
+    }
+
+    /* Tell dnsmasq to reread the configuration file. */
+    kill (network->dnsmasqPid, SIGHUP);
+
+    return 0;
+}
+
 static virDriver qemuDriver = {
     VIR_DRV_QEMU,
     "QEMU",
@@ -2933,6 +3324,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	25 Feb 2008 16:45:35 -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 */
Index: src/remote_internal.c
===================================================================
RCS file: /data/cvs/libvirt/src/remote_internal.c,v
retrieving revision 1.60
diff -u -r1.60 remote_internal.c
--- src/remote_internal.c	20 Feb 2008 15:23:36 -0000	1.60
+++ src/remote_internal.c	25 Feb 2008 16:45:38 -0000
@@ -2681,8 +2681,115 @@
     return 0;
 }
 
+static int
+remoteNetworkNumOfDHCPHostMappings (virNetworkPtr network)
+{
+    remote_network_num_of_dhcp_host_mappings_args args;
+    remote_network_num_of_dhcp_host_mappings_ret ret;
+    GET_NETWORK_PRIVATE (network->conn, -1);
+
+    make_nonnull_network (&args.net, network);
+
+    if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_NUM_OF_DHCP_HOST_MAPPINGS,
+              (xdrproc_t) xdr_remote_network_num_of_dhcp_host_mappings_args, (char *) &args,
+              (xdrproc_t) xdr_remote_network_num_of_dhcp_host_mappings_ret, (char *) &ret) == -1)
+        return -1;
+
+    return ret.num;
+}
+
+
+static int
+remoteNetworkListDHCPHostMappings (virNetworkPtr network,
+                                   char **const hwaddrs,
+                                   char **const ipaddrs,
+                                   char **const hostnames,
+                                   int maxmappings)
+{
+    remote_network_list_dhcp_host_mappings_args args;
+    remote_network_list_dhcp_host_mappings_ret ret;
+    int i;
+    GET_NETWORK_PRIVATE (network->conn, -1);
+
+    make_nonnull_network (&args.net, network);
+    args.maxmappings = maxmappings;
+
+    if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_LIST_DHCP_HOST_MAPPINGS,
+              (xdrproc_t) xdr_remote_network_list_dhcp_host_mappings_args, (char *) &args,
+              (xdrproc_t) xdr_remote_network_list_dhcp_host_mappings_ret, (char *) &ret) == -1)
+        return -1;
+
+    if (ret.hwaddrs.hwaddrs_len > maxmappings ||
+        ret.ipaddrs.ipaddrs_len > maxmappings ||
+        ret.hostnames.hostnames_len > maxmappings) {
+        error (network->conn, VIR_ERR_INTERNAL_ERROR,
+               _("remote end returned more mappings than we asked for"));
+        return -1;
+    }
+
+    if (ret.hwaddrs.hwaddrs_len != ret.ipaddrs.ipaddrs_len ||
+        ret.ipaddrs.ipaddrs_len != ret.hostnames.hostnames_len) {
+        error (network->conn, VIR_ERR_INTERNAL_ERROR,
+               _("incoherent numbers of mappings from remote end"));
+        return -1;
+    }
+
+    /* Caller frees.  Since xdr_free will free up the strings we
+     * have to strdup them here.
+     */
+    for (i = 0; i < ret.hwaddrs.hwaddrs_len; ++i) {
+        hwaddrs[i] = strdup (ret.hwaddrs.hwaddrs_val[i]);
+        ipaddrs[i] = strdup (ret.ipaddrs.ipaddrs_val[i]);
+        /* See note in remote_protocol.x */
+        hostnames[i] =
+            STREQ (ret.hostnames.hostnames_val[i], "") ?
+            NULL : strdup (ret.hostnames.hostnames_val[i]);
+    }
+
+    return ret.hwaddrs.hwaddrs_len;
+}
+
+static int
+remoteNetworkAddDHCPHostMapping (virNetworkPtr network,
+                                 const char *hwaddr,
+                                 const char *ipaddr,
+                                 const char *hostname,
+                                 unsigned int flags)
+{
+    remote_network_add_dhcp_host_mapping_args args;
+    GET_NETWORK_PRIVATE (network->conn, -1);
+
+    make_nonnull_network (&args.net, network);
+    args.hwaddr = (char *) hwaddr;
+    args.ipaddr = (char *) ipaddr;
+    args.hostname = hostname == NULL ? NULL : (char **) &hostname;
+    args.flags = flags;
 
+    if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_ADD_DHCP_HOST_MAPPING,
+              (xdrproc_t) xdr_remote_network_add_dhcp_host_mapping_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
 
+    return 0;
+}
+
+static int
+remoteNetworkDeleteDHCPHostMapping (virNetworkPtr network,
+                                    const char *hwaddr)
+{
+    remote_network_add_dhcp_host_mapping_args args;
+    GET_NETWORK_PRIVATE (network->conn, -1);
+
+    make_nonnull_network (&args.net, network);
+    args.hwaddr = (char *) hwaddr;
+
+    if (call (network->conn, priv, 0, REMOTE_PROC_NETWORK_DELETE_DHCP_HOST_MAPPING,
+              (xdrproc_t) xdr_remote_network_delete_dhcp_host_mapping_args, (char *) &args,
+              (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
 
 /*----------------------------------------------------------------------*/
 
@@ -4671,6 +4778,10 @@
     .networkGetBridgeName = remoteNetworkGetBridgeName,
     .networkGetAutostart = remoteNetworkGetAutostart,
     .networkSetAutostart = remoteNetworkSetAutostart,
+    .networkNumOfDHCPHostMappings = remoteNetworkNumOfDHCPHostMappings,
+    .networkListDHCPHostMappings = remoteNetworkListDHCPHostMappings,
+    .networkAddDHCPHostMapping = remoteNetworkAddDHCPHostMapping,
+    .networkDeleteDHCPHostMapping = remoteNetworkDeleteDHCPHostMapping,
 };
 
 static virStorageDriver storage_driver = {
Index: src/test.c
===================================================================
RCS file: /data/cvs/libvirt/src/test.c,v
retrieving revision 1.66
diff -u -r1.66 test.c
--- src/test.c	22 Feb 2008 15:55:04 -0000	1.66
+++ src/test.c	25 Feb 2008 16:45:39 -0000
@@ -2013,6 +2013,10 @@
     testNetworkGetBridgeName, /* networkGetBridgeName */
     testNetworkGetAutostart, /* networkGetAutostart */
     testNetworkSetAutostart, /* networkSetAutostart */
+    NULL, /* networkNumOfDHCPHostMappings */
+    NULL, /* networkListDHCPHostMappings */
+    NULL, /* networkAddDHCPHostMapping */
+    NULL, /* networkDeleteDHCPHostMapping */
 };
 
 
Index: src/virsh.c
===================================================================
RCS file: /data/cvs/libvirt/src/virsh.c,v
retrieving revision 1.133
diff -u -r1.133 virsh.c
--- src/virsh.c	22 Feb 2008 15:55:04 -0000	1.133
+++ src/virsh.c	25 Feb 2008 16:45:43 -0000
@@ -2438,6 +2438,181 @@
 
 
 /*
+ * "net-dhcp-host-list" command
+ */
+static vshCmdInfo info_network_dhcp_host_list[] = {
+    {"syntax", "net-dhcp-host-list <network>"},
+    {"help", gettext_noop("list DHCP host mappings on a network")},
+    {"desc", gettext_noop("Returns list of DHCP host mappings on a network.")},
+    {NULL, NULL}
+};
+
+static vshCmdOptDef opts_network_dhcp_host_list[] = {
+    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("network name, id or uuid")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdNetworkDHCPHostList (vshControl *ctl, vshCmd *cmd)
+{
+    virNetworkPtr network;
+    int i, ret;
+    char *name;
+    char **hwaddrs, **ipaddrs, **hostnames;
+    int nr_mappings;
+
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        return FALSE;
+
+    if (!(network = vshCommandOptNetwork(ctl, cmd, "network", &name)))
+        return FALSE;
+
+    nr_mappings = virNetworkNumOfDHCPHostMappings (network);
+    if (nr_mappings == -1) {
+        virNetworkFree (network);
+        return FALSE;
+    }
+
+    ipaddrs = vshMalloc (ctl, nr_mappings * sizeof (char *));
+    hwaddrs = vshMalloc (ctl, nr_mappings * sizeof (char *));
+    hostnames = vshMalloc (ctl, nr_mappings * sizeof (char *));
+    ret =
+        virNetworkListDHCPHostMappings (network,
+                                        hwaddrs, ipaddrs, hostnames,
+                                        nr_mappings);
+
+    if (ret == -1) {
+        virNetworkFree (network);
+        free (ipaddrs);
+        free (hwaddrs);
+        free (hostnames);
+        return FALSE;
+    }
+
+    for (i = 0; i < ret; ++i) {
+        printf ("%-20s %-20s ", hwaddrs[i], ipaddrs[i]);
+        if (hostnames[i]) printf ("%s", hostnames[i]);
+        printf ("\n");
+    }
+
+    for (i = 0; i < ret; ++i) {
+        free (ipaddrs[i]);
+        free (hwaddrs[i]);
+        free (hostnames[i]);
+    }
+    free (ipaddrs);
+    free (hwaddrs);
+    free (hostnames);
+
+    virNetworkFree(network);
+
+    return TRUE;
+}
+
+
+/*
+ * "net-dhcp-host-add" command
+ */
+static vshCmdInfo info_network_dhcp_host_add[] = {
+    {"syntax", "net-dhcp-host-list <network> <hwaddr> <ipaddr> [<hostname>]"},
+    {"help", gettext_noop("add a DHCP host mappings")},
+    {"desc", gettext_noop("Adds a DHCP host mapping to the given network.")},
+    {NULL, NULL}
+};
+
+static vshCmdOptDef opts_network_dhcp_host_add[] = {
+    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("network name, id or uuid")},
+    {"hwaddr", VSH_OT_STRING, VSH_OFLAG_REQ, gettext_noop("hardware (MAC) address")},
+    {"ipaddr", VSH_OT_STRING, VSH_OFLAG_REQ, gettext_noop("IP address")},
+    {"hostname", VSH_OT_STRING, VSH_OFLAG_NONE, gettext_noop("optional hostname to assign over DHCP")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdNetworkDHCPHostAdd (vshControl *ctl, vshCmd *cmd)
+{
+    virNetworkPtr network;
+    int ret;
+    char *name;
+    char *hwaddr, *ipaddr, *hostname;
+
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        return FALSE;
+
+    if (!(network = vshCommandOptNetwork(ctl, cmd, "network", &name)))
+        return FALSE;
+
+    if (!(hwaddr = vshCommandOptString (cmd, "hwaddr", NULL))) {
+        virNetworkFree (network);
+        return FALSE;
+    }
+    if (!(ipaddr = vshCommandOptString (cmd, "ipaddr", NULL))) {
+        virNetworkFree (network);
+        return FALSE;
+    }
+
+    hostname = vshCommandOptString (cmd, "hostname", NULL);
+
+    ret = virNetworkAddDHCPHostMapping (network, hwaddr, ipaddr, hostname, 0);
+    if (ret == -1) {
+        virNetworkFree (network);
+        return FALSE;
+    }
+
+    virNetworkFree(network);
+
+    return TRUE;
+}
+
+
+/*
+ * "net-dhcp-host-delete" command
+ */
+static vshCmdInfo info_network_dhcp_host_delete[] = {
+    {"syntax", "net-dhcp-host-list <network> <hwaddr>"},
+    {"help", gettext_noop("delete a DHCP host mappings")},
+    {"desc", gettext_noop("Deletes a DHCP host mapping from the given network.")},
+    {NULL, NULL}
+};
+
+static vshCmdOptDef opts_network_dhcp_host_delete[] = {
+    {"network", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("network name, id or uuid")},
+    {"hwaddr", VSH_OT_STRING, VSH_OFLAG_REQ, gettext_noop("hardware (MAC) address")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdNetworkDHCPHostDelete (vshControl *ctl, vshCmd *cmd)
+{
+    virNetworkPtr network;
+    int ret;
+    char *name;
+    char *hwaddr;
+
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        return FALSE;
+
+    if (!(network = vshCommandOptNetwork(ctl, cmd, "network", &name)))
+        return FALSE;
+
+    if (!(hwaddr = vshCommandOptString (cmd, "hwaddr", NULL))) {
+        virNetworkFree (network);
+        return FALSE;
+    }
+
+    ret = virNetworkDeleteDHCPHostMapping (network, hwaddr);
+    if (ret == -1) {
+        virNetworkFree (network);
+        return FALSE;
+    }
+
+    virNetworkFree(network);
+
+    return TRUE;
+}
+
+
+/*
  * "net-dumpxml" command
  */
 static vshCmdInfo info_network_dumpxml[] = {
@@ -5110,6 +5285,9 @@
     {"net-create", cmdNetworkCreate, opts_network_create, info_network_create},
     {"net-define", cmdNetworkDefine, opts_network_define, info_network_define},
     {"net-destroy", cmdNetworkDestroy, opts_network_destroy, info_network_destroy},
+    {"net-dhcp-host-add", cmdNetworkDHCPHostAdd, opts_network_dhcp_host_add, info_network_dhcp_host_add},
+    {"net-dhcp-host-delete", cmdNetworkDHCPHostDelete, opts_network_dhcp_host_delete, info_network_dhcp_host_delete},
+    {"net-dhcp-host-list", cmdNetworkDHCPHostList, opts_network_dhcp_host_list, info_network_dhcp_host_list},
     {"net-dumpxml", cmdNetworkDumpXML, opts_network_dumpxml, info_network_dumpxml},
     {"net-list", cmdNetworkList, opts_network_list, info_network_list},
     {"net-name", cmdNetworkName, opts_network_name, info_network_name},


More information about the libvir-list mailing list