[libvirt] [PATCH 0/4] New APIs and virsh commands to manage saved-state & core-dump files

This patch series is a followup of "[PATCH 0/3] Restrict saved-state and core-dump files in controlled directories". A few new APIs and virsh commands are added to manage saved-state & core-dump files. Hong Xiang (4): New APIs to manage saved-state & core-dump files. qemu driver for new APIs to manage saved-state & core-dump files remote driver for new APIs to manage saved-state & core-dump files New virsh commands to manage saved-state and core-dump files include/libvirt/libvirt.h.in | 18 +++ python/generator.py | 2 + python/libvirt-override-api.xml | 10 ++ python/libvirt-override.c | 92 ++++++++++++ src/driver.h | 34 +++++ src/libvirt.c | 314 +++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 12 ++ src/qemu/qemu_driver.c | 253 +++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 8 + src/remote/remote_protocol.x | 61 ++++++++- src/rpc/gendispatch.pl | 1 + tools/virsh.c | 309 ++++++++++++++++++++++++++++++++++++++ 12 files changed, 1113 insertions(+), 1 deletions(-)

New APIS: . virConnectNumOfSavedImages . virConnectListSavedImages . virSavedImageRemove . virSavedImageDownload . virConnectNumOfCoreDumps . virConnectListCoreDumps . virCoreDumpRemove . virCoreDumpDownload * include/libvirt/libvirt.h.in: declarations * src/driver.h: driver extension for new APIs * src/libvirt.c, src/libvirt_public.syms: entry points for new APIs * python/generator.py, python/libvirt-override-api.xml, python/libvirt-override.c: overridden python binding Signed-off-by: Hong Xiang <hxiang@linux.vnet.ibm.com> --- include/libvirt/libvirt.h.in | 18 +++ python/generator.py | 2 + python/libvirt-override-api.xml | 10 ++ python/libvirt-override.c | 92 ++++++++++++ src/driver.h | 34 +++++ src/libvirt.c | 314 +++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 12 ++ 7 files changed, 482 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 361881a..6a2c4e4 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1084,6 +1084,15 @@ int virDomainSaveImageDefineXML (virConnectPtr conn, const char *file, const char *dxml, unsigned int flags); +int virConnectNumOfSavedImages (virConnectPtr conn); +int virConnectListSavedImages (virConnectPtr conn, + char **const files, + int maxfiles); +int virSavedImageRemove (virConnectPtr conn, + const char *file); +int virSavedImageDownload (virConnectPtr conn, + virStreamPtr stream, + const char *file); /* * Managed domain save @@ -1101,6 +1110,15 @@ int virDomainManagedSaveRemove(virDomainPtr dom, int virDomainCoreDump (virDomainPtr domain, const char *to, unsigned int flags); +int virConnectNumOfCoreDumps(virConnectPtr conn); +int virConnectListCoreDumps (virConnectPtr conn, + char **const files, + int maxfiles); +int virCoreDumpRemove (virConnectPtr conn, + const char *file); +int virCoreDumpDownload (virConnectPtr conn, + virStreamPtr stream, + const char *file); /* * Screenshot of current domain console diff --git a/python/generator.py b/python/generator.py index 71afdb7..6ce5add 100755 --- a/python/generator.py +++ b/python/generator.py @@ -414,6 +414,8 @@ skip_impl = ( 'virDomainGetBlockJobInfo', 'virDomainMigrateGetMaxSpeed', 'virDomainBlockStatsFlags', + 'virConnectListSavedImages', + 'virConnectListCoreDumps', ) qemu_skip_impl = ( diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index ef02f34..4a71afb 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -375,5 +375,15 @@ <arg name='flags' type='unsigned int' info='flags, currently unused, pass 0.'/> <return type='unsigned long' info='current max migration speed, or None in case of error'/> </function> + <function name='virConnectListSavedImages' file='python'> + <info>Lists saved state files.</info> + <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> + <return type='str *' info='the list of Names of None in case of error'/> + </function> + <function name='virConnectListCoreDumps' file='python'> + <info>Lists core dump files.</info> + <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> + <return type='str *' info='the list of Names of None in case of error'/> + </function> </symbols> </api> diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 523c03b..caee110 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -4695,6 +4695,96 @@ libvirt_virDomainMigrateGetMaxSpeed(PyObject *self ATTRIBUTE_UNUSED, PyObject *a return(py_retval); } +static PyObject * +libvirt_virConnectListSavedImages(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + PyObject *py_retval; + char **names = NULL; + int c_retval, i; + virConnectPtr conn; + PyObject *pyobj_conn; + + + if (!PyArg_ParseTuple(args, (char *)"O:virConnectListSavedImages", &pyobj_conn)) + return(NULL); + conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virConnectNumOfSavedImages(conn); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) + return VIR_PY_NONE; + + if (c_retval) { + names = malloc(sizeof(*names) * c_retval); + if (!names) + return VIR_PY_NONE; + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virConnectListSavedImages(conn, names, c_retval); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) { + free(names); + return VIR_PY_NONE; + } + } + py_retval = PyList_New(c_retval); + + if (names) { + for (i = 0;i < c_retval;i++) { + PyList_SetItem(py_retval, i, libvirt_constcharPtrWrap(names[i])); + free(names[i]); + } + free(names); + } + + return(py_retval); +} + +static PyObject * +libvirt_virConnectListCoreDumps(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + PyObject *py_retval; + char **names = NULL; + int c_retval, i; + virConnectPtr conn; + PyObject *pyobj_conn; + + + if (!PyArg_ParseTuple(args, (char *)"O:virConnectListCoreDumps", &pyobj_conn)) + return(NULL); + conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virConnectNumOfCoreDumps(conn); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) + return VIR_PY_NONE; + + if (c_retval) { + names = malloc(sizeof(*names) * c_retval); + if (!names) + return VIR_PY_NONE; + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virConnectListCoreDumps(conn, names, c_retval); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) { + free(names); + return VIR_PY_NONE; + } + } + py_retval = PyList_New(c_retval); + + if (names) { + for (i = 0;i < c_retval;i++) { + PyList_SetItem(py_retval, i, libvirt_constcharPtrWrap(names[i])); + free(names[i]); + } + free(names); + } + + return(py_retval); +} + /************************************************************************ * * * The registration stuff * @@ -4786,6 +4876,8 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virDomainGetBlockJobInfo", libvirt_virDomainGetBlockJobInfo, METH_VARARGS, NULL}, {(char *) "virDomainSendKey", libvirt_virDomainSendKey, METH_VARARGS, NULL}, {(char *) "virDomainMigrateGetMaxSpeed", libvirt_virDomainMigrateGetMaxSpeed, METH_VARARGS, NULL}, + {(char *) "virConnectListSavedImages", libvirt_virConnectListSavedImages, METH_VARARGS, NULL}, + {(char *) "virConnectListCoreDumps", libvirt_virConnectListCoreDumps, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; diff --git a/src/driver.h b/src/driver.h index b899d0e..f83db6f 100644 --- a/src/driver.h +++ b/src/driver.h @@ -734,6 +734,32 @@ typedef int typedef int (*virDrvDomainBlockPull)(virDomainPtr dom, const char *path, unsigned long bandwidth, unsigned int flags); +typedef int + (*virDrvNumOfSavedImages) (virConnectPtr conn); +typedef int + (*virDrvListSavedImages) (virConnectPtr conn, + char **const files, + int maxfiles); +typedef int + (*virDrvSavedImageRemove) (virConnectPtr conn, + const char *file); +typedef int + (*virDrvSavedImageDownload) (virConnectPtr conn, + virStreamPtr stream, + const char *file); +typedef int + (*virDrvNumOfCoreDumps) (virConnectPtr conn); +typedef int + (*virDrvListCoreDumps) (virConnectPtr conn, + char **const files, + int maxfiles); +typedef int + (*virDrvCoreDumpRemove) (virConnectPtr conn, + const char *file); +typedef int + (*virDrvCoreDumpDownload) (virConnectPtr conn, + virStreamPtr stream, + const char *file); /** @@ -893,6 +919,14 @@ struct _virDriver { virDrvDomainGetBlockJobInfo domainGetBlockJobInfo; virDrvDomainBlockJobSetSpeed domainBlockJobSetSpeed; virDrvDomainBlockPull domainBlockPull; + virDrvNumOfSavedImages numOfSavedImages; + virDrvListSavedImages listSavedImages; + virDrvSavedImageRemove savedImageRemove; + virDrvSavedImageDownload savedImageDownload; + virDrvNumOfCoreDumps numOfCoreDumps; + virDrvListCoreDumps listCoreDumps; + virDrvCoreDumpRemove coreDumpRemove; + virDrvCoreDumpDownload coreDumpDownload; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index 5de60c7..b715f0b 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -2829,6 +2829,163 @@ error: } /** + * virConnectNumOfSavedImages: + * @conn: pointer to the hypervisor connection + * + * Provides the number of saved state files. + * + * Returns number of saved state files on success and -1 on failure. + */ +int +virConnectNumOfSavedImages(virConnectPtr conn) +{ + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->driver->numOfSavedImages) { + int ret; + + ret = conn->driver->numOfSavedImages(conn); + + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + +/** + * virConnectListSavedImages: + * @conn: pointer to the hypervisor connection + * @files: pointer to array to store save state filenames + * @maxfiles: size of the array + * + * Lists saved state files. + * + * Returns number of saved state files on success and -1 on failure. + */ +int +virConnectListSavedImages(virConnectPtr conn, + char **const files, + int maxfiles) +{ + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->driver->listSavedImages) { + int ret; + + ret = conn->driver->listSavedImages(conn, files, maxfiles); + if (ret < 0) + goto error; + + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + +/** + * virSavedImageRemove: + * @conn: pointer to the hypervisor connection + * @file: path to saved state file + * + * Removes specified saved state file. + * + * Returns 0 on success and -1 on failure. + */ +int +virSavedImageRemove(virConnectPtr conn, const char *file) +{ + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->savedImageRemove) { + int ret; + + ret = conn->driver->savedImageRemove(conn, file); + + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + +/** + * virSavedImageDownload: + * @conn: pointer to the hypervisor connection + * @stream: stream to use as output + * @file: path to saved state file + * + * Downloads specified saved state file. + * + * Returns 0 on success and -1 on failure. + */ +int +virSavedImageDownload(virConnectPtr conn, + virStreamPtr stream, + const char *file) +{ + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->driver->savedImageDownload) { + int ret; + + ret = conn->driver->savedImageDownload(conn, stream, file); + + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + +/** * virDomainCoreDump: * @domain: a domain object * @to: path for the core file @@ -2913,6 +3070,163 @@ error: } /** + * virConnectNumOfCoreDumps: + * @conn: pointer to the hypervisor connection + * + * Provides number of core files. + * + * Returns number of core files on success and -1 on failure. + */ +int +virConnectNumOfCoreDumps(virConnectPtr conn) +{ + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->driver->numOfCoreDumps) { + int ret; + + ret = conn->driver->numOfCoreDumps(conn); + + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + +/** + * virConnectListCoreDumps: + * @conn: pointer to the hypervisor connection + * @files: pointer to array to store core filenames + * @maxfiles: size of the array + * + * Lists core files. + * + * Returns number of core files on success and -1 on failure. + */ +int +virConnectListCoreDumps(virConnectPtr conn, + char **const files, + int maxfiles) +{ + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->driver->listCoreDumps) { + int ret; + + ret = conn->driver->listCoreDumps(conn, files, maxfiles); + if (ret < 0) + goto error; + + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + +/** + * virCoreDumpRemove: + * @conn: pointer to the hypervisor connection + * @file: path to core file + * + * Removes specified core file. + * + * Returns 0 on success and -1 on failure. + */ +int +virCoreDumpRemove(virConnectPtr conn, const char *file) +{ + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->coreDumpRemove) { + int ret; + + ret = conn->driver->coreDumpRemove(conn, file); + + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + +/** + * virCoreDumpDownload: + * @conn: pointer to the hypervisor connection + * @stream: stream to use as output + * @file: path to core file + * + * Downloads specified core file. + * + * Returns 0 on success and -1 on failure. + */ +int +virCoreDumpDownload(virConnectPtr conn, + virStreamPtr stream, + const char *file) +{ + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->driver->coreDumpDownload) { + int ret; + + ret = conn->driver->coreDumpDownload(conn, stream, file); + + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + +/** * virDomainScreenshot: * @domain: a domain object * @stream: stream to use as output diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 9762fc4..b97b24f 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -498,3 +498,15 @@ LIBVIRT_0.9.7 { } LIBVIRT_0.9.5; # .... define new API here using predicted next version number .... + +LIBVIRT_0.9.8 { + global: + virConnectNumOfSavedImages; + virConnectListSavedImages; + virSavedImageRemove; + virSavedImageDownload; + virConnectNumOfCoreDumps; + virConnectListCoreDumps; + virCoreDumpRemove; + virCoreDumpDownload; +} LIBVIRT_0.9.7; -- 1.7.1

On Tue, Oct 25, 2011 at 03:44:36PM +0800, Hong Xiang wrote:
New APIS: . virConnectNumOfSavedImages . virConnectListSavedImages . virSavedImageRemove . virSavedImageDownload . virConnectNumOfCoreDumps . virConnectListCoreDumps . virCoreDumpRemove . virCoreDumpDownload
* include/libvirt/libvirt.h.in: declarations * src/driver.h: driver extension for new APIs * src/libvirt.c, src/libvirt_public.syms: entry points for new APIs * python/generator.py, python/libvirt-override-api.xml, python/libvirt-override.c: overridden python binding
Signed-off-by: Hong Xiang <hxiang@linux.vnet.ibm.com> --- include/libvirt/libvirt.h.in | 18 +++ python/generator.py | 2 + python/libvirt-override-api.xml | 10 ++ python/libvirt-override.c | 92 ++++++++++++ src/driver.h | 34 +++++ src/libvirt.c | 314 +++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 12 ++ 7 files changed, 482 insertions(+), 0 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 361881a..6a2c4e4 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1084,6 +1084,15 @@ int virDomainSaveImageDefineXML (virConnectPtr conn, const char *file, const char *dxml, unsigned int flags); +int virConnectNumOfSavedImages (virConnectPtr conn); +int virConnectListSavedImages (virConnectPtr conn, + char **const files, + int maxfiles); +int virSavedImageRemove (virConnectPtr conn, + const char *file); +int virSavedImageDownload (virConnectPtr conn, + virStreamPtr stream, + const char *file);
THis looks very much like it is tending towards re-inventing most of the existing snapshot and/or managed save functionality.
@@ -1101,6 +1110,15 @@ int virDomainManagedSaveRemove(virDomainPtr dom, int virDomainCoreDump (virDomainPtr domain, const char *to, unsigned int flags); +int virConnectNumOfCoreDumps(virConnectPtr conn); +int virConnectListCoreDumps (virConnectPtr conn, + char **const files, + int maxfiles); +int virCoreDumpRemove (virConnectPtr conn, + const char *file); +int virCoreDumpDownload (virConnectPtr conn, + virStreamPtr stream, + const char *file);
This is slightly more palatable. IMHO we do want some kind of 'managed core dump' functionality to associate these APIs with, since we can't change the semantics of the existing virDOmainCoreDump API. ie we want something like virDomainManagedCoreDump(virDomainPtr dom, int flags); and now libvirt will pick the core dump filename, probably based on date. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 10/25/2011 05:28 PM, Daniel P. Berrange wrote:
+int virConnectNumOfSavedImages (virConnectPtr conn); +int virConnectListSavedImages (virConnectPtr conn, + char **const files, + int maxfiles); +int virSavedImageRemove (virConnectPtr conn, + const char *file); +int virSavedImageDownload (virConnectPtr conn, + virStreamPtr stream, + const char *file);
THis looks very much like it is tending towards re-inventing most of the existing snapshot and/or managed save functionality.
I think one limitation of current managed save functionality is there can be at most 1 saved state file per domain. I'm not sure for snapshot, but looks like it's not supported for raw image, while save is.
@@ -1101,6 +1110,15 @@ int virDomainManagedSaveRemove(virDomainPtr dom, int virDomainCoreDump (virDomainPtr domain, const char *to, unsigned int flags); +int virConnectNumOfCoreDumps(virConnectPtr conn); +int virConnectListCoreDumps (virConnectPtr conn, + char **const files, + int maxfiles); +int virCoreDumpRemove (virConnectPtr conn, + const char *file); +int virCoreDumpDownload (virConnectPtr conn, + virStreamPtr stream, + const char *file);
This is slightly more palatable. IMHO we do want some kind of 'managed core dump' functionality to associate these APIs with, since we can't change the semantics of the existing virDOmainCoreDump API. ie we want something like
virDomainManagedCoreDump(virDomainPtr dom, int flags);
and now libvirt will pick the core dump filename, probably based on date.
Daniel
Maybe we can also have: virDomainManagedCoreDumpExt(virDomainPtr dom, int flags, char * id); To support more than one core dump files. Similar for managed save. IMHO, existing functionality is a little buggy, and there's already a bug for this: https://bugzilla.redhat.com/show_bug.cgi?id=746072 Thanks. Hong Xiang

qemu driver implementation of new APIs. * src/qemu/qemu_driver.c: qemu driver implementation Signed-off-by: Hong Xiang <hxiang@linux.vnet.ibm.com> --- src/qemu/qemu_driver.c | 253 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 253 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 36d6284..0dc0530 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3113,6 +3113,128 @@ cleanup: return ret; } +static int +qemuNumOfCoreDumps(virConnectPtr conn) +{ + struct qemud_driver *driver = conn->privateData; + DIR * dir = NULL; + struct dirent * ent; + int num = 0; + + qemuDriverLock(driver); + if (NULL == (dir = opendir(driver->coreDumpDir))) { + qemuReportError(VIR_ERR_OPEN_FAILED, _("cannot open directory '%s'"), + driver->coreDumpDir); + goto cleanup; + } + while (NULL != (ent = readdir(dir))) { + if('.' == ent->d_name[0]) + continue; + num ++; + } + closedir(dir); + qemuDriverUnlock(driver); + return num; + +cleanup: + if (NULL != dir) + closedir(dir); + qemuDriverUnlock(driver); + return -1; +} + +static int +qemuListCoreDumps(virConnectPtr conn, + char **const files, + int maxfiles) +{ + struct qemud_driver *driver = conn->privateData; + DIR * dir = NULL; + struct dirent * ent; + int num = 0; + + qemuDriverLock(driver); + if (NULL == (dir = opendir(driver->coreDumpDir))) { + qemuReportError(VIR_ERR_SYSTEM_ERROR, _("cannot open directory '%s'"), + driver->coreDumpDir); + goto cleanup; + } + while (NULL != (ent = readdir(dir))) { + int res; + if ('.' == ent->d_name[0]) + continue; + if (num >= maxfiles) + break; + res = virBase64DecodePathname(ent->d_name, &(files[num])); + if (-2 == res) { + VIR_WARN("Invalid coredump name '%s'", ent->d_name); + continue; + } else if (0 > res) { + goto cleanup; + } + num ++; + } + closedir(dir); + qemuDriverUnlock(driver); + return num; + +cleanup: + for ( ; num > 0; num --) { + VIR_FREE(files[num - 1]); + } + if (NULL != dir) + closedir(dir); + qemuDriverUnlock(driver); + return -1; +} + +static int +qemuCoreDumpRemove(virConnectPtr conn, + const char *file) +{ + struct qemud_driver *driver = conn->privateData; + char *path = NULL; + + qemuDriverLock(driver); + if (NULL == (path = qemuCoreDumpPath(driver, file))) + goto cleanup; + if (unlink(path)) { + qemuReportError(VIR_ERR_SYSTEM_ERROR, _("cannot remove '%s'"), path); + goto cleanup; + } + VIR_FREE(path); + qemuDriverUnlock(driver); + return 0; + +cleanup: + VIR_FREE(path); + qemuDriverUnlock(driver); + return -1; +} + +static int +qemuCoreDumpDownload(virConnectPtr conn, + virStreamPtr stream, + const char *file) +{ + struct qemud_driver *driver = conn->privateData; + char *path = NULL; + + qemuDriverLock(driver); + if (NULL == (path = qemuCoreDumpPath(driver, file))) + goto cleanup; + if (virFDStreamOpenFile(stream, path, 0, 0, O_RDONLY)) + goto cleanup; + VIR_FREE(path); + qemuDriverUnlock(driver); + return 0; + +cleanup: + VIR_FREE(path); + qemuDriverUnlock(driver); + return -1; +} + static char * qemuDomainScreenshot(virDomainPtr dom, virStreamPtr st, @@ -4439,6 +4561,129 @@ cleanup: return ret; } +static int +qemuNumOfSavedImages(virConnectPtr conn) +{ + struct qemud_driver *driver = conn->privateData; + DIR * dir = NULL; + struct dirent * ent; + int num = 0; + + qemuDriverLock(driver); + if (NULL == (dir = opendir(driver->savedImageDir))) { + qemuReportError(VIR_ERR_OPEN_FAILED, _("cannot open directory '%s'"), + driver->savedImageDir); + goto cleanup; + } + while (NULL != (ent = readdir(dir))) { + if ('.' == ent->d_name[0]) + continue; + num ++; + } + closedir(dir); + qemuDriverUnlock(driver); + return num; + +cleanup: + if (NULL != dir) + closedir(dir); + qemuDriverUnlock(driver); + return -1; +} + +static int +qemuListSavedImages(virConnectPtr conn, + char **const files, + int maxfiles) +{ + struct qemud_driver *driver = conn->privateData; + DIR * dir = NULL; + struct dirent * ent; + int num = 0; + + qemuDriverLock(driver); + if (NULL == (dir = opendir(driver->savedImageDir))) { + qemuReportError(VIR_ERR_SYSTEM_ERROR, _("cannot open directory '%s'"), + driver->savedImageDir); + goto cleanup; + } + while (NULL != (ent = readdir(dir))) { + int res; + + if ('.' == ent->d_name[0]) + continue; + if (num >= maxfiles) + break; + res = virBase64DecodePathname(ent->d_name, &(files[num])); + if (-2 == res) { + VIR_WARN("Invalid saved image name '%s'", ent->d_name); + continue; + } else if (0 > res) { + goto cleanup; + } + num ++; + } + closedir(dir); + qemuDriverUnlock(driver); + return num; + +cleanup: + for ( ; num > 0; num --) { + VIR_FREE(files[num - 1]); + } + if (NULL != dir) + closedir(dir); + qemuDriverUnlock(driver); + return -1; +} + +static int +qemuSavedImageRemove(virConnectPtr conn, + const char *file) +{ + struct qemud_driver *driver = conn->privateData; + char *path = NULL; + + qemuDriverLock(driver); + if (NULL == (path = qemuSavedImagePath(driver, file))) + goto cleanup; + if (unlink(path)) { + qemuReportError(VIR_ERR_SYSTEM_ERROR, _("cannot remove '%s'"), path); + goto cleanup; + } + VIR_FREE(path); + qemuDriverUnlock(driver); + return 0; + +cleanup: + VIR_FREE(path); + qemuDriverUnlock(driver); + return -1; +} + +static int +qemuSavedImageDownload(virConnectPtr conn, + virStreamPtr stream, + const char *file) +{ + struct qemud_driver *driver = conn->privateData; + char *path = NULL; + + qemuDriverLock(driver); + if (NULL == (path = qemuSavedImagePath(driver, file))) + goto cleanup; + if (virFDStreamOpenFile(stream, path, 0, 0, O_RDONLY)) + goto cleanup; + VIR_FREE(path); + qemuDriverUnlock(driver); + return 0; + +cleanup: + VIR_FREE(path); + qemuDriverUnlock(driver); + return -1; +} + /* Return 0 on success, 1 if incomplete saved image was silently unlinked, * and -1 on failure with error raised. */ static int @@ -10893,6 +11138,14 @@ static virDriver qemuDriver = { .domainGetBlockJobInfo = qemuDomainGetBlockJobInfo, /* 0.9.4 */ .domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ + .numOfSavedImages = qemuNumOfSavedImages, /* 0.9.8 */ + .listSavedImages = qemuListSavedImages, /* 0.9.8 */ + .savedImageRemove = qemuSavedImageRemove, /* 0.9.8 */ + .savedImageDownload = qemuSavedImageDownload, /* 0.9.8 */ + .numOfCoreDumps = qemuNumOfCoreDumps, /* 0.9.8 */ + .listCoreDumps = qemuListCoreDumps, /* 0.9.8 */ + .coreDumpRemove = qemuCoreDumpRemove, /* 0.9.8 */ + .coreDumpDownload = qemuCoreDumpDownload, /* 0.9.8 */ }; -- 1.7.1

remote driver implementation of new APIs. * src/remote/remote_driver.c: new fields for remote_driver * src/remote/remote_protocol.x: remote protocol definition * src/rpc/gendispatch.pl: special case priv_name for (SavedImage|CoreDump) Signed-off-by: Hong Xiang <hxiang@linux.vnet.ibm.com> --- src/remote/remote_driver.c | 8 +++++ src/remote/remote_protocol.x | 61 +++++++++++++++++++++++++++++++++++++++++- src/rpc/gendispatch.pl | 1 + 3 files changed, 69 insertions(+), 1 deletions(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 1dea327..b432154 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -4438,6 +4438,14 @@ static virDriver remote_driver = { .domainGetBlockJobInfo = remoteDomainGetBlockJobInfo, /* 0.9.4 */ .domainBlockJobSetSpeed = remoteDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = remoteDomainBlockPull, /* 0.9.4 */ + .numOfSavedImages = remoteNumOfSavedImages, /* 0.9.8 */ + .listSavedImages = remoteListSavedImages, /* 0.9.8 */ + .savedImageRemove = remoteSavedImageRemove, /* 0.9.8 */ + .savedImageDownload = remoteSavedImageDownload, /* 0.9.8 */ + .numOfCoreDumps = remoteNumOfCoreDumps, /* 0.9.8 */ + .listCoreDumps = remoteListCoreDumps, /* 0.9.8 */ + .coreDumpRemove = remoteCoreDumpRemove, /* 0.9.8 */ + .coreDumpDownload = remoteCoreDumpDownload, /* 0.9.8 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index f95253e..ae7049e 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -196,6 +196,16 @@ const REMOTE_CPU_BASELINE_MAX = 256; */ const REMOTE_DOMAIN_SEND_KEY_MAX = 16; +/* + * Max number of save images. + */ +const REMOTE_DOMAIN_SAVED_IMAGE_MAX = 1024; + +/* + * Max number of core dumps. + */ +const REMOTE_DOMAIN_CORE_DUMP_MAX = 1024; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -2253,6 +2263,46 @@ struct remote_domain_get_control_info_ret { /* insert@1 */ unsigned hyper stateTime; }; +struct remote_num_of_saved_images_ret { + int num; +}; + +struct remote_list_saved_images_args { + int maxfiles; +}; + +struct remote_list_saved_images_ret { + remote_nonnull_string files<REMOTE_DOMAIN_SAVED_IMAGE_MAX>; /* insert@1 */ +}; + +struct remote_saved_image_remove_args { + remote_nonnull_string file; +}; + +struct remote_saved_image_download_args { + remote_nonnull_string file; +}; + +struct remote_num_of_core_dumps_ret { + int num; +}; + +struct remote_list_core_dumps_args { + int maxfiles; +}; + +struct remote_list_core_dumps_ret { + remote_nonnull_string files<REMOTE_DOMAIN_CORE_DUMP_MAX>; /* insert@1 */ +}; + +struct remote_core_dump_remove_args { + remote_nonnull_string file; +}; + +struct remote_core_dump_download_args { + remote_nonnull_string file; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -2546,7 +2596,16 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SNAPSHOT_GET_PARENT = 244, /* autogen autogen priority:high */ REMOTE_PROC_DOMAIN_RESET = 245, /* autogen autogen */ REMOTE_PROC_DOMAIN_SNAPSHOT_NUM_CHILDREN = 246, /* autogen autogen priority:high */ - REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_CHILDREN_NAMES = 247 /* autogen autogen priority:high */ + REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_CHILDREN_NAMES = 247, /* autogen autogen priority:high */ + REMOTE_PROC_NUM_OF_SAVED_IMAGES = 248, /* autogen autogen */ + REMOTE_PROC_LIST_SAVED_IMAGES = 249, /* autogen autogen */ + REMOTE_PROC_SAVED_IMAGE_REMOVE = 250, /* autogen autogen */ + + REMOTE_PROC_SAVED_IMAGE_DOWNLOAD = 251, /* autogen autogen | readstream@1 */ + REMOTE_PROC_NUM_OF_CORE_DUMPS = 252, /* autogen autogen */ + REMOTE_PROC_LIST_CORE_DUMPS = 253, /* autogen autogen */ + REMOTE_PROC_CORE_DUMP_REMOVE = 254, /* autogen autogen */ + REMOTE_PROC_CORE_DUMP_DOWNLOAD = 255 /* autogen autogen | readstream@1 */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index b7ac3c8..234b751 100755 --- a/src/rpc/gendispatch.pl +++ b/src/rpc/gendispatch.pl @@ -1174,6 +1174,7 @@ elsif ($opt_k) { # fix priv_name for the NumOf* functions if ($priv_name eq "privateData" and !($call->{ProcName} =~ m/(Domains|DomainSnapshot)/) and + !($call->{ProcName} =~ m/(SavedImage|CoreDump)/) and ($call->{ProcName} =~ m/NumOf(Defined|Domain)*(\S+)s/ or $call->{ProcName} =~ m/List(Defined|Domain)*(\S+)s/)) { my $prefix = lc $2; -- 1.7.1

New virsh commands: . save-image-list . save-image-remove <file> . save-image-download <file> <local-file> . dump-list . dump-remove <file> . dump-download <file> <local-file> * tools/virsh.c: new commands implementations Signed-off-by: Hong Xiang <hxiang@linux.vnet.ibm.com> --- tools/virsh.c | 309 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 309 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 72344f0..a1cafbe 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -300,6 +300,8 @@ static int vshCommandOptULongLong(const vshCmd *cmd, const char *name, static bool vshCommandOptBool(const vshCmd *cmd, const char *name); static const vshCmdOpt *vshCommandOptArgv(const vshCmd *cmd, const vshCmdOpt *opt); +static int vshStreamSink(virStreamPtr st ATTRIBUTE_UNUSED, + const char *bytes, size_t nbytes, void *opaque); #define VSH_BYID (1 << 1) #define VSH_BYUUID (1 << 2) @@ -2445,6 +2447,155 @@ cleanup: } /* + * "save-image-list" command + */ +static const vshCmdInfo info_save_image_list[] = { + {"help", N_("list saved state files")}, + {"desc", N_("Output the list of saved state files.\n")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_save_image_list[] = { + {NULL, 0, 0, NULL} +}; + +static bool +cmdSaveImageList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + int maxfiles = 0, i; + char ** files = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if(0 > (maxfiles = virConnectNumOfSavedImages(ctl->conn))) { + vshError(ctl, "%s", _("Failed to list saved state files")); + return false; + } + if(maxfiles) { + files = vshMalloc(ctl, sizeof(char *) * maxfiles); + if(0 > (maxfiles = virConnectListSavedImages(ctl->conn, files, + maxfiles))) { + vshError(ctl, "%s", _("Failed to list saved state files")); + VIR_FREE(files); + return false; + } + qsort(files, maxfiles, sizeof(char *), namesorter); + } + for(i = 0; i < maxfiles; i ++) { + vshPrint(ctl, "%s\n", files[i]); + VIR_FREE(files[i]); + } + + VIR_FREE(files); + return true; +} + +/* + * "save-image-remove" command + */ +static const vshCmdInfo info_save_image_remove[] = { + {"help", N_("remove a saved state file")}, + {"desc", N_("Remove the specified saved state file")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_save_image_remove[] = { + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to remove")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdSaveImageRemove(vshControl *ctl, const vshCmd *cmd) +{ + const char *file = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (vshCommandOptString(cmd, "file", &file) <= 0) + return false; + + if(virSavedImageRemove(ctl->conn, file)) { + vshPrint(ctl, _("Failed to remove saved state file '%s'.\n"), + file); + return false; + } + + return true; +} + +/* + * "save-image-download" command + */ +static const vshCmdInfo info_save_image_download[] = { + {"help", N_("download a saved state file to local")}, + {"desc", N_("Download the specified saved state file to local.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_save_image_download[] = { + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("saved state file to download")}, + {"local-file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("local file")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdSaveImageDownload(vshControl *ctl, const vshCmd *cmd) +{ + const char *file = NULL; + const char *local_file = NULL; + bool ret = false; + int fd = -1; + virStreamPtr stream = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (vshCommandOptString(cmd, "file", &file) <= 0) + return false; + if (vshCommandOptString(cmd, "local-file", &local_file) <= 0) + return false; + + if (0 > (fd = open(local_file, O_WRONLY | O_CREAT | O_TRUNC, 0666))) { + vshError(ctl, _("cannot open file %s"), local_file); + goto cleanup; + } + if (NULL == (stream = virStreamNew(ctl->conn, 0))) { + vshError(ctl, _("cannot open stream")); + goto cleanup; + } + if (0 > virSavedImageDownload(ctl->conn, stream, file)) { + vshError(ctl, _("cannot download saved state file %s"), file); + goto cleanup; + } + if (0 > virStreamRecvAll(stream, vshStreamSink, &fd)) { + vshError(ctl, _("cannot receive data from saved state file %s"), file); + goto cleanup; + } + + if (0 > VIR_CLOSE(fd)) { + vshError(ctl, _("cannot close file %s"), local_file); + virStreamAbort(stream); + goto cleanup; + } + if (0 > virStreamFinish(stream)) { + vshError(ctl, _("cannot close saved state file %s"), file); + goto cleanup; + } + + ret = true; + +cleanup: + VIR_FORCE_CLOSE(fd); + if(!ret) + ignore_value(unlink(local_file)); + if(stream) + virStreamFree(stream); + return ret; +} + +/* * "managedsave" command */ static const vshCmdInfo info_managedsave[] = { @@ -2935,6 +3086,155 @@ cleanup: return ret; } +/* + * "dump-list" command + */ +static const vshCmdInfo info_dump_list[] = { + {"help", N_("list core dump files")}, + {"desc", N_("Output the list of core dump files.\n")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_dump_list[] = { + {NULL, 0, 0, NULL} +}; + +static bool +cmdDumpList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + int maxfiles = 0, i; + char ** files = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if(0 > (maxfiles = virConnectNumOfCoreDumps(ctl->conn))) { + vshError(ctl, "%s", _("Failed to list core dump files")); + return false; + } + if(maxfiles) { + files = vshMalloc(ctl, sizeof(char *) * maxfiles); + if(0 > (maxfiles = virConnectListCoreDumps(ctl->conn, files, + maxfiles))) { + vshError(ctl, "%s", _("Failed to list core dump files")); + VIR_FREE(files); + return false; + } + qsort(files, maxfiles, sizeof(char *), namesorter); + } + for(i = 0; i < maxfiles; i ++) { + vshPrint(ctl, "%s\n", files[i]); + VIR_FREE(files[i]); + } + + VIR_FREE(files); + return true; +} + +/* + * "dump-remove" command + */ +static const vshCmdInfo info_dump_remove[] = { + {"help", N_("remove a core dump file")}, + {"desc", N_("Remove the specified core dump file")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_dump_remove[] = { + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("core dump file to remove")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdDumpRemove(vshControl *ctl, const vshCmd *cmd) +{ + const char *file = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (vshCommandOptString(cmd, "file", &file) <= 0) + return false; + + if(virCoreDumpRemove(ctl->conn, file)) { + vshPrint(ctl, _("Failed to remove core dump file %s.\n"), + file); + return false; + } + + return true; +} + +/* + * "dump-download" command + */ +static const vshCmdInfo info_dump_download[] = { + {"help", N_("download a core dump file to local")}, + {"desc", N_("Download the specified core dump file to local.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_dump_download[] = { + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("core dump file to download")}, + {"local-file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("local file")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdDumpDownload(vshControl *ctl, const vshCmd *cmd) +{ + const char *file = NULL; + const char *local_file = NULL; + bool ret = false; + int fd = -1; + virStreamPtr stream = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (vshCommandOptString(cmd, "file", &file) <= 0) + return false; + if (vshCommandOptString(cmd, "local-file", &local_file) <= 0) + return false; + + if (0 > (fd = open(local_file, O_WRONLY | O_CREAT | O_TRUNC, 0666))) { + vshError(ctl, _("cannot open file %s"), local_file); + goto cleanup; + } + if(NULL == (stream = virStreamNew(ctl->conn, 0))) { + vshError(ctl, _("cannot open stream")); + goto cleanup; + } + if (0 > virCoreDumpDownload(ctl->conn, stream, file)) { + vshError(ctl, _("cannot download core dump file %s"), file); + goto cleanup; + } + if (0 > virStreamRecvAll(stream, vshStreamSink, &fd)) { + vshError(ctl, _("cannot receive data from core dump file %s"), file); + goto cleanup; + } + + if (0 > VIR_CLOSE(fd)) { + vshError(ctl, _("cannot close file %s"), local_file); + virStreamAbort(stream); + goto cleanup; + } + if (0 > virStreamFinish(stream)) { + vshError(ctl, _("cannot close core dump file %s"), file); + goto cleanup; + } + + ret = true; + +cleanup: + VIR_FORCE_CLOSE(fd); + if(!ret) + ignore_value(unlink(local_file)); + if(stream) + virStreamFree(stream); + return ret; +} + static const vshCmdInfo info_screenshot[] = { {"help", N_("take a screenshot of a current domain console and store it " "into a file")}, @@ -14041,6 +14341,9 @@ static const vshCmdDef domManagementCmds[] = { {"domxml-to-native", cmdDomXMLToNative, opts_domxmltonative, info_domxmltonative, 0}, {"dump", cmdDump, opts_dump, info_dump, 0}, + {"dump-list", cmdDumpList, opts_dump_list, info_dump_list, 0}, + {"dump-remove", cmdDumpRemove, opts_dump_remove, info_dump_remove, 0}, + {"dump-download", cmdDumpDownload, opts_dump_download, info_dump_download, 0}, {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml, 0}, {"edit", cmdEdit, opts_edit, info_edit, 0}, {"inject-nmi", cmdInjectNMI, opts_inject_nmi, info_inject_nmi, 0}, @@ -14068,6 +14371,12 @@ static const vshCmdDef domManagementCmds[] = { info_save_image_dumpxml, 0}, {"save-image-edit", cmdSaveImageEdit, opts_save_image_edit, info_save_image_edit, 0}, + {"save-image-list", cmdSaveImageList, opts_save_image_list, + info_save_image_list, 0}, + {"save-image-remove", cmdSaveImageRemove, opts_save_image_remove, + info_save_image_remove, 0}, + {"save-image-download", cmdSaveImageDownload, opts_save_image_download, + info_save_image_download, 0}, {"schedinfo", cmdSchedinfo, opts_schedinfo, info_schedinfo, 0}, {"screenshot", cmdScreenshot, opts_screenshot, info_screenshot, 0}, {"setmaxmem", cmdSetmaxmem, opts_setmaxmem, info_setmaxmem, 0}, -- 1.7.1
participants (2)
-
Daniel P. Berrange
-
Hong Xiang