2020-10-30 17:10:29 +03:00
/*
* 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
* < http : //www.gnu.org/licenses/>.
*/
# include <config.h>
# include <unistd.h>
# include <sys/types.h>
# include <fcntl.h>
# include "testutils.h"
# include "internal.h"
# include "testutilsqemu.h"
2021-02-10 22:37:28 +03:00
# include "testutilsqemuschema.h"
2020-10-30 17:10:29 +03:00
# include "configmake.h"
2021-02-10 22:37:28 +03:00
# define LIBVIRT_QEMU_MIGRATION_PARAMSPRIV_H_ALLOW
2020-10-30 17:10:29 +03:00
# include "qemu/qemu_migration_cookie.h"
2021-02-10 22:37:28 +03:00
# include "qemu/qemu_migration_paramspriv.h"
2020-10-30 17:10:29 +03:00
# define VIR_FROM_THIS VIR_FROM_NONE
static virQEMUDriver driver ;
static virBuffer testnamebuf = VIR_BUFFER_INITIALIZER ;
static const char *
tn ( const char * str , . . . )
{
va_list ap ;
virBufferFreeAndReset ( & testnamebuf ) ;
virBufferAdd ( & testnamebuf , str , - 1 ) ;
va_start ( ap , str ) ;
virBufferStrcatVArgs ( & testnamebuf , ap ) ;
va_end ( ap ) ;
return virBufferCurrentContent ( & testnamebuf ) ;
}
struct testQemuMigrationCookieData {
const char * name ;
char * inStatus ;
2021-03-11 10:16:13 +03:00
virDomainObj * vm ;
2020-10-30 17:10:29 +03:00
unsigned int cookiePopulateFlags ;
unsigned int cookieParseFlags ;
qemuMigrationParty cookiePopulateParty ;
2021-03-11 10:16:13 +03:00
qemuMigrationCookie * cookie ;
2021-02-10 22:37:28 +03:00
2020-10-30 17:10:29 +03:00
char * xmlstr ;
int xmlstrlen ;
char * infile ;
char * outfile ;
2021-02-10 22:37:28 +03:00
char * outmigparamsfile ;
2020-10-30 17:10:29 +03:00
} ;
2021-02-10 22:37:28 +03:00
static void
testQemuMigrationCookieDataFree ( struct testQemuMigrationCookieData * data )
{
if ( ! data )
return ;
qemuMigrationCookieFree ( data - > cookie ) ;
g_free ( data - > xmlstr ) ;
g_free ( data - > outfile ) ;
g_free ( data - > infile ) ;
g_free ( data - > outmigparamsfile ) ;
g_free ( data - > inStatus ) ;
virDomainObjEndAPI ( & data - > vm ) ;
g_free ( data ) ;
}
2020-10-30 17:10:29 +03:00
static int
testQemuMigrationCookiePopulate ( const void * opaque )
{
struct testQemuMigrationCookieData * data = ( struct testQemuMigrationCookieData * ) opaque ;
g_autoptr ( qemuMigrationCookie ) cookie = NULL ;
if ( ! ( cookie = qemuMigrationCookieNew ( data - > vm - > def , NULL ) ) )
return - 1 ;
/* doctor the hostname and uuid, so that the output can be simply used for
* the xml2xmltest where the parser validates UUID match ( yuck ) */
g_free ( cookie - > localHostname ) ;
cookie - > localHostname = g_strdup ( " hostname2 " ) ;
/* uuidgen --sha1 --namespace @dns --name "hostname2" */
if ( virUUIDParse ( " 8b3f4dc4-6a8e-5f9b-94a5-4c35babd8d95 " , cookie - > localHostuuid ) < 0 ) {
VIR_TEST_DEBUG ( " \n failed to parse fake UUID " ) ;
return - 1 ;
}
/* allow re-run for checking both miration parties */
g_clear_pointer ( & data - > xmlstr , g_free ) ;
if ( qemuMigrationCookieFormat ( cookie ,
& driver ,
data - > vm ,
data - > cookiePopulateParty ,
& data - > xmlstr ,
& data - > xmlstrlen ,
data - > cookiePopulateFlags ) < 0 ) {
VIR_TEST_DEBUG ( " \n failed to populate and format qemu migration cookie " ) ;
return - 1 ;
}
if ( virTestCompareToFile ( data - > xmlstr , data - > outfile ) < 0 )
return - 1 ;
return 0 ;
}
static int
testQemuMigrationCookieParse ( const void * opaque )
{
struct testQemuMigrationCookieData * data = ( struct testQemuMigrationCookieData * ) opaque ;
2023-11-09 16:31:43 +03:00
qemuDomainObjPrivate * priv ;
2020-10-30 17:10:29 +03:00
g_auto ( virBuffer ) actual = VIR_BUFFER_INITIALIZER ;
2023-11-09 16:31:43 +03:00
/* if the VM object parsing step failed there's nothing this test can do */
if ( ! data - > vm ) {
VIR_TEST_DEBUG ( " \n missing VM object \n " ) ;
return - 1 ;
}
priv = data - > vm - > privateData ;
2021-02-10 22:37:28 +03:00
if ( ! ( data - > cookie = qemuMigrationCookieParse ( & driver ,
2022-11-30 17:47:15 +03:00
data - > vm ,
2021-02-10 22:37:28 +03:00
data - > vm - > def ,
NULL ,
2022-11-30 18:05:56 +03:00
priv - > qemuCaps ,
2021-02-10 22:37:28 +03:00
data - > xmlstr ,
data - > xmlstrlen ,
2022-11-30 17:47:15 +03:00
data - > cookieParseFlags ) ) ) {
2020-10-30 17:10:29 +03:00
VIR_TEST_DEBUG ( " \n failed to parse qemu migration cookie: \n %s \n " , data - > xmlstr ) ;
return - 1 ;
}
/* set all flags so that formatter attempts to format everything */
2021-02-10 22:37:28 +03:00
data - > cookie - > flags = ~ 0 ;
2020-10-30 17:10:29 +03:00
if ( qemuMigrationCookieXMLFormat ( & driver ,
priv - > qemuCaps ,
& actual ,
2021-02-10 22:37:28 +03:00
data - > cookie ) < 0 ) {
2020-10-30 17:10:29 +03:00
VIR_TEST_DEBUG ( " \n failed to format back qemu migration cookie " ) ;
return - 1 ;
}
if ( virTestCompareToFile ( virBufferCurrentContent ( & actual ) , data - > outfile ) < 0 )
return - 1 ;
return 0 ;
}
static int
testQemuMigrationCookieDomInit ( const void * opaque )
{
struct testQemuMigrationCookieData * data = ( struct testQemuMigrationCookieData * ) opaque ;
if ( ! ( data - > vm = virDomainObjParseFile ( data - > inStatus , driver . xmlopt ,
VIR_DOMAIN_DEF_PARSE_STATUS |
VIR_DOMAIN_DEF_PARSE_ACTUAL_NET |
VIR_DOMAIN_DEF_PARSE_PCI_ORIG_STATES |
VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE |
VIR_DOMAIN_DEF_PARSE_ALLOW_POST_PARSE_FAIL ) ) ) {
VIR_TEST_DEBUG ( " \n failed to parse status xml'%s' " , data - > inStatus ) ;
return - 1 ;
}
return 0 ;
}
static int
testQemuMigrationCookieXMLLoad ( const void * opaque )
{
struct testQemuMigrationCookieData * data = ( struct testQemuMigrationCookieData * ) opaque ;
if ( virTestLoadFile ( data - > infile , & data - > xmlstr ) < 0 )
return - 1 ;
data - > xmlstrlen = strlen ( data - > xmlstr ) + 1 ;
return 0 ;
}
static int
testQemuMigrationCookieDom2XML ( const char * namesuffix ,
const char * domxml ,
unsigned int cookiePopulateFlags ,
unsigned int cookieParseFlags )
{
struct testQemuMigrationCookieData * data = g_new0 ( struct testQemuMigrationCookieData , 1 ) ;
int ret = 0 ;
if ( cookiePopulateFlags = = 0 ) {
/* flags unsupported by default:
* - lockstate : internals are NULL in tests , causes crash
* - nbd : monitor not present
2021-02-10 22:37:28 +03:00
* - dirty bitmaps : monitor not present
2020-10-30 17:10:29 +03:00
*/
unsigned int cookiePopulateFlagMask = QEMU_MIGRATION_COOKIE_LOCKSTATE |
2021-02-10 22:37:28 +03:00
QEMU_MIGRATION_COOKIE_NBD |
QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS ;
2020-10-30 17:10:29 +03:00
data - > cookiePopulateFlags = ~ cookiePopulateFlagMask ;
}
if ( cookieParseFlags = = 0 )
data - > cookieParseFlags = ~ 0 ;
data - > inStatus = g_strconcat ( abs_srcdir , " / " , domxml , NULL ) ;
/* load status XML as domain object */
if ( virTestRun ( tn ( " qemumigrationcookiedom2xml-load- " , namesuffix , NULL ) ,
testQemuMigrationCookieDomInit , data ) < 0 )
ret = - 1 ;
/* test dom -> migration cookie conversion for source */
data - > cookiePopulateParty = QEMU_MIGRATION_SOURCE ;
data - > outfile = g_strconcat ( abs_srcdir , " /qemumigrationcookiexmldata/ " ,
namesuffix , " -dom-out-source.xml " , NULL ) ;
if ( virTestRun ( tn ( " qemumigrationcookiedom2xml-source-populate- " , namesuffix , NULL ) ,
testQemuMigrationCookiePopulate , data ) < 0 )
ret = - 1 ;
/* test dom -> migration cookie conversion for destination */
g_free ( data - > outfile ) ;
data - > cookiePopulateParty = QEMU_MIGRATION_DESTINATION ;
data - > outfile = g_strconcat ( abs_srcdir , " /qemumigrationcookiexmldata/ " ,
namesuffix , " -dom-out-dest.xml " , NULL ) ;
if ( virTestRun ( tn ( " qemumigrationcookiedom2xml-dest-populate- " , namesuffix , NULL ) ,
testQemuMigrationCookiePopulate , data ) < 0 )
ret = - 1 ;
testQemuMigrationCookieDataFree ( data ) ;
return ret ;
}
static int
testQemuMigrationCookieXML2XML ( const char * name ,
const char * statusxml ,
unsigned int cookieParseFlags )
{
struct testQemuMigrationCookieData * data = g_new0 ( struct testQemuMigrationCookieData , 1 ) ;
int ret = 0 ;
if ( cookieParseFlags = = 0 )
data - > cookieParseFlags = ~ 0 ;
data - > inStatus = g_strconcat ( abs_srcdir , " / " , statusxml , NULL ) ;
data - > infile = g_strconcat ( abs_srcdir , " /qemumigrationcookiexmldata/ " ,
name , " -xml2xml-in.xml " , NULL ) ;
data - > outfile = g_strconcat ( abs_srcdir , " /qemumigrationcookiexmldata/ " ,
name , " -xml2xml-out.xml " , NULL ) ;
if ( virTestRun ( tn ( " qemumigrationcookieXML2XML-dom- " , name , NULL ) ,
testQemuMigrationCookieDomInit , data ) < 0 )
ret = - 1 ;
if ( virTestRun ( tn ( " qemumigrationcookieXML2XML-load- " , name , NULL ) ,
testQemuMigrationCookieXMLLoad , data ) < 0 )
ret = - 1 ;
if ( virTestRun ( tn ( " qemumigrationcookieXML2XML-parse- " , name , NULL ) ,
testQemuMigrationCookieParse , data ) < 0 )
ret = - 1 ;
testQemuMigrationCookieDataFree ( data ) ;
return ret ;
}
2021-02-10 22:37:28 +03:00
static int
testQemuMigrationCookieBlockDirtyBitmaps ( const void * opaque )
{
const struct testQemuMigrationCookieData * data = opaque ;
g_autoptr ( virJSONValue ) migParamsBitmaps = NULL ;
g_autofree char * actualJSON = NULL ;
g_autoptr ( virJSONValue ) paramsOut = NULL ;
g_auto ( virBuffer ) debug = VIR_BUFFER_INITIALIZER ;
g_autoptr ( qemuMigrationParams ) migParams = NULL ;
g_autoptr ( GHashTable ) qmpschema = NULL ;
GSList * next ;
if ( ! ( qmpschema = testQEMUSchemaLoadLatest ( " x86_64 " ) ) ) {
VIR_TEST_VERBOSE ( " failed to load QMP schema " ) ;
return - 1 ;
}
2023-11-09 16:31:43 +03:00
/* if the VM object parsing step failed there's nothing this test can do */
if ( ! data - > vm ) {
VIR_TEST_DEBUG ( " \n missing VM object \n " ) ;
return - 1 ;
}
2021-02-10 22:37:28 +03:00
if ( qemuMigrationCookieBlockDirtyBitmapsMatchDisks ( data - > vm - > def ,
data - > cookie - > blockDirtyBitmaps ) < 0 )
return - 1 ;
for ( next = data - > cookie - > blockDirtyBitmaps ; next ; next = next - > next ) {
2021-03-11 10:16:13 +03:00
qemuMigrationBlockDirtyBitmapsDisk * disk = next - > data ;
qemuMigrationBlockDirtyBitmapsDiskBitmap * bitmap = disk - > bitmaps - > data ;
2021-02-10 22:37:28 +03:00
bitmap - > persistent = VIR_TRISTATE_BOOL_YES ;
}
if ( qemuMigrationCookieBlockDirtyBitmapsToParams ( data - > cookie - > blockDirtyBitmaps ,
& migParamsBitmaps ) )
return - 1 ;
if ( ! ( migParams = qemuMigrationParamsNew ( ) ) )
return - 1 ;
qemuMigrationParamsSetBlockDirtyBitmapMapping ( migParams , & migParamsBitmaps ) ;
2022-06-30 13:52:38 +03:00
if ( ! ( paramsOut = qemuMigrationParamsToJSON ( migParams , false ) ) | |
2021-02-10 22:37:28 +03:00
! ( actualJSON = virJSONValueToString ( paramsOut , true ) ) )
return - 1 ;
if ( testQEMUSchemaValidateCommand ( " migrate-set-parameters " ,
paramsOut ,
qmpschema ,
false ,
false ,
2021-10-15 13:06:14 +03:00
false ,
2021-02-10 22:37:28 +03:00
& debug ) < 0 ) {
VIR_TEST_VERBOSE ( " failed to validate migration params '%s' against QMP schema: %s " ,
actualJSON , virBufferCurrentContent ( & debug ) ) ;
return - 1 ;
}
if ( virTestCompareToFile ( actualJSON , data - > outmigparamsfile ) < 0 )
return - 1 ;
return 0 ;
}
/* tests also the conversion to list of migrated bitmaps */
static int
testQemuMigrationCookieXML2XMLBitmaps ( const char * name ,
const char * statusxml ,
unsigned int cookieParseFlags )
{
struct testQemuMigrationCookieData * data = g_new0 ( struct testQemuMigrationCookieData , 1 ) ;
int ret = 0 ;
if ( cookieParseFlags = = 0 )
data - > cookieParseFlags = ~ 0 ;
data - > inStatus = g_strconcat ( abs_srcdir , " / " , statusxml , NULL ) ;
data - > infile = g_strconcat ( abs_srcdir , " /qemumigrationcookiexmldata/ " ,
name , " -xml2xml-in.xml " , NULL ) ;
data - > outfile = g_strconcat ( abs_srcdir , " /qemumigrationcookiexmldata/ " ,
name , " -xml2xml-out.xml " , NULL ) ;
data - > outmigparamsfile = g_strconcat ( abs_srcdir , " /qemumigrationcookiexmldata/ " ,
name , " -xml2xml-migparams.json " , NULL ) ;
if ( virTestRun ( tn ( " qemumigrationcookieXML2XML-dom- " , name , NULL ) ,
testQemuMigrationCookieDomInit , data ) < 0 )
ret = - 1 ;
if ( virTestRun ( tn ( " qemumigrationcookieXML2XML-load- " , name , NULL ) ,
testQemuMigrationCookieXMLLoad , data ) < 0 )
ret = - 1 ;
if ( virTestRun ( tn ( " qemumigrationcookieXML2XML-parse- " , name , NULL ) ,
testQemuMigrationCookieParse , data ) < 0 )
ret = - 1 ;
if ( virTestRun ( tn ( " qemumigrationcookieXML2XML-migparams- " , name , NULL ) ,
testQemuMigrationCookieBlockDirtyBitmaps , data ) < 0 )
ret = - 1 ;
testQemuMigrationCookieDataFree ( data ) ;
return ret ;
}
2020-10-30 17:10:29 +03:00
static int
mymain ( void )
{
int ret = 0 ;
g_autoptr ( virConnect ) conn = NULL ;
if ( qemuTestDriverInit ( & driver ) < 0 )
return EXIT_FAILURE ;
if ( ! ( conn = virGetConnect ( ) ) )
goto cleanup ;
virSetConnectInterface ( conn ) ;
virSetConnectNetwork ( conn ) ;
virSetConnectNWFilter ( conn ) ;
virSetConnectNodeDev ( conn ) ;
virSetConnectSecret ( conn ) ;
virSetConnectStorage ( conn ) ;
if ( testQemuMigrationCookieDom2XML ( " modern " , " qemustatusxml2xmldata/modern-in.xml " , 0 , 0 ) < 0 )
ret = - 1 ;
2020-10-30 19:09:47 +03:00
if ( testQemuMigrationCookieXML2XML ( " basic " , " qemustatusxml2xmldata/modern-in.xml " , 0 ) < 0 | |
testQemuMigrationCookieXML2XML ( " full " , " qemustatusxml2xmldata/modern-in.xml " , 0 ) < 0 )
2020-10-30 17:10:29 +03:00
ret = - 1 ;
2021-02-10 22:37:28 +03:00
if ( testQemuMigrationCookieXML2XMLBitmaps ( " nbd-bitmaps " , " qemustatusxml2xmldata/migration-out-nbd-bitmaps-in.xml " , 0 ) < 0 )
ret = - 1 ;
2020-10-30 17:10:29 +03:00
virBufferFreeAndReset ( & testnamebuf ) ;
cleanup :
qemuTestDriverFree ( & driver ) ;
return ret = = 0 ? EXIT_SUCCESS : EXIT_FAILURE ;
}
VIR_TEST_MAIN_PRELOAD ( mymain ,
VIR_TEST_MOCK ( " virpci " ) ,
VIR_TEST_MOCK ( " virrandom " ) ,
VIR_TEST_MOCK ( " domaincaps " ) ,
VIR_TEST_MOCK ( " virhostid " ) )