[libvirt] [PATCHv2 0.5/4] network: fix virNetworkObjAssignDef and persistence

John Ferlan jferlan at redhat.com
Thu Apr 24 16:09:14 UTC 2014



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...

>  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.

> +                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


> +            }
>          }
> -    } 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...

> +        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.

Just figured I'd close the loop on this review at least...

John
>  
>      event = virNetworkEventLifecycleNew(network->def->name,
>                                          network->def->uuid,
> diff --git a/src/parallels/parallels_network.c b/src/parallels/parallels_network.c
> index cbb44b9..0ebeb7b 100644
> --- a/src/parallels/parallels_network.c
> +++ b/src/parallels/parallels_network.c
> @@ -2,7 +2,7 @@
>   * parallels_network.c: core privconn functions for managing
>   * Parallels Cloud Server hosts
>   *
> - * Copyright (C) 2013 Red Hat, Inc.
> + * Copyright (C) 2013-2014 Red Hat, Inc.
>   * Copyright (C) 2012 Parallels, Inc.
>   *
>   * This library is free software; you can redistribute it and/or
> @@ -231,7 +231,6 @@ parallelsLoadNetwork(parallelsConnPtr privconn, virJSONValuePtr jobj)
>          goto cleanup;
>      }
>      net->active = 1;
> -    net->persistent = 1;
>      net->autostart = 1;
>      virNetworkObjUnlock(net);
>      return net;
> @@ -267,7 +266,6 @@ parallelsAddRoutedNetwork(parallelsConnPtr privconn)
>          goto cleanup;
>      }
>      net->active = 1;
> -    net->persistent = 1;
>      net->autostart = 1;
>      virNetworkObjUnlock(net);
>  
> diff --git a/src/test/test_driver.c b/src/test/test_driver.c
> index 64f3daa..37756e7 100644
> --- a/src/test/test_driver.c
> +++ b/src/test/test_driver.c
> @@ -784,7 +784,6 @@ testOpenDefault(virConnectPtr conn)
>          goto error;
>      }
>      netobj->active = 1;
> -    netobj->persistent = 1;
>      virNetworkObjUnlock(netobj);
>  
>      if (!(interfacedef = virInterfaceDefParseString(defaultInterfaceXML)))
> @@ -1155,7 +1154,6 @@ testParseNetworks(testConnPtr privconn,
>              goto error;
>          }
>  
> -        obj->persistent = 1;
>          obj->active = 1;
>          virNetworkObjUnlock(obj);
>      }
> @@ -3711,7 +3709,7 @@ static virNetworkPtr testNetworkCreateXML(virConnectPtr conn, const char *xml)
>      if ((def = virNetworkDefParseString(xml)) == NULL)
>          goto cleanup;
>  
> -    if (!(net = virNetworkAssignDef(&privconn->networks, def, false)))
> +    if (!(net = virNetworkAssignDef(&privconn->networks, def, true)))
>          goto cleanup;
>      def = NULL;
>      net->active = 1;
> @@ -3748,7 +3746,6 @@ virNetworkPtr testNetworkDefineXML(virConnectPtr conn, const char *xml)
>      if (!(net = virNetworkAssignDef(&privconn->networks, def, false)))
>          goto cleanup;
>      def = NULL;
> -    net->persistent = 1;
>  
>      event = virNetworkEventLifecycleNew(net->def->name, net->def->uuid,
>                                          VIR_NETWORK_EVENT_DEFINED,
> 





More information about the libvir-list mailing list