[libvirt] [python PATCH v7] Add virDomainCheckpoint APIs

Copies heavily from existing virDomainSnapshot handling, regarding what special cases the generator has to be taught and what overrides need to be written. Signed-off-by: Eric Blake <eblake@redhat.com> --- Python counterparts to my incremental backup patches. An earlier version was already reviewed by Dan; the main diff here is the addition of virDomainSnapshotCreateXML2 handling. HACKING | 2 + MANIFEST.in | 1 + generator.py | 37 ++++++++-- libvirt-override-api.xml | 12 ++++ libvirt-override-virDomain.py | 13 ++++ libvirt-override-virDomainCheckpoint.py | 19 +++++ libvirt-override.c | 96 +++++++++++++++++++++++++ sanitytest.py | 11 +-- typewrappers.c | 13 ++++ typewrappers.h | 10 +++ 10 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 libvirt-override-virDomainCheckpoint.py diff --git a/HACKING b/HACKING index 6eeb9e6..39e7cd3 100644 --- a/HACKING +++ b/HACKING @@ -28,6 +28,8 @@ hand written source files the virConnect class - libvirt-override-virDomain.py - high level overrides in the virDomain class + - libvirt-override-virDomainCheckpoint.py - high level overrides in + the virDomainCheckpoint class - libvirt-override-virDomainSnapshot.py - high level overrides in the virDomainSnapshot class - libvirt-override-virStoragePool.py - high level overrides in diff --git a/MANIFEST.in b/MANIFEST.in index b6788f4..5d2f559 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -23,6 +23,7 @@ include libvirt-override.c include libvirt-override.py include libvirt-override-virConnect.py include libvirt-override-virDomain.py +include libvirt-override-virDomainCheckpoint.py include libvirt-override-virDomainSnapshot.py include libvirt-override-virStoragePool.py include libvirt-override-virStream.py diff --git a/generator.py b/generator.py index ffa3ce5..a16f9b1 100755 --- a/generator.py +++ b/generator.py @@ -35,6 +35,7 @@ libvirt_headers = [ "libvirt", "libvirt-common", "libvirt-domain", + "libvirt-domain-checkpoint", "libvirt-domain-snapshot", "libvirt-event", "libvirt-host", @@ -364,6 +365,10 @@ py_types = { 'virStream *': ('O', "virStream", "virStreamPtr", "virStreamPtr"), 'const virStream *': ('O', "virStream", "virStreamPtr", "virStreamPtr"), + 'virDomainCheckpointPtr': ('O', "virDomainCheckpoint", "virDomainCheckpointPtr", "virDomainCheckpointPtr"), + 'virDomainCheckpoint *': ('O', "virDomainCheckpoint", "virDomainCheckpointPtr", "virDomainCheckpointPtr"), + 'const virDomainCheckpoint *': ('O', "virDomainCheckpoint", "virDomainCheckpointPtr", "virDomainCheckpointPtr"), + 'virDomainSnapshotPtr': ('O', "virDomainSnapshot", "virDomainSnapshotPtr", "virDomainSnapshotPtr"), 'virDomainSnapshot *': ('O', "virDomainSnapshot", "virDomainSnapshotPtr", "virDomainSnapshotPtr"), 'const virDomainSnapshot *': ('O', "virDomainSnapshot", "virDomainSnapshotPtr", "virDomainSnapshotPtr"), @@ -536,6 +541,8 @@ skip_function = ( 'virSaveLastError', # We have our own python error wrapper 'virFreeError', # Only needed if we use virSaveLastError 'virConnectListAllDomains', # overridden in virConnect.py + 'virDomainListAllCheckpoints', # overridden in virDomain.py + 'virDomainCheckpointListAllChildren', # overridden in virDomainCheckpoint.py 'virDomainListAllSnapshots', # overridden in virDomain.py 'virDomainSnapshotListAllChildren', # overridden in virDomainSnapshot.py 'virConnectListAllStoragePools', # overridden in virConnect.py @@ -582,6 +589,7 @@ skip_function = ( "virStoragePoolRef", "virStorageVolRef", "virStreamRef", + "virDomainCheckpointRef", "virDomainSnapshotRef", # This functions shouldn't be called via the bindings (and even the docs @@ -594,6 +602,8 @@ skip_function = ( "virNWFilterGetConnect", "virStoragePoolGetConnect", "virStorageVolGetConnect", + "virDomainCheckpointGetConnect", + "virDomainCheckpointGetDomain", "virDomainSnapshotGetConnect", "virDomainSnapshotGetDomain", @@ -1023,6 +1033,8 @@ classes_type = { "virStream *": ("._o", "virStream(self, _obj=%s)", "virStream"), "virConnectPtr": ("._o", "virConnect(_obj=%s)", "virConnect"), "virConnect *": ("._o", "virConnect(_obj=%s)", "virConnect"), + "virDomainCheckpointPtr": ("._o", "virDomainCheckpoint(self,_obj=%s)", "virDomainCheckpoint"), + "virDomainCheckpoint *": ("._o", "virDomainCheckpoint(self, _obj=%s)", "virDomainCheckpoint"), "virDomainSnapshotPtr": ("._o", "virDomainSnapshot(self,_obj=%s)", "virDomainSnapshot"), "virDomainSnapshot *": ("._o", "virDomainSnapshot(self, _obj=%s)", "virDomainSnapshot"), } @@ -1031,7 +1043,7 @@ primary_classes = ["virDomain", "virNetwork", "virInterface", "virStoragePool", "virStorageVol", "virConnect", "virNodeDevice", "virSecret", "virNWFilter", "virNWFilterBinding", - "virStream", "virDomainSnapshot"] + "virStream", "virDomainCheckpoint", "virDomainSnapshot"] classes_destructors = { "virDomain": "virDomainFree", @@ -1043,6 +1055,7 @@ classes_destructors = { "virSecret": "virSecretFree", "virNWFilter": "virNWFilterFree", "virNWFilterBinding": "virNWFilterBindingFree", + "virDomainCheckpoint": "virDomainCheckpointFree", "virDomainSnapshot": "virDomainSnapshotFree", # We hand-craft __del__ for this one #"virStream": "virStreamFree", @@ -1053,6 +1066,7 @@ class_skip_connect_impl = { } class_domain_impl = { + "virDomainCheckpoint": True, "virDomainSnapshot": True, } @@ -1171,6 +1185,18 @@ def nameFixup(name, classe, type, file): elif name[0:12] == "virDomainGet": func = name[12:] func = func[0:1].lower() + func[1:] + elif name[0:31] == "virDomainCheckpointLookupByName": + func = name[9:] + func = func[0:1].lower() + func[1:] + elif name[0:28] == "virDomainCheckpointCreateXML": + func = name[9:] + func = func[0:1].lower() + func[1:] + elif name[0:26] == "virDomainCheckpointCurrent": + func = name[9:] + func = func[0:1].lower() + func[1:] + elif name[0:19] == "virDomainCheckpoint": + func = name[19:] + func = func[0:1].lower() + func[1:] elif name[0:29] == "virDomainSnapshotLookupByName": func = name[9:] func = func[0:1].lower() + func[1:] @@ -1183,6 +1209,9 @@ def nameFixup(name, classe, type, file): elif name[0:20] == "virDomainSnapshotNum": func = name[9:] func = func[0:1].lower() + func[1:] + elif name[0:27] == "virDomainSnapshotCreateXML2": + func = name[9:] + func = func[0:1].lower() + func[1:] elif name[0:26] == "virDomainSnapshotCreateXML": func = name[9:] func = func[0:1].lower() + func[1:] @@ -1501,7 +1530,7 @@ def buildWrappers(module): "virStorageVol", "virNodeDevice", "virSecret","virStream", "virNWFilter", "virNWFilterBinding" ]: classes.write(" def __init__(self, conn, _obj=None):\n") - elif classname in [ 'virDomainSnapshot' ]: + elif classname in [ "virDomainCheckpoint", "virDomainSnapshot" ]: classes.write(" def __init__(self, dom, _obj=None):\n") else: classes.write(" def __init__(self, _obj=None):\n") @@ -1513,7 +1542,7 @@ def buildWrappers(module): classes.write(" self._conn = conn\n" + \ " if not isinstance(conn, virConnect):\n" + \ " self._conn = conn._conn\n") - elif classname in [ "virDomainSnapshot" ]: + elif classname in [ "virDomainCheckpoint", "virDomainSnapshot" ]: classes.write(" self._dom = dom\n") classes.write(" self._conn = dom.connect()\n") classes.write(" if type(_obj).__name__ not in [\"PyCapsule\", \"PyCObject\"]:\n") @@ -1641,7 +1670,7 @@ def buildWrappers(module): classes.write( " if ret is None:raise libvirtError('%s() failed', vol=self)\n" % (name)) - elif classname == "virDomainSnapshot": + elif classname in [ "virDomainCheckpoint", "virDomainSnapshot"]: classes.write( " if ret is None:raise libvirtError('%s() failed', dom=self._dom)\n" % (name)) diff --git a/libvirt-override-api.xml b/libvirt-override-api.xml index 7f578e0..1edf0c5 100644 --- a/libvirt-override-api.xml +++ b/libvirt-override-api.xml @@ -576,6 +576,18 @@ <arg name='flags' type='unsigned int' info='flags'/> <return type='int' info="0 on success, -1 on error"/> </function> + <function name='virDomainListAllCheckpoints' file='python'> + <info>returns the list of checkpoints for the given domain</info> + <arg name='dom' type='virDomainPtr' info='pointer to the domain'/> + <arg name='flags' type='unsigned int' info='flags'/> + <return type='char *' info='the list of checkpoints or None in case of error'/> + </function> + <function name='virDomainCheckpointListAllChildren' file='python'> + <info>collect the list of child checkpoint names for the given checkpoint</info> + <arg name='checkpoint' type='virDomainCheckpointPtr' info='pointer to the checkpoint'/> + <arg name='flags' type='unsigned int' info='flags'/> + <return type='char *' info='the list of checkpoints or None in case of error'/> + </function> <function name='virDomainGetBlockJobInfo' file='python'> <info>Get progress information for a block job</info> <arg name='dom' type='virDomainPtr' info='pointer to the domain'/> diff --git a/libvirt-override-virDomain.py b/libvirt-override-virDomain.py index 7c417b8..7ce34f1 100644 --- a/libvirt-override-virDomain.py +++ b/libvirt-override-virDomain.py @@ -11,6 +11,19 @@ return retlist + def listAllCheckpoints(self, flags=0): + """List all checkpoints and returns a list of checkpoint objects""" + ret = libvirtmod.virDomainListAllCheckpoints(self._o, flags) + if ret is None: + raise libvirtError("virDomainListAllCheckpoints() failed", conn=self) + + retlist = list() + for chkptr in ret: + retlist.append(virDomainCheckpoint(self, _obj=chkptr)) + + return retlist + + def createWithFiles(self, files, flags=0): """Launch a defined domain. If the call succeeds the domain moves from the defined to the running domains pools. diff --git a/libvirt-override-virDomainCheckpoint.py b/libvirt-override-virDomainCheckpoint.py new file mode 100644 index 0000000..371b0fd --- /dev/null +++ b/libvirt-override-virDomainCheckpoint.py @@ -0,0 +1,19 @@ + def getConnect(self): + """Get the connection that owns the domain that a checkpoint was created for""" + return self.connect() + + def getDomain(self): + """Get the domain that a checkpoint was created for""" + return self.domain() + + def listAllChildren(self, flags=0): + """List all child checkpoints and returns a list of checkpoint objects""" + ret = libvirtmod.virDomainCheckpointListAllChildren(self._o, flags) + if ret is None: + raise libvirtError("virDomainCheckpointListAllChildren() failed", conn=self) + + retlist = list() + for chkptr in ret: + retlist.append(virDomainCheckpoint(self, _obj=chkptr)) + + return retlist diff --git a/libvirt-override.c b/libvirt-override.c index c5e2908..777f533 100644 --- a/libvirt-override.c +++ b/libvirt-override.c @@ -2325,6 +2325,98 @@ libvirt_virConnectListDefinedDomains(PyObject *self ATTRIBUTE_UNUSED, goto cleanup; } +#if LIBVIR_CHECK_VERSION(5, 1, 0) +static PyObject * +libvirt_virDomainListAllCheckpoints(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *py_retval = NULL; + virDomainCheckpointPtr *chks = NULL; + int c_retval; + ssize_t i; + virDomainPtr dom; + PyObject *pyobj_dom; + unsigned int flags; + + if (!PyArg_ParseTuple(args, (char *)"OI:virDomainListAllCheckpoints", + &pyobj_dom, &flags)) + return NULL; + dom = (virDomainPtr) PyvirDomain_Get(pyobj_dom); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virDomainListAllCheckpoints(dom, &chks, flags); + LIBVIRT_END_ALLOW_THREADS; + + if (c_retval < 0) + return VIR_PY_NONE; + + if (!(py_retval = PyList_New(c_retval))) + goto cleanup; + + for (i = 0; i < c_retval; i++) { + VIR_PY_LIST_SET_GOTO(py_retval, i, + libvirt_virDomainCheckpointPtrWrap(chks[i]), error); + chks[i] = NULL; + } + + cleanup: + for (i = 0; i < c_retval; i++) + if (chks[i]) + virDomainCheckpointFree(chks[i]); + VIR_FREE(chks); + return py_retval; + + error: + Py_CLEAR(py_retval); + goto cleanup; +} + +static PyObject * +libvirt_virDomainCheckpointListAllChildren(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *py_retval = NULL; + virDomainCheckpointPtr *chks = NULL; + int c_retval; + ssize_t i; + virDomainCheckpointPtr parent; + PyObject *pyobj_parent; + unsigned int flags; + + if (!PyArg_ParseTuple(args, (char *)"OI:virDomainCheckpointListAllChildren", + &pyobj_parent, &flags)) + return NULL; + parent = (virDomainCheckpointPtr) PyvirDomainCheckpoint_Get(pyobj_parent); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virDomainCheckpointListAllChildren(parent, &chks, flags); + LIBVIRT_END_ALLOW_THREADS; + + if (c_retval < 0) + return VIR_PY_NONE; + + if (!(py_retval = PyList_New(c_retval))) + goto cleanup; + + for (i = 0; i < c_retval; i++) { + VIR_PY_LIST_SET_GOTO(py_retval, i, + libvirt_virDomainCheckpointPtrWrap(chks[i]), error); + chks[i] = NULL; + } + + cleanup: + for (i = 0; i < c_retval; i++) + if (chks[i]) + virDomainCheckpointFree(chks[i]); + VIR_FREE(chks); + return py_retval; + + error: + Py_CLEAR(py_retval); + goto cleanup; +} +#endif /* LIBVIR_CHECK_VERSION(5, 1, 0) */ + static PyObject * libvirt_virDomainSnapshotListNames(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) @@ -10100,6 +10192,10 @@ static PyMethodDef libvirtMethods[] = { #if LIBVIR_CHECK_VERSION(1, 0, 3) {(char *) "virDomainGetJobStats", libvirt_virDomainGetJobStats, METH_VARARGS, NULL}, #endif /* LIBVIR_CHECK_VERSION(1, 0, 3) */ +#if LIBVIR_CHECK_VERSION(5, 1, 0) + {(char *) "virDomainListAllCheckpoints", libvirt_virDomainListAllCheckpoints, METH_VARARGS, NULL}, + {(char *) "virDomainCheckpointListAllChildren", libvirt_virDomainCheckpointListAllChildren, METH_VARARGS, NULL}, +#endif /* LIBVIR_CHECK_VERSION(5, 1, 0) */ {(char *) "virDomainSnapshotListNames", libvirt_virDomainSnapshotListNames, METH_VARARGS, NULL}, #if LIBVIR_CHECK_VERSION(0, 9, 13) {(char *) "virDomainListAllSnapshots", libvirt_virDomainListAllSnapshots, METH_VARARGS, NULL}, diff --git a/sanitytest.py b/sanitytest.py index 68dde6b..b044231 100644 --- a/sanitytest.py +++ b/sanitytest.py @@ -249,13 +249,13 @@ for name in sorted(basicklassmap): # Remove 'Get' prefix from most APIs, except those in virConnect # and virDomainSnapshot namespaces which stupidly used a different # convention which we now can't fix without breaking API - if func[0:3] == "Get" and klass not in ["virConnect", "virDomainSnapshot", "libvirt"]: + if func[0:3] == "Get" and klass not in ["virConnect", "virDomainCheckpoint", "virDomainSnapshot", "libvirt"]: if func not in ["GetCPUStats", "GetTime"]: func = func[3:] # The object creation and lookup APIs all have to get re-mapped # into the parent class - if func in ["CreateXML", "CreateLinux", "CreateXMLWithFiles", + if func in ["CreateXML", "CreateXML2", "CreateLinux", "CreateXMLWithFiles", "DefineXML", "CreateXMLFrom", "LookupByUUID", "LookupByUUIDString", "LookupByVolume" "LookupByName", "LookupByID", "LookupByName", "LookupByKey", "LookupByPath", @@ -266,7 +266,7 @@ for name in sorted(basicklassmap): if klass != "virDomain": func = klass[3:] + func - if klass == "virDomainSnapshot": + if klass in [ "virDomainCheckpoint", "virDomainSnapshot"]: klass = "virDomain" func = func[6:] elif klass == "virStorageVol" and func in ["StorageVolCreateXMLFrom", "StorageVolCreateXML"]: @@ -297,10 +297,13 @@ for name in sorted(basicklassmap): if func[0:6] == "Change": klass = "virConnect" - # Need to special case the snapshot APIs + # Need to special case the checkpoint and snapshot APIs if klass == "virDomainSnapshot" and func in ["Current", "ListNames", "Num"]: klass = "virDomain" func = "snapshot" + func + elif klass == "virDomainCheckpoint" and func == "Current": + klass = "virDomain" + func = "checkpoint" + func # Names should start with lowercase letter... func = func[0:1].lower() + func[1:] diff --git a/typewrappers.c b/typewrappers.c index 9ba14b4..cd7a70b 100644 --- a/typewrappers.c +++ b/typewrappers.c @@ -568,6 +568,19 @@ libvirt_virStreamPtrWrap(virStreamPtr node) return ret; } +PyObject * +libvirt_virDomainCheckpointPtrWrap(virDomainCheckpointPtr node) +{ + PyObject *ret; + + if (node == NULL) { + return VIR_PY_NONE; + } + + ret = libvirt_buildPyObject(node, "virDomainCheckpointPtr", NULL); + return ret; +} + PyObject * libvirt_virDomainSnapshotPtrWrap(virDomainSnapshotPtr node) { diff --git a/typewrappers.h b/typewrappers.h index 4423774..198397b 100644 --- a/typewrappers.h +++ b/typewrappers.h @@ -128,6 +128,15 @@ typedef struct { } PyvirStream_Object; +#define PyvirDomainCheckpoint_Get(v) (((v) == Py_None) ? NULL : \ + (((PyvirDomainCheckpoint_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + virDomainCheckpointPtr obj; +} PyvirDomainCheckpoint_Object; + + #define PyvirDomainSnapshot_Get(v) (((v) == Py_None) ? NULL : \ (((PyvirDomainSnapshot_Object *)(v))->obj)) @@ -204,6 +213,7 @@ PyObject * libvirt_virSecretPtrWrap(virSecretPtr node); PyObject * libvirt_virNWFilterPtrWrap(virNWFilterPtr node); PyObject * libvirt_virNWFilterBindingPtrWrap(virNWFilterBindingPtr node); PyObject * libvirt_virStreamPtrWrap(virStreamPtr node); +PyObject * libvirt_virDomainCheckpointPtrWrap(virDomainCheckpointPtr node); PyObject * libvirt_virDomainSnapshotPtrWrap(virDomainSnapshotPtr node); -- 2.20.1

On Wed, Mar 27, 2019 at 05:09:27AM -0500, Eric Blake wrote:
Copies heavily from existing virDomainSnapshot handling, regarding what special cases the generator has to be taught and what overrides need to be written.
Signed-off-by: Eric Blake <eblake@redhat.com> ---
Python counterparts to my incremental backup patches. An earlier version was already reviewed by Dan; the main diff here is the addition of virDomainSnapshotCreateXML2 handling.
HACKING | 2 + MANIFEST.in | 1 + generator.py | 37 ++++++++-- libvirt-override-api.xml | 12 ++++ libvirt-override-virDomain.py | 13 ++++ libvirt-override-virDomainCheckpoint.py | 19 +++++ libvirt-override.c | 96 +++++++++++++++++++++++++ sanitytest.py | 11 +-- typewrappers.c | 13 ++++ typewrappers.h | 10 +++ 10 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 libvirt-override-virDomainCheckpoint.py
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 4/3/19 4:49 AM, Daniel P. Berrangé wrote:
On Wed, Mar 27, 2019 at 05:09:27AM -0500, Eric Blake wrote:
Copies heavily from existing virDomainSnapshot handling, regarding what special cases the generator has to be taught and what overrides need to be written.
Signed-off-by: Eric Blake <eblake@redhat.com> ---
Python counterparts to my incremental backup patches. An earlier version was already reviewed by Dan; the main diff here is the addition of virDomainSnapshotCreateXML2 handling.
Now that 5.6.0 will have the virDomainCheckpoint API but not virDomainSnapshotCreateXML2, I've reduced this patch down to the portions still relevant and pushed. When we later get around to integrating snapshots and checkpoints, we'll tweak the python bindings again at that time to cover any additional APIs.
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Regards, Daniel
-- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org
participants (2)
-
Daniel P. Berrangé
-
Eric Blake