I've attached a diff at the end that will address all the problems I've
brought up in my review. I can either squash that in and push the result
(once the rest of the series is reviewed and ready), or Michal can
squash it into his local tree and resubmit.
On 06/13/2011 12:55 PM, Michal Novotny wrote:
This commit introduces the<dns> element and<txt> record
for the virtual DNS
network. The DNS TXT record can be defined using following syntax in the
network XML file:
<dns>
<txt name="example" value="example value" />
</dns>
Signed-off-by: Michal Novotny<minovotn(a)redhat.com>
---
docs/formatnetwork.html.in | 19 ++++
docs/schemas/network.rng | 13 +++
src/conf/network_conf.c | 97 ++++++++++++++++++++
src/conf/network_conf.h | 16 +++
src/network/bridge_driver.c | 21 ++++-
.../nat-network-dns-txt-record.xml | 24 +++++
.../nat-network-dns-txt-record.xml | 24 +++++
tests/networkxml2xmltest.c | 1 +
8 files changed, 214 insertions(+), 1 deletions(-)
create mode 100644 tests/networkxml2xmlin/nat-network-dns-txt-record.xml
create mode 100644 tests/networkxml2xmlout/nat-network-dns-txt-record.xml
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 589aaff..008897d 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -114,6 +114,9 @@
<pre>
...
<mac address='00:16:3E:5D:C7:9E'/>
+<dns>
+<txt name="example" value="example value" />
+</dns>
<ip address="192.168.122.1"
netmask="255.255.255.0">
<dhcp>
<range start="192.168.122.100"
end="192.168.122.254" />
@@ -166,6 +169,22 @@
supported for IPv6 addresses, can only be specified on a single IPv4 address
per network.
<span class="since">Since 0.7.1</span>
+
+</dd><dt><code>dns</code></dt><dd>
+ The dns element of a network contains configuration information for the
+ virtual network's DNS server.<span class="since">Since
0.9.1</span>
s/0.9.1/0.9.3/
+ Currently supported elements are:
+</dd>
+<dt><code>txt</code></dt>
+<dd>A<code>dns</code> element can have 0 or
more<code>txt</code> elements.
+ Each txt element defines a DNS TXT record and has two attributes, both
+ required: a name that can be queried via dns, and a value that will be
+ returned when that name is queried. names cannot contain embedded spaces
+ or commas. value is a single string that can contain multiple values which are
+ comma-separated which is allowed for the TXT records and it is represented a
+ single value.<span class="since">Since 0.9.1</span>
"a single string which can contain multiple values separated by commas."
seems more concise.
and again, s/0.9.1/0.9.3/
+</dd>
+
</dd><dt><code>dhcp</code></dt><dd>Also
within the<code>ip</code> element there is an
optional<code>dhcp</code> element. The presence of this element
enables DHCP services on the virtual network. It will further
diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
index 6d01b06..3780af5 100644
--- a/docs/schemas/network.rng
+++ b/docs/schemas/network.rng
@@ -87,6 +87,19 @@
</element>
</optional>
+<!-- Define the DNS related elements like TXT records
+ and other features in the<dns> element -->
+<optional>
+<element name="dns">
+<zeroOrMore>
+<element name="txt">
+<attribute name="name"><text/></attribute>
I see that Daniel recommends restricting this to A-Z, a-z, and -. I
couldn't figure out from RFC 1035 if that's really what's intended (and
am not sure if there's another later RFC that amends 1035). But since
this is currently only used for tests, I guess it's okay to be too
restrictive; we can always loosen it up later if we need to.
+<attribute
name="value"><text/></attribute>
+</element>
+</zeroOrMore>
+</element>
+</optional>
+
<!--<ip> element -->
<zeroOrMore>
<!-- The IP element sets up NAT'ing and an optional DHCP server
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index e4765ea..133ac84 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
There's no code here to critique, because you totally left it out - you
aren't free'ing the virNetworkDNSDef when you free the virNetworkDef.
@@ -435,6 +435,69 @@ virNetworkDHCPRangeDefParseXML(const char
*networkName,
}
static int
+virNetworkDNSDefParseXML(virNetworkDNSDefPtr *dnsdef,
+ xmlNodePtr node)
+{
+ xmlNodePtr cur;
+ int ret = -1;
+ char *name = NULL;
+ char *value = NULL;
+ virNetworkDNSDefPtr def = NULL;
+
+ if (VIR_ALLOC(def)) {
+ virReportOOMError();
+ goto error;
+ }
+
+ cur = node->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE&&
+ xmlStrEqual(cur->name, BAD_CAST "txt")) {
+ if (!(name = virXMLPropString(cur, "name"))) {
+ cur = cur->next;
+ continue;
+ }
+ if (!(value = virXMLPropString(cur, "value"))) {
+ VIR_FREE(name);
+ cur = cur->next;
+ continue;
+ }
These should be errors rather than silently ignoring the record.
+
+ if (strchr(name, ' ') != NULL) {
+ virNetworkReportError(VIR_ERR_XML_DETAIL,
+ _("TXT record names in DNS don't support
spaces in names (name is '%s')"), name);
+ goto error;
+ }
+
+ if (VIR_REALLOC_N(def->txtrecords, def->ntxtrecords + 1)< 0) {
+ virReportOOMError();
+ goto error;
+ }
+
+ def->txtrecords[def->ntxtrecords].name = name;
+ def->txtrecords[def->ntxtrecords].value = value;
+ def->ntxtrecords++;
+ }
+
+ cur = cur->next;
+ }
+
+ ret = 0;
+error:
+ if (ret< 0) {
+ name = NULL;
+ value = NULL;
Uh, I don't think you want those assignments there :-)
+ VIR_FREE(name);
+ VIR_FREE(value);
Also you need to free def here, since you're not returning it.
+ }
+ else
+ if (dnsdef != NULL)
+ *dnsdef = def;
This isn't proper style for libvirt. The "}", "else", and
"if" should
be on the same line, and the else clause needs to have brackets around
it (since the if clause has brackets).
Also, dnsdef had *better* be non-NULL, or 1) you'll be leaking the
virNetworkDNSDef, and 2) it would be pointless to call this function,
since the whole purpose is to return the new DNSDef. Since you know it
will always be non-NULL, we can just leave the if condition out of the else.
+
+ return ret;
+}
+
+static int
virNetworkIPParseXML(const char *networkName,
virNetworkIpDefPtr def,
xmlNodePtr node,
@@ -584,6 +647,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
virNetworkDefPtr def;
char *tmp;
xmlNodePtr *ipNodes = NULL;
+ xmlNodePtr dnsNode = NULL;
int nIps;
if (VIR_ALLOC(def)< 0) {
@@ -641,6 +705,12 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
def->mac_specified = true;
}
+ dnsNode = virXPathNode("./dns", ctxt);
+ if (dnsNode != NULL) {
+ if (virNetworkDNSDefParseXML(&def->dns, dnsNode)< 0)
+ goto error;
+ }
+
nIps = virXPathNodeSet("./ip", ctxt,&ipNodes);
if (nIps< 0)
goto error;
@@ -695,6 +765,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
error:
virNetworkDefFree(def);
VIR_FREE(ipNodes);
+ VIR_FREE(dnsNode);
virXPathNode doesn't allocate anything, it just returns an existing
pointer, so dnsNode shouldn't be freed.
return NULL;
}
@@ -751,6 +822,29 @@ cleanup:
}
static int
+virNetworkDNSDefFormat(virBufferPtr buf,
+ virNetworkDNSDefPtr def)
+{
+ int result = 0;
+ int i;
+
+ if (def == NULL)
+ goto out;
+
+ virBufferAddLit(buf, "<dns>\n");
+
+ for (i = 0 ; i< def->ntxtrecords ; i++) {
+ virBufferAsprintf(buf, "<txt name='%s' value='%s'
/>\n",
+ def->txtrecords[i].name,
+ def->txtrecords[i].value);
+ }
+
+ virBufferAddLit(buf, "</dns>\n");
+out:
+ return result;
+}
+
+static int
virNetworkIpDefFormat(virBufferPtr buf,
const virNetworkIpDefPtr def)
{
@@ -881,6 +975,9 @@ char *virNetworkDefFormat(const virNetworkDefPtr def)
if (def->domain)
virBufferAsprintf(&buf, "<domain name='%s'/>\n",
def->domain);
+ if (virNetworkDNSDefFormat(&buf, def->dns)< 0)
+ goto error;
+
for (ii = 0; ii< def->nips; ii++) {
if (virNetworkIpDefFormat(&buf,&def->ips[ii])< 0)
goto error;
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 281124b..d0dac02 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -57,6 +57,20 @@ struct _virNetworkDHCPHostDef {
virSocketAddr ip;
};
+typedef struct _virNetworkDNSTxtRecordsDef virNetworkDNSTxtRecordsDef;
+typedef virNetworkDNSTxtRecordsDef *virNetworkDNSTxtRecordsDefPtr;
+struct _virNetworkDNSTxtRecordsDef {
+ char *name;
+ char *value;
+};
+
+struct virNetworkDNSDef {
+ unsigned int ntxtrecords;
+ virNetworkDNSTxtRecordsDefPtr txtrecords;
+} virNetworkDNSDef;
+
+typedef struct virNetworkDNSDef *virNetworkDNSDefPtr;
+
typedef struct _virNetworkIpDef virNetworkIpDef;
typedef virNetworkIpDef *virNetworkIpDefPtr;
struct _virNetworkIpDef {
@@ -101,6 +115,8 @@ struct _virNetworkDef {
size_t nips;
virNetworkIpDefPtr ips; /* ptr to array of IP addresses on this network */
+
+ virNetworkDNSDefPtr dns; /* ptr to dns related configuration */
};
typedef struct _virNetworkObj virNetworkObj;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 5e4b4d9..169bc08 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -456,7 +456,6 @@ networkSaveDnsmasqHostsfile(virNetworkIpDefPtr ipdef,
return 0;
}
-
static int
networkBuildDnsmasqArgv(virNetworkObjPtr network,
virNetworkIpDefPtr ipdef,
@@ -511,6 +510,26 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
if (network->def->forwardType == VIR_NETWORK_FORWARD_NONE)
virCommandAddArg(cmd, "--dhcp-option=3");
+ if (network->def->dns != NULL) {
+ virNetworkDNSDefPtr dns = network->def->dns;
+ int i;
+
+ for (i = 0; i< dns->ntxtrecords; i++) {
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ virBufferAsprintf(&buf, "%s,%s",
+ dns->txtrecords[i].name,
+ dns->txtrecords[i].value);
I can't see a reason for using virBufferAsprintf() here - since you have
a small, fixed number of args, virAsprintf() would do just as well.
+
+ if (virBufferError(&buf)) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ virCommandAddArgPair(cmd, "--txt-record",
virBufferContentAndReset(&buf));
+ virBufferFreeAndReset(&buf);
This is redundant.
+ }
+ }
+
/*
* --interface does not actually work with dnsmasq< 2.47,
* due to DAD for ipv6 addresses on the interface.
diff --git a/tests/networkxml2xmlin/nat-network-dns-txt-record.xml
b/tests/networkxml2xmlin/nat-network-dns-txt-record.xml
new file mode 100644
index 0000000..bd16976
--- /dev/null
+++ b/tests/networkxml2xmlin/nat-network-dns-txt-record.xml
@@ -0,0 +1,24 @@
+<network>
+<name>default</name>
+<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+<forward dev='eth1' mode='nat'/>
+<bridge name='virbr0' stp='on' delay='0' />
+<dns>
+<txt name='example' value='example value' />
+</dns>
+<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>
+</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-txt-record.xml
b/tests/networkxml2xmlout/nat-network-dns-txt-record.xml
new file mode 100644
index 0000000..bd16976
--- /dev/null
+++ b/tests/networkxml2xmlout/nat-network-dns-txt-record.xml
@@ -0,0 +1,24 @@
+<network>
+<name>default</name>
+<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+<forward dev='eth1' mode='nat'/>
+<bridge name='virbr0' stp='on' delay='0' />
+<dns>
+<txt name='example' value='example value' />
+</dns>
+<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>
+</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 468785b..2cc8e56 100644
--- a/tests/networkxml2xmltest.c
+++ b/tests/networkxml2xmltest.c
@@ -86,6 +86,7 @@ mymain(void)
DO_TEST("nat-network");
DO_TEST("netboot-network");
DO_TEST("netboot-proxy-network");
+ DO_TEST("nat-network-dns-txt-record");
return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
}