aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/conf/network_conf.c180
1 files changed, 173 insertions, 7 deletions
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 2a65f1db9..db398ae89 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -2261,7 +2261,6 @@ virNetworkDefUpdateNoSupport(virNetworkDefPtr def, const char *section)
section, def->name);
}
-#if 0
static int
virNetworkDefUpdateCheckElementName(virNetworkDefPtr def,
xmlNodePtr node,
@@ -2276,7 +2275,6 @@ virNetworkDefUpdateCheckElementName(virNetworkDefPtr def,
}
return 0;
}
-#endif
static int
virNetworkDefUpdateBridge(virNetworkDefPtr def,
@@ -2314,16 +2312,184 @@ virNetworkDefUpdateIP(virNetworkDefPtr def,
return -1;
}
+static virNetworkIpDefPtr
+virNetworkIpDefByIndex(virNetworkDefPtr def, int parentIndex)
+{
+ virNetworkIpDefPtr ipdef = NULL;
+ int ii;
+
+ /* first find which ip element's dhcp host list to work on */
+ if (parentIndex >= 0) {
+ ipdef = virNetworkDefGetIpByIndex(def, AF_UNSPEC, parentIndex);
+ if (!(ipdef &&
+ VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET))) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("couldn't update dhcp host entry - "
+ "no <ip family='ipv4'> "
+ "element found at index %d in network '%s'"),
+ parentIndex, def->name);
+ }
+ return ipdef;
+ }
+
+ /* -1 means "find the most appropriate", which in this case
+ * means the one and only <ip> that has <dhcp> element
+ */
+ for (ii = 0;
+ (ipdef = virNetworkDefGetIpByIndex(def, AF_UNSPEC, ii));
+ ii++) {
+ if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET) &&
+ (ipdef->nranges || ipdef->nhosts)) {
+ break;
+ }
+ }
+ if (!ipdef)
+ ipdef = virNetworkDefGetIpByIndex(def, AF_INET, 0);
+ if (!ipdef) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("couldn't update dhcp host entry - "
+ "no <ip family='ipv4'> "
+ "element found in network '%s'"), def->name);
+ }
+ return ipdef;
+}
+
static int
virNetworkDefUpdateIPDHCPHost(virNetworkDefPtr def,
- unsigned int command ATTRIBUTE_UNUSED,
- int parentIndex ATTRIBUTE_UNUSED,
- xmlXPathContextPtr ctxt ATTRIBUTE_UNUSED,
+ unsigned int command,
+ int parentIndex,
+ xmlXPathContextPtr ctxt,
/* virNetworkUpdateFlags */
unsigned int fflags ATTRIBUTE_UNUSED)
{
- virNetworkDefUpdateNoSupport(def, "ip dhcp host");
- return -1;
+ int ii, ret = -1;
+ virNetworkIpDefPtr ipdef = virNetworkIpDefByIndex(def, parentIndex);
+ virNetworkDHCPHostDef host;
+
+ memset(&host, 0, sizeof(host));
+
+ if (virNetworkDefUpdateCheckElementName(def, ctxt->node, "host") < 0)
+ goto cleanup;
+
+ /* ipdef is the ip element that needs its host array updated */
+ if (!ipdef)
+ goto cleanup;
+
+ /* parse the xml into a virNetworkDHCPHostDef */
+ if (command == VIR_NETWORK_UPDATE_COMMAND_MODIFY) {
+
+ if (virNetworkDHCPHostDefParse(def->name, ctxt->node, &host, false) < 0)
+ goto cleanup;
+
+ /* search for the entry with this (mac|name),
+ * and update the IP+(mac|name) */
+ for (ii = 0; ii < ipdef->nhosts; ii++) {
+ if ((host.mac &&
+ !virMacAddrCompare(host.mac, ipdef->hosts[ii].mac)) ||
+ (host.name &&
+ STREQ_NULLABLE(host.name, ipdef->hosts[ii].name))) {
+ break;
+ }
+ }
+
+ if (ii == ipdef->nhosts) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("couldn't locate an existing dhcp host entry with "
+ "\"mac='%s'\" in network '%s'"),
+ host.mac, def->name);
+ goto cleanup;
+ }
+
+ /* clear the existing hosts entry, move the new one in its place,
+ * then clear out the extra copy to get rid of the duplicate pointers
+ * to its data (mac and name strings).
+ */
+ virNetworkDHCPHostDefClear(&ipdef->hosts[ii]);
+ ipdef->hosts[ii] = host;
+ memset(&host, 0, sizeof(host));
+
+ } else if ((command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST) ||
+ (command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST)) {
+
+ if (virNetworkDHCPHostDefParse(def->name, ctxt->node, &host, true) < 0)
+ goto cleanup;
+
+ /* log error if an entry with same name/address/ip already exists */
+ for (ii = 0; ii < ipdef->nhosts; ii++) {
+ if ((host.mac &&
+ !virMacAddrCompare(host.mac, ipdef->hosts[ii].mac)) ||
+ (host.name &&
+ STREQ_NULLABLE(host.name, ipdef->hosts[ii].name)) ||
+ (VIR_SOCKET_ADDR_VALID(&host.ip) &&
+ virSocketAddrEqual(&host.ip, &ipdef->hosts[ii].ip))) {
+ char *ip = virSocketAddrFormat(&host.ip);
+
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("there is an existing dhcp host entry in "
+ "network '%s' that matches "
+ "\"<host mac='%s' name='%s' ip='%s'/>\""),
+ def->name, host.mac, host.name,
+ ip ? ip : "unknown");
+ VIR_FREE(ip);
+ goto cleanup;
+ }
+ }
+ /* add to beginning/end of list */
+ if (VIR_REALLOC_N(ipdef->hosts, ipdef->nhosts +1) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (command == VIR_NETWORK_UPDATE_COMMAND_ADD_LAST) {
+
+ ipdef->hosts[ipdef->nhosts] = host;
+ ipdef->nhosts++;
+ memset(&host, 0, sizeof(host));
+
+ } else { /* implied (command == VIR_NETWORK_UPDATE_COMMAND_ADD_FIRST) */
+
+ memmove(ipdef->hosts + 1, ipdef->hosts,
+ sizeof(ipdef->hosts) * ipdef->nhosts);
+ ipdef->hosts[0] = host;
+ ipdef->nhosts++;
+ memset(&host, 0, sizeof(host));
+ }
+
+ } else if (command == VIR_NETWORK_UPDATE_COMMAND_DELETE) {
+
+ if (virNetworkDHCPHostDefParse(def->name, ctxt->node, &host, false) < 0)
+ goto cleanup;
+
+ /* find matching entry - all specified attributes must match */
+ for (ii = 0; ii < ipdef->nhosts; ii++) {
+ if ((!host.mac ||
+ !virMacAddrCompare(host.mac, ipdef->hosts[ii].mac)) &&
+ (!host.name ||
+ STREQ_NULLABLE(host.name, ipdef->hosts[ii].name)) &&
+ (!VIR_SOCKET_ADDR_VALID(&host.ip) ||
+ virSocketAddrEqual(&host.ip, &ipdef->hosts[ii].ip))) {
+ break;
+ }
+ }
+ if (ii == ipdef->nhosts) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("couldn't locate a matching dhcp host entry "
+ "in network '%s'"), def->name);
+ goto cleanup;
+ }
+
+ /* remove it */
+ virNetworkDHCPHostDefClear(&ipdef->hosts[ii]);
+ memmove(ipdef->hosts + ii, ipdef->hosts + ii + 1,
+ sizeof(ipdef->hosts) * ipdef->nhosts - ii - 1);
+ ipdef->nhosts--;
+ ignore_value(VIR_REALLOC_N(ipdef->hosts, ipdef->nhosts));
+ }
+
+ ret = 0;
+cleanup:
+ virNetworkDHCPHostDefClear(&host);
+ return ret;
}
static int