[libvirt] [PATCH v8 3/4] domifaddr: Implement the API for qemu

John Ferlan jferlan at redhat.com
Fri Feb 13 18:35:01 UTC 2015



On 01/25/2015 01:38 PM, Nehal J Wani wrote:
> By querying the qemu guest agent with the QMP command
> "guest-network-get-interfaces" and converting the received JSON
> output to structured objects.
> 
> Although "ifconfig" is deprecated, IP aliases created by "ifconfig"
> are supported by this API. The legacy syntax of an IP alias is:
> "<ifname>:<alias-name>". Since we want all aliases to be clubbed
> under parent interface, simply stripping ":<alias-name>" suffices.
> Note that IP aliases formed by "ip" aren't visible to "ifconfig",
> and aliases created by "ip" do not have any specific name. But
> we are lucky, as qemu guest agent detects aliases created by both.
> 
> src/qemu/qemu_agent.h:
>   * Define qemuAgentGetInterfaces
> 
> src/qemu/qemu_agent.c:
>   * Implement qemuAgentGetInterface
> 
> src/qemu/qemu_driver.c:
>   * New function qemuGetDHCPInterfaces
>   * New function qemuDomainInterfaceAddresses
> 
> src/remote_protocol-sructs:
>   * Define new structs
> 
> tests/qemuagenttest.c:
>   * Add new test: testQemuAgentGetInterfaces
>     Test cases for IP aliases, 0 or multiple ipv4/ipv6 address(es)
> 
> Signed-off-by: Nehal J Wani <nehaljw.kkd1 at gmail.com>
> ---
>  src/qemu/qemu_agent.c  | 202 +++++++++++++++++++++++++++++++++++++++++++++++++
>  src/qemu/qemu_agent.h  |   4 +
>  src/qemu/qemu_driver.c | 173 ++++++++++++++++++++++++++++++++++++++++++
>  tests/qemuagenttest.c  | 188 +++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 567 insertions(+)
> 
> diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c
> index 5fcc40f..e881cdc 100644
> --- a/src/qemu/qemu_agent.c
> +++ b/src/qemu/qemu_agent.c
> @@ -1953,3 +1953,205 @@ qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr **info,
>      virJSONValueFree(reply);
>      return ret;
>  }
> +
> +/*
> + * qemuAgentGetInterfaces:
> + * @mon: Agent monitor
> + * @ifaces: pointer to an array of pointers pointing to interface objects
> + *
> + * Issue guest-network-get-interfaces to guest agent, which returns a
> + * list of interfaces of a running domain along with their IP and MAC
> + * addresses.
> + *
> + * Returns: number of interfaces on success, -1 on error.
> + */
> +int
> +qemuAgentGetInterfaces(qemuAgentPtr mon,
> +                       virDomainInterfacePtr **ifaces)
> +{
> +    int ret = -1;
> +    size_t i, j;
> +    int size = -1;
> +    virJSONValuePtr cmd = NULL;
> +    virJSONValuePtr reply = NULL;
> +    virJSONValuePtr ret_array = NULL;
> +    size_t ifaces_count = 0;
> +    size_t addrs_count = 0;
> +    virDomainInterfacePtr *ifaces_ret = NULL;
> +    virHashTablePtr ifaces_store = NULL;
> +    char **ifname = NULL;
> +
> +    /* Hash table to handle the interface alias */
> +    if (!(ifaces_store = virHashCreate(ifaces_count, NULL))) {
> +        virHashFree(ifaces_store);
> +        return -1;
> +    }
> +
> +    if (!(cmd = qemuAgentMakeCommand("guest-network-get-interfaces", NULL)))
> +        goto cleanup;
> +
> +    if (qemuAgentCommand(mon, cmd, &reply, false, VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0 ||
> +        qemuAgentCheckError(cmd, reply) < 0) {
> +        goto cleanup;
> +    }
> +
> +    if (!(ret_array = virJSONValueObjectGet(reply, "return"))) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                       _("qemu agent didn't provide 'return' field"));
> +        goto cleanup;
> +    }
> +
> +    if ((size = virJSONValueArraySize(ret_array)) < 0) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                       _("qemu agent didn't return an array of interfaces"));
> +        goto cleanup;
> +    }
> +
> +    for (i = 0; i < size; i++) {
> +        virJSONValuePtr tmp_iface = virJSONValueArrayGet(ret_array, i);
> +        virJSONValuePtr ip_addr_arr = NULL;
> +        const char *hwaddr, *ifname_s, *name = NULL;
> +        int ip_addr_arr_size;
> +        virDomainInterfacePtr iface = NULL;
> +
> +        /* Shouldn't happen but doesn't hurt to check neither */
> +        if (!tmp_iface) {
> +            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                           _("something has went really wrong"));
> +            goto error;
> +        }
> +
> +        /* interface name is required to be presented */
> +        name = virJSONValueObjectGetString(tmp_iface, "name");
> +        if (!name) {
> +            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                           _("qemu agent didn't provide 'name' field"));
> +            goto error;
> +        }
> +
> +        /* Handle interface alias (<ifname>:<alias>) */
> +        ifname = virStringSplit(name, ":", 2);
> +        ifname_s = ifname[0];
> +
> +        iface = virHashLookup(ifaces_store, ifname_s);
> +
> +        /* If the hash table doesn't contain this iface, add it */
> +        if (!iface) {
> +            if (VIR_EXPAND_N(ifaces_ret, ifaces_count, 1) < 0)
> +                goto error;
> +
> +            if (VIR_ALLOC(ifaces_ret[ifaces_count - 1]) < 0)
> +                goto error;
> +
> +            if (virHashAddEntry(ifaces_store, ifname_s,
> +                                ifaces_ret[ifaces_count - 1]) < 0)
> +                goto error;
> +
> +            iface = ifaces_ret[ifaces_count - 1];
> +            iface->naddrs = 0;
> +
> +            if (VIR_STRDUP(iface->name, ifname_s) < 0)
> +                goto error;
> +
> +            hwaddr = virJSONValueObjectGetString(tmp_iface, "hardware-address");
> +            if (!hwaddr) {
> +                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                               _("qemu agent didn't provide"
> +                                 " 'hardware-address' field"));
> +                goto error;
> +            }
> +
> +            if (VIR_STRDUP(iface->hwaddr, hwaddr) < 0)
> +                goto error;

[1]  According to this set of checks and the one later in the DHCP code
- upon return iface->hwaddr is filled in; however, in patch 2 there's a
check for "if (iface->hwaddr)" before the VIR_ALLOC/VIR_STRDUP (or as I
asked, why not VIR_ALLOC_N/memcpy).

Given that this and the other occurrance guarantee something filled in -
why does the remote code make the extra handstand in order to validate?

> +        }
> +
> +        /* Has to be freed for each interface. */
> +        virStringFreeList(ifname);
> +
> +        /* as well as IP address which - moreover -
> +         * can be presented multiple times */

This comment in combination the one directly above made me first think -
we're free'ing something here...

> +        ip_addr_arr = virJSONValueObjectGet(tmp_iface, "ip-addresses");
> +        if (!ip_addr_arr)
> +            continue;
> +

Remove extra line whitespace

> +
> +        if ((ip_addr_arr_size = virJSONValueArraySize(ip_addr_arr)) < 0)
> +            /* Mmm, empty 'ip-address'? */
> +            goto error;
> +
> +        /* If current iface already exists, continue with the count */
> +        addrs_count = iface->naddrs;
> +
> +        for (j = 0; j < ip_addr_arr_size; j++) {
> +            if (VIR_EXPAND_N(iface->addrs, addrs_count, 1)  < 0)
> +                goto error;
> +
> +            virJSONValuePtr ip_addr_obj = virJSONValueArrayGet(ip_addr_arr, j);
> +            virDomainIPAddressPtr ip_addr = &iface->addrs[addrs_count - 1];
> +            const char *type, *addr;

here again we have definitions after code - move the definitions up or
the VIR_EXPAND_N down depending on how you view it... in fact I'd
probably move the VIR_EXPAND_N down below the following "if"...
> +
> +            /* Shouldn't happen but doesn't hurt to check neither */
> +            if (!ip_addr_obj) {
> +                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                               _("something has went really wrong"));
> +                goto error;
> +            }
> +
> +            type = virJSONValueObjectGetString(ip_addr_obj, "ip-address-type");
> +            if (!type) {
> +                virReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("qemu agent didn't provide 'ip-address-type'"
> +                                 " field for interface '%s'"), name);
> +                goto error;
> +            } else if (STREQ(type, "ipv4")) {
> +                ip_addr->type = VIR_IP_ADDR_TYPE_IPV4;
> +            } else if (STREQ(type, "ipv6")) {
> +                ip_addr->type = VIR_IP_ADDR_TYPE_IPV6;
> +            } else {
> +                virReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("unknown ip address type '%s'"),
> +                               type);
> +                goto error;
> +            }
> +
> +            addr = virJSONValueObjectGetString(ip_addr_obj, "ip-address");
> +            if (!addr) {
> +                virReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("qemu agent didn't provide 'ip-address'"
> +                                 " field for interface '%s'"), name);
> +                goto error;
> +            }
> +            if (VIR_STRDUP(ip_addr->addr, addr) < 0)
> +                goto error;
> +
> +            if (virJSONValueObjectGetNumberUint(ip_addr_obj, "prefix",
> +                                                &ip_addr->prefix) < 0) {
> +                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                               _("malformed 'prefix' field"));
> +                goto error;
> +            }
> +        }
> +
> +        iface->naddrs = addrs_count;
> +    }
> +
> +    *ifaces = ifaces_ret;
> +    ifaces_ret = NULL;
> +    ret = ifaces_count;
> +
> + cleanup:
> +    virJSONValueFree(cmd);
> +    virJSONValueFree(reply);
> +    virHashFree(ifaces_store);
> +    return ret;
> +
> + error:
> +    if (ifaces_ret) {
> +        for (i = 0; i < ifaces_count; i++)
> +            virDomainInterfaceFree(ifaces_ret[i]);
> +    }
> +    VIR_FREE(ifaces_ret);
> +    virStringFreeList(ifname);
> +
> +    goto cleanup;
> +}
> diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h
> index c983828..1cd5749 100644
> --- a/src/qemu/qemu_agent.h
> +++ b/src/qemu/qemu_agent.h
> @@ -108,4 +108,8 @@ int qemuAgentSetTime(qemuAgentPtr mon,
>                       long long seconds,
>                       unsigned int nseconds,
>                       bool sync);
> +
> +int qemuAgentGetInterfaces(qemuAgentPtr mon,
> +                           virDomainInterfacePtr **ifaces);
> +
>  #endif /* __QEMU_AGENT_H__ */
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index bc6aae4..5e7207b 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -171,6 +171,9 @@ static int qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid,
>                            const char *path, int oflags,
>                            bool *needUnlink, bool *bypassSecurityDriver);
>  
> +static int qemuGetDHCPInterfaces(virDomainPtr dom,
> +                                 virDomainObjPtr vm,
> +                                 virDomainInterfacePtr **ifaces);
>  
>  virQEMUDriverPtr qemu_driver = NULL;
>  
> @@ -18897,6 +18900,175 @@ qemuDomainGetFSInfo(virDomainPtr dom,
>      return ret;
>  }
>  
> +static int
> +qemuDomainInterfaceAddresses(virDomainPtr dom,
> +                             virDomainInterfacePtr **ifaces,
> +                             unsigned int flags)
> +{
> +    virQEMUDriverPtr driver = dom->conn->privateData;
> +    qemuDomainObjPrivatePtr priv = NULL;
> +    virDomainObjPtr vm = NULL;
> +    int ret = -1, i = 0;
> +    bool tryLease, tryAgent;
> +
> +    if (!(vm = qemuDomObjFromDomain(dom)))
> +        goto cleanup;
> +
> +    priv = vm->privateData;
> +
> +    if (!virDomainObjIsActive(vm)) {
> +        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
> +                       _("domain is not running"));
> +        goto cleanup;
> +    }
> +
> +    if (virDomainInterfaceAddressesEnsureACL(dom->conn, vm->def) < 0)
> +        goto cleanup;
> +
> +    if (flags) {
> +        tryLease = (flags & VIR_DOMAIN_INTERFACE_ADDRESSES_LEASE);
> +        tryAgent = (flags & VIR_DOMAIN_INTERFACE_ADDRESSES_AGENT);
> +    } else {
> +        tryLease = false;
> +        i = vm->def->nnets;
> +        /* check if the guest has any NICs on libvirt virtual network */
> +        while (i >= 0 && !tryLease)
> +            tryLease = (vm->def->nets[--i]->type == VIR_DOMAIN_NET_TYPE_NETWORK);
> +
> +        tryAgent = (priv->agent != NULL);
> +    }
> +
> +    if (tryLease) {
> +        ret = qemuGetDHCPInterfaces(dom, vm, ifaces);
> +        goto cleanup;
> +    }
> +
> +    if (tryAgent) {
> +        if (priv->agentError) {
> +            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                           _("QEMU guest agent is not "
> +                             "available due to an error"));
> +            goto cleanup;
> +        }
> +
More recent agent related code seems to have BeginJob here...  then
check again for domain obj active:


   if (!virDomainObjIsActive(vm)) {
        virReportError(VIR_ERR_OPERATION_INVALID,
                       "%s", _("domain is not running"));
        goto endjob;
    }


> +        if (!priv->agent) {
> +            virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
> +                           _("QEMU guest agent is not configured"));
> +            goto cleanup;

then have goto endjob here...

> +        }
> +
> +        if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0)
> +            goto cleanup;
> +
> +        qemuDomainObjEnterAgent(vm);
> +        ret = qemuAgentGetInterfaces(priv->agent, ifaces);
> +        qemuDomainObjExitAgent(vm);
> +
 endjob:

> +        qemuDomainObjEndJob(driver, vm);
> +
> +        goto cleanup;
> +    }
> +
> +    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                   _("No IP address data source found"));
> +
> + cleanup:
> +    if (vm)
> +        virObjectUnlock(vm);
> +    return ret;
> +}
> +
> +static int
> +qemuGetDHCPInterfaces(virDomainPtr dom,
> +                      virDomainObjPtr vm,
> +                      virDomainInterfacePtr **ifaces)
> +{
> +    int rv = -1;
> +    int n_leases = 0;
> +    size_t i, j;
> +    size_t ifaces_count = 0;
> +    virNetworkPtr network;
> +    char macaddr[VIR_MAC_STRING_BUFLEN];
> +    virDomainInterfacePtr iface = NULL;
> +    virNetworkDHCPLeasePtr *leases = NULL;
> +    virDomainInterfacePtr *ifaces_ret = NULL;
> +

DHCP is not my area of expertise...

> +    if (dom->conn->networkDriver &&
> +        dom->conn->networkDriver->networkGetDHCPLeases) {

So you're checking if this connection has the API...  and then calling
the GetDHCPLeases 'generally' - I dunno, this just seems odd and out of
place.

> +
> +        for (i = 0; i < vm->def->nnets; i++) {
> +            if (vm->def->nets[i]->type !=  VIR_DOMAIN_NET_TYPE_NETWORK)
                                            ^^

Extra space

> +                continue;
> +
> +            virMacAddrFormat(&(vm->def->nets[i]->mac), macaddr);
> +            network = virNetworkLookupByName(dom->conn,
> +                                             vm->def->nets[i]->data.network.name);
> +
> +            if ((n_leases = virNetworkGetDHCPLeases(network, macaddr,
> +                                               &leases, 0)) < 0)
                                                  ^^^ move under network
alignment issues.


> +                goto error;
> +
> +            if (n_leases) {
> +                if (VIR_EXPAND_N(ifaces_ret, ifaces_count, 1) < 0)
> +                    goto error;
> +
> +                if (VIR_ALLOC(ifaces_ret[ifaces_count - 1]) < 0)
> +                    goto error;
> +
> +                iface = ifaces_ret[ifaces_count - 1];
> +                /* Assuming each lease corresponds to a separate IP */
> +                iface->naddrs = n_leases;
> +
> +                if (VIR_ALLOC_N(iface->addrs, iface->naddrs) < 0)
> +                    goto error;
> +
> +                if (VIR_STRDUP(iface->name, vm->def->nets[i]->ifname) < 0)
> +                    goto cleanup;
> +
> +                if (VIR_STRDUP(iface->hwaddr, macaddr) < 0)
> +                    goto cleanup;

[1] See comment above in agent code regarding this value and it's
relationship in the 2/4 (remote) code...

> +            }
> +
> +            for (j = 0; j < n_leases; j++) {
> +                virNetworkDHCPLeasePtr lease = leases[j];
> +                virDomainIPAddressPtr ip_addr = &iface->addrs[j];
> +
> +                if (VIR_STRDUP(ip_addr->addr, lease->ipaddr) < 0)
> +                    goto cleanup;
> +
> +                ip_addr->type = lease->type;
> +                ip_addr->prefix = lease->prefix;
> +            }
> +
> +            for (j = 0; j < n_leases; j++)
> +                virNetworkDHCPLeaseFree(leases[j]);
> +
> +            VIR_FREE(leases);
> +        }
> +    }
> +
> +    *ifaces = ifaces_ret;
> +    ifaces_ret = NULL;
> +    rv = ifaces_count;

If we failed the conn->networkDriver comparison, then we fall to here
and return *ifaces = NULL; and rv = 0;

Which means the caller gets nothing and seems OK, but I'm just trying to
think through this...  With a 0 return here - is there condition or
reason that perhaps you'd want to try the agent if flags weren't
provided in the caller rather than just returning nothing?  Just trying
to think of some strange edge condition...

> +
> + cleanup:
> +    if (leases) {
> +        for (i = 0; i < n_leases; i++)
> +            virNetworkDHCPLeaseFree(leases[i]);
> +    }
> +    VIR_FREE(leases);
> +
> +    return rv;
> +
> + error:
> +    if (ifaces_ret) {
> +        for (i = 0; i < ifaces_count; i++)
> +            virDomainInterfaceFree(ifaces_ret[i]);
> +    }
> +    VIR_FREE(ifaces_ret);
> +
> +    goto cleanup;
> +}
>  
>  static virHypervisorDriver qemuDriver = {
>      .no = VIR_DRV_QEMU,
> @@ -19100,6 +19272,7 @@ static virHypervisorDriver qemuDriver = {
>      .connectGetAllDomainStats = qemuConnectGetAllDomainStats, /* 1.2.8 */
>      .nodeAllocPages = qemuNodeAllocPages, /* 1.2.9 */
>      .domainGetFSInfo = qemuDomainGetFSInfo, /* 1.2.11 */
> +    .domainInterfaceAddresses = qemuDomainInterfaceAddresses, /* 1.2.12 */

1.2.13 (at least)

>  };
>  
>  

Yah! tests...  didn't see anything in the tests that appeared to be
wrong/missing.


John
> diff --git a/tests/qemuagenttest.c b/tests/qemuagenttest.c
> index 54a45df..d8d1e41 100644
> --- a/tests/qemuagenttest.c
> +++ b/tests/qemuagenttest.c
> @@ -723,6 +723,193 @@ testQemuAgentTimeout(const void *data)
>      return ret;
>  }
>  
> +static const char testQemuAgentGetInterfacesResponse[] =
> +    "{\"return\": "
> +    "    ["
> +    "       {\"name\":\"eth2\","
> +    "        \"hardware-address\":\"52:54:00:36:2a:e5\""
> +    "       },"
> +    "       {\"name\":\"eth1:0\","
> +    "        \"ip-addresses\":"
> +    "          ["
> +    "             {\"ip-address-type\":\"ipv4\","
> +    "              \"ip-address\":\"192.168.10.91\","
> +    "              \"prefix\":24"
> +    "             },"
> +    "             {\"ip-address-type\":\"ipv6\","
> +    "              \"ip-address\":\"fe80::fc54:ff:fefe:4c4f\","
> +    "              \"prefix\":64"
> +    "             }"
> +    "          ],"
> +    "        \"hardware-address\":\"52:54:00:d3:39:ee\""
> +    "       },"
> +    "       {\"name\":\"eth0\","
> +    "        \"ip-addresses\":"
> +    "          ["
> +    "             {\"ip-address-type\":\"ipv6\","
> +    "              \"ip-address\":\"fe80::5054:ff:fe89:ad35\","
> +    "              \"prefix\":64"
> +    "             },"
> +    "             {\"ip-address-type\":\"ipv4\","
> +    "              \"ip-address\":\"192.168.102.142\","
> +    "              \"prefix\":24"
> +    "             },"
> +    "             {\"ip-address-type\":\"ipv4\","
> +    "              \"ip-address\":\"192.168.234.152\","
> +    "              \"prefix\":16"
> +    "             },"
> +    "             {\"ip-address-type\":\"ipv6\","
> +    "              \"ip-address\":\"fe80::5054:ff:fec3:68bb\","
> +    "              \"prefix\":64"
> +    "             }"
> +    "          ],"
> +    "        \"hardware-address\":\"52:54:00:89:ad:35\""
> +    "       },"
> +    "       {\"name\":\"eth1\","
> +    "        \"ip-addresses\":"
> +    "          ["
> +    "             {\"ip-address-type\":\"ipv4\","
> +    "              \"ip-address\":\"192.168.103.83\","
> +    "              \"prefix\":32"
> +    "             },"
> +    "             {\"ip-address-type\":\"ipv6\","
> +    "              \"ip-address\":\"fe80::5054:ff:fed3:39ee\","
> +    "              \"prefix\":64"
> +    "             }"
> +    "          ],"
> +    "        \"hardware-address\":\"52:54:00:d3:39:ee\""
> +    "       },"
> +    "       {\"name\":\"lo\","
> +    "        \"ip-addresses\":"
> +    "          ["
> +    "             {\"ip-address-type\":\"ipv4\","
> +    "              \"ip-address\":\"127.0.0.1\","
> +    "              \"prefix\":8"
> +    "             },"
> +    "             {\"ip-address-type\":\"ipv6\","
> +    "              \"ip-address\":\"::1\","
> +    "              \"prefix\":128"
> +    "             }"
> +    "          ],"
> +    "        \"hardware-address\":\"00:00:00:00:00:00\""
> +    "       }"
> +    "    ]"
> +    "}";
> +
> +static int
> +testQemuAgentGetInterfaces(const void *data)
> +{
> +    virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data;
> +    qemuMonitorTestPtr test = qemuMonitorTestNewAgent(xmlopt);
> +    size_t i;
> +    int ret = -1;
> +    int ifaces_count = 0;
> +    virDomainInterfacePtr *ifaces = NULL;
> +
> +    if (!test)
> +        return -1;
> +
> +    if (qemuMonitorTestAddAgentSyncResponse(test) < 0)
> +        goto cleanup;
> +
> +    if (qemuMonitorTestAddItem(test, "guest-network-get-interfaces",
> +                               testQemuAgentGetInterfacesResponse) < 0)
> +        goto cleanup;
> +
> +    if ((ifaces_count = qemuAgentGetInterfaces(qemuMonitorTestGetAgent(test),
> +                                               &ifaces)) < 0)
> +        goto cleanup;
> +
> +    if (ifaces_count != 4) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR,
> +                       "expected 4 interfaces, got %d", ret);
> +        goto cleanup;
> +    }
> +
> +    if (STRNEQ(ifaces[0]->name, "eth2") ||
> +        STRNEQ(ifaces[1]->name, "eth1") ||
> +        STRNEQ(ifaces[2]->name, "eth0") ||
> +        STRNEQ(ifaces[3]->name, "lo")) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                       "unexpected return values for interface names");
> +        goto cleanup;
> +    }
> +
> +    if (STRNEQ(ifaces[0]->hwaddr, "52:54:00:36:2a:e5") ||
> +        STRNEQ(ifaces[1]->hwaddr, "52:54:00:d3:39:ee") ||
> +        STRNEQ(ifaces[2]->hwaddr, "52:54:00:89:ad:35") ||
> +        STRNEQ(ifaces[3]->hwaddr, "00:00:00:00:00:00")) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                       "unexpected return values for MAC addresses");
> +        goto cleanup;
> +    }
> +
> +    if (ifaces[0]->naddrs != 0 ||
> +        ifaces[1]->naddrs != 4 ||
> +        ifaces[2]->naddrs != 4 ||
> +        ifaces[3]->naddrs != 2) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                       "unexpected return values for number of IP addresses");
> +        goto cleanup;
> +    }
> +
> +    if (ifaces[1]->addrs[0].type != VIR_IP_ADDR_TYPE_IPV4 ||
> +        ifaces[1]->addrs[1].type != VIR_IP_ADDR_TYPE_IPV6 ||
> +        ifaces[1]->addrs[2].type != VIR_IP_ADDR_TYPE_IPV4 ||
> +        ifaces[1]->addrs[3].type != VIR_IP_ADDR_TYPE_IPV6 ||
> +        ifaces[2]->addrs[0].type != VIR_IP_ADDR_TYPE_IPV6 ||
> +        ifaces[2]->addrs[1].type != VIR_IP_ADDR_TYPE_IPV4 ||
> +        ifaces[2]->addrs[2].type != VIR_IP_ADDR_TYPE_IPV4 ||
> +        ifaces[2]->addrs[3].type != VIR_IP_ADDR_TYPE_IPV6 ||
> +        ifaces[3]->addrs[0].type != VIR_IP_ADDR_TYPE_IPV4 ||
> +        ifaces[3]->addrs[1].type != VIR_IP_ADDR_TYPE_IPV6) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                       "unexpected return values for IP address types");
> +        goto cleanup;
> +    }
> +
> +    if (ifaces[1]->addrs[0].prefix != 24 ||
> +        ifaces[1]->addrs[1].prefix != 64 ||
> +        ifaces[1]->addrs[2].prefix != 32 ||
> +        ifaces[1]->addrs[3].prefix != 64 ||
> +        ifaces[2]->addrs[0].prefix != 64 ||
> +        ifaces[2]->addrs[1].prefix != 24 ||
> +        ifaces[2]->addrs[2].prefix != 16 ||
> +        ifaces[2]->addrs[3].prefix != 64 ||
> +        ifaces[3]->addrs[0].prefix != 8 ||
> +        ifaces[3]->addrs[1].prefix != 128) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                       "unexpected return values for IP address prefix");
> +        goto cleanup;
> +    }
> +
> +    if (STRNEQ(ifaces[1]->addrs[0].addr, "192.168.10.91") ||
> +        STRNEQ(ifaces[1]->addrs[1].addr, "fe80::fc54:ff:fefe:4c4f") ||
> +        STRNEQ(ifaces[1]->addrs[2].addr, "192.168.103.83") ||
> +        STRNEQ(ifaces[1]->addrs[3].addr, "fe80::5054:ff:fed3:39ee") ||
> +        STRNEQ(ifaces[2]->addrs[0].addr, "fe80::5054:ff:fe89:ad35") ||
> +        STRNEQ(ifaces[2]->addrs[1].addr, "192.168.102.142") ||
> +        STRNEQ(ifaces[2]->addrs[2].addr, "192.168.234.152") ||
> +        STRNEQ(ifaces[2]->addrs[3].addr, "fe80::5054:ff:fec3:68bb") ||
> +        STRNEQ(ifaces[3]->addrs[0].addr, "127.0.0.1") ||
> +        STRNEQ(ifaces[3]->addrs[1].addr, "::1")) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                       "unexpected return values for IP address values");
> +        goto cleanup;
> +    }
> +
> +    ret = 0;
> +
> + cleanup:
> +    qemuMonitorTestFree(test);
> +    if (ifaces) {
> +        for (i = 0; i < ifaces_count; i++)
> +            virDomainInterfaceFree(ifaces[i]);
> +    }
> +    VIR_FREE(ifaces);
> +
> +    return ret;
> +}
>  
>  static int
>  mymain(void)
> @@ -753,6 +940,7 @@ mymain(void)
>      DO_TEST(Shutdown);
>      DO_TEST(CPU);
>      DO_TEST(ArbitraryCommand);
> +    DO_TEST(GetInterfaces);
>  
>      DO_TEST(Timeout); /* Timeout should always be called last */
>  
> 




More information about the libvir-list mailing list