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.