[libvirt] [PATCH 00/11] Generic data stream handling

The following series of patches introduce support for generic data streams in the libvirt API, the remote protocol, client & daemon. The public API takes the form of a new object virStreamPtr and methods to read/write/close it The remote protocol was the main hard bit. Since the protocol allows multiple concurrent API calls on a single connection, this needed to also allow concurrent data streams. It is also not acceptable for a large data stream to block other traffic while it is transferring. Thus, we introduce a new protocol message type 'REMOTE_STREAM' to handle transfer for the stream data. A method involving a data streams starts off in the normal way with a REMOTE_CALL to the server, and a REMOTE_REPLY response message. If this was successful, there now follows the data stream traffic. For outgoing streams (data from client to server), the client will send zero or more REMOTE_STREAM packets containing the data with status == REMOTE_CONTINUE. These are asynchronous and not acknowledged by the server. At any time the server may send an async message with a type of REMOTE_STREAM and status of REMOTE_ERROR. This indicates to the client that the transfer is aborting at server request. If the client wishes to abort, it can send the server a REMOTE_STREAM+REMOTE_ERROR message. If the client finishes its data transfer, it will send a final REMOTE_STREAM+REMOTE_OK message, and the server will respond with the same. This full roundtrip handshake ensures any async error messages are guarenteed to be flushed For incoming data streams (data from server to client), the server sends zero or more REMOTE_STREAM packets containing the data with status == REMOTE_CONTINUE. These are asynchronous and not acknowledged by the client. At any time the client may send an async message with a type of REMOTE_STREAM and status of REMOTE_ERROR. This indicates to the server that the transfer is aborting at client request. If the server wishes to abort, it can send the server a REMOTE_STREAM+REMOTE_ERROR message. When the server finishes its data transfer, it will send a final REMOTE_STREAM+REMOTE_CONTINUE message ewith a data length of zero (ie EOF). The client will then send a REMOTE_STREAM+REMOTE_OK packet and the server will respond with the same. This full roundtrip handshake ensures any async error messages are guarenteed to be flushed This all ensures that multiple data streams can be active in parallel, and with a maximum data packet size of 256 KB, no single stream can cause too much latency on the connection for other API calls/streams. The only thing it does not allow for is one API method to use two or more streams. These may be famous last words, but I don't think that use case will be neccessary for any of our APIs... The last 5 patches with a subject of [DEMO] are *NOT* intended to be committed to the repository. They merely demonstrate the use of data streams for a couple of hypothetical file upload and download APIs. Actually they were mostly to allow me to test the code streams code without messing around with the QEMU migration code. The immediate use case for this data stream code is Chris' QEMU migration patchset. The next use case is to allow serial console access to be tunnelled over libvirtd, eg to make 'virsh console GUEST' work remotely. This use case is why I included the support for non-blocking data streams and event loop integration (not required for Chris' migration use case) Anyway, assuming Chris confirms that I've not broken his code, then patches 1-6 are targetted for this next release. .x-sc_avoid_write | 3 include/libvirt/libvirt.h | 101 ++++ include/libvirt/libvirt.h.in | 101 ++++ po/POTFILES.in | 1 qemud/Makefile.am | 1 qemud/dispatch.c | 131 ++++++ qemud/dispatch.h | 17 qemud/event.c | 54 +- qemud/qemud.c | 56 +- qemud/qemud.h | 33 + qemud/remote.c | 237 ++++++++++- qemud/remote.h | 1 qemud/remote_dispatch_args.h | 2 qemud/remote_dispatch_prototypes.h | 153 +++++++ qemud/remote_dispatch_table.h | 10 qemud/remote_generate_stubs.pl | 1 qemud/remote_protocol.c | 18 qemud/remote_protocol.h | 20 qemud/remote_protocol.x | 57 ++ qemud/stream.c | 605 +++++++++++++++++++++++++++++ qemud/stream.h | 53 ++ src/datatypes.c | 59 ++ src/datatypes.h | 33 + src/driver.h | 40 + src/esx/esx_driver.c | 2 src/libvirt.c | 769 +++++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 2 src/libvirt_public.syms | 18 src/lxc_driver.c | 2 src/opennebula/one_driver.c | 2 src/openvz_driver.c | 2 src/qemu_driver.c | 2 src/remote_internal.c | 601 ++++++++++++++++++++++++++++ src/test.c | 477 ++++++++++++++++++++++ src/uml_driver.c | 2 src/vbox/vbox_tmpl.c | 3 src/virsh.c | 164 +++++++ src/xen_unified.c | 2 tests/eventtest.c | 19 39 files changed, 3783 insertions(+), 71 deletions(-) Regards, Daniel

* include/libvirt/libvirt.h, include/libvirt/libvirt.h.in: Public API contract for virStreamPtr object * src/libvirt_public.syms: Export data stream APIs * src/libvirt_private.syms: Export internal helper APIs * src/libvirt.c: Data stream API driver dispatch * src/datatypes.h, src/datatypes.c: Internal helpers for virStreamPtr object * src/driver.h: Define internal driver API for streams * .x-sc_avoid_write: Ignore src/libvirt.c because it trips up on comments including write() --- .x-sc_avoid_write | 1 + include/libvirt/libvirt.h | 93 ++++++ include/libvirt/libvirt.h.in | 93 ++++++ src/datatypes.c | 59 ++++ src/datatypes.h | 33 ++ src/driver.h | 34 ++ src/libvirt.c | 683 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_private.syms | 2 + src/libvirt_public.syms | 15 + 9 files changed, 1013 insertions(+), 0 deletions(-) diff --git a/.x-sc_avoid_write b/.x-sc_avoid_write index 8ed87c5..c5a7535 100644 --- a/.x-sc_avoid_write +++ b/.x-sc_avoid_write @@ -1,5 +1,6 @@ ^src/util\.c$ ^src/xend_internal\.c$ ^src/util-lib\.c$ +^src/libvirt\.c$ ^qemud/qemud.c$ ^gnulib/ diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 855f755..5dcecfd 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -110,6 +110,24 @@ typedef enum { VIR_DOMAIN_NONE = 0 } virDomainCreateFlags; + + +/** + * virStream: + * + * a virStream is a private structure representing a data stream. + */ +typedef struct _virStream virStream; + +/** + * virStreamPtr: + * + * a virStreamPtr is pointer to a virStream private structure, this is the + * type used to reference a data stream in the API. + */ +typedef virStream *virStreamPtr; + + /** * VIR_SECURITY_LABEL_BUFLEN: * @@ -1448,6 +1466,81 @@ void virEventRegisterImpl(virEventAddHandleFunc addHandle, virEventAddTimeoutFunc addTimeout, virEventUpdateTimeoutFunc updateTimeout, virEventRemoveTimeoutFunc removeTimeout); + +enum { + VIR_STREAM_NONBLOCK = (1 << 0), +}; + +virStreamPtr virStreamNew(virConnectPtr conn, + unsigned int flags); +int virStreamRef(virStreamPtr st); + +int virStreamSend(virStreamPtr st, + const char *data, + size_t nbytes); + +int virStreamRecv(virStreamPtr st, + char *data, + size_t nbytes); + + +typedef int (*virStreamSourceFunc)(virStreamPtr st, + char *data, + size_t nbytes, + void *opaque); + +int virStreamSendAll(virStreamPtr st, + virStreamSourceFunc handler, + void *opaque); + +typedef int (*virStreamSinkFunc)(virStreamPtr st, + const char *data, + size_t nbytes, + void *opaque); + +int virStreamRecvAll(virStreamPtr st, + virStreamSinkFunc handler, + void *opaque); + +typedef enum { + VIR_STREAM_EVENT_READABLE = (1 << 0), + VIR_STREAM_EVENT_WRITABLE = (1 << 1), + VIR_STREAM_EVENT_ERROR = (1 << 2), + VIR_STREAM_EVENT_HANGUP = (1 << 3), +} virStreamEventType; + + +/** + * virStreamEventCallback: + * + * @stream: stream on which the event occurred + * @events: bitset of events from virEventHandleType constants + * @opaque: user data registered with handle + * + * Callback for receiving stream events. The callback will + * be invoked once for each event which is pending. + */ +typedef void (*virStreamEventCallback)(virStreamPtr stream, int events, void *opaque); + +int virStreamEventAddCallback(virStreamPtr stream, + int events, + virStreamEventCallback cb, + void *opaque, + virFreeCallback ff); + +int virStreamEventUpdateCallback(virStreamPtr stream, + int events); + +int virStreamEventRemoveCallback(virStreamPtr stream); + + +int virStreamFinish(virStreamPtr st); +int virStreamAbort(virStreamPtr st); + +int virStreamFree(virStreamPtr st); + + + #ifdef __cplusplus } #endif diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index e6536c7..db091dc 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -110,6 +110,24 @@ typedef enum { VIR_DOMAIN_NONE = 0 } virDomainCreateFlags; + + +/** + * virStream: + * + * a virStream is a private structure representing a data stream. + */ +typedef struct _virStream virStream; + +/** + * virStreamPtr: + * + * a virStreamPtr is pointer to a virStream private structure, this is the + * type used to reference a data stream in the API. + */ +typedef virStream *virStreamPtr; + + /** * VIR_SECURITY_LABEL_BUFLEN: * @@ -1448,6 +1466,81 @@ void virEventRegisterImpl(virEventAddHandleFunc addHandle, virEventAddTimeoutFunc addTimeout, virEventUpdateTimeoutFunc updateTimeout, virEventRemoveTimeoutFunc removeTimeout); + +enum { + VIR_STREAM_NONBLOCK = (1 << 0), +}; + +virStreamPtr virStreamNew(virConnectPtr conn, + unsigned int flags); +int virStreamRef(virStreamPtr st); + +int virStreamSend(virStreamPtr st, + const char *data, + size_t nbytes); + +int virStreamRecv(virStreamPtr st, + char *data, + size_t nbytes); + + +typedef int (*virStreamSourceFunc)(virStreamPtr st, + char *data, + size_t nbytes, + void *opaque); + +int virStreamSendAll(virStreamPtr st, + virStreamSourceFunc handler, + void *opaque); + +typedef int (*virStreamSinkFunc)(virStreamPtr st, + const char *data, + size_t nbytes, + void *opaque); + +int virStreamRecvAll(virStreamPtr st, + virStreamSinkFunc handler, + void *opaque); + +typedef enum { + VIR_STREAM_EVENT_READABLE = (1 << 0), + VIR_STREAM_EVENT_WRITABLE = (1 << 1), + VIR_STREAM_EVENT_ERROR = (1 << 2), + VIR_STREAM_EVENT_HANGUP = (1 << 3), +} virStreamEventType; + + +/** + * virStreamEventCallback: + * + * @stream: stream on which the event occurred + * @events: bitset of events from virEventHandleType constants + * @opaque: user data registered with handle + * + * Callback for receiving stream events. The callback will + * be invoked once for each event which is pending. + */ +typedef void (*virStreamEventCallback)(virStreamPtr stream, int events, void *opaque); + +int virStreamEventAddCallback(virStreamPtr stream, + int events, + virStreamEventCallback cb, + void *opaque, + virFreeCallback ff); + +int virStreamEventUpdateCallback(virStreamPtr stream, + int events); + +int virStreamEventRemoveCallback(virStreamPtr stream); + + +int virStreamFinish(virStreamPtr st); +int virStreamAbort(virStreamPtr st); + +int virStreamFree(virStreamPtr st); + + + #ifdef __cplusplus } #endif diff --git a/src/datatypes.c b/src/datatypes.c index d03a679..3611b62 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -1129,3 +1129,62 @@ virUnrefNodeDevice(virNodeDevicePtr dev) { virMutexUnlock(&dev->conn->lock); return (refs); } + + +virStreamPtr virGetStream(virConnectPtr conn) { + virStreamPtr ret = NULL; + + virMutexLock(&conn->lock); + + if (VIR_ALLOC(ret) < 0) { + virReportOOMError(conn); + goto error; + } + ret->magic = VIR_STREAM_MAGIC; + ret->conn = conn; + conn->refs++; + ret->refs++; + virMutexUnlock(&conn->lock); + return(ret); + +error: + virMutexUnlock(&conn->lock); + VIR_FREE(ret); + return(NULL); +} + +static void +virReleaseStream(virStreamPtr st) { + virConnectPtr conn = st->conn; + DEBUG("release dev %p", st); + + st->magic = -1; + VIR_FREE(st); + + DEBUG("unref connection %p %d", conn, conn->refs); + conn->refs--; + if (conn->refs == 0) { + virReleaseConnect(conn); + /* Already unlocked mutex */ + return; + } + + virMutexUnlock(&conn->lock); +} + +int virUnrefStream(virStreamPtr st) { + int refs; + + virMutexLock(&st->conn->lock); + DEBUG("unref stream %p %d", st, st->refs); + st->refs--; + refs = st->refs; + if (refs == 0) { + virReleaseStream(st); + /* Already unlocked mutex */ + return (0); + } + + virMutexUnlock(&st->conn->lock); + return (refs); +} diff --git a/src/datatypes.h b/src/datatypes.h index da83e02..0fed07f 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -100,6 +100,17 @@ /** + * VIR_STREAM_MAGIC: + * + * magic value used to protect the API when pointers to stream structures + * are passed down by the users. + */ +#define VIR_STREAM_MAGIC 0x1DEAD666 +#define VIR_IS_STREAM(obj) ((obj) && (obj)->magic==VIR_STREAM_MAGIC) +#define VIR_IS_CONNECTED_STREAM(obj) (VIR_IS_STREAM(obj) && VIR_IS_CONNECT((obj)->conn)) + + +/** * _virConnect: * * Internal structure associated to a connection @@ -234,6 +245,25 @@ struct _virNodeDevice { }; +typedef int (*virStreamAbortFunc)(virStreamPtr, void *opaque); +typedef int (*virStreamFinishFunc)(virStreamPtr, void *opaque); + +/** + * _virStream: + * + * Internal structure associated with an input stream + */ +struct _virStream { + unsigned int magic; + virConnectPtr conn; + int refs; + int flags; + + virStreamDriverPtr driver; + void *privateData; +}; + + /************************************************************************ * * * API for domain/connections (de)allocations and lookups * @@ -270,4 +300,7 @@ virNodeDevicePtr virGetNodeDevice(virConnectPtr conn, const char *name); int virUnrefNodeDevice(virNodeDevicePtr dev); +virStreamPtr virGetStream(virConnectPtr conn); +int virUnrefStream(virStreamPtr st); + #endif diff --git a/src/driver.h b/src/driver.h index 79d46ff..25d34b6 100644 --- a/src/driver.h +++ b/src/driver.h @@ -799,6 +799,40 @@ struct _virDeviceMonitor { virDrvNodeDeviceDestroy deviceDestroy; }; +typedef struct _virStreamDriver virStreamDriver; +typedef virStreamDriver *virStreamDriverPtr; + +typedef int (*virDrvStreamSend)(virStreamPtr st, + const char *data, + size_t nbytes); +typedef int (*virDrvStreamRecv)(virStreamPtr st, + char *data, + size_t nbytes); + +typedef int (*virDrvStreamEventAddCallback)(virStreamPtr stream, + int events, + virStreamEventCallback cb, + void *opaque, + virFreeCallback ff); + +typedef int (*virDrvStreamEventUpdateCallback)(virStreamPtr stream, + int events); +typedef int (*virDrvStreamEventRemoveCallback)(virStreamPtr stream); +typedef int (*virDrvStreamFinish)(virStreamPtr st); +typedef int (*virDrvStreamAbort)(virStreamPtr st); + + +struct _virStreamDriver { + virDrvStreamSend streamSend; + virDrvStreamRecv streamRecv; + virDrvStreamEventAddCallback streamAddCallback; + virDrvStreamEventUpdateCallback streamUpdateCallback; + virDrvStreamEventRemoveCallback streamRemoveCallback; + virDrvStreamFinish streamFinish; + virDrvStreamAbort streamAbort; +}; + + /* * Registration * TODO: also need ways to (des)activate a given driver diff --git a/src/libvirt.c b/src/libvirt.c index ca8e003..d6536f4 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -559,6 +559,10 @@ virLibNodeDeviceError(virNodeDevicePtr dev, virErrorNumber error, errmsg, info, NULL, 0, 0, errmsg, info); } +#define virLibStreamError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_NONE, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + /** * virRegisterNetworkDriver: * @driver: pointer to a network driver block @@ -8626,3 +8630,682 @@ error: virSetConnError(conn); return -1; } + + +/** + * virStreamNew: + * @conn: pointer to the connection + * @flags: control features of the stream + * + * Creates a new stream object which can be used to perform + * streamed I/O with other public API function. + * + * When no longer needed, a stream object must be released + * with virStreamFree. If a data stream has been used, + * then the application must call virStreamFinish or + * virStreamAbort before free'ing to, in order to notify + * the driver of termination. + * + * If a non-blocking data stream is required passed + * VIR_STREAM_NONBLOCK for flags, otherwise pass 0. + * + * Returns the new stream, or NULL upon error + */ +virStreamPtr +virStreamNew(virConnectPtr conn, + unsigned int flags) +{ + virStreamPtr st; + + DEBUG("conn=%p, flags=%u", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + + st = virGetStream(conn); + if (st) + st->flags = flags; + + return st; +} + + +/** + * virStreamRef: + * @stream: pointer to the stream + * + * Increment the reference count on the stream. For each + * additional call to this method, there shall be a corresponding + * call to virStreamFree to release the reference count, once + * the caller no longer needs the reference to this object. + * + * Returns 0 in case of success, -1 in case of failure + */ +int +virStreamRef(virStreamPtr stream) +{ + if ((!VIR_IS_CONNECTED_STREAM(stream))) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + virMutexLock(&stream->conn->lock); + DEBUG("stream=%p refs=%d", stream, stream->refs); + stream->refs++; + virMutexUnlock(&stream->conn->lock); + return 0; +} + + +/** + * virStreamSend: + * @stream: pointer to the stream object + * @data: buffer to write to stream + * @nbytes: size of @data buffer + * + * Write a series of bytes to the stream. This method may + * block the calling application for an arbitrary amount + * of time. Once an application has finished sending data + * it should call virStreamFinish to wait for succesful + * confirmation from the driver, or detect any error + * + * This method may not be used if a stream source has been + * registered + * + * Errors are not guaranteed to be reported synchronously + * with the call, but may instead be delayed until a + * subsequent call. + * + * A example using this with a hypothetical file upload + * API looks like + * + * virStreamPtr st = virStreamNew(conn, 0); + * int fd = open("demo.iso", O_RDONLY) + * + * virConnectUploadFile(conn, "demo.iso", st); + * + * while (1) { + * char buf[1024]; + * int got = read(fd, buf, 1024); + * if (got < 0) { + * virStreamAbort(st); + * break; + * } + * if (got == 0) { + * virStreamFinish(st); + * break; + * } + * int offset = 0; + * while (offset < got) { + * int sent = virStreamSend(st, buf+offset, got-offset) + * if (sent < 0) { + * virStreamAbort(st); + * goto done; + * } + * offset += sent; + * } + * } + * done: + * virStreamFree(st); + * close(fd); + * + * Returns the number of bytes written, which may be less + * than requested. + * + * Returns -1 upon error, at which time the stream will + * be marked as aborted, and the caller should now release + * the stream with virStreamFree. + * + * Returns -2 if the outgoing transmit buffers are full & + * the stream is marked as non-blocking. + */ +int virStreamSend(virStreamPtr stream, + const char *data, + size_t nbytes) +{ + DEBUG("stream=%p, data=%p, nbytes=%zi", stream, data, nbytes); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamSend) { + int ret; + ret = (stream->driver->streamSend)(stream, data, nbytes); + if (ret == -2) + return -2; + + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + +/** + * virStreamRecv: + * @stream: pointer to the stream object + * @data: buffer to write to stream + * @nbytes: size of @data buffer + * + * Write a series of bytes to the stream. This method may + * block the calling application for an arbitrary amount + * of time. + * + * Errors are not guaranteed to be reported synchronously + * with the call, but may instead be delayed until a + * subsequent call. + * + * A example using this with a hypothetical file download + * API looks like + * + * virStreamPtr st = virStreamNew(conn, 0); + * int fd = open("demo.iso", O_WRONLY, 0600) + * + * virConnectDownloadFile(conn, "demo.iso", st); + * + * while (1) { + * char buf[1024]; + * int got = virStreamRecv(st, buf, 1024); + * if (got < 0) + * break; + * if (got == 0) { + * virStreamFinish(st); + * break; + * } + * int offset = 0; + * while (offset < got) { + * int sent = write(fd, buf+offset, got-offset) + * if (sent < 0) { + * virStreamAbort(st); + * goto done; + * } + * offset += sent; + * } + * } + * done: + * virStreamFree(st); + * close(fd); + * + * + * Returns the number of bytes read, which may be less + * than requested. + * + * Returns 0 when the end of the stream is reached, at + * which time the caller should invoke virStreamFinish() + * to get confirmation of stream completion. + * + * Returns -1 upon error, at which time the stream will + * be marked as aborted, and the caller should now release + * the stream with virStreamFree. + * + * Returns -2 if there is no data pending to be read & the + * stream is marked as non-blocking. + */ +int virStreamRecv(virStreamPtr stream, + char *data, + size_t nbytes) +{ + DEBUG("stream=%p, data=%p, nbytes=%zi", stream, data, nbytes); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamRecv) { + int ret; + ret = (stream->driver->streamRecv)(stream, data, nbytes); + if (ret == -2) + return -2; + + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + + +/** + * virStreamSendAll: + * @stream: pointer to the stream object + * @handler: source callback for reading data from application + * @opaque: application defined data + * + * Send the entire data stream, reading the data from the + * requested data source. This is simply a convenient alternative + * to virStreamSend, for apps that do blocking-I/o. + * + * A example using this with a hypothetical file upload + * API looks like + * + * int mysource(virStreamPtr st, char *buf, int nbytes, void *opaque) { + * int *fd = opaque; + * + * return read(*fd, buf, nbytes); + * } + * + * virStreamPtr st = virStreamNew(conn, 0); + * int fd = open("demo.iso", O_RDONLY) + * + * virConnectUploadFile(conn, st); + * virStreamSendAll(st, mysource, &fd); + * virStreamFree(st); + * close(fd); + * + * Returns 0 if all the data was succesfully sent. The stream + * will be marked as finished on success, so the caller need + * only call virStreamFree(). + * + * Returns -1 upon any error, with the stream being marked as + * aborted, so the caller need only call virStreamFree() + */ +int virStreamSendAll(virStreamPtr stream, + virStreamSourceFunc handler, + void *opaque) +{ + char *bytes = NULL; + int want = 1024*64; + int ret = -1; + DEBUG("stream=%p, handler=%p, opaque=%p", stream, handler, opaque); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->flags & VIR_STREAM_NONBLOCK) { + virLibConnError(NULL, VIR_ERR_OPERATION_INVALID, + _("data sources cannot be used for non-blocking streams")); + goto cleanup; + } + + if (VIR_ALLOC_N(bytes, want) < 0) { + virReportOOMError(stream->conn); + goto cleanup; + } + + for (;;) { + int got, offset = 0; + got = (handler)(stream, bytes, want, opaque); + if (got < 0) { + virStreamAbort(stream); + goto cleanup; + } + if (got == 0) + break; + while (offset < got) { + int done; + done = virStreamSend(stream, bytes + offset, got - offset); + if (done < 0) + goto cleanup; + offset += done; + } + } + ret = 0; + +cleanup: + VIR_FREE(bytes); + + /* Copy to connection error object for back compatability */ + if (ret != 0) + virSetConnError(stream->conn); + + return ret; +} + + +/** + * virStreamRecvAll: + * @stream: pointer to the stream object + * @handler: sink callback for writing data to application + * @opaque: application defined data + * + * Receive the entire data stream, sending the data to the + * requested data sink. This is simply a convenient alternative + * to virStreamRecv, for apps that do blocking-I/o. + * + * A example using this with a hypothetical file download + * API looks like + * + * int mysink(virStreamPtr st, const char *buf, int nbytes, void *opaque) { + * int *fd = opaque; + * + * return write(*fd, buf, nbytes); + * } + * + * virStreamPtr st = virStreamNew(conn, 0); + * int fd = open("demo.iso", O_WRONLY) + * + * virConnectUploadFile(conn, st); + * virStreamRecvAll(st, mysink, &fd); + * virStreamFree(st); + * close(fd); + * + * Returns 0 if all the data was succesfully received. The stream + * will be marked as finished on success, so the caller need + * only call virStreamFree(). + * + * Returns -1 upon any error, with the stream being marked as + * aborted, so the caller need only call virStreamFree() + */ +int virStreamRecvAll(virStreamPtr stream, + virStreamSinkFunc handler, + void *opaque) +{ + char *bytes = NULL; + int want = 1024*64; + int ret = -1; + DEBUG("stream=%p, handler=%p, opaque=%p", stream, handler, opaque); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->flags & VIR_STREAM_NONBLOCK) { + virLibConnError(NULL, VIR_ERR_OPERATION_INVALID, + _("data sinks cannot be used for non-blocking streams")); + goto cleanup; + } + + + if (VIR_ALLOC_N(bytes, want) < 0) { + virReportOOMError(stream->conn); + goto cleanup; + } + + for (;;) { + int got, offset = 0; + got = virStreamRecv(stream, bytes, want); + if (got < 0) + goto cleanup; + if (got == 0) + break; + while (offset < got) { + int done; + done = (handler)(stream, bytes + offset, got - offset, opaque); + if (done < 0) { + virStreamAbort(stream); + goto cleanup; + } + offset += done; + } + } + ret = 0; + +cleanup: + VIR_FREE(bytes); + + /* Copy to connection error object for back compatability */ + if (ret != 0) + virSetConnError(stream->conn); + + return ret; +} + + +/** + * virStreamEventAddCallback + * @stream: pointer to the stream object + * @events: set of events to monitor + * @cb: callback to invoke when an event occurs + * @opaque: application defined data + * @ff: callback to free @opaque data + * + * Register a callback to be notified when a stream + * becomes writable, or readable. This is most commonly + * used in conjunction with non-blocking data streams + * to integrate into an event loop + * + * Return 0 on success, -1 upon error + */ +int virStreamEventAddCallback(virStreamPtr stream, + int events, + virStreamEventCallback cb, + void *opaque, + virFreeCallback ff) +{ + DEBUG("stream=%p, events=%d, cb=%p, opaque=%p, ff=%p", stream, events, cb, opaque, ff); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamAddCallback) { + int ret; + ret = (stream->driver->streamAddCallback)(stream, events, cb, opaque, ff); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + + +/** + * virStreamEventUpdateCallback + * @stream: pointer to the stream object + * @events: set of events to monitor + * + * Changes the set of events to monitor for a stream. This allows + * for event notification to be changed without having to + * unregister & register the callback completely. This method + * is guarenteed to succeed if a callback is already registered + * + * Returns 0 on success, -1 if no callback is registered + */ +int virStreamEventUpdateCallback(virStreamPtr stream, + int events) +{ + DEBUG("stream=%p, events=%d", stream, events); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamUpdateCallback) { + int ret; + ret = (stream->driver->streamUpdateCallback)(stream, events); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + + +/** + * virStreamEventRemoveCallback + * @stream: pointer to the stream object + * + * Remove a event callback from the stream + * + * Return 0 on success, -1 on error + */ +int virStreamEventRemoveCallback(virStreamPtr stream) +{ + DEBUG("stream=%p", stream); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamRemoveCallback) { + int ret; + ret = (stream->driver->streamRemoveCallback)(stream); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + + +/** + * virStreamFinish: + * @stream: pointer to the stream object + * + * Indicate that there is no further data is to be transmitted + * on the stream. For output streams this should be called once + * all data has been written. For input streams this should be + * called once virStreamRecv returns end-of-file. + * + * This method is a synchronization point for all asynchronous + * errors, so if this returns a success code the application can + * be sure that all data has been successfully processed. + * + * Returns 0 on success, -1 upon error + */ +int virStreamFinish(virStreamPtr stream) +{ + DEBUG("stream=%p", stream); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamFinish) { + int ret; + ret = (stream->driver->streamFinish)(stream); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + + +/** + * virStreamAbort: + * @stream: pointer to the stream object + * + * Request that the in progress data transfer be cancelled + * abnormally before the end of the stream has been reached. + * For output streams this can be used to inform the driver + * that the stream is being terminated early. For input + * streams this can be used to inform the driver that it + * should stop sending data. + * + * Returns 0 on success, -1 upon error + */ +int virStreamAbort(virStreamPtr stream) +{ + DEBUG("stream=%p", stream); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamAbort) { + int ret; + ret = (stream->driver->streamAbort)(stream); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + + +/** + * virStreamFree: + * @stream: pointer to the stream object + * + * Decrement the reference count on a stream, releasing + * the stream object if the reference count has hit zero. + * + * There must not be a active data transfer in progress + * when releasing the stream. If a stream needs to be + * disposed of prior to end of stream being reached, then + * the virStreamAbort function should be called first. + * + * Returns 0 upon success, or -1 on error + */ +int virStreamFree(virStreamPtr stream) +{ + DEBUG("stream=%p", stream); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + /* XXX Enforce shutdown before free'ing resources ? */ + + if (virUnrefStream(stream) < 0) + return (-1); + return (0); +} diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2bf4e15..12d552e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -56,6 +56,8 @@ virUnrefStorageVol; virGetNodeDevice; virUnrefDomain; virUnrefConnect; +virGetStream; +virUnrefStream; # domain_conf.h diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index c06f51e..f48b8c5 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -291,4 +291,19 @@ LIBVIRT_0.7.0 { virConnectListDefinedInterfaces; } LIBVIRT_0.6.4; +LIBVIRT_0.7.1 { + virStreamNew; + virStreamRef; + virStreamSend; + virStreamRecv; + virStreamSendAll; + virStreamRecvAll; + virStreamEventAddCallback; + virStreamEventUpdateCallback; + virStreamEventRemoveCallback; + virStreamFinish; + virStreamAbort; + virStreamFree; +} LIBVIRT_0.7.0; + # .... define new API here using predicted next version number .... -- 1.6.2.5

On Mon, Aug 24, 2009 at 09:51:04PM +0100, Daniel P. Berrange wrote:
* include/libvirt/libvirt.h, include/libvirt/libvirt.h.in: Public API contract for virStreamPtr object * src/libvirt_public.syms: Export data stream APIs * src/libvirt_private.syms: Export internal helper APIs * src/libvirt.c: Data stream API driver dispatch * src/datatypes.h, src/datatypes.c: Internal helpers for virStreamPtr object * src/driver.h: Define internal driver API for streams * .x-sc_avoid_write: Ignore src/libvirt.c because it trips up on comments including write() [...] --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -110,6 +110,24 @@ typedef enum { VIR_DOMAIN_NONE = 0 } virDomainCreateFlags;
+ + +/** + * virStream: + * + * a virStream is a private structure representing a data stream. + */ +typedef struct _virStream virStream; + +/** + * virStreamPtr: + * + * a virStreamPtr is pointer to a virStream private structure, this is the + * type used to reference a data stream in the API. + */ +typedef virStream *virStreamPtr; + + /** * VIR_SECURITY_LABEL_BUFLEN: * @@ -1448,6 +1466,81 @@ void virEventRegisterImpl(virEventAddHandleFunc addHandle, virEventAddTimeoutFunc addTimeout, virEventUpdateTimeoutFunc updateTimeout, virEventRemoveTimeoutFunc removeTimeout); + +enum { + VIR_STREAM_NONBLOCK = (1 << 0), +}; + +virStreamPtr virStreamNew(virConnectPtr conn, + unsigned int flags);
Would flags be sufficient if we were to encode some priorities to streams? If we end up limiting the number of active streams, giving higher priority or some reserved slots for quicker operations may be important, flags should be sufficient for this if the need arise so I don't see a problem but I raise the point.
+int virStreamRef(virStreamPtr st); + +int virStreamSend(virStreamPtr st, + const char *data, + size_t nbytes); + +int virStreamRecv(virStreamPtr st, + char *data, + size_t nbytes); + + +typedef int (*virStreamSourceFunc)(virStreamPtr st, + char *data, + size_t nbytes, + void *opaque);
I think the signature is fine but we need to document all the arguments and the return values.
+int virStreamSendAll(virStreamPtr st, + virStreamSourceFunc handler, + void *opaque);
Hum, I had to look at the comment to really understand. I'm not 100% sure, maybe we should allow for handler() to be called multiple time, not just once. For example I would allow virStreamSendAll() code to call handler multiple time, until the handler like a read() returns 0 (or -1 on error), in any case the signature should be documented fully.
+typedef int (*virStreamSinkFunc)(virStreamPtr st, + const char *data, + size_t nbytes, + void *opaque);
Same thing do we allow a sink function to be called repeatedly ? If we want to allow this in some ways we will need an extra argument to indicate the end of the stream. Even if we don't plan this yet, I would suggest to add a flags to allow for this possibility in the future. With a chunk size of 256K at the protocol level it may not be a good idea to keep the full data in memory, so I would allow for this interface to call the sink multiple times. And IMHO it's best to pass the indication of end of transfer directly at the sink level rather than wait for the virStreamFree() coming from the user.
+int virStreamRecvAll(virStreamPtr st, + virStreamSinkFunc handler, + void *opaque);
Okay
+typedef enum { + VIR_STREAM_EVENT_READABLE = (1 << 0), + VIR_STREAM_EVENT_WRITABLE = (1 << 1), + VIR_STREAM_EVENT_ERROR = (1 << 2), + VIR_STREAM_EVENT_HANGUP = (1 << 3), +} virStreamEventType; + + +/** + * virStreamEventCallback: + * + * @stream: stream on which the event occurred + * @events: bitset of events from virEventHandleType constants + * @opaque: user data registered with handle + * + * Callback for receiving stream events. The callback will + * be invoked once for each event which is pending. + */ +typedef void (*virStreamEventCallback)(virStreamPtr stream, int events, void *opaque); + +int virStreamEventAddCallback(virStreamPtr stream, + int events, + virStreamEventCallback cb, + void *opaque, + virFreeCallback ff); + +int virStreamEventUpdateCallback(virStreamPtr stream, + int events); + +int virStreamEventRemoveCallback(virStreamPtr stream); + + +int virStreamFinish(virStreamPtr st); +int virStreamAbort(virStreamPtr st);
For those 2 maybe add a flag, to allow for example background disconnection. Even if the stream wasn't created ASYNC, we may want sometime to abruptly end without waiting.
+int virStreamFree(virStreamPtr st);
With the exception of the extra flags suggestion for the sink callback and finish/abort, the aPI looks fine to me. [...]
+/** + * virStreamSendAll: + * @stream: pointer to the stream object + * @handler: source callback for reading data from application + * @opaque: application defined data + * + * Send the entire data stream, reading the data from the + * requested data source. This is simply a convenient alternative + * to virStreamSend, for apps that do blocking-I/o. + * + * A example using this with a hypothetical file upload + * API looks like + * + * int mysource(virStreamPtr st, char *buf, int nbytes, void *opaque) { + * int *fd = opaque; + * + * return read(*fd, buf, nbytes); + * } + * + * virStreamPtr st = virStreamNew(conn, 0); + * int fd = open("demo.iso", O_RDONLY) + * + * virConnectUploadFile(conn, st); + * virStreamSendAll(st, mysource, &fd); + * virStreamFree(st); + * close(fd);
Hum, the clasic example of blocking I/Os is if people use multithreaded apps. What is a second thread calls calls virStreamAbort() while virStreamSendAll() is in progress, e.g. the user clicked on an abort button from the UI ? Same question for virStreamRecvAll() ?
+ * Returns 0 if all the data was succesfully sent. The stream + * will be marked as finished on success, so the caller need + * only call virStreamFree(). + * + * Returns -1 upon any error, with the stream being marked as + * aborted, so the caller need only call virStreamFree() + */ +int virStreamSendAll(virStreamPtr stream, + virStreamSourceFunc handler, + void *opaque)
+ * virConnectUploadFile(conn, st); + * virStreamRecvAll(st, mysink, &fd); + * virStreamFree(st); + * close(fd);
Would the current API allow for the sink callback to close the fd() at the end of the transfer ? Right now, I don't think so because we don't know what is the last callback (assuming multiple ones). Except for those couple of uncertainties, the API looks fine to me ! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Fri, Sep 25, 2009 at 12:09:34PM +0200, Daniel Veillard wrote:
On Mon, Aug 24, 2009 at 09:51:04PM +0100, Daniel P. Berrange wrote:
@@ -1448,6 +1466,81 @@ void virEventRegisterImpl(virEventAddHandleFunc addHandle, virEventAddTimeoutFunc addTimeout, virEventUpdateTimeoutFunc updateTimeout, virEventRemoveTimeoutFunc removeTimeout); + +enum { + VIR_STREAM_NONBLOCK = (1 << 0), +}; + +virStreamPtr virStreamNew(virConnectPtr conn, + unsigned int flags);
Would flags be sufficient if we were to encode some priorities to streams? If we end up limiting the number of active streams, giving higher priority or some reserved slots for quicker operations may be important, flags should be sufficient for this if the need arise so I don't see a problem but I raise the point.
I guess it would be sufficient if you wanted todo LOW/MEDIUM/HIGH static priorties. If we wanted to get more advanced, we could always introduce an extra API, since there is always a point between virStreamNew() and then using it, eg in virConnectUploadFile(), when you can do more setup calls. We already allow event callbacks to be registered this way, so we could in future add a virStreamSetPriority(virStreamPtr st, ....options ...); if we needed more than just plain flags. On the subject of flags though, I've never been entirely sure about whether it would be worth mandating the use of a flag(s) for indicating I/O direction at time of stream creation. Currently this is implicit base on the API that the stream is later used with, eg, currently virStreamPtr st = virStreamNew(conn, 0); virConnectUploadFile(conn, st, filename); implicitly configures the stream for writing, but I constantly wonder whether we ought to make it explicit via a flag like virStreamPtr st = virStreamNew(conn, VIR_STREAM_WRITE); virConnectUploadFile(conn, st, filename); and require that the call of virStreamNew() provide either VIR_STREAM_WRITE, or VIR_STREAM_READ, or both. ANd then also have the methods using streams like virConnectUploadFile check that the flags match. If we wanted to mandate use of READ/WRITE flags for stream creation, we'd obviously need todo it from teh start, since we couldn't add that as a mandatory flag once the API is released & in use by apps.
+int virStreamRef(virStreamPtr st); + +int virStreamSend(virStreamPtr st, + const char *data, + size_t nbytes); + +int virStreamRecv(virStreamPtr st, + char *data, + size_t nbytes); + + +typedef int (*virStreamSourceFunc)(virStreamPtr st, + char *data, + size_t nbytes, + void *opaque);
I think the signature is fine but we need to document all the arguments and the return values.
+int virStreamSendAll(virStreamPtr st, + virStreamSourceFunc handler, + void *opaque);
Hum, I had to look at the comment to really understand. I'm not 100% sure, maybe we should allow for handler() to be called multiple time, not just once. For example I would allow virStreamSendAll() code to call handler multiple time, until the handler like a read() returns 0 (or -1 on error), in any case the signature should be documented fully.
This method is essentially just a convenient way to call the virStreamSend() in a loop, for apps that are happy todo blocking data I/O. As such the handler() will definitely be called multiple times to fetch the data to be sent. You can see how it was used later on in this patch, where virStreamSendAll is implemented. I expect most apps would use virStreamSend() though, to allow them todo interruptible & non-blocking I/O
+typedef int (*virStreamSinkFunc)(virStreamPtr st, + const char *data, + size_t nbytes, + void *opaque);
Same thing do we allow a sink function to be called repeatedly ? If we want to allow this in some ways we will need an extra argument to indicate the end of the stream. Even if we don't plan this yet, I would suggest to add a flags to allow for this possibility in the future. With a chunk size of 256K at the protocol level it may not be a good idea to keep the full data in memory, so I would allow for this interface to call the sink multiple times. And IMHO it's best to pass the indication of end of transfer directly at the sink level rather than wait for the virStreamFree() coming from the user.
+int virStreamRecvAll(virStreamPtr st, + virStreamSinkFunc handler, + void *opaque);
Okay
Same as for SendAll, this API will invoke the handler multilpe times to write out data that is being received. In both cases the implementation is invoking the handler with 64kb buffers to avoid pulling lots of data into memory.
+typedef enum { + VIR_STREAM_EVENT_READABLE = (1 << 0), + VIR_STREAM_EVENT_WRITABLE = (1 << 1), + VIR_STREAM_EVENT_ERROR = (1 << 2), + VIR_STREAM_EVENT_HANGUP = (1 << 3), +} virStreamEventType; + + +/** + * virStreamEventCallback: + * + * @stream: stream on which the event occurred + * @events: bitset of events from virEventHandleType constants + * @opaque: user data registered with handle + * + * Callback for receiving stream events. The callback will + * be invoked once for each event which is pending. + */ +typedef void (*virStreamEventCallback)(virStreamPtr stream, int events, void *opaque); + +int virStreamEventAddCallback(virStreamPtr stream, + int events, + virStreamEventCallback cb, + void *opaque, + virFreeCallback ff); + +int virStreamEventUpdateCallback(virStreamPtr stream, + int events); + +int virStreamEventRemoveCallback(virStreamPtr stream); + + +int virStreamFinish(virStreamPtr st); +int virStreamAbort(virStreamPtr st);
For those 2 maybe add a flag, to allow for example background disconnection. Even if the stream wasn't created ASYNC, we may want sometime to abruptly end without waiting.
The virStreamAbort() operation is always asynchronous, regardless of whether the stream is non-blocking. The virStreamFinish() operation has to be synchronous, because for it to be useful you need to do a round-trip to flush any error being sent back from the server. So I don't think we need any flags here. If you want to close it abruptly then virStreamAbort() is suitable, if you want to close it cleanly then virStreamFinish() is suitable.
+/** + * virStreamSendAll: + * @stream: pointer to the stream object + * @handler: source callback for reading data from application + * @opaque: application defined data + * + * Send the entire data stream, reading the data from the + * requested data source. This is simply a convenient alternative + * to virStreamSend, for apps that do blocking-I/o. + * + * A example using this with a hypothetical file upload + * API looks like + * + * int mysource(virStreamPtr st, char *buf, int nbytes, void *opaque) { + * int *fd = opaque; + * + * return read(*fd, buf, nbytes); + * } + * + * virStreamPtr st = virStreamNew(conn, 0); + * int fd = open("demo.iso", O_RDONLY) + * + * virConnectUploadFile(conn, st); + * virStreamSendAll(st, mysource, &fd); + * virStreamFree(st); + * close(fd);
Hum, the clasic example of blocking I/Os is if people use multithreaded apps. What is a second thread calls calls virStreamAbort() while virStreamSendAll() is in progress, e.g. the user clicked on an abort button from the UI ? Same question for virStreamRecvAll() ?
If you want to abort a stream part way through, then you should not be using virStreamSendAll/RecvAll. These APIs are optional convenience APIs for apps which are happy todo a blocking operation. Apps which need ability to abort part way through can use the virStreamRecv() and virStreamSend() APIs instead. That all said, it is safe for another thread to call virStreamAbort(), it will cause this Send/RecvAll method to return an error on the next virStreamSend/Recv call it makes
+ * Returns 0 if all the data was succesfully sent. The stream + * will be marked as finished on success, so the caller need + * only call virStreamFree(). + * + * Returns -1 upon any error, with the stream being marked as + * aborted, so the caller need only call virStreamFree() + */ +int virStreamSendAll(virStreamPtr stream, + virStreamSourceFunc handler, + void *opaque)
+ * virConnectUploadFile(conn, st); + * virStreamRecvAll(st, mysink, &fd); + * virStreamFree(st); + * close(fd);
Would the current API allow for the sink callback to close the fd() at the end of the transfer ? Right now, I don't think so because we don't know what is the last callback (assuming multiple ones).
The sink/source callbacks do not need to close the FD since this is easily done with RecvAll/SendAll returns control to the application. THis is in fact important, because it is not until RecvAll/SendAll returns that you can call virStreamFinish to check for success. If it did not suceed then you may want do other cleanup before closing the FD, such as unlinking the file Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Fri, Sep 25, 2009 at 12:25:50PM +0100, Daniel P. Berrange wrote:
On Fri, Sep 25, 2009 at 12:09:34PM +0200, Daniel Veillard wrote:
On Mon, Aug 24, 2009 at 09:51:04PM +0100, Daniel P. Berrange wrote:
@@ -1448,6 +1466,81 @@ void virEventRegisterImpl(virEventAddHandleFunc addHandle, virEventAddTimeoutFunc addTimeout, virEventUpdateTimeoutFunc updateTimeout, virEventRemoveTimeoutFunc removeTimeout); + +enum { + VIR_STREAM_NONBLOCK = (1 << 0), +}; + +virStreamPtr virStreamNew(virConnectPtr conn, + unsigned int flags);
Would flags be sufficient if we were to encode some priorities to streams? If we end up limiting the number of active streams, giving higher priority or some reserved slots for quicker operations may be important, flags should be sufficient for this if the need arise so I don't see a problem but I raise the point.
I guess it would be sufficient if you wanted todo LOW/MEDIUM/HIGH static priorties.
yeah, I just wanted to make sure it didn't contradict something you may have in mind :-)
If we wanted to get more advanced, we could always introduce an extra API, since there is always a point between virStreamNew() and then using it, eg in virConnectUploadFile(), when you can do more setup calls. We already allow event callbacks to be registered this way, so we could in future add a
virStreamSetPriority(virStreamPtr st, ....options ...);
right,
if we needed more than just plain flags.
On the subject of flags though, I've never been entirely sure about whether it would be worth mandating the use of a flag(s) for indicating I/O direction at time of stream creation. Currently this is implicit base on the API that the stream is later used with,
eg, currently
virStreamPtr st = virStreamNew(conn, 0);
virConnectUploadFile(conn, st, filename);
implicitly configures the stream for writing, but I constantly wonder whether we ought to make it explicit via a flag like
virStreamPtr st = virStreamNew(conn, VIR_STREAM_WRITE);
virConnectUploadFile(conn, st, filename);
and require that the call of virStreamNew() provide either VIR_STREAM_WRITE, or VIR_STREAM_READ, or both. ANd then also have the methods using streams like virConnectUploadFile check that the flags match.
Hum, this would then raise the signal that stream can be used both ways, do we really want to suggest this at the API level, I can see how we're gonna use this internally, but aren't we opening the door to much complexity ?
If we wanted to mandate use of READ/WRITE flags for stream creation, we'd obviously need todo it from teh start, since we couldn't add that as a mandatory flag once the API is released & in use by apps.
yes that's a good point, a design issue too. If you really expect API usage for both read and write, then I would say we should make those flags mandatory. The only point is that our existing flags use in APIs are just fine with 0 as being the 'default', and we would break this but it's not a big deal IMHO, that will be caught immediately.
+int virStreamRef(virStreamPtr st); + +int virStreamSend(virStreamPtr st, + const char *data, + size_t nbytes); + +int virStreamRecv(virStreamPtr st, + char *data, + size_t nbytes); + + +typedef int (*virStreamSourceFunc)(virStreamPtr st, + char *data, + size_t nbytes, + void *opaque);
I think the signature is fine but we need to document all the arguments and the return values.
+int virStreamSendAll(virStreamPtr st, + virStreamSourceFunc handler, + void *opaque);
Hum, I had to look at the comment to really understand. I'm not 100% sure, maybe we should allow for handler() to be called multiple time, not just once. For example I would allow virStreamSendAll() code to call handler multiple time, until the handler like a read() returns 0 (or -1 on error), in any case the signature should be documented fully.
This method is essentially just a convenient way to call the virStreamSend() in a loop, for apps that are happy todo blocking data I/O. As such the handler() will definitely be called multiple times to fetch the data to be sent. You can see how it was used later on in this patch, where virStreamSendAll is implemented.
I expect most apps would use virStreamSend() though, to allow them todo interruptible & non-blocking I/O
Okay, but let's describe all args and return values for the callbacks.
+typedef int (*virStreamSinkFunc)(virStreamPtr st, + const char *data, + size_t nbytes, + void *opaque);
Same thing do we allow a sink function to be called repeatedly ? If we want to allow this in some ways we will need an extra argument to indicate the end of the stream. Even if we don't plan this yet, I would suggest to add a flags to allow for this possibility in the future. With a chunk size of 256K at the protocol level it may not be a good idea to keep the full data in memory, so I would allow for this interface to call the sink multiple times. And IMHO it's best to pass the indication of end of transfer directly at the sink level rather than wait for the virStreamFree() coming from the user.
+int virStreamRecvAll(virStreamPtr st, + virStreamSinkFunc handler, + void *opaque);
Okay
Same as for SendAll, this API will invoke the handler multilpe times to write out data that is being received. In both cases the implementation is invoking the handler with 64kb buffers to avoid pulling lots of data into memory.
Okay, but I think being able to indicate there that a packet is the last one may be important, for example if the application design prefer to initiate the closure of the transfer (close/sync/...) as soon as possible. Also how flexible are we in the design with callbacks taling a long time to complete, for example reads crossing the network, or slow output devices ? Maybe this should be hinted in the callback descriptions. [...]
+int virStreamFinish(virStreamPtr st); +int virStreamAbort(virStreamPtr st);
For those 2 maybe add a flag, to allow for example background disconnection. Even if the stream wasn't created ASYNC, we may want sometime to abruptly end without waiting.
The virStreamAbort() operation is always asynchronous, regardless of whether the stream is non-blocking. The virStreamFinish() operation has to be synchronous, because for it to be useful you need to do a round-trip to flush any error being sent back from the server. So I don't think we need any flags here. If you want to close it abruptly then virStreamAbort() is suitable, if you want to close it cleanly then virStreamFinish() is suitable.
hum, okay ... [...]
+ * virConnectUploadFile(conn, st); + * virStreamSendAll(st, mysource, &fd); + * virStreamFree(st); + * close(fd);
Hum, the clasic example of blocking I/Os is if people use multithreaded apps. What is a second thread calls calls virStreamAbort() while virStreamSendAll() is in progress, e.g. the user clicked on an abort button from the UI ? Same question for virStreamRecvAll() ?
If you want to abort a stream part way through, then you should not be using virStreamSendAll/RecvAll. These APIs are optional convenience APIs for apps which are happy todo a blocking operation. Apps which need ability to abort part way through can use the virStreamRecv() and virStreamSend() APIs instead.
Callback programming is hard, people get very easilly confused by them and handling of errors can turn really nasty if you use them a lot. So I think it's important to have foolproof synchronous transfers, even if you think they may be a bit abused, it's really easier on the programmer.
That all said, it is safe for another thread to call virStreamAbort(), it will cause this Send/RecvAll method to return an error on the next virStreamSend/Recv call it makes
good :-)
+ * Returns 0 if all the data was succesfully sent. The stream + * will be marked as finished on success, so the caller need + * only call virStreamFree(). + * + * Returns -1 upon any error, with the stream being marked as + * aborted, so the caller need only call virStreamFree() + */ +int virStreamSendAll(virStreamPtr stream, + virStreamSourceFunc handler, + void *opaque)
+ * virConnectUploadFile(conn, st); + * virStreamRecvAll(st, mysink, &fd); + * virStreamFree(st); + * close(fd);
Would the current API allow for the sink callback to close the fd() at the end of the transfer ? Right now, I don't think so because we don't know what is the last callback (assuming multiple ones).
The sink/source callbacks do not need to close the FD since this is easily done with RecvAll/SendAll returns control to the application. THis is in fact important, because it is not until RecvAll/SendAll returns that you can call virStreamFinish to check for success. If it did not suceed then you may want do other cleanup before closing the FD, such as unlinking the file
Hum, the two example for RecvAll and SendAll don't suggest virStreamFinish() to be called to get the status, I would expect error reporting to show up as the result code from RecvAll and SendAll. I still think the callbacks behaviour should be clarified especially behaviour and reporting in case of error. thanks, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Fri, Sep 25, 2009 at 02:32:39PM +0200, Daniel Veillard wrote:
On Fri, Sep 25, 2009 at 12:25:50PM +0100, Daniel P. Berrange wrote:
if we needed more than just plain flags.
On the subject of flags though, I've never been entirely sure about whether it would be worth mandating the use of a flag(s) for indicating I/O direction at time of stream creation. Currently this is implicit base on the API that the stream is later used with,
eg, currently
virStreamPtr st = virStreamNew(conn, 0);
virConnectUploadFile(conn, st, filename);
implicitly configures the stream for writing, but I constantly wonder whether we ought to make it explicit via a flag like
virStreamPtr st = virStreamNew(conn, VIR_STREAM_WRITE);
virConnectUploadFile(conn, st, filename);
and require that the call of virStreamNew() provide either VIR_STREAM_WRITE, or VIR_STREAM_READ, or both. ANd then also have the methods using streams like virConnectUploadFile check that the flags match.
Hum, this would then raise the signal that stream can be used both ways, do we really want to suggest this at the API level, I can see how we're gonna use this internally, but aren't we opening the door to much complexity ?
Yeah, that's more or less why I left it out so far - I've not yet found a case where I absolutely needed the WRITE/READ flags to be set explicitly by apps.
If we wanted to mandate use of READ/WRITE flags for stream creation, we'd obviously need todo it from teh start, since we couldn't add that as a mandatory flag once the API is released & in use by apps.
yes that's a good point, a design issue too. If you really expect API usage for both read and write, then I would say we should make those flags mandatory. The only point is that our existing flags use in APIs are just fine with 0 as being the 'default', and we would break this but it's not a big deal IMHO, that will be caught immediately.
There is one likely API where we'd have a full read+write stream. I've thought about adding ability to tunnel a serial port PTY over libvirt, so 'virsh console' could be made to work remotely. eg, something like virDomainOpenConsole(virDomainPtr dom, virStreamPtr stream const char *consolename); In this case you'd be reading & writing from /to the same stream. It still wouldn't really require that we set the READ+WRITE flags when doing virStreamNew()
+typedef int (*virStreamSinkFunc)(virStreamPtr st, + const char *data, + size_t nbytes, + void *opaque);
Same thing do we allow a sink function to be called repeatedly ? If we want to allow this in some ways we will need an extra argument to indicate the end of the stream. Even if we don't plan this yet, I would suggest to add a flags to allow for this possibility in the future. With a chunk size of 256K at the protocol level it may not be a good idea to keep the full data in memory, so I would allow for this interface to call the sink multiple times. And IMHO it's best to pass the indication of end of transfer directly at the sink level rather than wait for the virStreamFree() coming from the user.
+int virStreamRecvAll(virStreamPtr st, + virStreamSinkFunc handler, + void *opaque);
Okay
Same as for SendAll, this API will invoke the handler multilpe times to write out data that is being received. In both cases the implementation is invoking the handler with 64kb buffers to avoid pulling lots of data into memory.
Okay, but I think being able to indicate there that a packet is the last one may be important, for example if the application design prefer to initiate the closure of the transfer (close/sync/...) as soon as possible.
Actually in the case of the 'source' function, the app already knows when its got to the end, because its source funtion will be returning '0' for end-of-file. For the 'sink' function we'd have to make sure we called it once at the end with a length of '0' to indicate EOF in that direction. I can't remember offhand if we'll do that already or not.
Also how flexible are we in the design with callbacks taling a long time to complete, for example reads crossing the network, or slow output devices ? Maybe this should be hinted in the callback descriptions.
These callbacks are the app's responsibility & execute in its context, so libvirt doesn't care whether they are slow or fast to execute. Everything internal to libvirt relating to streams is non-blocking & fast.
+ * virConnectUploadFile(conn, st); + * virStreamSendAll(st, mysource, &fd); + * virStreamFree(st); + * close(fd);
+ * virConnectUploadFile(conn, st); + * virStreamRecvAll(st, mysink, &fd); + * virStreamFree(st); + * close(fd);
Would the current API allow for the sink callback to close the fd() at the end of the transfer ? Right now, I don't think so because we don't know what is the last callback (assuming multiple ones).
The sink/source callbacks do not need to close the FD since this is easily done with RecvAll/SendAll returns control to the application. THis is in fact important, because it is not until RecvAll/SendAll returns that you can call virStreamFinish to check for success. If it did not suceed then you may want do other cleanup before closing the FD, such as unlinking the file
Hum, the two example for RecvAll and SendAll don't suggest virStreamFinish() to be called to get the status, I would expect error reporting to show up as the result code from RecvAll and SendAll.
Actually these 2 code examples are wrong. There should be a call to virStreamFinish in there, before the virStreamFree. This was not required in an earlier version of my patch, because SendAll would call Finish for you, but I realized this made it impossible for callers to detect certain error conditions. So the app should always call either Finish or Abort once they're done with I/O. I'll update the example Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Fri, Sep 25, 2009 at 02:09:54PM +0100, Daniel P. Berrange wrote:
On Fri, Sep 25, 2009 at 02:32:39PM +0200, Daniel Veillard wrote:
Hum, this would then raise the signal that stream can be used both ways, do we really want to suggest this at the API level, I can see how we're gonna use this internally, but aren't we opening the door to much complexity ?
Yeah, that's more or less why I left it out so far - I've not yet found a case where I absolutely needed the WRITE/READ flags to be set explicitly by apps.
Okay, let's keep it that way. Ultimately we could add another API to create the Stream.
If we wanted to mandate use of READ/WRITE flags for stream creation, we'd obviously need todo it from teh start, since we couldn't add that as a mandatory flag once the API is released & in use by apps.
yes that's a good point, a design issue too. If you really expect API usage for both read and write, then I would say we should make those flags mandatory. The only point is that our existing flags use in APIs are just fine with 0 as being the 'default', and we would break this but it's not a big deal IMHO, that will be caught immediately.
There is one likely API where we'd have a full read+write stream. I've thought about adding ability to tunnel a serial port PTY over libvirt, so 'virsh console' could be made to work remotely. eg, something like
virDomainOpenConsole(virDomainPtr dom, virStreamPtr stream const char *consolename);
In this case you'd be reading & writing from /to the same stream. It still wouldn't really require that we set the READ+WRITE flags when doing virStreamNew()
okay
+typedef int (*virStreamSinkFunc)(virStreamPtr st, + const char *data, + size_t nbytes, + void *opaque);
Same thing do we allow a sink function to be called repeatedly ? If we want to allow this in some ways we will need an extra argument to indicate the end of the stream. Even if we don't plan this yet, I would suggest to add a flags to allow for this possibility in the future. With a chunk size of 256K at the protocol level it may not be a good idea to keep the full data in memory, so I would allow for this interface to call the sink multiple times. And IMHO it's best to pass the indication of end of transfer directly at the sink level rather than wait for the virStreamFree() coming from the user.
+int virStreamRecvAll(virStreamPtr st, + virStreamSinkFunc handler, + void *opaque);
Okay
Same as for SendAll, this API will invoke the handler multilpe times to write out data that is being received. In both cases the implementation is invoking the handler with 64kb buffers to avoid pulling lots of data into memory.
Okay, but I think being able to indicate there that a packet is the last one may be important, for example if the application design prefer to initiate the closure of the transfer (close/sync/...) as soon as possible.
Actually in the case of the 'source' function, the app already knows when its got to the end, because its source funtion will be returning '0' for end-of-file.
yes for read it's not a problem
For the 'sink' function we'd have to make sure we called it once at the end with a length of '0' to indicate EOF in that direction. I can't remember offhand if we'll do that already or not.
ah write 0 lenght, yes that's another way to pass the signal.
Also how flexible are we in the design with callbacks taling a long time to complete, for example reads crossing the network, or slow output devices ? Maybe this should be hinted in the callback descriptions.
These callbacks are the app's responsibility & execute in its context, so libvirt doesn't care whether they are slow or fast to execute. Everything internal to libvirt relating to streams is non-blocking & fast.
okidoc, I just wanted to doub;e-check :-)
The sink/source callbacks do not need to close the FD since this is easily done with RecvAll/SendAll returns control to the application. THis is in fact important, because it is not until RecvAll/SendAll returns that you can call virStreamFinish to check for success. If it did not suceed then you may want do other cleanup before closing the FD, such as unlinking the file
Hum, the two example for RecvAll and SendAll don't suggest virStreamFinish() to be called to get the status, I would expect error reporting to show up as the result code from RecvAll and SendAll.
Actually these 2 code examples are wrong. There should be a call to virStreamFinish in there, before the virStreamFree. This was not required in an earlier version of my patch, because SendAll would call Finish for you, but I realized this made it impossible for callers to detect certain error conditions. So the app should always call either Finish or Abort once they're done with I/O. I'll update the example
Ah, okay ! ACK Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

If a file descriptor with events=0 was added to the libvirtd event loop, it would still be added to the poll() fds' array. While it wouldn't see any POLLIN/OUT events, it'd still get triggered for HANGUP/ERROR events which was not in compliance with the libvirt events API contract. * qemud/event.c: Don't poll on FDs with events=0 * tests/eventtest.c: Add test case to validate fix to event.c --- qemud/event.c | 54 +++++++++++++++++++++++++++++++++++++--------------- tests/eventtest.c | 19 ++++++++++++++++++ 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/qemud/event.c b/qemud/event.c index a57d967..10847c4 100644 --- a/qemud/event.c +++ b/qemud/event.c @@ -109,7 +109,7 @@ int virEventAddHandleImpl(int fd, int events, void *opaque, virFreeCallback ff) { int watch; - EVENT_DEBUG("Add handle %d %d %p %p", fd, events, cb, opaque); + EVENT_DEBUG("Add handle fd=%d events=%d cb=%p opaque=%p", fd, events, cb, opaque); virEventLock(); if (eventLoop.handlesCount == eventLoop.handlesAlloc) { EVENT_DEBUG("Used %d handle slots, adding %d more", @@ -170,7 +170,7 @@ void virEventUpdateHandleImpl(int watch, int events) { */ int virEventRemoveHandleImpl(int watch) { int i; - EVENT_DEBUG("Remove handle %d", watch); + EVENT_DEBUG("Remove handle w=%d", watch); if (watch <= 0) { VIR_WARN("Ignoring invalid remove watch %d", watch); @@ -350,22 +350,32 @@ static int virEventCalculateTimeout(int *timeout) { * file handles. The caller must free the returned data struct * returns: the pollfd array, or NULL on error */ -static struct pollfd *virEventMakePollFDs(void) { +static struct pollfd *virEventMakePollFDs(int *nfds) { struct pollfd *fds; int i; + *nfds = 0; + for (i = 0 ; i < eventLoop.handlesCount ; i++) { + if (eventLoop.handles[i].events) + (*nfds)++; + } + /* Setup the poll file handle data structs */ - if (VIR_ALLOC_N(fds, eventLoop.handlesCount) < 0) + if (VIR_ALLOC_N(fds, *nfds) < 0) return NULL; + *nfds = 0; for (i = 0 ; i < eventLoop.handlesCount ; i++) { EVENT_DEBUG("Prepare n=%d w=%d, f=%d e=%d", i, eventLoop.handles[i].watch, eventLoop.handles[i].fd, eventLoop.handles[i].events); - fds[i].fd = eventLoop.handles[i].fd; - fds[i].events = eventLoop.handles[i].events; - fds[i].revents = 0; + if (!eventLoop.handles[i].events) + continue; + fds[*nfds].fd = eventLoop.handles[i].fd; + fds[*nfds].events = eventLoop.handles[i].events; + fds[*nfds].revents = 0; + (*nfds)++; //EVENT_DEBUG("Wait for %d %d", eventLoop.handles[i].fd, eventLoop.handles[i].events); } @@ -391,6 +401,7 @@ static int virEventDispatchTimeouts(void) { int i; /* Save this now - it may be changed during dispatch */ int ntimeouts = eventLoop.timeoutsCount; + DEBUG("Dispatch %d", ntimeouts); if (gettimeofday(&tv, NULL) < 0) { return -1; @@ -429,28 +440,38 @@ static int virEventDispatchTimeouts(void) { * Returns 0 upon success, -1 if an error occurred */ static int virEventDispatchHandles(int nfds, struct pollfd *fds) { - int i; + int i, n; + DEBUG("Dispatch %d", nfds); /* NB, use nfds not eventLoop.handlesCount, because new * fds might be added on end of list, and they're not * in the fds array we've got */ - for (i = 0 ; i < nfds ; i++) { + for (i = 0, n = 0 ; n < nfds && i < eventLoop.handlesCount ; n++) { + while ((eventLoop.handles[i].fd != fds[n].fd || + eventLoop.handles[i].events == 0) && + i < eventLoop.handlesCount) { + i++; + } + if (i == eventLoop.handlesCount) + break; + + DEBUG("i=%d w=%d", i, eventLoop.handles[i].watch); if (eventLoop.handles[i].deleted) { EVENT_DEBUG("Skip deleted n=%d w=%d f=%d", i, eventLoop.handles[i].watch, eventLoop.handles[i].fd); continue; } - if (fds[i].revents) { + if (fds[n].revents) { virEventHandleCallback cb = eventLoop.handles[i].cb; void *opaque = eventLoop.handles[i].opaque; - int hEvents = virPollEventToEventHandleType(fds[i].revents); + int hEvents = virPollEventToEventHandleType(fds[n].revents); EVENT_DEBUG("Dispatch n=%d f=%d w=%d e=%d %p", i, - fds[i].fd, eventLoop.handles[i].watch, - fds[i].revents, eventLoop.handles[i].opaque); + fds[n].fd, eventLoop.handles[i].watch, + fds[n].revents, eventLoop.handles[i].opaque); virEventUnlock(); (cb)(eventLoop.handles[i].watch, - fds[i].fd, hEvents, opaque); + fds[n].fd, hEvents, opaque); virEventLock(); } } @@ -465,6 +486,7 @@ static int virEventDispatchHandles(int nfds, struct pollfd *fds) { */ static int virEventCleanupTimeouts(void) { int i; + DEBUG("Cleanup %d", eventLoop.timeoutsCount); /* Remove deleted entries, shuffling down remaining * entries as needed to form contiguous series @@ -505,6 +527,7 @@ static int virEventCleanupTimeouts(void) { */ static int virEventCleanupHandles(void) { int i; + DEBUG("Cleanupo %d", eventLoop.handlesCount); /* Remove deleted entries, shuffling down remaining * entries as needed to form contiguous series @@ -554,10 +577,9 @@ int virEventRunOnce(void) { virEventCleanupHandles() < 0) goto error; - if (!(fds = virEventMakePollFDs()) || + if (!(fds = virEventMakePollFDs(&nfds)) || virEventCalculateTimeout(&timeout) < 0) goto error; - nfds = eventLoop.handlesCount; virEventUnlock(); diff --git a/tests/eventtest.c b/tests/eventtest.c index d25381d..68ac2fc 100644 --- a/tests/eventtest.c +++ b/tests/eventtest.c @@ -430,6 +430,25 @@ mymain(int argc, char **argv) for (i = 0 ; i < NUM_TIME ; i++) virEventRemoveTimeoutImpl(timers[i].timer); + resetAll(); + + /* Final test, register same FD twice, once with no + * events, and make sure the right callback runs */ + handles[0].pipeFD[0] = handles[1].pipeFD[0]; + handles[0].pipeFD[1] = handles[1].pipeFD[1]; + + handles[0].watch = virEventAddHandleImpl(handles[0].pipeFD[0], + 0, + testPipeReader, + &handles[0], NULL); + handles[1].watch = virEventAddHandleImpl(handles[1].pipeFD[0], + VIR_EVENT_HANDLE_READABLE, + testPipeReader, + &handles[1], NULL); + startJob("Write duplicate", &test); + ret = safewrite(handles[1].pipeFD[1], &one, 1); + if (finishJob(1, -1) != EXIT_SUCCESS) + return EXIT_FAILURE; //pthread_kill(eventThread, SIGTERM); -- 1.6.2.5

On Mon, Aug 24, 2009 at 09:51:05PM +0100, Daniel P. Berrange wrote:
If a file descriptor with events=0 was added to the libvirtd event loop, it would still be added to the poll() fds' array. While it wouldn't see any POLLIN/OUT events, it'd still get triggered for HANGUP/ERROR events which was not in compliance with the libvirt events API contract.
Independantly of the rest of the stream patches this sounds fine to push, ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Daniel Veillard wrote:
On Mon, Aug 24, 2009 at 09:51:05PM +0100, Daniel P. Berrange wrote:
If a file descriptor with events=0 was added to the libvirtd event loop, it would still be added to the poll() fds' array. While it wouldn't see any POLLIN/OUT events, it'd still get triggered for HANGUP/ERROR events which was not in compliance with the libvirt events API contract.
Independantly of the rest of the stream patches this sounds fine to push,
Hm, this patch seems to be causing a regression. If I start up a KVM guest, let it completely boot, then run "shutdown -h now" inside the guest, the guest properly shuts down and qemu-kvm exits. However, it is never removed from the list of "active" guests (i.e. it shows up in "virsh list"), and the state is set to "no state". Reverting just this patch fixes the issue. I'll look at it some more to try to figure out what is happening, but we might want to revert it for the time being (since we don't technically need it at the moment). -- Chris Lalancette

Chris Lalancette wrote:
Daniel Veillard wrote:
If a file descriptor with events=0 was added to the libvirtd event loop, it would still be added to the poll() fds' array. While it wouldn't see any POLLIN/OUT events, it'd still get triggered for HANGUP/ERROR events which was not in compliance with the libvirt events API contract. Independantly of the rest of the stream patches this sounds fine to
On Mon, Aug 24, 2009 at 09:51:05PM +0100, Daniel P. Berrange wrote: push,
Hm, this patch seems to be causing a regression. If I start up a KVM guest, let it completely boot, then run "shutdown -h now" inside the guest, the guest properly shuts down and qemu-kvm exits. However, it is never removed from the list of "active" guests (i.e. it shows up in "virsh list"), and the state is set to "no state". Reverting just this patch fixes the issue. I'll look at it some more to try to figure out what is happening, but we might want to revert it for the time being (since we don't technically need it at the moment).
OK, danpb pointed out the problem. Don't revert this patch, I'll post a follow up to fix the problem in a couple of minutes. -- Chris Lalancette

Defines the extensions to the remote protocol for generic data streams. Adds a bunch of helper code to the libvirtd daemon for working with data streams. * qemud/Makefile.am: Add stream.c/stream.h to build * qemud/stream.c, qemud/stream.h: Generic helper functions for creating new streams, associating streams with clients, finding existing streams for a client and removing/deleting streams. * qemud/remote_protocol.x: Add a new 'REMOTE_STREAM' constant for the 'enum remote_message_type' for encoding stream data in wire messages. Add a new 'REMOTE_CONTINUE' constant to 'enum remote_message_status' to indicate further data stream messsages are expected to follow. Document how the remote_message_header is used to encode data streams * qemud/remote_protocol.h: Regenerate * qemud/dispatch.c: Remove assumption that a error message sent to client is always type=REMOTE_REPLY. It may now also be type=REMOTE_STREAM. Add convenient method for sending outgoing stream data packets. Log and ignore non-filtered incoming stream packets. Add a method for serializing a stream error message * qemud/dispatch.h: Add API for serializing stream errors and sending stream data packets * qemud/qemud.h: Add struct qemud_client_stream for tracking active data streams for clients. Tweak filter function operation so that it accepts a client object too. * qemud/qemud.c: Refactor code for free'ing message objects which have been fully transmitted into separate method. Release all active streams when client shuts down. Change filter function to be responsible for queueing the message --- qemud/Makefile.am | 1 + qemud/dispatch.c | 127 ++++++++++++++++++++++++++++- qemud/dispatch.h | 17 ++++ qemud/qemud.c | 54 ++++++++---- qemud/qemud.h | 32 ++++++- qemud/remote_protocol.h | 4 + qemud/remote_protocol.x | 44 +++++++++- qemud/stream.c | 210 +++++++++++++++++++++++++++++++++++++++++++++++ qemud/stream.h | 49 +++++++++++ 9 files changed, 509 insertions(+), 29 deletions(-) create mode 100644 qemud/stream.c create mode 100644 qemud/stream.h diff --git a/qemud/Makefile.am b/qemud/Makefile.am index 959ff88..5f1376e 100644 --- a/qemud/Makefile.am +++ b/qemud/Makefile.am @@ -5,6 +5,7 @@ DAEMON_SOURCES = \ qemud.c qemud.h \ remote.c remote.h \ dispatch.c dispatch.h \ + stream.c stream.h \ remote_dispatch_prototypes.h \ remote_dispatch_table.h \ remote_dispatch_args.h \ diff --git a/qemud/dispatch.c b/qemud/dispatch.c index a60f2f4..1934d24 100644 --- a/qemud/dispatch.c +++ b/qemud/dispatch.c @@ -104,7 +104,7 @@ void remoteDispatchOOMError (remote_error *rerr) { remoteDispatchStringError(rerr, VIR_ERR_NO_MEMORY, - NULL); + "out of memory"); } @@ -136,6 +136,10 @@ remoteSerializeError(struct qemud_client *client, unsigned int len; struct qemud_client_message *msg = NULL; + DEBUG("prog=%d ver=%d proc=%d type=%d serial=%d, msg=%s", + program, version, procedure, type, serial, + rerr->message ? *rerr->message : "(none)"); + if (VIR_ALLOC(msg) < 0) goto fatal_error; @@ -206,19 +210,38 @@ fatal_error: * * Returns 0 if the error was sent, -1 upon fatal error */ -static int +int remoteSerializeReplyError(struct qemud_client *client, remote_error *rerr, remote_message_header *req) { + /* + * For data streams, errors are sent back as data streams + * For method calls, errors are sent back as method replies + */ return remoteSerializeError(client, rerr, req->prog, req->vers, req->proc, - REMOTE_REPLY, + req->type == REMOTE_STREAM ? REMOTE_STREAM : REMOTE_REPLY, req->serial); } +int +remoteSerializeStreamError(struct qemud_client *client, + remote_error *rerr, + int proc, + int serial) +{ + return remoteSerializeError(client, + rerr, + REMOTE_PROGRAM, + REMOTE_PROTOCOL_VERSION, + proc, + REMOTE_STREAM, + serial); +} + /* * @msg: the complete incoming message, whose header to decode * @@ -338,6 +361,10 @@ remoteDispatchClientRequest (struct qemud_server *server, { remote_error rerr; + DEBUG("prog=%d ver=%d type=%d satus=%d serial=%d proc=%d", + msg->hdr.prog, msg->hdr.vers, msg->hdr.type, + msg->hdr.status, msg->hdr.serial, msg->hdr.proc); + memset(&rerr, 0, sizeof rerr); /* Check version, etc. */ @@ -358,11 +385,24 @@ remoteDispatchClientRequest (struct qemud_server *server, case REMOTE_CALL: return remoteDispatchClientCall(server, client, msg); + case REMOTE_STREAM: + /* Since stream data is non-acked, async, we may continue to received + * stream packets after we closed down a stream. Just drop & ignore + * these. + */ + VIR_INFO("Ignoring unexpected stream data serial=%d proc=%d status=%d", + msg->hdr.serial, msg->hdr.proc, msg->hdr.status); + qemudClientMessageRelease(client, msg); + break; + default: remoteDispatchFormatError (&rerr, _("type (%d) != REMOTE_CALL"), (int) msg->hdr.type); + goto error; } + return 0; + error: return remoteSerializeReplyError(client, &rerr, &msg->hdr); } @@ -532,3 +572,84 @@ xdr_error: fatal_error: return -1; } + + +int +remoteSendStreamData(struct qemud_client *client, + struct qemud_client_stream *stream, + const char *data, + size_t len) +{ + struct qemud_client_message *msg; + XDR xdr; + + DEBUG("client=%p stream=%p data=%p len=%d", client, stream, data, len); + + if (VIR_ALLOC(msg) < 0) { + return -1; + } + + /* Return header. We're re-using same message object, so + * only need to tweak type/status fields */ + msg->hdr.prog = REMOTE_PROGRAM; + msg->hdr.vers = REMOTE_PROTOCOL_VERSION; + msg->hdr.proc = stream->procedure; + msg->hdr.type = REMOTE_STREAM; + msg->hdr.serial = stream->serial; + /* + * NB + * data != NULL + len > 0 => REMOTE_CONTINUE (Sending back data) + * data != NULL + len == 0 => REMOTE_CONTINUE (Sending read EOF) + * data == NULL => REMOTE_OK (Sending finish handshake confirmation) + */ + msg->hdr.status = data ? REMOTE_CONTINUE : REMOTE_OK; + + if (remoteEncodeClientMessageHeader(msg) < 0) + goto fatal_error; + + if (data && len) { + if ((msg->bufferLength - msg->bufferOffset) < len) + goto fatal_error; + + /* Now for the payload */ + xdrmem_create (&xdr, + msg->buffer, + msg->bufferLength, + XDR_ENCODE); + + /* Skip over existing header already written */ + if (xdr_setpos(&xdr, msg->bufferOffset) == 0) + goto xdr_error; + + memcpy(msg->buffer + msg->bufferOffset, data, len); + msg->bufferOffset += len; + + /* Update the length word. */ + len = msg->bufferOffset; + if (xdr_setpos (&xdr, 0) == 0) + goto xdr_error; + + if (!xdr_u_int (&xdr, &len)) + goto xdr_error; + + xdr_destroy (&xdr); + + DEBUG("Total %d", msg->bufferOffset); + } + + /* Reset ready for I/O */ + msg->bufferLength = msg->bufferOffset; + msg->bufferOffset = 0; + + /* Put reply on end of tx queue to send out */ + qemudClientMessageQueuePush(&client->tx, msg); + qemudUpdateClientEvent(client); + + return 0; + +xdr_error: + xdr_destroy (&xdr); +fatal_error: + VIR_FREE(msg); + return -1; +} diff --git a/qemud/dispatch.h b/qemud/dispatch.h index 1d85df9..03a699c 100644 --- a/qemud/dispatch.h +++ b/qemud/dispatch.h @@ -49,6 +49,17 @@ void remoteDispatchOOMError (remote_error *rerr); void remoteDispatchConnError (remote_error *rerr, virConnectPtr conn); + +int +remoteSerializeReplyError(struct qemud_client *client, + remote_error *rerr, + remote_message_header *req); +int +remoteSerializeStreamError(struct qemud_client *client, + remote_error *rerr, + int proc, + int serial); + /* Having this here is dubious. It should be in remote.h * but qemud.c shouldn't depend on that header directly. * Refactor this later to deal with this properly. @@ -60,4 +71,10 @@ int remoteRelayDomainEvent (virConnectPtr conn ATTRIBUTE_UNUSED, void *opaque); +int +remoteSendStreamData(struct qemud_client *client, + struct qemud_client_stream *stream, + const char *data, + size_t len); + #endif /* __LIBVIRTD_DISPATCH_H__ */ diff --git a/qemud/qemud.c b/qemud/qemud.c index e657cf2..6c81dec 100644 --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -61,6 +61,7 @@ #include "conf.h" #include "event.h" #include "memory.h" +#include "stream.h" #ifdef HAVE_AVAHI #include "mdns.h" #endif @@ -1718,10 +1719,15 @@ readmore: /* Check if any filters match this message */ filter = client->filters; while (filter) { - if ((filter->query)(msg, filter->opaque)) { - qemudClientMessageQueuePush(&filter->dx, msg); + int ret; + ret = (filter->query)(client, msg, filter->opaque); + if (ret == 1) { msg = NULL; break; + } else if (ret == -1) { + VIR_FREE(msg); + qemudDispatchClientFailure(client); + return; } filter = filter->next; } @@ -1883,6 +1889,29 @@ static ssize_t qemudClientWrite(struct qemud_client *client) { } +void +qemudClientMessageRelease(struct qemud_client *client, + struct qemud_client_message *msg) +{ + if (!msg->async) + client->nrequests--; + + /* See if the recv queue is currently throttled */ + if (!client->rx && + client->nrequests < max_client_requests) { + /* Reset message record for next RX attempt */ + memset(msg, 0, sizeof(*msg)); + client->rx = msg; + /* Get ready to receive next message */ + client->rx->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; + } else { + VIR_FREE(msg); + } + + qemudUpdateClientEvent(client); +} + + /* * Process all queued client->tx messages until * we would block on I/O @@ -1906,26 +1935,10 @@ qemudDispatchClientWrite(struct qemud_client *client) { /* Get finished reply from head of tx queue */ reply = qemudClientMessageQueueServe(&client->tx); - /* If its not an async message, then we have - * just completed an RPC request */ - if (!reply->async) - client->nrequests--; - - /* Move record to end of 'rx' ist */ - if (!client->rx && - client->nrequests < max_client_requests) { - /* Reset message record for next RX attempt */ - client->rx = reply; - client->rx->bufferOffset = 0; - client->rx->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; - } else { - VIR_FREE(reply); - } + qemudClientMessageRelease(client, reply); if (client->closing) qemudDispatchClientFailure(client); - else - qemudUpdateClientEvent(client); } } } @@ -2137,6 +2150,9 @@ static void qemudFreeClient(struct qemud_client *client) { VIR_FREE(msg); } + while (client->streams) + remoteRemoveClientStream(client, client->streams); + if (client->conn) virConnectClose(client->conn); virMutexDestroy(&client->lock); diff --git a/qemud/qemud.h b/qemud/qemud.h index 254db44..8ef5871 100644 --- a/qemud/qemud.h +++ b/qemud/qemud.h @@ -129,26 +129,43 @@ struct qemud_client_message { unsigned int bufferLength; unsigned int bufferOffset; - int async : 1; + unsigned int async : 1; remote_message_header hdr; struct qemud_client_message *next; }; +struct qemud_client; + /* Allow for filtering of incoming messages to a custom * dispatch processing queue, instead of client->dx. */ -typedef int (*qemud_client_filter_func)(struct qemud_client_message *msg, void *opaque); +typedef int (*qemud_client_filter_func)(struct qemud_client *client, + struct qemud_client_message *msg, void *opaque); struct qemud_client_filter { qemud_client_filter_func query; void *opaque; - struct qemud_client_message *dx; - struct qemud_client_filter *next; }; +struct qemud_client_stream { + virStreamPtr st; + int procedure; + int serial; + + unsigned int recvEOF : 1; + unsigned int closed : 1; + + struct qemud_client_filter filter; + + struct qemud_client_message *rx; + int tx; + + struct qemud_client_stream *next; +}; + /* Stores the per-client connection state */ struct qemud_client { virMutex lock; @@ -197,6 +214,10 @@ struct qemud_client { * end up on the 'dx' queue */ struct qemud_client_filter *filters; + /* Data streams */ + struct qemud_client_stream *streams; + + /* This is only valid if a remote open call has been made on this * connection, otherwise it will be NULL. Also if remote close is * called, it will be set back to NULL if that succeeds. @@ -275,6 +296,9 @@ qemudClientMessageQueuePush(struct qemud_client_message **queue, struct qemud_client_message * qemudClientMessageQueueServe(struct qemud_client_message **queue); +void +qemudClientMessageRelease(struct qemud_client *client, + struct qemud_client_message *msg); #if HAVE_POLKIT diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h index 2e5bc81..2ff9075 100644 --- a/qemud/remote_protocol.h +++ b/qemud/remote_protocol.h @@ -16,6 +16,8 @@ extern "C" { #include "internal.h" #include <arpa/inet.h> #define REMOTE_MESSAGE_MAX 262144 +#define REMOTE_MESSAGE_HEADER_MAX 24 +#define REMOTE_MESSAGE_PAYLOAD_MAX 262120 #define REMOTE_STRING_MAX 65536 typedef char *remote_nonnull_string; @@ -1576,12 +1578,14 @@ enum remote_message_type { REMOTE_CALL = 0, REMOTE_REPLY = 1, REMOTE_MESSAGE = 2, + REMOTE_STREAM = 3, }; typedef enum remote_message_type remote_message_type; enum remote_message_status { REMOTE_OK = 0, REMOTE_ERROR = 1, + REMOTE_CONTINUE = 2, }; typedef enum remote_message_status remote_message_status; #define REMOTE_MESSAGE_HEADER_XDR_LEN 4 diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x index 8f9b6db..e24c428 100644 --- a/qemud/remote_protocol.x +++ b/qemud/remote_protocol.x @@ -44,6 +44,12 @@ /* Maximum total message size (serialised). */ const REMOTE_MESSAGE_MAX = 262144; +/* Size of struct remote_message_header (serialized)*/ +const REMOTE_MESSAGE_HEADER_MAX = 24; + +/* Size of message payload */ +const REMOTE_MESSAGE_PAYLOAD_MAX = 262120; + /* Length of long, but not unbounded, strings. * This is an arbitrary limit designed to stop the decoder from trying * to allocate unbounded amounts of memory when fed with a bad message. @@ -1448,8 +1454,27 @@ enum remote_procedure { * * serial matches that from the corresponding REMOTE_CALL * * - type == REMOTE_MESSAGE - * * serial matches that from the corresponding REMOTE_CALL, or zero + * * serial is always zero + * + * - type == REMOTE_STREAM + * * serial matches that from the corresponding REMOTE_CALL * + * and the 'status' field varies according to: + * + * - type == REMOTE_CALL + * * REMOTE_OK always + * + * - type == REMOTE_REPLY + * * REMOTE_OK if RPC finished successfully + * * REMOTE_ERROR if something failed + * + * - type == REMOTE_MESSAGE + * * REMOTE_OK always + * + * - type == REMOTE_STREAM + * * REMOTE_CONTINUE if more data is following + * * REMOTE_OK if stream is complete + * * REMOTE_ERROR if stream had an error * * Payload varies according to type and status: * @@ -1468,6 +1493,13 @@ enum remote_procedure { * * status == REMOTE_ERROR * remote_error Error information * + * - type == REMOTE_STREAM + * * status == REMOTE_CONTINUE + * byte[] raw stream data + * * status == REMOTE_ERROR + * remote_error error information + * * status == REMOTE_OK + * <empty> */ enum remote_message_type { /* client -> server. args from a method call */ @@ -1475,7 +1507,9 @@ enum remote_message_type { /* server -> client. reply/error from a method call */ REMOTE_REPLY = 1, /* either direction. async notification */ - REMOTE_MESSAGE = 2 + REMOTE_MESSAGE = 2, + /* either direction. stream data packet */ + REMOTE_STREAM = 3 }; enum remote_message_status { @@ -1487,7 +1521,11 @@ enum remote_message_status { /* For replies, indicates that an error happened, and a struct * remote_error follows. */ - REMOTE_ERROR = 1 + REMOTE_ERROR = 1, + + /* For streams, indicates that more data is still expected + */ + REMOTE_CONTINUE = 2 }; /* 4 byte length word per header */ diff --git a/qemud/stream.c b/qemud/stream.c new file mode 100644 index 0000000..1644a1b --- /dev/null +++ b/qemud/stream.c @@ -0,0 +1,210 @@ +/* + * stream.c: APIs for managing client streams + * + * Copyright (C) 2009 Red Hat, Inc. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + + +#include <config.h> + +#include "stream.h" +#include "memory.h" +#include "dispatch.h" +#include "logging.h" + + +/* + * @client: a locked client object + * + * Invoked by the main loop when filtering incoming messages. + * + * Returns 1 if the message was processed, 0 if skipped, + * -1 on fatal client error + */ +static int +remoteStreamFilter(struct qemud_client *client ATTRIBUTE_UNUSED, + struct qemud_client_message *msg ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) +{ + return 0; +} + + +/* + * @conn: a connection object to associate the stream with + * @hdr: the method call to associate with the stram + * + * Creates a new stream for this conn + * + * Returns a new stream object, or NULL upon OOM + */ +struct qemud_client_stream * +remoteCreateClientStream(virConnectPtr conn, + remote_message_header *hdr) +{ + struct qemud_client_stream *stream; + + DEBUG("proc=%d serial=%d", hdr->proc, hdr->serial); + + if (VIR_ALLOC(stream) < 0) + return NULL; + + stream->procedure = hdr->proc; + stream->serial = hdr->serial; + + stream->st = virStreamNew(conn, VIR_STREAM_NONBLOCK); + if (!stream->st) { + VIR_FREE(stream); + return NULL; + } + + stream->filter.query = remoteStreamFilter; + stream->filter.opaque = stream; + + return stream; +} + +/* + * @stream: an unused client stream + * + * Frees the memory associated with this inactive client + * stream + */ +void remoteFreeClientStream(struct qemud_client *client, + struct qemud_client_stream *stream) +{ + struct qemud_client_message *msg; + + if (!stream) + return; + + DEBUG("proc=%d serial=%d", stream->procedure, stream->serial); + + msg = stream->rx; + while (msg) { + struct qemud_client_message *tmp = msg->next; + qemudClientMessageRelease(client, msg); + msg = tmp; + } + + virStreamFree(stream->st); + VIR_FREE(stream); +} + + +/* + * @client: a locked client to add the stream to + * @stream: a stream to add + */ +int remoteAddClientStream(struct qemud_client *client, + struct qemud_client_stream *stream) +{ + struct qemud_client_stream *tmp = client->streams; + + DEBUG("client=%p proc=%d serial=%d", client, stream->procedure, stream->serial); + + if (tmp) { + while (tmp->next) + tmp = tmp->next; + tmp->next = stream; + } else { + client->streams = stream; + } + + stream->filter.next = client->filters; + client->filters = &stream->filter; + + stream->tx = 1; + + return 0; +} + + +/* + * @client: a locked client object + * @procedure: procedure associated with the stream + * @serial: serial number associated with the stream + * + * Finds a existing active stream + * + * Returns a stream object matching the procedure+serial number, or NULL + */ +struct qemud_client_stream * +remoteFindClientStream(struct qemud_client *client, + virStreamPtr st) +{ + struct qemud_client_stream *stream = client->streams; + + while (stream) { + if (stream->st == st) + return stream; + stream = stream->next; + } + + return NULL; +} + + +/* + * @client: a locked client object + * @stream: an inactive, closed stream object + * + * Removes a stream from the list of active streams for the client + * + * Returns 0 if the stream was removd, -1 if it doesn't exist + */ +int +remoteRemoveClientStream(struct qemud_client *client, + struct qemud_client_stream *stream) +{ + DEBUG("client=%p proc=%d serial=%d", client, stream->procedure, stream->serial); + + struct qemud_client_stream *curr = client->streams; + struct qemud_client_stream *prev = NULL; + struct qemud_client_filter *filter = NULL; + + if (client->filters == &stream->filter) { + client->filters = client->filters->next; + } else { + filter = client->filters; + while (filter) { + if (filter->next == &stream->filter) { + filter->next = filter->next->next; + break; + } + } + } + + if (!stream->closed) + virStreamAbort(stream->st); + + while (curr) { + if (curr == stream) { + if (prev) + prev->next = curr->next; + else + client->streams = curr->next; + remoteFreeClientStream(client, stream); + return 0; + } + prev = curr; + curr = curr->next; + } + return -1; +} diff --git a/qemud/stream.h b/qemud/stream.h new file mode 100644 index 0000000..fe5ce6f --- /dev/null +++ b/qemud/stream.h @@ -0,0 +1,49 @@ +/* + * stream.h: APIs for managing client streams + * + * Copyright (C) 2009 Red Hat, Inc. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + + +#ifndef __LIBVIRTD_STREAM_H__ +#define __LIBVIRTD_STREAM_H__ + +#include "qemud.h" + + + +struct qemud_client_stream * +remoteCreateClientStream(virConnectPtr conn, + remote_message_header *hdr); + +void remoteFreeClientStream(struct qemud_client *client, + struct qemud_client_stream *stream); + +int remoteAddClientStream(struct qemud_client *client, + struct qemud_client_stream *stream); + +struct qemud_client_stream * +remoteFindClientStream(struct qemud_client *client, + virStreamPtr stream); + +int +remoteRemoveClientStream(struct qemud_client *client, + struct qemud_client_stream *stream); + +#endif /* __LIBVIRTD_STREAM_H__ */ -- 1.6.2.5

On Mon, Aug 24, 2009 at 09:51:06PM +0100, Daniel P. Berrange wrote:
Defines the extensions to the remote protocol for generic data streams. Adds a bunch of helper code to the libvirtd daemon for working with data streams.
* qemud/Makefile.am: Add stream.c/stream.h to build * qemud/stream.c, qemud/stream.h: Generic helper functions for creating new streams, associating streams with clients, finding existing streams for a client and removing/deleting streams. * qemud/remote_protocol.x: Add a new 'REMOTE_STREAM' constant for the 'enum remote_message_type' for encoding stream data in wire messages. Add a new 'REMOTE_CONTINUE' constant to 'enum remote_message_status' to indicate further data stream messsages are expected to follow. Document how the remote_message_header is used to encode data streams * qemud/remote_protocol.h: Regenerate * qemud/dispatch.c: Remove assumption that a error message sent to client is always type=REMOTE_REPLY. It may now also be type=REMOTE_STREAM. Add convenient method for sending outgoing stream data packets. Log and ignore non-filtered incoming stream packets. Add a method for serializing a stream error message * qemud/dispatch.h: Add API for serializing stream errors and sending stream data packets * qemud/qemud.h: Add struct qemud_client_stream for tracking active data streams for clients. Tweak filter function operation so that it accepts a client object too. * qemud/qemud.c: Refactor code for free'ing message objects which have been fully transmitted into separate method. Release all active streams when client shuts down. Change filter function to be responsible for queueing the message
ACK, but this is hard to review, only testing can really bring confidence for this kind of code :-) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

* qemud/stream.c: Handle incoming stream data packets, queuing until stream becomes writable. Handle stream completion handshake * po/POTFILES.in: Add qemud/stream.c --- po/POTFILES.in | 1 + qemud/stream.c | 305 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 303 insertions(+), 3 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index 66d3ebd..d144689 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,6 +1,7 @@ qemud/dispatch.c qemud/qemud.c qemud/remote.c +qemud/stream.c src/bridge.c src/conf.c src/console.c diff --git a/qemud/stream.c b/qemud/stream.c index 1644a1b..1fe0e58 100644 --- a/qemud/stream.c +++ b/qemud/stream.c @@ -28,6 +28,93 @@ #include "dispatch.h" #include "logging.h" +static int +remoteStreamHandleWrite(struct qemud_client *client, + struct qemud_client_stream *stream); +static int +remoteStreamHandleFinish(struct qemud_client *client, + struct qemud_client_stream *stream, + struct qemud_client_message *msg); +static int +remoteStreamHandleAbort(struct qemud_client *client, + struct qemud_client_stream *stream, + struct qemud_client_message *msg); + + + +static void +remoteStreamUpdateEvents(struct qemud_client_stream *stream) +{ + int newEvents = 0; + if (stream->rx) + newEvents |= VIR_STREAM_EVENT_WRITABLE; + + virStreamEventUpdateCallback(stream->st, newEvents); +} + + +/* + * Callback that gets invoked when a stream becomes writable/readable + */ +static void +remoteStreamEvent(virStreamPtr st, int events, void *opaque) +{ + struct qemud_client *client = opaque; + struct qemud_client_stream *stream; + + /* XXX sub-optimal - we really should be taking the server lock + * first, but we have no handle to the server object + * We're lucky to get away with it for now, due to this callback + * executing in the main thread, but this should really be fixed + */ + virMutexLock(&client->lock); + + stream = remoteFindClientStream(client, st); + + if (!stream) { + VIR_WARN("event for client=%p stream st=%p, but missing stream state", client, st); + virStreamEventRemoveCallback(st); + goto cleanup; + } + + DEBUG("st=%p events=%d", st, events); + + if (events & VIR_STREAM_EVENT_WRITABLE) { + if (remoteStreamHandleWrite(client, stream) < 0) { + remoteRemoveClientStream(client, stream); + qemudDispatchClientFailure(client); + goto cleanup; + } + } + + if (!stream->closed && + (events & (VIR_STREAM_EVENT_ERROR | VIR_STREAM_EVENT_HANGUP))) { + int ret; + remote_error rerr; + memset(&rerr, 0, sizeof rerr); + stream->closed = 1; + virStreamAbort(stream->st); + if (events & VIR_STREAM_EVENT_HANGUP) + remoteDispatchFormatError(&rerr, "%s", _("stream had unexpected termination")); + else + remoteDispatchFormatError(&rerr, "%s", _("stream had I/O failure")); + ret = remoteSerializeStreamError(client, &rerr, stream->procedure, stream->serial); + remoteRemoveClientStream(client, stream); + if (ret < 0) + qemudDispatchClientFailure(client); + goto cleanup; + } + + if (stream->closed) { + remoteRemoveClientStream(client, stream); + } else { + remoteStreamUpdateEvents(stream); + } + +cleanup: + virMutexUnlock(&client->lock); +} + /* * @client: a locked client object @@ -38,10 +125,54 @@ * -1 on fatal client error */ static int -remoteStreamFilter(struct qemud_client *client ATTRIBUTE_UNUSED, - struct qemud_client_message *msg ATTRIBUTE_UNUSED, - void *opaque ATTRIBUTE_UNUSED) +remoteStreamFilter(struct qemud_client *client, + struct qemud_client_message *msg, void *opaque) { + struct qemud_client_stream *stream = opaque; + + if (msg->hdr.serial == stream->serial && + msg->hdr.proc == stream->procedure && + msg->hdr.type == REMOTE_STREAM) { + DEBUG("Incoming rx=%p serial=%d proc=%d status=%d", + stream->rx, msg->hdr.proc, msg->hdr.serial, msg->hdr.status); + + /* If there are queued packets, we need to queue all further + * messages, since they must be processed strictly in order. + * If there are no queued packets, then OK/ERROR messages + * should be processed immediately. Data packets are still + * queued to only be processed when the stream is marked as + * writable. + */ + if (stream->rx) { + qemudClientMessageQueuePush(&stream->rx, msg); + remoteStreamUpdateEvents(stream); + } else { + int ret = 0; + switch (msg->hdr.status) { + case REMOTE_OK: + ret = remoteStreamHandleFinish(client, stream, msg); + if (ret == 0) + qemudClientMessageRelease(client, msg); + break; + + case REMOTE_CONTINUE: + qemudClientMessageQueuePush(&stream->rx, msg); + remoteStreamUpdateEvents(stream); + break; + + case REMOTE_ERROR: + default: + ret = remoteStreamHandleAbort(client, stream, msg); + if (ret == 0) + qemudClientMessageRelease(client, msg); + break; + } + + if (ret < 0) + return -1; + } + return 1; + } return 0; } @@ -119,6 +250,10 @@ int remoteAddClientStream(struct qemud_client *client, DEBUG("client=%p proc=%d serial=%d", client, stream->procedure, stream->serial); + if (virStreamEventAddCallback(stream->st, 0, + remoteStreamEvent, client, NULL) < 0) + return -1; + if (tmp) { while (tmp->next) tmp = tmp->next; @@ -132,6 +267,8 @@ int remoteAddClientStream(struct qemud_client *client, stream->tx = 1; + remoteStreamUpdateEvents(stream); + return 0; } @@ -208,3 +345,165 @@ remoteRemoveClientStream(struct qemud_client *client, } return -1; } + + +/* + * Returns: + * -1 if fatal error occurred + * 0 if message was fully processed + * 1 if message is still being processed + */ +static int +remoteStreamHandleWriteData(struct qemud_client *client, + struct qemud_client_stream *stream, + struct qemud_client_message *msg) +{ + remote_error rerr; + int ret; + + DEBUG("stream=%p proc=%d serial=%d len=%d offset=%d", + stream, msg->hdr.proc, msg->hdr.serial, msg->bufferLength, msg->bufferOffset); + + memset(&rerr, 0, sizeof rerr); + + ret = virStreamSend(stream->st, + msg->buffer + msg->bufferOffset, + msg->bufferLength - msg->bufferOffset); + + if (ret > 0) { + msg->bufferOffset += ret; + + /* Partial write, so indicate we have more todo later */ + if (msg->bufferOffset < msg->bufferLength) + return 1; + } else if (ret == -2) { + /* Blocking, so indicate we have more todo later */ + return 1; + } else { + VIR_INFO0("Stream send failed"); + stream->closed = 1; + remoteDispatchConnError(&rerr, client->conn); + return remoteSerializeReplyError(client, &rerr, &msg->hdr); + } + + return 0; +} + + +/* + * Process an finish handshake from the client. + * + * Returns a REMOTE_OK confirmation if successful, or a REMOTE_ERROR + * if there was a stream error + * + * Returns 0 if successfully sent RPC reply, -1 upon fatal error + */ +static int +remoteStreamHandleFinish(struct qemud_client *client, + struct qemud_client_stream *stream, + struct qemud_client_message *msg) +{ + remote_error rerr; + int ret; + + DEBUG("stream=%p proc=%d serial=%d", + stream, msg->hdr.proc, msg->hdr.serial); + + memset(&rerr, 0, sizeof rerr); + + stream->closed = 1; + ret = virStreamFinish(stream->st); + + if (ret < 0) { + remoteDispatchConnError(&rerr, client->conn); + return remoteSerializeReplyError(client, &rerr, &msg->hdr); + } else { + /* Send zero-length confirm */ + if (remoteSendStreamData(client, stream, NULL, 0) < 0) + return -1; + } + + return 0; +} + + +/* + * Process an abort request from the client. + * + * Returns 0 if successfully aborted, -1 upon error + */ +static int +remoteStreamHandleAbort(struct qemud_client *client, + struct qemud_client_stream *stream, + struct qemud_client_message *msg) +{ + remote_error rerr; + + DEBUG("stream=%p proc=%d serial=%d", + stream, msg->hdr.proc, msg->hdr.serial); + + memset(&rerr, 0, sizeof rerr); + + stream->closed = 1; + virStreamAbort(stream->st); + + if (msg->hdr.status == REMOTE_ERROR) + remoteDispatchFormatError(&rerr, "%s", _("stream aborted at client request")); + else { + VIR_WARN("unexpected stream status %d", msg->hdr.status); + remoteDispatchFormatError(&rerr, _("stream aborted with unexpected status %d"), + msg->hdr.status); + } + + return remoteSerializeReplyError(client, &rerr, &msg->hdr); +} + + + +/* + * Called when the stream is signalled has being able to accept + * data writes. Will process all pending incoming messages + * until they're all gone, or I/O blocks + * + * Returns 0 on success, or -1 upon fatal error + */ +static int +remoteStreamHandleWrite(struct qemud_client *client, + struct qemud_client_stream *stream) +{ + struct qemud_client_message *msg, *tmp; + + DEBUG("stream=%p", stream); + + msg = stream->rx; + while (msg && !stream->closed) { + int ret; + switch (msg->hdr.status) { + case REMOTE_OK: + ret = remoteStreamHandleFinish(client, stream, msg); + break; + + case REMOTE_CONTINUE: + ret = remoteStreamHandleWriteData(client, stream, msg); + break; + + case REMOTE_ERROR: + default: + ret = remoteStreamHandleAbort(client, stream, msg); + break; + } + + if (ret == 0) + qemudClientMessageQueueServe(&stream->rx); + else if (ret < 0) + return -1; + else + break; /* still processing data */ + + tmp = msg->next; + qemudClientMessageRelease(client, msg); + msg = tmp; + } + + return 0; +} -- 1.6.2.5

On Mon, Aug 24, 2009 at 09:51:07PM +0100, Daniel P. Berrange wrote:
* qemud/stream.c: Handle incoming stream data packets, queuing until stream becomes writable. Handle stream completion handshake * po/POTFILES.in: Add qemud/stream.c [...] +/* + * Callback that gets invoked when a stream becomes writable/readable + */ +static void +remoteStreamEvent(virStreamPtr st, int events, void *opaque) +{ + struct qemud_client *client = opaque; + struct qemud_client_stream *stream; + + /* XXX sub-optimal - we really should be taking the server lock + * first, but we have no handle to the server object + * We're lucky to get away with it for now, due to this callback + * executing in the main thread, but this should really be fixed + */ + virMutexLock(&client->lock);
Shouldn't the stream stucture carry a reference to the connection/server ? [...]
static int -remoteStreamFilter(struct qemud_client *client ATTRIBUTE_UNUSED, - struct qemud_client_message *msg ATTRIBUTE_UNUSED, - void *opaque ATTRIBUTE_UNUSED) +remoteStreamFilter(struct qemud_client *client, + struct qemud_client_message *msg, void *opaque) { + struct qemud_client_stream *stream = opaque; + + if (msg->hdr.serial == stream->serial && + msg->hdr.proc == stream->procedure && + msg->hdr.type == REMOTE_STREAM) { + DEBUG("Incoming rx=%p serial=%d proc=%d status=%d", + stream->rx, msg->hdr.proc, msg->hdr.serial, msg->hdr.status); + + /* If there are queued packets, we need to queue all further + * messages, since they must be processed strictly in order. + * If there are no queued packets, then OK/ERROR messages + * should be processed immediately. Data packets are still + * queued to only be processed when the stream is marked as + * writable. + */
Since we are already separating different classes of packets, why not extend this to provide 2-3 classes for the data too: BACKGROUND/NORMAL/URGENT that can be added later using the current flags for creating a stream. ACK, though this need much testing, ideally we can automate this in TCK, especially about Abort effect at various point in time of a transfer. This is hard to test by the application writer so we should try to make the API as foolproof as prossible. Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

* qemud/dispatch.c: Set streamTX flag on outgoing data packets * qemud/qemud.h: Add streamTX flag to track outgoing data * qemud/qemud.c: Re-enable further TX when outgoing data packet has been fully sent. * qemud/stream.h, qemud/strea.c: Add method for enabling TX. Support reading from streams and transmitting data out to client --- qemud/dispatch.c | 2 + qemud/qemud.c | 4 ++- qemud/qemud.h | 1 + qemud/stream.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ qemud/stream.h | 4 ++ 5 files changed, 106 insertions(+), 1 deletions(-) diff --git a/qemud/dispatch.c b/qemud/dispatch.c index 1934d24..7417001 100644 --- a/qemud/dispatch.c +++ b/qemud/dispatch.c @@ -636,6 +636,8 @@ remoteSendStreamData(struct qemud_client *client, DEBUG("Total %d", msg->bufferOffset); } + if (data) + msg->streamTX = 1; /* Reset ready for I/O */ msg->bufferLength = msg->bufferOffset; diff --git a/qemud/qemud.c b/qemud/qemud.c index 6c81dec..af71495 100644 --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -1893,7 +1893,9 @@ void qemudClientMessageRelease(struct qemud_client *client, struct qemud_client_message *msg) { - if (!msg->async) + if (msg->streamTX) { + remoteStreamMessageFinished(client, msg); + } else if (!msg->async) client->nrequests--; /* See if the recv queue is currently throttled */ diff --git a/qemud/qemud.h b/qemud/qemud.h index 8ef5871..911cdc3 100644 --- a/qemud/qemud.h +++ b/qemud/qemud.h @@ -130,6 +130,7 @@ struct qemud_client_message { unsigned int bufferOffset; unsigned int async : 1; + unsigned int streamTX : 1; remote_message_header hdr; diff --git a/qemud/stream.c b/qemud/stream.c index 1fe0e58..584268d 100644 --- a/qemud/stream.c +++ b/qemud/stream.c @@ -32,6 +32,9 @@ static int remoteStreamHandleWrite(struct qemud_client *client, struct qemud_client_stream *stream); static int +remoteStreamHandleRead(struct qemud_client *client, + struct qemud_client_stream *stream); +static int remoteStreamHandleFinish(struct qemud_client *client, struct qemud_client_stream *stream, struct qemud_client_message *msg); @@ -48,6 +51,8 @@ remoteStreamUpdateEvents(struct qemud_client_stream *stream) int newEvents = 0; if (stream->rx) newEvents |= VIR_STREAM_EVENT_WRITABLE; + if (stream->tx && !stream->recvEOF) + newEvents |= VIR_STREAM_EVENT_READABLE; virStreamEventUpdateCallback(stream->st, newEvents); } @@ -87,6 +92,16 @@ remoteStreamEvent(virStreamPtr st, int events, void *opaque) } } + if (!stream->recvEOF && + (events & (VIR_STREAM_EVENT_READABLE | VIR_STREAM_EVENT_HANGUP))) { + events = events & ~(VIR_STREAM_EVENT_READABLE | VIR_STREAM_EVENT_HANGUP); + if (remoteStreamHandleRead(client, stream) < 0) { + remoteRemoveClientStream(client, stream); + qemudDispatchClientFailure(client); + goto cleanup; + } + } + if (!stream->closed && (events & (VIR_STREAM_EVENT_ERROR | VIR_STREAM_EVENT_HANGUP))) { int ret; @@ -507,3 +522,84 @@ remoteStreamHandleWrite(struct qemud_client *client, return 0; } + + + +/* + * Invoked when a stream is signalled as having data + * available to read. This reads upto one message + * worth of data, and then queues that for transmission + * to the client. + * + * Returns 0 if data was queued for TX, or a error RPC + * was sent, or -1 on fatal error, indicating client should + * be killed + */ +static int +remoteStreamHandleRead(struct qemud_client *client, + struct qemud_client_stream *stream) +{ + char *buffer; + size_t bufferLen = REMOTE_MESSAGE_PAYLOAD_MAX; + int ret; + + DEBUG("stream=%p", stream); + + /* Shouldn't ever be called unless we're marked able to + * transmit, but doesn't hurt to check */ + if (!stream->tx) + return 0; + + if (VIR_ALLOC_N(buffer, bufferLen) < 0) + return -1; + + ret = virStreamRecv(stream->st, buffer, bufferLen); + if (ret == -2) { + /* Should never get this, since we're only called when we know + * we're readable, but hey things change... */ + ret = 0; + } else if (ret < 0) { + remote_error rerr; + memset(&rerr, 0, sizeof rerr); + remoteDispatchConnError(&rerr, NULL); + + ret = remoteSerializeStreamError(client, &rerr, stream->procedure, stream->serial); + } else { + stream->tx = 0; + if (ret == 0) + stream->recvEOF = 1; + ret = remoteSendStreamData(client, stream, buffer, ret); + } + + VIR_FREE(buffer); + return ret; +} + + +/* + * Invoked when an outgoing data packet message has been fully sent. + * This simply re-enables TX of further data. + * + * The idea is to stop the daemon growing without bound due to + * fast stream, but slow client + */ +void +remoteStreamMessageFinished(struct qemud_client *client, + struct qemud_client_message *msg) +{ + struct qemud_client_stream *stream = client->streams; + + while (stream) { + if (msg->hdr.proc == stream->procedure && + msg->hdr.serial == stream->serial) + break; + stream = stream->next; + } + + DEBUG("Message client=%p stream=%p proc=%d serial=%d", client, stream, msg->hdr.proc, msg->hdr.serial); + + if (stream) { + stream->tx = 1; + remoteStreamUpdateEvents(stream); + } +} diff --git a/qemud/stream.h b/qemud/stream.h index fe5ce6f..de738ba 100644 --- a/qemud/stream.h +++ b/qemud/stream.h @@ -46,4 +46,8 @@ int remoteRemoveClientStream(struct qemud_client *client, struct qemud_client_stream *stream); +void +remoteStreamMessageFinished(struct qemud_client *client, + struct qemud_client_message *msg); + #endif /* __LIBVIRTD_STREAM_H__ */ -- 1.6.2.5

* src/remote_internal.c: Add helper APIs for processing data streams --- src/remote_internal.c | 530 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 524 insertions(+), 6 deletions(-) diff --git a/src/remote_internal.c b/src/remote_internal.c index de3c288..12e3bb9 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -1,4 +1,3 @@ - /* * remote_internal.c: driver to provide access to libvirtd running * on a remote machine @@ -111,7 +110,8 @@ enum { struct remote_thread_call { int mode; - /* 4 byte length, followed by RPC message header+body */ + /* Buffer for outgoing data packet + * 4 byte length, followed by RPC message header+body */ char buffer[4 + REMOTE_MESSAGE_MAX]; unsigned int bufferLength; unsigned int bufferOffset; @@ -121,6 +121,7 @@ struct remote_thread_call { virCond cond; + int want_reply; xdrproc_t ret_filter; char *ret; @@ -129,6 +130,26 @@ struct remote_thread_call { struct remote_thread_call *next; }; +struct private_stream_data { + unsigned int has_error : 1; + remote_error err; + + unsigned int serial; + unsigned int proc_nr; + + /* XXX this is potentially unbounded if the client + * app has domain events registered, since packets + * may be read off wire, while app isn't ready to + * recv them. Figure out how to address this some + * time.... + */ + char *incoming; + unsigned int incomingOffset; + unsigned int incomingLength; + + struct private_stream_data *next; +}; + struct private_data { virMutex lock; @@ -155,7 +176,8 @@ struct private_data { unsigned int saslEncodedOffset; #endif - /* 4 byte length, followed by RPC message header+body */ + /* Buffer for incoming data packets + * 4 byte length, followed by RPC message header+body */ char buffer[4 + REMOTE_MESSAGE_MAX]; unsigned int bufferLength; unsigned int bufferOffset; @@ -176,6 +198,8 @@ struct private_data { /* List of threads currently waiting for dispatch */ struct remote_thread_call *waitDispatch; + + struct private_stream_data *streams; }; enum { @@ -194,6 +218,10 @@ static void remoteDriverUnlock(struct private_data *driver) virMutexUnlock(&driver->lock); } +static int remoteIO(virConnectPtr conn, + struct private_data *priv, + int flags, + struct remote_thread_call *thiscall); static int call (virConnectPtr conn, struct private_data *priv, int flags, int proc_nr, xdrproc_t args_filter, char *args, @@ -6319,6 +6347,361 @@ done: return rv; } + +#if 0 +static struct private_stream_data * +remoteStreamOpen(virStreamPtr st, + int output ATTRIBUTE_UNUSED, + unsigned int proc_nr, + unsigned int serial) +{ + struct private_data *priv = st->conn->privateData; + struct private_stream_data *stpriv; + + if (VIR_ALLOC(stpriv) < 0) + return NULL; + + /* Initialize call object used to receive replies */ + stpriv->proc_nr = proc_nr; + stpriv->serial = serial; + + stpriv->next = priv->streams; + priv->streams = stpriv; + + return stpriv; +} + + +static int +remoteStreamPacket(virStreamPtr st, + int status, + const char *data, + size_t nbytes) +{ + DEBUG("st=%p status=%d data=%p nbytes=%d", st, status, data, nbytes); + struct private_data *priv = st->conn->privateData; + struct private_stream_data *privst = st->privateData; + XDR xdr; + struct remote_thread_call *thiscall; + remote_message_header hdr; + + memset(&hdr, 0, sizeof hdr); + + if (VIR_ALLOC(thiscall) < 0) { + virReportOOMError(st->conn); + return -1; + } + + thiscall->mode = REMOTE_MODE_WAIT_TX; + thiscall->serial = privst->serial; + thiscall->proc_nr = privst->proc_nr; + if (status == REMOTE_OK || + status == REMOTE_ERROR) + thiscall->want_reply = 1; + + if (virCondInit(&thiscall->cond) < 0) { + VIR_FREE(thiscall); + error (st->conn, VIR_ERR_INTERNAL_ERROR, + _("cannot initialize mutex")); + return -1; + } + + /* Don't fill in any other fields in 'thiscall' since + * we're not expecting a reply for this */ + + hdr.prog = REMOTE_PROGRAM; + hdr.vers = REMOTE_PROTOCOL_VERSION; + hdr.proc = privst->proc_nr; + hdr.type = REMOTE_STREAM; + hdr.serial = privst->serial; + hdr.status = status; + + + /* Length must include the length word itself (always encoded in + * 4 bytes as per RFC 4506), so offset start length. We write this + * later. + */ + thiscall->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN; + + /* Serialise header followed by args. */ + xdrmem_create (&xdr, thiscall->buffer + thiscall->bufferLength, + REMOTE_MESSAGE_MAX, XDR_ENCODE); + if (!xdr_remote_message_header (&xdr, &hdr)) { + error (st->conn, + VIR_ERR_RPC, _("xdr_remote_message_header failed")); + goto error; + } + + thiscall->bufferLength += xdr_getpos (&xdr); + xdr_destroy (&xdr); + + if (status == REMOTE_CONTINUE) { + if (((4 + REMOTE_MESSAGE_MAX) - thiscall->bufferLength) < nbytes) { + errorf(st->conn, + VIR_ERR_RPC, _("data size %d too large for payload %d"), + nbytes, ((4 + REMOTE_MESSAGE_MAX) - thiscall->bufferLength)); + goto error; + } + + memcpy(thiscall->buffer + thiscall->bufferLength, data, nbytes); + thiscall->bufferLength += nbytes; + } + + /* Go back to packet start and encode the length word. */ + xdrmem_create (&xdr, thiscall->buffer, REMOTE_MESSAGE_HEADER_XDR_LEN, XDR_ENCODE); + if (!xdr_u_int (&xdr, &thiscall->bufferLength)) { + error(st->conn, VIR_ERR_RPC, + _("xdr_u_int (length word)")); + goto error; + } + xdr_destroy (&xdr); + + /* remoteIO frees 'thiscall' for us (XXX that's dubious semantics) */ + if (remoteIO(st->conn, priv, 0, thiscall) < 0) + return -1; + + return nbytes; + +error: + xdr_destroy (&xdr); + VIR_FREE(thiscall); + return -1; +} + +static int +remoteStreamHasError(virStreamPtr st) { + struct private_stream_data *privst = st->privateData; + if (!privst->has_error) { + return 0; + } + + VIR_WARN0("Raising async error"); + virRaiseErrorFull(st->conn, + __FILE__, __FUNCTION__, __LINE__, + privst->err.domain, + privst->err.code, + privst->err.level, + privst->err.str1 ? *privst->err.str1 : NULL, + privst->err.str2 ? *privst->err.str2 : NULL, + privst->err.str3 ? *privst->err.str3 : NULL, + privst->err.int1, + privst->err.int2, + "%s", privst->err.message ? *privst->err.message : NULL); + + return 1; +} + +static void +remoteStreamRelease(virStreamPtr st) +{ + struct private_data *priv = st->conn->privateData; + struct private_stream_data *privst = st->privateData; + + if (priv->streams == privst) + priv->streams = privst->next; + else { + struct private_stream_data *tmp = priv->streams; + while (tmp && tmp->next) { + if (tmp->next == privst) { + tmp->next = privst->next; + break; + } + } + } + + if (privst->has_error) + xdr_free((xdrproc_t)xdr_remote_error, (char *)&privst->err); + + VIR_FREE(privst); + + st->driver = NULL; + st->privateData = NULL; +} + + +static int +remoteStreamSend(virStreamPtr st, + const char *data, + size_t nbytes) +{ + DEBUG("st=%p data=%p nbytes=%d", st, data, nbytes); + struct private_data *priv = st->conn->privateData; + int rv = -1; + + remoteDriverLock(priv); + + if (remoteStreamHasError(st)) + goto cleanup; + + rv = remoteStreamPacket(st, + REMOTE_CONTINUE, + data, + nbytes); + +cleanup: + if (rv == -1) + remoteStreamRelease(st); + + remoteDriverUnlock(priv); + + return rv; +} + + +static int +remoteStreamRecv(virStreamPtr st, + char *data, + size_t nbytes) +{ + DEBUG("st=%p data=%p nbytes=%d", st, data, nbytes); + struct private_data *priv = st->conn->privateData; + struct private_stream_data *privst = st->privateData; + int rv = -1; + + remoteDriverLock(priv); + + if (remoteStreamHasError(st)) + goto cleanup; + + if (!privst->incomingOffset) { + struct remote_thread_call *thiscall; + + if (VIR_ALLOC(thiscall) < 0) { + virReportOOMError(st->conn); + goto cleanup; + } + + /* We're not really doing an RPC calls, so we're + * skipping straight to RX part */ + thiscall->mode = REMOTE_MODE_WAIT_RX; + thiscall->serial = privst->serial; + thiscall->proc_nr = privst->proc_nr; + thiscall->want_reply = 1; + + if (virCondInit(&thiscall->cond) < 0) { + VIR_FREE(thiscall); + error (st->conn, VIR_ERR_INTERNAL_ERROR, + _("cannot initialize mutex")); + goto cleanup; + } + + /* remoteIO frees 'thiscall' for us (XXX that's dubious semantics) */ + if (remoteIO(st->conn, priv, 0, thiscall) < 0) + goto cleanup; + } + + DEBUG("After IO %d", privst->incomingOffset); + if (privst->incomingOffset) { + int want = privst->incomingOffset; + if (want > nbytes) + want = nbytes; + memcpy(data, privst->incoming, want); + if (want < privst->incomingOffset) { + memmove(privst->incoming, privst->incoming + want, privst->incomingOffset - want); + privst->incomingOffset -= want; + } else { + VIR_FREE(privst->incoming); + privst->incomingOffset = privst->incomingLength = 0; + } + rv = want; + } else { + rv = 0; + } + + DEBUG("Done %d", rv); + +cleanup: + if (rv == -1) + remoteStreamRelease(st); + remoteDriverUnlock(priv); + + return rv; +} + +static int +remoteStreamEventAddCallback(virStreamPtr stream ATTRIBUTE_UNUSED, + int events ATTRIBUTE_UNUSED, + virStreamEventCallback cb ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED, + virFreeCallback ff ATTRIBUTE_UNUSED) +{ + return -1; +} + +static int +remoteStreamEventUpdateCallback(virStreamPtr stream ATTRIBUTE_UNUSED, + int events ATTRIBUTE_UNUSED) +{ + return -1; +} + + +static int +remoteStreamEventRemoveCallback(virStreamPtr stream ATTRIBUTE_UNUSED) +{ + return -1; +} + +static int +remoteStreamFinish(virStreamPtr st) +{ + struct private_data *priv = st->conn->privateData; + int ret = -1; + + remoteDriverLock(priv); + + if (remoteStreamHasError(st)) + goto cleanup; + + ret = remoteStreamPacket(st, + REMOTE_OK, + NULL, + 0); + +cleanup: + remoteStreamRelease(st); + + remoteDriverUnlock(priv); + return ret; +} + +static int +remoteStreamAbort(virStreamPtr st) +{ + struct private_data *priv = st->conn->privateData; + int ret = -1; + + remoteDriverLock(priv); + + if (remoteStreamHasError(st)) + goto cleanup; + + ret = remoteStreamPacket(st, + REMOTE_ERROR, + NULL, + 0); + +cleanup: + remoteStreamRelease(st); + + remoteDriverUnlock(priv); + return ret; +} + + + +static virStreamDriver remoteStreamDrv = { + .streamRecv = remoteStreamRecv, + .streamSend = remoteStreamSend, + .streamFinish = remoteStreamFinish, + .streamAbort = remoteStreamAbort, + .streamAddCallback = remoteStreamEventAddCallback, + .streamUpdateCallback = remoteStreamEventUpdateCallback, + .streamRemoveCallback = remoteStreamEventRemoveCallback, +}; +#endif + + /*----------------------------------------------------------------------*/ @@ -6350,6 +6733,7 @@ prepareCall(virConnectPtr conn, rv->proc_nr = proc_nr; rv->ret_filter = ret_filter; rv->ret = ret; + rv->want_reply = 1; hdr.prog = REMOTE_PROGRAM; hdr.vers = REMOTE_PROTOCOL_VERSION; @@ -6535,7 +6919,10 @@ remoteIOWriteMessage(virConnectPtr conn, if (priv->saslEncodedOffset == priv->saslEncodedLength) { priv->saslEncoded = NULL; priv->saslEncodedOffset = priv->saslEncodedLength = 0; - thecall->mode = REMOTE_MODE_WAIT_RX; + if (thecall->want_reply) + thecall->mode = REMOTE_MODE_WAIT_RX; + else + thecall->mode = REMOTE_MODE_COMPLETE; } } else { #endif @@ -6549,7 +6936,10 @@ remoteIOWriteMessage(virConnectPtr conn, if (thecall->bufferOffset == thecall->bufferLength) { thecall->bufferOffset = thecall->bufferLength = 0; - thecall->mode = REMOTE_MODE_WAIT_RX; + if (thecall->want_reply) + thecall->mode = REMOTE_MODE_WAIT_RX; + else + thecall->mode = REMOTE_MODE_COMPLETE; } #if HAVE_SASL } @@ -6703,6 +7093,12 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, remote_message_header *hdr, XDR *xdr); +static int +processCallDispatchStream(virConnectPtr conn, struct private_data *priv, + int in_open, + remote_message_header *hdr, + XDR *xdr); + static int processCallDispatch(virConnectPtr conn, struct private_data *priv, @@ -6712,14 +7108,19 @@ processCallDispatch(virConnectPtr conn, struct private_data *priv, int len = priv->bufferLength - 4; int rv = -1; + /* Length word has already been read */ + priv->bufferOffset = 4; + /* Deserialise reply header. */ - xdrmem_create (&xdr, priv->buffer + 4, len, XDR_DECODE); + xdrmem_create (&xdr, priv->buffer + priv->bufferOffset, len, XDR_DECODE); if (!xdr_remote_message_header (&xdr, &hdr)) { error (in_open ? NULL : conn, VIR_ERR_RPC, _("invalid header in reply")); return -1; } + priv->bufferOffset += xdr_getpos(&xdr); + /* Check program, version, etc. are what we expect. */ if (hdr.prog != REMOTE_PROGRAM) { virRaiseError (in_open ? NULL : conn, @@ -6738,6 +7139,7 @@ processCallDispatch(virConnectPtr conn, struct private_data *priv, return -1; } + switch (hdr.type) { case REMOTE_REPLY: /* Normal RPC replies */ rv = processCallDispatchReply(conn, priv, in_open, @@ -6749,6 +7151,11 @@ processCallDispatch(virConnectPtr conn, struct private_data *priv, &hdr, &xdr); break; + case REMOTE_STREAM: /* Stream protocol */ + rv = processCallDispatchStream(conn, priv, in_open, + &hdr, &xdr); + break; + default: virRaiseError (in_open ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, @@ -6811,6 +7218,7 @@ processCallDispatchReply(virConnectPtr conn, struct private_data *priv, return 0; case REMOTE_ERROR: + VIR_WARN0("Method call error"); memset (&thecall->err, 0, sizeof thecall->err); if (!xdr_remote_error (xdr, &thecall->err)) { error (in_open ? NULL : conn, @@ -6854,6 +7262,113 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, return 0; } +static int +processCallDispatchStream(virConnectPtr conn ATTRIBUTE_UNUSED, + struct private_data *priv, + int in_open ATTRIBUTE_UNUSED, + remote_message_header *hdr, + XDR *xdr) { + struct private_stream_data *privst; + struct remote_thread_call *thecall; + + /* Try and find a matching stream */ + privst = priv->streams; + while (privst && + privst->serial != hdr->serial && + privst->proc_nr != hdr->proc) + privst = privst->next; + + if (!privst) { + VIR_WARN("No registered stream matching serial=%d, proc=%d", + hdr->serial, hdr->proc); + return -1; + } + + /* See if there's also a (optional) call waiting for this reply */ + thecall = priv->waitDispatch; + while (thecall && + thecall->serial != hdr->serial) + thecall = thecall->next; + + + /* Status is either REMOTE_OK (meaning that what follows is a ret + * structure), or REMOTE_ERROR (and what follows is a remote_error + * structure). + */ + switch (hdr->status) { + case REMOTE_CONTINUE: { + int avail = privst->incomingLength - privst->incomingOffset; + int need = priv->bufferLength - priv->bufferOffset; + VIR_WARN0("Got a stream data packet"); + + /* XXX flag stream as complete somwhere if need==0 */ + + if (need > avail) { + int extra = need - avail; + if (VIR_REALLOC_N(privst->incoming, + privst->incomingLength + extra) < 0) { + VIR_WARN0("Out of memory"); + return -1; + } + privst->incomingLength += extra; + } + + memcpy(privst->incoming + privst->incomingOffset, + priv->buffer + priv->bufferOffset, + priv->bufferLength - priv->bufferOffset); + privst->incomingOffset += (priv->bufferLength - priv->bufferOffset); + + if (thecall && thecall->want_reply) { + VIR_WARN("Got sync data packet offset=%d", privst->incomingOffset); + thecall->mode = REMOTE_MODE_COMPLETE; + } else { + VIR_WARN("Got aysnc data packet offset=%d", privst->incomingOffset); + } + return 0; + } + + case REMOTE_OK: + VIR_WARN0("Got a synchronous confirm"); + if (!thecall) { + VIR_WARN0("Got unexpected stream finish confirmation"); + return -1; + } + thecall->mode = REMOTE_MODE_COMPLETE; + return 0; + + case REMOTE_ERROR: + if (thecall && thecall->want_reply) { + VIR_WARN0("Got a synchronous error"); + /* Give the error straight to this call */ + memset (&thecall->err, 0, sizeof thecall->err); + if (!xdr_remote_error (xdr, &thecall->err)) { + error (in_open ? NULL : conn, + VIR_ERR_RPC, _("unmarshalling remote_error")); + return -1; + } + thecall->mode = REMOTE_MODE_ERROR; + } else { + VIR_WARN0("Got a asynchronous error"); + /* No call, so queue the error against the stream */ + if (privst->has_error) { + VIR_WARN0("Got unexpected duplicate stream error"); + return -1; + } + privst->has_error = 1; + memset (&privst->err, 0, sizeof privst->err); + if (!xdr_remote_error (xdr, &privst->err)) { + VIR_WARN0("Failed to unmarshall error"); + return -1; + } + } + return 0; + + default: + VIR_WARN("Stream with unexpected serial=%d, proc=%d, status=%d", + hdr->serial, hdr->proc, hdr->status); + return -1; + } +} static int remoteIOHandleInput(virConnectPtr conn, struct private_data *priv, @@ -6934,6 +7449,9 @@ remoteIOEventLoop(virConnectPtr conn, tmp = tmp->next; } + if (priv->streams) + fds[0].events |= POLLIN; + /* Release lock while poll'ing so other threads * can stuff themselves on the queue */ remoteDriverUnlock(priv); -- 1.6.2.5

* include/libvirt/libvirt.h.in: Add virConnectPutFile and virConnectGetFile APis to demo streams * src/driver.h, src/libvirt.c, src/libvirt_public.syms: Stub code for get/putfile APIs * src/esx/esx_driver.c, src/lxc_driver.c, src/opennebula/one_driver.c, src/openvz_driver.c, src/qemu_driver.c, src/remote_internal.c, src/test.c, src/uml_driver.c, src/vbox/vbox_tmpl.c, src/xen_unified.c: Add dummy entries in driver table for new APIs --- include/libvirt/libvirt.h | 8 ++++ include/libvirt/libvirt.h.in | 8 ++++ src/driver.h | 6 +++ src/esx/esx_driver.c | 2 + src/libvirt.c | 86 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 3 + src/lxc_driver.c | 2 + src/opennebula/one_driver.c | 2 + src/openvz_driver.c | 2 + src/qemu_driver.c | 2 + src/remote_internal.c | 2 + src/test.c | 2 + src/uml_driver.c | 2 + src/vbox/vbox_tmpl.c | 3 +- src/xen_unified.c | 2 + 15 files changed, 131 insertions(+), 1 deletions(-) diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 5dcecfd..a654a05 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -1540,6 +1540,14 @@ int virStreamAbort(virStreamPtr st); int virStreamFree(virStreamPtr st); +int virConnectPutFile(virConnectPtr conn, + const char *name, + virStreamPtr st); + +int virConnectGetFile(virConnectPtr conn, + const char *name, + virStreamPtr st); + #ifdef __cplusplus } diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index db091dc..d6c52dc 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1540,6 +1540,14 @@ int virStreamAbort(virStreamPtr st); int virStreamFree(virStreamPtr st); +int virConnectPutFile(virConnectPtr conn, + const char *name, + virStreamPtr st); + +int virConnectGetFile(virConnectPtr conn, + const char *name, + virStreamPtr st); + #ifdef __cplusplus } diff --git a/src/driver.h b/src/driver.h index 25d34b6..bc862a8 100644 --- a/src/driver.h +++ b/src/driver.h @@ -344,6 +344,10 @@ typedef int (*virDrvNodeDeviceReset) (virNodeDevicePtr dev); + +typedef int (*virDrvPutFile)(virConnectPtr conn, const char *name, virStreamPtr st); +typedef int (*virDrvGetFile)(virConnectPtr conn, const char *name, virStreamPtr st); + /** * _virDriver: * @@ -424,6 +428,8 @@ struct _virDriver { virDrvNodeDeviceDettach nodeDeviceDettach; virDrvNodeDeviceReAttach nodeDeviceReAttach; virDrvNodeDeviceReset nodeDeviceReset; + virDrvPutFile putFile; + virDrvGetFile getFile; }; typedef int diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index 0225e9a..d094ac1 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -2941,6 +2941,8 @@ static virDriver esxDriver = { NULL, /* nodeDeviceDettach */ NULL, /* nodeDeviceReAttach */ NULL, /* nodeDeviceReset */ + NULL, + NULL }; diff --git a/src/libvirt.c b/src/libvirt.c index d6536f4..f460876 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -9309,3 +9309,89 @@ int virStreamFree(virStreamPtr stream) return (-1); return (0); } + + +int virConnectPutFile(virConnectPtr conn, + const char *name, + virStreamPtr st) +{ + VIR_DEBUG("conn=%p, name=%s, stream=%p", conn, NULLSTR(name), st); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return -1; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (name == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + if (conn != st->conn) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (conn->driver->putFile) { + int rv = conn->driver->putFile(conn, name, st); + if (rv < 0) + goto error; + return rv; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); + return -1; +} + + +int virConnectGetFile(virConnectPtr conn, + const char *name, + virStreamPtr st) +{ + VIR_DEBUG("conn=%p, name=%s, stream=%p", conn, NULLSTR(name), st); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return -1; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (name == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + if (conn != st->conn) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (conn->driver->getFile) { + int rv = conn->driver->getFile(conn, name, st); + if (rv < 0) + goto error; + return rv; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index f48b8c5..d06c0a4 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -304,6 +304,9 @@ LIBVIRT_0.7.1 { virStreamFinish; virStreamAbort; virStreamFree; + virConnectPutFile; + virConnectGetFile; } LIBVIRT_0.7.0; + # .... define new API here using predicted next version number .... diff --git a/src/lxc_driver.c b/src/lxc_driver.c index bd0cf0e..b9bf1e4 100644 --- a/src/lxc_driver.c +++ b/src/lxc_driver.c @@ -1931,6 +1931,8 @@ static virDriver lxcDriver = { NULL, /* nodeDeviceDettach */ NULL, /* nodeDeviceReAttach */ NULL, /* nodeDeviceReset */ + NULL, + NULL, }; static virStateDriver lxcStateDriver = { diff --git a/src/opennebula/one_driver.c b/src/opennebula/one_driver.c index 135397c..184720b 100644 --- a/src/opennebula/one_driver.c +++ b/src/opennebula/one_driver.c @@ -779,6 +779,8 @@ static virDriver oneDriver = { NULL, /* nodeDeviceDettach; */ NULL, /* nodeDeviceReAttach; */ NULL, /* nodeDeviceReset; */ + NULL, + NULL }; static virStateDriver oneStateDriver = { diff --git a/src/openvz_driver.c b/src/openvz_driver.c index 6b7c49d..fdc3a6e 100644 --- a/src/openvz_driver.c +++ b/src/openvz_driver.c @@ -1392,6 +1392,8 @@ static virDriver openvzDriver = { NULL, /* nodeDeviceDettach */ NULL, /* nodeDeviceReAttach */ NULL, /* nodeDeviceReset */ + NULL, + NULL, }; int openvzRegister(void) { diff --git a/src/qemu_driver.c b/src/qemu_driver.c index eb22940..81485b5 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -7261,6 +7261,8 @@ static virDriver qemuDriver = { qemudNodeDeviceDettach, /* nodeDeviceDettach */ qemudNodeDeviceReAttach, /* nodeDeviceReAttach */ qemudNodeDeviceReset, /* nodeDeviceReset */ + NULL, + NULL, }; diff --git a/src/remote_internal.c b/src/remote_internal.c index 12e3bb9..c8ea1a3 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -8047,6 +8047,8 @@ static virDriver remote_driver = { remoteNodeDeviceDettach, /* nodeDeviceDettach */ remoteNodeDeviceReAttach, /* nodeDeviceReAttach */ remoteNodeDeviceReset, /* nodeDeviceReset */ + NULL, + NULL, }; static virNetworkDriver network_driver = { diff --git a/src/test.c b/src/test.c index 305f2c9..cda7be7 100644 --- a/src/test.c +++ b/src/test.c @@ -4242,6 +4242,8 @@ static virDriver testDriver = { NULL, /* nodeDeviceDettach */ NULL, /* nodeDeviceReAttach */ NULL, /* nodeDeviceReset */ + NULL, + NULL, }; static virNetworkDriver testNetworkDriver = { diff --git a/src/uml_driver.c b/src/uml_driver.c index dc1e8ef..53e7e10 100644 --- a/src/uml_driver.c +++ b/src/uml_driver.c @@ -1853,6 +1853,8 @@ static virDriver umlDriver = { NULL, /* nodeDeviceDettach */ NULL, /* nodeDeviceReAttach */ NULL, /* nodeDeviceReset */ + NULL, + NULL, }; diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index d3158ef..5a3079e 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -5621,7 +5621,8 @@ virDriver NAME(Driver) = { NULL, /* nodeDeviceDettach */ NULL, /* nodeDeviceReAttach */ NULL, /* nodeDeviceReset */ - + NULL, + NULL, }; virNetworkDriver NAME(NetworkDriver) = { diff --git a/src/xen_unified.c b/src/xen_unified.c index dfa9ca5..ac52ce9 100644 --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -1722,6 +1722,8 @@ static virDriver xenUnifiedDriver = { xenUnifiedNodeDeviceDettach, /* nodeDeviceDettach */ xenUnifiedNodeDeviceReAttach, /* nodeDeviceReAttach */ xenUnifiedNodeDeviceReset, /* nodeDeviceReset */ + NULL, + NULL, }; /** -- 1.6.2.5

* src/virsh.c: Implement 'put' and 'get' commands * .x-sc_avoid_write: Ignore src/virsh.c --- .x-sc_avoid_write | 1 + src/virsh.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 0 deletions(-) diff --git a/.x-sc_avoid_write b/.x-sc_avoid_write index c5a7535..c37574d 100644 --- a/.x-sc_avoid_write +++ b/.x-sc_avoid_write @@ -2,5 +2,6 @@ ^src/xend_internal\.c$ ^src/util-lib\.c$ ^src/libvirt\.c$ +^src/virsh\.c$ ^qemud/qemud.c$ ^gnulib/ diff --git a/src/virsh.c b/src/virsh.c index 2d0cf81..6387a3c 100644 --- a/src/virsh.c +++ b/src/virsh.c @@ -6780,6 +6780,167 @@ cmdEdit (vshControl *ctl, const vshCmd *cmd) } /* + * "put" command + */ +static const vshCmdInfo info_put[] = { + {"help", gettext_noop("upload a file")}, + {"desc", gettext_noop("Upload a file")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_put[] = { + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("file")}, + {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name")}, + {NULL, 0, 0, NULL} +}; + + +static int +cmdPutSource(virStreamPtr st ATTRIBUTE_UNUSED, + char *bytes, size_t nbytes, void *opaque) +{ + int *fd = opaque; + + return read(*fd, bytes, nbytes); +} + +/* + */ +static int +cmdPut (vshControl *ctl, const vshCmd *cmd) +{ + char *file = NULL; + char *name = NULL; + int ret = FALSE; + int fd = -1; + virStreamPtr st = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + name = vshCommandOptString (cmd, "name", NULL); + if (name == NULL) + goto cleanup; + file = vshCommandOptString (cmd, "file", NULL); + if (file == NULL) + goto cleanup; + + if ((fd = open(file, O_RDONLY)) < 0) { + vshError(ctl, FALSE, "cannot read %s", name); + goto cleanup; + } + + st = virStreamNew(ctl->conn, 0); + if (virConnectPutFile(ctl->conn, name, st) < 0) + goto cleanup; + + if (virStreamSendAll(st, cmdPutSource, &fd) < 0) + goto cleanup; + + if (close(fd) < 0) { + virStreamAbort(st); + goto cleanup; + } + fd = -1; + if (virStreamFinish(st) < 0) + goto cleanup; + + ret = TRUE; + +cleanup: + if (st) + virStreamFree(st); + free(name); + free(file); + if (fd != -1) + close(fd); + return ret; +} + + + +/* + * "get" command + */ +static const vshCmdInfo info_get[] = { + {"help", gettext_noop("upload a file")}, + {"desc", gettext_noop("Upload a file")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_get[] = { + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("file")}, + {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name")}, + {NULL, 0, 0, NULL} +}; + + +static int +cmdGetSink(virStreamPtr st ATTRIBUTE_UNUSED, + const char *bytes, size_t nbytes, void *opaque) +{ + int *fd = opaque; + + return write(*fd, bytes, nbytes); +} + +/* + */ +static int +cmdGet (vshControl *ctl, const vshCmd *cmd) +{ + char *file = NULL; + char *name = NULL; + int ret = FALSE; + int fd = -1; + virStreamPtr st = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + name = vshCommandOptString (cmd, "name", NULL); + if (name == NULL) + goto cleanup; + file = vshCommandOptString (cmd, "file", NULL); + if (file == NULL) + goto cleanup; + + if ((fd = open(file, O_WRONLY|O_TRUNC|O_CREAT,0700)) < 0) { + vshError(ctl, FALSE, "cannot create %s", name); + goto cleanup; + } + + st = virStreamNew(ctl->conn, 0); + if (virConnectGetFile(ctl->conn, name, st) < 0) + goto cleanup; + + if (virStreamRecvAll(st, cmdGetSink, &fd) < 0) + goto cleanup; + + if (close(fd) < 0) { + virStreamAbort(st); + goto cleanup; + } + fd = -1; + if (virStreamFinish(st) < 0) + goto cleanup; + + ret = TRUE; + +cleanup: + if (ret == FALSE) + unlink(file); + if (st) + virStreamFree(st); + free(name); + free(file); + if (fd != -1) + close(fd); + return ret; +} + + +/* * "net-edit" command */ static const vshCmdInfo info_network_edit[] = { @@ -6958,6 +7119,9 @@ static const vshCmdDef commands[] = { {"vcpupin", cmdVcpupin, opts_vcpupin, info_vcpupin}, {"version", cmdVersion, NULL, info_version}, {"vncdisplay", cmdVNCDisplay, opts_vncdisplay, info_vncdisplay}, + + {"put", cmdPut, opts_put, info_put}, + {"get", cmdGet, opts_get, info_get}, {NULL, NULL, NULL, NULL} }; -- 1.6.2.5

* src/test.c: Implement get/put APIs * .x-sc_avoid_write: ignore src/test.c --- .x-sc_avoid_write | 1 + src/test.c | 479 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 478 insertions(+), 2 deletions(-) diff --git a/.x-sc_avoid_write b/.x-sc_avoid_write index c37574d..201a064 100644 --- a/.x-sc_avoid_write +++ b/.x-sc_avoid_write @@ -2,6 +2,7 @@ ^src/xend_internal\.c$ ^src/util-lib\.c$ ^src/libvirt\.c$ +^src/test\.c$ ^src/virsh\.c$ ^qemud/qemud.c$ ^gnulib/ diff --git a/src/test.c b/src/test.c index cda7be7..3a4731e 100644 --- a/src/test.c +++ b/src/test.c @@ -30,6 +30,9 @@ #include <unistd.h> #include <sys/stat.h> #include <libxml/xmlsave.h> +#if HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif #include "virterror_internal.h" @@ -4174,6 +4177,478 @@ static void testDomainEventQueue(testConnPtr driver, } +struct testStreamFile { + int pid; + int fd; + char *filename; + int watch; + unsigned int create : 1; + unsigned int dispatching : 1; + unsigned int cbRemoved : 1; + virStreamEventCallback cb; + void *opaque; + virFreeCallback ff; +}; +typedef struct testStreamFile *testStreamFilePtr; + + +static int testStreamFileFree(testStreamFilePtr fs, + int aborting) +{ + int ret = 0; + int saved = 0; + int childstat = 0; + + if (!fs) + return 0; + + if (fs->fd != -1) { + if (close(fs->fd) != 0 && !aborting) { + saved = errno; + ret = -1; + } + } + /* Wait for intermediate process to exit */ + while (waitpid(fs->pid, &childstat, 0) == -1 && + errno == EINTR); + + if (childstat != 0) { + saved = EIO; + ret = -1; + } + + if ((aborting || ret != 0) && fs->create) + unlink(fs->filename); + VIR_FREE(fs->filename); + VIR_FREE(fs); + if (ret != 0) + errno = saved; + + return ret; +} + +static int testStreamFileClose(virStreamPtr st, + int aborting) +{ + testStreamFilePtr fs = st->privateData; + int ret = 0; + + if (!fs) + return 0; + + if (fs->watch > 0) + virEventRemoveHandle(fs->watch); + + ret = testStreamFileFree(fs, aborting); + + st->privateData = NULL; + return ret; +} + +static testStreamFilePtr +testStreamFileOpen(virStreamPtr st, + const char *filename, + int create) +{ + testStreamFilePtr fs = NULL; + int ret; + char *arg = NULL; + int fds[2] = { -1, -1 }; + const char *cmd[] = { + "dd", arg, NULL + }; + + if (!create && + access(filename, R_OK) < 0) { + virReportSystemError(st->conn, errno, + _("cannot access '%s'"), filename); + goto error; + } + + if (VIR_ALLOC(fs) < 0) + goto no_memory; + fs->fd = -1; + fs->watch = 0; + fs->create = create; + + if (!(fs->filename = strdup(filename))) + goto no_memory; + + if (create) { + ret = virAsprintf(&arg, "of=%s", filename); + } else { + ret = virAsprintf(&arg, "if=%s", filename); + } + if (ret < 0) + goto no_memory; + cmd[1] = arg; + + if (pipe(fds) < 0) { + virReportSystemError(st->conn, errno, "%s", + _("cannot allocate pipe")); + goto error; + } + + if ((st->flags & VIR_STREAM_NONBLOCK) && + virSetNonBlock(create ? fds[1] : fds[0]) < 0) { + virReportSystemError(st->conn, errno, "%s", + _("cannot make stream non-blocking")); + goto error; + } + + ret = virExec(st->conn, cmd, NULL, NULL, &fs->pid, + create ? fds[0] : -1, + create ? NULL : &fds[1], NULL, 0); + + if (ret < 0) + goto error; + + if (create) { + close(fds[0]); + fs->fd = fds[1]; + } else { + close(fds[1]); + fs->fd = fds[0]; + } + + VIR_FREE(arg); + return fs; + +no_memory: + virReportOOMError(st->conn); +error: + testStreamFileFree(fs, 1); + VIR_FREE(arg); + if (fds[0] != -1) close(fds[0]); + if (fds[1] != -1) close(fds[1]); + return NULL; +} + +static int +testStreamFileAbort(virStreamPtr st) +{ + testConnPtr privconn = st->conn->privateData; + int ret; + + testDriverLock(privconn); + + ret = testStreamFileClose(st, 1); + + testDriverUnlock(privconn); + return 0; +} + +static int +testStreamFileFinish(virStreamPtr st) +{ + testConnPtr privconn = st->conn->privateData; + int ret = -1; + + if (!st->privateData) { + testError(st->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("stream is not open")); + return -1; + } + + testDriverLock(privconn); + + ret = testStreamFileClose(st, 0); + if (ret != 0) + virReportSystemError(st->conn, errno, "%s", + _("cannot close stream")); + + testDriverUnlock(privconn); + return ret; +} + +static int +testStreamFileWrite(virStreamPtr st, + const char *bytes, + size_t nbytes) +{ + testConnPtr privconn = st->conn->privateData; + testStreamFilePtr fs = st->privateData; + int ret; + + if (!fs) { + testError(st->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("stream is not open")); + return -1; + } + + testDriverLock(privconn); + +retry: + ret = write(fs->fd, bytes, nbytes); + if (ret < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ret = -2; + } else if (errno == EINTR) { + goto retry; + } else { + ret = -1; + virReportSystemError(st->conn, errno, "%s", + _("cannot write to stream")); + testStreamFileClose(st, 1); + } + } + + testDriverUnlock(privconn); + return ret; +} + +static int +testStreamFileRead(virStreamPtr st, + char *bytes, + size_t nbytes) +{ + testConnPtr privconn = st->conn->privateData; + testStreamFilePtr fs = st->privateData; + int ret; + + if (!fs) { + testError(st->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("stream is not open")); + return -1; + } + + testDriverLock(privconn); + +retry: + ret = read(fs->fd, bytes, nbytes); + if (ret < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ret = -2; + } else if (errno == EINTR) { + goto retry; + } else { + ret = -1; + virReportSystemError(st->conn, errno, "%s", + _("cannot read from stream")); + testStreamFileClose(st, 1); + } + } + + testDriverUnlock(privconn); + return ret; +} + +static void +testStreamFileEvent(int watch ATTRIBUTE_UNUSED, + int fd ATTRIBUTE_UNUSED, + int events, void *opaque) { + virStreamPtr stream = opaque; + testConnPtr privconn = stream->conn->privateData; + testStreamFilePtr fs = stream->privateData; + virStreamEventCallback cb; + void *cbopaque; + virFreeCallback ff; + + testDriverLock(privconn); + + if (!fs || !fs->cb) { + testDriverUnlock(privconn); + return; + } + + cb = fs->cb; + cbopaque = fs->opaque; + ff = fs->ff; + fs->dispatching = 1; + testDriverUnlock(privconn); + + cb(stream, events, cbopaque); + + testDriverLock(privconn); + if (stream->privateData == NULL) { + if (ff) + (ff)(cbopaque); + } else { + fs->dispatching = 0; + if (fs->cbRemoved && ff) + (ff)(cbopaque); + } + testDriverUnlock(privconn); +} + +static int +testStreamFileAddCallback(virStreamPtr stream, + int events, + virStreamEventCallback cb, + void *opaque, + virFreeCallback ff) +{ + testConnPtr privconn = stream->conn->privateData; + testStreamFilePtr fs = stream->privateData; + int ret = -1; + + if (!fs) { + testError(stream->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("stream is not open")); + return -1; + } + + testDriverLock(privconn); + + if (fs->watch != 0) { + testError(stream->conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("stream already has a callback registered")); + goto cleanup; + } + + if ((fs->watch = virEventAddHandle(fs->fd, events, + testStreamFileEvent, + stream, + NULL)) < 0) { + testError(stream->conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot register file watch on stream")); + goto cleanup; + } + + fs->cbRemoved = 0; + fs->cb = cb; + fs->opaque = opaque; + fs->ff = ff; + virStreamRef(stream); + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + return ret; +} + +static int +testStreamFileUpdateCallback(virStreamPtr stream, + int events) +{ + testConnPtr privconn = stream->conn->privateData; + testStreamFilePtr fs = stream->privateData; + int ret = -1; + + if (!fs) { + testError(stream->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("stream is not open")); + return -1; + } + + testDriverLock(privconn); + + if (fs->watch == 0) { + testError(stream->conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("stream does not have a callback registered")); + goto cleanup; + } + + virEventUpdateHandle(fs->watch, events); + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + return ret; +} + +static int +testStreamFileRemoveCallback(virStreamPtr stream) +{ + testConnPtr privconn = stream->conn->privateData; + testStreamFilePtr fs = stream->privateData; + int ret = -1; + + if (!fs) { + testError(stream->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("stream is not open")); + return -1; + } + + testDriverLock(privconn); + + if (fs->watch == 0) { + testError(stream->conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("stream does not have a callback registered")); + goto cleanup; + } + + virEventRemoveHandle(fs->watch); + if (fs->dispatching) + fs->cbRemoved = 1; + else if (fs->ff) + (fs->ff)(fs->opaque); + + fs->watch = 0; + fs->ff = NULL; + fs->cb = NULL; + fs->opaque = NULL; + + virStreamFree(stream); + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + return ret; +} + + +static virStreamDriver streamFileDrv = { + .streamRecv = testStreamFileRead, + .streamSend = testStreamFileWrite, + .streamFinish = testStreamFileFinish, + .streamAbort = testStreamFileAbort, + .streamAddCallback = testStreamFileAddCallback, + .streamUpdateCallback = testStreamFileUpdateCallback, + .streamRemoveCallback = testStreamFileRemoveCallback +}; + +static int +testConnectPutFile(virConnectPtr conn, + const char *name, + virStreamPtr st) +{ + testConnPtr privconn = conn->privateData; + testStreamFilePtr fs = NULL; + int ret = -1; + testDriverLock(privconn); + + if (!(fs = testStreamFileOpen(st, name, 1))) + goto cleanup; + + st->driver = &streamFileDrv; + st->privateData = fs; + ret = 0; + +cleanup: + testDriverUnlock(privconn); + + return ret; +} + + +static int +testConnectGetFile(virConnectPtr conn, + const char *name, + virStreamPtr st) +{ + testConnPtr privconn = conn->privateData; + testStreamFilePtr fs = NULL; + int ret = -1; + testDriverLock(privconn); + + if (!(fs = testStreamFileOpen(st, name, 0))) + goto cleanup; + + st->driver = &streamFileDrv; + st->privateData = fs; + ret = 0; + +cleanup: + testDriverUnlock(privconn); + + return ret; +} + + static virDriver testDriver = { VIR_DRV_TEST, "Test", @@ -4242,8 +4717,8 @@ static virDriver testDriver = { NULL, /* nodeDeviceDettach */ NULL, /* nodeDeviceReAttach */ NULL, /* nodeDeviceReset */ - NULL, - NULL, + testConnectPutFile, + testConnectGetFile, }; static virNetworkDriver testNetworkDriver = { -- 1.6.2.5

* qemud/dispatch.c: Pass msg->hdr to API handlers * qemud/remote.c, qemud/remote.h: Add remote_message_header to all API handlers * qemud/remote_generate_stubs.pl: Update to include the remote_message_header in API handler definitions * qemud/remote_dispatch_prototypes.h: Re-generate --- qemud/dispatch.c | 2 +- qemud/remote.c | 173 ++++++++++++++++++++++++++++++++---- qemud/remote.h | 1 + qemud/remote_dispatch_prototypes.h | 137 ++++++++++++++++++++++++++++ qemud/remote_generate_stubs.pl | 1 + 5 files changed, 297 insertions(+), 17 deletions(-) diff --git a/qemud/dispatch.c b/qemud/dispatch.c index 7417001..0c6a902 100644 --- a/qemud/dispatch.c +++ b/qemud/dispatch.c @@ -496,7 +496,7 @@ remoteDispatchClientCall (struct qemud_server *server, * * 'conn', 'rerr', 'args and 'ret' */ - rv = (data->fn)(server, client, conn, &rerr, &args, &ret); + rv = (data->fn)(server, client, conn, &msg->hdr, &rerr, &args, &ret); virMutexLock(&server->lock); virMutexLock(&client->lock); diff --git a/qemud/remote.c b/qemud/remote.c index d32d513..3034023 100644 --- a/qemud/remote.c +++ b/qemud/remote.c @@ -127,6 +127,7 @@ static int remoteDispatchOpen (struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, struct remote_open_args *args, void *ret ATTRIBUTE_UNUSED) { @@ -174,6 +175,7 @@ static int remoteDispatchClose (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn ATTRIBUTE_UNUSED, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr ATTRIBUTE_UNUSED, void *args ATTRIBUTE_UNUSED, void *ret ATTRIBUTE_UNUSED) { @@ -191,6 +193,7 @@ static int remoteDispatchSupportsFeature (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_supports_feature_args *args, remote_supports_feature_ret *ret) { @@ -208,6 +211,7 @@ static int remoteDispatchGetType (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_get_type_ret *ret) { @@ -235,6 +239,7 @@ static int remoteDispatchGetVersion (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_get_version_ret *ret) @@ -254,6 +259,7 @@ static int remoteDispatchGetHostname (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_get_hostname_ret *ret) @@ -274,6 +280,7 @@ static int remoteDispatchGetUri (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_get_uri_ret *ret) @@ -295,6 +302,7 @@ static int remoteDispatchGetMaxVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_get_max_vcpus_args *args, remote_get_max_vcpus_ret *ret) @@ -315,6 +323,7 @@ static int remoteDispatchNodeGetInfo (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_node_get_info_ret *ret) @@ -342,6 +351,7 @@ static int remoteDispatchGetCapabilities (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_get_capabilities_ret *ret) @@ -362,6 +372,7 @@ static int remoteDispatchNodeGetCellsFreeMemory (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_node_get_cells_free_memory_args *args, remote_node_get_cells_free_memory_ret *ret) @@ -399,6 +410,7 @@ static int remoteDispatchNodeGetFreeMemory (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_node_get_free_memory_ret *ret) @@ -419,6 +431,7 @@ static int remoteDispatchDomainGetSchedulerType (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_get_scheduler_type_args *args, remote_domain_get_scheduler_type_ret *ret) @@ -450,6 +463,7 @@ static int remoteDispatchDomainGetSchedulerParameters (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_get_scheduler_parameters_args *args, remote_domain_get_scheduler_parameters_ret *ret) @@ -533,6 +547,7 @@ static int remoteDispatchDomainSetSchedulerParameters (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_set_scheduler_parameters_args *args, void *ret ATTRIBUTE_UNUSED) @@ -596,6 +611,7 @@ static int remoteDispatchDomainBlockStats (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_block_stats_args *args, remote_domain_block_stats_ret *ret) @@ -631,6 +647,7 @@ static int remoteDispatchDomainInterfaceStats (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_interface_stats_args *args, remote_domain_interface_stats_ret *ret) @@ -669,6 +686,7 @@ static int remoteDispatchDomainBlockPeek (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_block_peek_args *args, remote_domain_block_peek_ret *ret) @@ -721,6 +739,7 @@ static int remoteDispatchDomainMemoryPeek (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_memory_peek_args *args, remote_domain_memory_peek_ret *ret) @@ -771,6 +790,7 @@ static int remoteDispatchDomainAttachDevice (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_attach_device_args *args, void *ret ATTRIBUTE_UNUSED) @@ -796,6 +816,7 @@ static int remoteDispatchDomainCreate (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_create_args *args, void *ret ATTRIBUTE_UNUSED) @@ -821,6 +842,7 @@ static int remoteDispatchDomainCreateXml (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_create_xml_args *args, remote_domain_create_xml_ret *ret) @@ -843,6 +865,7 @@ static int remoteDispatchDomainDefineXml (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_define_xml_args *args, remote_domain_define_xml_ret *ret) @@ -865,6 +888,7 @@ static int remoteDispatchDomainDestroy (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_destroy_args *args, void *ret ATTRIBUTE_UNUSED) @@ -890,6 +914,7 @@ static int remoteDispatchDomainDetachDevice (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_detach_device_args *args, void *ret ATTRIBUTE_UNUSED) @@ -916,6 +941,7 @@ static int remoteDispatchDomainDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_dump_xml_args *args, remote_domain_dump_xml_ret *ret) @@ -943,6 +969,7 @@ static int remoteDispatchDomainXmlFromNative (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_xml_from_native_args *args, remote_domain_xml_from_native_ret *ret) @@ -963,6 +990,7 @@ static int remoteDispatchDomainXmlToNative (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_xml_to_native_args *args, remote_domain_xml_to_native_ret *ret) @@ -984,6 +1012,7 @@ static int remoteDispatchDomainGetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_get_autostart_args *args, remote_domain_get_autostart_ret *ret) @@ -1009,6 +1038,7 @@ static int remoteDispatchDomainGetInfo (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_get_info_args *args, remote_domain_get_info_ret *ret) @@ -1043,6 +1073,7 @@ static int remoteDispatchDomainGetMaxMemory (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_get_max_memory_args *args, remote_domain_get_max_memory_ret *ret) @@ -1069,6 +1100,7 @@ static int remoteDispatchDomainGetMaxVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_get_max_vcpus_args *args, remote_domain_get_max_vcpus_ret *ret) @@ -1095,6 +1127,7 @@ static int remoteDispatchDomainGetSecurityLabel(struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_get_security_label_args *args, remote_domain_get_security_label_ret *ret) @@ -1132,6 +1165,7 @@ static int remoteDispatchNodeGetSecurityModel(struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_node_get_security_model_ret *ret) @@ -1165,6 +1199,7 @@ static int remoteDispatchDomainGetOsType (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_get_os_type_args *args, remote_domain_get_os_type_ret *ret) @@ -1192,6 +1227,7 @@ static int remoteDispatchDomainGetVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_get_vcpus_args *args, remote_domain_get_vcpus_ret *ret) @@ -1272,6 +1308,7 @@ static int remoteDispatchDomainMigratePrepare (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_migrate_prepare_args *args, remote_domain_migrate_prepare_ret *ret) @@ -1320,6 +1357,7 @@ static int remoteDispatchDomainMigratePerform (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_migrate_perform_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1354,6 +1392,7 @@ static int remoteDispatchDomainMigrateFinish (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_migrate_finish_args *args, remote_domain_migrate_finish_ret *ret) @@ -1380,6 +1419,7 @@ static int remoteDispatchDomainMigratePrepare2 (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_migrate_prepare2_args *args, remote_domain_migrate_prepare2_ret *ret) @@ -1424,6 +1464,7 @@ static int remoteDispatchDomainMigrateFinish2 (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_migrate_finish2_args *args, remote_domain_migrate_finish2_ret *ret) @@ -1451,6 +1492,7 @@ static int remoteDispatchListDefinedDomains (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_list_defined_domains_args *args, remote_list_defined_domains_ret *ret) @@ -1484,6 +1526,7 @@ static int remoteDispatchDomainLookupById (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_lookup_by_id_args *args, remote_domain_lookup_by_id_ret *ret) @@ -1505,6 +1548,7 @@ static int remoteDispatchDomainLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_lookup_by_name_args *args, remote_domain_lookup_by_name_ret *ret) @@ -1526,6 +1570,7 @@ static int remoteDispatchDomainLookupByUuid (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_lookup_by_uuid_args *args, remote_domain_lookup_by_uuid_ret *ret) @@ -1547,6 +1592,7 @@ static int remoteDispatchNumOfDefinedDomains (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_num_of_defined_domains_ret *ret) @@ -1565,6 +1611,7 @@ static int remoteDispatchDomainPinVcpu (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_pin_vcpu_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1600,6 +1647,7 @@ static int remoteDispatchDomainReboot (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_reboot_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1625,6 +1673,7 @@ static int remoteDispatchDomainRestore (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_restore_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1642,6 +1691,7 @@ static int remoteDispatchDomainResume (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_resume_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1667,6 +1717,7 @@ static int remoteDispatchDomainSave (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_save_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1692,6 +1743,7 @@ static int remoteDispatchDomainCoreDump (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_core_dump_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1717,6 +1769,7 @@ static int remoteDispatchDomainSetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_set_autostart_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1742,6 +1795,7 @@ static int remoteDispatchDomainSetMaxMemory (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_set_max_memory_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1767,6 +1821,7 @@ static int remoteDispatchDomainSetMemory (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_set_memory_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1792,6 +1847,7 @@ static int remoteDispatchDomainSetVcpus (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_set_vcpus_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1817,6 +1873,7 @@ static int remoteDispatchDomainShutdown (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_shutdown_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1842,6 +1899,7 @@ static int remoteDispatchDomainSuspend (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_suspend_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1867,6 +1925,7 @@ static int remoteDispatchDomainUndefine (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_domain_undefine_args *args, void *ret ATTRIBUTE_UNUSED) @@ -1892,6 +1951,7 @@ static int remoteDispatchListDefinedNetworks (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_list_defined_networks_args *args, remote_list_defined_networks_ret *ret) @@ -1925,6 +1985,7 @@ static int remoteDispatchListDomains (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_list_domains_args *args, remote_list_domains_ret *ret) @@ -1957,6 +2018,7 @@ static int remoteDispatchListNetworks (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_list_networks_args *args, remote_list_networks_ret *ret) @@ -1990,6 +2052,7 @@ static int remoteDispatchNetworkCreate (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_network_create_args *args, void *ret ATTRIBUTE_UNUSED) @@ -2015,6 +2078,7 @@ static int remoteDispatchNetworkCreateXml (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_network_create_xml_args *args, remote_network_create_xml_ret *ret) @@ -2036,6 +2100,7 @@ static int remoteDispatchNetworkDefineXml (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_network_define_xml_args *args, remote_network_define_xml_ret *ret) @@ -2057,6 +2122,7 @@ static int remoteDispatchNetworkDestroy (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_network_destroy_args *args, void *ret ATTRIBUTE_UNUSED) @@ -2082,6 +2148,7 @@ static int remoteDispatchNetworkDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_network_dump_xml_args *args, remote_network_dump_xml_ret *ret) @@ -2109,6 +2176,7 @@ static int remoteDispatchNetworkGetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_network_get_autostart_args *args, remote_network_get_autostart_ret *ret) @@ -2134,6 +2202,7 @@ static int remoteDispatchNetworkGetBridgeName (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_network_get_bridge_name_args *args, remote_network_get_bridge_name_ret *ret) @@ -2161,6 +2230,7 @@ static int remoteDispatchNetworkLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_network_lookup_by_name_args *args, remote_network_lookup_by_name_ret *ret) @@ -2182,6 +2252,7 @@ static int remoteDispatchNetworkLookupByUuid (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_network_lookup_by_uuid_args *args, remote_network_lookup_by_uuid_ret *ret) @@ -2203,6 +2274,7 @@ static int remoteDispatchNetworkSetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_network_set_autostart_args *args, void *ret ATTRIBUTE_UNUSED) @@ -2228,6 +2300,7 @@ static int remoteDispatchNetworkUndefine (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_network_undefine_args *args, void *ret ATTRIBUTE_UNUSED) @@ -2253,6 +2326,7 @@ static int remoteDispatchNumOfDefinedNetworks (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_num_of_defined_networks_ret *ret) @@ -2271,6 +2345,7 @@ static int remoteDispatchNumOfDomains (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_num_of_domains_ret *ret) @@ -2289,6 +2364,7 @@ static int remoteDispatchNumOfNetworks (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_num_of_networks_ret *ret) @@ -2309,6 +2385,7 @@ static int remoteDispatchNumOfInterfaces (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_num_of_interfaces_ret *ret) @@ -2327,6 +2404,7 @@ static int remoteDispatchListInterfaces (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_list_interfaces_args *args, remote_list_interfaces_ret *ret) @@ -2360,6 +2438,7 @@ static int remoteDispatchNumOfDefinedInterfaces (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_num_of_defined_interfaces_ret *ret) @@ -2378,6 +2457,7 @@ static int remoteDispatchListDefinedInterfaces (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_list_defined_interfaces_args *args, remote_list_defined_interfaces_ret *ret) @@ -2411,6 +2491,7 @@ static int remoteDispatchInterfaceLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_interface_lookup_by_name_args *args, remote_interface_lookup_by_name_ret *ret) @@ -2432,6 +2513,7 @@ static int remoteDispatchInterfaceLookupByMacString (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_interface_lookup_by_mac_string_args *args, remote_interface_lookup_by_mac_string_ret *ret) @@ -2453,6 +2535,7 @@ static int remoteDispatchInterfaceGetXmlDesc (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_interface_get_xml_desc_args *args, remote_interface_get_xml_desc_ret *ret) @@ -2480,6 +2563,7 @@ static int remoteDispatchInterfaceDefineXml (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_interface_define_xml_args *args, remote_interface_define_xml_ret *ret) @@ -2499,11 +2583,12 @@ remoteDispatchInterfaceDefineXml (struct qemud_server *server ATTRIBUTE_UNUSED, static int remoteDispatchInterfaceUndefine (struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_error *rerr, - remote_interface_undefine_args *args, - void *ret ATTRIBUTE_UNUSED) + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_interface_undefine_args *args, + void *ret ATTRIBUTE_UNUSED) { virInterfacePtr iface; @@ -2524,11 +2609,12 @@ remoteDispatchInterfaceUndefine (struct qemud_server *server ATTRIBUTE_UNUSED, static int remoteDispatchInterfaceCreate (struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_error *rerr, - remote_interface_create_args *args, - void *ret ATTRIBUTE_UNUSED) + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_interface_create_args *args, + void *ret ATTRIBUTE_UNUSED) { virInterfacePtr iface; @@ -2549,11 +2635,12 @@ remoteDispatchInterfaceCreate (struct qemud_server *server ATTRIBUTE_UNUSED, static int remoteDispatchInterfaceDestroy (struct qemud_server *server ATTRIBUTE_UNUSED, - struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn, - remote_error *rerr, - remote_interface_destroy_args *args, - void *ret ATTRIBUTE_UNUSED) + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_interface_destroy_args *args, + void *ret ATTRIBUTE_UNUSED) { virInterfacePtr iface; @@ -2578,6 +2665,7 @@ static int remoteDispatchAuthList (struct qemud_server *server, struct qemud_client *client, virConnectPtr conn ATTRIBUTE_UNUSED, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_auth_list_ret *ret) @@ -2636,6 +2724,7 @@ static int remoteDispatchAuthSaslInit (struct qemud_server *server, struct qemud_client *client, virConnectPtr conn ATTRIBUTE_UNUSED, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_auth_sasl_init_ret *ret) @@ -2893,6 +2982,7 @@ static int remoteDispatchAuthSaslStart (struct qemud_server *server, struct qemud_client *client, virConnectPtr conn ATTRIBUTE_UNUSED, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret) @@ -2980,6 +3070,7 @@ static int remoteDispatchAuthSaslStep (struct qemud_server *server, struct qemud_client *client, virConnectPtr conn ATTRIBUTE_UNUSED, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret) @@ -3069,6 +3160,7 @@ static int remoteDispatchAuthSaslInit (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn ATTRIBUTE_UNUSED, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_auth_sasl_init_ret *ret ATTRIBUTE_UNUSED) @@ -3082,6 +3174,7 @@ static int remoteDispatchAuthSaslStart (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn ATTRIBUTE_UNUSED, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_auth_sasl_start_args *args ATTRIBUTE_UNUSED, remote_auth_sasl_start_ret *ret ATTRIBUTE_UNUSED) @@ -3094,7 +3187,8 @@ remoteDispatchAuthSaslStart (struct qemud_server *server ATTRIBUTE_UNUSED, static int remoteDispatchAuthSaslStep (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, - virConnectPtr conn ATTRIBUTE_UNUSED, + virConnectPtr conn ATTRIBUTE_UNUSED + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_auth_sasl_step_args *args ATTRIBUTE_UNUSED, remote_auth_sasl_step_ret *ret ATTRIBUTE_UNUSED) @@ -3111,6 +3205,7 @@ static int remoteDispatchAuthPolkit (struct qemud_server *server, struct qemud_client *client, virConnectPtr conn ATTRIBUTE_UNUSED, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_auth_polkit_ret *ret) @@ -3223,6 +3318,7 @@ static int remoteDispatchAuthPolkit (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn ATTRIBUTE_UNUSED, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_auth_polkit_ret *ret ATTRIBUTE_UNUSED) @@ -3243,6 +3339,7 @@ static int remoteDispatchListDefinedStoragePools (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_list_defined_storage_pools_args *args, remote_list_defined_storage_pools_ret *ret) @@ -3276,6 +3373,7 @@ static int remoteDispatchListStoragePools (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_list_storage_pools_args *args, remote_list_storage_pools_ret *ret) @@ -3309,6 +3407,7 @@ static int remoteDispatchFindStoragePoolSources (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_find_storage_pool_sources_args *args, remote_find_storage_pool_sources_ret *ret) @@ -3331,6 +3430,7 @@ static int remoteDispatchStoragePoolCreate (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_create_args *args, void *ret ATTRIBUTE_UNUSED) @@ -3356,6 +3456,7 @@ static int remoteDispatchStoragePoolCreateXml (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_create_xml_args *args, remote_storage_pool_create_xml_ret *ret) @@ -3377,6 +3478,7 @@ static int remoteDispatchStoragePoolDefineXml (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_define_xml_args *args, remote_storage_pool_define_xml_ret *ret) @@ -3398,6 +3500,7 @@ static int remoteDispatchStoragePoolBuild (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_build_args *args, void *ret ATTRIBUTE_UNUSED) @@ -3424,6 +3527,7 @@ static int remoteDispatchStoragePoolDestroy (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_destroy_args *args, void *ret ATTRIBUTE_UNUSED) @@ -3449,6 +3553,7 @@ static int remoteDispatchStoragePoolDelete (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_delete_args *args, void *ret ATTRIBUTE_UNUSED) @@ -3474,6 +3579,7 @@ static int remoteDispatchStoragePoolRefresh (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_refresh_args *args, void *ret ATTRIBUTE_UNUSED) @@ -3499,6 +3605,7 @@ static int remoteDispatchStoragePoolGetInfo (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_get_info_args *args, remote_storage_pool_get_info_ret *ret) @@ -3532,6 +3639,7 @@ static int remoteDispatchStoragePoolDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_dump_xml_args *args, remote_storage_pool_dump_xml_ret *ret) @@ -3559,6 +3667,7 @@ static int remoteDispatchStoragePoolGetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_get_autostart_args *args, remote_storage_pool_get_autostart_ret *ret) @@ -3585,6 +3694,7 @@ static int remoteDispatchStoragePoolLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_lookup_by_name_args *args, remote_storage_pool_lookup_by_name_ret *ret) @@ -3606,6 +3716,7 @@ static int remoteDispatchStoragePoolLookupByUuid (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_lookup_by_uuid_args *args, remote_storage_pool_lookup_by_uuid_ret *ret) @@ -3627,6 +3738,7 @@ static int remoteDispatchStoragePoolLookupByVolume (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_lookup_by_volume_args *args, remote_storage_pool_lookup_by_volume_ret *ret) @@ -3656,6 +3768,7 @@ static int remoteDispatchStoragePoolSetAutostart (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_set_autostart_args *args, void *ret ATTRIBUTE_UNUSED) @@ -3681,6 +3794,7 @@ static int remoteDispatchStoragePoolUndefine (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_undefine_args *args, void *ret ATTRIBUTE_UNUSED) @@ -3706,6 +3820,7 @@ static int remoteDispatchNumOfStoragePools (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_num_of_storage_pools_ret *ret) @@ -3724,6 +3839,7 @@ static int remoteDispatchNumOfDefinedStoragePools (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, void *args ATTRIBUTE_UNUSED, remote_num_of_defined_storage_pools_ret *ret) @@ -3742,6 +3858,7 @@ static int remoteDispatchStoragePoolListVolumes (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_list_volumes_args *args, remote_storage_pool_list_volumes_ret *ret) @@ -3785,6 +3902,7 @@ static int remoteDispatchStoragePoolNumOfVolumes (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_pool_num_of_volumes_args *args, remote_storage_pool_num_of_volumes_ret *ret) @@ -3818,6 +3936,7 @@ static int remoteDispatchStorageVolCreateXml (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_vol_create_xml_args *args, remote_storage_vol_create_xml_ret *ret) @@ -3847,6 +3966,7 @@ static int remoteDispatchStorageVolCreateXmlFrom (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_vol_create_xml_from_args *args, remote_storage_vol_create_xml_from_ret *ret) @@ -3882,6 +4002,7 @@ static int remoteDispatchStorageVolDelete (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_vol_delete_args *args, void *ret ATTRIBUTE_UNUSED) @@ -3907,6 +4028,7 @@ static int remoteDispatchStorageVolGetInfo (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_vol_get_info_args *args, remote_storage_vol_get_info_ret *ret) @@ -3939,6 +4061,7 @@ static int remoteDispatchStorageVolDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_vol_dump_xml_args *args, remote_storage_vol_dump_xml_ret *ret) @@ -3967,6 +4090,7 @@ static int remoteDispatchStorageVolGetPath (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_vol_get_path_args *args, remote_storage_vol_get_path_ret *ret) @@ -3995,6 +4119,7 @@ static int remoteDispatchStorageVolLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_vol_lookup_by_name_args *args, remote_storage_vol_lookup_by_name_ret *ret) @@ -4024,6 +4149,7 @@ static int remoteDispatchStorageVolLookupByKey (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_vol_lookup_by_key_args *args, remote_storage_vol_lookup_by_key_ret *ret) @@ -4046,6 +4172,7 @@ static int remoteDispatchStorageVolLookupByPath (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_storage_vol_lookup_by_path_args *args, remote_storage_vol_lookup_by_path_ret *ret) @@ -4072,6 +4199,7 @@ static int remoteDispatchNodeNumOfDevices (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_node_num_of_devices_args *args, remote_node_num_of_devices_ret *ret) @@ -4094,6 +4222,7 @@ static int remoteDispatchNodeListDevices (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_node_list_devices_args *args, remote_node_list_devices_ret *ret) @@ -4130,6 +4259,7 @@ static int remoteDispatchNodeDeviceLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_node_device_lookup_by_name_args *args, remote_node_device_lookup_by_name_ret *ret) @@ -4154,6 +4284,7 @@ static int remoteDispatchNodeDeviceDumpXml (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_node_device_dump_xml_args *args, remote_node_device_dump_xml_ret *ret) @@ -4183,6 +4314,7 @@ static int remoteDispatchNodeDeviceGetParent (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_node_device_get_parent_args *args, remote_node_device_get_parent_ret *ret) @@ -4225,6 +4357,7 @@ static int remoteDispatchNodeDeviceNumOfCaps (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_node_device_num_of_caps_args *args, remote_node_device_num_of_caps_ret *ret) @@ -4253,6 +4386,7 @@ static int remoteDispatchNodeDeviceListCaps (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_node_device_list_caps_args *args, remote_node_device_list_caps_ret *ret) @@ -4295,6 +4429,7 @@ static int remoteDispatchNodeDeviceDettach (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_node_device_dettach_args *args, void *ret ATTRIBUTE_UNUSED) @@ -4321,6 +4456,7 @@ static int remoteDispatchNodeDeviceReAttach (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_node_device_re_attach_args *args, void *ret ATTRIBUTE_UNUSED) @@ -4347,6 +4483,7 @@ static int remoteDispatchNodeDeviceReset (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_node_device_reset_args *args, void *ret ATTRIBUTE_UNUSED) @@ -4373,6 +4510,7 @@ static int remoteDispatchNodeDeviceCreateXml(struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_node_device_create_xml_args *args, remote_node_device_create_xml_ret *ret) @@ -4396,6 +4534,7 @@ static int remoteDispatchNodeDeviceDestroy(struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr, remote_node_device_destroy_args *args, void *ret ATTRIBUTE_UNUSED) @@ -4425,6 +4564,7 @@ static int remoteDispatchDomainEventsRegister (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr ATTRIBUTE_UNUSED, void *args ATTRIBUTE_UNUSED, remote_domain_events_register_ret *ret ATTRIBUTE_UNUSED) @@ -4444,6 +4584,7 @@ static int remoteDispatchDomainEventsDeregister (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client ATTRIBUTE_UNUSED, virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, remote_error *rerr ATTRIBUTE_UNUSED, void *args ATTRIBUTE_UNUSED, remote_domain_events_deregister_ret *ret ATTRIBUTE_UNUSED) diff --git a/qemud/remote.h b/qemud/remote.h index e3ee696..9baf045 100644 --- a/qemud/remote.h +++ b/qemud/remote.h @@ -54,6 +54,7 @@ typedef union { typedef int (*dispatch_fn) (struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, dispatch_args *args, dispatch_ret *ret); diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h index d9f6aad..a25ea16 100644 --- a/qemud/remote_dispatch_prototypes.h +++ b/qemud/remote_dispatch_prototypes.h @@ -6,6 +6,7 @@ static int remoteDispatchAuthList( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_auth_list_ret *ret); @@ -13,6 +14,7 @@ static int remoteDispatchAuthPolkit( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_auth_polkit_ret *ret); @@ -20,6 +22,7 @@ static int remoteDispatchAuthSaslInit( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_auth_sasl_init_ret *ret); @@ -27,6 +30,7 @@ static int remoteDispatchAuthSaslStart( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret); @@ -34,6 +38,7 @@ static int remoteDispatchAuthSaslStep( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret); @@ -41,6 +46,7 @@ static int remoteDispatchClose( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, void *ret); @@ -48,6 +54,7 @@ static int remoteDispatchDomainAttachDevice( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_attach_device_args *args, void *ret); @@ -55,6 +62,7 @@ static int remoteDispatchDomainBlockPeek( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_block_peek_args *args, remote_domain_block_peek_ret *ret); @@ -62,6 +70,7 @@ static int remoteDispatchDomainBlockStats( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_block_stats_args *args, remote_domain_block_stats_ret *ret); @@ -69,6 +78,7 @@ static int remoteDispatchDomainCoreDump( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_core_dump_args *args, void *ret); @@ -76,6 +86,7 @@ static int remoteDispatchDomainCreate( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_create_args *args, void *ret); @@ -83,6 +94,7 @@ static int remoteDispatchDomainCreateXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_create_xml_args *args, remote_domain_create_xml_ret *ret); @@ -90,6 +102,7 @@ static int remoteDispatchDomainDefineXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_define_xml_args *args, remote_domain_define_xml_ret *ret); @@ -97,6 +110,7 @@ static int remoteDispatchDomainDestroy( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_destroy_args *args, void *ret); @@ -104,6 +118,7 @@ static int remoteDispatchDomainDetachDevice( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_detach_device_args *args, void *ret); @@ -111,6 +126,7 @@ static int remoteDispatchDomainDumpXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_dump_xml_args *args, remote_domain_dump_xml_ret *ret); @@ -118,6 +134,7 @@ static int remoteDispatchDomainEventsDeregister( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_domain_events_deregister_ret *ret); @@ -125,6 +142,7 @@ static int remoteDispatchDomainEventsRegister( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_domain_events_register_ret *ret); @@ -132,6 +150,7 @@ static int remoteDispatchDomainGetAutostart( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_get_autostart_args *args, remote_domain_get_autostart_ret *ret); @@ -139,6 +158,7 @@ static int remoteDispatchDomainGetInfo( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_get_info_args *args, remote_domain_get_info_ret *ret); @@ -146,6 +166,7 @@ static int remoteDispatchDomainGetMaxMemory( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_get_max_memory_args *args, remote_domain_get_max_memory_ret *ret); @@ -153,6 +174,7 @@ static int remoteDispatchDomainGetMaxVcpus( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_get_max_vcpus_args *args, remote_domain_get_max_vcpus_ret *ret); @@ -160,6 +182,7 @@ static int remoteDispatchDomainGetOsType( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_get_os_type_args *args, remote_domain_get_os_type_ret *ret); @@ -167,6 +190,7 @@ static int remoteDispatchDomainGetSchedulerParameters( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_get_scheduler_parameters_args *args, remote_domain_get_scheduler_parameters_ret *ret); @@ -174,6 +198,7 @@ static int remoteDispatchDomainGetSchedulerType( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_get_scheduler_type_args *args, remote_domain_get_scheduler_type_ret *ret); @@ -181,6 +206,7 @@ static int remoteDispatchDomainGetSecurityLabel( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_get_security_label_args *args, remote_domain_get_security_label_ret *ret); @@ -188,6 +214,7 @@ static int remoteDispatchDomainGetVcpus( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_get_vcpus_args *args, remote_domain_get_vcpus_ret *ret); @@ -195,6 +222,7 @@ static int remoteDispatchDomainInterfaceStats( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_interface_stats_args *args, remote_domain_interface_stats_ret *ret); @@ -202,6 +230,7 @@ static int remoteDispatchDomainLookupById( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_lookup_by_id_args *args, remote_domain_lookup_by_id_ret *ret); @@ -209,6 +238,7 @@ static int remoteDispatchDomainLookupByName( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_lookup_by_name_args *args, remote_domain_lookup_by_name_ret *ret); @@ -216,6 +246,7 @@ static int remoteDispatchDomainLookupByUuid( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_lookup_by_uuid_args *args, remote_domain_lookup_by_uuid_ret *ret); @@ -223,6 +254,7 @@ static int remoteDispatchDomainMemoryPeek( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_memory_peek_args *args, remote_domain_memory_peek_ret *ret); @@ -230,6 +262,7 @@ static int remoteDispatchDomainMigrateFinish( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_migrate_finish_args *args, remote_domain_migrate_finish_ret *ret); @@ -237,6 +270,7 @@ static int remoteDispatchDomainMigrateFinish2( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_migrate_finish2_args *args, remote_domain_migrate_finish2_ret *ret); @@ -244,6 +278,7 @@ static int remoteDispatchDomainMigratePerform( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_migrate_perform_args *args, void *ret); @@ -251,6 +286,7 @@ static int remoteDispatchDomainMigratePrepare( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_migrate_prepare_args *args, remote_domain_migrate_prepare_ret *ret); @@ -258,6 +294,7 @@ static int remoteDispatchDomainMigratePrepare2( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_migrate_prepare2_args *args, remote_domain_migrate_prepare2_ret *ret); @@ -265,6 +302,7 @@ static int remoteDispatchDomainPinVcpu( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_pin_vcpu_args *args, void *ret); @@ -272,6 +310,7 @@ static int remoteDispatchDomainReboot( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_reboot_args *args, void *ret); @@ -279,6 +318,7 @@ static int remoteDispatchDomainRestore( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_restore_args *args, void *ret); @@ -286,6 +326,7 @@ static int remoteDispatchDomainResume( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_resume_args *args, void *ret); @@ -293,6 +334,7 @@ static int remoteDispatchDomainSave( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_save_args *args, void *ret); @@ -300,6 +342,7 @@ static int remoteDispatchDomainSetAutostart( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_set_autostart_args *args, void *ret); @@ -307,6 +350,7 @@ static int remoteDispatchDomainSetMaxMemory( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_set_max_memory_args *args, void *ret); @@ -314,6 +358,7 @@ static int remoteDispatchDomainSetMemory( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_set_memory_args *args, void *ret); @@ -321,6 +366,7 @@ static int remoteDispatchDomainSetSchedulerParameters( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_set_scheduler_parameters_args *args, void *ret); @@ -328,6 +374,7 @@ static int remoteDispatchDomainSetVcpus( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_set_vcpus_args *args, void *ret); @@ -335,6 +382,7 @@ static int remoteDispatchDomainShutdown( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_shutdown_args *args, void *ret); @@ -342,6 +390,7 @@ static int remoteDispatchDomainSuspend( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_suspend_args *args, void *ret); @@ -349,6 +398,7 @@ static int remoteDispatchDomainUndefine( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_undefine_args *args, void *ret); @@ -356,6 +406,7 @@ static int remoteDispatchDomainXmlFromNative( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_xml_from_native_args *args, remote_domain_xml_from_native_ret *ret); @@ -363,6 +414,7 @@ static int remoteDispatchDomainXmlToNative( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_domain_xml_to_native_args *args, remote_domain_xml_to_native_ret *ret); @@ -370,6 +422,7 @@ static int remoteDispatchFindStoragePoolSources( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_find_storage_pool_sources_args *args, remote_find_storage_pool_sources_ret *ret); @@ -377,6 +430,7 @@ static int remoteDispatchGetCapabilities( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_get_capabilities_ret *ret); @@ -384,6 +438,7 @@ static int remoteDispatchGetHostname( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_get_hostname_ret *ret); @@ -391,6 +446,7 @@ static int remoteDispatchGetMaxVcpus( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_get_max_vcpus_args *args, remote_get_max_vcpus_ret *ret); @@ -398,6 +454,7 @@ static int remoteDispatchGetType( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_get_type_ret *ret); @@ -405,6 +462,7 @@ static int remoteDispatchGetUri( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_get_uri_ret *ret); @@ -412,6 +470,7 @@ static int remoteDispatchGetVersion( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_get_version_ret *ret); @@ -419,6 +478,7 @@ static int remoteDispatchInterfaceCreate( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_interface_create_args *args, void *ret); @@ -426,6 +486,7 @@ static int remoteDispatchInterfaceDefineXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_interface_define_xml_args *args, remote_interface_define_xml_ret *ret); @@ -433,6 +494,7 @@ static int remoteDispatchInterfaceDestroy( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_interface_destroy_args *args, void *ret); @@ -440,6 +502,7 @@ static int remoteDispatchInterfaceGetXmlDesc( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_interface_get_xml_desc_args *args, remote_interface_get_xml_desc_ret *ret); @@ -447,6 +510,7 @@ static int remoteDispatchInterfaceLookupByMacString( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_interface_lookup_by_mac_string_args *args, remote_interface_lookup_by_mac_string_ret *ret); @@ -454,6 +518,7 @@ static int remoteDispatchInterfaceLookupByName( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_interface_lookup_by_name_args *args, remote_interface_lookup_by_name_ret *ret); @@ -461,6 +526,7 @@ static int remoteDispatchInterfaceUndefine( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_interface_undefine_args *args, void *ret); @@ -468,6 +534,7 @@ static int remoteDispatchListDefinedDomains( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_list_defined_domains_args *args, remote_list_defined_domains_ret *ret); @@ -475,6 +542,7 @@ static int remoteDispatchListDefinedInterfaces( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_list_defined_interfaces_args *args, remote_list_defined_interfaces_ret *ret); @@ -482,6 +550,7 @@ static int remoteDispatchListDefinedNetworks( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_list_defined_networks_args *args, remote_list_defined_networks_ret *ret); @@ -489,6 +558,7 @@ static int remoteDispatchListDefinedStoragePools( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_list_defined_storage_pools_args *args, remote_list_defined_storage_pools_ret *ret); @@ -496,6 +566,7 @@ static int remoteDispatchListDomains( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_list_domains_args *args, remote_list_domains_ret *ret); @@ -503,6 +574,7 @@ static int remoteDispatchListInterfaces( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_list_interfaces_args *args, remote_list_interfaces_ret *ret); @@ -510,6 +582,7 @@ static int remoteDispatchListNetworks( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_list_networks_args *args, remote_list_networks_ret *ret); @@ -517,6 +590,7 @@ static int remoteDispatchListStoragePools( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_list_storage_pools_args *args, remote_list_storage_pools_ret *ret); @@ -524,6 +598,7 @@ static int remoteDispatchNetworkCreate( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_network_create_args *args, void *ret); @@ -531,6 +606,7 @@ static int remoteDispatchNetworkCreateXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_network_create_xml_args *args, remote_network_create_xml_ret *ret); @@ -538,6 +614,7 @@ static int remoteDispatchNetworkDefineXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_network_define_xml_args *args, remote_network_define_xml_ret *ret); @@ -545,6 +622,7 @@ static int remoteDispatchNetworkDestroy( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_network_destroy_args *args, void *ret); @@ -552,6 +630,7 @@ static int remoteDispatchNetworkDumpXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_network_dump_xml_args *args, remote_network_dump_xml_ret *ret); @@ -559,6 +638,7 @@ static int remoteDispatchNetworkGetAutostart( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_network_get_autostart_args *args, remote_network_get_autostart_ret *ret); @@ -566,6 +646,7 @@ static int remoteDispatchNetworkGetBridgeName( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_network_get_bridge_name_args *args, remote_network_get_bridge_name_ret *ret); @@ -573,6 +654,7 @@ static int remoteDispatchNetworkLookupByName( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_network_lookup_by_name_args *args, remote_network_lookup_by_name_ret *ret); @@ -580,6 +662,7 @@ static int remoteDispatchNetworkLookupByUuid( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_network_lookup_by_uuid_args *args, remote_network_lookup_by_uuid_ret *ret); @@ -587,6 +670,7 @@ static int remoteDispatchNetworkSetAutostart( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_network_set_autostart_args *args, void *ret); @@ -594,6 +678,7 @@ static int remoteDispatchNetworkUndefine( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_network_undefine_args *args, void *ret); @@ -601,6 +686,7 @@ static int remoteDispatchNodeDeviceCreateXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_node_device_create_xml_args *args, remote_node_device_create_xml_ret *ret); @@ -608,6 +694,7 @@ static int remoteDispatchNodeDeviceDestroy( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_node_device_destroy_args *args, void *ret); @@ -615,6 +702,7 @@ static int remoteDispatchNodeDeviceDettach( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_node_device_dettach_args *args, void *ret); @@ -622,6 +710,7 @@ static int remoteDispatchNodeDeviceDumpXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_node_device_dump_xml_args *args, remote_node_device_dump_xml_ret *ret); @@ -629,6 +718,7 @@ static int remoteDispatchNodeDeviceGetParent( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_node_device_get_parent_args *args, remote_node_device_get_parent_ret *ret); @@ -636,6 +726,7 @@ static int remoteDispatchNodeDeviceListCaps( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_node_device_list_caps_args *args, remote_node_device_list_caps_ret *ret); @@ -643,6 +734,7 @@ static int remoteDispatchNodeDeviceLookupByName( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_node_device_lookup_by_name_args *args, remote_node_device_lookup_by_name_ret *ret); @@ -650,6 +742,7 @@ static int remoteDispatchNodeDeviceNumOfCaps( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_node_device_num_of_caps_args *args, remote_node_device_num_of_caps_ret *ret); @@ -657,6 +750,7 @@ static int remoteDispatchNodeDeviceReAttach( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_node_device_re_attach_args *args, void *ret); @@ -664,6 +758,7 @@ static int remoteDispatchNodeDeviceReset( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_node_device_reset_args *args, void *ret); @@ -671,6 +766,7 @@ static int remoteDispatchNodeGetCellsFreeMemory( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_node_get_cells_free_memory_args *args, remote_node_get_cells_free_memory_ret *ret); @@ -678,6 +774,7 @@ static int remoteDispatchNodeGetFreeMemory( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_node_get_free_memory_ret *ret); @@ -685,6 +782,7 @@ static int remoteDispatchNodeGetInfo( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_node_get_info_ret *ret); @@ -692,6 +790,7 @@ static int remoteDispatchNodeGetSecurityModel( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_node_get_security_model_ret *ret); @@ -699,6 +798,7 @@ static int remoteDispatchNodeListDevices( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_node_list_devices_args *args, remote_node_list_devices_ret *ret); @@ -706,6 +806,7 @@ static int remoteDispatchNodeNumOfDevices( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_node_num_of_devices_args *args, remote_node_num_of_devices_ret *ret); @@ -713,6 +814,7 @@ static int remoteDispatchNumOfDefinedDomains( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_num_of_defined_domains_ret *ret); @@ -720,6 +822,7 @@ static int remoteDispatchNumOfDefinedInterfaces( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_num_of_defined_interfaces_ret *ret); @@ -727,6 +830,7 @@ static int remoteDispatchNumOfDefinedNetworks( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_num_of_defined_networks_ret *ret); @@ -734,6 +838,7 @@ static int remoteDispatchNumOfDefinedStoragePools( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_num_of_defined_storage_pools_ret *ret); @@ -741,6 +846,7 @@ static int remoteDispatchNumOfDomains( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_num_of_domains_ret *ret); @@ -748,6 +854,7 @@ static int remoteDispatchNumOfInterfaces( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_num_of_interfaces_ret *ret); @@ -755,6 +862,7 @@ static int remoteDispatchNumOfNetworks( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_num_of_networks_ret *ret); @@ -762,6 +870,7 @@ static int remoteDispatchNumOfStoragePools( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, void *args, remote_num_of_storage_pools_ret *ret); @@ -769,6 +878,7 @@ static int remoteDispatchOpen( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_open_args *args, void *ret); @@ -776,6 +886,7 @@ static int remoteDispatchStoragePoolBuild( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_build_args *args, void *ret); @@ -783,6 +894,7 @@ static int remoteDispatchStoragePoolCreate( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_create_args *args, void *ret); @@ -790,6 +902,7 @@ static int remoteDispatchStoragePoolCreateXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_create_xml_args *args, remote_storage_pool_create_xml_ret *ret); @@ -797,6 +910,7 @@ static int remoteDispatchStoragePoolDefineXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_define_xml_args *args, remote_storage_pool_define_xml_ret *ret); @@ -804,6 +918,7 @@ static int remoteDispatchStoragePoolDelete( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_delete_args *args, void *ret); @@ -811,6 +926,7 @@ static int remoteDispatchStoragePoolDestroy( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_destroy_args *args, void *ret); @@ -818,6 +934,7 @@ static int remoteDispatchStoragePoolDumpXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_dump_xml_args *args, remote_storage_pool_dump_xml_ret *ret); @@ -825,6 +942,7 @@ static int remoteDispatchStoragePoolGetAutostart( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_get_autostart_args *args, remote_storage_pool_get_autostart_ret *ret); @@ -832,6 +950,7 @@ static int remoteDispatchStoragePoolGetInfo( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_get_info_args *args, remote_storage_pool_get_info_ret *ret); @@ -839,6 +958,7 @@ static int remoteDispatchStoragePoolListVolumes( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_list_volumes_args *args, remote_storage_pool_list_volumes_ret *ret); @@ -846,6 +966,7 @@ static int remoteDispatchStoragePoolLookupByName( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_lookup_by_name_args *args, remote_storage_pool_lookup_by_name_ret *ret); @@ -853,6 +974,7 @@ static int remoteDispatchStoragePoolLookupByUuid( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_lookup_by_uuid_args *args, remote_storage_pool_lookup_by_uuid_ret *ret); @@ -860,6 +982,7 @@ static int remoteDispatchStoragePoolLookupByVolume( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_lookup_by_volume_args *args, remote_storage_pool_lookup_by_volume_ret *ret); @@ -867,6 +990,7 @@ static int remoteDispatchStoragePoolNumOfVolumes( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_num_of_volumes_args *args, remote_storage_pool_num_of_volumes_ret *ret); @@ -874,6 +998,7 @@ static int remoteDispatchStoragePoolRefresh( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_refresh_args *args, void *ret); @@ -881,6 +1006,7 @@ static int remoteDispatchStoragePoolSetAutostart( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_set_autostart_args *args, void *ret); @@ -888,6 +1014,7 @@ static int remoteDispatchStoragePoolUndefine( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_pool_undefine_args *args, void *ret); @@ -895,6 +1022,7 @@ static int remoteDispatchStorageVolCreateXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_vol_create_xml_args *args, remote_storage_vol_create_xml_ret *ret); @@ -902,6 +1030,7 @@ static int remoteDispatchStorageVolCreateXmlFrom( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_vol_create_xml_from_args *args, remote_storage_vol_create_xml_from_ret *ret); @@ -909,6 +1038,7 @@ static int remoteDispatchStorageVolDelete( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_vol_delete_args *args, void *ret); @@ -916,6 +1046,7 @@ static int remoteDispatchStorageVolDumpXml( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_vol_dump_xml_args *args, remote_storage_vol_dump_xml_ret *ret); @@ -923,6 +1054,7 @@ static int remoteDispatchStorageVolGetInfo( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_vol_get_info_args *args, remote_storage_vol_get_info_ret *ret); @@ -930,6 +1062,7 @@ static int remoteDispatchStorageVolGetPath( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_vol_get_path_args *args, remote_storage_vol_get_path_ret *ret); @@ -937,6 +1070,7 @@ static int remoteDispatchStorageVolLookupByKey( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_vol_lookup_by_key_args *args, remote_storage_vol_lookup_by_key_ret *ret); @@ -944,6 +1078,7 @@ static int remoteDispatchStorageVolLookupByName( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_vol_lookup_by_name_args *args, remote_storage_vol_lookup_by_name_ret *ret); @@ -951,6 +1086,7 @@ static int remoteDispatchStorageVolLookupByPath( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_storage_vol_lookup_by_path_args *args, remote_storage_vol_lookup_by_path_ret *ret); @@ -958,6 +1094,7 @@ static int remoteDispatchSupportsFeature( struct qemud_server *server, struct qemud_client *client, virConnectPtr conn, + remote_message_header *hdr, remote_error *err, remote_supports_feature_args *args, remote_supports_feature_ret *ret); diff --git a/qemud/remote_generate_stubs.pl b/qemud/remote_generate_stubs.pl index 9bca0cc..8b26e3d 100755 --- a/qemud/remote_generate_stubs.pl +++ b/qemud/remote_generate_stubs.pl @@ -115,6 +115,7 @@ elsif ($opt_p) { print " struct qemud_server *server,\n"; print " struct qemud_client *client,\n"; print " virConnectPtr conn,\n"; + print " remote_message_header *hdr,\n"; print " remote_error *err,\n"; print " $calls{$_}->{args} *args,\n"; print " $calls{$_}->{ret} *ret);\n"; -- 1.6.2.5

* qemud/remote.c: Implement remoteDispatchPutFile and remoteDispatchGetFile API handlers * qemud/remote_protocol.x: Define wire protocol for the virConnectGetFile/PutFile APIs * qemud/remote_protocol.c, qemud/remote_protocol.h, qemud/remote_dispatch_table.h, qemud/remote_dispatch_prototypes.h, qemud/remote_dispatch_args.h: Re-generate * src/remote_internal.c: Client side of get/put APIs --- qemud/remote.c | 64 ++++++++++++++++++++++++++++++ qemud/remote_dispatch_args.h | 2 + qemud/remote_dispatch_prototypes.h | 16 +++++++ qemud/remote_dispatch_table.h | 10 +++++ qemud/remote_protocol.c | 18 ++++++++ qemud/remote_protocol.h | 16 +++++++ qemud/remote_protocol.x | 13 +++++- src/remote_internal.c | 77 ++++++++++++++++++++++++++++++++++-- 8 files changed, 210 insertions(+), 6 deletions(-) diff --git a/qemud/remote.c b/qemud/remote.c index 3034023..ff060b3 100644 --- a/qemud/remote.c +++ b/qemud/remote.c @@ -55,6 +55,7 @@ #include "datatypes.h" #include "memory.h" #include "util.h" +#include "stream.h" #define VIR_FROM_THIS VIR_FROM_REMOTE #define REMOTE_DEBUG(fmt, ...) DEBUG(fmt, __VA_ARGS__) @@ -4554,6 +4555,69 @@ remoteDispatchNodeDeviceDestroy(struct qemud_server *server ATTRIBUTE_UNUSED, return 0; } +static int remoteDispatchPutFile(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *rerr, + remote_put_file_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + struct qemud_client_stream *stream; + + stream = remoteCreateClientStream(conn, hdr); + if (!stream) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + if (virConnectPutFile(conn, args->name, stream->st) < 0) { + remoteDispatchConnError(rerr, conn); + remoteFreeClientStream(client, stream); + return -1; + } + + if (remoteAddClientStream(client, stream) < 0) { + remoteDispatchConnError(rerr, conn); + virStreamAbort(stream->st); + remoteFreeClientStream(client, stream); + return -1; + } + + return 0; +} + +static int remoteDispatchGetFile(struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *rerr, + remote_get_file_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + struct qemud_client_stream *stream; + + stream = remoteCreateClientStream(conn, hdr); + if (!stream) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + if (virConnectGetFile(conn, args->name, stream->st) < 0) { + remoteDispatchConnError(rerr, conn); + remoteFreeClientStream(client, stream); + return -1; + } + + if (remoteAddClientStream(client, stream) < 0) { + remoteDispatchConnError(rerr, conn); + virStreamAbort(stream->st); + remoteFreeClientStream(client, stream); + return -1; + } + + return 0; +} diff --git a/qemud/remote_dispatch_args.h b/qemud/remote_dispatch_args.h index 9dacfb8..e0415cb 100644 --- a/qemud/remote_dispatch_args.h +++ b/qemud/remote_dispatch_args.h @@ -117,3 +117,5 @@ remote_domain_xml_from_native_args val_remote_domain_xml_from_native_args; remote_domain_xml_to_native_args val_remote_domain_xml_to_native_args; remote_list_defined_interfaces_args val_remote_list_defined_interfaces_args; + remote_put_file_args val_remote_put_file_args; + remote_get_file_args val_remote_get_file_args; diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h index a25ea16..1814aba 100644 --- a/qemud/remote_dispatch_prototypes.h +++ b/qemud/remote_dispatch_prototypes.h @@ -434,6 +434,14 @@ static int remoteDispatchGetCapabilities( remote_error *err, void *args, remote_get_capabilities_ret *ret); +static int remoteDispatchGetFile( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_get_file_args *args, + void *ret); static int remoteDispatchGetHostname( struct qemud_server *server, struct qemud_client *client, @@ -882,6 +890,14 @@ static int remoteDispatchOpen( remote_error *err, remote_open_args *args, void *ret); +static int remoteDispatchPutFile( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_put_file_args *args, + void *ret); static int remoteDispatchStoragePoolBuild( struct qemud_server *server, struct qemud_client *client, diff --git a/qemud/remote_dispatch_table.h b/qemud/remote_dispatch_table.h index 449786d..1325ac5 100644 --- a/qemud/remote_dispatch_table.h +++ b/qemud/remote_dispatch_table.h @@ -697,3 +697,13 @@ .args_filter = (xdrproc_t) xdr_remote_list_defined_interfaces_args, .ret_filter = (xdrproc_t) xdr_remote_list_defined_interfaces_ret, }, +{ /* PutFile => 139 */ + .fn = (dispatch_fn) remoteDispatchPutFile, + .args_filter = (xdrproc_t) xdr_remote_put_file_args, + .ret_filter = (xdrproc_t) xdr_void, +}, +{ /* GetFile => 140 */ + .fn = (dispatch_fn) remoteDispatchGetFile, + .args_filter = (xdrproc_t) xdr_remote_get_file_args, + .ret_filter = (xdrproc_t) xdr_void, +}, diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c index 7b46096..279a590 100644 --- a/qemud/remote_protocol.c +++ b/qemud/remote_protocol.c @@ -2534,6 +2534,24 @@ xdr_remote_domain_xml_to_native_ret (XDR *xdrs, remote_domain_xml_to_native_ret } bool_t +xdr_remote_put_file_args (XDR *xdrs, remote_put_file_args *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_get_file_args (XDR *xdrs, remote_get_file_args *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h index 2ff9075..394b982 100644 --- a/qemud/remote_protocol.h +++ b/qemud/remote_protocol.h @@ -1429,6 +1429,16 @@ struct remote_domain_xml_to_native_ret { remote_nonnull_string nativeConfig; }; typedef struct remote_domain_xml_to_native_ret remote_domain_xml_to_native_ret; + +struct remote_put_file_args { + remote_nonnull_string name; +}; +typedef struct remote_put_file_args remote_put_file_args; + +struct remote_get_file_args { + remote_nonnull_string name; +}; +typedef struct remote_get_file_args remote_get_file_args; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -1571,6 +1581,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_XML_TO_NATIVE = 136, REMOTE_PROC_NUM_OF_DEFINED_INTERFACES = 137, REMOTE_PROC_LIST_DEFINED_INTERFACES = 138, + REMOTE_PROC_PUT_FILE = 139, + REMOTE_PROC_GET_FILE = 140, }; typedef enum remote_procedure remote_procedure; @@ -1835,6 +1847,8 @@ extern bool_t xdr_remote_domain_xml_from_native_args (XDR *, remote_domain_xml_ extern bool_t xdr_remote_domain_xml_from_native_ret (XDR *, remote_domain_xml_from_native_ret*); extern bool_t xdr_remote_domain_xml_to_native_args (XDR *, remote_domain_xml_to_native_args*); extern bool_t xdr_remote_domain_xml_to_native_ret (XDR *, remote_domain_xml_to_native_ret*); +extern bool_t xdr_remote_put_file_args (XDR *, remote_put_file_args*); +extern bool_t xdr_remote_get_file_args (XDR *, remote_get_file_args*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_type (XDR *, remote_message_type*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -2073,6 +2087,8 @@ extern bool_t xdr_remote_domain_xml_from_native_args (); extern bool_t xdr_remote_domain_xml_from_native_ret (); extern bool_t xdr_remote_domain_xml_to_native_args (); extern bool_t xdr_remote_domain_xml_to_native_ret (); +extern bool_t xdr_remote_put_file_args (); +extern bool_t xdr_remote_get_file_args (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_type (); extern bool_t xdr_remote_message_status (); diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x index e24c428..42c71ca 100644 --- a/qemud/remote_protocol.x +++ b/qemud/remote_protocol.x @@ -1272,6 +1272,14 @@ struct remote_domain_xml_to_native_ret { }; +struct remote_put_file_args { + remote_nonnull_string name; +}; + +struct remote_get_file_args { + remote_nonnull_string name; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -1428,9 +1436,10 @@ enum remote_procedure { REMOTE_PROC_INTERFACE_DESTROY = 134, REMOTE_PROC_DOMAIN_XML_FROM_NATIVE = 135, REMOTE_PROC_DOMAIN_XML_TO_NATIVE = 136, - REMOTE_PROC_NUM_OF_DEFINED_INTERFACES = 137, - REMOTE_PROC_LIST_DEFINED_INTERFACES = 138 + REMOTE_PROC_LIST_DEFINED_INTERFACES = 138, + REMOTE_PROC_PUT_FILE = 139, + REMOTE_PROC_GET_FILE = 140 }; diff --git a/src/remote_internal.c b/src/remote_internal.c index c8ea1a3..8b1d1c6 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -6348,7 +6348,6 @@ done: } -#if 0 static struct private_stream_data * remoteStreamOpen(virStreamPtr st, int output ATTRIBUTE_UNUSED, @@ -6699,7 +6698,77 @@ static virStreamDriver remoteStreamDrv = { .streamUpdateCallback = remoteStreamEventUpdateCallback, .streamRemoveCallback = remoteStreamEventRemoveCallback, }; -#endif + +static int +remoteConnectPutFile(virConnectPtr conn, + const char *name, + virStreamPtr st) +{ + struct private_data *priv = conn->privateData; + struct private_stream_data *privst = NULL; + int rv = -1; + remote_put_file_args args; + + remoteDriverLock(priv); + + if (!(privst = remoteStreamOpen(st, 1, REMOTE_PROC_PUT_FILE, priv->counter))) + goto done; + + st->driver = &remoteStreamDrv; + st->privateData = privst; + + args.name = (char *) name; + + if (call (conn, priv, 0, REMOTE_PROC_PUT_FILE, + (xdrproc_t) xdr_remote_put_file_args, (char *) &args, + (xdrproc_t) xdr_void, NULL) == -1) { + remoteStreamRelease(st); + goto done; + } + + rv = 0; + +done: + remoteDriverUnlock(priv); + + return rv; +} + + +static int +remoteConnectGetFile(virConnectPtr conn, + const char *name, + virStreamPtr st) +{ + struct private_data *priv = conn->privateData; + struct private_stream_data *privst = NULL; + int rv = -1; + remote_put_file_args args; + + remoteDriverLock(priv); + + if (!(privst = remoteStreamOpen(st, 1, REMOTE_PROC_GET_FILE, priv->counter))) + goto done; + + st->driver = &remoteStreamDrv; + st->privateData = privst; + + args.name = (char *) name; + + if (call (conn, priv, 0, REMOTE_PROC_GET_FILE, + (xdrproc_t) xdr_remote_put_file_args, (char *) &args, + (xdrproc_t) xdr_void, NULL) == -1) { + remoteStreamRelease(st); + goto done; + } + + rv = 0; + +done: + remoteDriverUnlock(priv); + + return rv; +} /*----------------------------------------------------------------------*/ @@ -8047,8 +8116,8 @@ static virDriver remote_driver = { remoteNodeDeviceDettach, /* nodeDeviceDettach */ remoteNodeDeviceReAttach, /* nodeDeviceReAttach */ remoteNodeDeviceReset, /* nodeDeviceReset */ - NULL, - NULL, + remoteConnectPutFile, + remoteConnectGetFile, }; static virNetworkDriver network_driver = { -- 1.6.2.5

Daniel P. Berrange wrote:
The immediate use case for this data stream code is Chris' QEMU migration patchset.
The next use case is to allow serial console access to be tunnelled over libvirtd, eg to make 'virsh console GUEST' work remotely. This use case is why I included the support for non-blocking data streams and event loop integration (not required for Chris' migration use case)
Anyway, assuming Chris confirms that I've not broken his code, then patches 1-6 are targetted for this next release.
I'm sorry for the very long delay in getting back to this. I've been playing around with my tunnelled migration patches on top of this code, and I just can't seem to make the new nonblocking stuff work properly. I'm getting a couple of behaviors that are highly undesirable: 1) Immediately after starting the stream, I get a virStreamRecv() callback on the destination side. The problem is that this is wrong for migration; there's no data that I can read *from* the destination qemu process which makes any sense. While I could implement the method and just throw away the data, that doesn't seem right to me. This leads to... 2) A crash in libvirtd on the source side of the destination. It doesn't happen every single time, but when it has happened I've traced it down to the fact that src/remote_internal.c:remoteDomainEventFired() can get called *after* conn->privateData has been set to NULL, leading to a SEGV on NULL pointer dereference. I can provide a core-dump for this if needed. 3) (minor) The python bindings refuse to build with these patches in place. It's probably just a matter of fixing up the generator.py, but it needs to be done. I've uploaded the code that I'm trying out at the moment to: http://gitorious.org/~clalance/libvirt/clalance-staging/commits/tunnelled-mi... Dan, can you take a look and make any suggestions about where I might be going wrong? If you want to test out the tunnelled migration yourself, you'll need to make sure you at least have the "exec non-blocking patch" (i.e. c/s 907500095851230a480b14bc852c4e49d32cb16d from the upstream qemu repo) in place. I have a qemu F-11 package with this patch in it available at: http://people.redhat.com/clalance/qemu-exec-nonblock -- Chris Lalancette

On Tue, Sep 15, 2009 at 02:35:02PM +0200, Chris Lalancette wrote:
I've uploaded the code that I'm trying out at the moment to:
http://gitorious.org/~clalance/libvirt/clalance-staging/commits/tunnelled-mi...
Dan, can you take a look and make any suggestions about where I might be going wrong?
I've not look at your migration code yet, but there's a mistake in your change to the test driver. http://gitorious.org/~clalance/libvirt/clalance-staging/commit/e77dc1f1ba4e1... The test driver is delibrately not using saferead/write because those helpers do not handle EAGAIN. If you get EGAIN they'll return -1 and you are left with no idea how much data you've written which is not helpful :-) At very least this will cause the stream to terminate with an error message. If I got something wrong, perhaps its causing a crash. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Daniel P. Berrange wrote:
On Tue, Sep 15, 2009 at 02:35:02PM +0200, Chris Lalancette wrote:
I've uploaded the code that I'm trying out at the moment to:
http://gitorious.org/~clalance/libvirt/clalance-staging/commits/tunnelled-mi...
Dan, can you take a look and make any suggestions about where I might be going wrong?
I've not look at your migration code yet, but there's a mistake in your change to the test driver.
http://gitorious.org/~clalance/libvirt/clalance-staging/commit/e77dc1f1ba4e1...
The test driver is delibrately not using saferead/write because those helpers do not handle EAGAIN. If you get EGAIN they'll return -1 and you are left with no idea how much data you've written which is not helpful :-) At very least this will cause the stream to terminate with an error message. If I got something wrong, perhaps its causing a crash.
Ah, I see. I've switched that back, and switched over my tunnelled implementation as well, but it doesn't seem to have an effect on my problem. -- Chris Lalancette

On Wed, Sep 16, 2009 at 09:35:21AM +0200, Chris Lalancette wrote:
Daniel P. Berrange wrote:
On Tue, Sep 15, 2009 at 02:35:02PM +0200, Chris Lalancette wrote:
I've uploaded the code that I'm trying out at the moment to:
http://gitorious.org/~clalance/libvirt/clalance-staging/commits/tunnelled-mi...
Dan, can you take a look and make any suggestions about where I might be going wrong?
I've not look at your migration code yet, but there's a mistake in your change to the test driver.
http://gitorious.org/~clalance/libvirt/clalance-staging/commit/e77dc1f1ba4e1...
The test driver is delibrately not using saferead/write because those helpers do not handle EAGAIN. If you get EGAIN they'll return -1 and you are left with no idea how much data you've written which is not helpful :-) At very least this will cause the stream to terminate with an error message. If I got something wrong, perhaps its causing a crash.
Ah, I see. I've switched that back, and switched over my tunnelled implementation as well, but it doesn't seem to have an effect on my problem.
I still see a safewrite() in the your virStreamWrite() impl in the code currently pushed to gitorious.or, but perhaps you've changed that locally already. The other thing is that if the stream open flags included VIR_STREAM_NONBLOCK, you must make sur eyou put your socket in non-blocking mode, eg if ((st->flags & VIR_STREAM_NONBLOCK) && virSetNonBlock(create ? fds[1] : fds[0]) < 0) { virReportSystemError(st->conn, errno, "%s", _("cannot make stream non-blocking")); goto error; } in your stream open method. That shouldn't have caused a crash though - it would merely make libvirtd non-responsive for a while it QEMU blocked the incoming migration socket. All in all though the code looks reasonable and I don't see any obvious problems. I'll have to try running it to see if any crash appears.... Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Daniel P. Berrange wrote:
I still see a safewrite() in the your virStreamWrite() impl in the code currently pushed to gitorious.or, but perhaps you've changed that locally already. The other thing is that if the stream open
Yeah, sorry, I just never pushed it up to gitorious. I'll make the changes along with the virSetNonBlock() and push it up there, probably tomorrow.
flags included VIR_STREAM_NONBLOCK, you must make sur eyou put your socket in non-blocking mode, eg
if ((st->flags & VIR_STREAM_NONBLOCK) && virSetNonBlock(create ? fds[1] : fds[0]) < 0) { virReportSystemError(st->conn, errno, "%s", _("cannot make stream non-blocking")); goto error; }
in your stream open method. That shouldn't have caused a crash though - it would merely make libvirtd non-responsive for a while it QEMU blocked the incoming migration socket.
All in all though the code looks reasonable and I don't see any obvious problems. I'll have to try running it to see if any crash appears....
Thanks. -- Chris Lalancette

On Tue, Sep 15, 2009 at 02:35:02PM +0200, Chris Lalancette wrote:
Daniel P. Berrange wrote:
The immediate use case for this data stream code is Chris' QEMU migration patchset.
The next use case is to allow serial console access to be tunnelled over libvirtd, eg to make 'virsh console GUEST' work remotely. This use case is why I included the support for non-blocking data streams and event loop integration (not required for Chris' migration use case)
Anyway, assuming Chris confirms that I've not broken his code, then patches 1-6 are targetted for this next release.
I'm sorry for the very long delay in getting back to this. I've been playing around with my tunnelled migration patches on top of this code, and I just can't seem to make the new nonblocking stuff work properly. I'm getting a couple of behaviors that are highly undesirable:
1) Immediately after starting the stream, I get a virStreamRecv() callback on the destination side. The problem is that this is wrong for migration; there's no data that I can read *from* the destination qemu process which makes any sense. While I could implement the method and just throw away the data, that doesn't seem right to me. This leads to...
I realize this is due to the remoteAddClientStream() method in qemud/stream.c. It unconditionally sets 'stream->tx' to 1. I didn't notice the problem myself, since the test driver is using pipes which are unidirectional, but yor UNIX domain socket is bi-directional. We could either add a flag to remoteAddClientStream() to indicate whether the stream should allow read or write or both. Or you might be able to call shutdown(sockfd, SHUT_RD) on your UNIX socket to indicate that its only going to be used for write effectively making it unidirectional. A 3rd option is to define more flags for virStreamNew(), one for READ, one for WRITE, and have the remote daemon pay attention to these
2) A crash in libvirtd on the source side of the destination. It doesn't happen every single time, but when it has happened I've traced it down to the fact that src/remote_internal.c:remoteDomainEventFired() can get called *after* conn->privateData has been set to NULL, leading to a SEGV on NULL pointer dereference. I can provide a core-dump for this if needed.
I don't have any explanation for this - its a little wierd and we ought to try and figure it out if possible.
3) (minor) The python bindings refuse to build with these patches in place.
Yeah I completely forgot to add rules for virSecret APIs there Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Daniel P. Berrange wrote:
1) Immediately after starting the stream, I get a virStreamRecv() callback on the destination side. The problem is that this is wrong for migration; there's no data that I can read *from* the destination qemu process which makes any sense. While I could implement the method and just throw away the data, that doesn't seem right to me. This leads to...
I realize this is due to the remoteAddClientStream() method in qemud/stream.c. It unconditionally sets 'stream->tx' to 1. I didn't notice the problem myself, since the test driver is using pipes which are unidirectional, but yor UNIX domain socket is bi-directional.
We could either add a flag to remoteAddClientStream() to indicate whether the stream should allow read or write or both. Or you might be able to call shutdown(sockfd, SHUT_RD) on your UNIX socket to indicate that its only going to be used for write effectively making it unidirectional.
I tried the shutdown(sockfd, SHUT_RD) method, without success. Then I commented out the stream->tx = 1 line as a test, and the migration (mostly) worked. At least, it transferred the data to the other side, at which point trying to "virsh console" on the destination side caused a libvirtd segfault again. So your idea is correct, although I think we still have a problem with the cleanup of the stream. I'm still debugging that. -- Chris Lalancette

In looking at your migration patches I realized we could tweak things a little bit to allow the implementation of a new style migration API which does not require the destination virConnectPtr object. More importantly this could be used independantly of the tunnelled migration. So the patch that follows takes the public API bits of your migration code and adds a new flag VIR_MIGRATE_PEER2PEER, and virDomainMigrateToURI method. It implements it for the Xen driver. For the QEMU driver you already have code which copes with the combination of VIR_MIGRATE_PEER2PEER + VIR_MIGRATE_TUNNELLED. We could easily adapt that to also cope with doing a migration using VIR_MIGRATE_PEER2PEER on its own. ie, source libvirtd opens connection to destination libvirtd, runs the existing prepare method, then uses QEMU monitor todo a plain TCP migration and then invokes the existing finish method. This patch applies between the data streams code & your migration code - it'll clash a little - it ought to cover everything you have relating to the public API with exception of the new internal method virDomainMigratePrepareTunnel Regards, Daniel

Introduces several new public API options for migration - VIR_MIGRATE_PEER2PEER: With this flag the client only invokes the virDomainMigratePerform method, expecting the source host driver to do whatever is required to complete the entire migration process. - VIR_MIGRATE_TUNNELLED: With this flag the actual data for migration will be tunnelled over the libvirtd RPC channel. This requires that VIR_MIGRATE_PEER2PEER is also set. - virDomainMigrateToURI: This is variant of the existing virDomainMigrate method which can be used when the VIR_MIGRATE_PEER2PEER flag is set. The benefit of this method is that no virConnectPtr object is required for the destination host, only a destination URI. The URI for VIR_MIGRATE_TUNNELLED must be a valid libvirt URI. For non-tunnelled migration a hypervisor specific migration URI is used. * include/libvirt/libvirt.h, include/libvirt/libvirt.h.in: Add VIR_MIGRATE_PEER2PEER, VIR_MIGRATE_TUNNELLED and virDomainMigrateToURI. * src/driver.h: Remove feature flags * src/libvirt_internal.h: Add feature flags, and include new VIR_FEATURE_MIGRATE_P2P indicating support for the new VIR_MIGRATE_PEER2PEER mode. * src/libvirt.c: Implement support for VIR_MIGRATE_PEER2PEER and virDomainMigrateToURI APIs. * src/virsh.c: Add --p2p and --tunnelled args and use the new virDomainMigrateToURI method where possible. * src/xen_unified.c: Advertise support for P2P migration * src/xend_internal.c: Accept VIR_MIGRATE_PEER2PEER flag. * src/libvirt_public.syms: Export virDomainMigrateToURI method --- include/libvirt/libvirt.h | 8 ++- include/libvirt/libvirt.h.in | 8 ++- src/driver.h | 19 ---- src/internal.h | 2 + src/libvirt.c | 224 +++++++++++++++++++++++++++++++++++++----- src/libvirt_internal.h | 30 ++++++ src/libvirt_public.syms | 1 + src/virsh.c | 42 ++++++-- src/xen_unified.c | 7 +- src/xend_internal.c | 6 + 10 files changed, 286 insertions(+), 61 deletions(-) diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 10ec04a..1745396 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -336,7 +336,9 @@ typedef virDomainInterfaceStatsStruct *virDomainInterfaceStatsPtr; /* Domain migration flags. */ typedef enum { - VIR_MIGRATE_LIVE = 1, /* live migration */ + VIR_MIGRATE_LIVE = (1 << 0), /* live migration */ + VIR_MIGRATE_PEER2PEER = (1 << 1), /* direct source -> dest host control channel */ + VIR_MIGRATE_TUNNELLED = (1 << 2), /* tunnel migration data over libvirtd connection */ } virDomainMigrateFlags; /* Domain migration. */ @@ -344,6 +346,10 @@ virDomainPtr virDomainMigrate (virDomainPtr domain, virConnectPtr dconn, unsigned long flags, const char *dname, const char *uri, unsigned long bandwidth); +int virDomainMigrateToURI (virDomainPtr domain, const char *duri, + unsigned long flags, const char *dname, + unsigned long bandwidth); + /** * VIR_NODEINFO_MAXCPUS: * @nodeinfo: virNodeInfo instance diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 012d30e..5e1a500 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -336,7 +336,9 @@ typedef virDomainInterfaceStatsStruct *virDomainInterfaceStatsPtr; /* Domain migration flags. */ typedef enum { - VIR_MIGRATE_LIVE = 1, /* live migration */ + VIR_MIGRATE_LIVE = (1 << 0), /* live migration */ + VIR_MIGRATE_PEER2PEER = (1 << 1), /* direct source -> dest host control channel */ + VIR_MIGRATE_TUNNELLED = (1 << 2), /* tunnel migration data over libvirtd connection */ } virDomainMigrateFlags; /* Domain migration. */ @@ -344,6 +346,10 @@ virDomainPtr virDomainMigrate (virDomainPtr domain, virConnectPtr dconn, unsigned long flags, const char *dname, const char *uri, unsigned long bandwidth); +int virDomainMigrateToURI (virDomainPtr domain, const char *duri, + unsigned long flags, const char *dname, + unsigned long bandwidth); + /** * VIR_NODEINFO_MAXCPUS: * @nodeinfo: virNodeInfo instance diff --git a/src/driver.h b/src/driver.h index 6a3dcc2..893e98b 100644 --- a/src/driver.h +++ b/src/driver.h @@ -44,25 +44,6 @@ typedef enum { VIR_DRV_OPEN_ERROR = -2, } virDrvOpenStatus; -/* Feature detection. This is a libvirt-private interface for determining - * what features are supported by the driver. - * - * The remote driver passes features through to the real driver at the - * remote end unmodified, except if you query a VIR_DRV_FEATURE_REMOTE* - * feature. - */ - /* Driver supports V1-style virDomainMigrate, ie. domainMigratePrepare/ - * domainMigratePerform/domainMigrateFinish. - */ -#define VIR_DRV_FEATURE_MIGRATION_V1 1 - - /* Driver is not local. */ -#define VIR_DRV_FEATURE_REMOTE 2 - - /* Driver supports V2-style virDomainMigrate, ie. domainMigratePrepare2/ - * domainMigratePerform/domainMigrateFinish2. - */ -#define VIR_DRV_FEATURE_MIGRATION_V2 3 /* Internal feature-detection macro. Don't call drv->supports_feature * directly, because it may be NULL, use this macro instead. diff --git a/src/internal.h b/src/internal.h index 8fa579c..bd1cfe6 100644 --- a/src/internal.h +++ b/src/internal.h @@ -24,6 +24,8 @@ #include "libvirt/libvirt.h" #include "libvirt/virterror.h" +#include "libvirt_internal.h" + /* On architectures which lack these limits, define them (ie. Cygwin). * Note that the libvirt code should be robust enough to handle the * case where actual value is longer than these limits (eg. by setting diff --git a/src/libvirt.c b/src/libvirt.c index 124e5db..481afec 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -35,7 +35,6 @@ #include "virterror_internal.h" #include "logging.h" #include "datatypes.h" -#include "libvirt_internal.h" #include "driver.h" #include "uuid.h" @@ -3059,6 +3058,38 @@ virDomainMigrateVersion2 (virDomainPtr domain, return ddomain; } + + /* + * This is sort of a migration v3 + * + * This performs a peer-2-peer migration where source host + * does all the communication with the destination host. + */ +static int +virDomainMigrateP2P (virDomainPtr domain, + unsigned long flags, + const char *dname, + const char *uri, + unsigned long bandwidth) +{ + if (!domain->conn->driver->domainMigratePerform) { + virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + } + + /* Perform the migration. The driver isn't supposed to return + * until the migration is complete. + */ + return domain->conn->driver->domainMigratePerform(domain, + NULL, /* cookie */ + 0, /* cookielen */ + uri, + flags, + dname, + bandwidth); +} + + /** * virDomainMigrate: * @domain: a domain object @@ -3072,22 +3103,34 @@ virDomainMigrateVersion2 (virDomainPtr domain, * host given by dconn (a connection to the destination host). * * Flags may be one of more of the following: - * VIR_MIGRATE_LIVE Attempt a live migration. + * VIR_MIGRATE_LIVE Do not pause the VM during migration + * VIR_MIGRATE_PEER2PEER Direct connection between source & destination hosts + * VIR_MIGRATE_TUNNELLED Tunnel migration data over the libvirt RPC channel + * + * VIR_MIGRATE_TUNNELLED requires that VIR_MIGRATE_PEER2PEER be set. + * Applications using the VIR_MIGRATE_PEER2PEER flag will probably + * prefer to invoke virDomainMigrateToURI, avoiding the need to + * open connection to the destination host themselves. * * If a hypervisor supports renaming domains during migration, * then you may set the dname parameter to the new name (otherwise * it keeps the same name). If this is not supported by the * hypervisor, dname must be NULL or else you will get an error. * - * Since typically the two hypervisors connect directly to each - * other in order to perform the migration, you may need to specify - * a path from the source to the destination. This is the purpose - * of the uri parameter. If uri is NULL, then libvirt will try to - * find the best method. Uri may specify the hostname or IP address - * of the destination host as seen from the source. Or uri may be - * a URI giving transport, hostname, user, port, etc. in the usual - * form. Refer to driver documentation for the particular URIs - * supported. + * If the VIR_MIGRATE_TUNNELLED flag is set, the uri parameter + * must be a valid libvirt connection URI, by which the source + * libvirt driver can connect to the destination libvirt. If + * omitted, the dconn connection object will be queried for its + * current URI. + * + * If the VIR_MIGRATE_TUNNELED flag is NOT set, the URI parameter + * takes a hypervisor specific format. The hypervisor capabilities + * XML includes details of the support URI schemes. If omitted + * the dconn will be asked for a default URI. + * + * In either case it is typically only neccessary to specify a + * URI if the destination host has multiple interfaces and a + * specific interface is required to transmit migration data. * * The maximum bandwidth (in Mbps) that will be used to do migration * can be specified with the bandwidth parameter. If set to 0, @@ -3142,24 +3185,50 @@ virDomainMigrate (virDomainPtr domain, goto error; } - /* Check that migration is supported by both drivers. */ - if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, - VIR_DRV_FEATURE_MIGRATION_V1) && - VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, - VIR_DRV_FEATURE_MIGRATION_V1)) - ddomain = virDomainMigrateVersion1 (domain, dconn, flags, dname, uri, bandwidth); - else if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, - VIR_DRV_FEATURE_MIGRATION_V2) && - VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, - VIR_DRV_FEATURE_MIGRATION_V2)) - ddomain = virDomainMigrateVersion2 (domain, dconn, flags, dname, uri, bandwidth); - else { - virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - goto error; + if (flags & VIR_MIGRATE_PEER2PEER) { + if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_P2P)) { + char *duri = NULL; + if (!uri) + duri = virConnectGetURI(dconn); + + if (virDomainMigrateP2P (domain, flags, dname, uri ? uri : duri, bandwidth) < 0) { + VIR_FREE(duri); + goto error; + } + VIR_FREE(duri); + + ddomain = virDomainLookupByName (dconn, dname ? dname : domain->name); + } else { + virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + } else { + if (flags & VIR_MIGRATE_TUNNELLED) { + virLibConnError(domain->conn, VIR_ERR_OPERATION_INVALID, + _("cannot perform tunnelled migration without using peer2peer flag")); + goto error; + } + + /* Check that migration is supported by both drivers. */ + if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_V1) && + VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, + VIR_DRV_FEATURE_MIGRATION_V1)) + ddomain = virDomainMigrateVersion1 (domain, dconn, flags, dname, uri, bandwidth); + else if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_V2) && + VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, + VIR_DRV_FEATURE_MIGRATION_V2)) + ddomain = virDomainMigrateVersion2 (domain, dconn, flags, dname, uri, bandwidth); + else { + virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } } - if (ddomain == NULL) - goto error; + if (ddomain == NULL) + goto error; return ddomain; @@ -3169,6 +3238,107 @@ error: return NULL; } + +/** + * virDomainMigrateToURI: + * @domain: a domain object + * @duri: libvirt connection URI for destination host + * @flags: flags + * @dname: (optional) rename domain to this at destination + * @bandwidth: (optional) specify migration bandwidth limit in Mbps + * + * Migrate the domain object from its current host to the destination + * host given by duri. The duri is a libvirt connection URI that the + * source host will use to talk to the destination. + * + * The difference from the regular virDomainMigrate method is + * that the calling application does not need a direct connection + * to the destination host. The source libvirt driver makes a + * direct peer-to-peer connection to the destination libvirtd + * without the client appliction being involved. + * + * The VIR_MIGRATE_PEER2PEER flag is mandatory for this method. + * If an application wishes to run without this flag, then it + * may use the alternative virDomainMigrate method which requires + * an virConnectPtr object for the destination host + * + * The following additional flags may also be set + * VIR_MIGRATE_LIVE Do not pause the VM during migration + * VIR_MIGRATE_TUNNELLED Tunnel migration data over the libvirt RPC channel + * + * If a hypervisor supports renaming domains during migration, + * then you may set the dname parameter to the new name (otherwise + * it keeps the same name). If this is not supported by the + * hypervisor, dname must be NULL or else you will get an error. + * + * The maximum bandwidth (in Mbps) that will be used to do migration + * can be specified with the bandwidth parameter. If set to 0, + * libvirt will choose a suitable default. Some hypervisors do + * not support this feature and will return an error if bandwidth + * is not 0. + * + * To see which features are supported by the current hypervisor, + * see virConnectGetCapabilities, /capabilities/host/migration_features. + * + * There are many limitations on migration imposed by the underlying + * technology - for example it may not be possible to migrate between + * different processors even with the same architecture, or between + * different types of hypervisor. + * + * Returns 0 if the migration succeeded, -1 upon error. + */ +int +virDomainMigrateToURI (virDomainPtr domain, + const char *duri, + unsigned long flags, + const char *dname, + unsigned long bandwidth) +{ + DEBUG("domain=%p, duri=%p, flags=%lu, dname=%s, bandwidth=%lu", + domain, NULLSTR(duri), flags, NULLSTR(dname), bandwidth); + + virResetLastError(); + + /* First checkout the source */ + if (!VIR_IS_CONNECTED_DOMAIN (domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (duri == NULL) { + virLibConnError (domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + /* XXX perhaps we should just automatically set this flag instead ? */ + if (flags & VIR_MIGRATE_PEER2PEER) { + if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_P2P)) { + if (virDomainMigrateP2P (domain, flags, dname, duri, bandwidth) < 0) + goto error; + } else { + virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + } else { + virLibConnError (domain->conn, VIR_ERR_OPERATION_INVALID, + _("cannot migrate to a destination URI without peer2peer flag")); + goto error; + } + + return 0; + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); + return -1; +} + + /* * Not for public use. This function is part of the internal * implementation of migration in the remote case. diff --git a/src/libvirt_internal.h b/src/libvirt_internal.h index 5913798..5f1a7fe 100644 --- a/src/libvirt_internal.h +++ b/src/libvirt_internal.h @@ -17,6 +17,7 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * NB This file is (secret) ABI sensitive. Append only */ #ifndef __LIBVIRT_H_ @@ -31,6 +32,35 @@ int virStateReload(void); int virStateActive(void); #endif +/* Feature detection. This is a libvirt-private interface for determining + * what features are supported by the driver. + * + * The remote driver passes features through to the real driver at the + * remote end unmodified, except if you query a VIR_DRV_FEATURE_REMOTE* + * feature. + * + */ +enum { + /* Driver supports V1-style virDomainMigrate, ie. domainMigratePrepare/ + * domainMigratePerform/domainMigrateFinish. + */ + VIR_DRV_FEATURE_MIGRATION_V1 = 1, + + /* Driver is not local. */ + VIR_DRV_FEATURE_REMOTE = 2, + + /* Driver supports V2-style virDomainMigrate, ie. domainMigratePrepare2/ + * domainMigratePerform/domainMigrateFinish2. + */ + VIR_DRV_FEATURE_MIGRATION_V2 = 3, + + /* Driver supports peer-2-peer virDomainMigrate ie soruce host + * do all the prepare/perform/finish steps directly + */ + VIR_DRV_FEATURE_MIGRATION_P2P = 4, +}; + + int virDrvSupportsFeature (virConnectPtr conn, int feature); int virDomainMigratePrepare (virConnectPtr dconn, diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 888ea26..757e54c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -323,6 +323,7 @@ LIBVIRT_0.7.1 { virStreamFinish; virStreamAbort; virStreamFree; + virDomainMigrateToURI; } LIBVIRT_0.7.0; # .... define new API here using predicted next version number .... diff --git a/src/virsh.c b/src/virsh.c index 4825f1c..cabbd3d 100644 --- a/src/virsh.c +++ b/src/virsh.c @@ -2463,6 +2463,8 @@ static const vshCmdInfo info_migrate[] = { static const vshCmdOptDef opts_migrate[] = { {"live", VSH_OT_BOOL, 0, gettext_noop("live migration")}, + {"p2p", VSH_OT_BOOL, 0, gettext_noop("peer-2-peer migration")}, + {"tunnelled", VSH_OT_BOOL, 0, gettext_noop("tunnelled migration")}, {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, {"desturi", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("connection URI of the destination host")}, {"migrateuri", VSH_OT_DATA, 0, gettext_noop("migration URI, usually can be omitted")}, @@ -2478,8 +2480,6 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd) const char *migrateuri; const char *dname; int flags = 0, found, ret = FALSE; - virConnectPtr dconn = NULL; - virDomainPtr ddom = NULL; if (!vshConnectionUsability (ctl, ctl->conn, TRUE)) return FALSE; @@ -2500,20 +2500,40 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd) if (vshCommandOptBool (cmd, "live")) flags |= VIR_MIGRATE_LIVE; - /* Temporarily connect to the destination host. */ - dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0); - if (!dconn) goto done; + if (vshCommandOptBool (cmd, "p2p")) + flags |= VIR_MIGRATE_PEER2PEER; - /* Migrate. */ - ddom = virDomainMigrate (dom, dconn, flags, dname, migrateuri, 0); - if (!ddom) goto done; + if (vshCommandOptBool (cmd, "tunnelled")) + flags |= VIR_MIGRATE_TUNNELLED; - ret = TRUE; + if ((flags & VIR_MIGRATE_PEER2PEER)) { + /* For peer2peer migration we only expect one URI, a libvirt URI */ + + if (migrateuri != NULL) { + vshError(ctl, FALSE, "%s", _("migrate: Unexpected migrateuri for peer2peer migration")); + goto done; + } + + if (virDomainMigrateToURI (dom, desturi, flags, dname, 0) == 0) + ret = TRUE; + } else { + /* For regular live migration, connect to the destination host directly. */ + virConnectPtr dconn = NULL; + virDomainPtr ddom = NULL; + + dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0); + if (!dconn) goto done; + + ddom = virDomainMigrate (dom, dconn, flags, dname, migrateuri, 0); + if (ddom) { + virDomainFree(ddom); + ret = TRUE; + } + virConnectClose (dconn); + } done: if (dom) virDomainFree (dom); - if (ddom) virDomainFree (ddom); - if (dconn) virConnectClose (dconn); return ret; } diff --git a/src/xen_unified.c b/src/xen_unified.c index dfa9ca5..954b187 100644 --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -455,8 +455,11 @@ static int xenUnifiedSupportsFeature (virConnectPtr conn ATTRIBUTE_UNUSED, int feature) { switch (feature) { - case VIR_DRV_FEATURE_MIGRATION_V1: return 1; - default: return 0; + case VIR_DRV_FEATURE_MIGRATION_V1: + case VIR_DRV_FEATURE_MIGRATION_P2P: + return 1; + default: + return 0; } } diff --git a/src/xend_internal.c b/src/xend_internal.c index 7f55116..da5c039 100644 --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -4400,6 +4400,12 @@ xenDaemonDomainMigratePerform (virDomainPtr domain, strcpy (live, "1"); flags &= ~VIR_MIGRATE_LIVE; } + /* Trivially support this in Xen, since XenD on dest is always + * ready to accept incoming migration */ + if ((flags & VIR_MIGRATE_PEER2PEER)) { + flags &= ~VIR_MIGRATE_PEER2PEER; + } + /* XXX we could easily do tunnelled migration too if we want to */ if (flags != 0) { virXendError (conn, VIR_ERR_NO_SUPPORT, "%s", _("xenDaemonDomainMigrate: unsupported flag")); -- 1.6.2.5

[ Sending again due to mail output problems ] On Thu, Sep 17, 2009 at 06:25:01PM +0100, Daniel P. Berrange wrote:
Introduces several new public API options for migration
- VIR_MIGRATE_PEER2PEER: With this flag the client only invokes the virDomainMigratePerform method, expecting the source host driver to do whatever is required to complete the entire migration process. - VIR_MIGRATE_TUNNELLED: With this flag the actual data for migration will be tunnelled over the libvirtd RPC channel. This requires that VIR_MIGRATE_PEER2PEER is also set.
Hum, I would rather add that when entering libvirt.c entry point set explicitely VIR_MIGRATE_PEER2PEER if VIR_MIGRATE_TUNNELLED is asked for, that's an internal impl. detail.
- virDomainMigrateToURI: This is variant of the existing virDomainMigrate method which can be used when the VIR_MIGRATE_PEER2PEER flag is set. The benefit of this method is that no virConnectPtr object is required for the destination host, only a destination URI.
Sounds good if we have proper error handling. I'm also wondering how authentification and access control can be handled in that mode, the normal method of access somehow garantee that the user credentials can be checked in some ways, but if it's direct daemon to daemon talking how can this be performed ?
The URI for VIR_MIGRATE_TUNNELLED must be a valid libvirt URI. For non-tunnelled migration a hypervisor specific migration URI is used. [...] diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 10ec04a..1745396 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -336,7 +336,9 @@ typedef virDomainInterfaceStatsStruct *virDomainInterfaceStatsPtr;
/* Domain migration flags. */ typedef enum { - VIR_MIGRATE_LIVE = 1, /* live migration */ + VIR_MIGRATE_LIVE = (1 << 0), /* live migration */ + VIR_MIGRATE_PEER2PEER = (1 << 1), /* direct source -> dest host control channel */ + VIR_MIGRATE_TUNNELLED = (1 << 2), /* tunnel migration data over libvirtd connection */ } virDomainMigrateFlags;
/* Domain migration. */ @@ -344,6 +346,10 @@ virDomainPtr virDomainMigrate (virDomainPtr domain, virConnectPtr dconn, unsigned long flags, const char *dname, const char *uri, unsigned long bandwidth);
+int virDomainMigrateToURI (virDomainPtr domain, const char *duri, + unsigned long flags, const char *dname, + unsigned long bandwidth); +
s/duri/dest/ or target, the fact it's a connection URI should be made obvious in the function comment. [...]
+ } else { + if (flags & VIR_MIGRATE_TUNNELLED) { + virLibConnError(domain->conn, VIR_ERR_OPERATION_INVALID, + _("cannot perform tunnelled migration without using peer2peer flag")); + goto error; + }
I don't really agree, we should just or the flag and hide this from the API user.
+ +/** + * virDomainMigrateToURI: + * @domain: a domain object + * @duri: libvirt connection URI for destination host + * @flags: flags + * @dname: (optional) rename domain to this at destination + * @bandwidth: (optional) specify migration bandwidth limit in Mbps + * + * Migrate the domain object from its current host to the destination + * host given by duri. The duri is a libvirt connection URI that the + * source host will use to talk to the destination. + * + * The difference from the regular virDomainMigrate method is + * that the calling application does not need a direct connection + * to the destination host. The source libvirt driver makes a + * direct peer-to-peer connection to the destination libvirtd + * without the client appliction being involved. + * + * The VIR_MIGRATE_PEER2PEER flag is mandatory for this method. + * If an application wishes to run without this flag, then it + * may use the alternative virDomainMigrate method which requires + * an virConnectPtr object for the destination host
looks good except that block. Why force a flag systematically ?
+ * The following additional flags may also be set + * VIR_MIGRATE_LIVE Do not pause the VM during migration + * VIR_MIGRATE_TUNNELLED Tunnel migration data over the libvirt RPC channel + * + * If a hypervisor supports renaming domains during migration, + * then you may set the dname parameter to the new name (otherwise + * it keeps the same name). If this is not supported by the + * hypervisor, dname must be NULL or else you will get an error. + * + * The maximum bandwidth (in Mbps) that will be used to do migration + * can be specified with the bandwidth parameter. If set to 0, + * libvirt will choose a suitable default. Some hypervisors do + * not support this feature and will return an error if bandwidth + * is not 0. + * + * To see which features are supported by the current hypervisor, + * see virConnectGetCapabilities, /capabilities/host/migration_features. + * + * There are many limitations on migration imposed by the underlying + * technology - for example it may not be possible to migrate between + * different processors even with the same architecture, or between + * different types of hypervisor. + * + * Returns 0 if the migration succeeded, -1 upon error. + */ +int +virDomainMigrateToURI (virDomainPtr domain, + const char *duri, + unsigned long flags, + const char *dname, + unsigned long bandwidth) +{ + DEBUG("domain=%p, duri=%p, flags=%lu, dname=%s, bandwidth=%lu", + domain, NULLSTR(duri), flags, NULLSTR(dname), bandwidth); + + virResetLastError(); + + /* First checkout the source */ + if (!VIR_IS_CONNECTED_DOMAIN (domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return -1; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (duri == NULL) { + virLibConnError (domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + /* XXX perhaps we should just automatically set this flag instead ? */
Ah , yes :-)
+ if (flags & VIR_MIGRATE_PEER2PEER) { + if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_P2P)) { + if (virDomainMigrateP2P (domain, flags, dname, duri, bandwidth) < 0) + goto error; + } else { + virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + } else { + virLibConnError (domain->conn, VIR_ERR_OPERATION_INVALID, + _("cannot migrate to a destination URI without peer2peer flag")); + goto error; + } + + return 0; + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(domain->conn); + return -1; +} + + /* * Not for public use. This function is part of the internal * implementation of migration in the remote case. diff --git a/src/libvirt_internal.h b/src/libvirt_internal.h index 5913798..5f1a7fe 100644 --- a/src/libvirt_internal.h +++ b/src/libvirt_internal.h @@ -17,6 +17,7 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * NB This file is (secret) ABI sensitive. Append only
Huh ???
*/
#ifndef __LIBVIRT_H_ @@ -31,6 +32,35 @@ int virStateReload(void); int virStateActive(void); #endif
+/* Feature detection. This is a libvirt-private interface for determining + * what features are supported by the driver. + * + * The remote driver passes features through to the real driver at the + * remote end unmodified, except if you query a VIR_DRV_FEATURE_REMOTE* + * feature. + * + */ +enum { + /* Driver supports V1-style virDomainMigrate, ie. domainMigratePrepare/ + * domainMigratePerform/domainMigrateFinish. + */ + VIR_DRV_FEATURE_MIGRATION_V1 = 1, + + /* Driver is not local. */ + VIR_DRV_FEATURE_REMOTE = 2, + + /* Driver supports V2-style virDomainMigrate, ie. domainMigratePrepare2/ + * domainMigratePerform/domainMigrateFinish2. + */ + VIR_DRV_FEATURE_MIGRATION_V2 = 3, + + /* Driver supports peer-2-peer virDomainMigrate ie soruce host + * do all the prepare/perform/finish steps directly + */ + VIR_DRV_FEATURE_MIGRATION_P2P = 4, +}; + + int virDrvSupportsFeature (virConnectPtr conn, int feature);
int virDomainMigratePrepare (virConnectPtr dconn, diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 888ea26..757e54c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -323,6 +323,7 @@ LIBVIRT_0.7.1 { virStreamFinish; virStreamAbort; virStreamFree; + virDomainMigrateToURI; } LIBVIRT_0.7.0;
# .... define new API here using predicted next version number .... diff --git a/src/virsh.c b/src/virsh.c index 4825f1c..cabbd3d 100644 --- a/src/virsh.c +++ b/src/virsh.c @@ -2463,6 +2463,8 @@ static const vshCmdInfo info_migrate[] = {
static const vshCmdOptDef opts_migrate[] = { {"live", VSH_OT_BOOL, 0, gettext_noop("live migration")}, + {"p2p", VSH_OT_BOOL, 0, gettext_noop("peer-2-peer migration")}, + {"tunnelled", VSH_OT_BOOL, 0, gettext_noop("tunnelled migration")}, {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, {"desturi", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("connection URI of the destination host")}, {"migrateuri", VSH_OT_DATA, 0, gettext_noop("migration URI, usually can be omitted")}, @@ -2478,8 +2480,6 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd) const char *migrateuri; const char *dname; int flags = 0, found, ret = FALSE; - virConnectPtr dconn = NULL; - virDomainPtr ddom = NULL;
if (!vshConnectionUsability (ctl, ctl->conn, TRUE)) return FALSE; @@ -2500,20 +2500,40 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd) if (vshCommandOptBool (cmd, "live")) flags |= VIR_MIGRATE_LIVE;
- /* Temporarily connect to the destination host. */ - dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0); - if (!dconn) goto done; + if (vshCommandOptBool (cmd, "p2p")) + flags |= VIR_MIGRATE_PEER2PEER;
- /* Migrate. */ - ddom = virDomainMigrate (dom, dconn, flags, dname, migrateuri, 0); - if (!ddom) goto done; + if (vshCommandOptBool (cmd, "tunnelled")) + flags |= VIR_MIGRATE_TUNNELLED;
- ret = TRUE; + if ((flags & VIR_MIGRATE_PEER2PEER)) { + /* For peer2peer migration we only expect one URI, a libvirt URI */ + + if (migrateuri != NULL) { + vshError(ctl, FALSE, "%s", _("migrate: Unexpected migrateuri for peer2peer migration")); + goto done; + } + + if (virDomainMigrateToURI (dom, desturi, flags, dname, 0) == 0) + ret = TRUE; + } else { + /* For regular live migration, connect to the destination host directly. */ + virConnectPtr dconn = NULL; + virDomainPtr ddom = NULL; + + dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0); + if (!dconn) goto done; + + ddom = virDomainMigrate (dom, dconn, flags, dname, migrateuri, 0); + if (ddom) { + virDomainFree(ddom); + ret = TRUE; + } + virConnectClose (dconn); + }
done: if (dom) virDomainFree (dom); - if (ddom) virDomainFree (ddom); - if (dconn) virConnectClose (dconn); return ret; }
diff --git a/src/xen_unified.c b/src/xen_unified.c index dfa9ca5..954b187 100644 --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -455,8 +455,11 @@ static int xenUnifiedSupportsFeature (virConnectPtr conn ATTRIBUTE_UNUSED, int feature) { switch (feature) { - case VIR_DRV_FEATURE_MIGRATION_V1: return 1; - default: return 0; + case VIR_DRV_FEATURE_MIGRATION_V1: + case VIR_DRV_FEATURE_MIGRATION_P2P: + return 1; + default: + return 0; } }
diff --git a/src/xend_internal.c b/src/xend_internal.c index 7f55116..da5c039 100644 --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -4400,6 +4400,12 @@ xenDaemonDomainMigratePerform (virDomainPtr domain, strcpy (live, "1"); flags &= ~VIR_MIGRATE_LIVE; } + /* Trivially support this in Xen, since XenD on dest is always + * ready to accept incoming migration */ + if ((flags & VIR_MIGRATE_PEER2PEER)) { + flags &= ~VIR_MIGRATE_PEER2PEER; + } + /* XXX we could easily do tunnelled migration too if we want to */ if (flags != 0) { virXendError (conn, VIR_ERR_NO_SUPPORT, "%s", _("xenDaemonDomainMigrate: unsupported flag"));
Sounds and looks good to me, with the small caveat of the required VIR_MIGRATE_PEER2PEER flag and making sure it would work later with QEmu/KVM before commiting this. I must admit peer-2-peer left me a bit puzzled at the beginning it wasn't obvious what kind of transfer it actually meant. Only de the documentation clarified it but I have no better suggestion :-) "direct" maybe ... Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Tue, Sep 22, 2009 at 02:43:20PM +0200, Daniel Veillard wrote:
[ Sending again due to mail output problems ]
On Thu, Sep 17, 2009 at 06:25:01PM +0100, Daniel P. Berrange wrote:
Introduces several new public API options for migration
- VIR_MIGRATE_PEER2PEER: With this flag the client only invokes the virDomainMigratePerform method, expecting the source host driver to do whatever is required to complete the entire migration process. - VIR_MIGRATE_TUNNELLED: With this flag the actual data for migration will be tunnelled over the libvirtd RPC channel. This requires that VIR_MIGRATE_PEER2PEER is also set.
Hum, I would rather add that when entering libvirt.c entry point set explicitely VIR_MIGRATE_PEER2PEER if VIR_MIGRATE_TUNNELLED is asked for, that's an internal impl. detail.
I didn't do that, because I wanted to leave open the option to supporting TUNNELLED mode, without PEER2PEER mode. I don't really know what that would look like, but I think it is better future proofing to not presume one implies the other.
- virDomainMigrateToURI: This is variant of the existing virDomainMigrate method which can be used when the VIR_MIGRATE_PEER2PEER flag is set. The benefit of this method is that no virConnectPtr object is required for the destination host, only a destination URI.
Sounds good if we have proper error handling. I'm also wondering how authentification and access control can be handled in that mode, the normal method of access somehow garantee that the user credentials can be checked in some ways, but if it's direct daemon to daemon talking how can this be performed ?
The client application authenticates with the source libvirtd in the normal manner. The source libvirtd will authenticate with the destination libvirtd directly. The client application will not be authenticating with the destination libvirtd itself. While this may sound like a security issue, I don't think it really is. It is more of an authorization problem - eg the admin needs to declare somewhere which libvirtd instances are allowed to migrate guests between then. This authorization problem is really outside the scope of the client application or our public app API
+int virDomainMigrateToURI (virDomainPtr domain, const char *duri, + unsigned long flags, const char *dname, + unsigned long bandwidth); +
s/duri/dest/ or target, the fact it's a connection URI should be made obvious in the function comment.
Yep, the function comment later only clearly describes what URIs are acceptable.
[...]
+ } else { + if (flags & VIR_MIGRATE_TUNNELLED) { + virLibConnError(domain->conn, VIR_ERR_OPERATION_INVALID, + _("cannot perform tunnelled migration without using peer2peer flag")); + goto error; + }
I don't really agree, we should just or the flag and hide this from the API user.
I really did want to leave open the option of relaxing this restriction in the future, and thus didn't want to automatically set the PEER2PEER flag - once its set we can never unset it in the future.
+ + /* * Not for public use. This function is part of the internal * implementation of migration in the remote case. diff --git a/src/libvirt_internal.h b/src/libvirt_internal.h index 5913798..5f1a7fe 100644 --- a/src/libvirt_internal.h +++ b/src/libvirt_internal.h @@ -17,6 +17,7 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * NB This file is (secret) ABI sensitive. Append only
Huh ???
The methods here are encoded in the wire protocol ABI, but not included in the public libvirt.h header. Hence they are ABI sensitive, but secret & thus append only like the rest of our API
diff --git a/src/xen_unified.c b/src/xen_unified.c index dfa9ca5..954b187 100644 --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -455,8 +455,11 @@ static int xenUnifiedSupportsFeature (virConnectPtr conn ATTRIBUTE_UNUSED, int feature) { switch (feature) { - case VIR_DRV_FEATURE_MIGRATION_V1: return 1; - default: return 0; + case VIR_DRV_FEATURE_MIGRATION_V1: + case VIR_DRV_FEATURE_MIGRATION_P2P: + return 1; + default: + return 0; } }
diff --git a/src/xend_internal.c b/src/xend_internal.c index 7f55116..da5c039 100644 --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -4400,6 +4400,12 @@ xenDaemonDomainMigratePerform (virDomainPtr domain, strcpy (live, "1"); flags &= ~VIR_MIGRATE_LIVE; } + /* Trivially support this in Xen, since XenD on dest is always + * ready to accept incoming migration */ + if ((flags & VIR_MIGRATE_PEER2PEER)) { + flags &= ~VIR_MIGRATE_PEER2PEER; + } + /* XXX we could easily do tunnelled migration too if we want to */ if (flags != 0) { virXendError (conn, VIR_ERR_NO_SUPPORT, "%s", _("xenDaemonDomainMigrate: unsupported flag"));
Sounds and looks good to me, with the small caveat of the required VIR_MIGRATE_PEER2PEER flag and making sure it would work later with QEmu/KVM before commiting this.
I must admit peer-2-peer left me a bit puzzled at the beginning it wasn't obvious what kind of transfer it actually meant. Only de the documentation clarified it but I have no better suggestion :-) "direct" maybe ...
I called it peer-2-peer, because the 2 libvirtd instances are talking to each other p2p, rather than having all communcation go via the client application. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Fri, Sep 25, 2009 at 02:27:23PM +0100, Daniel P. Berrange wrote:
On Tue, Sep 22, 2009 at 02:43:20PM +0200, Daniel Veillard wrote:
[ Sending again due to mail output problems ]
On Thu, Sep 17, 2009 at 06:25:01PM +0100, Daniel P. Berrange wrote:
Introduces several new public API options for migration
- VIR_MIGRATE_PEER2PEER: With this flag the client only invokes the virDomainMigratePerform method, expecting the source host driver to do whatever is required to complete the entire migration process. - VIR_MIGRATE_TUNNELLED: With this flag the actual data for migration will be tunnelled over the libvirtd RPC channel. This requires that VIR_MIGRATE_PEER2PEER is also set.
Hum, I would rather add that when entering libvirt.c entry point set explicitely VIR_MIGRATE_PEER2PEER if VIR_MIGRATE_TUNNELLED is asked for, that's an internal impl. detail.
I didn't do that, because I wanted to leave open the option to supporting TUNNELLED mode, without PEER2PEER mode. I don't really know what that would look like, but I think it is better future proofing to not presume one implies the other.
Okay, let's go for this.
- virDomainMigrateToURI: This is variant of the existing virDomainMigrate method which can be used when the VIR_MIGRATE_PEER2PEER flag is set. The benefit of this method is that no virConnectPtr object is required for the destination host, only a destination URI.
Sounds good if we have proper error handling. I'm also wondering how authentification and access control can be handled in that mode, the normal method of access somehow garantee that the user credentials can be checked in some ways, but if it's direct daemon to daemon talking how can this be performed ?
The client application authenticates with the source libvirtd in the normal manner. The source libvirtd will authenticate with the destination libvirtd directly.
The client application will not be authenticating with the destination libvirtd itself.
While this may sound like a security issue, I don't think it really is. It is more of an authorization problem - eg the admin needs to declare somewhere which libvirtd instances are allowed to migrate guests between then. This authorization problem is really outside the scope of the client application or our public app API
Okay, as soon as you want to allow automatic migrations that will be considered normal.
+int virDomainMigrateToURI (virDomainPtr domain, const char *duri, + unsigned long flags, const char *dname, + unsigned long bandwidth); +
s/duri/dest/ or target, the fact it's a connection URI should be made obvious in the function comment.
Yep, the function comment later only clearly describes what URIs are acceptable.
[...]
+ } else { + if (flags & VIR_MIGRATE_TUNNELLED) { + virLibConnError(domain->conn, VIR_ERR_OPERATION_INVALID, + _("cannot perform tunnelled migration without using peer2peer flag")); + goto error; + }
I don't really agree, we should just or the flag and hide this from the API user.
I really did want to leave open the option of relaxing this restriction in the future, and thus didn't want to automatically set the PEER2PEER flag - once its set we can never unset it in the future.
+ + /* * Not for public use. This function is part of the internal * implementation of migration in the remote case. diff --git a/src/libvirt_internal.h b/src/libvirt_internal.h index 5913798..5f1a7fe 100644 --- a/src/libvirt_internal.h +++ b/src/libvirt_internal.h @@ -17,6 +17,7 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * NB This file is (secret) ABI sensitive. Append only
Huh ???
The methods here are encoded in the wire protocol ABI, but not included in the public libvirt.h header. Hence they are ABI sensitive, but secret & thus append only like the rest of our API
diff --git a/src/xen_unified.c b/src/xen_unified.c index dfa9ca5..954b187 100644 --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -455,8 +455,11 @@ static int xenUnifiedSupportsFeature (virConnectPtr conn ATTRIBUTE_UNUSED, int feature) { switch (feature) { - case VIR_DRV_FEATURE_MIGRATION_V1: return 1; - default: return 0; + case VIR_DRV_FEATURE_MIGRATION_V1: + case VIR_DRV_FEATURE_MIGRATION_P2P: + return 1; + default: + return 0; } }
diff --git a/src/xend_internal.c b/src/xend_internal.c index 7f55116..da5c039 100644 --- a/src/xend_internal.c +++ b/src/xend_internal.c @@ -4400,6 +4400,12 @@ xenDaemonDomainMigratePerform (virDomainPtr domain, strcpy (live, "1"); flags &= ~VIR_MIGRATE_LIVE; } + /* Trivially support this in Xen, since XenD on dest is always + * ready to accept incoming migration */ + if ((flags & VIR_MIGRATE_PEER2PEER)) { + flags &= ~VIR_MIGRATE_PEER2PEER; + } + /* XXX we could easily do tunnelled migration too if we want to */ if (flags != 0) { virXendError (conn, VIR_ERR_NO_SUPPORT, "%s", _("xenDaemonDomainMigrate: unsupported flag"));
Sounds and looks good to me, with the small caveat of the required VIR_MIGRATE_PEER2PEER flag and making sure it would work later with QEmu/KVM before commiting this.
I must admit peer-2-peer left me a bit puzzled at the beginning it wasn't obvious what kind of transfer it actually meant. Only de the documentation clarified it but I have no better suggestion :-) "direct" maybe ...
I called it peer-2-peer, because the 2 libvirtd instances are talking to each other p2p, rather than having all communcation go via the client application.
it's just that p2p raises the idea of a mesh of machines in my mind and I'm probably not the only one :-) No problem ! ACK Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On 09/17/2009 07:25 PM, Daniel P. Berrange wrote:
+ if (flags& VIR_MIGRATE_PEER2PEER) { + if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_P2P)) { + char *duri = NULL;
Is there a reason why you shouldn't try a VIR_MIGRATE_PEER2PEER if dconn is NULL, rather than having a separate flag? If I read the code correctly, this would make virDomainMigrateToURI unnecessary. Paolo

This patch introduces a peer-to-peer migration protocol where the client only invokes the virDomainMigratePerform method, expecting the source host driver to do whatever is required to complete the entire migration process. This method is used by virsh whenever the hypervisor supports it and the destination URI is not given. In addition, this patch makes the dconn parameter to virDomainMigrate optional. In this case, libvirt will try to use peer-to-peer migration or, alternatively, will fall back to opening a temporary connection. * src/driver.h: Remove feature flags. * src/libvirt_internal.h: Add feature flags, and include new VIR_DRV_FEATURE_MIGRATION_P2P indicating support for the new migration protocol. Add VIR_MIGRATE_PEER2PEER private flag passed to virMigratePerform. * src/libvirt.c: Implement support for peer-to-peer migration. * src/virsh.c: Let libvirt create the dconn where possible. * src/xen/xen_driver.c: Advertise support for P2P migration * src/xen/xend_internal.c: Accept VIR_MIGRATE_PEER2PEER. --- > Is there a reason why you shouldn't try a VIR_MIGRATE_PEER2PEER > if dconn is NULL, rather than having a separate flag? Well it wasn't that simple, anyway this is what I meant. I splitted out the non-fleshed-out tunnelled migration parts to ease committing. However I've not tested this beyond compiling yet. docs/libvirt-api.xml | 23 +++--- docs/libvirt-refs.xml | 120 +++++++++++++++++++------------- src/driver.h | 19 ----- src/internal.h | 2 + src/libvirt.c | 177 +++++++++++++++++++++++++++++++++++------------ src/libvirt_internal.h | 36 ++++++++++ src/xen/xen_driver.c | 7 ++- src/xen/xend_internal.c | 5 ++ tools/virsh.c | 28 ++++--- 9 files changed, 279 insertions(+), 138 deletions(-) diff --git a/src/driver.h b/src/driver.h index 6a3dcc2..893e98b 100644 --- a/src/driver.h +++ b/src/driver.h @@ -44,25 +44,6 @@ typedef enum { VIR_DRV_OPEN_ERROR = -2, } virDrvOpenStatus; -/* Feature detection. This is a libvirt-private interface for determining - * what features are supported by the driver. - * - * The remote driver passes features through to the real driver at the - * remote end unmodified, except if you query a VIR_DRV_FEATURE_REMOTE* - * feature. - */ - /* Driver supports V1-style virDomainMigrate, ie. domainMigratePrepare/ - * domainMigratePerform/domainMigrateFinish. - */ -#define VIR_DRV_FEATURE_MIGRATION_V1 1 - - /* Driver is not local. */ -#define VIR_DRV_FEATURE_REMOTE 2 - - /* Driver supports V2-style virDomainMigrate, ie. domainMigratePrepare2/ - * domainMigratePerform/domainMigrateFinish2. - */ -#define VIR_DRV_FEATURE_MIGRATION_V2 3 /* Internal feature-detection macro. Don't call drv->supports_feature * directly, because it may be NULL, use this macro instead. diff --git a/src/internal.h b/src/internal.h index 8fa579c..bd1cfe6 100644 --- a/src/internal.h +++ b/src/internal.h @@ -24,6 +24,8 @@ #include "libvirt/libvirt.h" #include "libvirt/virterror.h" +#include "libvirt_internal.h" + /* On architectures which lack these limits, define them (ie. Cygwin). * Note that the libvirt code should be robust enough to handle the * case where actual value is longer than these limits (eg. by setting diff --git a/src/libvirt.c b/src/libvirt.c index dacf8c4..f0ec640 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -35,7 +35,6 @@ #include "virterror_internal.h" #include "logging.h" #include "datatypes.h" -#include "libvirt_internal.h" #include "driver.h" #include "uuid.h" @@ -3044,6 +3043,39 @@ virDomainMigrateVersion2 (virDomainPtr domain, return ddomain; } + + /* + * This is sort of a migration v3 + * + * This performs a peer-to-peer migration where source host + * does all the communication with the destination host. + */ +static int +virDomainMigrateP2P (virDomainPtr domain, + unsigned long flags, + const char *dname, + const char *uri, + unsigned long bandwidth) +{ + if (!domain->conn->driver->domainMigratePerform) { + virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + } + + /* Perform the migration. The driver isn't supposed to return + * until the migration is complete. + */ + flags |= VIR_MIGRATE_PEER2PEER; + return domain->conn->driver->domainMigratePerform(domain, + NULL, /* cookie */ + 0, /* cookielen */ + uri, + flags, + dname, + bandwidth); +} + + /** * virDomainMigrate: * @domain: a domain object @@ -3057,22 +3089,23 @@ virDomainMigrateVersion2 (virDomainPtr domain, * host given by dconn (a connection to the destination host). * * Flags may be one of more of the following: - * VIR_MIGRATE_LIVE Attempt a live migration. + * VIR_MIGRATE_LIVE Do not pause the VM during migration * * If a hypervisor supports renaming domains during migration, * then you may set the dname parameter to the new name (otherwise * it keeps the same name). If this is not supported by the * hypervisor, dname must be NULL or else you will get an error. * - * Since typically the two hypervisors connect directly to each - * other in order to perform the migration, you may need to specify - * a path from the source to the destination. This is the purpose - * of the uri parameter. If uri is NULL, then libvirt will try to - * find the best method. Uri may specify the hostname or IP address - * of the destination host as seen from the source. Or uri may be - * a URI giving transport, hostname, user, port, etc. in the usual - * form. Refer to driver documentation for the particular URIs - * supported. + * dconn can be NULL. Then the uri parameter will then be a + * valid libvirt connection URI, by which the source libvirt + * driver can connect to the destination libvirt. + * + * If the dconn argument is set, the URI parameter takes a + * hypervisor specific format. The hypervisor capabilities XML + * includes details of the support URI schemes. If omitted the dconn + * will be asked for a default URI. It is typically only necessary + * to specify a URI if the destination host has multiple interfaces + * and a specific interface is required to transmit migration data. * * The maximum bandwidth (in Mbps) that will be used to do migration * can be specified with the bandwidth parameter. If set to 0, @@ -3089,8 +3122,10 @@ virDomainMigrateVersion2 (virDomainPtr domain, * different types of hypervisor. * * Returns the new domain object if the migration was successful, - * or NULL in case of error. Note that the new domain object - * exists in the scope of the destination connection (dconn). + * or NULL in case of error. Note that the new domain object + * exists in the scope of the destination connection (dconn) if + * one was provided; else the migrated domain is returned + * with an added reference. */ virDomainPtr virDomainMigrate (virDomainPtr domain, @@ -3101,6 +3136,9 @@ virDomainMigrate (virDomainPtr domain, unsigned long bandwidth) { virDomainPtr ddomain = NULL; + int protocol = 0; + int free_conn = 0; + DEBUG("domain=%p, dconn=%p, flags=%lu, dname=%s, uri=%s, bandwidth=%lu", domain, dconn, flags, NULLSTR(dname), NULLSTR(uri), bandwidth); @@ -3109,51 +3147,103 @@ virDomainMigrate (virDomainPtr domain, /* First checkout the source */ if (!VIR_IS_CONNECTED_DOMAIN (domain)) { virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return NULL; + goto cleanup; } if (domain->conn->flags & VIR_CONNECT_RO) { virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - goto error; + goto cleanup; } - /* Now checkout the destination */ - if (!VIR_IS_CONNECT (dconn)) { - virLibConnError (domain->conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - goto error; - } - if (dconn->flags & VIR_CONNECT_RO) { - /* NB, deliberately report error against source object, not dest */ - virLibDomainError (domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); - goto error; - } + /* Now see if we have to open the destination ourselves... */ + if (!dconn) { + if (!uri) { + virLibConnError (domain->conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + goto cleanup; + } + + if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_P2P)) + protocol = VIR_DRV_FEATURE_MIGRATION_P2P; + else { + dconn = virConnectOpenAuth (uri, virConnectAuthPtrDefault, 0); + uri = NULL; + if (!dconn) + goto cleanup; + free_conn = 1; + } + } + + /* ... and check it out. */ + if (dconn) { + if (!VIR_IS_CONNECT (dconn)) { + virLibConnError (domain->conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + goto cleanup; + } + if (dconn->flags & VIR_CONNECT_RO) { + /* NB, deliberately report error against source object, not dest */ + virLibDomainError (domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto cleanup; + } - /* Check that migration is supported by both drivers. */ - if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, - VIR_DRV_FEATURE_MIGRATION_V1) && - VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, - VIR_DRV_FEATURE_MIGRATION_V1)) - ddomain = virDomainMigrateVersion1 (domain, dconn, flags, dname, uri, bandwidth); - else if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, - VIR_DRV_FEATURE_MIGRATION_V2) && - VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, - VIR_DRV_FEATURE_MIGRATION_V2)) + /* Choose a protocol. Some features are not supported by + * all protocols. */ + if (!uri && + VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_P2P)) + protocol = VIR_DRV_FEATURE_MIGRATION_P2P; + + /* For these, migration has to be supported by both drivers. */ + else if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_V1) && + VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, + VIR_DRV_FEATURE_MIGRATION_V1)) + protocol = VIR_DRV_FEATURE_MIGRATION_V1; + + else if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_V2) && + VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, + VIR_DRV_FEATURE_MIGRATION_V2)) + protocol = VIR_DRV_FEATURE_MIGRATION_V2; + } + + if (protocol == VIR_DRV_FEATURE_MIGRATION_P2P) { + char *duri = NULL; + if (!uri) + uri = duri = virConnectGetURI(dconn); + + if (virDomainMigrateP2P (domain, flags, dname, uri, bandwidth) < 0) { + VIR_FREE(duri); + goto cleanup; + } + VIR_FREE(duri); + + if (dconn) + ddomain = virDomainLookupByName (dconn, dname ? dname : domain->name); + else { + ddomain = domain; + virDomainRef (domain); + } + } else if (protocol == VIR_DRV_FEATURE_MIGRATION_V1) + ddomain = virDomainMigrateVersion1 (domain, dconn, flags, dname, uri, bandwidth); + else if (protocol == VIR_DRV_FEATURE_MIGRATION_V2) ddomain = virDomainMigrateVersion2 (domain, dconn, flags, dname, uri, bandwidth); else { virLibConnError (domain->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - goto error; + goto cleanup; } - if (ddomain == NULL) - goto error; - - return ddomain; -error: +cleanup: /* Copy to connection error object for back compatability */ - virSetConnError(domain->conn); - return NULL; + if (ddomain == NULL) + virSetConnError(domain->conn); + if (free_conn) + virConnectClose(dconn); + return ddomain; } + + /* * Not for public use. This function is part of the internal * implementation of migration in the remote case. diff --git a/src/libvirt_internal.h b/src/libvirt_internal.h index 5913798..024c9dd 100644 --- a/src/libvirt_internal.h +++ b/src/libvirt_internal.h @@ -17,6 +17,7 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * NB: This file is private, but still contributes to the ABI. Append only. */ #ifndef __LIBVIRT_H_ @@ -31,6 +32,41 @@ int virStateReload(void); int virStateActive(void); #endif +/* Feature detection. This is a libvirt-private interface for determining + * what features are supported by the driver. + * + * The remote driver passes features through to the real driver at the + * remote end unmodified, except if you query a VIR_DRV_FEATURE_REMOTE* + * feature. + * + */ +enum { + /* Driver supports V1-style virDomainMigrate, ie. domainMigratePrepare/ + * domainMigratePerform/domainMigrateFinish. + */ + VIR_DRV_FEATURE_MIGRATION_V1 = 1, + + /* Driver is not local. */ + VIR_DRV_FEATURE_REMOTE = 2, + + /* Driver supports V2-style virDomainMigrate, ie. domainMigratePrepare2/ + * domainMigratePerform/domainMigrateFinish2. + */ + VIR_DRV_FEATURE_MIGRATION_V2 = 3, + + /* Driver supports peer-to-peer virDomainMigrate ie. the source host + * will do all the prepare/perform/finish steps directly + */ + VIR_DRV_FEATURE_MIGRATION_P2P = 4, +}; + +enum { + /* Driver was requested peer-to-peer virDomainMigrate. This is + * needed because the perform callback is shared. + */ + VIR_MIGRATE_PEER2PEER = (1 << 16), +} virDomainInternalMigrateFlags; + int virDrvSupportsFeature (virConnectPtr conn, int feature); int virDomainMigratePrepare (virConnectPtr dconn, diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index 9e1bc32..bc6fdfd 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -455,8 +455,11 @@ static int xenUnifiedSupportsFeature (virConnectPtr conn ATTRIBUTE_UNUSED, int feature) { switch (feature) { - case VIR_DRV_FEATURE_MIGRATION_V1: return 1; - default: return 0; + case VIR_DRV_FEATURE_MIGRATION_V1: + case VIR_DRV_FEATURE_MIGRATION_P2P: + return 1; + default: + return 0; } } diff --git a/src/xen/xend_internal.c b/src/xen/xend_internal.c index 49bdba9..e9b1de6 100644 --- a/src/xen/xend_internal.c +++ b/src/xen/xend_internal.c @@ -4409,6 +4409,11 @@ xenDaemonDomainMigratePerform (virDomainPtr domain, strcpy (live, "1"); flags &= ~VIR_MIGRATE_LIVE; } + /* Trivially support this in Xen, since XenD on dest is always + * ready to accept incoming migration */ + if ((flags & VIR_MIGRATE_PEER2PEER)) { + flags &= ~VIR_MIGRATE_PEER2PEER; + } if (flags != 0) { virXendError (conn, VIR_ERR_NO_SUPPORT, "%s", _("xenDaemonDomainMigrate: unsupported flag")); diff --git a/tools/virsh.c b/tools/virsh.c index 3482389..a3d92df 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2472,13 +2472,13 @@ static const vshCmdOptDef opts_migrate[] = { static int cmdMigrate (vshControl *ctl, const vshCmd *cmd) { + virConnectPtr dconn = NULL; virDomainPtr dom = NULL; + virDomainPtr ddom = NULL; const char *desturi; const char *migrateuri; const char *dname; int flags = 0, found, ret = FALSE; - virConnectPtr dconn = NULL; - virDomainPtr ddom = NULL; if (!vshConnectionUsability (ctl, ctl->conn, TRUE)) return FALSE; @@ -2499,20 +2499,24 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd) if (vshCommandOptBool (cmd, "live")) flags |= VIR_MIGRATE_LIVE; - /* Temporarily connect to the destination host. */ - dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0); - if (!dconn) goto done; - - /* Migrate. */ - ddom = virDomainMigrate (dom, dconn, flags, dname, migrateuri, 0); - if (!ddom) goto done; + if (migrateuri == NULL) { + /* Let libvirt handle opening the connection if necessary. */ + ddom = virDomainMigrate (dom, NULL, flags, dname, desturi, 0); + } else { + dconn = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0); + if (!dconn) goto done; + ddom = virDomainMigrate (dom, dconn, flags, dname, migrateuri, 0); + } - ret = TRUE; + if (ddom) { + virDomainFree(ddom); + ret = TRUE; + } + if (dconn) + virConnectClose (dconn); done: if (dom) virDomainFree (dom); - if (ddom) virDomainFree (ddom); - if (dconn) virConnectClose (dconn); return ret; } -- 1.6.2.5

This is just an example. Unlike the previous patch, this one of course is not ready for committing. * include/libvirt/libvirt.h.in: Add VIR_MIGRATE_TUNNELLED. * src/libvirt.c: Implement non-support for VIR_MIGRATE_TUNNELLED. * src/virsh.c: Add --tunnelled arg to virsh migrate. --- include/libvirt/libvirt.h.in | 3 ++- src/libvirt.c | 14 +++++++++++--- tools/virsh.c | 4 ++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 4e63e48..fa54be1 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -336,7 +336,8 @@ typedef virDomainInterfaceStatsStruct *virDomainInterfaceStatsPtr; /* Domain migration flags. */ typedef enum { - VIR_MIGRATE_LIVE = 1, /* live migration */ + VIR_MIGRATE_LIVE = (1 << 0), /* live migration */ + VIR_MIGRATE_TUNNELLED = (1 << 1), /* tunnel migration data over libvirtd connection */ } virDomainMigrateFlags; /* Domain migration. */ diff --git a/src/libvirt.c b/src/libvirt.c index f0ec640..811c50d 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -2918,6 +2918,8 @@ virDomainMigrateVersion1 (virDomainPtr domain, char *cookie = NULL; int cookielen = 0; + assert (!(flags & VIR_MIGRATE_TUNNELLED)); + /* Prepare the migration. * * The destination host may return a cookie, or leave cookie as @@ -2982,6 +2984,8 @@ virDomainMigrateVersion2 (virDomainPtr domain, char *dom_xml = NULL; int cookielen = 0, ret; + assert (!(flags & VIR_MIGRATE_TUNNELLED)); + /* Prepare the migration. * * The destination host may return a cookie, or leave cookie as @@ -3098,7 +3102,9 @@ virDomainMigrateP2P (virDomainPtr domain, * * dconn can be NULL. Then the uri parameter will then be a * valid libvirt connection URI, by which the source libvirt - * driver can connect to the destination libvirt. + * driver can connect to the destination libvirt. If this + * scheme is used and the hypervisor supports it, the + * VIR_MIGRATE_TUNNELLED flag can also be set. * * If the dconn argument is set, the URI parameter takes a * hypervisor specific format. The hypervisor capabilities XML @@ -3192,13 +3198,15 @@ virDomainMigrate (virDomainPtr domain, protocol = VIR_DRV_FEATURE_MIGRATION_P2P; /* For these, migration has to be supported by both drivers. */ - else if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + else if (!(flags & VIR_MIGRATE_TUNNELLED) && + VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, VIR_DRV_FEATURE_MIGRATION_V1) && VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, VIR_DRV_FEATURE_MIGRATION_V1)) protocol = VIR_DRV_FEATURE_MIGRATION_V1; - else if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + else if (!(flags & VIR_MIGRATE_TUNNELLED) && + VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, VIR_DRV_FEATURE_MIGRATION_V2) && VIR_DRV_SUPPORTS_FEATURE (dconn->driver, dconn, VIR_DRV_FEATURE_MIGRATION_V2)) diff --git a/tools/virsh.c b/tools/virsh.c index a3d92df..55b1d76 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2462,6 +2462,7 @@ static const vshCmdInfo info_migrate[] = { static const vshCmdOptDef opts_migrate[] = { {"live", VSH_OT_BOOL, 0, gettext_noop("live migration")}, + {"tunnelled", VSH_OT_BOOL, 0, gettext_noop("tunnelled migration")}, {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, {"desturi", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("connection URI of the destination host")}, {"migrateuri", VSH_OT_DATA, 0, gettext_noop("migration URI, usually can be omitted")}, @@ -2499,6 +2500,9 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd) if (vshCommandOptBool (cmd, "live")) flags |= VIR_MIGRATE_LIVE; + if (vshCommandOptBool (cmd, "tunnelled")) + flags |= VIR_MIGRATE_TUNNELLED; + if (migrateuri == NULL) { /* Let libvirt handle opening the connection if necessary. */ ddom = virDomainMigrate (dom, NULL, flags, dname, desturi, 0); -- 1.6.2.5

On Thu, Oct 01, 2009 at 11:43:17PM +0200, Paolo Bonzini wrote:
+ + if (dconn) + ddomain = virDomainLookupByName (dconn, dname ? dname : domain->name); + else { + ddomain = domain; + virDomainRef (domain); + }
This bit of code is exactly why we need a new API - the return value is declared to be a virDomainPtr on the destination host, not just a copy of the original source domain which no longer even exists on the source host if it was a transient domain. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On 10/02/2009 01:53 PM, Daniel P. Berrange wrote:
This bit of code is exactly why we need a new API - the return value is declared to be a virDomainPtr on the destination host, not just a copy of the original source domain which no longer even exists on the source host if it was a transient domain.
Yeah, I really did not like this. In Chris's code he actually opens the connection (after the migration is complete) in order to return the destination domain. Does that make sense? Paolo

On Fri, Oct 02, 2009 at 02:06:12PM +0200, Paolo Bonzini wrote:
On 10/02/2009 01:53 PM, Daniel P. Berrange wrote:
This bit of code is exactly why we need a new API - the return value is declared to be a virDomainPtr on the destination host, not just a copy of the original source domain which no longer even exists on the source host if it was a transient domain.
Yeah, I really did not like this.
In Chris's code he actually opens the connection (after the migration is complete) in order to return the destination domain.
Does that make sense?
Not really, because one of the requirements from libvirt-qpid is that we have an API that only needs a localhost connection for migration, to avoid the need to configure client app security credentials. This implies we cannot either require a destination virConnectPtr in the API, not open one internally. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Thu, Oct 01, 2009 at 03:57:13PM +0200, Paolo Bonzini wrote:
On 09/17/2009 07:25 PM, Daniel P. Berrange wrote:
+ if (flags& VIR_MIGRATE_PEER2PEER) { + if (VIR_DRV_SUPPORTS_FEATURE (domain->conn->driver, domain->conn, + VIR_DRV_FEATURE_MIGRATION_P2P)) { + char *duri = NULL;
Is there a reason why you shouldn't try a VIR_MIGRATE_PEER2PEER if dconn is NULL, rather than having a separate flag? If I read the code correctly, this would make virDomainMigrateToURI unnecessary.
If 'dconn' is NULL, it is impossible to create a 'virDomainPtr' on the destination connection, which is required as the return value of the virDomainMigrate function to indicate success. Hence we need a new virDomainMigrateToURI method which returns an 'int' status code, instead of virDomainPtr Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Mon, Aug 24, 2009 at 09:51:03PM +0100, Daniel P. Berrange wrote:
The following series of patches introduce support for generic data streams in the libvirt API, the remote protocol, client & daemon.
The public API takes the form of a new object virStreamPtr and methods to read/write/close it
The remote protocol was the main hard bit. Since the protocol allows multiple concurrent API calls on a single connection, this needed to also allow concurrent data streams. It is also not acceptable for a large data stream to block other traffic while it is transferring.
Thus, we introduce a new protocol message type 'REMOTE_STREAM' to handle transfer for the stream data. A method involving a data streams starts off in the normal way with a REMOTE_CALL to the server, and a REMOTE_REPLY response message. If this was successful, there now follows the data stream traffic.
For outgoing streams (data from client to server), the client will send zero or more REMOTE_STREAM packets containing the data with status == REMOTE_CONTINUE. These are asynchronous and not acknowledged by the server. At any time the server may send an async message with a type of REMOTE_STREAM and status of REMOTE_ERROR. This indicates to the client that the transfer is aborting at server request. If the client wishes to abort, it can send the server a REMOTE_STREAM+REMOTE_ERROR message. If the client finishes its data transfer, it will send a final REMOTE_STREAM+REMOTE_OK message, and the server will respond with the same. This full roundtrip handshake ensures any async error messages are guarenteed to be flushed
For incoming data streams (data from server to client), the server sends zero or more REMOTE_STREAM packets containing the data with status == REMOTE_CONTINUE. These are asynchronous and not acknowledged by the client. At any time the client may send an async message with a type of REMOTE_STREAM and status of REMOTE_ERROR. This indicates to the server that the transfer is aborting at client request. If the server wishes to abort, it can send the server a REMOTE_STREAM+REMOTE_ERROR message. When the server finishes its data transfer, it will send a final REMOTE_STREAM+REMOTE_CONTINUE message ewith a data length of zero (ie EOF). The client will then send a REMOTE_STREAM+REMOTE_OK packet and the server will respond with the same. This full roundtrip handshake ensures any async error messages are guarenteed to be flushed
This all ensures that multiple data streams can be active in parallel, and with a maximum data packet size of 256 KB, no single stream can cause too much latency on the connection for other API calls/streams.
Okay, this is very similar in principle with HTTP pipelining with IMHO the same benefits and the same potential drawbacks. A couple of things to check might be: - the maximum amount of concurrent active streams allowed, for example suppose you want to migrate in emergency all the domains out of a failing machine, some level of serialization may be better than say attempting to migrate all 100 domains at the same time. 10 parallel stream might be better, but we need to make sure the API allows to report such condition. - the maximum chunking size, but with 256k I think this is covered. - synchronization internally between threads to avoid deadlocks or poor performances, that can be very hard to debug, so I guess an effort should be provided to explain how things are designed internally. But this sounds fine in general.
The only thing it does not allow for is one API method to use two or more streams. These may be famous last words, but I don't think that use case will be neccessary for any of our APIs...
as long as the limitation is documented especially in the parts of teh code where the assumption is made, sounds fine.
The last 5 patches with a subject of [DEMO] are *NOT* intended to be committed to the repository. They merely demonstrate the use of data streams for a couple of hypothetical file upload and download APIs. Actually they were mostly to allow me to test the code streams code without messing around with the QEMU migration code.
The immediate use case for this data stream code is Chris' QEMU migration patchset.
The next use case is to allow serial console access to be tunnelled over libvirtd, eg to make 'virsh console GUEST' work remotely. This use case is why I included the support for non-blocking data streams and event loop integration (not required for Chris' migration use case)
Okay, next to individual patches reviews, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Fri, Sep 25, 2009 at 09:58:51AM +0200, Daniel Veillard wrote:
On Mon, Aug 24, 2009 at 09:51:03PM +0100, Daniel P. Berrange wrote:
Okay, this is very similar in principle with HTTP pipelining with IMHO the same benefits and the same potential drawbacks. A couple of things to check might be: - the maximum amount of concurrent active streams allowed, for example suppose you want to migrate in emergency all the domains out of a failing machine, some level of serialization may be better than say attempting to migrate all 100 domains at the same time. 10 parallel stream might be better, but we need to make sure the API allows to report such condition.
We could certainly add a tunable in /etc/libvirt/libvirtd.conf that limits the number of streams that are allowed per client.
- the maximum chunking size, but with 256k I think this is covered.
Yes, the remote protocol itself limits each message to 256k currently. I think this is a good enough size, since it avoids the stream delaying RPC calls, and the encryption chunk size is going to be smaller than this anyway, so you won't gain much from larger chunks.
- synchronization internally between threads to avoid deadlocks or poor performances, that can be very hard to debug, so I guess an effort should be provided to explain how things are designed internally.
Each individual virStreamPtr object is directly associated with a single API call, so in essence each virStreamPtr should only really be used from a single thread. That said, the virStreamPtr internal drivers should all lock the virStreamPtr object as they require to provide safety. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

FYI, the data streams patches are now committed Daniel On Fri, Sep 25, 2009 at 09:58:51AM +0200, Daniel Veillard wrote:
On Mon, Aug 24, 2009 at 09:51:03PM +0100, Daniel P. Berrange wrote:
The following series of patches introduce support for generic data streams in the libvirt API, the remote protocol, client & daemon.
The public API takes the form of a new object virStreamPtr and methods to read/write/close it
The remote protocol was the main hard bit. Since the protocol allows multiple concurrent API calls on a single connection, this needed to also allow concurrent data streams. It is also not acceptable for a large data stream to block other traffic while it is transferring.
Thus, we introduce a new protocol message type 'REMOTE_STREAM' to handle transfer for the stream data. A method involving a data streams starts off in the normal way with a REMOTE_CALL to the server, and a REMOTE_REPLY response message. If this was successful, there now follows the data stream traffic.
For outgoing streams (data from client to server), the client will send zero or more REMOTE_STREAM packets containing the data with status == REMOTE_CONTINUE. These are asynchronous and not acknowledged by the server. At any time the server may send an async message with a type of REMOTE_STREAM and status of REMOTE_ERROR. This indicates to the client that the transfer is aborting at server request. If the client wishes to abort, it can send the server a REMOTE_STREAM+REMOTE_ERROR message. If the client finishes its data transfer, it will send a final REMOTE_STREAM+REMOTE_OK message, and the server will respond with the same. This full roundtrip handshake ensures any async error messages are guarenteed to be flushed
For incoming data streams (data from server to client), the server sends zero or more REMOTE_STREAM packets containing the data with status == REMOTE_CONTINUE. These are asynchronous and not acknowledged by the client. At any time the client may send an async message with a type of REMOTE_STREAM and status of REMOTE_ERROR. This indicates to the server that the transfer is aborting at client request. If the server wishes to abort, it can send the server a REMOTE_STREAM+REMOTE_ERROR message. When the server finishes its data transfer, it will send a final REMOTE_STREAM+REMOTE_CONTINUE message ewith a data length of zero (ie EOF). The client will then send a REMOTE_STREAM+REMOTE_OK packet and the server will respond with the same. This full roundtrip handshake ensures any async error messages are guarenteed to be flushed
This all ensures that multiple data streams can be active in parallel, and with a maximum data packet size of 256 KB, no single stream can cause too much latency on the connection for other API calls/streams.
Okay, this is very similar in principle with HTTP pipelining with IMHO the same benefits and the same potential drawbacks. A couple of things to check might be: - the maximum amount of concurrent active streams allowed, for example suppose you want to migrate in emergency all the domains out of a failing machine, some level of serialization may be better than say attempting to migrate all 100 domains at the same time. 10 parallel stream might be better, but we need to make sure the API allows to report such condition. - the maximum chunking size, but with 256k I think this is covered. - synchronization internally between threads to avoid deadlocks or poor performances, that can be very hard to debug, so I guess an effort should be provided to explain how things are designed internally.
But this sounds fine in general.
The only thing it does not allow for is one API method to use two or more streams. These may be famous last words, but I don't think that use case will be neccessary for any of our APIs...
as long as the limitation is documented especially in the parts of teh code where the assumption is made, sounds fine.
The last 5 patches with a subject of [DEMO] are *NOT* intended to be committed to the repository. They merely demonstrate the use of data streams for a couple of hypothetical file upload and download APIs. Actually they were mostly to allow me to test the code streams code without messing around with the QEMU migration code.
The immediate use case for this data stream code is Chris' QEMU migration patchset.
The next use case is to allow serial console access to be tunnelled over libvirtd, eg to make 'virsh console GUEST' work remotely. This use case is why I included the support for non-blocking data streams and event loop integration (not required for Chris' migration use case)
Okay, next to individual patches reviews,
Daniel
-- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
-- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
participants (5)
-
Chris Lalancette
-
Daniel P. Berrange
-
Daniel Veillard
-
Paolo Bonzini
-
Paolo Bonzini