From nobody Tue Sep 22 22:51:42 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) client-ip=38.145.34.151; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1783604665; cv=none; d=zohomail.com; s=zohoarc; b=lNp8ex+3ebki8Fvp70wjb3Ofw2FbyKmnoTvxK9P87YgIaqfnC5sV8dSMbHGARshdFjuk+FAgS419SL82Ujcx1gi9AaVZibHf/ZNV/RK0kmaXWEQxU8StGX/hxUkRz88DU+BOdpJEiAwsX9hMoq65GyV7hS17IrzVbSVLbbAl7e0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783604665; h=Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id:Cc; bh=XxOcISFJxtf0qQH0mBnjNejJ+0O0mnVuW04rvR4hjT8=; b=K3bPvZ7u9YNPz9zjpF4iPm0i9+gS1EQ8iUHiybw9mgoWC6IQSJUHIYLdNZzn16BzKOOAKpnZut/nutTHj6BH0sI349lG7IMg04eEDnzEkrLOqiuuExnetJ9q+sAlH+FpZtzHACz3DDxjUx5qnVHBcGRBxAJQ+FtOEA0b9zgZD0M= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [38.145.34.151]) by mx.zohomail.com with SMTPS id 1783604665219986.0492104900948; Thu, 9 Jul 2026 06:44:25 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 993) id C253C41CE2; Thu, 9 Jul 2026 09:44:23 -0400 (EDT) Received: from [172.19.199.10] (unknown [10.16.107.18]) by lists.libvirt.org (Postfix) with ESMTP id E576941D9A; Thu, 9 Jul 2026 09:41:00 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 993) id B324041ADF; Thu, 9 Jul 2026 09:38:23 -0400 (EDT) Received: from relay.virtuozzo.com (relay.virtuozzo.com [130.117.225.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id E9A4141ADC for ; Thu, 9 Jul 2026 09:38:21 -0400 (EDT) Received: from ch-vpn.virtuozzo.com ([130.117.225.6] helo=mami) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1whoh5-006MF9-3C for devel@lists.libvirt.org; Thu, 09 Jul 2026 15:22:39 +0200 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=4.0.1 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=virtuozzo.com; s=relay; h=MIME-Version:Message-ID:Date:Subject:From: Content-Type; bh=XxOcISFJxtf0qQH0mBnjNejJ+0O0mnVuW04rvR4hjT8=; b=F9f0J6f/imuc AYp7DH0qq4XPDh/TjRksaK7pazFGI2vHufZtTb8LTOWAp490a9TMhFuesha5Avu0gs3Pxo0OvQHqx U8Y6sIfyiX4f1Qdf1C8UG/rRY5BPcBvf54koHUnUBBAy16KHfwm81yG1g/FrXkiJ1vYFk3fmbVSI4 1NNjcvBqSnQpMKc10Uxuz1q9bs6vy/iEp3Ctb+LQRBqLR+O+ATNKcIn+3PvSOqaMVorcD9EVkZDhm r5ZtdJbRTBgv2U6rfQ8NdtUC9rNEfkCHZpYOFlGz/cPc69uiaulSCy3YhgQmk+saEaeyFd5HeXM4X 6GM78Bbv9Bjrmh7H/iqd3Q==; To: devel@lists.libvirt.org Subject: [PATCH 6/6] tests: Add a test for qemuConnectAgent() to cover the vsock path Date: Thu, 9 Jul 2026 15:15:19 +0200 Message-ID: <20260709132213.2392145-7-polina.vishneva@virtuozzo.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> References: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-MailFrom: polina.vishneva@virtuozzo.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation Message-ID-Hash: CJOT55BDZPEGFOTXY5XVUXG34CKPZDKD X-Message-ID-Hash: CJOT55BDZPEGFOTXY5XVUXG34CKPZDKD X-Mailman-Approved-At: Thu, 09 Jul 2026 13:40:42 +0000 X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Polina Vishneva via Devel Reply-To: Polina Vishneva X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1783604668135158500 Content-Type: text/plain; charset="utf-8" qemuagenttest hands an fd straight to qemuAgentOpen(). Transport selection logic was added to qemuConnectAgent(), so to test the vsock path and related logic, we need to test it instead. Mock virVsockConnectQuiet() to the scripted unix QGA server, proving the CID-based vsock branch is taken and a command passes through. Signed-off-by: Polina Vishneva --- src/util/virvsock.h | 5 +- tests/meson.build | 1 + tests/qemuagenttest.c | 43 ++++++++++++- tests/qemumonitortestutils.c | 119 ++++++++++++++++++++++++++++++++--- tests/qemumonitortestutils.h | 4 ++ tests/qemuvsockagentmock.c | 65 +++++++++++++++++++ 6 files changed, 228 insertions(+), 9 deletions(-) create mode 100644 tests/qemuvsockagentmock.c diff --git a/src/util/virvsock.h b/src/util/virvsock.h index 8bf08f9228..215bde2d3e 100644 --- a/src/util/virvsock.h +++ b/src/util/virvsock.h @@ -18,6 +18,8 @@ =20 #pragma once =20 +#include "internal.h" + #define VIR_VSOCK_GUEST_CID_MIN 3 #define VIR_VSOCK_CONNECT_TIMEOUT_MS 200 =20 @@ -31,4 +33,5 @@ virVsockAcquireGuestCid(int fd, =20 int virVsockConnectQuiet(unsigned int cid, - unsigned int port); + unsigned int port) + ATTRIBUTE_MOCKABLE; diff --git a/tests/meson.build b/tests/meson.build index 63e0474a29..cd041dd086 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -178,6 +178,7 @@ if conf.has('WITH_QEMU') { 'name': 'qemucapsprobemock', 'link_with': [ test_qemu_driver_lib ] }, { 'name': 'qemucpumock' }, { 'name': 'qemuhotplugmock', 'link_with': [ test_qemu_driver_lib, test= _utils_qemu_lib, test_utils_lib ] }, + { 'name': 'qemuvsockagentmock' }, { 'name': 'qemuxml2argvmock', 'link_with': [ test_utils_lib ] }, { 'name': 'virhostidmock' }, ] diff --git a/tests/qemuagenttest.c b/tests/qemuagenttest.c index 74cd317e74..c8e11cfd3c 100644 --- a/tests/qemuagenttest.c +++ b/tests/qemuagenttest.c @@ -23,6 +23,7 @@ #include "testutilsqemu.h" #include "qemumonitortestutils.h" #include "qemu/qemu_conf.h" +#include "qemu/qemu_domain.h" #include "hypervisor/qemu_agent.h" #include "virerror.h" =20 @@ -1394,6 +1395,45 @@ testQemuAgentGetLoadAvg(const void *data) } =20 =20 +static int +testQemuAgentVsock(const void *data) +{ + virDomainXMLOption *xmlopt =3D (virDomainXMLOption *)data; + g_autoptr(qemuMonitorTest) test =3D qemuMonitorTestNewAgentVsock(&driv= er, xmlopt); + virDomainObj *vm; + int rc; + + if (!test) + return -1; + + /* The point of this test: qemuConnectAgent() chose vsock and flagged = it. */ + vm =3D qemuMonitorTestGetDomainObj(test); + if (!QEMU_DOMAIN_PRIVATE(vm)->agentIsVsock) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "agent transport is not vsock"); + return -1; + } + + if (qemuMonitorTestAddAgentSyncResponse(test) < 0) + return -1; + + if (qemuMonitorTestAddItem(test, "guest-fsfreeze-thaw", + "{ \"return\" : 5 }") < 0) + return -1; + + if ((rc =3D qemuAgentFSThaw(qemuMonitorTestGetAgent(test))) < 0) + return -1; + + if (rc !=3D 5) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "expected 5 thawed filesystems, got %d", rc); + return -1; + } + + return 0; +} + + static int mymain(void) { @@ -1431,6 +1471,7 @@ mymain(void) DO_TEST(SSHKeys); DO_TEST(GetDisks); DO_TEST(GetLoadAvg); + DO_TEST(Vsock); =20 DO_TEST(Timeout); /* Timeout should always be called last */ =20 @@ -1439,4 +1480,4 @@ mymain(void) return (ret =3D=3D 0) ? EXIT_SUCCESS : EXIT_FAILURE; } =20 -VIR_TEST_MAIN(mymain) +VIR_TEST_MAIN_PRELOAD(mymain, VIR_TEST_MOCK("qemuvsockagent")) diff --git a/tests/qemumonitortestutils.c b/tests/qemumonitortestutils.c index e83dd1d9c4..ffdf55a5f9 100644 --- a/tests/qemumonitortestutils.c +++ b/tests/qemumonitortestutils.c @@ -30,12 +30,14 @@ #include "hypervisor/qemu_agent.h" #include "qemu/qemu_domain.h" #include "qemu/qemu_processpriv.h" +#include "qemu/qemu_process.h" #include "qemu/qemu_monitor.h" #include "rpc/virnetsocket.h" #include "viralloc.h" #include "virlog.h" #include "virerror.h" #include "vireventthread.h" +#include "virvsock.h" =20 #define VIR_FROM_THIS VIR_FROM_NONE =20 @@ -1187,16 +1189,48 @@ qemuMonitorTestNewFromFileFull(const char *fileName, } =20 =20 +/* Build the bare agent test: the common test object plus the event + * thread the agent runs on. @src is filled in by the caller's transport. = */ +static qemuMonitorTest * +qemuMonitorTestNewAgentInit(virDomainXMLOption *xmlopt, + const char *threadName, + virDomainChrSourceDef *src) +{ + g_autoptr(qemuMonitorTest) test =3D NULL; + + if (!(test =3D qemuMonitorCommonTestNew(xmlopt, NULL, src))) + return NULL; + + if (!(test->eventThread =3D virEventThreadNew(threadName))) + return NULL; + + return g_steal_pointer(&test); +} + + +/* Finish an agent test once test->agent is connected: lock it, run the mo= nitor + * init and release @src. On failure the caller's error path frees @test. = */ +static int +qemuMonitorTestAgentStart(qemuMonitorTest *test, + virDomainChrSourceDef *src) +{ + virObjectLock(test->agent); + + if (qemuMonitorCommonTestInit(test) < 0) + return -1; + + virDomainChrSourceDefClear(src); + return 0; +} + + qemuMonitorTest * qemuMonitorTestNewAgent(virDomainXMLOption *xmlopt) { g_autoptr(qemuMonitorTest) test =3D NULL; virDomainChrSourceDef src =3D { 0 }; =20 - if (!(test =3D qemuMonitorCommonTestNew(xmlopt, NULL, &src))) - goto error; - - if (!(test->eventThread =3D virEventThreadNew("agent-test"))) + if (!(test =3D qemuMonitorTestNewAgentInit(xmlopt, "agent-test", &src)= )) goto error; =20 if (!(test->agent =3D qemuAgentOpen(test->vm, @@ -1206,16 +1240,87 @@ qemuMonitorTestNewAgent(virDomainXMLOption *xmlopt) QEMU_DOMAIN_PRIVATE(test->vm)->agent= Timeout))) goto error; =20 - virObjectLock(test->agent); - - if (qemuMonitorCommonTestInit(test) < 0) + if (qemuMonitorTestAgentStart(test, &src) < 0) goto error; =20 + return g_steal_pointer(&test); + + error: virDomainChrSourceDefClear(&src); + return NULL; +} + + +/* + * Like qemuMonitorTestNewAgent(), but drives the real public qemuConnectA= gent() + * so the transport-selection and vsock connect path actually run. The + * qemuvsockagentmock overrides virVsockConnectQuiet() to feed back an fd = wired + * to @test's scripted unix mock-QGA server, so no real AF_VSOCK is involv= ed. + * + * @driver must be the same driver @xmlopt was built for, so the domain's + * private data auto-wires priv->driver. + */ +qemuMonitorTest * +qemuMonitorTestNewAgentVsock(virQEMUDriver *driver, + virDomainXMLOption *xmlopt) +{ + g_autoptr(qemuMonitorTest) test =3D NULL; + virDomainChrSourceDef src =3D { 0 }; + qemuDomainObjPrivate *priv; + + if (!(test =3D qemuMonitorTestNewAgentInit(xmlopt, "agent-vsock-test",= &src))) + goto error; + + /* Make the domain look active and give it a usable guest CID so + * qemuConnectAgent() picks the vsock transport. */ + test->vm->def->id =3D 1; + test->vm->def->name =3D g_strdup("agent-vsock-test"); + + if (!(test->vm->def->vsock =3D virDomainVsockDefNew(xmlopt))) + goto error; + test->vm->def->vsock->guest_cid =3D VIR_VSOCK_GUEST_CID_MIN; + + priv =3D QEMU_DOMAIN_PRIVATE(test->vm); + priv->agentTimeout =3D 5; + /* Alias the event thread without a ref: production only reads it duri= ng the + * connect (the agent takes its own context ref). Severed below before= the + * private-data teardown could unref the thread qemuMonitorTestFree ow= ns. */ + priv->eventThread =3D test->eventThread; + + g_setenv("LIBVIRT_VSOCK_MOCK_PATH", src.data.nix.path, true); + + /* test->vm is already locked (virDomainObjNew() returns it locked), w= hich + * is what the connect path needs: it unlocks/relocks the vm internall= y. */ + + /* Drive the real connect. Do NOT ref the vm: qemuAgentOpenFd() takes = its + * own reference for the agent. */ + if (qemuConnectAgent(driver, test->vm) < 0 || + !priv->agent || !priv->agentIsVsock) { + /* A graceful failure may have armed a reconnect timer holding a vm + * ref; drop it (and the thread alias) before tearing down. */ + qemuDomainCancelAgentVsockReconnect(priv); + priv->eventThread =3D NULL; + virObjectUnlock(test->vm); + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "guest agent did not connect over vsock"); + goto error; + } + + /* Hand the agent to the test object (matches the unix path's ref acco= unting) + * and sever the thread alias. */ + test->agent =3D g_steal_pointer(&priv->agent); + priv->eventThread =3D NULL; + + virObjectUnlock(test->vm); + + if (qemuMonitorTestAgentStart(test, &src) < 0) + goto error; =20 + g_unsetenv("LIBVIRT_VSOCK_MOCK_PATH"); return g_steal_pointer(&test); =20 error: + g_unsetenv("LIBVIRT_VSOCK_MOCK_PATH"); virDomainChrSourceDefClear(&src); return NULL; } diff --git a/tests/qemumonitortestutils.h b/tests/qemumonitortestutils.h index c87cdf0e6a..1908e91fc0 100644 --- a/tests/qemumonitortestutils.h +++ b/tests/qemumonitortestutils.h @@ -99,6 +99,10 @@ qemuMonitorTestNewFromFileFull(const char *fileName, qemuMonitorTest * qemuMonitorTestNewAgent(virDomainXMLOption *xmlopt); =20 +qemuMonitorTest * +qemuMonitorTestNewAgentVsock(virQEMUDriver *driver, + virDomainXMLOption *xmlopt); + =20 void qemuMonitorTestFree(qemuMonitorTest *test); diff --git a/tests/qemuvsockagentmock.c b/tests/qemuvsockagentmock.c new file mode 100644 index 0000000000..d1c2760302 --- /dev/null +++ b/tests/qemuvsockagentmock.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2026 Virtuozzo International GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include + +#include "internal.h" +#include "virvsock.h" +#include "virfile.h" +#include "virstring.h" + +#include +#include +#include +#include + +/* + * Override the only genuinely vsock-specific syscall wrapper so the real + * qemuConnectAgent()/qemuChannelOpenVsock() dispatch runs under test. The + * cid/port are irrelevant here: hand back an fd wired to the scripted moc= k-QGA + * unix server the test set up (mirrors qemuMonitorTestOpenChannel()). + */ +int +virVsockConnectQuiet(unsigned int cid G_GNUC_UNUSED, + unsigned int port G_GNUC_UNUSED) +{ + const char *path =3D getenv("LIBVIRT_VSOCK_MOCK_PATH"); + struct sockaddr_un addr =3D { .sun_family =3D AF_UNIX }; + int fd; + + if (!path) { + errno =3D ENOENT; + return -1; + } + + if ((fd =3D socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + return -1; + + if (virStrcpyStatic(addr.sun_path, path) < 0) { + VIR_FORCE_CLOSE(fd); + errno =3D ENAMETOOLONG; + return -1; + } + + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + VIR_FORCE_CLOSE(fd); + return -1; + } + + return fd; +} --=20 2.54.0