[libvirt] [PATCHv2 0/7] interface: new public API for network config change transactions

(I'm sending v2 of this patch series for Michal Privoznik, as I had to tweak some things while integrating with the netcf part that I'm writing, and he will be out of the office tomorrow and thus unable to resend. In addition to the few small things I needed to change to make everything compile and work properly once the build system had a version of netcf supporting the new API, Michal incorporated all the comments from reviewers of the first version before forwarding the patches to me for integration.) This patch series implements three new APIs for the interface driver which support transactional changes to the host's network config - at any point you can begin a transaction (which saves a snapshot of the current config), then make any changes you like to the config, and later either commit those changes (the current implementation just removes the snapshotted files) or rollback to the original config. The actual implementation of this functionality lives in the netcf library; these patches create pass-through functions that call out to netcf on the machine that's running libvirtd. Most importantly, note that uses of "start" in the API names have been changed to "begin", and rather than a single virsh command with multiple subcommands, there are now three separate commands: iface-begin, iface-commit, and iface-rollback. Thanks to using AC_CHECK_LIB in configure.ac, this code can safely be pushed and built on systems that don't yet have a new enough netcf to contain the API extensions - those functions are simply not implemented in that case (and return the appropriate error).

From: Michal Privoznik <mprivozn@redhat.com> This is the API agreed on in: https://www.redhat.com/archives/libvir-list/2011-May/msg00026.html (with a slight name change to use "...begin" rather than "...start"). This implements transactional changes to the host network config. When a transaction is begun with ncf_change_begin(), all other netcf APIs will continue to work as they always have, but a snapshot of the existing config will be taken, allowing reversion (rollback, using ncf_change_rollback()) to the exact state of config at the time ncf_change_begin() was called. Alternately, if it's determined that the new changes are acceptable, ncf_change_commit() can be called, which will eliminate the snapshot and make the changes permanent. As a failsafe measure, if neither ncf_change_commit() or ncf_change_rollback() is called by the next time the system reboots, the netcf-transaction initscript will be automatically called to rollback the changes. --- include/libvirt/libvirt.h.in | 7 +++++++ src/libvirt_public.syms | 3 +++ 2 files changed, 10 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7cd6e13..4a45390 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1405,6 +1405,13 @@ int virInterfaceDestroy (virInterfacePtr iface, int virInterfaceRef (virInterfacePtr iface); int virInterfaceFree (virInterfacePtr iface); +int virInterfaceChangeBegin (virConnectPtr conn, + unsigned int flags); +int virInterfaceChangeCommit (virConnectPtr conn, + unsigned int flags); +int virInterfaceChangeRollback(virConnectPtr conn, + unsigned int flags); + /** * virStoragePool: * diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 0590535..f4f8262 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -442,6 +442,9 @@ LIBVIRT_0.9.2 { virDomainInjectNMI; virDomainScreenshot; virDomainSetSchedulerParametersFlags; + virInterfaceChangeBegin; + virInterfaceChangeCommit; + virInterfaceChangeRollback; } LIBVIRT_0.9.0; # .... define new API here using predicted next version number .... -- 1.7.3.4

-----Original Message----- From: libvir-list-bounces@redhat.com [mailto:libvir-list- bounces@redhat.com] On Behalf Of Laine Stump Sent: Thursday, May 19, 2011 1:51 PM To: libvir-list@redhat.com Subject: [libvirt] [PATCHv2 1/7] interface: new public API for networkconfig change transactions
From: Michal Privoznik <mprivozn@redhat.com>
This is the API agreed on in:
https://www.redhat.com/archives/libvir-list/2011-May/msg00026.html
(with a slight name change to use "...begin" rather than "...start"). This implements transactional changes to the host network config. When a transaction is begun with ncf_change_begin(), all other netcf APIs will continue to work as they always have, but a snapshot of the existing config will be taken, allowing reversion (rollback, using ncf_change_rollback()) to the exact state of config at the time ncf_change_begin() was called. Alternately, if it's determined that the new changes are acceptable, ncf_change_commit() can be called, which will eliminate the snapshot and make the changes permanent.
As a failsafe measure, if neither ncf_change_commit() or ncf_change_rollback() is called by the next time the system reboots, the netcf-transaction initscript will be automatically called to rollback the changes.
Why do you think the default rollback (post reboot, for non-committed transactions) is a good default? When/where is that automatic rollback enforced? (I could not find it in the patches). Does it trigger a log message too? Do you think it may make sense to have that default behavior controlled by a (global, ie, not per-interface) parameter? I am not suggesting the introduction of such parameter (but I would not be against it), but I would suggest a 4th virsh command to list at least the interfaces with a pending (ie, not committed yet) transaction. /Christian
--- include/libvirt/libvirt.h.in | 7 +++++++ src/libvirt_public.syms | 3 +++ 2 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7cd6e13..4a45390 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1405,6 +1405,13 @@ int virInterfaceDestroy (virInterfacePtr iface, int virInterfaceRef (virInterfacePtr iface); int virInterfaceFree (virInterfacePtr iface);
+int virInterfaceChangeBegin (virConnectPtr conn, + unsigned int flags); +int virInterfaceChangeCommit (virConnectPtr conn, + unsigned int flags); +int virInterfaceChangeRollback(virConnectPtr conn, + unsigned int flags); + /** * virStoragePool: * diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 0590535..f4f8262 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -442,6 +442,9 @@ LIBVIRT_0.9.2 { virDomainInjectNMI; virDomainScreenshot; virDomainSetSchedulerParametersFlags; + virInterfaceChangeBegin; + virInterfaceChangeCommit; + virInterfaceChangeRollback; } LIBVIRT_0.9.0;
# .... define new API here using predicted next version number .... -- 1.7.3.4
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 05/19/2011 06:15 PM, Christian Benvenuti (benve) wrote:
-----Original Message----- From: libvir-list-bounces@redhat.com [mailto:libvir-list- bounces@redhat.com] On Behalf Of Laine Stump Sent: Thursday, May 19, 2011 1:51 PM To: libvir-list@redhat.com Subject: [libvirt] [PATCHv2 1/7] interface: new public API for networkconfig change transactions
From: Michal Privoznik<mprivozn@redhat.com>
This is the API agreed on in:
https://www.redhat.com/archives/libvir-list/2011-May/msg00026.html
(with a slight name change to use "...begin" rather than "...start"). This implements transactional changes to the host network config. When a transaction is begun with ncf_change_begin(), all other netcf APIs will continue to work as they always have, but a snapshot of the existing config will be taken, allowing reversion (rollback, using ncf_change_rollback()) to the exact state of config at the time ncf_change_begin() was called. Alternately, if it's determined that the new changes are acceptable, ncf_change_commit() can be called, which will eliminate the snapshot and make the changes permanent.
As a failsafe measure, if neither ncf_change_commit() or ncf_change_rollback() is called by the next time the system reboots, the netcf-transaction initscript will be automatically called to rollback the changes. Why do you think the default rollback (post reboot, for non-committed transactions) is a good default?
It is intended as a failsafe device when the host becomes unstable, and this is the safest default behavior. If someone has gone to the trouble of beginning a transaction, that implies that they are nervous enough about the outcome of the changes that they think they might need to be reverted. And it also implies that they will be monitoring the changes and have ample opportunity to commit the transaction prior to the system rebooting.
When/where is that automatic rollback enforced?
It's done at boot time in an initscript that will be installed by netcf. See: https://fedorahosted.org/pipermail/netcf-devel/2011-May/000562.html
(I could not find it in the patches).
Almost all the functionality lives in the netcf library. the libvirt API just provides a simple pass-through to that functionality.
Does it trigger a log message too?
Ah - that's a good point! the initscript should log a message indicating that it is restoring something rather than just silently doing it. I'll make sure to squash that in before pushing the initscript to netcf.
Do you think it may make sense to have that default behavior controlled by a (global, ie, not per-interface) parameter?
So far you're the first person to ask for that. There's nothing in the API that would prevent us from adding it in, but my current inclination is to get this pushed with the current functionality so people can start using it.
I am not suggesting the introduction of such parameter (but I would not be against it), but I would suggest a 4th virsh command to list at least the interfaces with a pending (ie, not committed yet) transaction.
Another interesting idea, but something that, as we don't currently have a specific need for it, can be done at a later time.

On Thu, May 19, 2011 at 05:15:20PM -0500, Christian Benvenuti (benve) wrote:
As a failsafe measure, if neither ncf_change_commit() or ncf_change_rollback() is called by the next time the system reboots, the netcf-transaction initscript will be automatically called to rollback the changes.
Why do you think the default rollback (post reboot, for non-committed transactions) is a good default?
I think that's the expected one, basically if the interface commands lead to an unreachable host, at least thing are preserved on reboot, that's in-fine the goal of the whole transaction mechanism.
When/where is that automatic rollback enforced? (I could not find it in the patches). Does it trigger a log message too?
Do you think it may make sense to have that default behavior controlled by a (global, ie, not per-interface) parameter?
global parameters to the library would be a really bad idea. a parameter in a shared config file for netcf would be less troublesome. But I would prefer bhaviour controlled from the API, and in that case we can still do this using the flags parameters later on.
I am not suggesting the introduction of such parameter (but I would not be against it), but I would suggest a 4th virsh command to list at least the interfaces with a pending (ie, not committed yet) transaction.
yes that would be an useful extension but it should not prevent pushing the current code. The current API set looks fine to me, so ACK on this patch 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/

From: Michal Privoznik <mprivozn@redhat.com> --- src/driver.h | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-) diff --git a/src/driver.h b/src/driver.h index 450dd53..c9747aa 100644 --- a/src/driver.h +++ b/src/driver.h @@ -865,6 +865,15 @@ typedef int typedef int (*virDrvInterfaceIsActive)(virInterfacePtr iface); +typedef int + (*virDrvInterfaceChangeBegin) (virConnectPtr conn, + unsigned int flags); +typedef int + (*virDrvInterfaceChangeCommit) (virConnectPtr conn, + unsigned int flags); +typedef int + (*virDrvInterfaceChangeRollback)(virConnectPtr conn, + unsigned int flags); typedef struct _virInterfaceDriver virInterfaceDriver; typedef virInterfaceDriver *virInterfaceDriverPtr; @@ -895,6 +904,9 @@ struct _virInterfaceDriver { virDrvInterfaceCreate interfaceCreate; virDrvInterfaceDestroy interfaceDestroy; virDrvInterfaceIsActive interfaceIsActive; + virDrvInterfaceChangeBegin interfaceChangeBegin; + virDrvInterfaceChangeCommit interfaceChangeCommit; + virDrvInterfaceChangeRollback interfaceChangeRollback; }; -- 1.7.3.4

On Thu, May 19, 2011 at 04:51:24PM -0400, Laine Stump wrote:
From: Michal Privoznik <mprivozn@redhat.com>
--- src/driver.h | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/src/driver.h b/src/driver.h index 450dd53..c9747aa 100644 --- a/src/driver.h +++ b/src/driver.h @@ -865,6 +865,15 @@ typedef int typedef int (*virDrvInterfaceIsActive)(virInterfacePtr iface);
+typedef int + (*virDrvInterfaceChangeBegin) (virConnectPtr conn, + unsigned int flags); +typedef int + (*virDrvInterfaceChangeCommit) (virConnectPtr conn, + unsigned int flags); +typedef int + (*virDrvInterfaceChangeRollback)(virConnectPtr conn, + unsigned int flags);
typedef struct _virInterfaceDriver virInterfaceDriver; typedef virInterfaceDriver *virInterfaceDriverPtr; @@ -895,6 +904,9 @@ struct _virInterfaceDriver { virDrvInterfaceCreate interfaceCreate; virDrvInterfaceDestroy interfaceDestroy; virDrvInterfaceIsActive interfaceIsActive; + virDrvInterfaceChangeBegin interfaceChangeBegin; + virDrvInterfaceChangeCommit interfaceChangeCommit; + virDrvInterfaceChangeRollback interfaceChangeRollback; };
derives from 1/7 trivially, 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/

From: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 139 insertions(+), 2 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index ff16c48..786ce15 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -8143,7 +8143,9 @@ error: * @xml: the XML description for the interface, preferably in UTF-8 * @flags: and OR'ed set of extraction flags, not used yet * - * Define an interface (or modify existing interface configuration) + * Define an interface (or modify existing interface configuration). + * This can, however be affected by virInterfaceChangeBegin + * and/or friends. * * Returns NULL in case of error, a pointer to the interface otherwise */ @@ -8189,6 +8191,8 @@ error: * * Undefine an interface, ie remove it from the config. * This does not free the associated virInterfacePtr object. + * This can, however be affected by virInterfaceChangeBegin + * and/or friends. * * Returns 0 in case of success, -1 in case of error */ @@ -8230,7 +8234,9 @@ error: * @iface: pointer to a defined interface * @flags: and OR'ed set of extraction flags, not used yet * - * Activate an interface (ie call "ifup") + * Activate an interface (ie call "ifup"). + * This can, however be affected by virInterfaceChangeBegin + * and/or friends. * * Returns 0 in case of success, -1 in case of error */ @@ -8276,6 +8282,8 @@ error: * deactivate an interface (ie call "ifdown") * This does not remove the interface from the config, and * does not free the associated virInterfacePtr object. + * This can, however be affected by virInterfaceChangeBegin + * and/or friends. * * Returns 0 in case of success and -1 in case of failure. */ @@ -8374,6 +8382,135 @@ virInterfaceFree(virInterfacePtr iface) return 0; } +/** + * virInterfaceChangeBegin: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This functions creates a restore point to which one can return + * later by calling virInterfaceChangeRollback. This function should + * be called before any transaction with interface configuration. + * Once knowing, new configuration works, it can be commited via + * virInterfaceChangeCommit, which frees restore point. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virInterfaceChangeBegin(virConnectPtr conn, unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("conn=%p, flags=%d", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + goto end; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibInterfaceError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + virDispatchError(conn); + goto end; + } + + if (conn->interfaceDriver && conn->interfaceDriver->interfaceChangeBegin) { + ret = conn->interfaceDriver->interfaceChangeBegin(conn, flags); + } else { + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + } + +end: + VIR_DEBUG("returning: %d", ret); + return ret; +} + +/** + * virInterfaceChangeCommit: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This commits the changes made to interfaces and frees restore point + * created by virInterfaceChangeBegin. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virInterfaceChangeCommit(virConnectPtr conn, unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("conn=%p, flags=%d", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + goto end; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibInterfaceError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + virDispatchError(conn); + goto end; + } + + if (conn->interfaceDriver && conn->interfaceDriver->interfaceChangeCommit) { + ret = conn->interfaceDriver->interfaceChangeCommit(conn, flags); + } else { + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + } + +end: + VIR_DEBUG("returning: %d", ret); + return ret; +} + +/** + * virInterfaceChangeRollback: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This cancels changes made to interfaces settings by restoring previous + * state created by virInterfaceChangeBegin. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virInterfaceChangeRollback(virConnectPtr conn, unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("conn=%p, flags=%d", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + goto end; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibInterfaceError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + virDispatchError(conn); + goto end; + } + + if (conn->interfaceDriver && + conn->interfaceDriver->interfaceChangeRollback) { + ret = conn->interfaceDriver->interfaceChangeRollback(conn, flags); + } else { + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + } + +end: + VIR_DEBUG("returning: %d", ret); + return ret; +} /** * virStoragePoolGetConnect: -- 1.7.3.4

On Thu, May 19, 2011 at 04:51:25PM -0400, Laine Stump wrote:
From: Michal Privoznik <mprivozn@redhat.com>
--- src/libvirt.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 139 insertions(+), 2 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c index ff16c48..786ce15 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -8143,7 +8143,9 @@ error: * @xml: the XML description for the interface, preferably in UTF-8 * @flags: and OR'ed set of extraction flags, not used yet * - * Define an interface (or modify existing interface configuration) + * Define an interface (or modify existing interface configuration). + * This can, however be affected by virInterfaceChangeBegin + * and/or friends.
Small nit, I would suggest making the 4 extra comments a bit more explicit that it's about transaction support, something along the lines of: * This can however be affected by the transaction support for * interface configuration change, see virInterfaceChangeBegin(), * virInterfaceChangeCommit() and related functions. [...]
@@ -8374,6 +8382,135 @@ virInterfaceFree(virInterfacePtr iface) return 0; }
+/** + * virInterfaceChangeBegin: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This functions creates a restore point to which one can return + * later by calling virInterfaceChangeRollback. This function should + * be called before any transaction with interface configuration. + * Once knowing, new configuration works, it can be commited via + * virInterfaceChangeCommit, which frees restore point.
which frees the restore point. I would like the description of what happens ona sequence of virInterfaceChangeBegin() virInterfaceChangeBegin() calls for the same connection, is that an error ? Will netcf implement this, this behaviour should probably be documented.
+ * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virInterfaceChangeBegin(virConnectPtr conn, unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("conn=%p, flags=%d", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + goto end; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibInterfaceError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + virDispatchError(conn); + goto end; + } + + if (conn->interfaceDriver && conn->interfaceDriver->interfaceChangeBegin) { + ret = conn->interfaceDriver->interfaceChangeBegin(conn, flags); + } else { + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + } + +end: + VIR_DEBUG("returning: %d", ret); + return ret; +} + +/** + * virInterfaceChangeCommit: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This commits the changes made to interfaces and frees restore point
"frees the restore point"
+ * created by virInterfaceChangeBegin.
Same thing behaviour of virInterfaceChangeCommit() in case there was no Begin() associated should be documented.
+ * Returns 0 in case of success and -1 in case of failure. + */ +int +virInterfaceChangeCommit(virConnectPtr conn, unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("conn=%p, flags=%d", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + goto end; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibInterfaceError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + virDispatchError(conn); + goto end; + } + + if (conn->interfaceDriver && conn->interfaceDriver->interfaceChangeCommit) { + ret = conn->interfaceDriver->interfaceChangeCommit(conn, flags); + } else { + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + } + +end: + VIR_DEBUG("returning: %d", ret); + return ret; +} + +/** + * virInterfaceChangeRollback: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This cancels changes made to interfaces settings by restoring previous + * state created by virInterfaceChangeBegin. + *
Same thing behaviour of virInterfaceChangeRollback() in case there was no Begin() associated should be documented.
+ * Returns 0 in case of success and -1 in case of failure. + */ +int +virInterfaceChangeRollback(virConnectPtr conn, unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("conn=%p, flags=%d", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + goto end; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibInterfaceError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + virDispatchError(conn); + goto end; + } + + if (conn->interfaceDriver && + conn->interfaceDriver->interfaceChangeRollback) { + ret = conn->interfaceDriver->interfaceChangeRollback(conn, flags); + } else { + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + } + +end: + VIR_DEBUG("returning: %d", ret); + return ret; +}
[...] The partial code checking entry points seems fine but the real stuff happens somewhere else in netcf. I would like to have documented the behaviour in corner cases like I described but it would be good to clarify too what happens when 2 libvirt users/program/agents use simultanously those entry points targetting the same node. It seems to me virInterfaceChangeBegin() should be allowed only once on the full host before a Commit(), a Rollback() or a reboot happens, and if it's the case it should be documented in virInterfaceChangeBegin comment too. So with clarifications on semantic, 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 Tue, May 24, 2011 at 02:13:12PM +0800, Daniel Veillard wrote:
On Thu, May 19, 2011 at 04:51:25PM -0400, Laine Stump wrote:
From: Michal Privoznik <mprivozn@redhat.com>
--- src/libvirt.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 139 insertions(+), 2 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c index ff16c48..786ce15 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -8143,7 +8143,9 @@ error: * @xml: the XML description for the interface, preferably in UTF-8 * @flags: and OR'ed set of extraction flags, not used yet * - * Define an interface (or modify existing interface configuration) + * Define an interface (or modify existing interface configuration). + * This can, however be affected by virInterfaceChangeBegin + * and/or friends.
Small nit, I would suggest making the 4 extra comments a bit more explicit that it's about transaction support, something along the lines of:
* This can however be affected by the transaction support for * interface configuration change, see virInterfaceChangeBegin(), * virInterfaceChangeCommit() and related functions.
[...]
@@ -8374,6 +8382,135 @@ virInterfaceFree(virInterfacePtr iface) return 0; }
+/** + * virInterfaceChangeBegin: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This functions creates a restore point to which one can return + * later by calling virInterfaceChangeRollback. This function should + * be called before any transaction with interface configuration. + * Once knowing, new configuration works, it can be commited via + * virInterfaceChangeCommit, which frees restore point.
which frees the restore point.
I would like the description of what happens ona sequence of virInterfaceChangeBegin() virInterfaceChangeBegin()
calls for the same connection, is that an error ? Will netcf implement this, this behaviour should probably be documented.
It should return the VIR_ERR_INVALID_OPERATION code with a message that a transaction is already active.
+/** + * virInterfaceChangeCommit: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This commits the changes made to interfaces and frees restore point
"frees the restore point"
+ * created by virInterfaceChangeBegin.
Same thing behaviour of virInterfaceChangeCommit() in case there was no Begin() associated should be documented.
Likewise VIR_ERR_INVALID_OPERATION Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 05/24/2011 04:44 AM, Daniel P. Berrange wrote:
On Tue, May 24, 2011 at 02:13:12PM +0800, Daniel Veillard wrote:
From: Michal Privoznik<mprivozn@redhat.com>
--- src/libvirt.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 139 insertions(+), 2 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c index ff16c48..786ce15 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -8143,7 +8143,9 @@ error: * @xml: the XML description for the interface, preferably in UTF-8 * @flags: and OR'ed set of extraction flags, not used yet * - * Define an interface (or modify existing interface configuration) + * Define an interface (or modify existing interface configuration). + * This can, however be affected by virInterfaceChangeBegin + * and/or friends. Small nit, I would suggest making the 4 extra comments a bit more explicit that it's about transaction support, something along the
On Thu, May 19, 2011 at 04:51:25PM -0400, Laine Stump wrote: lines of:
* This can however be affected by the transaction support for * interface configuration change, see virInterfaceChangeBegin(), * virInterfaceChangeCommit() and related functions.
I made the comments more verbose (possibly *too* verbose) in the upcoming V3 of this patch. If the explanation is too much, you can let me know and I'll dial it back before pushing.
[...]
@@ -8374,6 +8382,135 @@ virInterfaceFree(virInterfacePtr iface) return 0; }
+/** + * virInterfaceChangeBegin: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This functions creates a restore point to which one can return + * later by calling virInterfaceChangeRollback. This function should + * be called before any transaction with interface configuration. + * Once knowing, new configuration works, it can be commited via + * virInterfaceChangeCommit, which frees restore point. which frees the restore point.
I would like the description of what happens ona sequence of virInterfaceChangeBegin() virInterfaceChangeBegin()
calls for the same connection, is that an error ? Will netcf implement this, this behaviour should probably be documented. It should return the VIR_ERR_INVALID_OPERATION code with a message that a transaction is already active.
Yes, netcf checks for this and returns an error (likewise if commit or rollback is called when there is no open transaction). I will make sure this is translated to VIR_ERR_INVALID_OPERATION so that it is logged properly. (it's good that you pointed this out now, because netcf had been just returning a generic error for this, so I modified the netcf patches to return a new unique code, and have updated patch 6/7 (the netcf driver additions) to recognize this new code when it's available).
+/** + * virInterfaceChangeCommit: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This commits the changes made to interfaces and frees restore point "frees the restore point"
+ * created by virInterfaceChangeBegin. Same thing behaviour of virInterfaceChangeCommit() in case there was no Begin() associated should be documented. Likewise VIR_ERR_INVALID_OPERATION
Yup.

From: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 176 insertions(+), 2 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index ff16c48..12b849a 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -8143,7 +8143,18 @@ error: * @xml: the XML description for the interface, preferably in UTF-8 * @flags: and OR'ed set of extraction flags, not used yet * - * Define an interface (or modify existing interface configuration) + + * Define an interface (or modify existing interface configuration). + * + * Normally this change in the interface configuration is immediately + * permanent/persistent, but if virInterfaceChangeBegin() has been + * previously called (i.e. if an interface config transaction is + * open), the new interface definition will only become permanent if + * virInterfaceChangeCommit() is called prior to the next reboot of + * the system running libvirtd. Prior to that time, it can be + * explicitly removed using virInterfaceChangeRollback(), or will be + * automatically removed during the next reboot of the system running + * libvirtd. * * Returns NULL in case of error, a pointer to the interface otherwise */ @@ -8190,6 +8201,16 @@ error: * Undefine an interface, ie remove it from the config. * This does not free the associated virInterfacePtr object. * + * Normally this change in the interface configuration is + * permanent/persistent, but if virInterfaceChangeBegin() has been + * previously called (i.e. if an interface config transaction is + * open), the removal of the interface definition will only become + * permanent if virInterfaceChangeCommit() is called prior to the next + * reboot of the system running libvirtd. Prior to that time, the + * definition can be explicitly restored using + * virInterfaceChangeRollback(), or will be automatically restored + * during the next reboot of the system running libvirtd. + * * Returns 0 in case of success, -1 in case of error */ int @@ -8230,7 +8251,12 @@ error: * @iface: pointer to a defined interface * @flags: and OR'ed set of extraction flags, not used yet * - * Activate an interface (ie call "ifup") + * Activate an interface (i.e. call "ifup"). + * + * If there was an open network config transaction at the time this + * interface was defined (that is, if virInterfaceChangeBegin() had + * been called), the interface will be brought back down (and then + * undefined) if virInterfaceChangeRollback() is called. * * Returns 0 in case of success, -1 in case of error */ @@ -8277,6 +8303,13 @@ error: * This does not remove the interface from the config, and * does not free the associated virInterfacePtr object. * + + * If there is an open network config transaction at the time this + * interface is destroyed (that is, if virInterfaceChangeBegin() had + * been called), and if the interface is later undefined and then + * virInterfaceChangeRollback() is called, the restoral of the + * interface definition will also bring the interface back up. + * * Returns 0 in case of success and -1 in case of failure. */ int @@ -8374,6 +8407,147 @@ virInterfaceFree(virInterfacePtr iface) return 0; } +/** + * virInterfaceChangeBegin: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This functions creates a restore point to which one can return + * later by calling virInterfaceChangeRollback(). This function should + * be called before any transaction with interface configuration. + * Once knowing, new configuration works, it can be commited via + * virInterfaceChangeCommit(), which frees the restore point. + * + * If virInterfaceChangeBegin() is called when a transaction is + * already opened, this function will fail, and a + * VIR_ERR_INVALID_OPERATION will be logged. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virInterfaceChangeBegin(virConnectPtr conn, unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("conn=%p, flags=%d", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + goto end; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibInterfaceError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + virDispatchError(conn); + goto end; + } + + if (conn->interfaceDriver && conn->interfaceDriver->interfaceChangeBegin) { + ret = conn->interfaceDriver->interfaceChangeBegin(conn, flags); + } else { + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + } + +end: + VIR_DEBUG("returning: %d", ret); + return ret; +} + +/** + * virInterfaceChangeCommit: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This commits the changes made to interfaces and frees the restore point + * created by virInterfaceChangeBegin(). + * + * If virInterfaceChangeCommit() is called when a transaction is not + * opened, this function will fail, and a VIR_ERR_INVALID_OPERATION + * will be logged. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virInterfaceChangeCommit(virConnectPtr conn, unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("conn=%p, flags=%d", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + goto end; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibInterfaceError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + virDispatchError(conn); + goto end; + } + + if (conn->interfaceDriver && conn->interfaceDriver->interfaceChangeCommit) { + ret = conn->interfaceDriver->interfaceChangeCommit(conn, flags); + } else { + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + } + +end: + VIR_DEBUG("returning: %d", ret); + return ret; +} + +/** + * virInterfaceChangeRollback: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This cancels changes made to interfaces settings by restoring previous + * state created by virInterfaceChangeBegin(). + * + * If virInterfaceChangeRollback() is called when a transaction is not + * opened, this function will fail, and a VIR_ERR_INVALID_OPERATION + * will be logged. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virInterfaceChangeRollback(virConnectPtr conn, unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("conn=%p, flags=%d", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + goto end; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibInterfaceError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + virDispatchError(conn); + goto end; + } + + if (conn->interfaceDriver && + conn->interfaceDriver->interfaceChangeRollback) { + ret = conn->interfaceDriver->interfaceChangeRollback(conn, flags); + } else { + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + } + +end: + VIR_DEBUG("returning: %d", ret); + return ret; +} /** * virStoragePoolGetConnect: -- 1.7.3.4

On 05/26/2011 01:33 AM, Laine Stump wrote:
From: Michal Privoznik<mprivozn@redhat.com>
--- src/libvirt.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 176 insertions(+), 2 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c index ff16c48..12b849a 100644 --- a/src/libvirt.c +++ b/src/libvirt.c
Dan: I accidentally deleted your reply to this message straight past the trash (while I thought shift-Del would cut text to the clipboard, it actually did a "permanently delete message"), so I don't have it to reply to anymore, but I see what you mean about the style of the functions being different, and have changed them in an upcoming repost.

On Thu, May 19, 2011 at 04:51:25PM -0400, Laine Stump wrote:
From: Michal Privoznik <mprivozn@redhat.com>
--- src/libvirt.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 139 insertions(+), 2 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c index ff16c48..786ce15 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -8143,7 +8143,9 @@ error: * @xml: the XML description for the interface, preferably in UTF-8 * @flags: and OR'ed set of extraction flags, not used yet * - * Define an interface (or modify existing interface configuration) + * Define an interface (or modify existing interface configuration). + * This can, however be affected by virInterfaceChangeBegin + * and/or friends. * * Returns NULL in case of error, a pointer to the interface otherwise */ @@ -8189,6 +8191,8 @@ error: * * Undefine an interface, ie remove it from the config. * This does not free the associated virInterfacePtr object. + * This can, however be affected by virInterfaceChangeBegin + * and/or friends. * * Returns 0 in case of success, -1 in case of error */ @@ -8230,7 +8234,9 @@ error: * @iface: pointer to a defined interface * @flags: and OR'ed set of extraction flags, not used yet * - * Activate an interface (ie call "ifup") + * Activate an interface (ie call "ifup"). + * This can, however be affected by virInterfaceChangeBegin + * and/or friends. * * Returns 0 in case of success, -1 in case of error */ @@ -8276,6 +8282,8 @@ error: * deactivate an interface (ie call "ifdown") * This does not remove the interface from the config, and * does not free the associated virInterfacePtr object. + * This can, however be affected by virInterfaceChangeBegin + * and/or friends. * * Returns 0 in case of success and -1 in case of failure. */ @@ -8374,6 +8382,135 @@ virInterfaceFree(virInterfacePtr iface) return 0; }
+/** + * virInterfaceChangeBegin: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This functions creates a restore point to which one can return + * later by calling virInterfaceChangeRollback. This function should + * be called before any transaction with interface configuration. + * Once knowing, new configuration works, it can be commited via + * virInterfaceChangeCommit, which frees restore point. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virInterfaceChangeBegin(virConnectPtr conn, unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("conn=%p, flags=%d", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + goto end; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibInterfaceError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + virDispatchError(conn); + goto end; + } + + if (conn->interfaceDriver && conn->interfaceDriver->interfaceChangeBegin) { + ret = conn->interfaceDriver->interfaceChangeBegin(conn, flags); + } else { + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + } + +end: + VIR_DEBUG("returning: %d", ret); + return ret; +} + +/** + * virInterfaceChangeCommit: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This commits the changes made to interfaces and frees restore point + * created by virInterfaceChangeBegin. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virInterfaceChangeCommit(virConnectPtr conn, unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("conn=%p, flags=%d", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + goto end; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibInterfaceError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + virDispatchError(conn); + goto end; + } + + if (conn->interfaceDriver && conn->interfaceDriver->interfaceChangeCommit) { + ret = conn->interfaceDriver->interfaceChangeCommit(conn, flags); + } else { + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + } + +end: + VIR_DEBUG("returning: %d", ret); + return ret; +} + +/** + * virInterfaceChangeRollback: + * @conn: pointer to hypervisor connection + * @flags: flags, not used yet + * + * This cancels changes made to interfaces settings by restoring previous + * state created by virInterfaceChangeBegin. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virInterfaceChangeRollback(virConnectPtr conn, unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("conn=%p, flags=%d", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + goto end; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibInterfaceError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + virDispatchError(conn); + goto end; + } + + if (conn->interfaceDriver && + conn->interfaceDriver->interfaceChangeRollback) { + ret = conn->interfaceDriver->interfaceChangeRollback(conn, flags); + } else { + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + } + +end: + VIR_DEBUG("returning: %d", ret); + return ret; +}
The code pattern / flow control in these 3 APIs is completely different from all the other public APIs, for no obvious benefit. It needs to be updated to follow the same code pattern as the existing APIs impls. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

From: Michal Privoznik <mprivozn@redhat.com> --- src/remote/remote_driver.c | 3 +++ src/remote/remote_protocol.x | 17 ++++++++++++++++- src/remote_protocol-structs | 9 +++++++++ 3 files changed, 28 insertions(+), 1 deletions(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8c69743..5d837d1 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6909,6 +6909,9 @@ static virInterfaceDriver interface_driver = { .interfaceCreate = remoteInterfaceCreate, /* 0.7.2 */ .interfaceDestroy = remoteInterfaceDestroy, /* 0.7.2 */ .interfaceIsActive = remoteInterfaceIsActive, /* 0.7.3 */ + .interfaceChangeBegin = remoteInterfaceChangeBegin, /* 0.9.2 */ + .interfaceChangeCommit = remoteInterfaceChangeCommit, /* 0.9.2 */ + .interfaceChangeRollback = remoteInterfaceChangeRollback, /* 0.9.2 */ }; static virStorageDriver storage_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index c784dba..12fd395 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1164,6 +1164,18 @@ struct remote_interface_destroy_args { unsigned int flags; }; +struct remote_interface_change_begin_args { + unsigned int flags; +}; + +struct remote_interface_change_commit_args { + unsigned int flags; +}; + +struct remote_interface_change_rollback_args { + unsigned int flags; +}; + /* Auth calls: */ @@ -2291,8 +2303,11 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MIGRATE_PERFORM3 = 216, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_MIGRATE_FINISH3 = 217, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_MIGRATE_CONFIRM3 = 218, /* skipgen skipgen */ - REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS_FLAGS = 219 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS_FLAGS = 219, /* skipgen skipgen */ + REMOTE_PROC_INTERFACE_CHANGE_BEGIN = 220, /* autogen autogen */ + REMOTE_PROC_INTERFACE_CHANGE_COMMIT = 221, /* autogen autogen */ + REMOTE_PROC_INTERFACE_CHANGE_ROLLBACK = 222 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? * Nice isn't it. Please keep it this way when adding more. diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 414b4d5..a28cd13 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -803,6 +803,15 @@ struct remote_interface_destroy_args { remote_nonnull_interface iface; u_int flags; }; +struct remote_interface_change_begin_args { + unsigned int flags; +}; +struct remote_interface_change_commit_args { + unsigned int flags; +}; +struct remote_interface_change_rollback_args { + unsigned int flags; +}; struct remote_auth_list_ret { struct { u_int types_len; -- 1.7.3.4

On Thu, May 19, 2011 at 04:51:26PM -0400, Laine Stump wrote:
From: Michal Privoznik <mprivozn@redhat.com>
--- src/remote/remote_driver.c | 3 +++ src/remote/remote_protocol.x | 17 ++++++++++++++++- src/remote_protocol-structs | 9 +++++++++ 3 files changed, 28 insertions(+), 1 deletions(-)
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8c69743..5d837d1 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6909,6 +6909,9 @@ static virInterfaceDriver interface_driver = { .interfaceCreate = remoteInterfaceCreate, /* 0.7.2 */ .interfaceDestroy = remoteInterfaceDestroy, /* 0.7.2 */ .interfaceIsActive = remoteInterfaceIsActive, /* 0.7.3 */ + .interfaceChangeBegin = remoteInterfaceChangeBegin, /* 0.9.2 */ + .interfaceChangeCommit = remoteInterfaceChangeCommit, /* 0.9.2 */ + .interfaceChangeRollback = remoteInterfaceChangeRollback, /* 0.9.2 */ };
static virStorageDriver storage_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index c784dba..12fd395 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1164,6 +1164,18 @@ struct remote_interface_destroy_args { unsigned int flags; };
+struct remote_interface_change_begin_args { + unsigned int flags; +}; + +struct remote_interface_change_commit_args { + unsigned int flags; +}; + +struct remote_interface_change_rollback_args { + unsigned int flags; +}; +
/* Auth calls: */
@@ -2291,8 +2303,11 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MIGRATE_PERFORM3 = 216, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_MIGRATE_FINISH3 = 217, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_MIGRATE_CONFIRM3 = 218, /* skipgen skipgen */ - REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS_FLAGS = 219 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS_FLAGS = 219, /* skipgen skipgen */ + REMOTE_PROC_INTERFACE_CHANGE_BEGIN = 220, /* autogen autogen */
+ REMOTE_PROC_INTERFACE_CHANGE_COMMIT = 221, /* autogen autogen */ + REMOTE_PROC_INTERFACE_CHANGE_ROLLBACK = 222 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? * Nice isn't it. Please keep it this way when adding more. diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 414b4d5..a28cd13 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -803,6 +803,15 @@ struct remote_interface_destroy_args { remote_nonnull_interface iface; u_int flags; }; +struct remote_interface_change_begin_args { + unsigned int flags; +}; +struct remote_interface_change_commit_args { + unsigned int flags; +}; +struct remote_interface_change_rollback_args { + unsigned int flags; +}; struct remote_auth_list_ret { struct { u_int types_len;
Looks fine, the cleanup on remote stub generation really make those remote support patches way nicer :-) 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/

From: Michal Privoznik <mprivozn@redhat.com> This implements the commands iface-begin, iface-commit, and iface-rollback, which simply call the corresponding functions in the libvirt API. --- tools/virsh.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 103 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index c2f4de6..6d72c75 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -5118,6 +5118,103 @@ cmdInterfaceDestroy(vshControl *ctl, const vshCmd *cmd) return ret; } +/* + * "iface-begin" command + */ +static const vshCmdInfo info_interface_begin[] = { + {"help", N_("create a snapshot of current interfaces settings, " + "which can be later commited (iface-commit) or " + "restored (iface-rollback)")}, + {"desc", N_("Create a restore point for interfaces settings")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_interface_begin[] = { + {NULL, 0, 0, NULL} +}; + +static bool +cmdInterfaceBegin(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + bool ret = false; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto end; + + if (virInterfaceChangeBegin(ctl->conn, 0) < 0) { + vshError(ctl, "%s", _("Failed to begin network config change transaction")); + } else { + vshPrint(ctl, "%s", _("Network config change transaction started\n")); + ret = true; + } + +end: + return ret; +} + +/* + * "iface-commit" command + */ +static const vshCmdInfo info_interface_commit[] = { + {"help", N_("commit changes made since iface-begin and free restore point")}, + {"desc", N_("commit changes and free restore point")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_interface_commit[] = { + {NULL, 0, 0, NULL} +}; + +static bool +cmdInterfaceCommit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + bool ret = false; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto end; + + if (virInterfaceChangeCommit(ctl->conn, 0) < 0) { + vshError(ctl, "%s", _("Failed to commit network config change transaction")); + } else { + vshPrint(ctl, "%s", _("Network config change transaction committed\n")); + ret = true; + } + +end: + return ret; +} + +/* + * "iface-rollback" command + */ +static const vshCmdInfo info_interface_rollback[] = { + {"help", N_("rollback to previous saved configuration created via iface-begin")}, + {"desc", N_("rollback to previous restore point")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_interface_rollback[] = { + {NULL, 0, 0, NULL} +}; + +static bool +cmdInterfaceRollback(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + bool ret = false; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto end; + + if (virInterfaceChangeRollback(ctl->conn, 0) < 0) { + vshError(ctl, "%s", _("Failed to rollback network config change transaction")); + } else { + vshPrint(ctl, "%s", _("Network config change transaction rolled back\n")); + ret = true; + } + +end: + return ret; +} /* * "nwfilter-define" command @@ -10933,6 +11030,12 @@ static const vshCmdDef ifaceCmds[] = { info_interface_start, 0}, {"iface-undefine", cmdInterfaceUndefine, opts_interface_undefine, info_interface_undefine, 0}, + {"iface-begin", cmdInterfaceBegin, opts_interface_begin, + info_interface_begin, 0}, + {"iface-commit", cmdInterfaceCommit, opts_interface_commit, + info_interface_commit, 0}, + {"iface-rollback", cmdInterfaceRollback, opts_interface_rollback, + info_interface_rollback, 0}, {NULL, NULL, NULL, NULL, 0} }; -- 1.7.3.4

On Thu, May 19, 2011 at 04:51:27PM -0400, Laine Stump wrote:
From: Michal Privoznik <mprivozn@redhat.com>
This implements the commands iface-begin, iface-commit, and iface-rollback, which simply call the corresponding functions in the libvirt API. --- tools/virsh.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 103 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index c2f4de6..6d72c75 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -5118,6 +5118,103 @@ cmdInterfaceDestroy(vshControl *ctl, const vshCmd *cmd) return ret; }
+/* + * "iface-begin" command + */ +static const vshCmdInfo info_interface_begin[] = { + {"help", N_("create a snapshot of current interfaces settings, " + "which can be later commited (iface-commit) or " + "restored (iface-rollback)")}, + {"desc", N_("Create a restore point for interfaces settings")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_interface_begin[] = { + {NULL, 0, 0, NULL} +}; + +static bool +cmdInterfaceBegin(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + bool ret = false; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto end; + + if (virInterfaceChangeBegin(ctl->conn, 0) < 0) { + vshError(ctl, "%s", _("Failed to begin network config change transaction")); + } else { + vshPrint(ctl, "%s", _("Network config change transaction started\n")); + ret = true; + } + +end: + return ret; +} + +/* + * "iface-commit" command + */ +static const vshCmdInfo info_interface_commit[] = { + {"help", N_("commit changes made since iface-begin and free restore point")}, + {"desc", N_("commit changes and free restore point")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_interface_commit[] = { + {NULL, 0, 0, NULL} +}; + +static bool +cmdInterfaceCommit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + bool ret = false; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto end; + + if (virInterfaceChangeCommit(ctl->conn, 0) < 0) { + vshError(ctl, "%s", _("Failed to commit network config change transaction")); + } else { + vshPrint(ctl, "%s", _("Network config change transaction committed\n")); + ret = true; + } + +end: + return ret; +} + +/* + * "iface-rollback" command + */ +static const vshCmdInfo info_interface_rollback[] = { + {"help", N_("rollback to previous saved configuration created via iface-begin")}, + {"desc", N_("rollback to previous restore point")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_interface_rollback[] = { + {NULL, 0, 0, NULL} +}; + +static bool +cmdInterfaceRollback(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + bool ret = false; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto end; + + if (virInterfaceChangeRollback(ctl->conn, 0) < 0) { + vshError(ctl, "%s", _("Failed to rollback network config change transaction")); + } else { + vshPrint(ctl, "%s", _("Network config change transaction rolled back\n")); + ret = true; + } + +end: + return ret; +}
/* * "nwfilter-define" command @@ -10933,6 +11030,12 @@ static const vshCmdDef ifaceCmds[] = { info_interface_start, 0}, {"iface-undefine", cmdInterfaceUndefine, opts_interface_undefine, info_interface_undefine, 0}, + {"iface-begin", cmdInterfaceBegin, opts_interface_begin, + info_interface_begin, 0}, + {"iface-commit", cmdInterfaceCommit, opts_interface_commit, + info_interface_commit, 0}, + {"iface-rollback", cmdInterfaceRollback, opts_interface_rollback, + info_interface_rollback, 0}, {NULL, NULL, NULL, NULL, 0} };
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 Thu, May 19, 2011 at 04:51:27PM -0400, Laine Stump wrote:
From: Michal Privoznik <mprivozn@redhat.com>
This implements the commands iface-begin, iface-commit, and iface-rollback, which simply call the corresponding functions in the libvirt API. --- tools/virsh.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 103 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index c2f4de6..6d72c75 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -5118,6 +5118,103 @@ cmdInterfaceDestroy(vshControl *ctl, const vshCmd *cmd) return ret; }
+/* + * "iface-begin" command + */ +static const vshCmdInfo info_interface_begin[] = { + {"help", N_("create a snapshot of current interfaces settings, " + "which can be later commited (iface-commit) or " + "restored (iface-rollback)")}, + {"desc", N_("Create a restore point for interfaces settings")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_interface_begin[] = { + {NULL, 0, 0, NULL} +}; + +static bool +cmdInterfaceBegin(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + bool ret = false; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto end; + + if (virInterfaceChangeBegin(ctl->conn, 0) < 0) { + vshError(ctl, "%s", _("Failed to begin network config change transaction")); + } else { + vshPrint(ctl, "%s", _("Network config change transaction started\n")); + ret = true; + } + +end: + return ret; +} + +/* + * "iface-commit" command + */ +static const vshCmdInfo info_interface_commit[] = { + {"help", N_("commit changes made since iface-begin and free restore point")}, + {"desc", N_("commit changes and free restore point")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_interface_commit[] = { + {NULL, 0, 0, NULL} +}; + +static bool +cmdInterfaceCommit(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + bool ret = false; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto end; + + if (virInterfaceChangeCommit(ctl->conn, 0) < 0) { + vshError(ctl, "%s", _("Failed to commit network config change transaction")); + } else { + vshPrint(ctl, "%s", _("Network config change transaction committed\n")); + ret = true; + } + +end: + return ret; +} + +/* + * "iface-rollback" command + */ +static const vshCmdInfo info_interface_rollback[] = { + {"help", N_("rollback to previous saved configuration created via iface-begin")}, + {"desc", N_("rollback to previous restore point")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_interface_rollback[] = { + {NULL, 0, 0, NULL} +}; + +static bool +cmdInterfaceRollback(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + bool ret = false; + + if (!vshConnectionUsability(ctl, ctl->conn)) + goto end; + + if (virInterfaceChangeRollback(ctl->conn, 0) < 0) { + vshError(ctl, "%s", _("Failed to rollback network config change transaction")); + } else { + vshPrint(ctl, "%s", _("Network config change transaction rolled back\n")); + ret = true; + } + +end: + return ret; +}
The code patterns in these methods are rather different from the normal practice used in virsh. Also 'end:' is also not following the hacking guidelines for naming conventions. These methods should be updated to follow the normal coding pattern. eg as per the cmdCapabilities() method style static bool cmdInterfaceRollback(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) { if (!vshConnectionUsability(ctl, ctl->conn)) return false; if (virInterfaceChangeRollback(ctl->conn, 0) < 0) { vshError(ctl, "%s", _("Failed to rollback network config change transaction")); return false; } vshPrint(ctl, "%s", _("Network config change transaction rolled back\n")); return true; } Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 05/26/2011 04:31 AM, Daniel P. Berrange wrote:
On Thu, May 19, 2011 at 04:51:27PM -0400, Laine Stump wrote:
From: Michal Privoznik<mprivozn@redhat.com>
This implements the commands iface-begin, iface-commit, and iface-rollback, which simply call the corresponding functions in the libvirt API. --- tools/virsh.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 103 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index c2f4de6..6d72c75 100644 --- a/tools/virsh.c +++ b/tools/virsh.c
The code patterns in these methods are rather different from the normal practice used in virsh. Also 'end:' is also not following the hacking guidelines for naming conventions. These methods should be updated to follow the normal coding pattern. eg as per the cmdCapabilities() method style
static bool cmdInterfaceRollback(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) { if (!vshConnectionUsability(ctl, ctl->conn)) return false;
if (virInterfaceChangeRollback(ctl->conn, 0)< 0) { vshError(ctl, "%s", _("Failed to rollback network config change transaction")); return false; }
vshPrint(ctl, "%s", _("Network config change transaction rolled back\n")); return true; }
Taken care of in the next version.

From: Michal Privoznik <mprivozn@redhat.com> This is the functionality at the end of the libvirt part of the call chain - for each function, the corresponding netcf API is called. --- configure.ac | 5 +++ src/interface/netcf_driver.c | 76 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 0 deletions(-) diff --git a/configure.ac b/configure.ac index 8db3226..dbec54c 100644 --- a/configure.ac +++ b/configure.ac @@ -1491,6 +1491,11 @@ if test "$with_netcf" = "yes" || test "$with_netcf" = "check"; then if test "$with_netcf" = "yes" ; then AC_DEFINE_UNQUOTED([WITH_NETCF], 1, [whether libnetcf is available to configure physical host network interfaces]) + AC_CHECK_LIB([netcf], [ncf_change_begin], [netcf_transactions=1], [netcf_transactions=0]) + if test "$netcf_transactions" = "1" ; then + AC_DEFINE_UNQUOTED([HAVE_NETCF_TRANSACTIONS], ["1"], + [we have sufficiently new version of netcf for transaction network API]) + fi fi fi AM_CONDITIONAL([WITH_NETCF], [test "$with_netcf" = "yes"]) diff --git a/src/interface/netcf_driver.c b/src/interface/netcf_driver.c index d5b401a..c94cbd5 100644 --- a/src/interface/netcf_driver.c +++ b/src/interface/netcf_driver.c @@ -540,6 +540,77 @@ cleanup: return ret; } +#ifdef HAVE_NETCF_TRANSACTIONS +static int interfaceChangeBegin(virConnectPtr conn ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct interface_driver *driver = conn->interfacePrivateData; + int ret; + + virCheckFlags(0, -1); /* currently flags must be 0 */ + + interfaceDriverLock(driver); + + ret = ncf_change_begin(driver->netcf, 0); + if (ret < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + interfaceReportError(netcf_to_vir_err(errcode), + _("failed to begin transaction (netcf: %s - %s)"), + errmsg, details ? details : ""); + } + + interfaceDriverUnlock(driver); + return ret; +} + +static int interfaceChangeCommit(virConnectPtr conn ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct interface_driver *driver = conn->interfacePrivateData; + int ret; + + virCheckFlags(0, -1); /* currently flags must be 0 */ + + interfaceDriverLock(driver); + + ret = ncf_change_commit(driver->netcf, 0); + if (ret < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + interfaceReportError(netcf_to_vir_err(errcode), + _("failed to begin transaction (netcf: %s - %s)"), + errmsg, details ? details : ""); + } + + interfaceDriverUnlock(driver); + return ret; +} + +static int interfaceChangeRollback(virConnectPtr conn ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct interface_driver *driver = conn->interfacePrivateData; + int ret; + + virCheckFlags(0, -1); /* currently flags must be 0 */ + + interfaceDriverLock(driver); + + ret = ncf_change_rollback(driver->netcf, 0); + if (ret < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + interfaceReportError(netcf_to_vir_err(errcode), + _("failed to begin transaction (netcf: %s - %s)"), + errmsg, details ? details : ""); + } + + interfaceDriverUnlock(driver); + return ret; +} +#endif /* HAVE_NETCF_TRANSACTIONS */ + static virInterfaceDriver interfaceDriver = { "Interface", .open = interfaceOpenInterface, /* 0.7.0 */ @@ -556,6 +627,11 @@ static virInterfaceDriver interfaceDriver = { .interfaceCreate = interfaceCreate, /* 0.7.0 */ .interfaceDestroy = interfaceDestroy, /* 0.7.0 */ .interfaceIsActive = interfaceIsActive, /* 0.7.3 */ +#ifdef HAVE_NETCF_TRANSACTIONS + .interfaceChangeBegin = interfaceChangeBegin, /* 0.9.2 */ + .interfaceChangeCommit = interfaceChangeCommit, /* 0.9.2 */ + .interfaceChangeRollback = interfaceChangeRollback, /* 0.9.2 */ +#endif /* HAVE_NETCF_TRANSACTIONS */ }; int interfaceRegister(void) { -- 1.7.3.4

On Thu, May 19, 2011 at 04:51:28PM -0400, Laine Stump wrote:
From: Michal Privoznik <mprivozn@redhat.com>
This is the functionality at the end of the libvirt part of the call chain - for each function, the corresponding netcf API is called. --- configure.ac | 5 +++ src/interface/netcf_driver.c | 76 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 0 deletions(-)
diff --git a/configure.ac b/configure.ac index 8db3226..dbec54c 100644 --- a/configure.ac +++ b/configure.ac @@ -1491,6 +1491,11 @@ if test "$with_netcf" = "yes" || test "$with_netcf" = "check"; then if test "$with_netcf" = "yes" ; then AC_DEFINE_UNQUOTED([WITH_NETCF], 1, [whether libnetcf is available to configure physical host network interfaces]) + AC_CHECK_LIB([netcf], [ncf_change_begin], [netcf_transactions=1], [netcf_transactions=0]) + if test "$netcf_transactions" = "1" ; then + AC_DEFINE_UNQUOTED([HAVE_NETCF_TRANSACTIONS], ["1"], + [we have sufficiently new version of netcf for transaction network API]) + fi fi fi AM_CONDITIONAL([WITH_NETCF], [test "$with_netcf" = "yes"]) diff --git a/src/interface/netcf_driver.c b/src/interface/netcf_driver.c index d5b401a..c94cbd5 100644 --- a/src/interface/netcf_driver.c +++ b/src/interface/netcf_driver.c @@ -540,6 +540,77 @@ cleanup: return ret; }
+#ifdef HAVE_NETCF_TRANSACTIONS +static int interfaceChangeBegin(virConnectPtr conn ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct interface_driver *driver = conn->interfacePrivateData; + int ret; + + virCheckFlags(0, -1); /* currently flags must be 0 */ + + interfaceDriverLock(driver); + + ret = ncf_change_begin(driver->netcf, 0); + if (ret < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + interfaceReportError(netcf_to_vir_err(errcode), + _("failed to begin transaction (netcf: %s - %s)"), + errmsg, details ? details : ""); + } + + interfaceDriverUnlock(driver); + return ret; +} + +static int interfaceChangeCommit(virConnectPtr conn ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct interface_driver *driver = conn->interfacePrivateData; + int ret; + + virCheckFlags(0, -1); /* currently flags must be 0 */ + + interfaceDriverLock(driver); + + ret = ncf_change_commit(driver->netcf, 0); + if (ret < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + interfaceReportError(netcf_to_vir_err(errcode), + _("failed to begin transaction (netcf: %s - %s)"), + errmsg, details ? details : ""); + } + + interfaceDriverUnlock(driver); + return ret; +} + +static int interfaceChangeRollback(virConnectPtr conn ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED) +{ + struct interface_driver *driver = conn->interfacePrivateData; + int ret; + + virCheckFlags(0, -1); /* currently flags must be 0 */ + + interfaceDriverLock(driver); + + ret = ncf_change_rollback(driver->netcf, 0); + if (ret < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + interfaceReportError(netcf_to_vir_err(errcode), + _("failed to begin transaction (netcf: %s - %s)"), + errmsg, details ? details : ""); + } + + interfaceDriverUnlock(driver); + return ret; +} +#endif /* HAVE_NETCF_TRANSACTIONS */ + static virInterfaceDriver interfaceDriver = { "Interface", .open = interfaceOpenInterface, /* 0.7.0 */ @@ -556,6 +627,11 @@ static virInterfaceDriver interfaceDriver = { .interfaceCreate = interfaceCreate, /* 0.7.0 */ .interfaceDestroy = interfaceDestroy, /* 0.7.0 */ .interfaceIsActive = interfaceIsActive, /* 0.7.3 */ +#ifdef HAVE_NETCF_TRANSACTIONS + .interfaceChangeBegin = interfaceChangeBegin, /* 0.9.2 */ + .interfaceChangeCommit = interfaceChangeCommit, /* 0.9.2 */ + .interfaceChangeRollback = interfaceChangeRollback, /* 0.9.2 */ +#endif /* HAVE_NETCF_TRANSACTIONS */ };
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 Thu, May 19, 2011 at 04:51:28PM -0400, Laine Stump wrote:
From: Michal Privoznik <mprivozn@redhat.com>
This is the functionality at the end of the libvirt part of the call chain - for each function, the corresponding netcf API is called. --- configure.ac | 5 +++ src/interface/netcf_driver.c | 76 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 0 deletions(-)
diff --git a/configure.ac b/configure.ac index 8db3226..dbec54c 100644 --- a/configure.ac +++ b/configure.ac @@ -1491,6 +1491,11 @@ if test "$with_netcf" = "yes" || test "$with_netcf" = "check"; then if test "$with_netcf" = "yes" ; then AC_DEFINE_UNQUOTED([WITH_NETCF], 1, [whether libnetcf is available to configure physical host network interfaces]) + AC_CHECK_LIB([netcf], [ncf_change_begin], [netcf_transactions=1], [netcf_transactions=0]) + if test "$netcf_transactions" = "1" ; then + AC_DEFINE_UNQUOTED([HAVE_NETCF_TRANSACTIONS], ["1"], + [we have sufficiently new version of netcf for transaction network API]) + fi fi fi AM_CONDITIONAL([WITH_NETCF], [test "$with_netcf" = "yes"]) diff --git a/src/interface/netcf_driver.c b/src/interface/netcf_driver.c index d5b401a..c94cbd5 100644 --- a/src/interface/netcf_driver.c +++ b/src/interface/netcf_driver.c @@ -540,6 +540,77 @@ cleanup: return ret; }
+#ifdef HAVE_NETCF_TRANSACTIONS +static int interfaceChangeBegin(virConnectPtr conn ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED)
Both conn and flags *are* used in this method, and the 2 following methods.
+ struct interface_driver *driver = conn->interfacePrivateData; ^^^^^
+ int ret; + + virCheckFlags(0, -1); /* currently flags must be 0 */
^^^^^^^^^^^^
+ + interfaceDriverLock(driver); + + ret = ncf_change_begin(driver->netcf, 0); + if (ret < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + interfaceReportError(netcf_to_vir_err(errcode), + _("failed to begin transaction (netcf: %s - %s)"), + errmsg, details ? details : "");
I don't think including the word 'netcf' in the error message here is relevant. If 'details' is "", then this also adds a trailing ' - ' to the error string. eg it needs to be interfaceReportError(netcf_to_vir_err(errcode), _("failed to begin transaction: %s%s%s)"), errmsg, details ? " - " : "", details ? details : ""); Likewise in the other methods Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 05/26/2011 04:35 AM, Daniel P. Berrange wrote:
On Thu, May 19, 2011 at 04:51:28PM -0400, Laine Stump wrote:
From: Michal Privoznik<mprivozn@redhat.com>
This is the functionality at the end of the libvirt part of the call chain - for each function, the corresponding netcf API is called. --- configure.ac | 5 +++ src/interface/netcf_driver.c | 76 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 0 deletions(-)
diff --git a/configure.ac b/configure.ac index 8db3226..dbec54c 100644 --- a/configure.ac +++ b/configure.ac @@ -1491,6 +1491,11 @@ if test "$with_netcf" = "yes" || test "$with_netcf" = "check"; then if test "$with_netcf" = "yes" ; then AC_DEFINE_UNQUOTED([WITH_NETCF], 1, [whether libnetcf is available to configure physical host network interfaces]) + AC_CHECK_LIB([netcf], [ncf_change_begin], [netcf_transactions=1], [netcf_transactions=0]) + if test "$netcf_transactions" = "1" ; then + AC_DEFINE_UNQUOTED([HAVE_NETCF_TRANSACTIONS], ["1"], + [we have sufficiently new version of netcf for transaction network API]) + fi fi fi AM_CONDITIONAL([WITH_NETCF], [test "$with_netcf" = "yes"]) diff --git a/src/interface/netcf_driver.c b/src/interface/netcf_driver.c index d5b401a..c94cbd5 100644 --- a/src/interface/netcf_driver.c +++ b/src/interface/netcf_driver.c @@ -540,6 +540,77 @@ cleanup: return ret; }
+#ifdef HAVE_NETCF_TRANSACTIONS +static int interfaceChangeBegin(virConnectPtr conn ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED) Both conn and flags *are* used in this method, and the 2 following methods.
Oops. yeah, fixed.
+ interfaceDriverLock(driver); + + ret = ncf_change_begin(driver->netcf, 0); + if (ret< 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf,&errmsg,&details); + interfaceReportError(netcf_to_vir_err(errcode), + _("failed to begin transaction (netcf: %s - %s)"), + errmsg, details ? details : ""); I don't think including the word 'netcf' in the error message here is relevant. If 'details' is "", then this also adds a trailing ' - ' to
+ the error string. eg it needs to be
interfaceReportError(netcf_to_vir_err(errcode), _("failed to begin transaction: %s%s%s)"), errmsg, details ? " - " : "", details ? details : "");
Likewise in the other methods
Okay, I've changed that in these three new methods, and will submit a followup patch to change it in the existing methods as well..

From: Michal Privoznik <mprivozn@redhat.com> --- src/conf/interface_conf.c | 45 ++++++++++++++++++++++++ src/conf/interface_conf.h | 4 ++ src/test/test_driver.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 0 deletions(-) diff --git a/src/conf/interface_conf.c b/src/conf/interface_conf.c index f3848bd..4ff68aa 100644 --- a/src/conf/interface_conf.c +++ b/src/conf/interface_conf.c @@ -1227,6 +1227,51 @@ void virInterfaceObjListFree(virInterfaceObjListPtr interfaces) interfaces->count = 0; } +int virInterfaceObjListClone(virConnectPtr conn, + virInterfaceObjListPtr src, + virInterfaceObjListPtr dest) +{ + int ret = -1; + unsigned int i, cnt; + + if (!src || !dest) + goto end; + + cnt = src->count; + for (i = 0; i < cnt; i++) { + virInterfaceDefPtr def = src->objs[i]->def; + virInterfaceDefPtr backup; + virInterfaceObjPtr iface; + char *xml = virInterfaceDefFormat(def); + + if (!xml) { + virReportOOMError(); + goto no_memory; + } + + if ((backup = virInterfaceDefParseString(xml)) == NULL) { + VIR_FREE(xml); + goto no_memory; + } + + VIR_FREE(xml); + if ((iface = virInterfaceAssignDef(dest, backup)) == NULL) + goto no_memory; + + virInterfaceObjUnlock(iface); + + conn->refs++; + } + + ret = cnt; +end: + return ret; + +no_memory: + virInterfaceObjListFree(dest); + goto end; +} + virInterfaceObjPtr virInterfaceAssignDef(virInterfaceObjListPtr interfaces, const virInterfaceDefPtr def) { diff --git a/src/conf/interface_conf.h b/src/conf/interface_conf.h index 6073b49..870a8ee 100644 --- a/src/conf/interface_conf.h +++ b/src/conf/interface_conf.h @@ -192,6 +192,10 @@ virInterfaceObjPtr virInterfaceFindByName(const virInterfaceObjListPtr void virInterfaceDefFree(virInterfaceDefPtr def); void virInterfaceObjFree(virInterfaceObjPtr iface); void virInterfaceObjListFree(virInterfaceObjListPtr vms); +int virInterfaceObjListClone(virConnectPtr conn, + virInterfaceObjListPtr src, + virInterfaceObjListPtr dest); + virInterfaceObjPtr virInterfaceAssignDef(virInterfaceObjListPtr interfaces, const virInterfaceDefPtr def); diff --git a/src/test/test_driver.c b/src/test/test_driver.c index e86bc4e..b58c5d2 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -85,6 +85,8 @@ struct _testConn { virDomainObjList domains; virNetworkObjList networks; virInterfaceObjList ifaces; + bool transaction_running; + virInterfaceObjList backupIfaces; virStoragePoolObjList pools; virNodeDeviceObjList devs; int numCells; @@ -3455,6 +3457,84 @@ cleanup: return ret; } +static int testInterfaceChangeBegin(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + if (privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("there is another transaction " + "running.")); + goto cleanup; + } + + privconn->transaction_running = true; + + if (virInterfaceObjListClone(conn, &privconn->ifaces, + &privconn->backupIfaces) < 0) + goto cleanup; + + ret = 0; +cleanup: + testDriverUnlock(privconn); + return ret; +} + +static int testInterfaceChangeCommit(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + + if (!privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("no transaction running, " + "nothing to be commited.")); + goto cleanup; + } + + virInterfaceObjListFree(&privconn->backupIfaces); + privconn->transaction_running = false; + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + + return ret; +} + +static int testInterfaceChangeRollback(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + + if (!privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("no transaction running, " + "nothing to rollback.")); + goto cleanup; + } + + virInterfaceObjListFree(&privconn->ifaces); + privconn->ifaces.count = privconn->backupIfaces.count; + privconn->ifaces.objs = privconn->backupIfaces.objs; + privconn->backupIfaces.count = 0; + privconn->backupIfaces.objs = NULL; + + privconn->transaction_running = false; + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + return ret; +} static char *testInterfaceGetXMLDesc(virInterfacePtr iface, unsigned int flags ATTRIBUTE_UNUSED) @@ -5428,6 +5508,9 @@ static virInterfaceDriver testInterfaceDriver = { .interfaceCreate = testInterfaceCreate, /* 0.7.0 */ .interfaceDestroy = testInterfaceDestroy, /* 0.7.0 */ .interfaceIsActive = testInterfaceIsActive, /* 0.7.3 */ + .interfaceChangeBegin = testInterfaceChangeBegin, /* 0.9.2 */ + .interfaceChangeCommit = testInterfaceChangeCommit, /* 0.9.2 */ + .interfaceChangeRollback = testInterfaceChangeRollback, /* 0.9.2 */ }; -- 1.7.3.4

-----Original Message----- From: libvir-list-bounces@redhat.com [mailto:libvir-list- bounces@redhat.com] On Behalf Of Laine Stump Sent: Thursday, May 19, 2011 1:51 PM To: libvir-list@redhat.com Subject: [libvirt] [PATCHv2 7/7] interface: implement a test driver fornetwork config transaction API.
From: Michal Privoznik <mprivozn@redhat.com>
--- src/conf/interface_conf.c | 45 ++++++++++++++++++++++++ src/conf/interface_conf.h | 4 ++ src/test/test_driver.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 0 deletions(-)
diff --git a/src/conf/interface_conf.c b/src/conf/interface_conf.c index f3848bd..4ff68aa 100644 --- a/src/conf/interface_conf.c +++ b/src/conf/interface_conf.c @@ -1227,6 +1227,51 @@ void virInterfaceObjListFree(virInterfaceObjListPtr interfaces) interfaces->count = 0; }
+int virInterfaceObjListClone(virConnectPtr conn, + virInterfaceObjListPtr src, + virInterfaceObjListPtr dest) +{ + int ret = -1; + unsigned int i, cnt; + + if (!src || !dest) + goto end; + + cnt = src->count; + for (i = 0; i < cnt; i++) { + virInterfaceDefPtr def = src->objs[i]->def; + virInterfaceDefPtr backup; + virInterfaceObjPtr iface; + char *xml = virInterfaceDefFormat(def); + + if (!xml) { + virReportOOMError(); + goto no_memory; + } + + if ((backup = virInterfaceDefParseString(xml)) == NULL) { + VIR_FREE(xml); + goto no_memory; + } + + VIR_FREE(xml); + if ((iface = virInterfaceAssignDef(dest, backup)) == NULL) + goto no_memory; + + virInterfaceObjUnlock(iface); + + conn->refs++; + } + + ret = cnt; +end: + return ret; + +no_memory: + virInterfaceObjListFree(dest); + goto end; +} + virInterfaceObjPtr virInterfaceAssignDef(virInterfaceObjListPtr interfaces, const virInterfaceDefPtr def) { diff --git a/src/conf/interface_conf.h b/src/conf/interface_conf.h index 6073b49..870a8ee 100644 --- a/src/conf/interface_conf.h +++ b/src/conf/interface_conf.h @@ -192,6 +192,10 @@ virInterfaceObjPtr virInterfaceFindByName(const virInterfaceObjListPtr void virInterfaceDefFree(virInterfaceDefPtr def); void virInterfaceObjFree(virInterfaceObjPtr iface); void virInterfaceObjListFree(virInterfaceObjListPtr vms); +int virInterfaceObjListClone(virConnectPtr conn, + virInterfaceObjListPtr src, + virInterfaceObjListPtr dest); +
virInterfaceObjPtr virInterfaceAssignDef(virInterfaceObjListPtr interfaces, const virInterfaceDefPtr def); diff --git a/src/test/test_driver.c b/src/test/test_driver.c index e86bc4e..b58c5d2 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -85,6 +85,8 @@ struct _testConn { virDomainObjList domains; virNetworkObjList networks; virInterfaceObjList ifaces; + bool transaction_running; + virInterfaceObjList backupIfaces; virStoragePoolObjList pools; virNodeDeviceObjList devs; int numCells; @@ -3455,6 +3457,84 @@ cleanup: return ret; }
+static int testInterfaceChangeBegin(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn);
+ if (privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("there is another transaction " + "running.")); + goto cleanup; + } + + privconn->transaction_running = true;
Based on the check above I assume the driver can decide whether to accept/support nested transactions (ie, this test driver does not support them for example, because it uses a boolean variable to remember its state). Is it correct? (If not, maybe you can move this check one layer above, before the driver) /Chris
+ if (virInterfaceObjListClone(conn, &privconn->ifaces, + &privconn->backupIfaces) < 0) + goto cleanup; + + ret = 0; +cleanup: + testDriverUnlock(privconn); + return ret; +} + +static int testInterfaceChangeCommit(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + + if (!privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("no transaction running, " + "nothing to be commited.")); + goto cleanup; + } + + virInterfaceObjListFree(&privconn->backupIfaces); + privconn->transaction_running = false; + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + + return ret; +} + +static int testInterfaceChangeRollback(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + + if (!privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("no transaction running, " + "nothing to rollback.")); + goto cleanup; + } + + virInterfaceObjListFree(&privconn->ifaces); + privconn->ifaces.count = privconn->backupIfaces.count; + privconn->ifaces.objs = privconn->backupIfaces.objs; + privconn->backupIfaces.count = 0; + privconn->backupIfaces.objs = NULL; + + privconn->transaction_running = false; + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + return ret; +}
static char *testInterfaceGetXMLDesc(virInterfacePtr iface, unsigned int flags ATTRIBUTE_UNUSED) @@ -5428,6 +5508,9 @@ static virInterfaceDriver testInterfaceDriver = { .interfaceCreate = testInterfaceCreate, /* 0.7.0 */ .interfaceDestroy = testInterfaceDestroy, /* 0.7.0 */ .interfaceIsActive = testInterfaceIsActive, /* 0.7.3 */ + .interfaceChangeBegin = testInterfaceChangeBegin, /* 0.9.2 */ + .interfaceChangeCommit = testInterfaceChangeCommit, /* 0.9.2 */ + .interfaceChangeRollback = testInterfaceChangeRollback, /* 0.9.2 */ };
-- 1.7.3.4
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On Thu, May 19, 2011 at 04:51:29PM -0400, Laine Stump wrote:
From: Michal Privoznik <mprivozn@redhat.com>
--- src/conf/interface_conf.c | 45 ++++++++++++++++++++++++ src/conf/interface_conf.h | 4 ++ src/test/test_driver.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 0 deletions(-)
diff --git a/src/conf/interface_conf.c b/src/conf/interface_conf.c index f3848bd..4ff68aa 100644 --- a/src/conf/interface_conf.c +++ b/src/conf/interface_conf.c @@ -1227,6 +1227,51 @@ void virInterfaceObjListFree(virInterfaceObjListPtr interfaces) interfaces->count = 0; }
+int virInterfaceObjListClone(virConnectPtr conn, + virInterfaceObjListPtr src, + virInterfaceObjListPtr dest) +{ + int ret = -1; + unsigned int i, cnt; + + if (!src || !dest) + goto end; + + cnt = src->count; + for (i = 0; i < cnt; i++) { + virInterfaceDefPtr def = src->objs[i]->def; + virInterfaceDefPtr backup; + virInterfaceObjPtr iface; + char *xml = virInterfaceDefFormat(def); + + if (!xml) { + virReportOOMError(); + goto no_memory; + } + + if ((backup = virInterfaceDefParseString(xml)) == NULL) { + VIR_FREE(xml); + goto no_memory; + } + + VIR_FREE(xml); + if ((iface = virInterfaceAssignDef(dest, backup)) == NULL) + goto no_memory; + + virInterfaceObjUnlock(iface); + + conn->refs++; + } + + ret = cnt; +end: + return ret; + +no_memory: + virInterfaceObjListFree(dest);
I'ts always a problem when the callee frees up memory allocated by the caller in case of error, as it's so easy to mess things up. I would rather not do that here, and have the caller do the free if we fail. example: src == NULL, dest != NULL, and we leak dest.
+ goto end; +} + virInterfaceObjPtr virInterfaceAssignDef(virInterfaceObjListPtr interfaces, const virInterfaceDefPtr def) { diff --git a/src/conf/interface_conf.h b/src/conf/interface_conf.h index 6073b49..870a8ee 100644 --- a/src/conf/interface_conf.h +++ b/src/conf/interface_conf.h @@ -192,6 +192,10 @@ virInterfaceObjPtr virInterfaceFindByName(const virInterfaceObjListPtr void virInterfaceDefFree(virInterfaceDefPtr def); void virInterfaceObjFree(virInterfaceObjPtr iface); void virInterfaceObjListFree(virInterfaceObjListPtr vms); +int virInterfaceObjListClone(virConnectPtr conn, + virInterfaceObjListPtr src, + virInterfaceObjListPtr dest); +
virInterfaceObjPtr virInterfaceAssignDef(virInterfaceObjListPtr interfaces, const virInterfaceDefPtr def); diff --git a/src/test/test_driver.c b/src/test/test_driver.c index e86bc4e..b58c5d2 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -85,6 +85,8 @@ struct _testConn { virDomainObjList domains; virNetworkObjList networks; virInterfaceObjList ifaces; + bool transaction_running; + virInterfaceObjList backupIfaces; virStoragePoolObjList pools; virNodeDeviceObjList devs; int numCells; @@ -3455,6 +3457,84 @@ cleanup: return ret; }
+static int testInterfaceChangeBegin(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + if (privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("there is another transaction " + "running.")); + goto cleanup; + } + + privconn->transaction_running = true; + + if (virInterfaceObjListClone(conn, &privconn->ifaces, + &privconn->backupIfaces) < 0) + goto cleanup; + + ret = 0; +cleanup: + testDriverUnlock(privconn); + return ret; +} + +static int testInterfaceChangeCommit(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + + if (!privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("no transaction running, " + "nothing to be commited.")); + goto cleanup; + } + + virInterfaceObjListFree(&privconn->backupIfaces); + privconn->transaction_running = false; + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + + return ret; +} + +static int testInterfaceChangeRollback(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + + if (!privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("no transaction running, " + "nothing to rollback.")); + goto cleanup; + } + + virInterfaceObjListFree(&privconn->ifaces); + privconn->ifaces.count = privconn->backupIfaces.count; + privconn->ifaces.objs = privconn->backupIfaces.objs; + privconn->backupIfaces.count = 0; + privconn->backupIfaces.objs = NULL; + + privconn->transaction_running = false; + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + return ret; +}
static char *testInterfaceGetXMLDesc(virInterfacePtr iface, unsigned int flags ATTRIBUTE_UNUSED) @@ -5428,6 +5508,9 @@ static virInterfaceDriver testInterfaceDriver = { .interfaceCreate = testInterfaceCreate, /* 0.7.0 */ .interfaceDestroy = testInterfaceDestroy, /* 0.7.0 */ .interfaceIsActive = testInterfaceIsActive, /* 0.7.3 */ + .interfaceChangeBegin = testInterfaceChangeBegin, /* 0.9.2 */ + .interfaceChangeCommit = testInterfaceChangeCommit, /* 0.9.2 */ + .interfaceChangeRollback = testInterfaceChangeRollback, /* 0.9.2 */ };
Except for the small nit on how virInterfaceObjListClone() handle errors, fine, and ACK once fixed (just do the free in the caller on error). 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 05/24/2011 02:24 AM, Daniel Veillard wrote:
From: Michal Privoznik<mprivozn@redhat.com>
--- src/conf/interface_conf.c | 45 ++++++++++++++++++++++++ src/conf/interface_conf.h | 4 ++ src/test/test_driver.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 0 deletions(-)
diff --git a/src/conf/interface_conf.c b/src/conf/interface_conf.c index f3848bd..4ff68aa 100644 --- a/src/conf/interface_conf.c +++ b/src/conf/interface_conf.c @@ -1227,6 +1227,51 @@ void virInterfaceObjListFree(virInterfaceObjListPtr interfaces) interfaces->count = 0; }
+int virInterfaceObjListClone(virConnectPtr conn, + virInterfaceObjListPtr src, + virInterfaceObjListPtr dest) +{ + int ret = -1; + unsigned int i, cnt; + + if (!src || !dest) + goto end; + + cnt = src->count; + for (i = 0; i< cnt; i++) { + virInterfaceDefPtr def = src->objs[i]->def; + virInterfaceDefPtr backup; + virInterfaceObjPtr iface; + char *xml = virInterfaceDefFormat(def); + + if (!xml) { + virReportOOMError(); + goto no_memory; + } + + if ((backup = virInterfaceDefParseString(xml)) == NULL) { + VIR_FREE(xml); + goto no_memory; + } + + VIR_FREE(xml); + if ((iface = virInterfaceAssignDef(dest, backup)) == NULL) + goto no_memory; + + virInterfaceObjUnlock(iface); + + conn->refs++; + } + + ret = cnt; +end: + return ret; + +no_memory: + virInterfaceObjListFree(dest); I'ts always a problem when the callee frees up memory allocated by
On Thu, May 19, 2011 at 04:51:29PM -0400, Laine Stump wrote: the caller in case of error, as it's so easy to mess things up. I would rather not do that here, and have the caller do the free if we fail. example: src == NULL, dest != NULL, and we leak dest.
But virInterfaceObjListFree(dest) doesn't free dest itself, it only frees the items that are contained by dest (not dest itself), and those items were not allocated by the caller, but were allocated within virInterfaceObjListClone(), so I don't see the problem you see. There *is* a problem in general with this function, though - dest is not cleaned out before copying over the contents of src, so if it starts out non-empty, there may be extra stuff in there at the end. Michal - since you wrote it, maybe you should answer this question - shouldn't virInterfaceObjListClone() call virInterfaceObjListFree(dest) as soon as it determines dest != NULL?
+ goto end; +} + virInterfaceObjPtr virInterfaceAssignDef(virInterfaceObjListPtr interfaces, const virInterfaceDefPtr def) { diff --git a/src/conf/interface_conf.h b/src/conf/interface_conf.h index 6073b49..870a8ee 100644 --- a/src/conf/interface_conf.h +++ b/src/conf/interface_conf.h @@ -192,6 +192,10 @@ virInterfaceObjPtr virInterfaceFindByName(const virInterfaceObjListPtr void virInterfaceDefFree(virInterfaceDefPtr def); void virInterfaceObjFree(virInterfaceObjPtr iface); void virInterfaceObjListFree(virInterfaceObjListPtr vms); +int virInterfaceObjListClone(virConnectPtr conn, + virInterfaceObjListPtr src, + virInterfaceObjListPtr dest); +
virInterfaceObjPtr virInterfaceAssignDef(virInterfaceObjListPtr interfaces, const virInterfaceDefPtr def); diff --git a/src/test/test_driver.c b/src/test/test_driver.c index e86bc4e..b58c5d2 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -85,6 +85,8 @@ struct _testConn { virDomainObjList domains; virNetworkObjList networks; virInterfaceObjList ifaces; + bool transaction_running; + virInterfaceObjList backupIfaces; virStoragePoolObjList pools; virNodeDeviceObjList devs; int numCells; @@ -3455,6 +3457,84 @@ cleanup: return ret; }
+static int testInterfaceChangeBegin(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + if (privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("there is another transaction " + "running.")); + goto cleanup; + } + + privconn->transaction_running = true; + + if (virInterfaceObjListClone(conn,&privconn->ifaces, +&privconn->backupIfaces)< 0) + goto cleanup; + + ret = 0; +cleanup: + testDriverUnlock(privconn); + return ret; +} + +static int testInterfaceChangeCommit(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + + if (!privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("no transaction running, " + "nothing to be commited.")); + goto cleanup; + } + + virInterfaceObjListFree(&privconn->backupIfaces); + privconn->transaction_running = false; + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + + return ret; +} + +static int testInterfaceChangeRollback(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + + if (!privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("no transaction running, " + "nothing to rollback.")); + goto cleanup; + } + + virInterfaceObjListFree(&privconn->ifaces); + privconn->ifaces.count = privconn->backupIfaces.count; + privconn->ifaces.objs = privconn->backupIfaces.objs; + privconn->backupIfaces.count = 0; + privconn->backupIfaces.objs = NULL; + + privconn->transaction_running = false; + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + return ret; +}
static char *testInterfaceGetXMLDesc(virInterfacePtr iface, unsigned int flags ATTRIBUTE_UNUSED) @@ -5428,6 +5508,9 @@ static virInterfaceDriver testInterfaceDriver = { .interfaceCreate = testInterfaceCreate, /* 0.7.0 */ .interfaceDestroy = testInterfaceDestroy, /* 0.7.0 */ .interfaceIsActive = testInterfaceIsActive, /* 0.7.3 */ + .interfaceChangeBegin = testInterfaceChangeBegin, /* 0.9.2 */ + .interfaceChangeCommit = testInterfaceChangeCommit, /* 0.9.2 */ + .interfaceChangeRollback = testInterfaceChangeRollback, /* 0.9.2 */ }; Except for the small nit on how virInterfaceObjListClone() handle errors, fine, and ACK once fixed (just do the free in the caller on error).
Daniel

On 05/24/2011 02:24 AM, Daniel Veillard wrote:
From: Michal Privoznik<mprivozn@redhat.com>
--- src/conf/interface_conf.c | 45 ++++++++++++++++++++++++ src/conf/interface_conf.h | 4 ++ src/test/test_driver.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 0 deletions(-)
diff --git a/src/conf/interface_conf.c b/src/conf/interface_conf.c index f3848bd..4ff68aa 100644 --- a/src/conf/interface_conf.c +++ b/src/conf/interface_conf.c @@ -1227,6 +1227,51 @@ void virInterfaceObjListFree(virInterfaceObjListPtr interfaces) interfaces->count = 0; }
+int virInterfaceObjListClone(virConnectPtr conn, + virInterfaceObjListPtr src, + virInterfaceObjListPtr dest) +{ + int ret = -1; + unsigned int i, cnt; + + if (!src || !dest) + goto end; + + cnt = src->count; + for (i = 0; i< cnt; i++) { + virInterfaceDefPtr def = src->objs[i]->def; + virInterfaceDefPtr backup; + virInterfaceObjPtr iface; + char *xml = virInterfaceDefFormat(def); + + if (!xml) { + virReportOOMError(); + goto no_memory; + } + + if ((backup = virInterfaceDefParseString(xml)) == NULL) { + VIR_FREE(xml); + goto no_memory; + } + + VIR_FREE(xml); + if ((iface = virInterfaceAssignDef(dest, backup)) == NULL) + goto no_memory; + + virInterfaceObjUnlock(iface); + + conn->refs++; + } + + ret = cnt; +end: + return ret; + +no_memory: + virInterfaceObjListFree(dest); I'ts always a problem when the callee frees up memory allocated by
On Thu, May 19, 2011 at 04:51:29PM -0400, Laine Stump wrote: the caller in case of error, as it's so easy to mess things up. I would rather not do that here, and have the caller do the free if we fail. example: src == NULL, dest != NULL, and we leak dest. I don't think so. There is a check for either src or dest being NULL at
On 26.05.2011 07:30, Laine Stump wrote: the beginning of this function. And if so - we don't touch anything and return -1 (meaning error).
But virInterfaceObjListFree(dest) doesn't free dest itself, it only frees the items that are contained by dest (not dest itself), and those items were not allocated by the caller, but were allocated within virInterfaceObjListClone(), so I don't see the problem you see.
Yes. We don't do VIR_FREE(dest) here, nor virInterfaceObjListFree().
There *is* a problem in general with this function, though - dest is not cleaned out before copying over the contents of src, so if it starts out non-empty, there may be extra stuff in there at the end.
Actually yes. So either rename this to virInterfaceObjListAppend() (virInterfaceAssignDef() appends to the end) or free list items of dest just before copying.
Michal - since you wrote it, maybe you should answer this question - shouldn't virInterfaceObjListClone() call virInterfaceObjListFree(dest) as soon as it determines dest != NULL?
+ goto end; +} + virInterfaceObjPtr virInterfaceAssignDef(virInterfaceObjListPtr interfaces, const virInterfaceDefPtr def) { diff --git a/src/conf/interface_conf.h b/src/conf/interface_conf.h index 6073b49..870a8ee 100644 --- a/src/conf/interface_conf.h +++ b/src/conf/interface_conf.h @@ -192,6 +192,10 @@ virInterfaceObjPtr virInterfaceFindByName(const virInterfaceObjListPtr void virInterfaceDefFree(virInterfaceDefPtr def); void virInterfaceObjFree(virInterfaceObjPtr iface); void virInterfaceObjListFree(virInterfaceObjListPtr vms); +int virInterfaceObjListClone(virConnectPtr conn, + virInterfaceObjListPtr src, + virInterfaceObjListPtr dest); +
virInterfaceObjPtr virInterfaceAssignDef(virInterfaceObjListPtr interfaces, const virInterfaceDefPtr def); diff --git a/src/test/test_driver.c b/src/test/test_driver.c index e86bc4e..b58c5d2 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -85,6 +85,8 @@ struct _testConn { virDomainObjList domains; virNetworkObjList networks; virInterfaceObjList ifaces; + bool transaction_running; + virInterfaceObjList backupIfaces; virStoragePoolObjList pools; virNodeDeviceObjList devs; int numCells; @@ -3455,6 +3457,84 @@ cleanup: return ret; }
+static int testInterfaceChangeBegin(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + if (privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("there is another transaction " + "running.")); + goto cleanup; + } + + privconn->transaction_running = true; + + if (virInterfaceObjListClone(conn,&privconn->ifaces, +&privconn->backupIfaces)< 0) + goto cleanup; + + ret = 0; +cleanup: + testDriverUnlock(privconn); + return ret; +} + +static int testInterfaceChangeCommit(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + + if (!privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("no transaction running, " + "nothing to be commited.")); + goto cleanup; + } + + virInterfaceObjListFree(&privconn->backupIfaces); + privconn->transaction_running = false; + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + + return ret; +} + +static int testInterfaceChangeRollback(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + + if (!privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("no transaction running, " + "nothing to rollback.")); + goto cleanup; + } + + virInterfaceObjListFree(&privconn->ifaces); + privconn->ifaces.count = privconn->backupIfaces.count; + privconn->ifaces.objs = privconn->backupIfaces.objs; + privconn->backupIfaces.count = 0; + privconn->backupIfaces.objs = NULL; + + privconn->transaction_running = false; + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + return ret; +}
static char *testInterfaceGetXMLDesc(virInterfacePtr iface, unsigned int flags ATTRIBUTE_UNUSED) @@ -5428,6 +5508,9 @@ static virInterfaceDriver testInterfaceDriver = { .interfaceCreate = testInterfaceCreate, /* 0.7.0 */ .interfaceDestroy = testInterfaceDestroy, /* 0.7.0 */ .interfaceIsActive = testInterfaceIsActive, /* 0.7.3 */ + .interfaceChangeBegin = testInterfaceChangeBegin, /* 0.9.2 */ + .interfaceChangeCommit = testInterfaceChangeCommit, /* 0.9.2 */ + .interfaceChangeRollback = testInterfaceChangeRollback, /* 0.9.2 */ }; Except for the small nit on how virInterfaceObjListClone() handle errors, fine, and ACK once fixed (just do the free in the caller on error).
Daniel
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
Michal

On 05/26/2011 04:18 AM, Michal Prívozník wrote:
On 26.05.2011 07:30, Laine Stump wrote:
There *is* a problem in general with this function, though - dest is not cleaned out before copying over the contents of src, so if it starts out non-empty, there may be extra stuff in there at the end. Actually yes. So either rename this to virInterfaceObjListAppend() (virInterfaceAssignDef() appends to the end) or free list items of dest just before copying.
It doesn't exactly "append" either - if there is already an item with the same name, the new item will replace the existing item, otherwise it will be added to the list. Based on that, I will change the function to clear out dest before it starts (even in the case that src is NULL). I'm also fixing Dan's nits, removing the bogus conn->defs++, eliminating conn from the parameter list (not used), and adding virInterfraceObjListClone to libvirt_private.syms

On Thu, May 19, 2011 at 04:51:29PM -0400, Laine Stump wrote:
From: Michal Privoznik <mprivozn@redhat.com>
--- src/conf/interface_conf.c | 45 ++++++++++++++++++++++++ src/conf/interface_conf.h | 4 ++ src/test/test_driver.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 0 deletions(-)
diff --git a/src/conf/interface_conf.c b/src/conf/interface_conf.c index f3848bd..4ff68aa 100644 --- a/src/conf/interface_conf.c +++ b/src/conf/interface_conf.c @@ -1227,6 +1227,51 @@ void virInterfaceObjListFree(virInterfaceObjListPtr interfaces) interfaces->count = 0; }
+int virInterfaceObjListClone(virConnectPtr conn, + virInterfaceObjListPtr src, + virInterfaceObjListPtr dest) +{ + int ret = -1; + unsigned int i, cnt; + + if (!src || !dest) + goto end; + + cnt = src->count; + for (i = 0; i < cnt; i++) { + virInterfaceDefPtr def = src->objs[i]->def; + virInterfaceDefPtr backup; + virInterfaceObjPtr iface; + char *xml = virInterfaceDefFormat(def); + + if (!xml) { + virReportOOMError(); + goto no_memory;
Bogus - virInterfaceDefFormat can fail for several reasons and it alrady reports the error
+ } + + if ((backup = virInterfaceDefParseString(xml)) == NULL) { + VIR_FREE(xml); + goto no_memory; + } + + VIR_FREE(xml); + if ((iface = virInterfaceAssignDef(dest, backup)) == NULL) + goto no_memory; + + virInterfaceObjUnlock(iface); + + conn->refs++; + } + + ret = cnt; +end:
'end:' is not following our naming conventions. It should be 'cleanup:'
+ return ret; + +no_memory: + virInterfaceObjListFree(dest); + goto end; +} + virInterfaceObjPtr virInterfaceAssignDef(virInterfaceObjListPtr interfaces, const virInterfaceDefPtr def) { diff --git a/src/conf/interface_conf.h b/src/conf/interface_conf.h index 6073b49..870a8ee 100644 --- a/src/conf/interface_conf.h +++ b/src/conf/interface_conf.h @@ -192,6 +192,10 @@ virInterfaceObjPtr virInterfaceFindByName(const virInterfaceObjListPtr void virInterfaceDefFree(virInterfaceDefPtr def); void virInterfaceObjFree(virInterfaceObjPtr iface); void virInterfaceObjListFree(virInterfaceObjListPtr vms); +int virInterfaceObjListClone(virConnectPtr conn, + virInterfaceObjListPtr src, + virInterfaceObjListPtr dest); +
virInterfaceObjPtr virInterfaceAssignDef(virInterfaceObjListPtr interfaces, const virInterfaceDefPtr def); diff --git a/src/test/test_driver.c b/src/test/test_driver.c index e86bc4e..b58c5d2 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -85,6 +85,8 @@ struct _testConn { virDomainObjList domains; virNetworkObjList networks; virInterfaceObjList ifaces; + bool transaction_running; + virInterfaceObjList backupIfaces; virStoragePoolObjList pools; virNodeDeviceObjList devs; int numCells; @@ -3455,6 +3457,84 @@ cleanup: return ret; }
+static int testInterfaceChangeBegin(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + if (privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("there is another transaction " + "running."));
Splitting the string over 2 lines here is not nice. Just move the entire _("....") to the next line, instead of breaking it half way through. Likewise for the other 2 APIs
+ goto cleanup; + } + + privconn->transaction_running = true; + + if (virInterfaceObjListClone(conn, &privconn->ifaces, + &privconn->backupIfaces) < 0) + goto cleanup; + + ret = 0; +cleanup: + testDriverUnlock(privconn); + return ret; +} + +static int testInterfaceChangeCommit(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + + if (!privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("no transaction running, " + "nothing to be commited.")); + goto cleanup; + } + + virInterfaceObjListFree(&privconn->backupIfaces); + privconn->transaction_running = false; + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + + return ret; +} + +static int testInterfaceChangeRollback(virConnectPtr conn, + unsigned int flags ATTRIBUTE_UNUSED) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + testDriverLock(privconn); + + if (!privconn->transaction_running) { + testError(VIR_ERR_OPERATION_INVALID, _("no transaction running, " + "nothing to rollback.")); + goto cleanup; + } + + virInterfaceObjListFree(&privconn->ifaces); + privconn->ifaces.count = privconn->backupIfaces.count; + privconn->ifaces.objs = privconn->backupIfaces.objs; + privconn->backupIfaces.count = 0; + privconn->backupIfaces.objs = NULL; + + privconn->transaction_running = false; + + ret = 0; + +cleanup: + testDriverUnlock(privconn); + return ret; +}
Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

I have one generic question. I know it is not a new problem, and it is not necessarily something worth investing time and code on, but what if the admin (directly or indirectly by activating some features) changes the interface configuration in between a BEGIN and a ROLLBACK with commands that do not go through virsh/libvirtd? Would ROLLBACK rollback those changes too? Does it make sense for libvirtd to subscribe to the relevant Netlink multicast groups to listen for such changes and flag an interface as "tainted" in such case? This way it may be able to generate a log or ask for a sort of extra "--force" parameter for the ROLLBACK case. /Christian
-----Original Message----- From: libvir-list-bounces@redhat.com [mailto:libvir-list- bounces@redhat.com] On Behalf Of Laine Stump Sent: Thursday, May 19, 2011 1:51 PM To: libvir-list@redhat.com Subject: [libvirt] [PATCHv2 0/7] interface: new public API for networkconfig change transactions
(I'm sending v2 of this patch series for Michal Privoznik, as I had to tweak some things while integrating with the netcf part that I'm writing, and he will be out of the office tomorrow and thus unable to resend. In addition to the few small things I needed to change to make everything compile and work properly once the build system had a version of netcf supporting the new API, Michal incorporated all the comments from reviewers of the first version before forwarding the patches to me for integration.)
This patch series implements three new APIs for the interface driver which support transactional changes to the host's network config - at any point you can begin a transaction (which saves a snapshot of the current config), then make any changes you like to the config, and later either commit those changes (the current implementation just removes the snapshotted files) or rollback to the original config.
The actual implementation of this functionality lives in the netcf library; these patches create pass-through functions that call out to netcf on the machine that's running libvirtd.
Most importantly, note that uses of "start" in the API names have been changed to "begin", and rather than a single virsh command with multiple subcommands, there are now three separate commands: iface-begin, iface-commit, and iface-rollback.
Thanks to using AC_CHECK_LIB in configure.ac, this code can safely be pushed and built on systems that don't yet have a new enough netcf to contain the API extensions - those functions are simply not implemented in that case (and return the appropriate error).
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 05/19/2011 06:14 PM, Christian Benvenuti (benve) wrote:
I have one generic question. I know it is not a new problem, and it is not necessarily something worth investing time and code on, but what if the admin (directly or indirectly by activating some features) changes the interface configuration in between a BEGIN and a ROLLBACK with commands that do not go through virsh/libvirtd? Would ROLLBACK rollback those changes too?
Yes, it would. This is only because of the particular implementation in the netcf library, though. ncf_change_begin() saves off a copy of all the network config files to a "snapshot" directory; ncf_change_commit() deletes that snapshot, and ncf_change_rollback() copies back the files that were put in the snapshot directory during ncf_change_begin(). You can see the backend of these functions (implemented as a shell script so that it can run at boottime) at: https://fedorahosted.org/pipermail/netcf-devel/2011-May/000562.html
Does it make sense for libvirtd to subscribe to the relevant Netlink multicast groups to listen for such changes and flag an interface as "tainted" in such case? This way it may be able to generate a log or ask for a sort of extra "--force" parameter for the ROLLBACK case.
That may be a nice thing to do, but isn't necessary for the initial implementation.

On Thu, May 19, 2011 at 05:14:54PM -0500, Christian Benvenuti (benve) wrote:
I have one generic question. I know it is not a new problem, and it is not necessarily something worth investing time and code on, but what if the admin (directly or indirectly by activating some features) changes the interface configuration in between a BEGIN and a ROLLBACK with commands that do not go through virsh/libvirtd? Would ROLLBACK rollback those changes too?
IMHO that would result in 'undefined behaviour'. It may or may not get rolled back. libvirt can only guarantee that changes made via its APIs are rolled back, for the very simple reason that there may be functional changes the admin makes which libvirt has absolutely no understanding or knowledge of, and thus would not be aware of the neeed to roll back.
Does it make sense for libvirtd to subscribe to the relevant Netlink multicast groups to listen for such changes and flag an interface as "tainted" in such case? This way it may be able to generate a log or ask for a sort of extra "--force" parameter for the ROLLBACK case.
There are two scenarios here a. Functionality that libvirt already understands. We could potentially roll it back b. Functionality that libvirt does not understand. libvirt can't roll it back, since it is likely not even aware that it exists. c. Functionality that libvirt partially understands. Due to option 'a' we would try to rollback the bits we understand but not do anything with the bits we don't understand. The result being a broken mess. Primarily due to 'c', I think trying to rollback changes made outside the scope of the APIs, would be giving a somewhat false sense of security/usability & quite possibly even create worse problems for the users. Thus I think the rollback behaviour should be left 'undefined' if the admin changes stuff outside libvirt. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (5)
-
Christian Benvenuti (benve)
-
Daniel P. Berrange
-
Daniel Veillard
-
Laine Stump
-
Michal Prívozník