backup: Parse and output checkpoint XML
Add a new file checkpoint_conf.c that performs the translation to and
from new XML describing a checkpoint. The code shares a common base
class with snapshots, since a checkpoint similarly represents the
domain state at a moment in time. Add some basic testing of round trip
XML handling through the new code.
Of note - this code intentionally differs from snapshots in that XML
schema validation is unconditional, rather than based on a public API
flag. We have many existing interfaces that still need to add a flag
for opt-in schema validation, but those interfaces have existing
clients that may not have been producing strictly-compliant XML, or we
may still uncover bugs where our RNG grammar is inconsistent with our
code (where omitting the opt-in flag allows existing apps to keep
working while waiting for an RNG patch). But since checkpoints are
brand-new, it's easier to ensure the code matches the schema by always
using the schema. If needed, a later patch could extend the API and
add a flag to turn on to request schema validation, rather than having
it forced (possibly just the validation of the <domain> sub-element
during REDEFINE) - but if a user encounters XML that looks like it
should be good but fails to validate with our RNG schema, they would
either have to upgrade to a new libvirt that adds the new flag, or
upgrade to a new libvirt that fixes the RNG schema, which implies
adding such a flag won't help much.
Also, the redefine flag requires the <domain> sub-element to be
present, rather than catering to historical back-compat to older
versions.
Signed-off-by: Eric Blake <eblake@redhat.com>
2018-07-08 05:01:14 +03:00
# include <config.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <sys/types.h>
# include <fcntl.h>
# include "testutils.h"
# ifdef WITH_QEMU
# include "internal.h"
# include "qemu / qemu_conf.h"
# include "qemu / qemu_domain.h"
# include "checkpoint_conf.h"
# include "testutilsqemu.h"
# include "virstring.h"
# define VIR_FROM_THIS VIR_FROM_NONE
static virQEMUDriver driver ;
enum {
TEST_REDEFINE = 1 < < 0 , /* Test use of REDEFINE parse flag */
TEST_PARENT = 1 < < 1 , /* hard-code parent after parse */
TEST_VDA_BITMAP = 1 < < 2 , /* hard-code disk vda after parse */
TEST_SIZE = 1 < < 3 , /* Test use of SIZE format flag */
TEST_INVALID = 1 < < 4 , /* Test that input fails parse */
} ;
static int
testCompareXMLToXMLFiles ( const char * inxml ,
const char * outxml ,
unsigned int flags )
{
unsigned int parseflags = 0 ;
unsigned int formatflags = VIR_DOMAIN_CHECKPOINT_FORMAT_SECURE ;
VIR_AUTOFREE ( char * ) inXmlData = NULL ;
VIR_AUTOFREE ( char * ) outXmlData = NULL ;
VIR_AUTOFREE ( char * ) actual = NULL ;
VIR_AUTOUNREF ( virDomainCheckpointDefPtr ) def = NULL ;
if ( flags & TEST_REDEFINE )
parseflags | = VIR_DOMAIN_CHECKPOINT_PARSE_REDEFINE ;
if ( virTestLoadFile ( inxml , & inXmlData ) < 0 )
return - 1 ;
if ( ! ( flags & TEST_INVALID ) & &
virTestLoadFile ( outxml , & outXmlData ) < 0 )
return - 1 ;
if ( ! ( def = virDomainCheckpointDefParseString ( inXmlData , driver . caps ,
2019-08-06 15:19:35 +03:00
driver . xmlopt , NULL ,
backup: Parse and output checkpoint XML
Add a new file checkpoint_conf.c that performs the translation to and
from new XML describing a checkpoint. The code shares a common base
class with snapshots, since a checkpoint similarly represents the
domain state at a moment in time. Add some basic testing of round trip
XML handling through the new code.
Of note - this code intentionally differs from snapshots in that XML
schema validation is unconditional, rather than based on a public API
flag. We have many existing interfaces that still need to add a flag
for opt-in schema validation, but those interfaces have existing
clients that may not have been producing strictly-compliant XML, or we
may still uncover bugs where our RNG grammar is inconsistent with our
code (where omitting the opt-in flag allows existing apps to keep
working while waiting for an RNG patch). But since checkpoints are
brand-new, it's easier to ensure the code matches the schema by always
using the schema. If needed, a later patch could extend the API and
add a flag to turn on to request schema validation, rather than having
it forced (possibly just the validation of the <domain> sub-element
during REDEFINE) - but if a user encounters XML that looks like it
should be good but fails to validate with our RNG schema, they would
either have to upgrade to a new libvirt that adds the new flag, or
upgrade to a new libvirt that fixes the RNG schema, which implies
adding such a flag won't help much.
Also, the redefine flag requires the <domain> sub-element to be
present, rather than catering to historical back-compat to older
versions.
Signed-off-by: Eric Blake <eblake@redhat.com>
2018-07-08 05:01:14 +03:00
parseflags ) ) ) {
if ( flags & TEST_INVALID )
return 0 ;
return - 1 ;
}
if ( flags & TEST_PARENT ) {
if ( def - > parent . parent_name )
return - 1 ;
if ( VIR_STRDUP ( def - > parent . parent_name , " 1525111885 " ) < 0 )
return - 1 ;
}
if ( flags & TEST_VDA_BITMAP ) {
virDomainCheckpointDiskDefPtr disk ;
if ( VIR_EXPAND_N ( def - > disks , def - > ndisks , 1 ) < 0 )
return - 1 ;
disk = & def - > disks [ 0 ] ;
if ( disk - > bitmap )
return - 1 ;
if ( ! disk - > name ) {
disk - > type = VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP ;
if ( VIR_STRDUP ( disk - > name , " vda " ) < 0 )
return - 1 ;
} else if ( STRNEQ ( disk - > name , " vda " ) ) {
return - 1 ;
}
if ( VIR_STRDUP ( disk - > bitmap , def - > parent . name ) < 0 )
return - 1 ;
}
if ( flags & TEST_SIZE ) {
def - > disks [ 0 ] . size = 1048576 ;
formatflags | = VIR_DOMAIN_CHECKPOINT_FORMAT_SIZE ;
}
/* Parsing XML does not populate the domain definition; work
* around that by not requesting domain on output */
if ( ! def - > parent . dom )
formatflags | = VIR_DOMAIN_CHECKPOINT_FORMAT_NO_DOMAIN ;
if ( ! ( actual = virDomainCheckpointDefFormat ( def , driver . caps ,
driver . xmlopt ,
formatflags ) ) )
return - 1 ;
if ( STRNEQ ( outXmlData , actual ) ) {
virTestDifferenceFull ( stderr , outXmlData , outxml , actual , inxml ) ;
return - 1 ;
}
return 0 ;
}
struct testInfo {
const char * inxml ;
const char * outxml ;
long long creationTime ;
unsigned int flags ;
} ;
static long long mocktime ;
static int
testCheckpointPostParse ( virDomainMomentDefPtr def )
{
if ( ! mocktime )
return 0 ;
if ( def - > creationTime )
return - 1 ;
def - > creationTime = mocktime ;
if ( ! def - > name & &
virAsprintf ( & def - > name , " %lld " , def - > creationTime ) < 0 )
return - 1 ;
return 0 ;
}
static int
testCompareXMLToXMLHelper ( const void * data )
{
const struct testInfo * info = data ;
mocktime = info - > creationTime ;
return testCompareXMLToXMLFiles ( info - > inxml , info - > outxml , info - > flags ) ;
}
static int
mymain ( void )
{
int ret = 0 ;
if ( qemuTestDriverInit ( & driver ) < 0 )
return EXIT_FAILURE ;
virDomainXMLOptionSetMomentPostParse ( driver . xmlopt ,
testCheckpointPostParse ) ;
# define DO_TEST(prefix, name, inpath, outpath, time, flags) \
do { \
const struct testInfo info = { abs_srcdir " / " inpath " / " name " .xml " , \
abs_srcdir " / " outpath " / " name " .xml " , \
time , flags } ; \
if ( virTestRun ( " CHECKPOINT XML-2-XML " prefix " " name , \
testCompareXMLToXMLHelper , & info ) < 0 ) \
ret = - 1 ; \
} while ( 0 )
# define DO_TEST_INOUT(name, time, flags) \
DO_TEST ( " in->out " , name , \
" qemudomaincheckpointxml2xmlin " , \
" qemudomaincheckpointxml2xmlout " , \
time , flags )
# define DO_TEST_OUT(name, flags) \
DO_TEST ( " out->out " , name , \
" qemudomaincheckpointxml2xmlout " , \
" qemudomaincheckpointxml2xmlout " , \
0 , flags | TEST_REDEFINE )
# define DO_TEST_INVALID(name) \
DO_TEST ( " in->out " , name , \
" qemudomaincheckpointxml2xmlin " , \
" qemudomaincheckpointxml2xmlout " , \
0 , TEST_INVALID )
/* Unset or set all envvars here that are copied in qemudBuildCommandLine
* using ADD_ENV_COPY , otherwise these tests may fail due to unexpected
* values for these envvars */
setenv ( " PATH " , " /bin " , 1 ) ;
/* Test a normal user redefine */
DO_TEST_OUT ( " redefine " , 0 ) ;
/* Tests of valid user input, and resulting output */
DO_TEST_INOUT ( " empty " , 1525889631 , TEST_VDA_BITMAP ) ;
DO_TEST_INOUT ( " disk-default " , 1525889631 , TEST_PARENT | TEST_VDA_BITMAP ) ;
DO_TEST_INOUT ( " sample " , 1525889631 , TEST_PARENT | TEST_VDA_BITMAP ) ;
DO_TEST_INOUT ( " size " , 1553648510 ,
TEST_PARENT | TEST_VDA_BITMAP | TEST_SIZE ) ;
/* Tests of invalid user input */
DO_TEST_INVALID ( " disk-invalid " ) ;
DO_TEST_INVALID ( " name-invalid " ) ;
qemuTestDriverFree ( & driver ) ;
return ret = = 0 ? EXIT_SUCCESS : EXIT_FAILURE ;
}
VIR_TEST_MAIN ( mymain )
# else
int
main ( void )
{
return EXIT_AM_SKIP ;
}
# endif /* WITH_QEMU */