Files
libvirt/tests/networkxmlconftest.c
T
Michal Privoznik ca98f39e64 networkxmlconftest: Expect success for "hostdev" case only on Linux
Our network has multiple means of forwarding the traffic and
'hostdev' is one of them. This mode means that the network is
configured to use a set of PCI devices which are then assigned to
individual domains to use (PCI device assignment). Now, as of
v12.0.0-61-gecb2e06bdf our test runners
(testCompareXMLToXMLFiles() and testCompareXMLToConfFiles()) call
networkValidateTests(). For aforementioned type of network this
means checking that the specified set of devices contains only
VFs (see v3.2.0-rc1~24 for more info). It is true that our
virpcimock is preloaded which mimics VFs, but our utils module
(virpci.c specifically) talks to sysfs to check various PCI
device attributes, including whether it's a VF.

This obviously works on Linux and doesn't work anywhere else.
Therefore, until our utils module is taught how to check PCI
attribs on other systems, make the "hostdev" test case expect
validation failure on non-Linux systems.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
2026-01-30 15:23:52 +01:00

350 lines
10 KiB
C

#include <config.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include "internal.h"
#include "testutils.h"
#include "network_conf.h"
#include "bridge_driver.h"
#define LIBVIRT_BRIDGE_DRIVER_PRIV_H_ALLOW
#include "bridge_driver_priv.h"
#define LIBVIRT_VIRCOMMANDPRIV_H_ALLOW
#include "vircommandpriv.h"
#define VIR_FROM_THIS VIR_FROM_NONE
typedef enum {
TEST_COMPARE_NET_XML2XML_RESULT_SUCCESS,
TEST_COMPARE_NET_XML2XML_RESULT_FAIL_PARSE,
TEST_COMPARE_NET_XML2XML_RESULT_FAIL_VALIDATE,
TEST_COMPARE_NET_XML2XML_RESULT_FAIL_FORMAT,
TEST_COMPARE_NET_XML2XML_RESULT_FAIL_COMPARE,
} testCompareNetXML2XMLResult;
struct _testInfo {
const char *name;
unsigned int flags;
testCompareNetXML2XMLResult expectResult;
virNetworkXMLOption *xmlopt; /* borrowed, immutable */
dnsmasqCaps *caps;
virNetworkDef *def;
char *inxml;
char *outxml;
char *outconf;
char *outhostsfile;
};
typedef struct _testInfo testInfo;
void testInfoFree(testInfo *info);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(testInfo, testInfoFree);
void testInfoFree(testInfo *info)
{
if (!info)
return;
virNetworkDefFree(info->def);
VIR_FREE(info->inxml);
VIR_FREE(info->outxml);
VIR_FREE(info->outconf);
VIR_FREE(info->outhostsfile);
VIR_FREE(info);
}
static int
testCompareXMLToXMLFiles(const void *data)
{
testInfo *info = (void *) data;
g_autofree char *actual = NULL;
int ret;
testCompareNetXML2XMLResult result = TEST_COMPARE_NET_XML2XML_RESULT_SUCCESS;
g_autoptr(virNetworkDef) def = NULL;
if (!(def = virNetworkDefParse(NULL, info->inxml, info->xmlopt, false))) {
result = TEST_COMPARE_NET_XML2XML_RESULT_FAIL_PARSE;
goto cleanup;
}
if (info->expectResult == TEST_COMPARE_NET_XML2XML_RESULT_FAIL_PARSE)
goto cleanup;
if (networkValidateTests(def) < 0) {
result = TEST_COMPARE_NET_XML2XML_RESULT_FAIL_VALIDATE;
goto cleanup;
}
if (info->expectResult == TEST_COMPARE_NET_XML2XML_RESULT_FAIL_VALIDATE)
goto cleanup;
if (!(actual = virNetworkDefFormat(def, info->xmlopt, info->flags))) {
result = TEST_COMPARE_NET_XML2XML_RESULT_FAIL_FORMAT;
goto cleanup;
}
if (info->expectResult == TEST_COMPARE_NET_XML2XML_RESULT_FAIL_FORMAT)
goto cleanup;
if (virTestCompareToFile(actual, info->outxml) < 0) {
result = TEST_COMPARE_NET_XML2XML_RESULT_FAIL_COMPARE;
goto cleanup;
}
if (info->expectResult == TEST_COMPARE_NET_XML2XML_RESULT_FAIL_COMPARE)
goto cleanup;
cleanup:
if (result == info->expectResult) {
ret = 0;
if (info->expectResult != TEST_COMPARE_NET_XML2XML_RESULT_SUCCESS) {
VIR_TEST_DEBUG("Got expected failure code=%d msg=%s",
result, virGetLastErrorMessage());
} else {
info->def = g_steal_pointer(&def);
}
} else {
ret = -1;
VIR_TEST_DEBUG("Expected result code=%d but received code=%d",
info->expectResult, result);
}
virResetLastError();
return ret;
}
static int
testCompareXMLToConfFiles(const void *data)
{
testInfo *info = (void *) data;
char *confactual = NULL;
g_autofree char *hostsfileactual = NULL;
int ret = -1;
virNetworkDef *def = NULL;
virNetworkObj *obj = NULL;
g_autofree char *pidfile = NULL;
g_autoptr(dnsmasqContext) dctx = NULL;
bool compareFailed = false;
if (!(obj = virNetworkObjNew()))
goto fail;
if (!(def = g_steal_pointer(&info->def))) {
/* Previous test wasn't executed. */
if (!(def = virNetworkDefParse(NULL, info->inxml, info->xmlopt, false)))
goto fail;
if (networkValidateTests(def) < 0) {
virNetworkDefFree(def);
goto fail;
}
}
virNetworkObjSetDef(obj, def);
if (!networkNeedsDnsmasq(def)) {
ret = EXIT_AM_SKIP;
goto fail;
}
dctx = dnsmasqContextNew(def->name, "/var/lib/libvirt/dnsmasq");
if (dctx == NULL)
goto fail;
if (networkDnsmasqConfContents(obj, pidfile, &confactual,
&hostsfileactual, dctx, info->caps) < 0)
goto fail;
/* Any changes to this function ^^ should be reflected here too. */
#ifndef __linux__
{
char * tmp;
if (!(tmp = virStringReplace(confactual,
"except-interface=lo0\n",
"except-interface=lo\n")))
goto fail;
VIR_FREE(confactual);
confactual = g_steal_pointer(&tmp);
}
#endif
if (virTestCompareToFile(confactual, info->outconf) < 0)
compareFailed = true;
if (hostsfileactual) {
if (virTestCompareToFile(hostsfileactual, info->outhostsfile) < 0) {
compareFailed = true;
}
} else {
if (virFileExists(info->outhostsfile)) {
VIR_TEST_DEBUG("%s: hostsfile exists but the configuration did not specify any host",
info->outhostsfile);
compareFailed = true;
}
}
if (compareFailed)
goto fail;
ret = 0;
fail:
VIR_FREE(confactual);
virNetworkObjEndAPI(&obj);
return ret;
}
static void
buildCapsCallback(const char *const*args,
const char *const*env G_GNUC_UNUSED,
const char *input G_GNUC_UNUSED,
char **output,
char **error G_GNUC_UNUSED,
int *status,
void *opaque G_GNUC_UNUSED)
{
if (STREQ(args[0], "/usr/sbin/dnsmasq") && STREQ(args[1], "--version")) {
*output = g_strdup("Dnsmasq version 2.67\n");
*status = EXIT_SUCCESS;
} else {
*status = EXIT_FAILURE;
}
}
static dnsmasqCaps *
buildCaps(void)
{
g_autoptr(dnsmasqCaps) caps = NULL;
g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
virCommandSetDryRun(dryRunToken, NULL, true, true, buildCapsCallback, NULL);
caps = dnsmasqCapsNewFromBinary();
return g_steal_pointer(&caps);
}
static void
testRun(const char *name,
int *ret,
virNetworkXMLOption *xmlopt,
dnsmasqCaps *caps,
testCompareNetXML2XMLResult expectResult,
unsigned int flags)
{
g_autofree char *name_xml2xml = g_strdup_printf("Network XML-2-XML %s", name);
g_autofree char *name_xml2conf = g_strdup_printf("Network XML-2-Conf %s", name);
g_autoptr(testInfo) info = g_new0(testInfo, 1);
info->name = name;
info->flags = flags;
info->expectResult = expectResult;
info->xmlopt = xmlopt;
info->caps = caps;
info->inxml = g_strdup_printf("%s/networkxmlconfdata/%s.xml", abs_srcdir, name);
info->outxml = g_strdup_printf("%s/networkxmlconfdata/%s.expect.xml", abs_srcdir, name);
info->outconf = g_strdup_printf("%s/networkxmlconfdata/%s.conf", abs_srcdir, name);
info->outhostsfile = g_strdup_printf("%s/networkxmlconfdata/%s.hostsfile", abs_srcdir, name);
virTestRunLog(ret, name_xml2xml, testCompareXMLToXMLFiles, info);
if (expectResult == TEST_COMPARE_NET_XML2XML_RESULT_SUCCESS)
virTestRunLog(ret, name_xml2conf, testCompareXMLToConfFiles, info);
}
static int
mymain(void)
{
g_autoptr(virNetworkXMLOption) xmlopt = NULL;
g_autoptr(dnsmasqCaps) caps = NULL;
int ret = 0;
if (!(xmlopt = networkDnsmasqCreateXMLConf()))
return -1;
if (!(caps = buildCaps()))
return -1;
#define DO_TEST_FULL(name, flags, expectResult) \
testRun(name, &ret, xmlopt, caps, expectResult, flags)
#define DO_TEST(name) \
DO_TEST_FULL(name, 0, TEST_COMPARE_NET_XML2XML_RESULT_SUCCESS)
#define DO_TEST_FLAGS(name, flags) \
DO_TEST_FULL(name, flags, TEST_COMPARE_NET_XML2XML_RESULT_SUCCESS)
#define DO_TEST_PARSE_ERROR(name) \
DO_TEST_FULL(name, 0, TEST_COMPARE_NET_XML2XML_RESULT_FAIL_PARSE)
#define DO_TEST_VALIDATE_ERROR(name) \
DO_TEST_FULL(name, 0, TEST_COMPARE_NET_XML2XML_RESULT_FAIL_VALIDATE)
DO_TEST("dhcp6-network");
DO_TEST("dhcp6-nat-network");
DO_TEST("dhcp6host-routed-network");
DO_TEST("empty-allow-ipv6");
DO_TEST("isolated-network");
DO_TEST("routed-network");
DO_TEST("routed-network-no-dns");
DO_TEST_PARSE_ERROR("routed-network-no-dns-extra-elements");
DO_TEST("open-network");
DO_TEST_PARSE_ERROR("open-network-with-forward-dev");
DO_TEST("nat-network");
DO_TEST("netboot-network");
DO_TEST("netboot-proxy-network");
DO_TEST("netboot-tftp");
DO_TEST("nat-network-dns-txt-record");
DO_TEST("nat-network-dns-srv-record");
DO_TEST("nat-network-dns-srv-records");
DO_TEST("nat-network-dns-srv-record-minimal");
DO_TEST("nat-network-dns-hosts");
DO_TEST("nat-network-dns-forward-plain");
DO_TEST("nat-network-dns-forwarders");
DO_TEST("nat-network-dns-forwarder-no-resolv");
DO_TEST("nat-network-dns-local-domain");
DO_TEST("nat-network-forward-nat-ipv6");
DO_TEST("nat-network-forward-nat-address");
DO_TEST("nat-network-forward-nat-no-address");
DO_TEST("nat-network-name-with-quotes");
DO_TEST("nat-network-mtu");
DO_TEST("8021Qbh-net");
DO_TEST("direct-net");
DO_TEST("host-bridge-net");
DO_TEST("vepa-net");
DO_TEST("bandwidth-network");
DO_TEST("openvswitch-net");
DO_TEST_VALIDATE_ERROR("passthrough-pf");
#ifdef __linux__
DO_TEST("hostdev");
#else
/* Our test runners call networkValidateTests() which for
* <forward mode='hostdev'/> means validating that PCI
* devices are VFs. It's done so by querying sysfs which
* obviously works on Linux only. Thus, expect a validation
* error elsewhere. */
DO_TEST_VALIDATE_ERROR("hostdev");
#endif
DO_TEST_FLAGS("hostdev-pf", VIR_NETWORK_XML_INACTIVE);
DO_TEST_FLAGS("hostdev-pf-driver-model", VIR_NETWORK_XML_INACTIVE);
DO_TEST("ptr-domains-auto");
DO_TEST_VALIDATE_ERROR("passthrough-address-crash");
DO_TEST("nat-network-explicit-flood");
DO_TEST("host-bridge-no-flood");
DO_TEST_PARSE_ERROR("hostdev-duplicate");
DO_TEST_PARSE_ERROR("passthrough-duplicate");
DO_TEST("metadata");
DO_TEST("set-mtu");
DO_TEST("dnsmasq-options");
DO_TEST("leasetime-seconds");
DO_TEST("leasetime-minutes");
DO_TEST("leasetime-hours");
DO_TEST("leasetime-infinite");
DO_TEST("isolated-ports");
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
VIR_TEST_MAIN_PRELOAD(mymain,
VIR_TEST_MOCK("virpci"),
VIR_TEST_MOCK("virdnsmasq"),
VIR_TEST_MOCK("virrandom"))