On 04/01/2011 06:45 AM, Michal Novotny wrote:
Make: passed
Make check: passed
Make syntax-check: passed
Hi,
this is the patch to add support for DNS hosts definition in the
network XML description to generate the the hosts file. This patch
uses the addnhosts* APIs implemented to the src/util/dnsmasq.c by
part 2 of this patch series.
Also, tests for the XML to XML definition and command-line
regression tests has been added.
Michal
Same comments about the commit message - remove the testing comments,
salutation, signature; add in a short example of the XML.
Signed-off-by: Michal Novotny<minovotn(a)redhat.com>
---
docs/formatnetwork.html.in | 7 +
docs/schemas/network.rng | 8 ++
src/conf/network_conf.c | 128 ++++++++++++++++++--
src/conf/network_conf.h | 9 ++
src/network/bridge_driver.c | 20 ++-
.../networkxml2argvdata/nat-network-dns-hosts.argv | 1 +
.../networkxml2argvdata/nat-network-dns-hosts.xml | 19 +++
tests/networkxml2argvtest.c | 1 +
tests/networkxml2xmlin/nat-network-dns-hosts.xml | 27 ++++
tests/networkxml2xmlout/nat-network-dns-hosts.xml | 27 ++++
tests/networkxml2xmltest.c | 1 +
11 files changed, 234 insertions(+), 14 deletions(-)
create mode 100644 tests/networkxml2argvdata/nat-network-dns-hosts.argv
create mode 100644 tests/networkxml2argvdata/nat-network-dns-hosts.xml
create mode 100644 tests/networkxml2xmlin/nat-network-dns-hosts.xml
create mode 100644 tests/networkxml2xmlout/nat-network-dns-hosts.xml
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 5211ed2..5d18ed9 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -222,6 +222,13 @@
separated by commas.
<span class="since">Since 0.9.1</span>
</dd>
+<dt><code>host</code></dt>
+<dd>The<code>host</code> element is the definition of DNS hosts to be
passed
+ to the DNS service. The IP address is identified by
the<code>ip</code> attribute
+ and the names for the IP addresses are identified in
the<code>hostname</code>
+ subelements of the<code>host</code> element.
+<span class="since">Since 0.9.1</span>
+</dd>
</dl>
<h2><a name="examples">Example
configuration</a></h2>
diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
index e27dace..05066a5 100644
--- a/docs/schemas/network.rng
+++ b/docs/schemas/network.rng
@@ -146,6 +146,14 @@
<attribute
name="value"><text/></attribute>
</element>
</zeroOrMore>
+<zeroOrMore>
+<element name="host">
+<attribute name="ip"><ref
name="ipv4-addr"/></attribute>
+<oneOrMore>
+<element name="hostname"><text/></element>
I think it would be better to simply call it "name" rather than
"hostname" (since "host" is already implied by its parent being
"<host>")
+</oneOrMore>
+</element>
+</zeroOrMore>
Also, this will move up a level along with the rest of <dns>.
</element>
</optional>
</element>
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index b7427d0..1f88649 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -435,6 +435,53 @@ virNetworkDHCPRangeDefParseXML(const char *networkName,
}
static int
+virNetworkDNSHostsDefParseXML(virNetworkIpDefPtr def,
(will be virNetworkDefPtr def...)
+ xmlNodePtr node,
+ virSocketAddr ip)
+{
+ xmlNodePtr cur;
+ int result = -1;
+
+ if (def->dns->hosts == NULL) {
+ if (VIR_ALLOC(def->dns->hosts)< 0)
+ goto oom_error;
+ def->dns->nhosts = 0;
+ }
+
+ cur = node->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE&&
+ xmlStrEqual(cur->name, BAD_CAST "hostname")) {
Again, s/hostname/name/
+ if (cur->children != NULL) {
+ char *hostname;
+
+ hostname = strdup((char *)cur->children->content);
+
+ if (VIR_REALLOC_N(def->dns->hosts, def->dns->nhosts +
1)< 0) {
+ VIR_FREE(hostname);
+ result = -1;
+ goto oom_error;
+ }
+
+ def->dns->hosts[def->dns->nhosts].name = strdup(hostname);
Rather than strduping hostname, then freeing it, just assign the
original directly into the struct.
+ def->dns->hosts[def->dns->nhosts].ip =
ip;
+ def->dns->nhosts++;
+
+ VIR_FREE(hostname);
+ }
+ }
+
+ cur = cur->next;
+ }
+
+ return 0;
+
+oom_error:
+ virReportOOMError();
+ return result;
+}
+
+static int
virNetworkDNSDefParseXML(virNetworkIpDefPtr def,
xmlNodePtr node)
{
@@ -476,6 +523,27 @@ virNetworkDNSDefParseXML(virNetworkIpDefPtr def,
VIR_FREE(name);
VIR_FREE(value);
+ } else if (cur->type == XML_ELEMENT_NODE&&
+ xmlStrEqual(cur->name, BAD_CAST "host")) {
+ char *ip;
+ virSocketAddr inaddr;
+ memset(&inaddr, 0, sizeof(inaddr));
+
+ if (!(ip = virXMLPropString(cur, "ip"))) {
+ cur = cur->next;
+ continue;
+ }
+ if ((ip == NULL) ||
+ (virSocketParseAddr(ip,&inaddr, AF_UNSPEC)< 0)) {
+ virNetworkReportError(VIR_ERR_XML_DETAIL,
+ _("Missing IP address in DNS host
definition"));
"Missing/incorrect". Also, since def will end up being virNetworkDefPtr,
you can add the name of the network, as well as the string that was
given for ip. That will help in finding the source of the error.
+ VIR_FREE(ip);
+ goto error;
+ }
+ VIR_FREE(ip);
+ result = virNetworkDNSHostsDefParseXML(def, cur, inaddr);
+ if (result)
+ goto error;
}
cur = cur->next;
@@ -485,6 +553,7 @@ virNetworkDNSDefParseXML(virNetworkIpDefPtr def,
oom_error:
virReportOOMError();
+error:
return result;
}
@@ -888,17 +957,60 @@ virNetworkIpDefFormat(virBufferPtr buf,
virBufferAddLit(buf, "</dhcp>\n");
}
- if ((def->dns != NULL)&& (def->dns->ntxtrecords)) {
- int ii;
-
+ if (def->dns != NULL) {
virBufferAddLit(buf, "<dns>\n");
- for (ii = 0 ; ii< def->dns->ntxtrecords ; ii++) {
- virBufferVSprintf(buf, "<txt-record name='%s'
value='%s' />\n",
- def->dns->txtrecords[ii].name,
- def->dns->txtrecords[ii].value);
+
+ if (def->dns->ntxtrecords) {
This if() should have been in the patch for <txt>. That way this patch
wouldn't be polluted with diffs unrelated to the new feature introduced
in this patch.
+ int ii;
+
+ for (ii = 0 ; ii< def->dns->ntxtrecords; ii++) {
+ virBufferVSprintf(buf, "<txt-record name='%s'
value='%s' />\n",
+ def->dns->txtrecords[ii].name,
+ def->dns->txtrecords[ii].value);
+ }
+ }
+ if (def->dns->nhosts) {
+ int ii, j;
+ char **iplist = NULL;
+ int iplist_size = 0;
+ bool in_list;
+
+ if (VIR_ALLOC(iplist)< 0)
+ goto error;
+
+ for (ii = 0 ; ii< def->dns->nhosts; ii++) {
+ char *ip = virSocketFormatAddr(&def->dns->hosts[ii].ip);
+ in_list = false;
+ for (j = 0; j< iplist_size; j++)
+ if (STREQ(iplist[j], ip))
+ in_list = true;
+
+ if (!in_list) {
+ virBufferVSprintf(buf, "<host ip='%s'>\n",
ip);
+
+ for (j = 0 ; j< def->dns->nhosts; j++) {
+ char *thisip =
virSocketFormatAddr(&def->dns->hosts[j].ip);
+ if (STREQ(ip, thisip))
+ virBufferVSprintf(buf,
"<hostname>%s</hostname>\n",
+ def->dns->hosts[j].name);
+ }
+ virBufferVSprintf(buf, "</host>\n");
+
+ if (VIR_REALLOC_N(iplist, iplist_size + 1)< 0)
+ goto error;
+
+ iplist[iplist_size] = strdup(ip);
+ iplist_size++;
+ }
+ }
+
+ for (j = 0; j< iplist_size; j++)
+ VIR_FREE(iplist[j]);
+ VIR_FREE(iplist);
It would make more sense if virNetworkDNSHostsDef->name was char** and
had an array of names, as you did in PATCH 4/5. That would make the
object a more direct representation of the XML data, and eliminate all
of this complicated code dealing with "iplist" and nested loops.
}
+
virBufferAddLit(buf, "</dns>\n");
- }
+ }
virBufferAddLit(buf, "</ip>\n");
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 5f47595..305ef0f 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -64,9 +64,18 @@ struct _virNetworkDNSTxtRecordsDef {
char *value;
};
+struct virNetworkDNSHostsDef {
+ virSocketAddr ip;
+ char *name;
Again - make name char** as you did in PATCH 4/5.
+} virNetworkDNSHostsDef;
+
+typedef struct virNetworkDNSHostsDef *virNetworkDNSHostsDefPtr;
+
struct virNetworkDNSDef {
unsigned int ntxtrecords;
+ unsigned int nhosts;
virNetworkDNSTxtRecordsDefPtr txtrecords;
+ virNetworkDNSHostsDefPtr hosts;
} virNetworkDNSDef;
typedef struct virNetworkDNSDef *virNetworkDNSDefPtr;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 4ad3143..99a61b0 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -434,13 +434,20 @@ networkSaveDnsmasqHostsfile(virNetworkIpDefPtr ipdef,
This function is going to also need a virNetworkDefPtr arg if dns moves
up a level.
goto cleanup;
}
- if (! force&& virFileExists(dctx->hostsfile->path))
- return 0;
+ if (!(! force&& virFileExists(dctx->hostsfile->path))) {
That is much easier to understand if written as:
if (force || !virFileExists(dctx->hostsfile->path)
+ for (i = 0; i< ipdef->nhosts; i++) {
+ virNetworkDHCPHostDefPtr host =&(ipdef->hosts[i]);
+ if ((host->mac)&& VIR_SOCKET_HAS_ADDR(&host->ip))
+ dnsmasqAddDhcpHost(dctx, host->mac,&host->ip, host->name);
+ }
+ }
- for (i = 0; i< ipdef->nhosts; i++) {
- virNetworkDHCPHostDefPtr host =&(ipdef->hosts[i]);
- if ((host->mac)&& VIR_SOCKET_HAS_ADDR(&host->ip))
- dnsmasqAddDhcpHost(dctx, host->mac,&host->ip, host->name);
+ if (ipdef->dns) {
+ for (i = 0; i< ipdef->dns->nhosts; i++) {
+ virNetworkDNSHostsDefPtr host =&(ipdef->dns->hosts[i]);
+ if (VIR_SOCKET_HAS_ADDR(&host->ip))
+ dnsmasqAddHost(dctx,&host->ip, host->name);
If you change the virNetworkDNSHostDefPtr to contain char **name instead
of char *name, and dnsmasqAddHost to accept char** rather than char*,
this can remain pretty much unchanged.
+ }
}
if (dnsmasqSave(dctx)< 0)
@@ -589,6 +596,7 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
if (dctx->hostsfile->nhosts)
virCommandAddArgPair(cmd, "--dhcp-hostsfile",
dctx->hostsfile->path);
+ VIR_DEBUG("ADDN HOSTS: %d => %p",
dctx->addnhostsfile->nhosts, ipdef->dns);
Did you mean to leave this DEBUG in?
if (dctx->addnhostsfile->nhosts)
virCommandAddArgPair(cmd, "--addn-hosts",
dctx->addnhostsfile->path);
diff --git a/tests/networkxml2argvdata/nat-network-dns-hosts.argv
b/tests/networkxml2argvdata/nat-network-dns-hosts.argv
new file mode 100644
index 0000000..99dc724
--- /dev/null
+++ b/tests/networkxml2argvdata/nat-network-dns-hosts.argv
@@ -0,0 +1 @@
+/usr/sbin/dnsmasq --strict-order --bind-interfaces
--pid-file=/var/run/libvirt/network/default.pid --conf-file= --except-interface lo
--listen-address 192.168.122.1 --dhcp-range 192.168.122.2,192.168.122.254
--dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases --dhcp-lease-max=253
--dhcp-no-override --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
--addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
diff --git a/tests/networkxml2argvdata/nat-network-dns-hosts.xml
b/tests/networkxml2argvdata/nat-network-dns-hosts.xml
new file mode 100644
index 0000000..35ee151
--- /dev/null
+++ b/tests/networkxml2argvdata/nat-network-dns-hosts.xml
@@ -0,0 +1,19 @@
+<network>
+<name>default</name>
+<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+<forward dev='eth1' mode='nat'/>
+<bridge name='virbr0' stp='on' delay='0' />
+<ip address='192.168.122.1' netmask='255.255.255.0'>
+<dhcp>
+<range start='192.168.122.2' end='192.168.122.254' />
+<host mac='00:16:3e:77:e2:ed' name='a.example.com'
ip='192.168.122.10' />
+<host mac='00:16:3e:3e:a9:1a' name='b.example.com'
ip='192.168.122.11' />
+</dhcp>
+<dns>
+<host ip='192.168.122.1'>
+<hostname>host</hostname>
+<hostname>gateway</hostname>
+</host>
+</dns>
+</ip>
+</network>
diff --git a/tests/networkxml2argvtest.c b/tests/networkxml2argvtest.c
index e3a8bb4..ce09206 100644
--- a/tests/networkxml2argvtest.c
+++ b/tests/networkxml2argvtest.c
@@ -112,6 +112,7 @@ mymain(int argc, char **argv)
DO_TEST("netboot-network");
DO_TEST("netboot-proxy-network");
DO_TEST("nat-network-dns-txt-record");
+ DO_TEST("nat-network-dns-hosts");
return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
diff --git a/tests/networkxml2xmlin/nat-network-dns-hosts.xml
b/tests/networkxml2xmlin/nat-network-dns-hosts.xml
new file mode 100644
index 0000000..fe545cf
--- /dev/null
+++ b/tests/networkxml2xmlin/nat-network-dns-hosts.xml
@@ -0,0 +1,27 @@
+<network>
+<name>default</name>
+<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+<forward dev='eth1' mode='nat'/>
+<bridge name='virbr0' stp='on' delay='0' />
+<ip address='192.168.122.1' netmask='255.255.255.0'>
+<dhcp>
+<range start='192.168.122.2' end='192.168.122.254' />
+<host mac='00:16:3e:77:e2:ed' name='a.example.com'
ip='192.168.122.10' />
+<host mac='00:16:3e:3e:a9:1a' name='b.example.com'
ip='192.168.122.11' />
+</dhcp>
+<dns>
+<host ip='192.168.122.1'>
+<hostname>host</hostname>
+<hostname>gateway</hostname>
+</host>
+</dns>
+</ip>
+<ip family='ipv4' address='192.168.123.1'
netmask='255.255.255.0'>
+</ip>
+<ip family='ipv6' address='2001:db8:ac10:fe01::1'
prefix='64'>
+</ip>
+<ip family='ipv6' address='2001:db8:ac10:fd01::1'
prefix='64'>
+</ip>
+<ip family='ipv4' address='10.24.10.1'>
+</ip>
+</network>
diff --git a/tests/networkxml2xmlout/nat-network-dns-hosts.xml
b/tests/networkxml2xmlout/nat-network-dns-hosts.xml
new file mode 100644
index 0000000..fe545cf
--- /dev/null
+++ b/tests/networkxml2xmlout/nat-network-dns-hosts.xml
@@ -0,0 +1,27 @@
+<network>
+<name>default</name>
+<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+<forward dev='eth1' mode='nat'/>
+<bridge name='virbr0' stp='on' delay='0' />
+<ip address='192.168.122.1' netmask='255.255.255.0'>
+<dhcp>
+<range start='192.168.122.2' end='192.168.122.254' />
+<host mac='00:16:3e:77:e2:ed' name='a.example.com'
ip='192.168.122.10' />
+<host mac='00:16:3e:3e:a9:1a' name='b.example.com'
ip='192.168.122.11' />
+</dhcp>
+<dns>
+<host ip='192.168.122.1'>
+<hostname>host</hostname>
+<hostname>gateway</hostname>
+</host>
+</dns>
+</ip>
+<ip family='ipv4' address='192.168.123.1'
netmask='255.255.255.0'>
+</ip>
+<ip family='ipv6' address='2001:db8:ac10:fe01::1'
prefix='64'>
+</ip>
+<ip family='ipv6' address='2001:db8:ac10:fd01::1'
prefix='64'>
+</ip>
+<ip family='ipv4' address='10.24.10.1'>
+</ip>
+</network>
diff --git a/tests/networkxml2xmltest.c b/tests/networkxml2xmltest.c
index beb00ef..f5c5715 100644
--- a/tests/networkxml2xmltest.c
+++ b/tests/networkxml2xmltest.c
@@ -91,6 +91,7 @@ mymain(int argc, char **argv)
DO_TEST("netboot-network");
DO_TEST("netboot-proxy-network");
DO_TEST("nat-network-dns-txt-record");
+ DO_TEST("nat-network-dns-hosts");
return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
}