[libvirt] [PATCHv2 0.5/4] network: fix virNetworkObjAssignDef and persistence
John Ferlan
jferlan at redhat.com
Fri Apr 25 18:24:37 UTC 2014
On 04/25/2014 01:55 PM, Laine Stump wrote:
> On 04/24/2014 07:09 PM, John Ferlan wrote:
>>
>> On 04/23/2014 09:49 AM, Laine Stump wrote:
>>> Experimentation showed that if virNetworkCreateXML() was called for a
>>> network that was already defined, and then the network was
>>> subsequently shutdown, the network would continue to be persistent
>>> after the shutdown (expected/desired), but the original config would
>>> be lost in favor of the transient config sent in with
>>> virNetworkCreateXML() (which would then be the new persistent config)
>>> (obviously unexpected/not desired).
>>>
>>> To fix this, virNetworkObjAssignDef() has been changed to
>>>
>>> 1) properly save/free network->def and network->newDef for all the
>>> various combinations of live/active/persistent, including some
>>> combinations that were previously considered to be an error but didn't
>>> need to be (e.g. setting a "live" config for a network that isn't yet
>>> active but soon will be - that was previously considered an error,
>>> even though in practice it can be very useful).
>>>
>>> 2) automatically set the persistent flag whenever a new non-live
>>> config is assigned to the network (and clear it when the non-live
>>> config is set to NULL). the libvirt network driver no longer directly
>>> manipulates network->persistent, but instead relies entirely on
>>> virNetworkObjAssignDef() to do the right thing automatically.
>>>
>>> After this patch, the following sequence will behave as expected:
>>>
>>> virNetworkDefineXML(X)
>>> virNetworkCreateXML(X') (same name but some config different)
>>> virNetworkDestroy(X)
>>>
>>> At the end of these calls, the network config will remain as it was
>>> after the initial virNetworkDefine(), whereas previously it would take
>>> on the changes given during virNetworkCreateXML().
>>>
>>> Another effect of this tighter coupling between a) setting a !live def
>>> and b) setting/clearing the "persistent" flag, is that future patches
>>> which change the details of network lifecycle management
>>> (e.g. upcoming patches to fix detection of "active" networks when
>>> libvirtd is restarted) will find it much more difficult to break
>>> persistence functionality.
>>> ---
>>> src/conf/network_conf.c | 78 +++++++++++++++++++++++----------------
>>> src/conf/network_conf.h | 6 +--
>>> src/network/bridge_driver.c | 34 +++++++----------
>>> src/parallels/parallels_network.c | 4 +-
>>> src/test/test_driver.c | 5 +--
>>> 5 files changed, 64 insertions(+), 63 deletions(-)
>>>
>>> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
>>> index 56c4a09..66945ce 100644
>>> --- a/src/conf/network_conf.c
>>> +++ b/src/conf/network_conf.c
>>> @@ -302,46 +302,63 @@ void virNetworkObjListFree(virNetworkObjListPtr nets)
>>> /*
>>> * virNetworkObjAssignDef:
>>> * @network: the network object to update
>>> - * @def: the new NetworkDef (will be consumed by this function iff successful)
>>> + * @def: the new NetworkDef (will be consumed by this function)
>>> * @live: is this new def the "live" version, or the "persistent" version
>>> *
>>> - * Replace the appropriate copy of the given network's NetworkDef
>>> + * Replace the appropriate copy of the given network's def or newDef
>>> * with def. Use "live" and current state of the network to determine
>>> - * which to replace.
>>> + * which to replace and what to do with the old defs. When a non-live
>>> + * def is set, indicate that the network is now persistent.
>>> + *
>>> + * NB: a persistent network can be made transient by calling with:
>>> + * virNetworkObjAssignDef(network, NULL, false) (i.e. set the
>>> + * persistent def to NULL)
>>> *
>>> * Returns 0 on success, -1 on failure.
>>> */
>>> -int
>>> +void
>> Well this doesn't jive with the comment above about what it returns...
>
> Yeah, when all the failure paths disappeared I changed the function to
> void, but forgot to remove that comment. I'll fix that now.
>
>>> virNetworkObjAssignDef(virNetworkObjPtr network,
>>> virNetworkDefPtr def,
>>> bool live)
>>> {
>>> - if (virNetworkObjIsActive(network)) {
>>> - if (live) {
>>> + if (live) {
>>> + /* before setting new live def, save (into newDef) any
>>> + * existing persistent (!live) def to be restored when the
>>> + * network is destroyed, unless there is one already saved.
>>> + */
>>> + if (network->persistent && !network->newDef)
>>> + network->newDef = network->def;
>>> + else
>>> virNetworkDefFree(network->def);
>>> - network->def = def;
>>> - } else if (network->persistent) {
>>> - /* save current configuration to be restored on network shutdown */
>>> - virNetworkDefFree(network->newDef);
>>> + network->def = def;
>>> + } else { /* !live */
>>> + virNetworkDefFree(network->newDef);
>>> + if (virNetworkObjIsActive(network)) {
>>> + /* save new configuration to be restored on network
>>> + * shutdown, leaving current live def alone
>>> + */
>>> network->newDef = def;
>>> - } else {
>>> - virReportError(VIR_ERR_OPERATION_INVALID,
>>> - _("cannot save persistent config of transient "
>>> - "network '%s'"), network->def->name);
>>> - return -1;
>>> + } else { /* !live and !active */
>>> + if (network->def && !network->persistent) {
>>> + /* network isn't (yet) marked active or persistent,
>>> + * but already has a "live" def set. This means we are
>>> + * currently setting the persistent def as a part of
>>> + * the process of starting the network, so we need to
>>> + * preserve the "not yet live" def in network->def.
>>> + */
>>> + network->newDef = def;
>>> + } else {
>>> + /* either there is no live def set, or this network
>>> + * was already set as persistent, so the proper thing
>>> + * is to overwrite network->def.
>>> + */
>>> + network->newDef = NULL;
>> If I'm reading correctly - the newDef is virNetworkDefFree()'d above as
>> we enter the "} else { /* !live */" clause, which is probably where this
>> statement should be moved.
>
> No, I just did some reduction of common code by moving it above the if
> statement. You'll notice that network->newDef is set to *something* in
> every path through those ifs and elses, so at the end of the function,
> it is either NULL or pointing to a valid object.
>
>>
>>> + virNetworkDefFree(network->def);
>>> + network->def = def;
>>
>> NOTE: I think this is what is causing issues in networkUndefine() -
>> that'll call this function with '!live' - thus you'll do the free the
>> 'def' and set it to NULL... Back in the caller there's all sorts of
>> touching of the network->def-> structure
>
> Yes, indirectly. But the *real* problem is that we shouldn't be calling
> this function until after we're finished using anything in network->def.
> See below for the small diff I'm squashing in to fix this.
>
>>
>>
>>> + }
>>> }
>>> - } else if (!live) {
>>> - virNetworkDefFree(network->newDef);
>>> - virNetworkDefFree(network->def);
>>> - network->newDef = NULL;
>>> - network->def = def;
>>> - } else {
>>> - virReportError(VIR_ERR_OPERATION_INVALID,
>>> - _("cannot save live config of inactive "
>>> - "network '%s'"), network->def->name);
>>> - return -1;
>>> + network->persistent = !!def;
>>> }
>>> - return 0;
>>> }
>>>
>>> /*
>>> @@ -365,10 +382,7 @@ virNetworkAssignDef(virNetworkObjListPtr nets,
>>> virNetworkObjPtr network;
>>>
>>> if ((network = virNetworkFindByName(nets, def->name))) {
>>> - if (virNetworkObjAssignDef(network, def, live) < 0) {
>>> - virNetworkObjUnlock(network);
>>> - return NULL;
>>> - }
>>> + virNetworkObjAssignDef(network, def, live);
>>> return network;
>>> }
>>>
>>> @@ -392,8 +406,9 @@ virNetworkAssignDef(virNetworkObjListPtr nets,
>>> ignore_value(virBitmapSetBit(network->class_id, 2));
>>>
>>> network->def = def;
>>> -
>>> + network->persistent = !live;
>>> return network;
>>> +
>>> error:
>>> virNetworkObjUnlock(network);
>>> virNetworkObjFree(network);
>>> @@ -3118,7 +3133,6 @@ virNetworkObjPtr virNetworkLoadConfig(virNetworkObjListPtr nets,
>>> goto error;
>>>
>>> net->autostart = autostart;
>>> - net->persistent = 1;
>>>
>>> VIR_FREE(configFile);
>>> VIR_FREE(autostartLink);
>>> diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
>>> index 1a864de..90da16f 100644
>>> --- a/src/conf/network_conf.h
>>> +++ b/src/conf/network_conf.h
>>> @@ -335,9 +335,9 @@ typedef bool (*virNetworkObjListFilter)(virConnectPtr conn,
>>> virNetworkObjPtr virNetworkAssignDef(virNetworkObjListPtr nets,
>>> virNetworkDefPtr def,
>>> bool live);
>>> -int virNetworkObjAssignDef(virNetworkObjPtr network,
>>> - virNetworkDefPtr def,
>>> - bool live);
>>> +void virNetworkObjAssignDef(virNetworkObjPtr network,
>>> + virNetworkDefPtr def,
>>> + bool live);
>>> int virNetworkObjSetDefTransient(virNetworkObjPtr network, bool live);
>>> void virNetworkObjUnsetDefTransient(virNetworkObjPtr network);
>>> virNetworkDefPtr virNetworkObjGetPersistentDef(virNetworkObjPtr network);
>>> diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
>>> index eb276cd..62d1c25 100644
>>> --- a/src/network/bridge_driver.c
>>> +++ b/src/network/bridge_driver.c
>>> @@ -2667,10 +2667,11 @@ static virNetworkPtr networkCreateXML(virConnectPtr conn, const char *xml)
>>> if (networkValidate(driver, def, true) < 0)
>>> goto cleanup;
>>>
>>> - /* NB: "live" is false because this transient network hasn't yet
>>> - * been started
>>> + /* NB: even though this transient network hasn't yet been started,
>>> + * we assign the def with live = true in anticipation that it will
>>> + * be started momentarily.
>>> */
>>> - if (!(network = virNetworkAssignDef(&driver->networks, def, false)))
>>> + if (!(network = virNetworkAssignDef(&driver->networks, def, true)))
>>> goto cleanup;
>>> def = NULL;
>>>
>>> @@ -2719,19 +2720,10 @@ static virNetworkPtr networkDefineXML(virConnectPtr conn, const char *xml)
>>> if (networkValidate(driver, def, false) < 0)
>>> goto cleanup;
>>>
>>> - if ((network = virNetworkFindByName(&driver->networks, def->name))) {
>>> - network->persistent = 1;
>>> - if (virNetworkObjAssignDef(network, def, false) < 0)
>>> - goto cleanup;
>>> - } else {
>>> - if (!(network = virNetworkAssignDef(&driver->networks, def, false)))
>>> - goto cleanup;
>>> - }
>>> -
>>> - /* define makes the network persistent - always */
>>> - network->persistent = 1;
>>> + if (!(network = virNetworkAssignDef(&driver->networks, def, false)))
>>> + goto cleanup;
>>>
>>> - /* def was asigned */
>>> + /* def was assigned to network object */
>>> freeDef = false;
>>>
>>> if (virNetworkSaveConfig(driver->networkConfigDir, def) < 0) {
>>> @@ -2740,9 +2732,11 @@ static virNetworkPtr networkDefineXML(virConnectPtr conn, const char *xml)
>>> network = NULL;
>>> goto cleanup;
>>> }
>>> - network->persistent = 0;
>>> - virNetworkDefFree(network->newDef);
>>> - network->newDef = NULL;
>>> + /* if network was active already, just undo new persistent
>>> + * definition by making it transient.
>>> + * XXX - this isn't necessarily the correct thing to do.
>>> + */
>> Leaving the XXX comment in here leaves me wondering what is the correct
>> thing to do...
>
> The correct thing to do is... well, to exactly rollback to what you had
> before the failure.
>
> However, the current code (both before *and* after this patch) does the
> (same) wrong thing in this case - if you fail during an attempt to
> redefine an already persistent+active network, you'll end up with a
> transient+active network (so it will disappear as soon as the network is
> stopped).
>
> It would be better if such a failure caused us to revert back to the
> previous persistent def. That would require saving off the existing def
> before trying to assign the new one or writing to disk. But that is a
> separate problem and should be in a separate patch anyway. The only
> reason I touched that area of the code in this patch was that I was
> replacing direct assignment of network->persistent with calling
> virNetworkObjAssignDef() (while preserving the existing functionality).
> I did think it was worth noting the problem with the existing
> functionality while touching the code, however.
>
> So, while I think this needs fixing, it's pre-existing and I'd rather
> not have these other patches delayed because of it.
>
>>
>>> + virNetworkObjAssignDef(network, NULL, false);
>>> goto cleanup;
>>> }
>>>
>>> @@ -2794,10 +2788,8 @@ networkUndefine(virNetworkPtr net)
>>> goto cleanup;
>>>
>>> /* make the network transient */
>>> - network->persistent = 0;
>>> network->autostart = 0;
>>> - virNetworkDefFree(network->newDef);
>>> - network->newDef = NULL;
>>> + virNetworkObjAssignDef(network, NULL, false);
>> This seems to be the cause of the crash I noted in private chat. If I
>> restore just these lines and not make the ObjAssignDef() call, then
>> things work again. That includes issues I was seeing in virt-test for
>> patches 3 & 4 as well.
>
> Yep. That call needs to be moved down to below the call to
> networkRemoveInactive. I've done that and all the tests complete without
> failure (as you've also separately reported on IRC). Here is what I
> squashed into this patch:
>
> diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
> index 309d967..2a54aa3 100644
> --- a/src/network/bridge_driver.c
> +++ b/src/network/bridge_driver.c
> @@ -2899,14 +2899,12 @@ networkUndefine(virNetworkPtr net)
> if (virNetworkObjIsActive(network))
> active = true;
>
> + /* remove autostart link */
> if (virNetworkDeleteConfig(driver->networkConfigDir,
> driver->networkAutostartDir,
> network) < 0)
> goto cleanup;
> -
> - /* make the network transient */
> network->autostart = 0;
> - virNetworkObjAssignDef(network, NULL, false);
>
> event = virNetworkEventLifecycleNew(network->def->name,
> network->def->uuid,
> @@ -2920,6 +2918,12 @@ networkUndefine(virNetworkPtr net)
> goto cleanup;
> }
> network = NULL;
> + } else {
> +
> + /* if the network still exists, it was active, and we need to make
> + * it transient (by deleting the persistent def
s/def/def)/
> + */
> + virNetworkObjAssignDef(network, NULL, false);
> }
>
> ret = 0;
> --
>
>>
>> Just figured I'd close the loop on this review at least...
>
> Do my responses clear up your concerns?
>
>
>
Yep - all set, thanks for the extra details...
You have an ACK with that extra ) above
John
More information about the libvir-list
mailing list