[libvirt] RFC: take two on live update of network configuration
Laine Stump
laine at laine.org
Wed Aug 22 04:36:59 UTC 2012
On 08/21/2012 11:47 PM, Daniel Veillard wrote:
> On Tue, Aug 21, 2012 at 11:26:08PM -0400, Laine Stump wrote:
> [...]
>> ===========
>> OPTION 2) have a separate API for each different type of element we may want to
>> change, e.g.:
>>
>>
>> virNetworkUpdateForwardInterface(virNetworkPtr network,
>> const char *xml,
>> unsigned int flags);
>> virNetworkUpdatePortgroup(virNetworkPtr network,
>> const char *xml,
>> unsigned int flags);
>> virNetworkUpdateIpDhcpHost(virNetworkPtr network,
>> const char *xml,
>> unsigned int flags);
>> virNetworkUpdateDnsEntry(virNetworkPtr network,
>> const char *xml,
>> unsigned int flags);
>>
>> /* The name of this one may confuse... */
>> virNetworkUpdateDomain(virNetworkPtr network,
>> const char *xml,
>> unsigned int flags);
>> virNetworkUpdateBridge(virNetworkPtr network,
>> const char *xml,
>> unsigned int flags);
>> virNetworkUpdateIpDnsHost(virNetworkPtr network,
>> const char *xml,
>> unsigned int flags);
>> etc. (etc. etc.)
>>
>> "flags" would include:
>>
>> /* force add if object with matching key doesn't exist */
>> VIR_NETWORK_UPDATE_ADD
>> /* delete existing object(s) that matches the xml */
>> VIR_NETWORK_UPDATE_DELETE
>> /* take effect immediately */
>> VIR_NETWORK_UPDATE_LIVE
>> /* persistent change */
>> VIR_NETWORK_UPDATE_CONFIG
>>
>>
>> This method could be problematic, e.g., for something like
>> virNetworkUpdateIpDhcpHost() - although currently only a single <ip>
>> element can have a <dhcp> entries, in the future we could allow any/all
>> of them to have dhcp entries. Another downside is that we would need to
>> add an new function for each new element we decide we want to be able to
>> update.
> that's an awful lot of potential APIs something more generic is needed
>
>> ===========
>> OPTION 3) Again, a single API, but with an added "nodespec" arg which would
>> be used to describe the parent node you of the node you want added/updated to:
>>
>> int virNetworkUpdate(virNetworkPtr network,
>> const char *nodeSpec,
>> const char *xml,
>> unsigned int flags);
>>
>> For example, if you wanted to add a new <host> entry to <ip
>> address='10.24.75.1' ...>'s dhcp element, you would do this:
>>
>> /* nodespec obviously uses an XPath expression */
>> virNetworkUpdate("/ip[address='10.24.75.1']/dhcp",
>> /* full text of <host> element to add */
>> "<host mac='00:16:3e:77:e2:ed' "
>> "name='X.example.com' ip='10.24.75.10'/>"
>> VIR_NETWORK_UPDATE_ADD | VIR_NETWORK_UPDATE_LIVE
>> | VIR_NETWORK_UPDATE_CONFIG);
>>
>> Or, to change the contents of the exiting portgroup "engineering" you
>> would use:
>>
>> virNetworkUpdate("/",
>> /* full text of portgroup */,
>> "<portgroup name='engineering'> ..... </portgroup>"
>> VIR_NETWORK_UPDATE_LIVE|VIR_NETWORK_UPDATE_CONFIG);
>>
>> To delete a static dhcp host entry, you would use this:
>>
>> virNetworkUpdate("/ip[address='10.24.75.1']/dhcp",
> shouldn't that be //ip[address='10.24.75.1']/dhcp or your really
> expect to have ip as the root ?
Keep in mind that I've only used xpath expressions as much as I've
needed them in libvirt's parsing functions, which usually use "./xxxxx"
style, so I probably got the syntax wrong.
I had figured that the root node would be <network>, and would be "/". I
*think* I've just learned from you that isn't the case, and that an <ip>
element within <network> would be specified (if network was the root
node) as "//ip".
>
>> /* just enough to match existing entry */
>> "<host mac='00:16:3e:77:e2:ed'/>",
>> VIR_NETWORK_UPDATE_DELETE | VIR_NETWORK_UPDATE_LIVE
>> | VIR_NETWORK_UPDATE_CONFIG);
>>
>> or if you preferred:
>>
>> virNetworkUpdate("/ip[address='10.24.75.1']/dhcp",
>> /* again, just enough to match */
>> "<host ip='10.24.75.10'/>",
>> VIR_NETWORK_UPDATE_DELETE |VIR_NETWORK_UPDATE_LIVE
>> | VIR_NETWORK_UPDATE_CONFIG);
>>
>> To remove an interface from the interface pool:
>>
>> virNetworkUpdate("/forward",
>> "<interface dev='eth20'/>",
>> VIR_NETWORK_UPDATE_DELETE | VIR_NETWORK_UPDATE_LIVE
>> | VIR_NETWORK_UPDATE_CONFIG)
>>
>> (adding an interface would be identical, except the flag).
>>
>> (An alternate implementation would be to have nodeSpec specify the node
>> that is being updated/removed rather than its parent. This may make more
>> logical sense, (although not for ADD) and would even make the rest of
>> the code simpler for update/remove (we wouldn't have to do a manual
>> search within the node).
>>
>> The positive side of this is that the one API function allows you to modify
>> *anything* (even stuff not yet invented, so it's future-proof). The negative
>> side is that the code that implements it would need to go to quite a bit of
>> extra trouble to determine what has been changed (basically, it would
>>
>> a) generate XML for the current state of the network,
>> b) parse that into an xmlNode,
>> c) perform the operation on xmlNode using nodeset to figure out where,
>> and adding in the new xml (or removing any nodes matching it),
>> d) convert modified xmlNode back to xml text,
>> e) parse the xml text into a virNetworkDef,
>> f) compare it to the original virNetworkDef to determine what to do.
> One problem I can see with 3) is that it would work for now
> but if we were to add namespaces to the XML then the API would
> not allow to address those elements/attributes
>
> //foo in XPath cannot address an element foo with a namespace
>
> to do this you need to do
>
> //prefix:foo and provide separately the mapping between prefix
> and the namespace URI
>
> So that will work as long as we don't add namespace to the XML
Hmm. I had been thinking recently of adding a private namespace to allow
adding custom commandline options to the dnsmasq commandline similar to
how we currently use a private namespace to allow adding custom
commandline options to the qemu commandline. Maybe something like this:
network xmlns:dnsmasq='http://libvirt.org/schemas/network/dnsmasq/1.0'>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='10.24.75.128' end='10.24.75.254' />
<dnsmasq:commandline>
<dnsmasq:arg value='--max-ttl'/>
<dnsmasq:arg value='25400'/>
<dnsmasq:arg value='--resolv-file=/etc/libvirt-resolv.conf'/>
</dnsmasq:commandline>
</dhcp>
</ip>
</network>
So you're saying that xpath wouldn't allow me to select the
<dnsmasq:commandline> element and replace it? (my guess at the
expression to do that would have been
"//ip[address='10.24.75.128']/dhcp/dnsmasq::commandline")
> Another problem is making sure we have the addressing complete,
> for example suppose we have
> <
>
> The other problem is that people who already have a beef with XML usage
> will see red when they see the need to learn XPath O:-) , but that could
> be alleviated in a large part with a good set of examples !
>
> Also the problem with Update APIs to XML is that the next step is
> people will want to add one part (not replace) and then you need
> to define where to add, before/after ...
Fortunately there is nothing in <network> that is order-dependent, but
in the general sense I understand the problem you're describing. I think
what I'm taking away from your comments is that this isn't the 100%
future-proof solution that I'd imagined.
>
> XML editing APIs are *hard*, I'm actually a bit worried to go there
> something like 2/ while potentially leading to more entry points
> probably makes thing easier for the user.
Or maybe I should reconsider option (1) (a single API that just replaces
the entire XML for the network). That would also require special
considerations for fine-grained access control though - it would need to
compare the old and new, then check to see if the current user was
authorized to change the bits that showed up in the diff. Again, I don't
know how well (if at all) that fits in with the model danpb is planning
to use for that - if it's a model where user's are granted access purely
on a per-API basis, then it won't work. If it's possible to do the
finer-grained check further down in the implementation of the API, then
it might work.
More information about the libvir-list
mailing list