[libvirt] [PATCH v4 00/29] network: refactor to decouple virt drivers from network driver

An update to v1: https://www.redhat.com/archives/libvir-list/2018-December/msg00681.html v2: https://www.redhat.com/archives/libvir-list/2019-February/msg01581.html v3: https://www.redhat.com/archives/libvir-list/2019-March/msg01259.html Currently the network driver registers a set of callbacks with the virt driver in order to handle allocating/releasing network ports associated with guest NICs. This series introduces a virNetworkPortPtr object and associated XML that describes a network port. The virt drivers now call public APIs associated with this new object to create/delete ports for guest NICs. Changed in 4: - Merged the ACKd patches which didn't depend on other un-acked parts - Improve bandwidth error messages - Ensure we set floor sum to zero when starting network - Misc fixes for previous review comments NB, I have not added missing docs for the new XML doc format yet. This is work in progress. Changed in v3: - Remove unused API symbol - Fix dist of test data files Changed in v2: - Fix many bugs related to upgrades with running VMs - Convert over bandwidth controls to the new APIs - Handle reconnecting VIFs to bridges during startup - Much much more that I can't remember Daniel P. Berrangé (29): network: ensure floor sum is reset to zero when starting networks network: explain reason for bandwidth floor rejection conf: don't pass interface type into virNetDevBandwidthParse network: use 'bridge' as actual type instead of 'network' virt drivers: don't handle type=network after resolving actual network type network: move re-attach of bridge device out of network driver network: move fixup for domain actual net def out of network driver conf: introduce virNetworkPortDefPtr struct and XML support network: make networkLogAllocation independent of domain conf conf: add APIs to convert virDomainNetDef to virNetworkPortDef network: convert networkAllocateActualDevice to virNetworkPortDef network: convert networkNotifyActualDevice to virNetworkPortDef network: convert networkReleaseActualDevice to virNetworkPortDef network: convert hook script to take a network port XML network: remove the virDomainNetBandwidthChangeAllowed callback network: introduce networkAllocatePort network: introduce networkNotifyPort network: introduce networkReleasePort network: introduce networkUpdatePortBandwidth network: add public APIs for network port object access: add permissions for network port objects remote: add support for new network port APIs virsh: add support for network port APIs conf: support recording ports against virNetworkObjPtr network: add implementation of network port APIs lxc, libxl: notify network driver of NICs during reconnect lxc, libxl: save domain status after reconnect conf: record a portid against the domain conf conf: switch over to use network port APIs for virt drivers docs/formatdomain.html.in | 8 + docs/hooks.html.in | 24 +- docs/schemas/domaincommon.rng | 5 + include/libvirt/libvirt-network.h | 122 ++ include/libvirt/virterror.h | 3 + src/access/genpolkit.pl | 2 +- src/access/viraccessdriver.h | 6 + src/access/viraccessdrivernop.c | 11 + src/access/viraccessdriverpolkit.c | 26 + src/access/viraccessdriverstack.c | 25 + src/access/viraccessmanager.c | 16 + src/access/viraccessmanager.h | 6 + src/access/viraccessperm.c | 6 + src/access/viraccessperm.h | 44 + src/conf/Makefile.inc.am | 2 + src/conf/domain_conf.c | 549 +++++++- src/conf/domain_conf.h | 49 +- src/conf/netdev_bandwidth_conf.c | 22 +- src/conf/netdev_bandwidth_conf.h | 2 +- src/conf/network_conf.c | 4 +- src/conf/virnetworkobj.c | 303 ++++ src/conf/virnetworkobj.h | 34 + src/conf/virnetworkportdef.c | 515 +++++++ src/conf/virnetworkportdef.h | 112 ++ src/datatypes.c | 60 + src/datatypes.h | 41 + src/driver-network.h | 41 + src/libvirt-network.c | 444 ++++++ src/libvirt_private.syms | 23 +- src/libvirt_public.syms | 15 + src/libxl/libxl_conf.c | 21 +- src/libxl/libxl_driver.c | 33 + src/lxc/lxc_driver.c | 15 +- src/lxc/lxc_process.c | 35 + src/network/bridge_driver.c | 1219 +++++++++-------- src/qemu/qemu_command.c | 8 +- src/qemu/qemu_driver.c | 10 +- src/qemu/qemu_hotplug.c | 13 +- src/qemu/qemu_interface.c | 12 +- src/qemu/qemu_process.c | 5 +- src/remote/remote_daemon_dispatch.c | 73 + src/remote/remote_driver.c | 69 + src/remote/remote_protocol.x | 124 +- src/remote_protocol-structs | 69 + src/rpc/gendispatch.pl | 18 +- src/util/virerror.c | 9 + src/util/virhook.c | 4 +- src/util/virhook.h | 4 +- tests/Makefile.am | 7 + .../net-virtio-network-portgroup.xml | 6 +- tests/virnetdevbandwidthtest.c | 2 +- .../plug-bridge-mactbl.xml | 9 + .../virnetworkportxml2xmldata/plug-bridge.xml | 12 + .../virnetworkportxml2xmldata/plug-direct.xml | 12 + .../plug-hostdev-pci.xml | 12 + tests/virnetworkportxml2xmldata/plug-none.xml | 8 + tests/virnetworkportxml2xmltest.c | 104 ++ tools/virsh-completer.c | 50 + tools/virsh-completer.h | 4 + tools/virsh-network.c | 399 +++++- tools/virsh-network.h | 5 + 61 files changed, 4171 insertions(+), 720 deletions(-) create mode 100644 src/conf/virnetworkportdef.c create mode 100644 src/conf/virnetworkportdef.h create mode 100644 tests/virnetworkportxml2xmldata/plug-bridge-mactbl.xml create mode 100644 tests/virnetworkportxml2xmldata/plug-bridge.xml create mode 100644 tests/virnetworkportxml2xmldata/plug-direct.xml create mode 100644 tests/virnetworkportxml2xmldata/plug-hostdev-pci.xml create mode 100644 tests/virnetworkportxml2xmldata/plug-none.xml create mode 100644 tests/virnetworkportxml2xmltest.c -- 2.20.1

In extreme cases libvirt can get mixed up about what VMs are running and attached to a network leading to the cached floor sum value being outdated. When this happens the only option is to destroy the network and then restart libvirtd. If we set floor sum back to zero when starting the network, we avoid the need for a libvirtd restart at least. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 6e15e43ca6..3008846074 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -2826,6 +2826,8 @@ networkStartNetwork(virNetworkDriverStatePtr driver, goto cleanup; } + virNetworkObjSetFloorSum(obj, 0); + /* finally we can call the 'started' hook script if any */ if (networkRunHook(obj, NULL, NULL, VIR_HOOK_NETWORK_OP_STARTED, -- 2.20.1

On 4/17/19 1:19 PM, Daniel P. Berrangé wrote:
In extreme cases libvirt can get mixed up about what VMs are running and attached to a network leading to the cached floor sum value being outdated. When this happens the only option is to destroy the network and then restart libvirtd. If we set floor sum back to zero when starting the network, we avoid the need for a libvirtd restart at least.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Laine Stump <laine@laine.org>
--- src/network/bridge_driver.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 6e15e43ca6..3008846074 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -2826,6 +2826,8 @@ networkStartNetwork(virNetworkDriverStatePtr driver, goto cleanup; }
+ virNetworkObjSetFloorSum(obj, 0); + /* finally we can call the 'started' hook script if any */ if (networkRunHook(obj, NULL, NULL, VIR_HOOK_NETWORK_OP_STARTED,

Reword error messages to make it clear that the combined floor settings of all NICs are exceeding the network inbound peak/average settings. Including the actual values being checked helps to diagnose what is actually wrong. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 3008846074..6ed0bf1e8e 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -5225,10 +5225,13 @@ networkCheckBandwidth(virNetworkObjPtr obj, tmp_new_rate = netBand->in->peak; if (tmp_floor_sum > netBand->in->peak) { virReportError(VIR_ERR_OPERATION_INVALID, - _("Cannot plug '%s' interface into '%s' because it " - "would overcommit 'peak' on network '%s'"), + _("Cannot plug '%s' interface into '%s' because " + "new combined inbound floor=%llu would overcommit " + "peak=%llu on network '%s'"), ifmac, def->bridge, + tmp_floor_sum, + netBand->in->peak, def->name); goto cleanup; } @@ -5236,10 +5239,13 @@ networkCheckBandwidth(virNetworkObjPtr obj, /* tmp_floor_sum can be between 'average' and 'peak' iff 'peak' is set. * Otherwise, tmp_floor_sum must be below 'average'. */ virReportError(VIR_ERR_OPERATION_INVALID, - _("Cannot plug '%s' interface into '%s' because it " - "would overcommit 'average' on network '%s'"), + _("Cannot plug '%s' interface into '%s' because " + "new combined inbound floor=%llu would overcommit " + "average=%llu on network '%s'"), ifmac, def->bridge, + tmp_floor_sum, + netBand->in->average, def->name); goto cleanup; } -- 2.20.1

On 4/17/19 1:19 PM, Daniel P. Berrangé wrote:
Reword error messages to make it clear that the combined floor settings of all NICs are exceeding the network inbound peak/average settings. Including the actual values being checked helps to diagnose what is actually wrong.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Laine Stump <laine@laine.org>

The virNetDevBandwidthParse method uses the interface type to decide whether to allow use of the "floor" parameter. Using the interface type is not convenient as callers may not have that available, but still wish to allow use of "floor". Switch to an explicit boolean to control its usage. Reviewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 4 ++-- src/conf/netdev_bandwidth_conf.c | 22 +++++++--------------- src/conf/netdev_bandwidth_conf.h | 2 +- src/conf/network_conf.c | 4 ++-- tests/virnetdevbandwidthtest.c | 2 +- 5 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 51aa48f421..0df3c2ed49 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -11270,7 +11270,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, if (bandwidth_node && virNetDevBandwidthParse(&actual->bandwidth, bandwidth_node, - actual->type) < 0) + def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0) goto error; vlanNode = virXPathNode("./vlan", ctxt); @@ -11609,7 +11609,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, } else if (virXMLNodeNameEqual(cur, "bandwidth")) { if (virNetDevBandwidthParse(&def->bandwidth, cur, - def->type) < 0) + def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0) goto error; } else if (virXMLNodeNameEqual(cur, "vlan")) { if (virNetDevVlanParse(cur, ctxt, &def->vlan) < 0) diff --git a/src/conf/netdev_bandwidth_conf.c b/src/conf/netdev_bandwidth_conf.c index 3113cde888..014941836d 100644 --- a/src/conf/netdev_bandwidth_conf.c +++ b/src/conf/netdev_bandwidth_conf.c @@ -100,18 +100,18 @@ virNetDevBandwidthParseRate(xmlNodePtr node, virNetDevBandwidthRatePtr rate) * virNetDevBandwidthParse: * @bandwidth: parsed bandwidth * @node: XML node - * @net_type: one of virDomainNetType + * @allowFloor: whether "floor" setting is supported * * Parse bandwidth XML and return pointer to structure. - * @net_type tell to which type will/is interface connected to. - * Pass -1 if this is not called on interface. + * The @allowFloor attribute indicates whether the caller + * is able to support use of the "floor" setting. * * Returns !NULL on success, NULL on error. */ int virNetDevBandwidthParse(virNetDevBandwidthPtr *bandwidth, xmlNodePtr node, - int net_type) + bool allowFloor) { int ret = -1; virNetDevBandwidthPtr def = NULL; @@ -162,17 +162,9 @@ virNetDevBandwidthParse(virNetDevBandwidthPtr *bandwidth, goto cleanup; } - if (def->in->floor && net_type != VIR_DOMAIN_NET_TYPE_NETWORK) { - if (net_type == -1) { - /* 'floor' on network isn't supported */ - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("floor attribute isn't supported for " - "network's bandwidth yet")); - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("floor attribute is supported only for " - "interfaces of type network")); - } + if (def->in->floor && !allowFloor) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("floor attribute is not supported for this config")); goto cleanup; } } diff --git a/src/conf/netdev_bandwidth_conf.h b/src/conf/netdev_bandwidth_conf.h index cb1ffd29e0..7fe750ce27 100644 --- a/src/conf/netdev_bandwidth_conf.h +++ b/src/conf/netdev_bandwidth_conf.h @@ -27,7 +27,7 @@ int virNetDevBandwidthParse(virNetDevBandwidthPtr *bandwidth, xmlNodePtr node, - int net_type) + bool allowFloor) ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; int virNetDevBandwidthFormat(virNetDevBandwidthPtr def, virBufferPtr buf); diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 78bff6f687..91562de269 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -1188,7 +1188,7 @@ virNetworkPortGroupParseXML(virPortGroupDefPtr def, bandwidth_node = virXPathNode("./bandwidth", ctxt); if (bandwidth_node && - virNetDevBandwidthParse(&def->bandwidth, bandwidth_node, -1) < 0) + virNetDevBandwidthParse(&def->bandwidth, bandwidth_node, false) < 0) goto cleanup; vlanNode = virXPathNode("./vlan", ctxt); @@ -1682,7 +1682,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) } if ((bandwidthNode = virXPathNode("./bandwidth", ctxt)) && - virNetDevBandwidthParse(&def->bandwidth, bandwidthNode, -1) < 0) + virNetDevBandwidthParse(&def->bandwidth, bandwidthNode, false) < 0) goto error; vlanNode = virXPathNode("./vlan", ctxt); diff --git a/tests/virnetdevbandwidthtest.c b/tests/virnetdevbandwidthtest.c index 96776fa033..23788b4164 100644 --- a/tests/virnetdevbandwidthtest.c +++ b/tests/virnetdevbandwidthtest.c @@ -55,7 +55,7 @@ struct testSetStruct { \ rc = virNetDevBandwidthParse(&(var), \ ctxt->node, \ - VIR_DOMAIN_NET_TYPE_NETWORK); \ + true); \ xmlFreeDoc(doc); \ xmlXPathFreeContext(ctxt); \ if (rc < 0) \ -- 2.20.1

On 4/17/19 1:19 PM, Daniel P. Berrangé wrote:
The virNetDevBandwidthParse method uses the interface type to decide whether to allow use of the "floor" parameter. Using the interface type is not convenient as callers may not have that available, but still wish to allow use of "floor".
Also, this is one of the things that gave rise to the distinction between actualType == NETWORK and actualType == BRIDGE, and this patch allows us to eliminate that (hopefully) without causing breakage.
Switch to an explicit boolean to control its usage.
Reviewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 4 ++-- src/conf/netdev_bandwidth_conf.c | 22 +++++++--------------- src/conf/netdev_bandwidth_conf.h | 2 +- src/conf/network_conf.c | 4 ++-- tests/virnetdevbandwidthtest.c | 2 +- 5 files changed, 13 insertions(+), 21 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 51aa48f421..0df3c2ed49 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -11270,7 +11270,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, if (bandwidth_node && virNetDevBandwidthParse(&actual->bandwidth, bandwidth_node, - actual->type) < 0) + def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0)
This bit here ^^^ doesn't compile, since def is undefined. You would need to check "parent->type" instead.
goto error;
vlanNode = virXPathNode("./vlan", ctxt); @@ -11609,7 +11609,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, } else if (virXMLNodeNameEqual(cur, "bandwidth")) { if (virNetDevBandwidthParse(&def->bandwidth, cur, - def->type) < 0) + def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0) goto error; } else if (virXMLNodeNameEqual(cur, "vlan")) { if (virNetDevVlanParse(cur, ctxt, &def->vlan) < 0) diff --git a/src/conf/netdev_bandwidth_conf.c b/src/conf/netdev_bandwidth_conf.c index 3113cde888..014941836d 100644 --- a/src/conf/netdev_bandwidth_conf.c +++ b/src/conf/netdev_bandwidth_conf.c @@ -100,18 +100,18 @@ virNetDevBandwidthParseRate(xmlNodePtr node, virNetDevBandwidthRatePtr rate) * virNetDevBandwidthParse: * @bandwidth: parsed bandwidth * @node: XML node - * @net_type: one of virDomainNetType + * @allowFloor: whether "floor" setting is supported * * Parse bandwidth XML and return pointer to structure. - * @net_type tell to which type will/is interface connected to. - * Pass -1 if this is not called on interface. + * The @allowFloor attribute indicates whether the caller + * is able to support use of the "floor" setting. * * Returns !NULL on success, NULL on error. */ int virNetDevBandwidthParse(virNetDevBandwidthPtr *bandwidth, xmlNodePtr node, - int net_type) + bool allowFloor) { int ret = -1; virNetDevBandwidthPtr def = NULL; @@ -162,17 +162,9 @@ virNetDevBandwidthParse(virNetDevBandwidthPtr *bandwidth, goto cleanup; }
- if (def->in->floor && net_type != VIR_DOMAIN_NET_TYPE_NETWORK) { - if (net_type == -1) { - /* 'floor' on network isn't supported */ - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("floor attribute isn't supported for " - "network's bandwidth yet")); - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("floor attribute is supported only for " - "interfaces of type network")); - } + if (def->in->floor && !allowFloor) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("floor attribute is not supported for this config")); goto cleanup; } } diff --git a/src/conf/netdev_bandwidth_conf.h b/src/conf/netdev_bandwidth_conf.h index cb1ffd29e0..7fe750ce27 100644 --- a/src/conf/netdev_bandwidth_conf.h +++ b/src/conf/netdev_bandwidth_conf.h @@ -27,7 +27,7 @@
int virNetDevBandwidthParse(virNetDevBandwidthPtr *bandwidth, xmlNodePtr node, - int net_type) + bool allowFloor) ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; int virNetDevBandwidthFormat(virNetDevBandwidthPtr def, virBufferPtr buf); diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 78bff6f687..91562de269 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -1188,7 +1188,7 @@ virNetworkPortGroupParseXML(virPortGroupDefPtr def,
bandwidth_node = virXPathNode("./bandwidth", ctxt); if (bandwidth_node && - virNetDevBandwidthParse(&def->bandwidth, bandwidth_node, -1) < 0) + virNetDevBandwidthParse(&def->bandwidth, bandwidth_node, false) < 0)
I think this is a bug, but even if it is, it is pre-existing. (The bandwidth in a <portgroup> should be applied to any interface tagged with that portgroup, i.e. it's not an attribute that's set for the entire network, but for just one interface. So iiuc, allowFloor should be true if the network is one that uses a routed connection (forward mode route/nat/none))
goto cleanup;
vlanNode = virXPathNode("./vlan", ctxt); @@ -1682,7 +1682,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) }
if ((bandwidthNode = virXPathNode("./bandwidth", ctxt)) && - virNetDevBandwidthParse(&def->bandwidth, bandwidthNode, -1) < 0) + virNetDevBandwidthParse(&def->bandwidth, bandwidthNode, false) < 0) goto error;
vlanNode = virXPathNode("./vlan", ctxt); diff --git a/tests/virnetdevbandwidthtest.c b/tests/virnetdevbandwidthtest.c index 96776fa033..23788b4164 100644 --- a/tests/virnetdevbandwidthtest.c +++ b/tests/virnetdevbandwidthtest.c @@ -55,7 +55,7 @@ struct testSetStruct { \ rc = virNetDevBandwidthParse(&(var), \ ctxt->node, \ - VIR_DOMAIN_NET_TYPE_NETWORK); \ + true); \ xmlFreeDoc(doc); \ xmlXPathFreeContext(ctxt); \ if (rc < 0) \
Assuming def->type is changed to parent->type in the first instance, Reviewed-by: Laine Stump <laine@laine.org> (also the issue in virNetworkPortGroupParseXML should be fixed, but in a separate patch)

On Wed, Apr 17, 2019 at 04:33:54PM -0400, Laine Stump wrote:
On 4/17/19 1:19 PM, Daniel P. Berrangé wrote:
The virNetDevBandwidthParse method uses the interface type to decide whether to allow use of the "floor" parameter. Using the interface type is not convenient as callers may not have that available, but still wish to allow use of "floor".
Also, this is one of the things that gave rise to the distinction between actualType == NETWORK and actualType == BRIDGE, and this patch allows us to eliminate that (hopefully) without causing breakage.
Well sort of. If using a plain bridge virtual network you can't use floor - that's only valid for routed networks. Trying to enforce this at parse time is doomed though - you can only corrrectly report this at runtime as you need the context of the virtual network itself. This was already an issue when parsing the top level netdef - only the actual netdef had any better checking. I'm almost inclined to remove all the logic preventing "floor" at XML parse time and rely on runtime checks entirely.
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 51aa48f421..0df3c2ed49 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -11270,7 +11270,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, if (bandwidth_node && virNetDevBandwidthParse(&actual->bandwidth, bandwidth_node, - actual->type) < 0) + def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0)
This bit here ^^^ doesn't compile, since def is undefined. You would need to check "parent->type" instead.
Should be "actual->type" in fact. Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 4/18/19 6:32 AM, Daniel P. Berrangé wrote:
On 4/17/19 1:19 PM, Daniel P. Berrangé wrote:
The virNetDevBandwidthParse method uses the interface type to decide whether to allow use of the "floor" parameter. Using the interface type is not convenient as callers may not have that available, but still wish to allow use of "floor".
Also, this is one of the things that gave rise to the distinction between actualType == NETWORK and actualType == BRIDGE, and this patch allows us to eliminate that (hopefully) without causing breakage. Well sort of. If using a plain bridge virtual network you can't use floor - that's only valid for routed networks. Trying to enforce
On Wed, Apr 17, 2019 at 04:33:54PM -0400, Laine Stump wrote: this at parse time is doomed though - you can only corrrectly report this at runtime as you need the context of the virtual network itself.
This was already an issue when parsing the top level netdef - only the actual netdef had any better checking.
I'm almost inclined to remove all the logic preventing "floor" at XML parse time and rely on runtime checks entirely.
Except that the same parse function is used for the <bandwidth> element in <network>, which never allows floor to be set. For the <bandwidth> in an interface though, you're correct that it's impossible to know beforehand.
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 51aa48f421..0df3c2ed49 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -11270,7 +11270,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, if (bandwidth_node && virNetDevBandwidthParse(&actual->bandwidth, bandwidth_node, - actual->type) < 0) + def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0)
This bit here ^^^ doesn't compile, since def is undefined. You would need to check "parent->type" instead. Should be "actual->type" in fact.
I was thinking in terms of making it still correct in the "new world order" where actual->type will always be bridge rather than network. But then later I saw that this line is removed in the next patch anyway, so it's kind of irrelevant - for the state of everything at the point of *this* patch, the two are equivalent.

Ports allocated on virtual networks with type=nat|route|open all get given an actual type of 'network'. Only ports in networks with type=bridge use an actual type of 'bridge'. This distinction makes little sense since the virtualization drivers will treat both actual types in exactly the same way, as they're all just bridge devices a VM needs to be connected to. This doesn't affect user visible XML since the "actual" device XML is internal only, but we need code to convert the data upgrades. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 28 +++++++++++++++++++++++----- src/network/bridge_driver.c | 11 +++-------- src/qemu/qemu_driver.c | 2 +- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0df3c2ed49..1dde45a6fd 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5081,6 +5081,19 @@ virDomainDiskDefPostParse(virDomainDiskDefPtr disk, return -1; } + /* Older libvirtd uses actualType==network, but we now + * just use actualType==bridge, as nothing needs to + * distinguish the two cases, and this simplifies virt + * drive code */ + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK && + net->data.network.actual != NULL && + net->data.network.actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + char mac[VIR_MAC_STRING_BUFLEN]; + virMacAddrFormat(&net->mac, mac); + VIR_DEBUG("Updating NIC %s actual type to bridge", mac); + net->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + } + return 0; } @@ -11267,11 +11280,16 @@ virDomainActualNetDefParseXML(xmlNodePtr node, } bandwidth_node = virXPathNode("./bandwidth", ctxt); - if (bandwidth_node && - virNetDevBandwidthParse(&actual->bandwidth, - bandwidth_node, - def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0) - goto error; + if (bandwidth_node) { + bool allowFloor = + (actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) || + (actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE && + actual->data.bridge.brname != NULL); + if (virNetDevBandwidthParse(&actual->bandwidth, + bandwidth_node, + allowFloor) < 0) + goto error; + } vlanNode = virXPathNode("./vlan", ctxt); if (vlanNode && virNetDevVlanParse(vlanNode, ctxt, &actual->vlan) < 0) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 6ed0bf1e8e..4055939ade 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4491,11 +4491,7 @@ networkAllocateActualDevice(virNetworkPtr net, case VIR_NETWORK_FORWARD_NAT: case VIR_NETWORK_FORWARD_ROUTE: case VIR_NETWORK_FORWARD_OPEN: - /* for these forward types, the actual net type really *is* - * NETWORK; we just keep the info from the portgroup in - * iface->data.network.actual - */ - iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_NETWORK; + iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE; /* we also store the bridge device and macTableManager settings * in iface->data.network.actual->data.bridge for later use @@ -5454,9 +5450,8 @@ networkBandwidthGenericChecks(virDomainNetDefPtr iface, virNetDevBandwidthPtr ifaceBand; unsigned long long old_floor, new_floor; - if (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_NETWORK && - (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_BRIDGE || - iface->data.network.actual->data.bridge.brname == NULL)) { + if (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_BRIDGE || + iface->data.network.actual->data.bridge.brname == NULL) { /* This is not an interface that's plugged into a network. * We don't care. Thus from our POV bandwidth change is allowed. */ return false; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c443c881d5..31d8647eee 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4567,7 +4567,7 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, syncNicRxFilterDeviceOptions(def->ifname, guestFilter, hostFilter); } - if (virDomainNetGetActualType(def) == VIR_DOMAIN_NET_TYPE_NETWORK) { + if (virDomainNetGetActualType(def) == VIR_DOMAIN_NET_TYPE_BRIDGE) { const char *brname = virDomainNetGetActualBridgeName(def); /* For libivrt network connections, set the following TUN/TAP network -- 2.20.1

On 4/17/19 1:19 PM, Daniel P. Berrangé wrote:
Ports allocated on virtual networks with type=nat|route|open all get given an actual type of 'network'.
Only ports in networks with type=bridge use an actual type of 'bridge'.
This distinction makes little sense since the virtualization drivers will treat both actual types in exactly the same way, as they're all just bridge devices a VM needs to be connected to.
Yeah, I don't remember if there was an operational difference between the two in the past (other than bandwidth floor) or if I just made the distinction because I was concerned that there *might be*, but definitely it's something that needs to go! (Heck, maybe there *still* is a reason for differentiating, but even if there is we should still be able to look at the config of the network itself to learn the necessary details).
This doesn't affect user visible XML since the "actual" device XML is internal only, but we need code to convert the data upgrades.
This does mean that downgrades will create "strange situations". Nothing horribly bad, but worth remembering for future reference.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 28 +++++++++++++++++++++++----- src/network/bridge_driver.c | 11 +++-------- src/qemu/qemu_driver.c | 2 +-
You also need to update the result of several xml2xml tests: qemustatusxml2xmldata/migration-out-nbd-out.xml qemustatusxml2xmldata/migration-in-params-out.xml qemustatusxml2xmldata/migration-out-params-out.xml qemustatusxml2xmldata/migration-out-nbd-tls-out.xml qemustatusxml2xmldata/disk-secinfo-upgrade-out.xml (thanks again to crobinson for VIR_TEST_REGENERATE_OUTPUT :-)
3 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0df3c2ed49..1dde45a6fd 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5081,6 +5081,19 @@ virDomainDiskDefPostParse(virDomainDiskDefPtr disk, return -1; }
+ /* Older libvirtd uses actualType==network, but we now + * just use actualType==bridge, as nothing needs to + * distinguish the two cases, and this simplifies virt + * drive code */ + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK && + net->data.network.actual != NULL && + net->data.network.actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + char mac[VIR_MAC_STRING_BUFLEN]; + virMacAddrFormat(&net->mac, mac); + VIR_DEBUG("Updating NIC %s actual type to bridge", mac); + net->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + } +
It looks like git was confused here during a rebase, and it merged the code into the wrong function - this patch puts it into virDomainDiskDefPostParse(), but I'm guessing it should go in virDomainNetDefPostParse() (moving it there certainly gets it to compile properly at least :-) (NB: once several years ago git merged a chunk into the wrong function and I learned that (at that time, hopefully it's changed since) git ignored the name of the function listed just after the line number info at the top of each chunk).
return 0; }
@@ -11267,11 +11280,16 @@ virDomainActualNetDefParseXML(xmlNodePtr node, }
bandwidth_node = virXPathNode("./bandwidth", ctxt); - if (bandwidth_node && - virNetDevBandwidthParse(&actual->bandwidth, - bandwidth_node, - def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0) - goto error; + if (bandwidth_node) { + bool allowFloor = + (actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) || + (actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE && + actual->data.bridge.brname != NULL); + if (virNetDevBandwidthParse(&actual->bandwidth, + bandwidth_node, + allowFloor) < 0) + goto error; + }
vlanNode = virXPathNode("./vlan", ctxt); if (vlanNode && virNetDevVlanParse(vlanNode, ctxt, &actual->vlan) < 0) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 6ed0bf1e8e..4055939ade 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4491,11 +4491,7 @@ networkAllocateActualDevice(virNetworkPtr net, case VIR_NETWORK_FORWARD_NAT: case VIR_NETWORK_FORWARD_ROUTE: case VIR_NETWORK_FORWARD_OPEN: - /* for these forward types, the actual net type really *is* - * NETWORK; we just keep the info from the portgroup in - * iface->data.network.actual - */ - iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_NETWORK; + iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
/* we also store the bridge device and macTableManager settings * in iface->data.network.actual->data.bridge for later use @@ -5454,9 +5450,8 @@ networkBandwidthGenericChecks(virDomainNetDefPtr iface, virNetDevBandwidthPtr ifaceBand; unsigned long long old_floor, new_floor;
- if (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_NETWORK && - (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_BRIDGE || - iface->data.network.actual->data.bridge.brname == NULL)) { + if (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_BRIDGE || + iface->data.network.actual->data.bridge.brname == NULL) { /* This is not an interface that's plugged into a network. * We don't care. Thus from our POV bandwidth change is allowed. */ return false; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c443c881d5..31d8647eee 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4567,7 +4567,7 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, syncNicRxFilterDeviceOptions(def->ifname, guestFilter, hostFilter); }
- if (virDomainNetGetActualType(def) == VIR_DOMAIN_NET_TYPE_NETWORK) { + if (virDomainNetGetActualType(def) == VIR_DOMAIN_NET_TYPE_BRIDGE) { const char *brname = virDomainNetGetActualBridgeName(def);
/* For libivrt network connections, set the following TUN/TAP network
This at first seemed really short since there are many more places that check for actualtype=network, but you add an error to all those places in the next patch, which makes sense. Assuming that you fix the xml2xml test cases and move the chunk that got merged into the wrong place: Reviewed-by: Laine Stump <laine@laine.org>

On 4/17/19 7:19 PM, Daniel P. Berrangé wrote:
Ports allocated on virtual networks with type=nat|route|open all get given an actual type of 'network'.
Only ports in networks with type=bridge use an actual type of 'bridge'.
This distinction makes little sense since the virtualization drivers will treat both actual types in exactly the same way, as they're all just bridge devices a VM needs to be connected to.
This doesn't affect user visible XML since the "actual" device XML is internal only, but we need code to convert the data upgrades.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 28 +++++++++++++++++++++++----- src/network/bridge_driver.c | 11 +++-------- src/qemu/qemu_driver.c | 2 +- 3 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0df3c2ed49..1dde45a6fd 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5081,6 +5081,19 @@ virDomainDiskDefPostParse(virDomainDiskDefPtr disk, return -1; }
+ /* Older libvirtd uses actualType==network, but we now + * just use actualType==bridge, as nothing needs to + * distinguish the two cases, and this simplifies virt + * drive code */ + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK && + net->data.network.actual != NULL && + net->data.network.actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + char mac[VIR_MAC_STRING_BUFLEN]; + virMacAddrFormat(&net->mac, mac); + VIR_DEBUG("Updating NIC %s actual type to bridge", mac); + net->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + } + return 0; }
@@ -11267,11 +11280,16 @@ virDomainActualNetDefParseXML(xmlNodePtr node, }
bandwidth_node = virXPathNode("./bandwidth", ctxt); - if (bandwidth_node && - virNetDevBandwidthParse(&actual->bandwidth, - bandwidth_node, - def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0) - goto error; + if (bandwidth_node) { + bool allowFloor = + (actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) || + (actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE && + actual->data.bridge.brname != NULL); + if (virNetDevBandwidthParse(&actual->bandwidth, + bandwidth_node, + allowFloor) < 0) + goto error; + }
vlanNode = virXPathNode("./vlan", ctxt); if (vlanNode && virNetDevVlanParse(vlanNode, ctxt, &actual->vlan) < 0) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 6ed0bf1e8e..4055939ade 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4491,11 +4491,7 @@ networkAllocateActualDevice(virNetworkPtr net, case VIR_NETWORK_FORWARD_NAT: case VIR_NETWORK_FORWARD_ROUTE: case VIR_NETWORK_FORWARD_OPEN: - /* for these forward types, the actual net type really *is* - * NETWORK; we just keep the info from the portgroup in - * iface->data.network.actual - */ - iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_NETWORK; + iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
Actually, this breaks a lot of stuff. Firstly, for a live XML the type is changed to <interface type='bridge'> .. </interface>. This is because virDomainNetDefFormat() decides to format actual info. Having said that, transitionally some APIs are broken too. For instance, I have the following interface for my domain (inactive XML): <interface type='network' trustGuestRxFilters='yes'> <mac address='52:54:00:a4:6f:91'/> <source network='default'/> <bandwidth> <inbound average='1024' peak='4096' floor='500' burst='1024'/> <outbound average='10240'/> </bandwidth> <model type='virtio'/> <mtu size='9000'/> </interface> Now, 'virsh domif-setlink', 'virsh update-device', 'virsh domiftune' don't work, because at runtime type is changed to 'bridge' and we don't implement many features for that network type. I'll post patches for the first two issues I've found. But they look pretty hacky and I'm not sure my testing was exhaustive enough to find other cases where this will break. The third one I have no idea how to fix because it boils down to looking a network in the bridge driver but we don't know the network name because we don't parse it for interface type bridge. Should we revert this? Michal

On 4/26/19 5:07 AM, Michal Privoznik wrote:
On 4/17/19 7:19 PM, Daniel P. Berrangé wrote:
Ports allocated on virtual networks with type=nat|route|open all get given an actual type of 'network'.
Only ports in networks with type=bridge use an actual type of 'bridge'.
This distinction makes little sense since the virtualization drivers will treat both actual types in exactly the same way, as they're all just bridge devices a VM needs to be connected to.
This doesn't affect user visible XML since the "actual" device XML is internal only, but we need code to convert the data upgrades.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 28 +++++++++++++++++++++++----- src/network/bridge_driver.c | 11 +++-------- src/qemu/qemu_driver.c | 2 +- 3 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0df3c2ed49..1dde45a6fd 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5081,6 +5081,19 @@ virDomainDiskDefPostParse(virDomainDiskDefPtr disk, return -1; } + /* Older libvirtd uses actualType==network, but we now + * just use actualType==bridge, as nothing needs to + * distinguish the two cases, and this simplifies virt + * drive code */ + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK && + net->data.network.actual != NULL && + net->data.network.actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + char mac[VIR_MAC_STRING_BUFLEN]; + virMacAddrFormat(&net->mac, mac); + VIR_DEBUG("Updating NIC %s actual type to bridge", mac); + net->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + } + return 0; } @@ -11267,11 +11280,16 @@ virDomainActualNetDefParseXML(xmlNodePtr node, } bandwidth_node = virXPathNode("./bandwidth", ctxt); - if (bandwidth_node && - virNetDevBandwidthParse(&actual->bandwidth, - bandwidth_node, - def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0) - goto error; + if (bandwidth_node) { + bool allowFloor = + (actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) || + (actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE && + actual->data.bridge.brname != NULL); + if (virNetDevBandwidthParse(&actual->bandwidth, + bandwidth_node, + allowFloor) < 0) + goto error; + } vlanNode = virXPathNode("./vlan", ctxt); if (vlanNode && virNetDevVlanParse(vlanNode, ctxt, &actual->vlan) < 0) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 6ed0bf1e8e..4055939ade 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4491,11 +4491,7 @@ networkAllocateActualDevice(virNetworkPtr net, case VIR_NETWORK_FORWARD_NAT: case VIR_NETWORK_FORWARD_ROUTE: case VIR_NETWORK_FORWARD_OPEN: - /* for these forward types, the actual net type really *is* - * NETWORK; we just keep the info from the portgroup in - * iface->data.network.actual - */ - iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_NETWORK; + iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
Actually, this breaks a lot of stuff. Firstly, for a live XML the type is changed to <interface type='bridge'> .. </interface>. This is because virDomainNetDefFormat() decides to format actual info. Having said that, transitionally some APIs are broken too. For instance, I have the following interface for my domain (inactive XML):
<interface type='network' trustGuestRxFilters='yes'> <mac address='52:54:00:a4:6f:91'/> <source network='default'/> <bandwidth> <inbound average='1024' peak='4096' floor='500' burst='1024'/> <outbound average='10240'/> </bandwidth> <model type='virtio'/> <mtu size='9000'/> </interface>
Now, 'virsh domif-setlink', 'virsh update-device', 'virsh domiftune' don't work, because at runtime type is changed to 'bridge' and we don't implement many features for that network type.
Ugh. The problem here is that we've always (or at least *I've* always) worked with the mistaken idea that there are 2 types of info about a domain - status and persistent config) - when really there are 3 - status, transient config, and persistent config. The problem being suffered is because the interface type returned in the live XML has in the past been partly status, partly transient config. Dan's patch has made it 100% status, but the virsh commands above treat it as transient config. I knew I was responsible for the change that made the status XML give the actual type of the interface (so "status") rather than the type from the persistent config, but couldn't remember why I did it, so I looked back in git history and found commit 7d5bf4847. A simpler way to look at it might be via the email from the list (that way you can see the review responses): https://www.redhat.com/archives/libvir-list/2014-February/msg01377.html In short, it was done so that the network hook "plugged" event (and other users of the public API) could get all the information about the way the network device had been setup. It seemed like a good idea at the time, but in retrospect, I think it was a mistake (in this case and in general) to provide status in the same attribute used for config when those two things might be different (when I say it out loud that way, it's pretty obious :-/). Instead, any "status" item that could *ever* possibly differ from a similar "transient config" item should have its own distinct read-only attribute/element in the XML. Once the rest of Dan's "virNetworkPort" series is in, a user will be able to get all the info they need by first getting the domain XML, then using the uuid of the interface to get the NetworkPort XML for that connection (that may still not be perfect, because interfaces that aren't type='network' won't have a networkport, but it will be enough for the use case we're considering here). It will be more complicated that just reading the domain XML a single time, but at least it will allow us to learn status info distinct from transient config.
I'll post patches for the first two issues I've found. But they look pretty hacky and I'm not sure my testing was exhaustive enough to find other cases where this will break. The third one I have no idea how to fix because it boils down to looking a network in the bridge driver but we don't know the network name because we don't parse it for interface type bridge.
Doesn't it all come down to the fact that the status XML now has "bridge" instead of "network"? Maybe if we make a patch to change *only* what is put in the externally visible status XML, but internally (and in the <actual> element of the status that's stored on disk) maintain the change from "network" to "bridge"? I can try making a patch like that.
Should we revert this?
If we can't do something to get these virsh commands working before the release, then I think we'll have to.

On 4/28/19 4:11 PM, Laine Stump wrote:
On 4/26/19 5:07 AM, Michal Privoznik wrote:
On 4/17/19 7:19 PM, Daniel P. Berrangé wrote:
Ports allocated on virtual networks with type=nat|route|open all get given an actual type of 'network'.
Only ports in networks with type=bridge use an actual type of 'bridge'.
This distinction makes little sense since the virtualization drivers will treat both actual types in exactly the same way, as they're all just bridge devices a VM needs to be connected to.
This doesn't affect user visible XML since the "actual" device XML is internal only, but we need code to convert the data upgrades.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 28 +++++++++++++++++++++++----- src/network/bridge_driver.c | 11 +++-------- src/qemu/qemu_driver.c | 2 +- 3 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0df3c2ed49..1dde45a6fd 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5081,6 +5081,19 @@ virDomainDiskDefPostParse(virDomainDiskDefPtr disk, return -1; } + /* Older libvirtd uses actualType==network, but we now + * just use actualType==bridge, as nothing needs to + * distinguish the two cases, and this simplifies virt + * drive code */ + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK && + net->data.network.actual != NULL && + net->data.network.actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + char mac[VIR_MAC_STRING_BUFLEN]; + virMacAddrFormat(&net->mac, mac); + VIR_DEBUG("Updating NIC %s actual type to bridge", mac); + net->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + } + return 0; } @@ -11267,11 +11280,16 @@ virDomainActualNetDefParseXML(xmlNodePtr node, } bandwidth_node = virXPathNode("./bandwidth", ctxt); - if (bandwidth_node && - virNetDevBandwidthParse(&actual->bandwidth, - bandwidth_node, - def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0) - goto error; + if (bandwidth_node) { + bool allowFloor = + (actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) || + (actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE && + actual->data.bridge.brname != NULL); + if (virNetDevBandwidthParse(&actual->bandwidth, + bandwidth_node, + allowFloor) < 0) + goto error; + } vlanNode = virXPathNode("./vlan", ctxt); if (vlanNode && virNetDevVlanParse(vlanNode, ctxt, &actual->vlan) < 0) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 6ed0bf1e8e..4055939ade 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4491,11 +4491,7 @@ networkAllocateActualDevice(virNetworkPtr net, case VIR_NETWORK_FORWARD_NAT: case VIR_NETWORK_FORWARD_ROUTE: case VIR_NETWORK_FORWARD_OPEN: - /* for these forward types, the actual net type really *is* - * NETWORK; we just keep the info from the portgroup in - * iface->data.network.actual - */ - iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_NETWORK; + iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
Actually, this breaks a lot of stuff. Firstly, for a live XML the type is changed to <interface type='bridge'> .. </interface>. This is because virDomainNetDefFormat() decides to format actual info. Having said that, transitionally some APIs are broken too. For instance, I have the following interface for my domain (inactive XML):
<interface type='network' trustGuestRxFilters='yes'> <mac address='52:54:00:a4:6f:91'/> <source network='default'/> <bandwidth> <inbound average='1024' peak='4096' floor='500' burst='1024'/> <outbound average='10240'/> </bandwidth> <model type='virtio'/> <mtu size='9000'/> </interface>
Now, 'virsh domif-setlink', 'virsh update-device', 'virsh domiftune' don't work, because at runtime type is changed to 'bridge' and we don't implement many features for that network type.
Ugh.
The problem here is that we've always (or at least *I've* always) worked with the mistaken idea that there are 2 types of info about a domain - status and persistent config) - when really there are 3 - status, transient config, and persistent config. The problem being suffered is because the interface type returned in the live XML has in the past been partly status, partly transient config. Dan's patch has made it 100% status, but the virsh commands above treat it as transient config.
I knew I was responsible for the change that made the status XML give the actual type of the interface (so "status") rather than the type from the persistent config, but couldn't remember why I did it, so I looked back in git history and found commit 7d5bf4847. A simpler way to look at it might be via the email from the list (that way you can see the review responses):
https://www.redhat.com/archives/libvir-list/2014-February/msg01377.html
In short, it was done so that the network hook "plugged" event (and other users of the public API) could get all the information about the way the network device had been setup.
It seemed like a good idea at the time, but in retrospect, I think it was a mistake (in this case and in general) to provide status in the same attribute used for config when those two things might be different (when I say it out loud that way, it's pretty obious :-/). Instead, any "status" item that could *ever* possibly differ from a similar "transient config" item should have its own distinct read-only attribute/element in the XML.
Once the rest of Dan's "virNetworkPort" series is in, a user will be able to get all the info they need by first getting the domain XML, then using the uuid of the interface to get the NetworkPort XML for that connection (that may still not be perfect, because interfaces that aren't type='network' won't have a networkport, but it will be enough for the use case we're considering here). It will be more complicated that just reading the domain XML a single time, but at least it will allow us to learn status info distinct from transient config.
I'll post patches for the first two issues I've found. But they look pretty hacky and I'm not sure my testing was exhaustive enough to find other cases where this will break. The third one I have no idea how to fix because it boils down to looking a network in the bridge driver but we don't know the network name because we don't parse it for interface type bridge.
Doesn't it all come down to the fact that the status XML now has "bridge" instead of "network"? Maybe if we make a patch to change *only* what is put in the externally visible status XML, but internally (and in the <actual> element of the status that's stored on disk) maintain the change from "network" to "bridge"? I can try making a patch like that.
Double Ugh. Not so easy as I'd assumed :-( The problem is that we can't just fix it by always putting the type from the config in the XML - if the config type is "network" and the network has a forward mode that uses IP routing to forward packets then the type stays at "network", but if the network has any other forward mode, then the status XML shows the actual type (bridge, direct, hostdev). Essentially, the forward mode of the network is encoded into the "actual type" of the interface, and it's not available from anywhere else at the time we format the status XML, so we can't "internally change the type, but format 'what we would have set the type to in the past' when we're creating the XML document to output. I *would like to say* that putting the actual type in the status XML at all is an error, and should be fixed. But it's been that way for nearly 5 years now, and changing it might break something else. Also, the actual type (i.e. status of the interface) isn't available from anywhere else. So my brain is kind of stuck on what is the right thing to do.
Should we revert this?
If we can't do something to get these virsh commands working before the release, then I think we'll have to.
In order to preserve compatibility with old virsh, I think we need to keep the interface type as it previously was in the status XML. On the other hand, the way it was is just... bad. Not only does it make it impossible to do a "read transient config. modify, update transient config" of an interface, it is encoding multiple things into a single attribute. (*This* is a great example of why I always worry excessively over any XML addition/change. For all the good it apparently does...)

On Fri, Apr 26, 2019 at 11:07:49AM +0200, Michal Privoznik wrote:
On 4/17/19 7:19 PM, Daniel P. Berrangé wrote:
Ports allocated on virtual networks with type=nat|route|open all get given an actual type of 'network'.
Only ports in networks with type=bridge use an actual type of 'bridge'.
This distinction makes little sense since the virtualization drivers will treat both actual types in exactly the same way, as they're all just bridge devices a VM needs to be connected to.
This doesn't affect user visible XML since the "actual" device XML is internal only, but we need code to convert the data upgrades.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 28 +++++++++++++++++++++++----- src/network/bridge_driver.c | 11 +++-------- src/qemu/qemu_driver.c | 2 +- 3 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0df3c2ed49..1dde45a6fd 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5081,6 +5081,19 @@ virDomainDiskDefPostParse(virDomainDiskDefPtr disk, return -1; } + /* Older libvirtd uses actualType==network, but we now + * just use actualType==bridge, as nothing needs to + * distinguish the two cases, and this simplifies virt + * drive code */ + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK && + net->data.network.actual != NULL && + net->data.network.actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + char mac[VIR_MAC_STRING_BUFLEN]; + virMacAddrFormat(&net->mac, mac); + VIR_DEBUG("Updating NIC %s actual type to bridge", mac); + net->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + } + return 0; } @@ -11267,11 +11280,16 @@ virDomainActualNetDefParseXML(xmlNodePtr node, } bandwidth_node = virXPathNode("./bandwidth", ctxt); - if (bandwidth_node && - virNetDevBandwidthParse(&actual->bandwidth, - bandwidth_node, - def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0) - goto error; + if (bandwidth_node) { + bool allowFloor = + (actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) || + (actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE && + actual->data.bridge.brname != NULL); + if (virNetDevBandwidthParse(&actual->bandwidth, + bandwidth_node, + allowFloor) < 0) + goto error; + } vlanNode = virXPathNode("./vlan", ctxt); if (vlanNode && virNetDevVlanParse(vlanNode, ctxt, &actual->vlan) < 0) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 6ed0bf1e8e..4055939ade 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4491,11 +4491,7 @@ networkAllocateActualDevice(virNetworkPtr net, case VIR_NETWORK_FORWARD_NAT: case VIR_NETWORK_FORWARD_ROUTE: case VIR_NETWORK_FORWARD_OPEN: - /* for these forward types, the actual net type really *is* - * NETWORK; we just keep the info from the portgroup in - * iface->data.network.actual - */ - iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_NETWORK; + iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
Actually, this breaks a lot of stuff. Firstly, for a live XML the type is changed to <interface type='bridge'> .. </interface>. This is because virDomainNetDefFormat() decides to format actual info.
Urgh this is a real pain point & really a regression. I believed the comments that the Actual def was internal only and not visible to users. I think this alone makes a revert necceessary and probably meansI have to abandon this idea completely :-(
Having said that, transitionally some APIs are broken too. For instance, I have the following interface for my domain (inactive XML):
<interface type='network' trustGuestRxFilters='yes'> <mac address='52:54:00:a4:6f:91'/> <source network='default'/> <bandwidth> <inbound average='1024' peak='4096' floor='500' burst='1024'/> <outbound average='10240'/> </bandwidth> <model type='virtio'/> <mtu size='9000'/> </interface>
Now, 'virsh domif-setlink', 'virsh update-device', 'virsh domiftune' don't work, because at runtime type is changed to 'bridge' and we don't implement many features for that network type.
I'll post patches for the first two issues I've found. But they look pretty hacky and I'm not sure my testing was exhaustive enough to find other cases where this will break. The third one I have no idea how to fix because it boils down to looking a network in the bridge driver but we don't know the network name because we don't parse it for interface type bridge.
Should we revert this?
I think reverting is going to be the easiest, certainly for this imminent release. Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

The call to resolve the actual network type will turn any NICs with type=network into one of the other types. Thus there should be no need to handle type=network in later switch() statements jumping off the actual type. Reviewed-by: Cole Robinson <crobinso@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/libxl/libxl_conf.c | 21 +++------------------ src/lxc/lxc_driver.c | 15 +++++++++++---- src/qemu/qemu_command.c | 8 ++++++-- src/qemu/qemu_hotplug.c | 13 +++++++++++-- src/qemu/qemu_interface.c | 12 ++++++++++-- src/qemu/qemu_process.c | 5 ++++- 6 files changed, 45 insertions(+), 29 deletions(-) diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index da1fc4f9f2..c701df3370 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -1352,25 +1352,10 @@ libxlMakeNic(virDomainDefPtr def, } break; case VIR_DOMAIN_NET_TYPE_NETWORK: - { - if (!(conn = virConnectOpen("xen:///system"))) - goto cleanup; - - if (!(network = - virNetworkLookupByName(conn, l_nic->data.network.name))) { - goto cleanup; - } - - if (l_nic->guestIP.nips > 0) { - x_nic->ip = xenMakeIPList(&l_nic->guestIP); - if (!x_nic->ip) - goto cleanup; - } + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpectedly found type=network for actual NIC type")); + goto cleanup; - if (!(x_nic->bridge = virNetworkGetBridgeName(network))) - goto cleanup; - break; - } case VIR_DOMAIN_NET_TYPE_VHOSTUSER: case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_SERVER: diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index a184e67e15..37002dbf23 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -3848,8 +3848,7 @@ lxcDomainAttachDeviceNetLive(virConnectPtr conn, actualType = virDomainNetGetActualType(net); switch (actualType) { - case VIR_DOMAIN_NET_TYPE_BRIDGE: - case VIR_DOMAIN_NET_TYPE_NETWORK: { + case VIR_DOMAIN_NET_TYPE_BRIDGE: { const char *brname = virDomainNetGetActualBridgeName(net); if (!brname) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", @@ -3867,6 +3866,10 @@ lxcDomainAttachDeviceNetLive(virConnectPtr conn, if (!(veth = virLXCProcessSetupInterfaceDirect(conn, vm->def, net))) goto cleanup; } break; + case VIR_DOMAIN_NET_TYPE_NETWORK: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpectedly found type=network for actual NIC type")); + goto cleanup; case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_VHOSTUSER: case VIR_DOMAIN_NET_TYPE_SERVER: @@ -3912,7 +3915,6 @@ lxcDomainAttachDeviceNetLive(virConnectPtr conn, } else if (veth) { switch (actualType) { case VIR_DOMAIN_NET_TYPE_BRIDGE: - case VIR_DOMAIN_NET_TYPE_NETWORK: case VIR_DOMAIN_NET_TYPE_ETHERNET: ignore_value(virNetDevVethDelete(veth)); break; @@ -3921,6 +3923,7 @@ lxcDomainAttachDeviceNetLive(virConnectPtr conn, ignore_value(virNetDevMacVLanDelete(veth)); break; + case VIR_DOMAIN_NET_TYPE_NETWORK: case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_VHOSTUSER: case VIR_DOMAIN_NET_TYPE_SERVER: @@ -4352,7 +4355,6 @@ lxcDomainDetachDeviceNetLive(virDomainObjPtr vm, switch (actualType) { case VIR_DOMAIN_NET_TYPE_BRIDGE: - case VIR_DOMAIN_NET_TYPE_NETWORK: case VIR_DOMAIN_NET_TYPE_ETHERNET: if (virNetDevVethDelete(detach->ifname) < 0) { virDomainAuditNet(vm, detach, NULL, "detach", false); @@ -4360,6 +4362,11 @@ lxcDomainDetachDeviceNetLive(virDomainObjPtr vm, } break; + case VIR_DOMAIN_NET_TYPE_NETWORK: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpectedly found type=network for actual NIC type")); + goto cleanup; + /* It'd be nice to support this, but with macvlan * once assigned to a container nothing exists on * the host side. Further the container can change diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 9df7b7e8ea..1425d97b1e 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8798,7 +8798,6 @@ qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver, } switch (actualType) { - case VIR_DOMAIN_NET_TYPE_NETWORK: case VIR_DOMAIN_NET_TYPE_BRIDGE: tapfdSize = net->driver.virtio.queues; if (!tapfdSize) @@ -8876,6 +8875,11 @@ qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver, break; + case VIR_DOMAIN_NET_TYPE_NETWORK: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpectedly found type=network for actual NIC type")); + goto cleanup; + case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_SERVER: case VIR_DOMAIN_NET_TYPE_CLIENT: @@ -8892,7 +8896,6 @@ qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver, */ switch ((virDomainNetType)actualType) { case VIR_DOMAIN_NET_TYPE_ETHERNET: - case VIR_DOMAIN_NET_TYPE_NETWORK: case VIR_DOMAIN_NET_TYPE_BRIDGE: case VIR_DOMAIN_NET_TYPE_DIRECT: { @@ -8910,6 +8913,7 @@ qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver, break; } + case VIR_DOMAIN_NET_TYPE_NETWORK: case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_VHOSTUSER: case VIR_DOMAIN_NET_TYPE_SERVER: diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index a4f7d111b1..16da2b95fd 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1444,7 +1444,6 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver, switch (actualType) { case VIR_DOMAIN_NET_TYPE_BRIDGE: - case VIR_DOMAIN_NET_TYPE_NETWORK: tapfdSize = vhostfdSize = net->driver.virtio.queues; if (!tapfdSize) tapfdSize = vhostfdSize = 1; @@ -1536,6 +1535,11 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver, /* No preparation needed. */ break; + case VIR_DOMAIN_NET_TYPE_NETWORK: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpectedly found type=network for actual NIC type")); + goto cleanup; + case VIR_DOMAIN_NET_TYPE_SERVER: case VIR_DOMAIN_NET_TYPE_CLIENT: case VIR_DOMAIN_NET_TYPE_MCAST: @@ -3656,8 +3660,13 @@ qemuDomainChangeNetFilter(virDomainObjPtr vm, switch (virDomainNetGetActualType(newdev)) { case VIR_DOMAIN_NET_TYPE_ETHERNET: case VIR_DOMAIN_NET_TYPE_BRIDGE: - case VIR_DOMAIN_NET_TYPE_NETWORK: break; + + case VIR_DOMAIN_NET_TYPE_NETWORK: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpectedly found type=network for actual NIC type")); + return -1; + case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_VHOSTUSER: case VIR_DOMAIN_NET_TYPE_SERVER: diff --git a/src/qemu/qemu_interface.c b/src/qemu/qemu_interface.c index c8effa68f4..9bb0d70986 100644 --- a/src/qemu/qemu_interface.c +++ b/src/qemu/qemu_interface.c @@ -58,7 +58,6 @@ qemuInterfaceStartDevice(virDomainNetDefPtr net) switch (actualType) { case VIR_DOMAIN_NET_TYPE_BRIDGE: - case VIR_DOMAIN_NET_TYPE_NETWORK: if (virDomainNetGetActualBridgeMACTableManager(net) == VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LIBVIRT) { /* libvirt is managing the FDB of the bridge this device @@ -111,6 +110,11 @@ qemuInterfaceStartDevice(virDomainNetDefPtr net) break; + case VIR_DOMAIN_NET_TYPE_NETWORK: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpectedly found type=network for actual NIC type")); + goto cleanup; + case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_VHOSTUSER: case VIR_DOMAIN_NET_TYPE_SERVER: @@ -164,7 +168,6 @@ qemuInterfaceStopDevice(virDomainNetDefPtr net) switch (actualType) { case VIR_DOMAIN_NET_TYPE_BRIDGE: - case VIR_DOMAIN_NET_TYPE_NETWORK: if (virDomainNetGetActualBridgeMACTableManager(net) == VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LIBVIRT) { /* remove the FDB entries that were added during @@ -198,6 +201,11 @@ qemuInterfaceStopDevice(virDomainNetDefPtr net) break; } + case VIR_DOMAIN_NET_TYPE_NETWORK: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpectedly found type=network for actual NIC type")); + goto cleanup; + case VIR_DOMAIN_NET_TYPE_ETHERNET: case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_VHOSTUSER: diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 71d4b39302..a7a475e75c 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -7302,12 +7302,15 @@ void qemuProcessStop(virQEMUDriverPtr driver, } break; case VIR_DOMAIN_NET_TYPE_BRIDGE: - case VIR_DOMAIN_NET_TYPE_NETWORK: #ifdef VIR_NETDEV_TAP_REQUIRE_MANUAL_CLEANUP if (!(vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH)) ignore_value(virNetDevTapDelete(net->ifname, net->backend.tap)); #endif break; + case VIR_DOMAIN_NET_TYPE_NETWORK: + VIR_WARN("Unexpectedly found type=network for actual NIC type"); + break; + case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_VHOSTUSER: case VIR_DOMAIN_NET_TYPE_SERVER: -- 2.20.1

During initial NIC setup the hypervisor drivers are responsible for attaching the TAP device to the bridge device. Any fixup after libvirtd restarts should thus also be their responsibility. Reviewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 20 +++++++++++++++++++- src/conf/domain_conf.h | 2 +- src/network/bridge_driver.c | 27 ++++++--------------------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 1dde45a6fd..b94b70aabc 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -54,6 +54,7 @@ #include "virsecret.h" #include "virstring.h" #include "virnetdev.h" +#include "virnetdevtap.h" #include "virnetdevmacvlan.h" #include "virhostdev.h" #include "virmdev.h" @@ -30444,8 +30445,25 @@ virDomainNetNotifyActualDevice(virConnectPtr conn, if (!(net = virNetworkLookupByName(conn, iface->data.network.name))) return; - netNotify(net, dom, iface); + if (netNotify(net, dom, iface) < 0) + goto cleanup; + + if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_BRIDGE) { + /* + * NB: we can't notify the guest of any MTU change anyway, + * so there is no point in trying to learn the actualMTU + * (final arg to virNetDevTapReattachBridge()) + */ + if (virNetDevTapReattachBridge(iface->ifname, + iface->data.network.actual->data.bridge.brname, + &iface->mac, dom->uuid, + virDomainNetGetActualVirtPortProfile(iface), + virDomainNetGetActualVlan(iface), + iface->mtu, NULL) < 0) + goto cleanup; + } + cleanup: virObjectUnref(net); } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 5910ed0beb..fb053a7ca3 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3552,7 +3552,7 @@ typedef int virDomainDefPtr dom, virDomainNetDefPtr iface); -typedef void +typedef int (*virDomainNetNotifyActualDeviceImpl)(virNetworkPtr net, virDomainDefPtr dom, virDomainNetDefPtr iface); diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 4055939ade..8b36376761 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4793,12 +4793,11 @@ networkAllocateActualDevice(virNetworkPtr net, * Called to notify the network driver when libvirtd is restarted and * finds an already running domain. If appropriate it will force an * allocation of the actual->direct.linkdev to get everything back in - * order, or re-attach the interface's tap device to the network's - * bridge. + * order. * - * No return value (but does log any failures) + * Returns 0 on success, -1 on failure. */ -static void +static int networkNotifyActualDevice(virNetworkPtr net, virDomainDefPtr dom, virDomainNetDefPtr iface) @@ -4809,6 +4808,7 @@ networkNotifyActualDevice(virNetworkPtr net, virNetworkDefPtr netdef; virNetworkForwardIfDefPtr dev = NULL; size_t i; + int ret = -1; obj = virNetworkObjFindByName(driver->networks, net->name); if (!obj) { @@ -4833,22 +4833,6 @@ networkNotifyActualDevice(virNetworkPtr net, goto error; } - /* see if we're connected to the correct bridge */ - if (netdef->bridge) { - /* - * NB: we can't notify the guest of any MTU change anyway, - * so there is no point in trying to learn the actualMTU - * (final arg to virNetDevTapReattachBridge()) - */ - if (virNetDevTapReattachBridge(iface->ifname, netdef->bridge, - &iface->mac, dom->uuid, - virDomainNetGetActualVirtPortProfile(iface), - virDomainNetGetActualVlan(iface), - iface->mtu, NULL) < 0) { - goto error; - } - } - if (!iface->data.network.actual || (actualType != VIR_DOMAIN_NET_TYPE_DIRECT && actualType != VIR_DOMAIN_NET_TYPE_HOSTDEV)) { @@ -4977,10 +4961,11 @@ networkNotifyActualDevice(virNetworkPtr net, goto error; } networkLogAllocation(netdef, actualType, dev, iface, true); + ret = 0; cleanup: virNetworkObjEndAPI(&obj); - return; + return ret; error: goto cleanup; -- 2.20.1

The hypervisor drivers are soon going to communicate with the network driver via public APIs only. As such the network driver will not ever see the domain actual network def. Thus the backwards compatibility fixup logic must be moved out of the network driver. Reviewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index b94b70aabc..c4c2eb0281 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -30437,6 +30437,7 @@ virDomainNetNotifyActualDevice(virConnectPtr conn, virDomainDefPtr dom, virDomainNetDefPtr iface) { + virDomainNetType actualType = virDomainNetGetActualType(iface); virNetworkPtr net = NULL; if (!netNotify) @@ -30445,6 +30446,16 @@ virDomainNetNotifyActualDevice(virConnectPtr conn, if (!(net = virNetworkLookupByName(conn, iface->data.network.name))) return; + /* Older libvirtd uses actualType==network, but we now + * just use actualType==bridge, as nothing needs to + * distinguish the two cases, and this simplifies virt + * drive code */ + if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK) { + iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + actualType = VIR_DOMAIN_NET_TYPE_BRIDGE; + } + + if (netNotify(net, dom, iface) < 0) goto cleanup; -- 2.20.1

Introduce a virNetworkPortDefPtr struct to represent the data associated with a virtual network port. Add APIs for parsing/formatting XML docs with the data. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/Makefile.inc.am | 2 + src/conf/virnetworkportdef.c | 515 ++++++++++++++++++ src/conf/virnetworkportdef.h | 112 ++++ src/libvirt_private.syms | 10 + tests/Makefile.am | 7 + .../plug-bridge-mactbl.xml | 9 + .../virnetworkportxml2xmldata/plug-bridge.xml | 12 + .../virnetworkportxml2xmldata/plug-direct.xml | 12 + .../plug-hostdev-pci.xml | 12 + tests/virnetworkportxml2xmldata/plug-none.xml | 8 + tests/virnetworkportxml2xmltest.c | 104 ++++ 11 files changed, 803 insertions(+) create mode 100644 src/conf/virnetworkportdef.c create mode 100644 src/conf/virnetworkportdef.h create mode 100644 tests/virnetworkportxml2xmldata/plug-bridge-mactbl.xml create mode 100644 tests/virnetworkportxml2xmldata/plug-bridge.xml create mode 100644 tests/virnetworkportxml2xmldata/plug-direct.xml create mode 100644 tests/virnetworkportxml2xmldata/plug-hostdev-pci.xml create mode 100644 tests/virnetworkportxml2xmldata/plug-none.xml create mode 100644 tests/virnetworkportxml2xmltest.c diff --git a/src/conf/Makefile.inc.am b/src/conf/Makefile.inc.am index 3e9fdd1aea..6b52ba674b 100644 --- a/src/conf/Makefile.inc.am +++ b/src/conf/Makefile.inc.am @@ -7,6 +7,8 @@ NETDEV_CONF_SOURCES = \ conf/netdev_vport_profile_conf.c \ conf/netdev_vlan_conf.h \ conf/netdev_vlan_conf.c \ + conf/virnetworkportdef.h \ + conf/virnetworkportdef.c \ $(NULL) DOMAIN_CONF_SOURCES = \ diff --git a/src/conf/virnetworkportdef.c b/src/conf/virnetworkportdef.c new file mode 100644 index 0000000000..0c2bb988f3 --- /dev/null +++ b/src/conf/virnetworkportdef.c @@ -0,0 +1,515 @@ +/* + * virnetworkportdef.c: network port XML processing + * + * Copyright (C) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "viralloc.h" +#include "virerror.h" +#include "virstring.h" +#include "virfile.h" +#include "virnetworkportdef.h" +#include "network_conf.h" + +#define VIR_FROM_THIS VIR_FROM_NETWORK + +VIR_ENUM_IMPL(virNetworkPortPlug, + VIR_NETWORK_PORT_PLUG_TYPE_LAST, + "none", "bridge", "direct", "hostdev-pci"); + +void +virNetworkPortDefFree(virNetworkPortDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->ownername); + VIR_FREE(def->group); + + virNetDevBandwidthFree(def->bandwidth); + virNetDevVlanClear(&def->vlan); + VIR_FREE(def->virtPortProfile); + + switch ((virNetworkPortPlugType)def->plugtype) { + case VIR_NETWORK_PORT_PLUG_TYPE_NONE: + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE: + VIR_FREE(def->plug.bridge.brname); + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT: + VIR_FREE(def->plug.direct.linkdev); + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI: + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_LAST: + default: + break; + } + + VIR_FREE(def); +} + + + +static virNetworkPortDefPtr +virNetworkPortDefParseXML(xmlXPathContextPtr ctxt) +{ + virNetworkPortDefPtr def; + char *uuid = NULL; + xmlNodePtr virtPortNode; + xmlNodePtr vlanNode; + xmlNodePtr bandwidthNode; + xmlNodePtr addressNode; + char *trustGuestRxFilters = NULL; + char *mac = NULL; + char *macmgr = NULL; + char *mode = NULL; + char *plugtype = NULL; + char *managed = NULL; + char *driver = NULL; + char *class_id = NULL; + + if (VIR_ALLOC(def) < 0) + return NULL; + + uuid = virXPathString("string(./uuid)", ctxt); + if (!uuid) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("network port has no uuid")); + goto error; + } + if (virUUIDParse(uuid, def->uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to parse UUID '%s'"), uuid); + goto error; + } + + def->ownername = virXPathString("string(./owner/name)", ctxt); + if (!def->ownername) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("network port has no owner name")); + goto error; + } + + VIR_FREE(uuid); + uuid = virXPathString("string(./owner/uuid)", ctxt); + if (!uuid) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("network port has no owner UUID")); + goto error; + } + + if (virUUIDParse(uuid, def->owneruuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to parse UUID '%s'"), uuid); + goto error; + } + + def->group = virXPathString("string(./group)", ctxt); + + virtPortNode = virXPathNode("./virtualport", ctxt); + if (virtPortNode && + (!(def->virtPortProfile = virNetDevVPortProfileParse(virtPortNode, 0)))) { + goto error; + } + + mac = virXPathString("string(./mac/@address)", ctxt); + if (!mac) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("network port has no mac")); + goto error; + } + if (virMacAddrParse(mac, &def->mac) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to parse MAC '%s'"), mac); + goto error; + } + + class_id = virXPathString("string(./class/@id)", ctxt); + if (class_id && + virStrToLong_ui(class_id, NULL, 10, &def->class_id) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to parse class id '%s'"), + class_id); + goto error; + } + + bandwidthNode = virXPathNode("./bandwidth", ctxt); + /* + * We don't know if the port will allow the "floor" param or + * not at this stage, so we must just tell virNetDevBandwidthParse + * to allow it regardless. Any bad config must be reported at + * time of use instead. + */ + if (bandwidthNode && + virNetDevBandwidthParse(&def->bandwidth, bandwidthNode, true) < 0) + goto error; + + vlanNode = virXPathNode("./vlan", ctxt); + if (vlanNode && virNetDevVlanParse(vlanNode, ctxt, &def->vlan) < 0) + goto error; + + + trustGuestRxFilters + = virXPathString("string(./rxfilters/@trustGuest)", ctxt); + if (trustGuestRxFilters) { + if ((def->trustGuestRxFilters + = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid guest rx filters trust setting '%s' "), + trustGuestRxFilters); + goto error; + } + } + + plugtype = virXPathString("string(./plug/@type)", ctxt); + + if (plugtype && + (def->plugtype = virNetworkPortPlugTypeFromString(plugtype)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid network prt plug type '%s'"), plugtype); + } + + switch (def->plugtype) { + case VIR_NETWORK_PORT_PLUG_TYPE_NONE: + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE: + if (!(def->plug.bridge.brname = virXPathString("string(./plug/@bridge)", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing network port bridge name")); + goto error; + } + macmgr = virXPathString("string(./plug/@macTableManager)", ctxt); + if (macmgr && + (def->plug.bridge.macTableManager = + virNetworkBridgeMACTableManagerTypeFromString(macmgr)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid macTableManager setting '%s' " + "in network port"), macmgr); + goto error; + } + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT: + if (!(def->plug.direct.linkdev = virXPathString("string(./plug/@dev)", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing network port link device name")); + goto error; + } + mode = virXPathString("string(./plug/@mode)", ctxt); + if (mode && + (def->plug.direct.mode = + virNetDevMacVLanModeTypeFromString(mode)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid mode setting '%s' in network port"), mode); + goto error; + } + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI: + managed = virXPathString("string(./plug/@managed)", ctxt); + if (managed && + (def->plug.hostdevpci.managed = + virTristateBoolTypeFromString(managed)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid managed setting '%s' in network port"), mode); + goto error; + } + driver = virXPathString("string(./plug/driver/@name)", ctxt); + if (driver && + (def->plug.hostdevpci.driver = + virNetworkForwardDriverNameTypeFromString(driver)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing network port driver name")); + goto error; + } + if (!(addressNode = virXPathNode("./plug/address", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing network port PCI address")); + goto error; + } + + if (virPCIDeviceAddressParseXML(addressNode, &def->plug.hostdevpci.addr) < 0) + goto error; + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_LAST: + default: + virReportEnumRangeError(virNetworkPortPlugType, def->plugtype); + goto error; + } + + cleanup: + VIR_FREE(class_id); + VIR_FREE(uuid); + VIR_FREE(plugtype); + VIR_FREE(mac); + VIR_FREE(mode); + VIR_FREE(macmgr); + VIR_FREE(driver); + VIR_FREE(managed); + return def; + + error: + virNetworkPortDefFree(def); + def = NULL; + goto cleanup; +} + + +virNetworkPortDefPtr +virNetworkPortDefParseNode(xmlDocPtr xml, + xmlNodePtr root) +{ + xmlXPathContextPtr ctxt = NULL; + virNetworkPortDefPtr def = NULL; + + if (STRNEQ((const char *)root->name, "networkport")) { + virReportError(VIR_ERR_XML_ERROR, + "%s", + _("unknown root element for network port")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node = root; + def = virNetworkPortDefParseXML(ctxt); + + cleanup: + xmlXPathFreeContext(ctxt); + return def; +} + + +static virNetworkPortDefPtr +virNetworkPortDefParse(const char *xmlStr, + const char *filename) +{ + virNetworkPortDefPtr def = NULL; + xmlDocPtr xml; + + if ((xml = virXMLParse(filename, xmlStr, _("(networkport_definition)")))) { + def = virNetworkPortDefParseNode(xml, xmlDocGetRootElement(xml)); + xmlFreeDoc(xml); + } + + return def; +} + + +virNetworkPortDefPtr +virNetworkPortDefParseString(const char *xmlStr) +{ + return virNetworkPortDefParse(xmlStr, NULL); +} + + +virNetworkPortDefPtr +virNetworkPortDefParseFile(const char *filename) +{ + return virNetworkPortDefParse(NULL, filename); +} + + +char * +virNetworkPortDefFormat(const virNetworkPortDef *def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (virNetworkPortDefFormatBuf(&buf, def) < 0) { + virBufferFreeAndReset(&buf); + return NULL; + } + + if (virBufferCheckError(&buf) < 0) + return NULL; + + return virBufferContentAndReset(&buf); +} + + +int +virNetworkPortDefFormatBuf(virBufferPtr buf, + const virNetworkPortDef *def) +{ + char uuid[VIR_UUID_STRING_BUFLEN]; + char macaddr[VIR_MAC_STRING_BUFLEN]; + + virBufferAddLit(buf, "<networkport>\n"); + + virBufferAdjustIndent(buf, 2); + + virUUIDFormat(def->uuid, uuid); + virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuid); + + virBufferAddLit(buf, "<owner>\n"); + virBufferAdjustIndent(buf, 2); + virBufferEscapeString(buf, "<name>%s</name>\n", def->ownername); + virUUIDFormat(def->owneruuid, uuid); + virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuid); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</owner>\n"); + + if (def->group) + virBufferEscapeString(buf, "<group>%s</group>\n", def->group); + + virMacAddrFormat(&def->mac, macaddr); + virBufferAsprintf(buf, "<mac address='%s'/>\n", macaddr); + + if (virNetDevVPortProfileFormat(def->virtPortProfile, buf) < 0) + return -1; + virNetDevBandwidthFormat(def->bandwidth, buf); + if (def->class_id) + virBufferAsprintf(buf, "<class id='%u'/>\n", def->class_id); + if (virNetDevVlanFormat(&def->vlan, buf) < 0) + return -1; + if (def->trustGuestRxFilters) + virBufferAsprintf(buf, "<rxfilters trustGuest='%s'/>\n", + virTristateBoolTypeToString(def->trustGuestRxFilters)); + + if (def->plugtype != VIR_NETWORK_PORT_PLUG_TYPE_NONE) { + virBufferAsprintf(buf, "<plug type='%s'", + virNetworkPortPlugTypeToString(def->plugtype)); + + switch (def->plugtype) { + case VIR_NETWORK_PORT_PLUG_TYPE_NONE: + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE: + virBufferEscapeString(buf, " bridge='%s'", def->plug.bridge.brname); + if (def->plug.bridge.macTableManager) + virBufferAsprintf(buf, " macTableManager='%s'", + virNetworkBridgeMACTableManagerTypeToString( + def->plug.bridge.macTableManager)); + virBufferAddLit(buf, "/>\n"); + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT: + virBufferEscapeString(buf, " dev='%s'", def->plug.direct.linkdev); + virBufferAsprintf(buf, " mode='%s'", + virNetDevMacVLanModeTypeToString( + def->plug.direct.mode)); + virBufferAddLit(buf, "/>\n"); + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI: + virBufferAsprintf(buf, " managed='%s'>\n", + def->plug.hostdevpci.managed ? "yes" : "no"); + virBufferAdjustIndent(buf, 2); + if (def->plug.hostdevpci.driver) + virBufferEscapeString(buf, "<driver name='%s'/>\n", + virNetworkForwardDriverNameTypeToString( + def->plug.hostdevpci.driver)); + + virPCIDeviceAddressFormat(buf, def->plug.hostdevpci.addr, false); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</plug>\n"); + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_LAST: + default: + virReportEnumRangeError(virNetworkPortPlugType, def->plugtype); + return -1; + } + } + + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</networkport>\n"); + + return 0; +} + + +static char * +virNetworkPortDefConfigFile(const char *dir, + const char *name) +{ + char *ret = NULL; + + ignore_value(virAsprintf(&ret, "%s/%s.xml", dir, name)); + return ret; +} + + +int +virNetworkPortDefSaveStatus(virNetworkPortDef *def, + const char *dir) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + char *path; + char *xml = NULL; + int ret = -1; + + virUUIDFormat(def->uuid, uuidstr); + + if (virFileMakePath(dir) < 0) + goto cleanup; + + if (!(path = virNetworkPortDefConfigFile(dir, uuidstr))) + goto cleanup; + + if (!(xml = virNetworkPortDefFormat(def))) + goto cleanup; + + if (virXMLSaveFile(path, uuidstr, "net-port-create", xml) < 0) + goto cleanup; + + ret = 0; + cleanup: + VIR_FREE(xml); + VIR_FREE(path); + return ret; +} + + +int +virNetworkPortDefDeleteStatus(virNetworkPortDef *def, + const char *dir) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + char *path; + int ret = -1; + + virUUIDFormat(def->uuid, uuidstr); + + if (!(path = virNetworkPortDefConfigFile(dir, uuidstr))) + goto cleanup; + + if (unlink(path) < 0 && errno != ENOENT) { + virReportSystemError(errno, + _("Unable to delete %s"), path); + goto cleanup; + } + + ret = 0; + cleanup: + VIR_FREE(path); + return ret; +} diff --git a/src/conf/virnetworkportdef.h b/src/conf/virnetworkportdef.h new file mode 100644 index 0000000000..3897013a86 --- /dev/null +++ b/src/conf/virnetworkportdef.h @@ -0,0 +1,112 @@ +/* + * virnetworkportdef.h: network port XML processing + * + * Copyright (C) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#ifndef LIBVIRT_VIRNETWORKPORTDEF_H +# define LIBVIRT_VIRNETWORKPORTDEF_H + +# include "internal.h" +# include "viruuid.h" +# include "virnetdevvlan.h" +# include "virnetdevvportprofile.h" +# include "virnetdevbandwidth.h" +# include "virpci.h" +# include "virxml.h" +# include "netdev_vport_profile_conf.h" +# include "netdev_bandwidth_conf.h" +# include "netdev_vlan_conf.h" + +typedef struct _virNetworkPortDef virNetworkPortDef; +typedef virNetworkPortDef *virNetworkPortDefPtr; + +typedef enum { + VIR_NETWORK_PORT_PLUG_TYPE_NONE, + VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE, + VIR_NETWORK_PORT_PLUG_TYPE_DIRECT, + VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI, + + VIR_NETWORK_PORT_PLUG_TYPE_LAST, +} virNetworkPortPlugType; + +VIR_ENUM_DECL(virNetworkPortPlug); + +struct _virNetworkPortDef { + unsigned char uuid[VIR_UUID_BUFLEN]; + char *ownername; + unsigned char owneruuid[VIR_UUID_BUFLEN]; + + char *group; + virMacAddr mac; + + virNetDevVPortProfilePtr virtPortProfile; + virNetDevBandwidthPtr bandwidth; + unsigned int class_id; /* class ID for bandwidth 'floor' */ + virNetDevVlan vlan; + int trustGuestRxFilters; /* enum virTristateBool */ + + int plugtype; /* virNetworkPortPlugType */ + union { + struct { + char *brname; + int macTableManager; /* enum virNetworkBridgeMACTableManagerType */ + } bridge; + struct { + char *linkdev; + int mode; /* enum virMacvtapMode from util/macvtap.h */ + } direct; + struct { + virPCIDeviceAddress addr; /* PCI Address of device */ + int driver; /* virNetworkForwardDriverNameType */ + int managed; + } hostdevpci; + } plug; +}; + + +void +virNetworkPortDefFree(virNetworkPortDefPtr port); + +virNetworkPortDefPtr +virNetworkPortDefParseNode(xmlDocPtr xml, + xmlNodePtr root); + +virNetworkPortDefPtr +virNetworkPortDefParseString(const char *xml); + +virNetworkPortDefPtr +virNetworkPortDefParseFile(const char *filename); + +char * +virNetworkPortDefFormat(const virNetworkPortDef *def); + +int +virNetworkPortDefFormatBuf(virBufferPtr buf, + const virNetworkPortDef *def); + +int +virNetworkPortDefSaveStatus(virNetworkPortDef *def, + const char *dir); + +int +virNetworkPortDefDeleteStatus(virNetworkPortDef *def, + const char *dir); + + +#endif /* LIBVIRT_VIRNETWORKPORTDEF_H */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index aa123978fa..8767f4429e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1074,6 +1074,16 @@ virNetworkObjUpdate; virNetworkObjUpdateAssignDef; +# conf/virnetworkportdef.h +virNetworkPortDefFormat; +virNetworkPortDefFormatBuf; +virNetworkPortDefFree; +virNetworkPortDefParseFile; +virNetworkPortDefParseNode; +virNetworkPortDefParseString; +virNetworkPortDefSaveStatus; + + # conf/virnodedeviceobj.h virNodeDeviceObjEndAPI; virNodeDeviceObjGetDef; diff --git a/tests/Makefile.am b/tests/Makefile.am index 46d94d2236..6865ee946e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -149,6 +149,7 @@ EXTRA_DIST = \ virmockstathelpers.c \ virnetdaemondata \ virnetdevtestdata \ + virnetworkportxml2xmldata \ virnwfilterbindingxml2xmldata \ virpcitestdata \ virscsidata \ @@ -335,6 +336,7 @@ endif WITH_YAJL test_programs += \ networkxml2xmltest \ networkxml2xmlupdatetest \ + virnetworkportxml2xmltest \ $(NULL) if WITH_NETWORK @@ -832,6 +834,11 @@ networkxml2xmlupdatetest_SOURCES = \ testutils.c testutils.h networkxml2xmlupdatetest_LDADD = $(LDADDS) +virnetworkportxml2xmltest_SOURCES = \ + virnetworkportxml2xmltest.c \ + testutils.c testutils.h +virnetworkportxml2xmltest_LDADD = $(LDADDS) + if WITH_NETWORK networkxml2conftest_SOURCES = \ networkxml2conftest.c \ diff --git a/tests/virnetworkportxml2xmldata/plug-bridge-mactbl.xml b/tests/virnetworkportxml2xmldata/plug-bridge-mactbl.xml new file mode 100644 index 0000000000..8036bc2e1c --- /dev/null +++ b/tests/virnetworkportxml2xmldata/plug-bridge-mactbl.xml @@ -0,0 +1,9 @@ +<networkport> + <uuid>5d744f21-ba4a-4d6e-bdb2-30a35ff3207d</uuid> + <owner> + <name>memtest</name> + <uuid>d54df46f-1ab5-4a22-8618-4560ef5fac2c</uuid> + </owner> + <mac address='52:54:00:7b:35:93'/> + <plug type='bridge' bridge='virbr0' macTableManager='libvirt'/> +</networkport> diff --git a/tests/virnetworkportxml2xmldata/plug-bridge.xml b/tests/virnetworkportxml2xmldata/plug-bridge.xml new file mode 100644 index 0000000000..6dd576e8a1 --- /dev/null +++ b/tests/virnetworkportxml2xmldata/plug-bridge.xml @@ -0,0 +1,12 @@ +<networkport> + <uuid>5d744f21-ba4a-4d6e-bdb2-30a35ff3207d</uuid> + <owner> + <name>memtest</name> + <uuid>d54df46f-1ab5-4a22-8618-4560ef5fac2c</uuid> + </owner> + <group>web1</group> + <mac address='52:54:00:7b:35:93'/> + <class id='1729'/> + <rxfilters trustGuest='yes'/> + <plug type='bridge' bridge='virbr0'/> +</networkport> diff --git a/tests/virnetworkportxml2xmldata/plug-direct.xml b/tests/virnetworkportxml2xmldata/plug-direct.xml new file mode 100644 index 0000000000..81554b4579 --- /dev/null +++ b/tests/virnetworkportxml2xmldata/plug-direct.xml @@ -0,0 +1,12 @@ +<networkport> + <uuid>5d744f21-ba4a-4d6e-bdb2-30a35ff3207d</uuid> + <owner> + <name>memtest</name> + <uuid>d54df46f-1ab5-4a22-8618-4560ef5fac2c</uuid> + </owner> + <mac address='52:54:00:7b:35:93'/> + <virtualport type='802.1Qbg'> + <parameters managerid='11' typeid='1193047' typeidversion='2'/> + </virtualport> + <plug type='direct' dev='ens3' mode='vepa'/> +</networkport> diff --git a/tests/virnetworkportxml2xmldata/plug-hostdev-pci.xml b/tests/virnetworkportxml2xmldata/plug-hostdev-pci.xml new file mode 100644 index 0000000000..cc4419f3fd --- /dev/null +++ b/tests/virnetworkportxml2xmldata/plug-hostdev-pci.xml @@ -0,0 +1,12 @@ +<networkport> + <uuid>5d744f21-ba4a-4d6e-bdb2-30a35ff3207d</uuid> + <owner> + <name>memtest</name> + <uuid>d54df46f-1ab5-4a22-8618-4560ef5fac2c</uuid> + </owner> + <mac address='52:54:00:7b:35:93'/> + <plug type='hostdev-pci' managed='yes'> + <driver name='vfio'/> + <address domain='0x0001' bus='0x02' slot='0x03' function='0x4'/> + </plug> +</networkport> diff --git a/tests/virnetworkportxml2xmldata/plug-none.xml b/tests/virnetworkportxml2xmldata/plug-none.xml new file mode 100644 index 0000000000..ed7199ec8c --- /dev/null +++ b/tests/virnetworkportxml2xmldata/plug-none.xml @@ -0,0 +1,8 @@ +<networkport> + <uuid>5d744f21-ba4a-4d6e-bdb2-30a35ff3207d</uuid> + <owner> + <name>memtest</name> + <uuid>d54df46f-1ab5-4a22-8618-4560ef5fac2c</uuid> + </owner> + <mac address='52:54:00:7b:35:93'/> +</networkport> diff --git a/tests/virnetworkportxml2xmltest.c b/tests/virnetworkportxml2xmltest.c new file mode 100644 index 0000000000..bb0ae8a8d5 --- /dev/null +++ b/tests/virnetworkportxml2xmltest.c @@ -0,0 +1,104 @@ +/* + * virnetworkportxml2xmltest.c: network port XML processing test suite + * + * Copyright (C) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <unistd.h> + +#include <sys/types.h> +#include <fcntl.h> + +#include "internal.h" +#include "testutils.h" +#include "virnetworkportdef.h" +#include "virstring.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + + +static int +testCompareXMLToXMLFiles(const char *expected) +{ + char *actual = NULL; + int ret = -1; + virNetworkPortDefPtr dev = NULL; + + if (!(dev = virNetworkPortDefParseFile(expected))) + goto cleanup; + + if (!(actual = virNetworkPortDefFormat(dev))) + goto cleanup; + + if (virTestCompareToFile(actual, expected) < 0) + goto cleanup; + + ret = 0; + cleanup: + VIR_FREE(actual); + virNetworkPortDefFree(dev); + return ret; +} + +struct testInfo { + const char *name; +}; + +static int +testCompareXMLToXMLHelper(const void *data) +{ + const struct testInfo *info = data; + int ret = -1; + char *xml = NULL; + + if (virAsprintf(&xml, "%s/virnetworkportxml2xmldata/%s.xml", + abs_srcdir, info->name) < 0) + goto cleanup; + + ret = testCompareXMLToXMLFiles(xml); + + cleanup: + VIR_FREE(xml); + + return ret; +} + +static int +mymain(void) +{ + int ret = 0; + +#define DO_TEST(name) \ + do { \ + const struct testInfo info = {name}; \ + if (virTestRun("virnetworkportdeftest " name, \ + testCompareXMLToXMLHelper, &info) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST("plug-none"); + DO_TEST("plug-bridge"); + DO_TEST("plug-bridge-mactbl"); + DO_TEST("plug-direct"); + DO_TEST("plug-hostdev-pci"); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIR_TEST_MAIN(mymain) -- 2.20.1

Stop passing a virDomainNetDefPtr parameter to networkLogAllocation, instead just pass in the MAC address. The actual device type is also not required, since virNetworkForwardIfDefPtr has a type field that can be used instead. Reviewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 8b36376761..68c876afad 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4335,32 +4335,29 @@ networkGetDHCPLeases(virNetworkPtr net, static void networkLogAllocation(virNetworkDefPtr netdef, - virDomainNetType actualType, virNetworkForwardIfDefPtr dev, - virDomainNetDefPtr iface, + virMacAddrPtr mac, bool inUse) { char macStr[VIR_MAC_STRING_BUFLEN]; const char *verb = inUse ? "using" : "releasing"; + virMacAddrFormat(mac, macStr); if (!dev) { VIR_INFO("MAC %s %s network %s (%d connections)", - virMacAddrFormat(&iface->mac, macStr), verb, - netdef->name, netdef->connections); + macStr, verb, netdef->name, netdef->connections); } else { - if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + if (dev->type == VIR_NETWORK_FORWARD_HOSTDEV_DEVICE_PCI) { VIR_INFO("MAC %s %s network %s (%d connections) " "physical device %04x:%02x:%02x.%x (%d connections)", - virMacAddrFormat(&iface->mac, macStr), verb, - netdef->name, netdef->connections, + macStr, verb, netdef->name, netdef->connections, dev->device.pci.domain, dev->device.pci.bus, dev->device.pci.slot, dev->device.pci.function, dev->connections); } else { VIR_INFO("MAC %s %s network %s (%d connections) " "physical device %s (%d connections)", - virMacAddrFormat(&iface->mac, macStr), verb, - netdef->name, netdef->connections, + macStr, verb, netdef->name, netdef->connections, dev->device.dev, dev->connections); } } @@ -4769,7 +4766,7 @@ networkAllocateActualDevice(virNetworkPtr net, dev->connections--; goto error; } - networkLogAllocation(netdef, actualType, dev, iface, true); + networkLogAllocation(netdef, dev, &iface->mac, true); ret = 0; @@ -4960,7 +4957,7 @@ networkNotifyActualDevice(virNetworkPtr net, netdef->connections--; goto error; } - networkLogAllocation(netdef, actualType, dev, iface, true); + networkLogAllocation(netdef, dev, &iface->mac, true); ret = 0; cleanup: @@ -5127,7 +5124,7 @@ networkReleaseActualDevice(virNetworkPtr net, /* finally we can call the 'unplugged' hook script if any */ networkRunHook(obj, dom, iface, VIR_HOOK_NETWORK_OP_IFACE_UNPLUGGED, VIR_HOOK_SUBOP_BEGIN); - networkLogAllocation(netdef, actualType, dev, iface, false); + networkLogAllocation(netdef, dev, &iface->mac, false); } ret = 0; cleanup: -- 2.20.1

Helper APIs are needed to - Populate basic virNetworkPortDef from virDomainNetDef - Set a virDomainActualNetDef from virNetworkPortDef - Populate a full virNetworkPortDef from virDomainActualNetDef Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 275 +++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 17 +++ src/libvirt_private.syms | 3 + 3 files changed, 295 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index c4c2eb0281..ad2b59abb4 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -39,6 +39,7 @@ #include "virbuffer.h" #include "virlog.h" #include "nwfilter_conf.h" +#include "virnetworkportdef.h" #include "storage_conf.h" #include "virstoragefile.h" #include "virfile.h" @@ -30388,6 +30389,280 @@ virDomainNetTypeSharesHostView(const virDomainNetDef *net) return false; } +virNetworkPortDefPtr +virDomainNetDefToNetworkPort(virDomainDefPtr dom, + virDomainNetDefPtr iface) +{ + virNetworkPortDefPtr port; + + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected an interface of type 'network' not '%s'"), + virDomainNetTypeToString(iface->type)); + return NULL; + } + + if (VIR_ALLOC(port) < 0) + return NULL; + + virUUIDGenerate(port->uuid); + + memcpy(port->owneruuid, dom->uuid, VIR_UUID_BUFLEN); + if (VIR_STRDUP(port->ownername, dom->name) < 0) + goto error; + + if (VIR_STRDUP(port->group, iface->data.network.portgroup) < 0) + goto error; + + memcpy(&port->mac, &iface->mac, VIR_MAC_BUFLEN); + + if (virNetDevVPortProfileCopy(&port->virtPortProfile, iface->virtPortProfile) < 0) + goto error; + + if (virNetDevBandwidthCopy(&port->bandwidth, iface->bandwidth) < 0) + goto error; + + if (virNetDevVlanCopy(&port->vlan, &iface->vlan) < 0) + goto error; + + port->trustGuestRxFilters = iface->trustGuestRxFilters; + + return port; + + error: + virNetworkPortDefFree(port); + return NULL; +} + +int +virDomainNetDefActualFromNetworkPort(virDomainNetDefPtr iface, + virNetworkPortDefPtr port) +{ + virDomainActualNetDefPtr actual = NULL; + + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected an interface of type 'network' not '%s'"), + virDomainNetTypeToString(iface->type)); + return -1; + } + + if (VIR_ALLOC(actual) < 0) + return -1; + + switch ((virNetworkPortPlugType)port->plugtype) { + case VIR_NETWORK_PORT_PLUG_TYPE_NONE: + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE: + if (VIR_STRDUP(actual->data.bridge.brname, + port->plug.bridge.brname) < 0) + goto error; + actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + actual->data.bridge.macTableManager = port->plug.bridge.macTableManager; + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT: + if (VIR_STRDUP(actual->data.direct.linkdev, + port->plug.direct.linkdev) < 0) + goto error; + actual->type = VIR_DOMAIN_NET_TYPE_DIRECT; + actual->data.direct.mode = port->plug.direct.mode; + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI: + actual->type = VIR_DOMAIN_NET_TYPE_HOSTDEV; + actual->data.hostdev.def.parentnet = iface; + actual->data.hostdev.def.info = &iface->info; + actual->data.hostdev.def.mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + actual->data.hostdev.def.managed = port->plug.hostdevpci.managed; + actual->data.hostdev.def.source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; + actual->data.hostdev.def.source.subsys.u.pci.addr = port->plug.hostdevpci.addr; + switch ((virNetworkForwardDriverNameType)port->plug.hostdevpci.driver) { + case VIR_NETWORK_FORWARD_DRIVER_NAME_DEFAULT: + actual->data.hostdev.def.source.subsys.u.pci.backend = + VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT; + break; + + case VIR_NETWORK_FORWARD_DRIVER_NAME_KVM: + actual->data.hostdev.def.source.subsys.u.pci.backend = + VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; + break; + + case VIR_NETWORK_FORWARD_DRIVER_NAME_VFIO: + actual->data.hostdev.def.source.subsys.u.pci.backend = + VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; + break; + + case VIR_NETWORK_FORWARD_DRIVER_NAME_LAST: + default: + virReportEnumRangeError(virNetworkForwardDriverNameType, + port->plug.hostdevpci.driver); + goto error; + } + + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_LAST: + default: + virReportEnumRangeError(virNetworkPortPlugType, port->plugtype); + goto error; + } + + if (virNetDevVPortProfileCopy(&actual->virtPortProfile, port->virtPortProfile) < 0) + goto error; + + if (virNetDevBandwidthCopy(&actual->bandwidth, port->bandwidth) < 0) + goto error; + + if (virNetDevVlanCopy(&actual->vlan, &port->vlan) < 0) + goto error; + + actual->class_id = port->class_id; + actual->trustGuestRxFilters = port->trustGuestRxFilters; + + virDomainActualNetDefFree(iface->data.network.actual); + iface->data.network.actual = actual; + + return 0; + + error: + virDomainActualNetDefFree(actual); + return -1; +} + +virNetworkPortDefPtr +virDomainNetDefActualToNetworkPort(virDomainDefPtr dom, + virDomainNetDefPtr iface) +{ + virDomainActualNetDefPtr actual; + virNetworkPortDefPtr port; + + if (!iface->data.network.actual) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing actual data for interface '%s'"), + iface->ifname); + return NULL; + } + + actual = iface->data.network.actual; + + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected an interface of type 'network' not '%s'"), + virDomainNetTypeToString(iface->type)); + return NULL; + } + + if (VIR_ALLOC(port) < 0) + return NULL; + + /* Bad - we need to preserve original port uuid */ + virUUIDGenerate(port->uuid); + + memcpy(port->owneruuid, dom->uuid, VIR_UUID_BUFLEN); + if (VIR_STRDUP(port->ownername, dom->name) < 0) + goto error; + + if (VIR_STRDUP(port->group, iface->data.network.portgroup) < 0) + goto error; + + memcpy(&port->mac, &iface->mac, VIR_MAC_BUFLEN); + + switch (virDomainNetGetActualType(iface)) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: + port->plugtype = VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE; + if (VIR_STRDUP(port->plug.bridge.brname, + actual->data.bridge.brname) < 0) + goto error; + port->plug.bridge.macTableManager = actual->data.bridge.macTableManager; + break; + + case VIR_DOMAIN_NET_TYPE_DIRECT: + port->plugtype = VIR_NETWORK_PORT_PLUG_TYPE_DIRECT; + if (VIR_STRDUP(port->plug.direct.linkdev, + actual->data.direct.linkdev) < 0) + goto error; + port->plug.direct.mode = actual->data.direct.mode; + break; + + case VIR_DOMAIN_NET_TYPE_HOSTDEV: + port->plugtype = VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI; + if (actual->data.hostdev.def.mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + actual->data.hostdev.def.source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Actual interface '%s' hostdev was not a PCI device"), + iface->ifname); + goto error; + } + port->plug.hostdevpci.managed = actual->data.hostdev.def.managed; + port->plug.hostdevpci.addr = actual->data.hostdev.def.source.subsys.u.pci.addr; + switch ((virDomainHostdevSubsysPCIBackendType)actual->data.hostdev.def.source.subsys.u.pci.backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + port->plug.hostdevpci.driver = VIR_NETWORK_FORWARD_DRIVER_NAME_DEFAULT; + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM: + port->plug.hostdevpci.driver = VIR_NETWORK_FORWARD_DRIVER_NAME_KVM; + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + port->plug.hostdevpci.driver = VIR_NETWORK_FORWARD_DRIVER_NAME_VFIO; + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Unexpected PCI backend 'xen'")); + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST: + default: + virReportEnumRangeError(virDomainHostdevSubsysPCIBackendType, + actual->data.hostdev.def.source.subsys.u.pci.backend); + goto error; + } + + break; + + case VIR_DOMAIN_NET_TYPE_CLIENT: + case VIR_DOMAIN_NET_TYPE_ETHERNET: + case VIR_DOMAIN_NET_TYPE_INTERNAL: + case VIR_DOMAIN_NET_TYPE_MCAST: + case VIR_DOMAIN_NET_TYPE_NETWORK: + case VIR_DOMAIN_NET_TYPE_SERVER: + case VIR_DOMAIN_NET_TYPE_UDP: + case VIR_DOMAIN_NET_TYPE_USER: + case VIR_DOMAIN_NET_TYPE_VHOSTUSER: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unexpected network port type %s"), + virDomainNetTypeToString(virDomainNetGetActualType(iface))); + goto error; + + case VIR_DOMAIN_NET_TYPE_LAST: + default: + virReportEnumRangeError(virNetworkPortPlugType, port->plugtype); + goto error; + } + + if (virNetDevVPortProfileCopy(&port->virtPortProfile, actual->virtPortProfile) < 0) + goto error; + + if (virNetDevBandwidthCopy(&port->bandwidth, actual->bandwidth) < 0) + goto error; + + if (virNetDevVlanCopy(&port->vlan, &actual->vlan) < 0) + goto error; + + port->class_id = actual->class_id; + port->trustGuestRxFilters = actual->trustGuestRxFilters; + + return port; + + error: + virNetworkPortDefFree(port); + return NULL; +} + static virDomainNetAllocateActualDeviceImpl netAllocate; static virDomainNetNotifyActualDeviceImpl netNotify; static virDomainNetReleaseActualDeviceImpl netRelease; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index fb053a7ca3..4f6229f289 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3547,6 +3547,23 @@ bool virDomainDefLifecycleActionAllowed(virDomainLifecycle type, virDomainLifecycleAction action); +// Forward decl to avoid pulling in virnetworkportdef.h because +// that pulls in virhostdev.h which pulls in domain_conf.h (evil) +typedef struct _virNetworkPortDef virNetworkPortDef; +typedef virNetworkPortDef *virNetworkPortDefPtr; + +virNetworkPortDefPtr +virDomainNetDefToNetworkPort(virDomainDefPtr dom, + virDomainNetDefPtr iface); + +int +virDomainNetDefActualFromNetworkPort(virDomainNetDefPtr iface, + virNetworkPortDefPtr port); + +virNetworkPortDefPtr +virDomainNetDefActualToNetworkPort(virDomainDefPtr dom, + virDomainNetDefPtr iface); + typedef int (*virDomainNetAllocateActualDeviceImpl)(virNetworkPtr net, virDomainDefPtr dom, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 8767f4429e..4b1498d83c 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -458,9 +458,12 @@ virDomainNetAllocateActualDevice; virDomainNetAppendIPAddress; virDomainNetBandwidthChangeAllowed; virDomainNetBandwidthUpdate; +virDomainNetDefActualFromNetworkPort; +virDomainNetDefActualToNetworkPort; virDomainNetDefClear; virDomainNetDefFormat; virDomainNetDefFree; +virDomainNetDefToNetworkPort; virDomainNetFind; virDomainNetFindByName; virDomainNetFindIdx; -- 2.20.1

Convert the virDomainNetDef object into a virNetworkPortDef object at the start of networkAllocateActualDevice. This largely decouples the method impl from the domain object type. Reviewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver.c | 222 +++++++++++++++--------------------- 1 file changed, 91 insertions(+), 131 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 68c876afad..fe05a3edda 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -67,6 +67,7 @@ #include "network_event.h" #include "virhook.h" #include "virjson.h" +#include "virnetworkportdef.h" #define VIR_FROM_THIS VIR_FROM_NETWORK #define MAX_BRIDGE_ID 256 @@ -4390,17 +4391,16 @@ networkAllocateActualDevice(virNetworkPtr net, virDomainNetDefPtr iface) { virNetworkDriverStatePtr driver = networkGetDriver(); - virDomainNetType actualType = iface->type; virNetworkObjPtr obj = NULL; virNetworkDefPtr netdef = NULL; - virNetDevBandwidthPtr bandwidth = NULL; virPortGroupDefPtr portgroup = NULL; - virNetDevVPortProfilePtr virtport = iface->virtPortProfile; - virNetDevVlanPtr vlan = NULL; virNetworkForwardIfDefPtr dev = NULL; size_t i; int ret = -1; + virNetDevVPortProfilePtr portprofile = NULL; + virNetworkPortDefPtr port = NULL; + VIR_DEBUG("Allocating port from net %s", net->name); obj = virNetworkObjFindByName(driver->networks, net->name); if (!obj) { virReportError(VIR_ERR_NO_NETWORK, @@ -4415,9 +4415,6 @@ networkAllocateActualDevice(virNetworkPtr net, goto error; } - virDomainActualNetDefFree(iface->data.network.actual); - iface->data.network.actual = NULL; - netdef = virNetworkObjGetDef(obj); if (!virNetworkObjIsActive(obj)) { @@ -4427,99 +4424,84 @@ networkAllocateActualDevice(virNetworkPtr net, goto error; } - if (VIR_ALLOC(iface->data.network.actual) < 0) + if (!(port = virDomainNetDefToNetworkPort(dom, iface))) goto error; + VIR_DEBUG("Interface port group %s", port->group); /* portgroup can be present for any type of network, in particular * for bandwidth information, so we need to check for that and * fill it in appropriately for all forward types. */ - portgroup = virPortGroupFindByName(netdef, iface->data.network.portgroup); - - /* If there is already interface-specific bandwidth, just use that - * (already in NetDef). Otherwise, if there is bandwidth info in - * the portgroup, fill that into the ActualDef. - */ - - if (iface->bandwidth) - bandwidth = iface->bandwidth; - else if (portgroup && portgroup->bandwidth) - bandwidth = portgroup->bandwidth; + portgroup = virPortGroupFindByName(netdef, port->group); - if (bandwidth && virNetDevBandwidthCopy(&iface->data.network.actual->bandwidth, - bandwidth) < 0) - goto error; + if (!port->bandwidth) { + if (portgroup && portgroup->bandwidth && + virNetDevBandwidthCopy(&port->bandwidth, + portgroup->bandwidth) < 0) + goto error; + } - /* copy appropriate vlan info to actualNet */ - if (iface->vlan.nTags > 0) - vlan = &iface->vlan; - else if (portgroup && portgroup->vlan.nTags > 0) - vlan = &portgroup->vlan; - else if (netdef->vlan.nTags > 0) - vlan = &netdef->vlan; + if (port->vlan.nTags == 0) { + virNetDevVlanPtr vlan = NULL; + if (portgroup && portgroup->vlan.nTags > 0) + vlan = &portgroup->vlan; + else if (netdef->vlan.nTags > 0) + vlan = &netdef->vlan; - if (vlan && virNetDevVlanCopy(&iface->data.network.actual->vlan, vlan) < 0) - goto error; + if (vlan && virNetDevVlanCopy(&port->vlan, vlan) < 0) + goto error; + } - if (iface->trustGuestRxFilters) - iface->data.network.actual->trustGuestRxFilters - = iface->trustGuestRxFilters; - else if (portgroup && portgroup->trustGuestRxFilters) - iface->data.network.actual->trustGuestRxFilters - = portgroup->trustGuestRxFilters; - else if (netdef->trustGuestRxFilters) - iface->data.network.actual->trustGuestRxFilters - = netdef->trustGuestRxFilters; + if (!port->trustGuestRxFilters) { + if (portgroup && portgroup->trustGuestRxFilters) + port->trustGuestRxFilters = portgroup->trustGuestRxFilters; + else if (netdef->trustGuestRxFilters) + port->trustGuestRxFilters = netdef->trustGuestRxFilters; + } /* merge virtualports from interface, network, and portgroup to * arrive at actual virtualport to use */ - if (virNetDevVPortProfileMerge3(&iface->data.network.actual->virtPortProfile, - iface->virtPortProfile, + if (virNetDevVPortProfileMerge3(&portprofile, + port->virtPortProfile, netdef->virtPortProfile, portgroup ? portgroup->virtPortProfile : NULL) < 0) { - goto error; + goto error; + } + if (portprofile) { + VIR_FREE(port->virtPortProfile); + port->virtPortProfile = portprofile; } - virtport = iface->data.network.actual->virtPortProfile; + VIR_DEBUG("Processing forward type %d", netdef->forward.type); switch ((virNetworkForwardType) netdef->forward.type) { case VIR_NETWORK_FORWARD_NONE: case VIR_NETWORK_FORWARD_NAT: case VIR_NETWORK_FORWARD_ROUTE: case VIR_NETWORK_FORWARD_OPEN: - iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + port->plugtype = VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE; - /* we also store the bridge device and macTableManager settings - * in iface->data.network.actual->data.bridge for later use - * after the domain's tap device is created (to attach to the - * bridge and set flood/learning mode on the tap device) - */ - if (VIR_STRDUP(iface->data.network.actual->data.bridge.brname, - netdef->bridge) < 0) + if (VIR_STRDUP(port->plug.bridge.brname, netdef->bridge) < 0) goto error; - iface->data.network.actual->data.bridge.macTableManager - = netdef->macTableManager; + port->plug.bridge.macTableManager = netdef->macTableManager; - if (virtport) { + if (port->virtPortProfile) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("<virtualport type='%s'> not supported for network " "'%s' which uses IP forwarding"), - virNetDevVPortTypeToString(virtport->virtPortType), + virNetDevVPortTypeToString(port->virtPortProfile->virtPortType), netdef->name); goto error; } - if (networkPlugBandwidth(obj, &iface->mac, iface->bandwidth, - iface->data.network.actual ? - &iface->data.network.actual->class_id : NULL) < 0) + if (networkPlugBandwidth(obj, &port->mac, port->bandwidth, &port->class_id) < 0) goto error; break; case VIR_NETWORK_FORWARD_HOSTDEV: { - virDomainHostdevSubsysPCIBackendType backend; + port->plugtype = VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI; - iface->data.network.actual->type = actualType = VIR_DOMAIN_NET_TYPE_HOSTDEV; if (networkCreateInterfacePool(netdef) < 0) goto error; @@ -4537,42 +4519,19 @@ networkAllocateActualDevice(virNetworkPtr net, netdef->name); goto error; } - iface->data.network.actual->data.hostdev.def.parentnet = iface; - iface->data.network.actual->data.hostdev.def.info = &iface->info; - iface->data.network.actual->data.hostdev.def.mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; - iface->data.network.actual->data.hostdev.def.managed = netdef->forward.managed ? 1 : 0; - iface->data.network.actual->data.hostdev.def.source.subsys.type = dev->type; - iface->data.network.actual->data.hostdev.def.source.subsys.u.pci.addr = dev->device.pci; - - switch (netdef->forward.driverName) { - case VIR_NETWORK_FORWARD_DRIVER_NAME_DEFAULT: - backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT; - break; - case VIR_NETWORK_FORWARD_DRIVER_NAME_KVM: - backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; - break; - case VIR_NETWORK_FORWARD_DRIVER_NAME_VFIO: - backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; - break; - default: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unrecognized driver name value %d " - " in network '%s'"), - netdef->forward.driverName, netdef->name); - goto error; - } - iface->data.network.actual->data.hostdev.def.source.subsys.u.pci.backend - = backend; + port->plug.hostdevpci.addr = dev->device.pci; + port->plug.hostdevpci.driver = netdef->forward.driverName; + port->plug.hostdevpci.managed = netdef->forward.managed; - if (virtport) { + if (port->virtPortProfile) { /* make sure type is supported for hostdev connections */ - if (virtport->virtPortType != VIR_NETDEV_VPORT_PROFILE_8021QBG && - virtport->virtPortType != VIR_NETDEV_VPORT_PROFILE_8021QBH) { + if (port->virtPortProfile->virtPortType != VIR_NETDEV_VPORT_PROFILE_8021QBG && + port->virtPortProfile->virtPortType != VIR_NETDEV_VPORT_PROFILE_8021QBH) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("<virtualport type='%s'> not supported for network " "'%s' which uses an SR-IOV Virtual Function " "via PCI passthrough"), - virNetDevVPortTypeToString(virtport->virtPortType), + virNetDevVPortTypeToString(port->virtPortProfile->virtPortType), netdef->name); goto error; } @@ -4586,28 +4545,24 @@ networkAllocateActualDevice(virNetworkPtr net, * is VIR_DOMAIN_NET_TYPE_BRIDGE */ - iface->data.network.actual->type = actualType = VIR_DOMAIN_NET_TYPE_BRIDGE; - if (VIR_STRDUP(iface->data.network.actual->data.bridge.brname, - netdef->bridge) < 0) + port->plugtype = VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE; + if (VIR_STRDUP(port->plug.bridge.brname, netdef->bridge) < 0) goto error; - iface->data.network.actual->data.bridge.macTableManager - = netdef->macTableManager; + port->plug.bridge.macTableManager = netdef->macTableManager; - if (virtport) { + if (port->virtPortProfile) { /* only type='openvswitch' is allowed for bridges */ - if (virtport->virtPortType != VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH) { + if (port->virtPortProfile->virtPortType != VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("<virtualport type='%s'> not supported for network " "'%s' which uses a bridge device"), - virNetDevVPortTypeToString(virtport->virtPortType), + virNetDevVPortTypeToString(port->virtPortProfile->virtPortType), netdef->name); goto error; } } - if (networkPlugBandwidth(obj, &iface->mac, iface->bandwidth, - iface->data.network.actual ? - &iface->data.network.actual->class_id : NULL) < 0) + if (networkPlugBandwidth(obj, &port->mac, port->bandwidth, &port->class_id) < 0) goto error; break; } @@ -4625,22 +4580,22 @@ networkAllocateActualDevice(virNetworkPtr net, */ /* Set type=direct and appropriate <source mode='xxx'/> */ - iface->data.network.actual->type = actualType = VIR_DOMAIN_NET_TYPE_DIRECT; + port->plugtype = VIR_NETWORK_PORT_PLUG_TYPE_DIRECT; /* NO need to check the value returned from virNetDevMacVLanModeTypeFromString * it must be valid for these forward type(bridge|private|vepa|passthrough) */ - iface->data.network.actual->data.direct.mode = + port->plug.direct.mode = virNetDevMacVLanModeTypeFromString(virNetworkForwardTypeToString(netdef->forward.type)); - if (virtport) { + if (port->virtPortProfile) { /* make sure type is supported for macvtap connections */ - if (virtport->virtPortType != VIR_NETDEV_VPORT_PROFILE_8021QBG && - virtport->virtPortType != VIR_NETDEV_VPORT_PROFILE_8021QBH) { + if (port->virtPortProfile->virtPortType != VIR_NETDEV_VPORT_PROFILE_8021QBG && + port->virtPortProfile->virtPortType != VIR_NETDEV_VPORT_PROFILE_8021QBH) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("<virtualport type='%s'> not supported for network " "'%s' which uses a macvtap device"), - virNetDevVPortTypeToString(virtport->virtPortType), + virNetDevVPortTypeToString(port->virtPortProfile->virtPortType), netdef->name); goto error; } @@ -4669,8 +4624,8 @@ networkAllocateActualDevice(virNetworkPtr net, */ if ((netdef->forward.type == VIR_NETWORK_FORWARD_PASSTHROUGH) || ((netdef->forward.type == VIR_NETWORK_FORWARD_PRIVATE) && - iface->data.network.actual->virtPortProfile && - (iface->data.network.actual->virtPortProfile->virtPortType + port->virtPortProfile && + (port->virtPortProfile->virtPortType == VIR_NETDEV_VPORT_PROFILE_8021QBH))) { /* pick first dev with 0 connections */ @@ -4696,7 +4651,7 @@ networkAllocateActualDevice(virNetworkPtr net, netdef->name); goto error; } - if (VIR_STRDUP(iface->data.network.actual->data.direct.linkdev, + if (VIR_STRDUP(port->plug.direct.linkdev, dev->device.dev) < 0) goto error; } @@ -4709,30 +4664,30 @@ networkAllocateActualDevice(virNetworkPtr net, } if (virNetworkObjMacMgrAdd(obj, driver->dnsmasqStateDir, - dom->name, &iface->mac) < 0) + dom->name, &port->mac) < 0) goto error; - if (virNetDevVPortProfileCheckComplete(virtport, true) < 0) + if (virNetDevVPortProfileCheckComplete(port->virtPortProfile, true) < 0) goto error; /* make sure that everything now specified for the device is * actually supported on this type of network. NB: network, * netdev, and iface->data.network.actual may all be NULL. */ + VIR_DEBUG("Sanity check port config"); - if (virDomainNetGetActualVlan(iface)) { + if (port->vlan.nTags) { /* vlan configuration via libvirt is only supported for PCI * Passthrough SR-IOV devices (hostdev or macvtap passthru * mode) and openvswitch bridges. Otherwise log an error and * fail */ - if (!(actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV || - (actualType == VIR_DOMAIN_NET_TYPE_DIRECT && - virDomainNetGetActualDirectMode(iface) - == VIR_NETDEV_MACVLAN_MODE_PASSTHRU) || - (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE && - virtport && virtport->virtPortType - == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH))) { + if (!(port->plugtype == VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI || + (port->plugtype == VIR_NETWORK_PORT_PLUG_TYPE_DIRECT && + port->plug.direct.mode == VIR_NETDEV_MACVLAN_MODE_PASSTHRU) || + (port->plugtype == VIR_DOMAIN_NET_TYPE_BRIDGE && + port->virtPortProfile && + port->virtPortProfile->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("an interface connecting to network '%s' " "is requesting a vlan tag, but that is not " @@ -4741,16 +4696,15 @@ networkAllocateActualDevice(virNetworkPtr net, goto error; } } - if (virDomainNetGetActualBandwidth(iface)) { - /* bandwidth configuration via libvirt is not supported for - * hostdev network devices - */ - if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("bandwidth settings are not supported " - "for hostdev interfaces")); - goto error; - } + + /* bandwidth configuration via libvirt is not supported for + * hostdev network devices + */ + if (port->bandwidth && port->plugtype == VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("bandwidth settings are not supported " + "for hostdev interfaces")); + goto error; } netdef->connections++; @@ -4768,9 +4722,15 @@ networkAllocateActualDevice(virNetworkPtr net, } networkLogAllocation(netdef, dev, &iface->mac, true); + VIR_DEBUG("Populating net def"); + if (virDomainNetDefActualFromNetworkPort(iface, port) < 0) + goto error; + + VIR_DEBUG("Port allocated"); ret = 0; cleanup: + virNetworkPortDefFree(port); virNetworkObjEndAPI(&obj); return ret; -- 2.20.1

Convert the virDomainNetDef object into a virNetworkPortDef object at the start of networkNotifyActualDevice. This largely decouples the method impl from the domain object type. Reviewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver.c | 91 ++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 47 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index fe05a3edda..55fd53b867 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4760,10 +4760,10 @@ networkNotifyActualDevice(virNetworkPtr net, virDomainNetDefPtr iface) { virNetworkDriverStatePtr driver = networkGetDriver(); - virDomainNetType actualType = virDomainNetGetActualType(iface); virNetworkObjPtr obj; virNetworkDefPtr netdef; virNetworkForwardIfDefPtr dev = NULL; + virNetworkPortDefPtr port = NULL; size_t i; int ret = -1; @@ -4790,40 +4790,34 @@ networkNotifyActualDevice(virNetworkPtr net, goto error; } - if (!iface->data.network.actual || - (actualType != VIR_DOMAIN_NET_TYPE_DIRECT && - actualType != VIR_DOMAIN_NET_TYPE_HOSTDEV)) { - VIR_DEBUG("Nothing to claim from network %s", iface->data.network.name); - goto success; - } - - if (networkCreateInterfacePool(netdef) < 0) - goto error; + if (!(port = virDomainNetDefActualToNetworkPort(dom, iface))) + goto cleanup; - if (netdef->forward.nifs == 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("network '%s' uses a direct or hostdev mode, " - "but has no forward dev and no interface pool"), - netdef->name); + switch (port->plugtype) { + case VIR_NETWORK_PORT_PLUG_TYPE_NONE: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpectedly got a network port without a plug")); goto error; - } - if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) { - const char *actualDev; - - actualDev = virDomainNetGetActualDirectDev(iface); - if (!actualDev) { + case VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE: + /* see if we're connected to the correct bridge */ + if (!netdef->bridge) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("the interface uses a direct mode, " - "but has no source dev")); + _("Unexpectedly got a network port plugged into a bridge")); goto error; } + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT: + if (networkCreateInterfacePool(netdef) < 0) + goto error; /* find the matching interface and increment its connections */ for (i = 0; i < netdef->forward.nifs; i++) { if (netdef->forward.ifs[i].type == VIR_NETWORK_FORWARD_HOSTDEV_DEVICE_NETDEV && - STREQ(actualDev, netdef->forward.ifs[i].device.dev)) { + STREQ(port->plug.direct.linkdev, + netdef->forward.ifs[i].device.dev)) { dev = &netdef->forward.ifs[i]; break; } @@ -4832,8 +4826,9 @@ networkNotifyActualDevice(virNetworkPtr net, if (!dev) { virReportError(VIR_ERR_INTERNAL_ERROR, _("network '%s' doesn't have dev='%s' " - "in use by domain"), - netdef->name, actualDev); + "in use by network port '%s'"), + netdef->name, port->plug.direct.linkdev, + port->uuid); goto error; } @@ -4844,31 +4839,26 @@ networkNotifyActualDevice(virNetworkPtr net, if ((dev->connections > 0) && ((netdef->forward.type == VIR_NETWORK_FORWARD_PASSTHROUGH) || ((netdef->forward.type == VIR_NETWORK_FORWARD_PRIVATE) && - iface->data.network.actual->virtPortProfile && - (iface->data.network.actual->virtPortProfile->virtPortType - == VIR_NETDEV_VPORT_PROFILE_8021QBH)))) { + port->virtPortProfile && + (port->virtPortProfile->virtPortType == VIR_NETDEV_VPORT_PROFILE_8021QBH)))) { virReportError(VIR_ERR_INTERNAL_ERROR, _("network '%s' claims dev='%s' is already in " - "use by a different domain"), - netdef->name, actualDev); + "use by a different port"), + netdef->name, port->plug.direct.linkdev); goto error; } - } else /* if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) */ { - virDomainHostdevDefPtr hostdev; + break; - hostdev = virDomainNetGetActualHostdev(iface); - if (!hostdev) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("the interface uses a hostdev mode, " - "but has no hostdev")); + case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI: + + if (networkCreateInterfacePool(netdef) < 0) goto error; - } /* find the matching interface and increment its connections */ for (i = 0; i < netdef->forward.nifs; i++) { if (netdef->forward.ifs[i].type == VIR_NETWORK_FORWARD_HOSTDEV_DEVICE_PCI && - virPCIDeviceAddressEqual(&hostdev->source.subsys.u.pci.addr, + virPCIDeviceAddressEqual(&port->plug.hostdevpci.addr, &netdef->forward.ifs[i].device.pci)) { dev = &netdef->forward.ifs[i]; break; @@ -4878,12 +4868,12 @@ networkNotifyActualDevice(virNetworkPtr net, if (!dev) { virReportError(VIR_ERR_INTERNAL_ERROR, _("network '%s' doesn't have " - "PCI device %04x:%02x:%02x.%x in use by domain"), + "PCI device %04x:%02x:%02x.%x in use by network port"), netdef->name, - hostdev->source.subsys.u.pci.addr.domain, - hostdev->source.subsys.u.pci.addr.bus, - hostdev->source.subsys.u.pci.addr.slot, - hostdev->source.subsys.u.pci.addr.function); + port->plug.hostdevpci.addr.domain, + port->plug.hostdevpci.addr.bus, + port->plug.hostdevpci.addr.slot, + port->plug.hostdevpci.addr.function); goto error; } @@ -4896,15 +4886,21 @@ networkNotifyActualDevice(virNetworkPtr net, virReportError(VIR_ERR_INTERNAL_ERROR, _("network '%s' claims the PCI device at " "domain=%d bus=%d slot=%d function=%d " - "is already in use by a different domain"), + "is already in use by a different network port"), netdef->name, dev->device.pci.domain, dev->device.pci.bus, dev->device.pci.slot, dev->device.pci.function); goto error; } + + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_LAST: + default: + virReportEnumRangeError(virNetworkPortPlugType, port->plugtype); + goto error; } - success: netdef->connections++; if (dev) dev->connections++; @@ -4922,6 +4918,7 @@ networkNotifyActualDevice(virNetworkPtr net, cleanup: virNetworkObjEndAPI(&obj); + virNetworkPortDefFree(port); return ret; error: -- 2.20.1

Convert the virDomainNetDef object into a virNetworkPortDef object at the start of networkReleaseActualDevice. This largely decouples the method impl from the domain object type. Reviewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver.c | 139 +++++++++++++++--------------------- 1 file changed, 57 insertions(+), 82 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 55fd53b867..26f32a23eb 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4943,10 +4943,10 @@ networkReleaseActualDevice(virNetworkPtr net, virDomainNetDefPtr iface) { virNetworkDriverStatePtr driver = networkGetDriver(); - virDomainNetType actualType = virDomainNetGetActualType(iface); virNetworkObjPtr obj; virNetworkDefPtr netdef; virNetworkForwardIfDefPtr dev = NULL; + virNetworkPortDefPtr port = NULL; size_t i; int ret = -1; @@ -4955,77 +4955,49 @@ networkReleaseActualDevice(virNetworkPtr net, virReportError(VIR_ERR_NO_NETWORK, _("no network with matching name '%s'"), net->name); - goto error; + goto cleanup; } if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Expected an interface for a virtual network")); - goto error; + goto cleanup; } + if (iface->data.network.actual == NULL) { + ret = 0; + goto cleanup; + } + + if (!(port = virDomainNetDefActualToNetworkPort(dom, iface))) + goto cleanup; + netdef = virNetworkObjGetDef(obj); - switch ((virNetworkForwardType) netdef->forward.type) { - case VIR_NETWORK_FORWARD_NONE: - case VIR_NETWORK_FORWARD_NAT: - case VIR_NETWORK_FORWARD_ROUTE: - case VIR_NETWORK_FORWARD_OPEN: - if (iface->data.network.actual && - networkUnplugBandwidth(obj, iface->bandwidth, - &iface->data.network.actual->class_id) < 0) - goto error; + switch ((virNetworkPortPlugType)port->plugtype) { + case VIR_NETWORK_PORT_PLUG_TYPE_NONE: + VIR_DEBUG("Releasing network device with no plug type"); break; - case VIR_NETWORK_FORWARD_BRIDGE: - if (iface->data.network.actual && - actualType == VIR_DOMAIN_NET_TYPE_BRIDGE && - networkUnplugBandwidth(obj, iface->bandwidth, - &iface->data.network.actual->class_id) < 0) - goto error; - break; - case VIR_NETWORK_FORWARD_PRIVATE: - case VIR_NETWORK_FORWARD_VEPA: - case VIR_NETWORK_FORWARD_PASSTHROUGH: - case VIR_NETWORK_FORWARD_HOSTDEV: + case VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE: + if (networkUnplugBandwidth(obj, port->bandwidth, + &port->class_id) < 0) + goto cleanup; break; - case VIR_NETWORK_FORWARD_LAST: - default: - virReportEnumRangeError(virNetworkForwardType, netdef->forward.type); - goto error; - } - - if ((!iface->data.network.actual) || - ((actualType != VIR_DOMAIN_NET_TYPE_DIRECT) && - (actualType != VIR_DOMAIN_NET_TYPE_HOSTDEV))) { - VIR_DEBUG("Nothing to release to network %s", iface->data.network.name); - goto success; - } - - if (netdef->forward.nifs == 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("network '%s' uses a direct/hostdev mode, but " - "has no forward dev and no interface pool"), - netdef->name); - goto error; - } - - if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) { - const char *actualDev; - - actualDev = virDomainNetGetActualDirectDev(iface); - if (!actualDev) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("the interface uses a direct mode, " - "but has no source dev")); - goto error; + case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT: + if (netdef->forward.nifs == 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("network '%s' uses a direct mode, but " + "has no forward dev and no interface pool"), + netdef->name); + goto cleanup; } for (i = 0; i < netdef->forward.nifs; i++) { if (netdef->forward.ifs[i].type == VIR_NETWORK_FORWARD_HOSTDEV_DEVICE_NETDEV && - STREQ(actualDev, netdef->forward.ifs[i].device.dev)) { + STREQ(port->plug.direct.linkdev, netdef->forward.ifs[i].device.dev)) { dev = &netdef->forward.ifs[i]; break; } @@ -5035,23 +5007,24 @@ networkReleaseActualDevice(virNetworkPtr net, virReportError(VIR_ERR_INTERNAL_ERROR, _("network '%s' doesn't have dev='%s' " "in use by domain"), - netdef->name, actualDev); - goto error; + netdef->name, port->plug.direct.linkdev); + goto cleanup; } - } else /* if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) */ { - virDomainHostdevDefPtr hostdev; + break; - hostdev = virDomainNetGetActualHostdev(iface); - if (!hostdev) { + case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI: + if (netdef->forward.nifs == 0) { virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("the interface uses a hostdev mode, but has no hostdev")); - goto error; + _("network '%s' uses a hostdev mode, but " + "has no forward dev and no interface pool"), + netdef->name); + goto cleanup; } for (i = 0; i < netdef->forward.nifs; i++) { if (netdef->forward.ifs[i].type == VIR_NETWORK_FORWARD_HOSTDEV_DEVICE_PCI && - virPCIDeviceAddressEqual(&hostdev->source.subsys.u.pci.addr, + virPCIDeviceAddressEqual(&port->plug.hostdevpci.addr, &netdef->forward.ifs[i].device.pci)) { dev = &netdef->forward.ifs[i]; break; @@ -5063,26 +5036,30 @@ networkReleaseActualDevice(virNetworkPtr net, _("network '%s' doesn't have " "PCI device %04x:%02x:%02x.%x in use by domain"), netdef->name, - hostdev->source.subsys.u.pci.addr.domain, - hostdev->source.subsys.u.pci.addr.bus, - hostdev->source.subsys.u.pci.addr.slot, - hostdev->source.subsys.u.pci.addr.function); - goto error; + port->plug.hostdevpci.addr.domain, + port->plug.hostdevpci.addr.bus, + port->plug.hostdevpci.addr.slot, + port->plug.hostdevpci.addr.function); + goto cleanup; } + break; + + case VIR_NETWORK_PORT_PLUG_TYPE_LAST: + default: + virReportEnumRangeError(virNetworkPortPlugType, port->plugtype); + goto cleanup; } - success: - virNetworkObjMacMgrDel(obj, driver->dnsmasqStateDir, dom->name, &iface->mac); + virNetworkObjMacMgrDel(obj, driver->dnsmasqStateDir, dom->name, &port->mac); + + netdef->connections--; + if (dev) + dev->connections--; + /* finally we can call the 'unplugged' hook script if any */ + networkRunHook(obj, dom, iface, VIR_HOOK_NETWORK_OP_IFACE_UNPLUGGED, + VIR_HOOK_SUBOP_BEGIN); + networkLogAllocation(netdef, dev, &port->mac, false); - if (iface->data.network.actual) { - netdef->connections--; - if (dev) - dev->connections--; - /* finally we can call the 'unplugged' hook script if any */ - networkRunHook(obj, dom, iface, VIR_HOOK_NETWORK_OP_IFACE_UNPLUGGED, - VIR_HOOK_SUBOP_BEGIN); - networkLogAllocation(netdef, dev, &iface->mac, false); - } ret = 0; cleanup: virNetworkObjEndAPI(&obj); @@ -5090,10 +5067,8 @@ networkReleaseActualDevice(virNetworkPtr net, virDomainActualNetDefFree(iface->data.network.actual); iface->data.network.actual = NULL; } + virNetworkPortDefFree(port); return ret; - - error: - goto cleanup; } -- 2.20.1

When (un)plugging an interface into a network, the 'plugged' and 'unplugged' operations are invoked in the hook script. The data provided to the script contains the network XML, the domain XML and the domain interface XML. When we strictly split the drivers up this will no longer be possible and thus breakage is unavoidable. The hook scripts are not considered to be covered by the API guarantee so this is OK. To avoid existing scripts taking the wrong action, the existing operations are changed to 'port-created' and 'port-deleted' instead. These will receive the network XML and the network port XML. Reviewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- docs/hooks.html.in | 24 +++++++++++------------- src/network/bridge_driver.c | 27 +++++++++++---------------- src/util/virhook.c | 4 ++-- src/util/virhook.h | 4 ++-- 4 files changed, 26 insertions(+), 33 deletions(-) diff --git a/docs/hooks.html.in b/docs/hooks.html.in index 2c4c39b771..7c9d3ef7f3 100644 --- a/docs/hooks.html.in +++ b/docs/hooks.html.in @@ -91,10 +91,8 @@ </network> </hookData></pre> - <p>In the case of an interface - being plugged/unplugged to/from the network, the network XML will be - followed with the full XML description of the domain containing the - interface that is being plugged/unplugged:</p> + <p>In the case of an network port being created / deleted, the network + XML will be followed with the full XML description of the port:</p> <pre><hookData> <network> @@ -102,11 +100,11 @@ <uuid>afca425a-2c3a-420c-b2fb-dd7b4950d722</uuid> ... </network> - <domain type='$domain_type' id='$domain_id'> - <name>$domain_name</name> - <uuid>afca425a-2c3a-420c-b2fb-dd7b4950d722</uuid> - ... - </domain> + <networkport> + <uuid>5d744f21-ba4a-4d6e-bdb2-30a35ff3207d</uuid> + ... + <plug type='direct' dev='ens3' mode='vepa'/> + </networkport> </hookData></pre> <p>Please note that this approach is different from other cases such as @@ -296,15 +294,15 @@ <pre>/etc/libvirt/hooks/network network_name stopped end -</pre></li> <li>Later, when network is started and there's an interface from a domain to be plugged into the network, the hook script is called as:<br/> - <pre>/etc/libvirt/hooks/network network_name plugged begin -</pre> + <pre>/etc/libvirt/hooks/network network_name port-created begin -</pre> Please note, that in this case, the script is passed both network and - domain XMLs on its stdin.</li> + port XMLs on its stdin.</li> <li>When network is updated, the hook script is called as:<br/> <pre>/etc/libvirt/hooks/network network_name updated begin -</pre></li> <li>When the domain from previous case is shutting down, the interface is unplugged. This leads to another script invocation:<br/> - <pre>/etc/libvirt/hooks/network network_name unplugged begin -</pre> - And again, as in previous case, both network and domain XMLs are passed + <pre>/etc/libvirt/hooks/network network_name port-deleted begin -</pre> + And again, as in previous case, both network and port XMLs are passed onto script's stdin.</li> </ul> diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 26f32a23eb..e212ba14e5 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -205,14 +205,13 @@ networkObjFromNetwork(virNetworkPtr net) static int networkRunHook(virNetworkObjPtr obj, - virDomainDefPtr dom, - virDomainNetDefPtr iface, + virNetworkPortDefPtr port, int op, int sub_op) { virNetworkDefPtr def; virBuffer buf = VIR_BUFFER_INITIALIZER; - char *xml = NULL, *net_xml = NULL, *dom_xml = NULL; + char *xml = NULL; int hookret; int ret = -1; @@ -226,11 +225,9 @@ networkRunHook(virNetworkObjPtr obj, virBufferAddLit(&buf, "<hookData>\n"); virBufferAdjustIndent(&buf, 2); - if (iface && virDomainNetDefFormat(&buf, iface, NULL, 0) < 0) - goto cleanup; if (virNetworkDefFormatBuf(&buf, def, 0) < 0) goto cleanup; - if (dom && virDomainDefFormatInternal(dom, NULL, 0, &buf, NULL) < 0) + if (port && virNetworkPortDefFormatBuf(&buf, port) < 0) goto cleanup; virBufferAdjustIndent(&buf, -2); @@ -256,8 +253,6 @@ networkRunHook(virNetworkObjPtr obj, cleanup: virBufferFreeAndReset(&buf); VIR_FREE(xml); - VIR_FREE(net_xml); - VIR_FREE(dom_xml); return ret; } @@ -2786,7 +2781,7 @@ networkStartNetwork(virNetworkDriverStatePtr driver, /* Run an early hook to set-up missing devices. * If the script raised an error abort the launch. */ - if (networkRunHook(obj, NULL, NULL, + if (networkRunHook(obj, NULL, VIR_HOOK_NETWORK_OP_START, VIR_HOOK_SUBOP_BEGIN) < 0) goto cleanup; @@ -2830,7 +2825,7 @@ networkStartNetwork(virNetworkDriverStatePtr driver, virNetworkObjSetFloorSum(obj, 0); /* finally we can call the 'started' hook script if any */ - if (networkRunHook(obj, NULL, NULL, + if (networkRunHook(obj, NULL, VIR_HOOK_NETWORK_OP_STARTED, VIR_HOOK_SUBOP_BEGIN) < 0) goto cleanup; @@ -2914,7 +2909,7 @@ networkShutdownNetwork(virNetworkDriverStatePtr driver, } /* now that we know it's stopped call the hook if present */ - networkRunHook(obj, NULL, NULL, VIR_HOOK_NETWORK_OP_STOPPED, + networkRunHook(obj, NULL, VIR_HOOK_NETWORK_OP_STOPPED, VIR_HOOK_SUBOP_END); virNetworkObjSetActive(obj, false); @@ -3890,7 +3885,7 @@ networkUpdate(virNetworkPtr net, } /* call the 'updated' network hook script */ - if (networkRunHook(obj, NULL, NULL, VIR_HOOK_NETWORK_OP_UPDATED, + if (networkRunHook(obj, NULL, VIR_HOOK_NETWORK_OP_UPDATED, VIR_HOOK_SUBOP_BEGIN) < 0) goto cleanup; @@ -4711,8 +4706,8 @@ networkAllocateActualDevice(virNetworkPtr net, if (dev) dev->connections++; /* finally we can call the 'plugged' hook script if any */ - if (networkRunHook(obj, dom, iface, - VIR_HOOK_NETWORK_OP_IFACE_PLUGGED, + if (networkRunHook(obj, port, + VIR_HOOK_NETWORK_OP_PORT_CREATED, VIR_HOOK_SUBOP_BEGIN) < 0) { /* adjust for failure */ netdef->connections--; @@ -4905,7 +4900,7 @@ networkNotifyActualDevice(virNetworkPtr net, if (dev) dev->connections++; /* finally we can call the 'plugged' hook script if any */ - if (networkRunHook(obj, dom, iface, VIR_HOOK_NETWORK_OP_IFACE_PLUGGED, + if (networkRunHook(obj, port, VIR_HOOK_NETWORK_OP_PORT_CREATED, VIR_HOOK_SUBOP_BEGIN) < 0) { /* adjust for failure */ if (dev) @@ -5056,7 +5051,7 @@ networkReleaseActualDevice(virNetworkPtr net, if (dev) dev->connections--; /* finally we can call the 'unplugged' hook script if any */ - networkRunHook(obj, dom, iface, VIR_HOOK_NETWORK_OP_IFACE_UNPLUGGED, + networkRunHook(obj, port, VIR_HOOK_NETWORK_OP_PORT_DELETED, VIR_HOOK_SUBOP_BEGIN); networkLogAllocation(netdef, dev, &port->mac, false); diff --git a/src/util/virhook.c b/src/util/virhook.c index 57549ef8e5..84b3954872 100644 --- a/src/util/virhook.c +++ b/src/util/virhook.c @@ -100,8 +100,8 @@ VIR_ENUM_IMPL(virHookNetworkOp, "start", "started", "stopped", - "plugged", - "unplugged", + "port-created", + "port-deleted", "updated", ); diff --git a/src/util/virhook.h b/src/util/virhook.h index 034fb8f263..fa188c89e3 100644 --- a/src/util/virhook.h +++ b/src/util/virhook.h @@ -79,8 +79,8 @@ typedef enum { VIR_HOOK_NETWORK_OP_START, /* network is about to start */ VIR_HOOK_NETWORK_OP_STARTED, /* network has start */ VIR_HOOK_NETWORK_OP_STOPPED, /* network has stopped */ - VIR_HOOK_NETWORK_OP_IFACE_PLUGGED, /* an interface has been plugged into the network */ - VIR_HOOK_NETWORK_OP_IFACE_UNPLUGGED, /* an interface was unplugged from the network */ + VIR_HOOK_NETWORK_OP_PORT_CREATED, /* port has been created in the network */ + VIR_HOOK_NETWORK_OP_PORT_DELETED, /* port has been deleted in the network */ VIR_HOOK_NETWORK_OP_UPDATED, /* network has been updated */ VIR_HOOK_NETWORK_OP_LAST, -- 2.20.1

The current qemu driver code for changing bandwidth on a NIC first asks the network driver if the change is supported, then changes the bandwidth on the VIF, and then tells the network driver to update the bandwidth on the bridge. This is potentially racing if a parallel API call causes the network driver to allocate bandwidth on the bridge between the check and the update phases. Change the code to just try to apply the network bridge update immediately and rollback at the end if something failed. Reviewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 15 -------- src/conf/domain_conf.h | 10 ----- src/libvirt_private.syms | 1 - src/network/bridge_driver.c | 75 ++++++++----------------------------- src/qemu/qemu_driver.c | 8 ++-- 5 files changed, 20 insertions(+), 89 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index ad2b59abb4..1be78e1e26 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -30666,7 +30666,6 @@ virDomainNetDefActualToNetworkPort(virDomainDefPtr dom, static virDomainNetAllocateActualDeviceImpl netAllocate; static virDomainNetNotifyActualDeviceImpl netNotify; static virDomainNetReleaseActualDeviceImpl netRelease; -static virDomainNetBandwidthChangeAllowedImpl netBandwidthChangeAllowed; static virDomainNetBandwidthUpdateImpl netBandwidthUpdate; @@ -30674,13 +30673,11 @@ void virDomainNetSetDeviceImpl(virDomainNetAllocateActualDeviceImpl allocate, virDomainNetNotifyActualDeviceImpl notify, virDomainNetReleaseActualDeviceImpl release, - virDomainNetBandwidthChangeAllowedImpl bandwidthChangeAllowed, virDomainNetBandwidthUpdateImpl bandwidthUpdate) { netAllocate = allocate; netNotify = notify; netRelease = release; - netBandwidthChangeAllowed = bandwidthChangeAllowed; netBandwidthUpdate = bandwidthUpdate; } @@ -30774,18 +30771,6 @@ virDomainNetReleaseActualDevice(virConnectPtr conn, return ret; } -bool -virDomainNetBandwidthChangeAllowed(virDomainNetDefPtr iface, - virNetDevBandwidthPtr newBandwidth) -{ - if (!netBandwidthChangeAllowed) { - virReportError(VIR_ERR_NO_SUPPORT, "%s", - _("Virtual networking driver is not available")); - return -1; - } - - return netBandwidthChangeAllowed(iface, newBandwidth); -} int virDomainNetBandwidthUpdate(virDomainNetDefPtr iface, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 4f6229f289..be43b31123 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3579,10 +3579,6 @@ typedef int virDomainDefPtr dom, virDomainNetDefPtr iface); -typedef bool -(*virDomainNetBandwidthChangeAllowedImpl)(virDomainNetDefPtr iface, - virNetDevBandwidthPtr newBandwidth); - typedef int (*virDomainNetBandwidthUpdateImpl)(virDomainNetDefPtr iface, virNetDevBandwidthPtr newBandwidth); @@ -3592,7 +3588,6 @@ void virDomainNetSetDeviceImpl(virDomainNetAllocateActualDeviceImpl allocate, virDomainNetNotifyActualDeviceImpl notify, virDomainNetReleaseActualDeviceImpl release, - virDomainNetBandwidthChangeAllowedImpl bandwidthChangeAllowed, virDomainNetBandwidthUpdateImpl bandwidthUpdate); int @@ -3613,11 +3608,6 @@ virDomainNetReleaseActualDevice(virConnectPtr conn, virDomainNetDefPtr iface) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); -bool -virDomainNetBandwidthChangeAllowed(virDomainNetDefPtr iface, - virNetDevBandwidthPtr newBandwidth) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); - int virDomainNetBandwidthUpdate(virDomainNetDefPtr iface, virNetDevBandwidthPtr newBandwidth) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4b1498d83c..29250cdb75 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -456,7 +456,6 @@ virDomainMemorySourceTypeFromString; virDomainMemorySourceTypeToString; virDomainNetAllocateActualDevice; virDomainNetAppendIPAddress; -virDomainNetBandwidthChangeAllowed; virDomainNetBandwidthUpdate; virDomainNetDefActualFromNetworkPort; virDomainNetDefActualToNetworkPort; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index e212ba14e5..f8b58b3a25 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -5352,63 +5352,6 @@ networkNetworkObjTaint(virNetworkObjPtr obj, } -static bool -networkBandwidthGenericChecks(virDomainNetDefPtr iface, - virNetDevBandwidthPtr newBandwidth) -{ - virNetDevBandwidthPtr ifaceBand; - unsigned long long old_floor, new_floor; - - if (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_BRIDGE || - iface->data.network.actual->data.bridge.brname == NULL) { - /* This is not an interface that's plugged into a network. - * We don't care. Thus from our POV bandwidth change is allowed. */ - return false; - } - - ifaceBand = virDomainNetGetActualBandwidth(iface); - old_floor = new_floor = 0; - - if (ifaceBand && ifaceBand->in) - old_floor = ifaceBand->in->floor; - if (newBandwidth && newBandwidth->in) - new_floor = newBandwidth->in->floor; - - return new_floor != old_floor; -} - - -static bool -networkBandwidthChangeAllowed(virDomainNetDefPtr iface, - virNetDevBandwidthPtr newBandwidth) -{ - virNetworkDriverStatePtr driver = networkGetDriver(); - virNetworkObjPtr obj = NULL; - virNetDevBandwidthPtr ifaceBand = virDomainNetGetActualBandwidth(iface); - bool ret = false; - - if (!networkBandwidthGenericChecks(iface, newBandwidth)) - return true; - - obj = virNetworkObjFindByName(driver->networks, iface->data.network.name); - if (!obj) { - virReportError(VIR_ERR_NO_NETWORK, - _("no network with matching name '%s'"), - iface->data.network.name); - return false; - } - - if (networkCheckBandwidth(obj, newBandwidth, ifaceBand, &iface->mac, NULL) < 0) - goto cleanup; - - ret = true; - - cleanup: - virNetworkObjEndAPI(&obj); - return ret; -} - - static int networkBandwidthUpdate(virDomainNetDefPtr iface, virNetDevBandwidthPtr newBandwidth) @@ -5419,6 +5362,7 @@ networkBandwidthUpdate(virDomainNetDefPtr iface, unsigned long long tmp_floor_sum; virNetDevBandwidthPtr ifaceBand = virDomainNetGetActualBandwidth(iface); unsigned long long new_rate = 0; + unsigned long long old_floor, new_floor; int plug_ret; int ret = -1; @@ -5428,7 +5372,21 @@ networkBandwidthUpdate(virDomainNetDefPtr iface, return -1; } - if (!networkBandwidthGenericChecks(iface, newBandwidth)) + if (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_BRIDGE || + iface->data.network.actual->data.bridge.brname == NULL) { + /* This is not an interface that's plugged into a bridge. + * We don't care. Thus from our POV bandwidth change is allowed. */ + return 0; + } + + old_floor = new_floor = 0; + + if (ifaceBand && ifaceBand->in) + old_floor = ifaceBand->in->floor; + if (newBandwidth && newBandwidth->in) + new_floor = newBandwidth->in->floor; + + if (new_floor == old_floor) return 0; obj = virNetworkObjFindByName(driver->networks, iface->data.network.name); @@ -5574,7 +5532,6 @@ networkRegister(void) networkAllocateActualDevice, networkNotifyActualDevice, networkReleaseActualDevice, - networkBandwidthChangeAllowed, networkBandwidthUpdate); return 0; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 31d8647eee..6393a25b9a 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -11628,17 +11628,17 @@ qemuDomainSetInterfaceParameters(virDomainPtr dom, } if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK && - !virDomainNetBandwidthChangeAllowed(net, newBandwidth)) + virDomainNetBandwidthUpdate(net, newBandwidth) < 0) goto endjob; if (virNetDevBandwidthSet(net->ifname, newBandwidth, false, - !virDomainNetTypeSharesHostView(net)) < 0 || - (net->type == VIR_DOMAIN_NET_TYPE_NETWORK && - virDomainNetBandwidthUpdate(net, newBandwidth) < 0)) { + !virDomainNetTypeSharesHostView(net)) < 0) { ignore_value(virNetDevBandwidthSet(net->ifname, net->bandwidth, false, !virDomainNetTypeSharesHostView(net))); + ignore_value(virDomainNetBandwidthUpdate(net, + net->bandwidth)); goto endjob; } -- 2.20.1

Separate network port allocation code from the domain driver network callback implementation. Reviewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver.c | 143 +++++++++++++++++++----------------- 1 file changed, 77 insertions(+), 66 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index f8b58b3a25..ff5677f6a7 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4368,60 +4368,38 @@ networkLogAllocation(virNetworkDefPtr netdef, * "backend" function table. */ -/* networkAllocateActualDevice: - * @dom: domain definition that @iface belongs to - * @iface: the original NetDef from the domain +/* networkAllocatePort: + * @obj: the network to allocate from + * @port: the port definition to allocate * - * Looks up the network reference by iface, allocates a physical + * Looks up the network reference by port, allocates a physical * device from that network (if appropriate), and returns with the - * virDomainActualNetDef filled in accordingly. If there are no - * changes to be made in the netdef, then just leave the actualdef - * empty. + * port configuration filled in accordingly. * * Returns 0 on success, -1 on failure. */ static int -networkAllocateActualDevice(virNetworkPtr net, - virDomainDefPtr dom, - virDomainNetDefPtr iface) +networkAllocatePort(virNetworkObjPtr obj, + virNetworkPortDefPtr port) { virNetworkDriverStatePtr driver = networkGetDriver(); - virNetworkObjPtr obj = NULL; virNetworkDefPtr netdef = NULL; virPortGroupDefPtr portgroup = NULL; virNetworkForwardIfDefPtr dev = NULL; size_t i; int ret = -1; virNetDevVPortProfilePtr portprofile = NULL; - virNetworkPortDefPtr port = NULL; - - VIR_DEBUG("Allocating port from net %s", net->name); - obj = virNetworkObjFindByName(driver->networks, net->name); - if (!obj) { - virReportError(VIR_ERR_NO_NETWORK, - _("no network with matching name '%s'"), - net->name); - goto error; - } - - if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Expected an interface for a virtual network")); - goto error; - } netdef = virNetworkObjGetDef(obj); + VIR_DEBUG("Allocating port from net %s", netdef->name); if (!virNetworkObjIsActive(obj)) { virReportError(VIR_ERR_OPERATION_INVALID, _("network '%s' is not active"), netdef->name); - goto error; + goto cleanup; } - if (!(port = virDomainNetDefToNetworkPort(dom, iface))) - goto error; - VIR_DEBUG("Interface port group %s", port->group); /* portgroup can be present for any type of network, in particular * for bandwidth information, so we need to check for that and @@ -4433,7 +4411,7 @@ networkAllocateActualDevice(virNetworkPtr net, if (portgroup && portgroup->bandwidth && virNetDevBandwidthCopy(&port->bandwidth, portgroup->bandwidth) < 0) - goto error; + goto cleanup; } if (port->vlan.nTags == 0) { @@ -4444,7 +4422,7 @@ networkAllocateActualDevice(virNetworkPtr net, vlan = &netdef->vlan; if (vlan && virNetDevVlanCopy(&port->vlan, vlan) < 0) - goto error; + goto cleanup; } if (!port->trustGuestRxFilters) { @@ -4462,7 +4440,7 @@ networkAllocateActualDevice(virNetworkPtr net, netdef->virtPortProfile, portgroup ? portgroup->virtPortProfile : NULL) < 0) { - goto error; + goto cleanup; } if (portprofile) { VIR_FREE(port->virtPortProfile); @@ -4478,7 +4456,7 @@ networkAllocateActualDevice(virNetworkPtr net, port->plugtype = VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE; if (VIR_STRDUP(port->plug.bridge.brname, netdef->bridge) < 0) - goto error; + goto cleanup; port->plug.bridge.macTableManager = netdef->macTableManager; if (port->virtPortProfile) { @@ -4487,18 +4465,18 @@ networkAllocateActualDevice(virNetworkPtr net, "'%s' which uses IP forwarding"), virNetDevVPortTypeToString(port->virtPortProfile->virtPortType), netdef->name); - goto error; + goto cleanup; } if (networkPlugBandwidth(obj, &port->mac, port->bandwidth, &port->class_id) < 0) - goto error; + goto cleanup; break; case VIR_NETWORK_FORWARD_HOSTDEV: { port->plugtype = VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI; if (networkCreateInterfacePool(netdef) < 0) - goto error; + goto cleanup; /* pick first dev with 0 connections */ for (i = 0; i < netdef->forward.nifs; i++) { @@ -4512,7 +4490,7 @@ networkAllocateActualDevice(virNetworkPtr net, _("network '%s' requires exclusive access " "to interfaces, but none are available"), netdef->name); - goto error; + goto cleanup; } port->plug.hostdevpci.addr = dev->device.pci; port->plug.hostdevpci.driver = netdef->forward.driverName; @@ -4528,7 +4506,7 @@ networkAllocateActualDevice(virNetworkPtr net, "via PCI passthrough"), virNetDevVPortTypeToString(port->virtPortProfile->virtPortType), netdef->name); - goto error; + goto cleanup; } } break; @@ -4542,7 +4520,7 @@ networkAllocateActualDevice(virNetworkPtr net, port->plugtype = VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE; if (VIR_STRDUP(port->plug.bridge.brname, netdef->bridge) < 0) - goto error; + goto cleanup; port->plug.bridge.macTableManager = netdef->macTableManager; if (port->virtPortProfile) { @@ -4553,12 +4531,12 @@ networkAllocateActualDevice(virNetworkPtr net, "'%s' which uses a bridge device"), virNetDevVPortTypeToString(port->virtPortProfile->virtPortType), netdef->name); - goto error; + goto cleanup; } } if (networkPlugBandwidth(obj, &port->mac, port->bandwidth, &port->class_id) < 0) - goto error; + goto cleanup; break; } @@ -4592,7 +4570,7 @@ networkAllocateActualDevice(virNetworkPtr net, "'%s' which uses a macvtap device"), virNetDevVPortTypeToString(port->virtPortProfile->virtPortType), netdef->name); - goto error; + goto cleanup; } } @@ -4604,12 +4582,12 @@ networkAllocateActualDevice(virNetworkPtr net, _("network '%s' uses a direct mode, but " "has no forward dev and no interface pool"), netdef->name); - goto error; + goto cleanup; } else { /* pick an interface from the pool */ if (networkCreateInterfacePool(netdef) < 0) - goto error; + goto cleanup; /* PASSTHROUGH mode, and PRIVATE Mode + 802.1Qbh both * require exclusive access to a device, so current @@ -4644,26 +4622,26 @@ networkAllocateActualDevice(virNetworkPtr net, _("network '%s' requires exclusive access " "to interfaces, but none are available"), netdef->name); - goto error; + goto cleanup; } if (VIR_STRDUP(port->plug.direct.linkdev, dev->device.dev) < 0) - goto error; + goto cleanup; } break; case VIR_NETWORK_FORWARD_LAST: default: virReportEnumRangeError(virNetworkForwardType, netdef->forward.type); - goto error; + goto cleanup; } if (virNetworkObjMacMgrAdd(obj, driver->dnsmasqStateDir, - dom->name, &port->mac) < 0) - goto error; + port->ownername, &port->mac) < 0) + goto cleanup; if (virNetDevVPortProfileCheckComplete(port->virtPortProfile, true) < 0) - goto error; + goto cleanup; /* make sure that everything now specified for the device is * actually supported on this type of network. NB: network, @@ -4688,7 +4666,7 @@ networkAllocateActualDevice(virNetworkPtr net, "is requesting a vlan tag, but that is not " "supported for this type of network"), netdef->name); - goto error; + goto cleanup; } } @@ -4699,7 +4677,7 @@ networkAllocateActualDevice(virNetworkPtr net, virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("bandwidth settings are not supported " "for hostdev interfaces")); - goto error; + goto cleanup; } netdef->connections++; @@ -4713,28 +4691,61 @@ networkAllocateActualDevice(virNetworkPtr net, netdef->connections--; if (dev) dev->connections--; - goto error; + goto cleanup; } - networkLogAllocation(netdef, dev, &iface->mac, true); - - VIR_DEBUG("Populating net def"); - if (virDomainNetDefActualFromNetworkPort(iface, port) < 0) - goto error; + networkLogAllocation(netdef, dev, &port->mac, true); VIR_DEBUG("Port allocated"); - ret = 0; + ret = 0; cleanup: - virNetworkPortDefFree(port); - virNetworkObjEndAPI(&obj); return ret; +} - error: - if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + +static int +networkAllocateActualDevice(virNetworkPtr net, + virDomainDefPtr dom, + virDomainNetDefPtr iface) +{ + virNetworkDriverStatePtr driver = networkGetDriver(); + virNetworkPortDefPtr port = NULL; + virNetworkObjPtr obj; + int ret = -1; + + obj = virNetworkObjFindByName(driver->networks, net->name); + if (!obj) { + virReportError(VIR_ERR_NO_NETWORK, + _("no network with matching name '%s'"), + net->name); + return -1; + } + + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Expected an interface for a virtual network")); + goto cleanup; + } + + if (!(port = virDomainNetDefToNetworkPort(dom, iface))) + goto cleanup; + + if (networkAllocatePort(obj, port) < 0) + goto cleanup; + + VIR_DEBUG("Populating net def"); + if (virDomainNetDefActualFromNetworkPort(iface, port) < 0) + goto cleanup; + + ret = 0; + cleanup: + if (ret < 0) { virDomainActualNetDefFree(iface->data.network.actual); iface->data.network.actual = NULL; } - goto cleanup; + virNetworkPortDefFree(port); + virNetworkObjEndAPI(&obj); + return ret; } -- 2.20.1

Separate network port notification code from the domain driver network callback implementation. Reviewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver.c | 106 +++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 43 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index ff5677f6a7..b407c1a9e3 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4749,74 +4749,51 @@ networkAllocateActualDevice(virNetworkPtr net, } -/* networkNotifyActualDevice: - * @dom: domain definition that @iface belongs to - * @iface: the domain's NetDef with an "actual" device already filled in. +/* networkNotifyPort: + * @obj: the network to notify + * @port: the port definition to notify * * Called to notify the network driver when libvirtd is restarted and * finds an already running domain. If appropriate it will force an * allocation of the actual->direct.linkdev to get everything back in * order. - * - * Returns 0 on success, -1 on failure. */ static int -networkNotifyActualDevice(virNetworkPtr net, - virDomainDefPtr dom, - virDomainNetDefPtr iface) +networkNotifyPort(virNetworkObjPtr obj, + virNetworkPortDefPtr port) { - virNetworkDriverStatePtr driver = networkGetDriver(); - virNetworkObjPtr obj; virNetworkDefPtr netdef; virNetworkForwardIfDefPtr dev = NULL; - virNetworkPortDefPtr port = NULL; size_t i; int ret = -1; - obj = virNetworkObjFindByName(driver->networks, net->name); - if (!obj) { - virReportError(VIR_ERR_NO_NETWORK, - _("no network with matching name '%s'"), - net->name); - goto error; - } - - if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Expected an interface for a virtual network")); - goto error; - } - netdef = virNetworkObjGetDef(obj); if (!virNetworkObjIsActive(obj)) { virReportError(VIR_ERR_OPERATION_INVALID, _("network '%s' is not active"), netdef->name); - goto error; - } - - if (!(port = virDomainNetDefActualToNetworkPort(dom, iface))) goto cleanup; + } switch (port->plugtype) { case VIR_NETWORK_PORT_PLUG_TYPE_NONE: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unexpectedly got a network port without a plug")); - goto error; + goto cleanup; case VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE: /* see if we're connected to the correct bridge */ if (!netdef->bridge) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unexpectedly got a network port plugged into a bridge")); - goto error; + goto cleanup; } break; case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT: if (networkCreateInterfacePool(netdef) < 0) - goto error; + goto cleanup; /* find the matching interface and increment its connections */ for (i = 0; i < netdef->forward.nifs; i++) { @@ -4835,7 +4812,7 @@ networkNotifyActualDevice(virNetworkPtr net, "in use by network port '%s'"), netdef->name, port->plug.direct.linkdev, port->uuid); - goto error; + goto cleanup; } /* PASSTHROUGH mode and PRIVATE Mode + 802.1Qbh both require @@ -4851,14 +4828,14 @@ networkNotifyActualDevice(virNetworkPtr net, _("network '%s' claims dev='%s' is already in " "use by a different port"), netdef->name, port->plug.direct.linkdev); - goto error; + goto cleanup; } break; case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI: if (networkCreateInterfacePool(netdef) < 0) - goto error; + goto cleanup; /* find the matching interface and increment its connections */ for (i = 0; i < netdef->forward.nifs; i++) { @@ -4880,7 +4857,7 @@ networkNotifyActualDevice(virNetworkPtr net, port->plug.hostdevpci.addr.bus, port->plug.hostdevpci.addr.slot, port->plug.hostdevpci.addr.function); - goto error; + goto cleanup; } /* PASSTHROUGH mode, PRIVATE Mode + 802.1Qbh, and hostdev (PCI @@ -4896,7 +4873,7 @@ networkNotifyActualDevice(virNetworkPtr net, netdef->name, dev->device.pci.domain, dev->device.pci.bus, dev->device.pci.slot, dev->device.pci.function); - goto error; + goto cleanup; } break; @@ -4904,7 +4881,7 @@ networkNotifyActualDevice(virNetworkPtr net, case VIR_NETWORK_PORT_PLUG_TYPE_LAST: default: virReportEnumRangeError(virNetworkPortPlugType, port->plugtype); - goto error; + goto cleanup; } netdef->connections++; @@ -4917,18 +4894,61 @@ networkNotifyActualDevice(virNetworkPtr net, if (dev) dev->connections--; netdef->connections--; - goto error; + goto cleanup; } - networkLogAllocation(netdef, dev, &iface->mac, true); + networkLogAllocation(netdef, dev, &port->mac, true); + ret = 0; + cleanup: + return ret; +} + + +static int +networkNotifyActualDevice(virNetworkPtr net, + virDomainDefPtr dom, + virDomainNetDefPtr iface) +{ + virNetworkDriverStatePtr driver = networkGetDriver(); + virNetworkObjPtr obj; + virNetworkDefPtr netdef; + virNetworkPortDefPtr port = NULL; + int ret = -1; + + obj = virNetworkObjFindByName(driver->networks, net->name); + if (!obj) { + virReportError(VIR_ERR_NO_NETWORK, + _("no network with matching name '%s'"), + net->name); + goto cleanup; + } + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Expected an interface for a virtual network")); + goto cleanup; + } + + netdef = virNetworkObjGetDef(obj); + + if (!virNetworkObjIsActive(obj)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("network '%s' is not active"), + netdef->name); + goto cleanup; + } + + if (!(port = virDomainNetDefActualToNetworkPort(dom, iface))) + goto cleanup; + + if (networkNotifyPort(obj, port) < 0) + goto cleanup; + + ret = 0; cleanup: virNetworkObjEndAPI(&obj); virNetworkPortDefFree(port); return ret; - - error: - goto cleanup; } -- 2.20.1

Separate network port deletion code from the domain driver network callback implementation. Reivewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver.c | 90 ++++++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 31 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index b407c1a9e3..d55334d9ab 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4952,9 +4952,9 @@ networkNotifyActualDevice(virNetworkPtr net, } -/* networkReleaseActualDevice: - * @dom: domain definition that @iface belongs to - * @iface: a domain's NetDef (interface definition) +/* networkReleasePort: + * @obj: the network to release from + * @port: the port definition to release * * Given a domain <interface> element that previously had its <actual> * element filled in (and possibly a physical device allocated to it), @@ -4964,40 +4964,15 @@ networkNotifyActualDevice(virNetworkPtr net, * Returns 0 on success, -1 on failure. */ static int -networkReleaseActualDevice(virNetworkPtr net, - virDomainDefPtr dom, - virDomainNetDefPtr iface) +networkReleasePort(virNetworkObjPtr obj, + virNetworkPortDefPtr port) { virNetworkDriverStatePtr driver = networkGetDriver(); - virNetworkObjPtr obj; virNetworkDefPtr netdef; virNetworkForwardIfDefPtr dev = NULL; - virNetworkPortDefPtr port = NULL; size_t i; int ret = -1; - obj = virNetworkObjFindByName(driver->networks, net->name); - if (!obj) { - virReportError(VIR_ERR_NO_NETWORK, - _("no network with matching name '%s'"), - net->name); - goto cleanup; - } - - if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Expected an interface for a virtual network")); - goto cleanup; - } - - if (iface->data.network.actual == NULL) { - ret = 0; - goto cleanup; - } - - if (!(port = virDomainNetDefActualToNetworkPort(dom, iface))) - goto cleanup; - netdef = virNetworkObjGetDef(obj); switch ((virNetworkPortPlugType)port->plugtype) { @@ -5076,7 +5051,7 @@ networkReleaseActualDevice(virNetworkPtr net, goto cleanup; } - virNetworkObjMacMgrDel(obj, driver->dnsmasqStateDir, dom->name, &port->mac); + virNetworkObjMacMgrDel(obj, driver->dnsmasqStateDir, port->ownername, &port->mac); netdef->connections--; if (dev) @@ -5086,6 +5061,59 @@ networkReleaseActualDevice(virNetworkPtr net, VIR_HOOK_SUBOP_BEGIN); networkLogAllocation(netdef, dev, &port->mac, false); + ret = 0; + cleanup: + return ret; +} + + +/* networkReleaseActualDevice: + * @dom: domain definition that @iface belongs to + * @iface: a domain's NetDef (interface definition) + * + * Given a domain <interface> element that previously had its <actual> + * element filled in (and possibly a physical device allocated to it), + * free up the physical device for use by someone else, and free the + * virDomainActualNetDef. + * + * Returns 0 on success, -1 on failure. + */ +static int +networkReleaseActualDevice(virNetworkPtr net, + virDomainDefPtr dom, + virDomainNetDefPtr iface) +{ + virNetworkDriverStatePtr driver = networkGetDriver(); + virNetworkObjPtr obj; + virNetworkPortDefPtr port = NULL; + int ret = -1; + + obj = virNetworkObjFindByName(driver->networks, net->name); + if (!obj) { + virReportError(VIR_ERR_NO_NETWORK, + _("no network with matching name '%s'"), + net->name); + goto cleanup; + } + + + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Expected an interface for a virtual network")); + goto cleanup; + } + + if (iface->data.network.actual == NULL) { + ret = 0; + goto cleanup; + } + + if (!(port = virDomainNetDefActualToNetworkPort(dom, iface))) + goto cleanup; + + if (networkReleasePort(obj, port) < 0) + goto cleanup; + ret = 0; cleanup: virNetworkObjEndAPI(&obj); -- 2.20.1

Separate network port bandwidth update code from the domain driver network callback implementation. Reviewed-by: Laine Stump <laine@laine.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver.c | 115 ++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index d55334d9ab..27e70c984c 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -5412,77 +5412,56 @@ networkNetworkObjTaint(virNetworkObjPtr obj, static int -networkBandwidthUpdate(virDomainNetDefPtr iface, - virNetDevBandwidthPtr newBandwidth) +networkUpdatePortBandwidth(virNetworkObjPtr obj, + virMacAddrPtr mac, + unsigned int *class_id, + virNetDevBandwidthPtr oldBandwidth, + virNetDevBandwidthPtr newBandwidth) { virNetworkDriverStatePtr driver = networkGetDriver(); - virNetworkObjPtr obj = NULL; virNetworkDefPtr def; unsigned long long tmp_floor_sum; - virNetDevBandwidthPtr ifaceBand = virDomainNetGetActualBandwidth(iface); unsigned long long new_rate = 0; unsigned long long old_floor, new_floor; int plug_ret; - int ret = -1; - - if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Expected an interface for a virtual network")); - return -1; - } - - if (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_BRIDGE || - iface->data.network.actual->data.bridge.brname == NULL) { - /* This is not an interface that's plugged into a bridge. - * We don't care. Thus from our POV bandwidth change is allowed. */ - return 0; - } old_floor = new_floor = 0; - if (ifaceBand && ifaceBand->in) - old_floor = ifaceBand->in->floor; + if (oldBandwidth && oldBandwidth->in) + old_floor = oldBandwidth->in->floor; if (newBandwidth && newBandwidth->in) new_floor = newBandwidth->in->floor; if (new_floor == old_floor) return 0; - obj = virNetworkObjFindByName(driver->networks, iface->data.network.name); - if (!obj) { - virReportError(VIR_ERR_NO_NETWORK, - _("no network with matching name '%s'"), - iface->data.network.name); - return ret; - } def = virNetworkObjGetDef(obj); - if ((plug_ret = networkCheckBandwidth(obj, newBandwidth, ifaceBand, - &iface->mac, &new_rate)) < 0) { + if ((plug_ret = networkCheckBandwidth(obj, newBandwidth, oldBandwidth, + mac, &new_rate)) < 0) { /* helper reported error */ - goto cleanup; + return -1; } if (plug_ret > 0) { /* no QoS needs to be set; claim success */ - ret = 0; - goto cleanup; + return 0; } /* Okay, there are three possible scenarios: */ - if (ifaceBand && ifaceBand->in && ifaceBand->in->floor && + if (oldBandwidth && oldBandwidth->in && oldBandwidth->in->floor && newBandwidth->in && newBandwidth->in->floor) { /* Either we just need to update @floor .. */ if (virNetDevBandwidthUpdateRate(def->bridge, - iface->data.network.actual->class_id, + *class_id, def->bandwidth, newBandwidth->in->floor) < 0) - goto cleanup; + return -1; tmp_floor_sum = virNetworkObjGetFloorSum(obj); - tmp_floor_sum -= ifaceBand->in->floor; + tmp_floor_sum -= oldBandwidth->in->floor; tmp_floor_sum += newBandwidth->in->floor; virNetworkObjSetFloorSum(obj, tmp_floor_sum); new_rate -= tmp_floor_sum; @@ -5492,34 +5471,70 @@ networkBandwidthUpdate(virDomainNetDefPtr iface, virNetworkObjSaveStatus(driver->stateDir, obj) < 0) { /* Ouch, rollback */ tmp_floor_sum -= newBandwidth->in->floor; - tmp_floor_sum += ifaceBand->in->floor; + tmp_floor_sum += oldBandwidth->in->floor; virNetworkObjSetFloorSum(obj, tmp_floor_sum); ignore_value(virNetDevBandwidthUpdateRate(def->bridge, - iface->data.network.actual->class_id, + *class_id, def->bandwidth, - ifaceBand->in->floor)); - goto cleanup; + oldBandwidth->in->floor)); + return -1; } } else if (newBandwidth->in && newBandwidth->in->floor) { /* .. or we need to plug in new .. */ - if (networkPlugBandwidthImpl(obj, &iface->mac, newBandwidth, - iface->data.network.actual ? - &iface->data.network.actual->class_id : NULL, + if (networkPlugBandwidthImpl(obj, mac, newBandwidth, + class_id, new_rate) < 0) - goto cleanup; + return -1; } else { /* .. or unplug old. */ - if (networkUnplugBandwidth(obj, iface->bandwidth, - iface->data.network.actual ? - &iface->data.network.actual->class_id : NULL) < 0) - goto cleanup; + if (networkUnplugBandwidth(obj, oldBandwidth, class_id) < 0) + return -1; } - ret = 0; - cleanup: + return 0; +} + + +static int +networkBandwidthUpdate(virDomainNetDefPtr iface, + virNetDevBandwidthPtr newBandwidth) +{ + virNetworkDriverStatePtr driver = networkGetDriver(); + virNetworkObjPtr obj = NULL; + virNetDevBandwidthPtr oldBandwidth = virDomainNetGetActualBandwidth(iface); + int ret = -1; + + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Expected an interface for a virtual network")); + return -1; + } + + if (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_BRIDGE || + iface->data.network.actual->data.bridge.brname == NULL) { + /* This is not an interface that's plugged into a bridge. + * We don't care. Thus from our POV bandwidth change is allowed. */ + return 0; + } + + obj = virNetworkObjFindByName(driver->networks, iface->data.network.name); + if (!obj) { + virReportError(VIR_ERR_NO_NETWORK, + _("no network with matching name '%s'"), + iface->data.network.name); + return ret; + } + + ret = networkUpdatePortBandwidth(obj, + &iface->mac, + iface->data.network.actual ? + &iface->data.network.actual->class_id : NULL, + newBandwidth, + oldBandwidth); + virNetworkObjEndAPI(&obj); return ret; } -- 2.20.1

Introduce a new virNetworPort object that will present an attachment to a virtual network from a VM. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- include/libvirt/libvirt-network.h | 122 ++++++++ include/libvirt/virterror.h | 3 + src/datatypes.c | 60 ++++ src/datatypes.h | 41 +++ src/driver-network.h | 41 +++ src/libvirt-network.c | 444 ++++++++++++++++++++++++++++++ src/libvirt_private.syms | 2 + src/libvirt_public.syms | 15 + src/util/virerror.c | 9 + 9 files changed, 737 insertions(+) diff --git a/include/libvirt/libvirt-network.h b/include/libvirt/libvirt-network.h index 5115251fbe..97eceef754 100644 --- a/include/libvirt/libvirt-network.h +++ b/include/libvirt/libvirt-network.h @@ -46,6 +46,22 @@ typedef struct _virNetwork virNetwork; */ typedef virNetwork *virNetworkPtr; +/** + * virNetworkPort: + * + * a virNetworkPort is a private structure representing a virtual network + * port + */ +typedef struct _virNetworkPort virNetworkPort; + +/** + * virNetworkPortPtr: + * + * a virNetworkPortPtr is pointer to a virNetworkPort private structure, + * this is the type used to reference a virtual network port in the API. + */ +typedef virNetworkPort *virNetworkPortPtr; + /* * Get connection from network. */ @@ -333,4 +349,110 @@ int virConnectNetworkEventRegisterAny(virConnectPtr conn, int virConnectNetworkEventDeregisterAny(virConnectPtr conn, int callbackID); + +virNetworkPortPtr +virNetworkPortLookupByUUID(virNetworkPtr net, + const unsigned char *uuid); + +virNetworkPortPtr +virNetworkPortLookupByUUIDString(virNetworkPtr net, + const char *uuidstr); + +typedef enum { + VIR_NETWORK_PORT_CREATE_RECLAIM = (1 << 0), /* reclaim existing used resources */ +} virNetworkPortCreateFlags; + +virNetworkPortPtr +virNetworkPortCreateXML(virNetworkPtr net, + const char *xmldesc, + unsigned int flags); + +virNetworkPtr +virNetworkPortGetNetwork(virNetworkPortPtr port); + +char * +virNetworkPortGetXMLDesc(virNetworkPortPtr port, + unsigned int flags); + +int +virNetworkPortGetUUID(virNetworkPortPtr port, + unsigned char *uuid); +int +virNetworkPortGetUUIDString(virNetworkPortPtr port, + char *buf); + +/* Management of interface parameters */ + +/** + * VIR_NETWORK_PORT_BANDWIDTH_IN_AVERAGE: + * + * Macro represents the inbound average of NIC bandwidth, as a uint. + */ +# define VIR_NETWORK_PORT_BANDWIDTH_IN_AVERAGE "inbound.average" + +/** + * VIR_NETWORK_PORT_BANDWIDTH_IN_PEAK: + * + * Macro represents the inbound peak of NIC bandwidth, as a uint. + */ +# define VIR_NETWORK_PORT_BANDWIDTH_IN_PEAK "inbound.peak" + +/** + * VIR_NETWORK_PORT_BANDWIDTH_IN_BURST: + * + * Macro represents the inbound burst of NIC bandwidth, as a uint. + */ +# define VIR_NETWORK_PORT_BANDWIDTH_IN_BURST "inbound.burst" + +/** + * VIR_NETWORK_PORT_BANDWIDTH_IN_FLOOR: + * + * Macro represents the inbound floor of NIC bandwidth, as a uint. + */ +# define VIR_NETWORK_PORT_BANDWIDTH_IN_FLOOR "inbound.floor" + +/** + * VIR_NETWORK_PORT_BANDWIDTH_OUT_AVERAGE: + * + * Macro represents the outbound average of NIC bandwidth, as a uint. + */ +# define VIR_NETWORK_PORT_BANDWIDTH_OUT_AVERAGE "outbound.average" + +/** + * VIR_NETWORK_PORT_BANDWIDTH_OUT_PEAK: + * + * Macro represents the outbound peak of NIC bandwidth, as a uint. + */ +# define VIR_NETWORK_PORT_BANDWIDTH_OUT_PEAK "outbound.peak" + +/** + * VIR_NETWORK_PORT_BANDWIDTH_OUT_BURST: + * + * Macro represents the outbound burst of NIC bandwidth, as a uint. + */ +# define VIR_NETWORK_PORT_BANDWIDTH_OUT_BURST "outbound.burst" + +int +virNetworkPortSetParameters(virNetworkPortPtr port, + virTypedParameterPtr params, + int nparams, + unsigned int flags); +int +virNetworkPortGetParameters(virNetworkPortPtr port, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags); + +int +virNetworkPortDelete(virNetworkPortPtr port, + unsigned int flags); + +int +virNetworkListAllPorts(virNetworkPtr network, + virNetworkPortPtr **ports, + unsigned int flags); + +int +virNetworkPortFree(virNetworkPortPtr port); + #endif /* LIBVIRT_NETWORK_H */ diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 6dc83a17cc..102a2573bf 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -326,6 +326,9 @@ typedef enum { VIR_ERR_INVALID_DOMAIN_CHECKPOINT = 102, /* invalid domain checkpoint */ VIR_ERR_NO_DOMAIN_CHECKPOINT = 103, /* domain checkpoint not found */ VIR_ERR_NO_DOMAIN_BACKUP = 104, /* domain backup job id not found */ + VIR_ERR_INVALID_NETWORK_PORT = 105, /* invalid network port object */ + VIR_ERR_NETWORK_PORT_EXIST = 106, /* the network port already exist */ + VIR_ERR_NO_NETWORK_PORT = 107, /* network port not found */ # ifdef VIR_ENUM_SENTINELS VIR_ERR_NUMBER_LAST diff --git a/src/datatypes.c b/src/datatypes.c index 14ad5442fd..33c3f6be88 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -40,6 +40,7 @@ virClassPtr virDomainCheckpointClass; virClassPtr virDomainSnapshotClass; virClassPtr virInterfaceClass; virClassPtr virNetworkClass; +virClassPtr virNetworkPortClass; virClassPtr virNodeDeviceClass; virClassPtr virNWFilterClass; virClassPtr virNWFilterBindingClass; @@ -55,6 +56,7 @@ static void virDomainCheckpointDispose(void *obj); static void virDomainSnapshotDispose(void *obj); static void virInterfaceDispose(void *obj); static void virNetworkDispose(void *obj); +static void virNetworkPortDispose(void *obj); static void virNodeDeviceDispose(void *obj); static void virNWFilterDispose(void *obj); static void virNWFilterBindingDispose(void *obj); @@ -92,6 +94,7 @@ virDataTypesOnceInit(void) DECLARE_CLASS(virDomainSnapshot); DECLARE_CLASS(virInterface); DECLARE_CLASS(virNetwork); + DECLARE_CLASS(virNetworkPort); DECLARE_CLASS(virNodeDevice); DECLARE_CLASS(virNWFilter); DECLARE_CLASS(virNWFilterBinding); @@ -388,6 +391,63 @@ virNetworkDispose(void *obj) } +/** + * virGetNetworkPort: + * @net: the network object + * @uuid: pointer to the uuid + * + * Allocates a new network port object. When the object is no longer needed, + * virObjectUnref() must be called in order to not leak data. + * + * Returns a pointer to the network port object, or NULL on error. + */ +virNetworkPortPtr +virGetNetworkPort(virNetworkPtr net, const unsigned char *uuid) +{ + virNetworkPortPtr ret = NULL; + + if (virDataTypesInitialize() < 0) + return NULL; + + virCheckNetworkGoto(net, error); + virCheckNonNullArgGoto(uuid, error); + + if (!(ret = virObjectNew(virNetworkPortClass))) + goto error; + + ret->net = virObjectRef(net); + memcpy(&(ret->uuid[0]), uuid, VIR_UUID_BUFLEN); + + return ret; + + error: + virObjectUnref(ret); + return NULL; +} + +/** + * virNetworkPortDispose: + * @obj: the network port to release + * + * Unconditionally release all memory associated with a network port. + * The network port object must not be used once this method returns. + * + * It will also unreference the associated network object, + * which may also be released if its ref count hits zero. + */ +static void +virNetworkPortDispose(void *obj) +{ + virNetworkPortPtr port = obj; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + virUUIDFormat(port->uuid, uuidstr); + VIR_DEBUG("release network port %p %s", port, uuidstr); + + virObjectUnref(port->net); +} + + /** * virGetInterface: * @conn: the hypervisor connection diff --git a/src/datatypes.h b/src/datatypes.h index 87f3d5e095..428b166b2b 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -35,6 +35,7 @@ extern virClassPtr virDomainCheckpointClass; extern virClassPtr virDomainSnapshotClass; extern virClassPtr virInterfaceClass; extern virClassPtr virNetworkClass; +extern virClassPtr virNetworkPortClass; extern virClassPtr virNodeDeviceClass; extern virClassPtr virNWFilterClass; extern virClassPtr virNWFilterBindingClass; @@ -117,6 +118,33 @@ extern virClassPtr virAdmClientClass; } \ } while (0) +# define virCheckNetworkPortReturn(obj, retval) \ + do { \ + virNetworkPortPtr _port = (obj); \ + if (!virObjectIsClass(_port, virNetworkPortClass) || \ + !virObjectIsClass(_port->net, virNetworkClass)) { \ + virReportErrorHelper(VIR_FROM_NETWORK, \ + VIR_ERR_INVALID_NETWORK_PORT, \ + __FILE__, __FUNCTION__, __LINE__, \ + __FUNCTION__); \ + virDispatchError(NULL); \ + return retval; \ + } \ + } while (0) + +# define virCheckNetworkPortGoto(obj, label) \ + do { \ + virNetworkPortPtr _port = (obj); \ + if (!virObjectIsClass(_port, virNetworkPortClass) || \ + !virObjectIsClass(_port->net, virNetworkClass)) { \ + virReportErrorHelper(VIR_FROM_NETWORK, \ + VIR_ERR_INVALID_NETWORK_PORT, \ + __FILE__, __FUNCTION__, __LINE__, \ + __FUNCTION__); \ + goto label; \ + } \ + } while (0) + # define virCheckInterfaceReturn(obj, retval) \ do { \ virInterfacePtr _iface = (obj); \ @@ -589,6 +617,17 @@ struct _virNetwork { unsigned char uuid[VIR_UUID_BUFLEN]; /* the network unique identifier */ }; +/** +* _virNetworkPort: +* +* Internal structure associated to a network port +*/ +struct _virNetworkPort { + virObject parent; + virNetworkPtr net; /* pointer back to the connection */ + unsigned char uuid[VIR_UUID_BUFLEN]; /* the network unique identifier */ +}; + /** * _virInterface: * @@ -746,6 +785,8 @@ virDomainPtr virGetDomain(virConnectPtr conn, virNetworkPtr virGetNetwork(virConnectPtr conn, const char *name, const unsigned char *uuid); +virNetworkPortPtr virGetNetworkPort(virNetworkPtr net, + const unsigned char *uuid); virInterfacePtr virGetInterface(virConnectPtr conn, const char *name, const char *mac); diff --git a/src/driver-network.h b/src/driver-network.h index 2715c7bfec..a33407436f 100644 --- a/src/driver-network.h +++ b/src/driver-network.h @@ -118,6 +118,40 @@ typedef int virNetworkDHCPLeasePtr **leases, unsigned int flags); +typedef virNetworkPortPtr +(*virDrvNetworkPortLookupByUUID)(virNetworkPtr net, + const unsigned char *uuid); + +typedef virNetworkPortPtr +(*virDrvNetworkPortCreateXML)(virNetworkPtr net, + const char *xmldesc, + unsigned int flags); + +typedef int +(*virDrvNetworkPortSetParameters)(virNetworkPortPtr port, + virTypedParameterPtr params, + int nparams, + unsigned int flags); + +typedef int +(*virDrvNetworkPortGetParameters)(virNetworkPortPtr port, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags); + +typedef char * +(*virDrvNetworkPortGetXMLDesc)(virNetworkPortPtr port, + unsigned int flags); + +typedef int +(*virDrvNetworkPortDelete)(virNetworkPortPtr port, + unsigned int flags); + +typedef int +(*virDrvNetworkListAllPorts)(virNetworkPtr network, + virNetworkPortPtr **ports, + unsigned int flags); + typedef struct _virNetworkDriver virNetworkDriver; typedef virNetworkDriver *virNetworkDriverPtr; @@ -151,6 +185,13 @@ struct _virNetworkDriver { virDrvNetworkIsActive networkIsActive; virDrvNetworkIsPersistent networkIsPersistent; virDrvNetworkGetDHCPLeases networkGetDHCPLeases; + virDrvNetworkPortLookupByUUID networkPortLookupByUUID; + virDrvNetworkPortCreateXML networkPortCreateXML; + virDrvNetworkPortGetXMLDesc networkPortGetXMLDesc; + virDrvNetworkPortSetParameters networkPortSetParameters; + virDrvNetworkPortGetParameters networkPortGetParameters; + virDrvNetworkPortDelete networkPortDelete; + virDrvNetworkListAllPorts networkListAllPorts; }; diff --git a/src/libvirt-network.c b/src/libvirt-network.c index d46a7d5c47..6ed32c8ba2 100644 --- a/src/libvirt-network.c +++ b/src/libvirt-network.c @@ -23,6 +23,7 @@ #include "datatypes.h" #include "viralloc.h" #include "virlog.h" +#include "virtypedparam.h" VIR_LOG_INIT("libvirt.network"); @@ -1246,3 +1247,446 @@ virNetworkDHCPLeaseFree(virNetworkDHCPLeasePtr lease) VIR_FREE(lease->clientid); VIR_FREE(lease); } + + +/** + * virNetworkPortLookupByUUID: + * @net: pointer to the network object + * @uuid: the raw UUID for the network port + * + * Try to lookup a port on the given network based on its UUID. + * + * virNetworkPortFree should be used to free the resources after the + * network port object is no longer needed. + * + * Returns a new network port object or NULL in case of failure. If the + * network port cannot be found, then VIR_ERR_NO_NETWORK_PORT error is raised. + */ +virNetworkPortPtr +virNetworkPortLookupByUUID(virNetworkPtr net, + const unsigned char *uuid) +{ + VIR_UUID_DEBUG(net, uuid); + + virResetLastError(); + + virCheckNetworkReturn(net, NULL); + virCheckNonNullArgGoto(uuid, error); + + if (net->conn->networkDriver && net->conn->networkDriver->networkPortLookupByUUID) { + virNetworkPortPtr ret; + ret = net->conn->networkDriver->networkPortLookupByUUID(net, uuid); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(net->conn); + return NULL; +} + + +/** + * virNetworkPortLookupByUUIDString: + * @net: pointer to the network object + * @uuidstr: the string UUID for the port + * + * Try to lookup a port on the given network based on its UUID. + * + * Returns a new network port object or NULL in case of failure. If the + * network port cannot be found, then VIR_ERR_NO_NETWORK_PORT error is raised. + */ +virNetworkPortPtr +virNetworkPortLookupByUUIDString(virNetworkPtr net, + const char *uuidstr) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + VIR_DEBUG("net=%p, uuidstr=%s", net, NULLSTR(uuidstr)); + + virResetLastError(); + + virCheckNetworkReturn(net, NULL); + virCheckNonNullArgGoto(uuidstr, error); + + if (virUUIDParse(uuidstr, uuid) < 0) { + virReportInvalidArg(uuidstr, + _("uuidstr in %s must be a valid UUID"), + __FUNCTION__); + goto error; + } + + return virNetworkPortLookupByUUID(net, &uuid[0]); + + error: + virDispatchError(net->conn); + return NULL; +} + + +/** + * virNetworkPortSetParameters: + * @port: a network port object + * @params: pointer to interface parameter objects + * @nparams: number of interface parameter (this value can be the same or + * less than the number of parameters supported) + * @flags: currently unused, pass 0 + * + * Change a subset or all parameters of the network port; currently this + * includes bandwidth parameters. + * + * Returns -1 in case of error, 0 in case of success. + */ +int +virNetworkPortSetParameters(virNetworkPortPtr port, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("port=%p, params=%p, nparams=%d, flags=0x%x", port, params, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + virCheckNetworkPortReturn(port, -1); + conn = port->net->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + if (conn->networkDriver && conn->networkDriver->networkPortSetParameters) { + int ret; + ret = conn->networkDriver->networkPortSetParameters(port, params, nparams, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return -1; +} + + +/** + * virNetworkPortGetParameters: + * @port: a network port object + * @params: pointer to pointer of interface parameter objects + * @nparams: pointer to received number of interface parameter + * @flags: currently unused, pass 0 + * + * Get all interface parameters. On input, @params should be initialized + * to NULL. On return @params will be allocated with the size large + * enough to hold all parameters, and @nparams will be updated to say + * how many parameters are present. @params should be freed by the caller + * on success. + * + * Returns -1 in case of error, 0 in case of success. + */ +int +virNetworkPortGetParameters(virNetworkPortPtr port, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("port=%p, params=%p, nparams=%p, flags=0x%x", port, params, nparams, flags); + + virResetLastError(); + + virCheckNetworkPortReturn(port, -1); + conn = port->net->conn; + + if (conn->networkDriver && conn->networkDriver->networkPortGetParameters) { + int ret; + ret = conn->networkDriver->networkPortGetParameters(port, params, nparams, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return -1; +} + + +/** + * virNetworkPortCreateXML: + * @net: pointer to the network object + * @xmldesc: an XML description of the port + * @flags: currently unused, pass 0 + * + * Create a new network port, based on an XML description + * similar to the one returned by virNetworkPortGetXMLDesc() + * + * virNetworkPortFree should be used to free the resources after the + * network port object is no longer needed. + * + * Returns a new network port object or NULL in case of failure + */ +virNetworkPortPtr +virNetworkPortCreateXML(virNetworkPtr net, + const char *xmldesc, + unsigned int flags) +{ + VIR_DEBUG("net=%p, xmldesc=%s, flags=0x%x", net, NULLSTR(xmldesc), flags); + + virResetLastError(); + + virCheckNetworkReturn(net, NULL); + virCheckNonNullArgGoto(xmldesc, error); + virCheckReadOnlyGoto(net->conn->flags, error); + + if (net->conn->networkDriver && net->conn->networkDriver->networkPortCreateXML) { + virNetworkPortPtr ret; + ret = net->conn->networkDriver->networkPortCreateXML(net, xmldesc, flags); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(net->conn); + return NULL; +} + +/** + * virNetworkPortGetNetwork: + * @port: pointer to a network port + * + * Provides the network pointer associated with a port. The + * reference counter on the connection is not increased by this + * call. + * + * Returns the virNetworkPtr or NULL in case of failure. + */ +virNetworkPtr +virNetworkPortGetNetwork(virNetworkPortPtr port) +{ + VIR_DEBUG("port=%p", port); + + virResetLastError(); + + virCheckNetworkPortReturn(port, NULL); + + return port->net; +} + + +/** + * virNetworkPortGetXMLDesc: + * @port: a network port object + * @flags: currently unused, pass 0 + * + * Provide an XML description of the network port. The description may be reused + * later to recreate the port with virNetworkPortCreateXML(). + * + * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case of error. + * the caller must free() the returned value. + */ +char * +virNetworkPortGetXMLDesc(virNetworkPortPtr port, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("port=%p, flags=0x%x", port, flags); + + virResetLastError(); + + virCheckNetworkPortReturn(port, NULL); + conn = port->net->conn; + + if (conn->networkDriver && conn->networkDriver->networkPortGetXMLDesc) { + char *ret; + ret = conn->networkDriver->networkPortGetXMLDesc(port, flags); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return NULL; +} + + +/** + * virNetworkPortGetUUID: + * @port: a network port object + * @uuid: pointer to a VIR_UUID_BUFLEN bytes array + * + * Get the UUID for a network port + * + * Returns -1 in case of error, 0 in case of success + */ +int +virNetworkPortGetUUID(virNetworkPortPtr port, + unsigned char *uuid) +{ + VIR_DEBUG("port=%p, uuid=%p", port, uuid); + + virResetLastError(); + + virCheckNetworkPortReturn(port, -1); + virCheckNonNullArgGoto(uuid, error); + + memcpy(uuid, &port->uuid[0], VIR_UUID_BUFLEN); + + return 0; + + error: + virDispatchError(port->net->conn); + return -1; +} + + +/** + * virNetworkPortGetUUIDString: + * @port: a network port object + * @buf: pointer to a VIR_UUID_STRING_BUFLEN bytes array + * + * Get the UUID for a network as string. For more information about + * UUID see RFC4122. + * + * Returns -1 in case of error, 0 in case of success + */ +int +virNetworkPortGetUUIDString(virNetworkPortPtr port, + char *buf) +{ + VIR_DEBUG("port=%p, buf=%p", port, buf); + + virResetLastError(); + + virCheckNetworkPortReturn(port, -1); + virCheckNonNullArgGoto(buf, error); + + virUUIDFormat(port->uuid, buf); + return 0; + + error: + virDispatchError(port->net->conn); + return -1; +} + +/** + * virNetworkPortDelete: + * @port: a port object + * @flags: currently unused, pass 0 + * + * Delete the network port. This does not free the + * associated virNetworkPortPtr object. It is the + * caller's responsibility to ensure the port is not + * still in use by a virtual machine before deleting + * port. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virNetworkPortDelete(virNetworkPortPtr port, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("port=%p, flags=0x%x", port, flags); + + virResetLastError(); + + virCheckNetworkPortReturn(port, -1); + conn = port->net->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + if (conn->networkDriver && conn->networkDriver->networkPortDelete) { + int ret; + ret = conn->networkDriver->networkPortDelete(port, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return -1; +} + + +/** + * virNetworkListAllPorts: + * @network: pointer to a network object + * @ports: Pointer to a variable to store the array containing network port + * objects or NULL if the list is not required (just returns number + * of ports). + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Collect the list of network ports, and allocate an array to store those + * objects. + * + * Returns the number of network ports found or -1 and sets @ports to + * NULL in case of error. On success, the array stored into @ports is + * guaranteed to have an extra allocated element set to NULL but not included + * in the return count, to make iteration easier. The caller is responsible + * for calling virNetworkPortFree() on each array element, then calling + * free() on @ports. + */ +int +virNetworkListAllPorts(virNetworkPtr network, + virNetworkPortPtr **ports, + unsigned int flags) +{ + VIR_DEBUG("network=%p, ports=%p, flags=0x%x", network, ports, flags); + + virResetLastError(); + + virCheckNetworkReturn(network, -1); + + if (network->conn->networkDriver && + network->conn->networkDriver->networkListAllPorts) { + int ret; + ret = network->conn->networkDriver->networkListAllPorts(network, ports, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(network->conn); + return -1; +} + + +/** + * virNetworkPortFree: + * @port: a network port object + * + * Free the network port object. + * The data structure is freed and should not be used thereafter. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virNetworkPortFree(virNetworkPortPtr port) +{ + VIR_DEBUG("port=%p", port); + + virResetLastError(); + + virCheckNetworkPortReturn(port, -1); + + virObjectUnref(port); + return 0; +} diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 29250cdb75..bcd49904b2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1278,6 +1278,7 @@ virGetDomainCheckpoint; virGetDomainSnapshot; virGetInterface; virGetNetwork; +virGetNetworkPort; virGetNodeDevice; virGetNWFilter; virGetNWFilterBinding; @@ -1287,6 +1288,7 @@ virGetStorageVol; virGetStream; virInterfaceClass; virNetworkClass; +virNetworkPortClass; virNewConnectCloseCallbackData; virNodeDeviceClass; virNWFilterClass; diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index dbce3336d5..c629528fbd 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -819,4 +819,19 @@ LIBVIRT_5.2.0 { virConnectGetStoragePoolCapabilities; } LIBVIRT_4.10.0; +LIBVIRT_5.3.0 { + virNetworkListAllPorts; + virNetworkPortLookupByUUID; + virNetworkPortLookupByUUIDString; + virNetworkPortCreateXML; + virNetworkPortGetNetwork; + virNetworkPortGetParameters; + virNetworkPortGetXMLDesc; + virNetworkPortGetUUID; + virNetworkPortGetUUIDString; + virNetworkPortDelete; + virNetworkPortFree; + virNetworkPortSetParameters; +} LIBVIRT_5.2.0; + # .... define new API here using predicted next version number .... diff --git a/src/util/virerror.c b/src/util/virerror.c index 37b5b2f3f9..67e2bc984e 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -1226,6 +1226,15 @@ const virErrorMsgTuple virErrorMsgStrings[VIR_ERR_NUMBER_LAST] = { [VIR_ERR_NO_DOMAIN_BACKUP] = { N_("Domain backup job id not found"), N_("Domain backup job id not found: %s") }, + [VIR_ERR_INVALID_NETWORK_PORT] = { + N_("Invalid network port pointer"), + N_("Invalid network port pointer: %s") }, + [VIR_ERR_NETWORK_PORT_EXIST] = { + N_("this network port exists already"), + N_("network port %s exists already") }, + [VIR_ERR_NO_NETWORK_PORT] = { + N_("network port not found"), + N_("network port not found: %s") }, }; -- 2.20.1

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/access/genpolkit.pl | 2 +- src/access/viraccessdriver.h | 6 ++++ src/access/viraccessdrivernop.c | 11 ++++++++ src/access/viraccessdriverpolkit.c | 26 ++++++++++++++++++ src/access/viraccessdriverstack.c | 25 +++++++++++++++++ src/access/viraccessmanager.c | 16 +++++++++++ src/access/viraccessmanager.h | 6 ++++ src/access/viraccessperm.c | 6 ++++ src/access/viraccessperm.h | 44 ++++++++++++++++++++++++++++++ 9 files changed, 141 insertions(+), 1 deletion(-) diff --git a/src/access/genpolkit.pl b/src/access/genpolkit.pl index e074c90eb6..f8f20caf65 100755 --- a/src/access/genpolkit.pl +++ b/src/access/genpolkit.pl @@ -21,7 +21,7 @@ use strict; use warnings; my @objects = ( - "CONNECT", "DOMAIN", "INTERFACE", + "CONNECT", "DOMAIN", "INTERFACE", "NETWORK_PORT", "NETWORK","NODE_DEVICE", "NWFILTER_BINDING", "NWFILTER", "SECRET", "STORAGE_POOL", "STORAGE_VOL", ); diff --git a/src/access/viraccessdriver.h b/src/access/viraccessdriver.h index 2cc3950f60..590d86fdf0 100644 --- a/src/access/viraccessdriver.h +++ b/src/access/viraccessdriver.h @@ -39,6 +39,11 @@ typedef int (*virAccessDriverCheckNetworkDrv)(virAccessManagerPtr manager, const char *driverName, virNetworkDefPtr network, virAccessPermNetwork av); +typedef int (*virAccessDriverCheckNetworkPortDrv)(virAccessManagerPtr manager, + const char *driverName, + virNetworkDefPtr network, + virNetworkPortDefPtr port, + virAccessPermNetworkPort av); typedef int (*virAccessDriverCheckNodeDeviceDrv)(virAccessManagerPtr manager, const char *driverName, virNodeDeviceDefPtr nodedev, @@ -82,6 +87,7 @@ struct _virAccessDriver { virAccessDriverCheckDomainDrv checkDomain; virAccessDriverCheckInterfaceDrv checkInterface; virAccessDriverCheckNetworkDrv checkNetwork; + virAccessDriverCheckNetworkPortDrv checkNetworkPort; virAccessDriverCheckNodeDeviceDrv checkNodeDevice; virAccessDriverCheckNWFilterDrv checkNWFilter; virAccessDriverCheckNWFilterBindingDrv checkNWFilterBinding; diff --git a/src/access/viraccessdrivernop.c b/src/access/viraccessdrivernop.c index 98ef9206c5..5e9d9db759 100644 --- a/src/access/viraccessdrivernop.c +++ b/src/access/viraccessdrivernop.c @@ -57,6 +57,16 @@ virAccessDriverNopCheckNetwork(virAccessManagerPtr manager ATTRIBUTE_UNUSED, return 1; /* Allow */ } +static int +virAccessDriverNopCheckNetworkPort(virAccessManagerPtr manager ATTRIBUTE_UNUSED, + const char *driverName ATTRIBUTE_UNUSED, + virNetworkDefPtr network ATTRIBUTE_UNUSED, + virNetworkPortDefPtr port ATTRIBUTE_UNUSED, + virAccessPermNetworkPort perm ATTRIBUTE_UNUSED) +{ + return 1; /* Allow */ +} + static int virAccessDriverNopCheckNodeDevice(virAccessManagerPtr manager ATTRIBUTE_UNUSED, const char *driverName ATTRIBUTE_UNUSED, @@ -119,6 +129,7 @@ virAccessDriver accessDriverNop = { .checkDomain = virAccessDriverNopCheckDomain, .checkInterface = virAccessDriverNopCheckInterface, .checkNetwork = virAccessDriverNopCheckNetwork, + .checkNetworkPort = virAccessDriverNopCheckNetworkPort, .checkNodeDevice = virAccessDriverNopCheckNodeDevice, .checkNWFilter = virAccessDriverNopCheckNWFilter, .checkNWFilterBinding = virAccessDriverNopCheckNWFilterBinding, diff --git a/src/access/viraccessdriverpolkit.c b/src/access/viraccessdriverpolkit.c index 6954d74a15..b1473cd0a4 100644 --- a/src/access/viraccessdriverpolkit.c +++ b/src/access/viraccessdriverpolkit.c @@ -237,6 +237,31 @@ virAccessDriverPolkitCheckNetwork(virAccessManagerPtr manager, attrs); } +static int +virAccessDriverPolkitCheckNetworkPort(virAccessManagerPtr manager, + const char *driverName, + virNetworkDefPtr network, + virNetworkPortDefPtr port, + virAccessPermNetworkPort perm) +{ + char uuidstr1[VIR_UUID_STRING_BUFLEN]; + char uuidstr2[VIR_UUID_STRING_BUFLEN]; + const char *attrs[] = { + "connect_driver", driverName, + "network_name", network->name, + "network_uuid", uuidstr1, + "port_uuid", uuidstr2, + NULL, + }; + virUUIDFormat(network->uuid, uuidstr1); + virUUIDFormat(port->uuid, uuidstr2); + + return virAccessDriverPolkitCheck(manager, + "network-port", + virAccessPermNetworkPortTypeToString(perm), + attrs); +} + static int virAccessDriverPolkitCheckNodeDevice(virAccessManagerPtr manager, const char *driverName, @@ -427,6 +452,7 @@ virAccessDriver accessDriverPolkit = { .checkDomain = virAccessDriverPolkitCheckDomain, .checkInterface = virAccessDriverPolkitCheckInterface, .checkNetwork = virAccessDriverPolkitCheckNetwork, + .checkNetworkPort = virAccessDriverPolkitCheckNetworkPort, .checkNodeDevice = virAccessDriverPolkitCheckNodeDevice, .checkNWFilter = virAccessDriverPolkitCheckNWFilter, .checkNWFilterBinding = virAccessDriverPolkitCheckNWFilterBinding, diff --git a/src/access/viraccessdriverstack.c b/src/access/viraccessdriverstack.c index 0ffc6abaf3..238caef115 100644 --- a/src/access/viraccessdriverstack.c +++ b/src/access/viraccessdriverstack.c @@ -151,6 +151,30 @@ virAccessDriverStackCheckNetwork(virAccessManagerPtr manager, return ret; } +static int +virAccessDriverStackCheckNetworkPort(virAccessManagerPtr manager, + const char *driverName, + virNetworkDefPtr network, + virNetworkPortDefPtr port, + virAccessPermNetworkPort perm) +{ + virAccessDriverStackPrivatePtr priv = virAccessManagerGetPrivateData(manager); + int ret = 1; + size_t i; + + for (i = 0; i < priv->managersLen; i++) { + int rv; + /* We do not short-circuit on first denial - always check all drivers */ + rv = virAccessManagerCheckNetworkPort(priv->managers[i], driverName, network, port, perm); + if (rv == 0 && ret != -1) + ret = 0; + else if (rv < 0) + ret = -1; + } + + return ret; +} + static int virAccessDriverStackCheckNodeDevice(virAccessManagerPtr manager, const char *driverName, @@ -298,6 +322,7 @@ virAccessDriver accessDriverStack = { .checkDomain = virAccessDriverStackCheckDomain, .checkInterface = virAccessDriverStackCheckInterface, .checkNetwork = virAccessDriverStackCheckNetwork, + .checkNetworkPort = virAccessDriverStackCheckNetworkPort, .checkNodeDevice = virAccessDriverStackCheckNodeDevice, .checkNWFilter = virAccessDriverStackCheckNWFilter, .checkNWFilterBinding = virAccessDriverStackCheckNWFilterBinding, diff --git a/src/access/viraccessmanager.c b/src/access/viraccessmanager.c index f5d62604cf..24d9713cfd 100644 --- a/src/access/viraccessmanager.c +++ b/src/access/viraccessmanager.c @@ -268,6 +268,22 @@ int virAccessManagerCheckNetwork(virAccessManagerPtr manager, return virAccessManagerSanitizeError(ret, driverName); } +int virAccessManagerCheckNetworkPort(virAccessManagerPtr manager, + const char *driverName, + virNetworkDefPtr network, + virNetworkPortDefPtr port, + virAccessPermNetworkPort perm) +{ + int ret = 0; + VIR_DEBUG("manager=%p(name=%s) driver=%s network=%p port=%p perm=%d", + manager, manager->drv->name, driverName, network, port, perm); + + if (manager->drv->checkNetworkPort) + ret = manager->drv->checkNetworkPort(manager, driverName, network, port, perm); + + return virAccessManagerSanitizeError(ret, driverName); +} + int virAccessManagerCheckNodeDevice(virAccessManagerPtr manager, const char *driverName, virNodeDeviceDefPtr nodedev, diff --git a/src/access/viraccessmanager.h b/src/access/viraccessmanager.h index ab5ef87585..bedd6ba475 100644 --- a/src/access/viraccessmanager.h +++ b/src/access/viraccessmanager.h @@ -30,6 +30,7 @@ # include "conf/secret_conf.h" # include "conf/interface_conf.h" # include "conf/virnwfilterbindingdef.h" +# include "conf/virnetworkportdef.h" # include "access/viraccessperm.h" typedef struct _virAccessManager virAccessManager; @@ -66,6 +67,11 @@ int virAccessManagerCheckNetwork(virAccessManagerPtr manager, const char *driverName, virNetworkDefPtr network, virAccessPermNetwork perm); +int virAccessManagerCheckNetworkPort(virAccessManagerPtr manager, + const char *driverName, + virNetworkDefPtr network, + virNetworkPortDefPtr port, + virAccessPermNetworkPort perm); int virAccessManagerCheckNodeDevice(virAccessManagerPtr manager, const char *driverName, virNodeDeviceDefPtr nodedev, diff --git a/src/access/viraccessperm.c b/src/access/viraccessperm.c index 67f751ef9c..74993e9f29 100644 --- a/src/access/viraccessperm.c +++ b/src/access/viraccessperm.c @@ -57,6 +57,12 @@ VIR_ENUM_IMPL(virAccessPermNetwork, VIR_ACCESS_PERM_NETWORK_LAST, "getattr", "read", "write", "save", "delete", "start", "stop", + "search_ports", +); + +VIR_ENUM_IMPL(virAccessPermNetworkPort, + VIR_ACCESS_PERM_NETWORK_PORT_LAST, + "getattr", "read", "write", "create", "delete", ); VIR_ENUM_IMPL(virAccessPermNodeDevice, diff --git a/src/access/viraccessperm.h b/src/access/viraccessperm.h index ed1f7168ca..0fe618328b 100644 --- a/src/access/viraccessperm.h +++ b/src/access/viraccessperm.h @@ -405,6 +405,12 @@ typedef enum { */ VIR_ACCESS_PERM_NETWORK_START, + /** + * @desc: List network ports + * @message: Listing network ports requires authorization + */ + VIR_ACCESS_PERM_NETWORK_SEARCH_PORTS, + /** * @desc: Stop network * @message: Stopping network requires authorization @@ -414,6 +420,43 @@ typedef enum { VIR_ACCESS_PERM_NETWORK_LAST } virAccessPermNetwork; +typedef enum { + + /** + * @desc: Access network port + * @message: Accessing network port requires authorization + * @anonymous: 1 + */ + VIR_ACCESS_PERM_NETWORK_PORT_GETATTR, + + /** + * @desc: Read network port + * @message: Reading network port configuration requires authorization + * @anonymous: 1 + */ + VIR_ACCESS_PERM_NETWORK_PORT_READ, + + /** + * @desc: Read network port + * @message: Writing network port configuration requires authorization + */ + VIR_ACCESS_PERM_NETWORK_PORT_WRITE, + + /** + * @desc: Create network port + * @message: Creating network port configuration requires authorization + */ + VIR_ACCESS_PERM_NETWORK_PORT_CREATE, + + /** + * @desc: Delete network port + * @message: Deleting network port configuration requires authorization + */ + VIR_ACCESS_PERM_NETWORK_PORT_DELETE, + + VIR_ACCESS_PERM_NETWORK_PORT_LAST +} virAccessPermNetworkPort; + typedef enum { /** @@ -693,6 +736,7 @@ VIR_ENUM_DECL(virAccessPermConnect); VIR_ENUM_DECL(virAccessPermDomain); VIR_ENUM_DECL(virAccessPermInterface); VIR_ENUM_DECL(virAccessPermNetwork); +VIR_ENUM_DECL(virAccessPermNetworkPort); VIR_ENUM_DECL(virAccessPermNodeDevice); VIR_ENUM_DECL(virAccessPermNWFilter); VIR_ENUM_DECL(virAccessPermNWFilterBinding); -- 2.20.1

Define the wire protocol for the virNetworkPort APIs and enable the client/server RPC dispatch. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/remote/remote_daemon_dispatch.c | 73 ++++++++++++++++ src/remote/remote_driver.c | 69 ++++++++++++++++ src/remote/remote_protocol.x | 124 +++++++++++++++++++++++++++- src/remote_protocol-structs | 69 ++++++++++++++++ src/rpc/gendispatch.pl | 18 ++-- 5 files changed, 346 insertions(+), 7 deletions(-) diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c index df28259042..856c5e48e7 100644 --- a/src/remote/remote_daemon_dispatch.c +++ b/src/remote/remote_daemon_dispatch.c @@ -83,6 +83,7 @@ struct daemonClientEventCallback { static virDomainPtr get_nonnull_domain(virConnectPtr conn, remote_nonnull_domain domain); static virNetworkPtr get_nonnull_network(virConnectPtr conn, remote_nonnull_network network); +static virNetworkPortPtr get_nonnull_network_port(virConnectPtr conn, remote_nonnull_network_port port); static virInterfacePtr get_nonnull_interface(virConnectPtr conn, remote_nonnull_interface iface); static virStoragePoolPtr get_nonnull_storage_pool(virConnectPtr conn, remote_nonnull_storage_pool pool); static virStorageVolPtr get_nonnull_storage_vol(virConnectPtr conn, remote_nonnull_storage_vol vol); @@ -93,6 +94,7 @@ static virDomainSnapshotPtr get_nonnull_domain_snapshot(virDomainPtr dom, remote static virNodeDevicePtr get_nonnull_node_device(virConnectPtr conn, remote_nonnull_node_device dev); static int make_nonnull_domain(remote_nonnull_domain *dom_dst, virDomainPtr dom_src) ATTRIBUTE_RETURN_CHECK; static int make_nonnull_network(remote_nonnull_network *net_dst, virNetworkPtr net_src) ATTRIBUTE_RETURN_CHECK; +static int make_nonnull_network_port(remote_nonnull_network_port *port_dst, virNetworkPortPtr port_src) ATTRIBUTE_RETURN_CHECK; static int make_nonnull_interface(remote_nonnull_interface *interface_dst, virInterfacePtr interface_src) ATTRIBUTE_RETURN_CHECK; static int make_nonnull_storage_pool(remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr pool_src) ATTRIBUTE_RETURN_CHECK; static int make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src) ATTRIBUTE_RETURN_CHECK; @@ -7175,6 +7177,54 @@ remoteDispatchStorageVolGetInfoFlags(virNetServerPtr server ATTRIBUTE_UNUSED, } +static int +remoteDispatchNetworkPortGetParameters(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_network_port_get_parameters_args *args, + remote_network_port_get_parameters_ret *ret) +{ + int rv = -1; + virNetworkPortPtr port = NULL; + virTypedParameterPtr params = NULL; + int nparams = 0; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->networkConn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(port = get_nonnull_network_port(priv->networkConn, args->port))) + goto cleanup; + + if (virNetworkPortGetParameters(port, ¶ms, &nparams, args->flags) < 0) + goto cleanup; + + if (nparams > REMOTE_NETWORK_PORT_PARAMETERS_MAX) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nparams too large")); + goto cleanup; + } + + if (virTypedParamsSerialize(params, nparams, + (virTypedParameterRemotePtr *) &ret->params.params_val, + &ret->params.params_len, + args->flags) < 0) + goto cleanup; + + rv = 0; + + cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virObjectUnref(port); + virTypedParamsFree(params, nparams); + return rv; +} + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire @@ -7198,6 +7248,19 @@ get_nonnull_network(virConnectPtr conn, remote_nonnull_network network) return virGetNetwork(conn, network.name, BAD_CAST network.uuid); } +static virNetworkPortPtr +get_nonnull_network_port(virConnectPtr conn, remote_nonnull_network_port port) +{ + virNetworkPortPtr ret; + virNetworkPtr net; + net = virGetNetwork(conn, port.net.name, BAD_CAST port.net.uuid); + if (!net) + return NULL; + ret = virGetNetworkPort(net, BAD_CAST port.uuid); + virObjectUnref(net); + return ret; +} + static virInterfacePtr get_nonnull_interface(virConnectPtr conn, remote_nonnull_interface iface) { @@ -7270,6 +7333,16 @@ make_nonnull_network(remote_nonnull_network *net_dst, virNetworkPtr net_src) return 0; } +static int +make_nonnull_network_port(remote_nonnull_network_port *port_dst, virNetworkPortPtr port_src) +{ + if (VIR_STRDUP(port_dst->net.name, port_src->net->name) < 0) + return -1; + memcpy(port_dst->net.uuid, port_src->net->uuid, VIR_UUID_BUFLEN); + memcpy(port_dst->uuid, port_src->uuid, VIR_UUID_BUFLEN); + return 0; +} + static int make_nonnull_interface(remote_nonnull_interface *interface_dst, virInterfacePtr interface_src) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 5c4dd41227..560174be4f 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -138,6 +138,7 @@ static int remoteAuthPolkit(virConnectPtr conn, struct private_data *priv, static virDomainPtr get_nonnull_domain(virConnectPtr conn, remote_nonnull_domain domain); static virNetworkPtr get_nonnull_network(virConnectPtr conn, remote_nonnull_network network); +static virNetworkPortPtr get_nonnull_network_port(virConnectPtr conn, remote_nonnull_network_port port); static virNWFilterPtr get_nonnull_nwfilter(virConnectPtr conn, remote_nonnull_nwfilter nwfilter); static virNWFilterBindingPtr get_nonnull_nwfilter_binding(virConnectPtr conn, remote_nonnull_nwfilter_binding binding); static virInterfacePtr get_nonnull_interface(virConnectPtr conn, remote_nonnull_interface iface); @@ -148,6 +149,7 @@ static virSecretPtr get_nonnull_secret(virConnectPtr conn, remote_nonnull_secret static virDomainSnapshotPtr get_nonnull_domain_snapshot(virDomainPtr domain, remote_nonnull_domain_snapshot snapshot); static void make_nonnull_domain(remote_nonnull_domain *dom_dst, virDomainPtr dom_src); static void make_nonnull_network(remote_nonnull_network *net_dst, virNetworkPtr net_src); +static void make_nonnull_network_port(remote_nonnull_network_port *port_dst, virNetworkPortPtr port_src); static void make_nonnull_interface(remote_nonnull_interface *interface_dst, virInterfacePtr interface_src); static void make_nonnull_storage_pool(remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr vol_src); static void make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); @@ -8132,6 +8134,45 @@ remoteStorageVolGetInfoFlags(virStorageVolPtr vol, } +static int +remoteNetworkPortGetParameters(virNetworkPortPtr port, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + int rv = -1; + struct private_data *priv = port->net->conn->privateData; + remote_network_port_get_parameters_args args; + remote_network_port_get_parameters_ret ret; + + remoteDriverLock(priv); + + make_nonnull_network_port(&args.port, port); + args.flags = flags; + + memset(&ret, 0, sizeof(ret)); + if (call(port->net->conn, priv, 0, REMOTE_PROC_NETWORK_PORT_GET_PARAMETERS, + (xdrproc_t) xdr_remote_network_port_get_parameters_args, (char *) &args, + (xdrproc_t) xdr_remote_network_port_get_parameters_ret, (char *) &ret) == -1) + goto done; + + if (virTypedParamsDeserialize((virTypedParameterRemotePtr) ret.params.params_val, + ret.params.params_len, + REMOTE_NETWORK_PORT_PARAMETERS_MAX, + params, + nparams) < 0) + goto cleanup; + + rv = 0; + + cleanup: + xdr_free((xdrproc_t) xdr_remote_network_port_get_parameters_ret, (char *) &ret); + done: + remoteDriverUnlock(priv); + return rv; +} + + /* get_nonnull_domain and get_nonnull_network turn an on-wire * (name, uuid) pair into virDomainPtr or virNetworkPtr object. * These can return NULL if underlying memory allocations fail, @@ -8149,6 +8190,19 @@ get_nonnull_network(virConnectPtr conn, remote_nonnull_network network) return virGetNetwork(conn, network.name, BAD_CAST network.uuid); } +static virNetworkPortPtr +get_nonnull_network_port(virConnectPtr conn, remote_nonnull_network_port port) +{ + virNetworkPortPtr ret; + virNetworkPtr net; + net = virGetNetwork(conn, port.net.name, BAD_CAST port.net.uuid); + if (!net) + return NULL; + ret = virGetNetworkPort(net, BAD_CAST port.uuid); + virObjectUnref(net); + return ret; +} + static virInterfacePtr get_nonnull_interface(virConnectPtr conn, remote_nonnull_interface iface) { @@ -8216,6 +8270,14 @@ make_nonnull_network(remote_nonnull_network *net_dst, virNetworkPtr net_src) memcpy(net_dst->uuid, net_src->uuid, VIR_UUID_BUFLEN); } +static void +make_nonnull_network_port(remote_nonnull_network_port *port_dst, virNetworkPortPtr port_src) +{ + port_dst->net.name = port_src->net->name; + memcpy(port_dst->net.uuid, port_src->net->uuid, VIR_UUID_BUFLEN); + memcpy(port_dst->uuid, port_src->uuid, VIR_UUID_BUFLEN); +} + static void make_nonnull_interface(remote_nonnull_interface *interface_dst, virInterfacePtr interface_src) @@ -8542,6 +8604,13 @@ static virNetworkDriver network_driver = { .networkIsActive = remoteNetworkIsActive, /* 0.7.3 */ .networkIsPersistent = remoteNetworkIsPersistent, /* 0.7.3 */ .networkGetDHCPLeases = remoteNetworkGetDHCPLeases, /* 1.2.6 */ + .networkListAllPorts = remoteNetworkListAllPorts, /* 5.3.0 */ + .networkPortLookupByUUID = remoteNetworkPortLookupByUUID, /* 5.3.0 */ + .networkPortCreateXML = remoteNetworkPortCreateXML, /* 5.3.0 */ + .networkPortGetXMLDesc = remoteNetworkPortGetXMLDesc, /* 5.3.0 */ + .networkPortSetParameters = remoteNetworkPortSetParameters, /* 5.3.0 */ + .networkPortGetParameters = remoteNetworkPortGetParameters, /* 5.3.0 */ + .networkPortDelete = remoteNetworkPortDelete, /* 5.3.0 */ }; static virInterfaceDriver interface_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 11f44ee267..d64b494cef 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -74,6 +74,9 @@ const REMOTE_MIGRATE_COOKIE_MAX = 4194304; /* Upper limit on lists of networks. */ const REMOTE_NETWORK_LIST_MAX = 16384; +/* Upper limit on lists of network ports. */ +const REMOTE_NETWORK_PORT_LIST_MAX = 16384; + /* Upper limit on lists of interfaces. */ const REMOTE_INTERFACE_LIST_MAX = 16384; @@ -263,6 +266,12 @@ const REMOTE_NODE_SEV_INFO_MAX = 64; /* Upper limit on number of launch security information entries */ const REMOTE_DOMAIN_LAUNCH_SECURITY_INFO_PARAMS_MAX = 64; +/* + * Upper limit on list of network port parameters + */ +const REMOTE_NETWORK_PORT_PARAMETERS_MAX = 16; + + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -279,6 +288,11 @@ struct remote_nonnull_network { remote_uuid uuid; }; +struct remote_nonnull_network_port { + remote_nonnull_network net; + remote_uuid uuid; +}; + /* A network filter which may not be NULL. */ struct remote_nonnull_nwfilter { remote_nonnull_string name; @@ -331,6 +345,7 @@ struct remote_nonnull_domain_snapshot { /* A domain or network which may be NULL. */ typedef remote_nonnull_domain *remote_domain; typedef remote_nonnull_network *remote_network; +typedef remote_nonnull_network_port *remote_network_port; typedef remote_nonnull_nwfilter *remote_nwfilter; typedef remote_nonnull_nwfilter_binding *remote_nwfilter_binding; typedef remote_nonnull_storage_pool *remote_storage_pool; @@ -3573,6 +3588,68 @@ struct remote_connect_get_storage_pool_capabilities_ret { remote_nonnull_string capabilities; }; +struct remote_network_list_all_ports_args { + remote_nonnull_network network; + int need_results; + unsigned int flags; +}; + +struct remote_network_list_all_ports_ret { /* insert@1 */ + remote_nonnull_network_port ports<REMOTE_NETWORK_PORT_LIST_MAX>; + unsigned int ret; +}; + +struct remote_network_port_lookup_by_uuid_args { + remote_nonnull_network network; + remote_uuid uuid; +}; + +struct remote_network_port_lookup_by_uuid_ret { + remote_nonnull_network_port port; +}; + +struct remote_network_port_create_xml_args { + remote_nonnull_network network; + remote_nonnull_string xml; + unsigned int flags; +}; + +struct remote_network_port_create_xml_ret { + remote_nonnull_network_port port; +}; + +struct remote_network_port_set_parameters_args { + remote_nonnull_network_port port; + remote_typed_param params<REMOTE_NETWORK_PORT_PARAMETERS_MAX>; + unsigned int flags; +}; + +struct remote_network_port_get_parameters_args { + remote_nonnull_network_port port; + int nparams; + unsigned int flags; +}; + +struct remote_network_port_get_parameters_ret { + remote_typed_param params<REMOTE_NETWORK_PORT_PARAMETERS_MAX>; + int nparams; +}; + +struct remote_network_port_get_xml_desc_args { + remote_nonnull_network_port port; + unsigned int flags; +}; + +struct remote_network_port_get_xml_desc_ret { + remote_nonnull_string xml; +}; + +struct remote_network_port_delete_args { + remote_nonnull_network_port port; + unsigned int flags; +}; + + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -6342,5 +6419,50 @@ enum remote_procedure { * @generate: both * @acl: connect:read */ - REMOTE_PROC_CONNECT_GET_STORAGE_POOL_CAPABILITIES = 403 + REMOTE_PROC_CONNECT_GET_STORAGE_POOL_CAPABILITIES = 403, + + /** + * @generate: both + * @priority: high + * @acl: network:search_ports + * @aclfilter: network_port:getattr + */ + REMOTE_PROC_NETWORK_LIST_ALL_PORTS = 404, + + /** + * @generate: both + * @priority: high + * @acl: network_port:getattr + */ + REMOTE_PROC_NETWORK_PORT_LOOKUP_BY_UUID = 405, + + /** + * @generate: both + * @acl: network_port:create + */ + REMOTE_PROC_NETWORK_PORT_CREATE_XML = 406, + + /** + * @generate: none + * @acl: network_port:read + */ + REMOTE_PROC_NETWORK_PORT_GET_PARAMETERS = 407, + + /** + * @generate: both + * @acl: network_port:write + */ + REMOTE_PROC_NETWORK_PORT_SET_PARAMETERS = 408, + + /** + * @generate: both + * @acl: network_port:read + */ + REMOTE_PROC_NETWORK_PORT_GET_XML_DESC = 409, + + /** + * @generate: both + * @acl: network_port:delete + */ + REMOTE_PROC_NETWORK_PORT_DELETE = 410 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 768189c573..2398494520 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -17,6 +17,10 @@ struct remote_nonnull_network { remote_nonnull_string name; remote_uuid uuid; }; +struct remote_nonnull_network_port { + remote_nonnull_network net; + remote_uuid uuid; +}; struct remote_nonnull_nwfilter { remote_nonnull_string name; remote_uuid uuid; @@ -2981,6 +2985,64 @@ struct remote_connect_get_storage_pool_capabilities_args { struct remote_connect_get_storage_pool_capabilities_ret { remote_nonnull_string capabilities; }; +struct remote_network_list_all_ports_args { + remote_nonnull_network network; + int need_results; + u_int flags; +}; +struct remote_network_list_all_ports_ret { + struct { + u_int ports_len; + remote_nonnull_network_port * ports_val; + } ports; + u_int ret; +}; +struct remote_network_port_lookup_by_uuid_args { + remote_nonnull_network network; + remote_uuid uuid; +}; +struct remote_network_port_lookup_by_uuid_ret { + remote_nonnull_network_port port; +}; +struct remote_network_port_create_xml_args { + remote_nonnull_network network; + remote_nonnull_string xml; + u_int flags; +}; +struct remote_network_port_create_xml_ret { + remote_nonnull_network_port port; +}; +struct remote_network_port_set_parameters_args { + remote_nonnull_network_port port; + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + u_int flags; +}; +struct remote_network_port_get_parameters_args { + remote_nonnull_network_port port; + int nparams; + u_int flags; +}; +struct remote_network_port_get_parameters_ret { + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + int nparams; +}; +struct remote_network_port_get_xml_desc_args { + remote_nonnull_network_port port; + u_int flags; +}; +struct remote_network_port_get_xml_desc_ret { + remote_nonnull_string xml; +}; +struct remote_network_port_delete_args { + remote_nonnull_network_port port; + u_int flags; +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3385,4 +3447,11 @@ enum remote_procedure { REMOTE_PROC_CONNECT_LIST_ALL_NWFILTER_BINDINGS = 401, REMOTE_PROC_DOMAIN_SET_IOTHREAD_PARAMS = 402, REMOTE_PROC_CONNECT_GET_STORAGE_POOL_CAPABILITIES = 403, + REMOTE_PROC_NETWORK_LIST_ALL_PORTS = 404, + REMOTE_PROC_NETWORK_PORT_LOOKUP_BY_UUID = 405, + REMOTE_PROC_NETWORK_PORT_CREATE_XML = 406, + REMOTE_PROC_NETWORK_PORT_GET_PARAMETERS = 407, + REMOTE_PROC_NETWORK_PORT_SET_PARAMETERS = 408, + REMOTE_PROC_NETWORK_PORT_GET_XML_DESC = 409, + REMOTE_PROC_NETWORK_PORT_DELETE = 410, }; diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index ae3a42c4c1..f683e711ce 100755 --- a/src/rpc/gendispatch.pl +++ b/src/rpc/gendispatch.pl @@ -557,7 +557,7 @@ elsif ($mode eq "server") { if ($args_member =~ m/^remote_nonnull_string name;/ and $has_node_device) { # ignore the name arg for node devices next - } elsif ($args_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|secret|nwfilter|nwfilter_binding) (\S+);/) { + } elsif ($args_member =~ m/^remote_nonnull_(domain|network|network_port|storage_pool|storage_vol|interface|secret|nwfilter|nwfilter_binding) (\S+);/) { my $type_name = name_to_TypeName($1); push(@vars_list, "vir${type_name}Ptr $2 = NULL"); @@ -722,7 +722,7 @@ elsif ($mode eq "server") { if (!$modern_ret_as_list) { push(@ret_list, "ret->$3 = tmp.$3;"); } - } elsif ($ret_member =~ m/(?:admin|remote)_nonnull_(secret|nwfilter|nwfilter_binding|node_device|interface|network|storage_vol|storage_pool|domain_snapshot|domain|server|client) (\S+)<(\S+)>;/) { + } elsif ($ret_member =~ m/(?:admin|remote)_nonnull_(secret|nwfilter|nwfilter_binding|node_device|interface|network|network_port|storage_vol|storage_pool|domain_snapshot|domain|server|client) (\S+)<(\S+)>;/) { $modern_ret_struct_name = $1; $single_ret_list_error_msg_type = $1; $single_ret_list_name = $2; @@ -780,7 +780,7 @@ elsif ($mode eq "server") { $single_ret_var = $1; $single_ret_by_ref = 0; $single_ret_check = " == NULL"; - } elsif ($ret_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|node_device|secret|nwfilter|nwfilter_binding|domain_snapshot) (\S+);/) { + } elsif ($ret_member =~ m/^remote_nonnull_(domain|network|network_port|storage_pool|storage_vol|interface|node_device|secret|nwfilter|nwfilter_binding|domain_snapshot) (\S+);/) { my $type_name = name_to_TypeName($1); if ($call->{ProcName} eq "DomainCreateWithFlags") { @@ -1328,7 +1328,7 @@ elsif ($mode eq "client") { $priv_src = "dev->conn"; push(@args_list, "virNodeDevicePtr dev"); push(@setters_list, "args.name = dev->name;"); - } elsif ($args_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|interface|secret|nwfilter|nwfilter_binding|domain_snapshot) (\S+);/) { + } elsif ($args_member =~ m/^remote_nonnull_(domain|network|network_port|storage_pool|storage_vol|interface|secret|nwfilter|nwfilter_binding|domain_snapshot) (\S+);/) { my $name = $1; my $arg_name = $2; my $type_name = name_to_TypeName($name); @@ -1336,6 +1336,8 @@ elsif ($mode eq "client") { if ($is_first_arg) { if ($name eq "domain_snapshot") { $priv_src = "$arg_name->domain->conn"; + } elsif ($name eq "network_port") { + $priv_src = "$arg_name->net->conn"; } else { $priv_src = "$arg_name->conn"; } @@ -1521,7 +1523,7 @@ elsif ($mode eq "client") { } push(@ret_list, "memcpy(result->$3, ret.$3, sizeof(result->$3));"); - } elsif ($ret_member =~ m/(?:admin|remote)_nonnull_(secret|nwfilter|nwfilter_binding|node_device|interface|network|storage_vol|storage_pool|domain_snapshot|domain|server|client) (\S+)<(\S+)>;/) { + } elsif ($ret_member =~ m/(?:admin|remote)_nonnull_(secret|nwfilter|nwfilter_binding|node_device|interface|network|network_port|storage_vol|storage_pool|domain_snapshot|domain|server|client) (\S+)<(\S+)>;/) { my $proc_name = name_to_TypeName($1); if ($structprefix eq "admin") { @@ -1574,7 +1576,7 @@ elsif ($mode eq "client") { push(@ret_list, "VIR_FREE(ret.$1);"); $single_ret_var = "char *rv = NULL"; $single_ret_type = "char *"; - } elsif ($ret_member =~ m/^remote_nonnull_(domain|network|storage_pool|storage_vol|node_device|interface|secret|nwfilter|nwfilter_binding|domain_snapshot) (\S+);/) { + } elsif ($ret_member =~ m/^remote_nonnull_(domain|network|network_port|storage_pool|storage_vol|node_device|interface|secret|nwfilter|nwfilter_binding|domain_snapshot) (\S+);/) { my $name = $1; my $arg_name = $2; my $type_name = name_to_TypeName($name); @@ -2135,6 +2137,8 @@ elsif ($mode eq "client") { if ($object ne "Connect") { if ($object eq "StorageVol") { push @argdecls, "virStoragePoolDefPtr pool"; + } elsif ($object eq "NetworkPort") { + push @argdecls, "virNetworkDefPtr net"; } push @argdecls, "$objecttype $arg"; } @@ -2164,6 +2168,8 @@ elsif ($mode eq "client") { if ($object ne "Connect") { if ($object eq "StorageVol") { push @argvars, "pool"; + } elsif ($object eq "NetworkPort") { + push @argvars, "net"; } push @argvars, $arg; } -- 2.20.1

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- tools/virsh-completer.c | 50 +++++ tools/virsh-completer.h | 4 + tools/virsh-network.c | 399 +++++++++++++++++++++++++++++++++++++++- tools/virsh-network.h | 5 + 4 files changed, 457 insertions(+), 1 deletion(-) diff --git a/tools/virsh-completer.c b/tools/virsh-completer.c index 918e409890..7d5cf8cb90 100644 --- a/tools/virsh-completer.c +++ b/tools/virsh-completer.c @@ -408,6 +408,56 @@ virshNetworkEventNameCompleter(vshControl *ctl ATTRIBUTE_UNUSED, } +char ** +virshNetworkPortUUIDCompleter(vshControl *ctl, + const vshCmd *cmd ATTRIBUTE_UNUSED, + unsigned int flags) +{ + virshControlPtr priv = ctl->privData; + virNetworkPtr net = NULL; + virNetworkPortPtr *ports = NULL; + int nports = 0; + size_t i = 0; + char **ret = NULL; + + virCheckFlags(0, NULL); + + if (!priv->conn || virConnectIsAlive(priv->conn) <= 0) + return NULL; + + if (!(net = virshCommandOptNetwork(ctl, cmd, NULL))) + return false; + + if ((nports = virNetworkListAllPorts(net, &ports, flags)) < 0) + return NULL; + + if (VIR_ALLOC_N(ret, nports + 1) < 0) + goto error; + + for (i = 0; i < nports; i++) { + char uuid[VIR_UUID_STRING_BUFLEN]; + + if (virNetworkPortGetUUIDString(ports[i], uuid) < 0 || + VIR_STRDUP(ret[i], uuid) < 0) + goto error; + + virNetworkPortFree(ports[i]); + } + VIR_FREE(ports); + + return ret; + + error: + for (; i < nports; i++) + virNetworkPortFree(ports[i]); + VIR_FREE(ports); + for (i = 0; i < nports; i++) + VIR_FREE(ret[i]); + VIR_FREE(ret); + return NULL; +} + + char ** virshNodeDeviceNameCompleter(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED, diff --git a/tools/virsh-completer.h b/tools/virsh-completer.h index ed37a26cc9..cd3cc9ecae 100644 --- a/tools/virsh-completer.h +++ b/tools/virsh-completer.h @@ -59,6 +59,10 @@ char ** virshNetworkEventNameCompleter(vshControl *ctl, const vshCmd *cmd, unsigned int flags); +char ** virshNetworkPortUUIDCompleter(vshControl *ctl, + const vshCmd *cmd, + unsigned int flags); + char ** virshNodeDeviceNameCompleter(vshControl *ctl, const vshCmd *cmd, unsigned int flags); diff --git a/tools/virsh-network.c b/tools/virsh-network.c index 14bf9a144f..af08441842 100644 --- a/tools/virsh-network.c +++ b/tools/virsh-network.c @@ -1,7 +1,7 @@ /* * virsh-network.c: Commands to manage network * - * Copyright (C) 2005, 2007-2016 Red Hat, Inc. + * Copyright (C) 2005, 2007-2019 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -54,6 +54,16 @@ #define VIRSH_COMMON_OPT_NETWORK_OT_STRING_FULL(cflags) \ VIRSH_COMMON_OPT_NETWORK_OT_STRING(N_("network name or uuid"), cflags) +#define VIRSH_COMMON_OPT_NETWORK_PORT(cflags) \ + {.name = "port", \ + .type = VSH_OT_DATA, \ + .flags = VSH_OFLAG_REQ, \ + .help = N_("port UUID"), \ + .completer = virshNetworkPortUUIDCompleter, \ + .completer_flags = cflags, \ + } + + virNetworkPtr virshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd, const char **name, unsigned int flags) @@ -92,6 +102,35 @@ virshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd, return network; } + +virNetworkPortPtr +virshCommandOptNetworkPort(vshControl *ctl, const vshCmd *cmd, + virNetworkPtr net, + const char **name) +{ + virNetworkPortPtr port = NULL; + const char *n = NULL; + const char *optname = "port"; + + if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0) + return NULL; + + vshDebug(ctl, VSH_ERR_INFO, "%s: found option <%s>: %s\n", + cmd->def->name, optname, n); + + if (name) + *name = n; + + vshDebug(ctl, VSH_ERR_DEBUG, "%s: <%s> trying as network UUID\n", + cmd->def->name, optname); + port = virNetworkPortLookupByUUIDString(net, n); + + if (!port) + vshError(ctl, _("failed to get network port '%s'"), n); + + return port; +} + /* * "net-autostart" command */ @@ -1437,6 +1476,340 @@ cmdNetworkDHCPLeases(vshControl *ctl, const vshCmd *cmd) return ret; } +/* + * "net-port-create" command + */ +static const vshCmdInfo info_network_port_create[] = { + {.name = "help", + .data = N_("create a network port from an XML file") + }, + {.name = "desc", + .data = N_("Create a network port.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_network_port_create[] = { + VIRSH_COMMON_OPT_NETWORK_FULL(0), + VIRSH_COMMON_OPT_FILE(N_("file containing an XML network port description")), + {.name = NULL} +}; + +static bool +cmdNetworkPortCreate(vshControl *ctl, const vshCmd *cmd) +{ + virNetworkPortPtr port = NULL; + const char *from = NULL; + bool ret = false; + char *buffer = NULL; + virNetworkPtr network = NULL; + + network = virshCommandOptNetwork(ctl, cmd, NULL); + if (network == NULL) + goto cleanup; + + if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0) + goto cleanup; + + if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) + goto cleanup; + + port = virNetworkPortCreateXML(network, buffer, 0); + + if (port != NULL) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virNetworkPortGetUUIDString(port, uuidstr); + vshPrintExtra(ctl, _("Network port %s created from %s\n"), + uuidstr, from); + } else { + vshError(ctl, _("Failed to create network from %s"), from); + goto cleanup; + } + + ret = true; + cleanup: + VIR_FREE(buffer); + if (port) + virNetworkPortFree(port); + if (network) + virNetworkFree(network); + return ret; +} + +/* + * "net-port-dumpxml" command + */ +static const vshCmdInfo info_network_port_dumpxml[] = { + {.name = "help", + .data = N_("network port information in XML") + }, + {.name = "desc", + .data = N_("Output the network port information as an XML dump to stdout.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_network_port_dumpxml[] = { + VIRSH_COMMON_OPT_NETWORK_FULL(0), + VIRSH_COMMON_OPT_NETWORK_PORT(0), + {.name = NULL} +}; + +static bool +cmdNetworkPortDumpXML(vshControl *ctl, const vshCmd *cmd) +{ + virNetworkPtr network; + virNetworkPortPtr port = NULL; + bool ret = true; + char *dump; + unsigned int flags = 0; + + if (!(network = virshCommandOptNetwork(ctl, cmd, NULL))) + goto cleanup; + + if (!(port = virshCommandOptNetworkPort(ctl, cmd, network, NULL))) + goto cleanup; + + dump = virNetworkPortGetXMLDesc(port, flags); + + if (dump != NULL) { + vshPrint(ctl, "%s", dump); + VIR_FREE(dump); + } else { + ret = false; + } + + cleanup: + if (port) + virNetworkPortFree(port); + if (network) + virNetworkFree(network); + return ret; +} + + +/* + * "net-port-delete" command + */ +static const vshCmdInfo info_network_port_delete[] = { + {.name = "help", + .data = N_("network port information in XML") + }, + {.name = "desc", + .data = N_("Output the network port information as an XML dump to stdout.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_network_port_delete[] = { + VIRSH_COMMON_OPT_NETWORK_FULL(0), + VIRSH_COMMON_OPT_NETWORK_PORT(0), + {.name = NULL} +}; + +static bool +cmdNetworkPortDelete(vshControl *ctl, const vshCmd *cmd) +{ + virNetworkPtr network = NULL; + virNetworkPortPtr port = NULL; + bool ret = true; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + if (!(network = virshCommandOptNetwork(ctl, cmd, NULL))) + goto cleanup; + + if (!(port = virshCommandOptNetworkPort(ctl, cmd, network, NULL))) + goto cleanup; + + if (virNetworkPortGetUUIDString(port, uuidstr) < 0) + goto cleanup; + + if (virNetworkPortDelete(port, 0) < 0) { + vshError(ctl, _("Failed to delete network port %s"), uuidstr); + goto cleanup; + } else { + vshPrintExtra(ctl, _("Network port %s deleted\n"), uuidstr); + } + + ret = true; + cleanup: + if (port) + virNetworkPortFree(port); + if (network) + virNetworkFree(network); + return ret; +} + + +static int +virshNetworkPortSorter(const void *a, const void *b) +{ + virNetworkPortPtr *na = (virNetworkPortPtr *) a; + virNetworkPortPtr *nb = (virNetworkPortPtr *) b; + unsigned char uuida[VIR_UUID_BUFLEN]; + unsigned char uuidb[VIR_UUID_BUFLEN]; + + if (*na && !*nb) + return -1; + + if (!*na) + return *nb != NULL; + + if (virNetworkPortGetUUID(*na, uuida) < 0 || + virNetworkPortGetUUID(*nb, uuidb) < 0) + return -1; + + return memcmp(uuida, uuidb, VIR_UUID_BUFLEN); +} + +struct virshNetworkPortList { + virNetworkPortPtr *ports; + size_t nports; +}; +typedef struct virshNetworkPortList *virshNetworkPortListPtr; + +static void +virshNetworkPortListFree(virshNetworkPortListPtr list) +{ + size_t i; + + if (list && list->ports) { + for (i = 0; i < list->nports; i++) { + if (list->ports[i]) + virNetworkPortFree(list->ports[i]); + } + VIR_FREE(list->ports); + } + VIR_FREE(list); +} + +static virshNetworkPortListPtr +virshNetworkPortListCollect(vshControl *ctl, + const vshCmd *cmd, + unsigned int flags) +{ + virshNetworkPortListPtr list = vshMalloc(ctl, sizeof(*list)); + int ret; + virNetworkPtr network = NULL; + bool success = false; + + if (!(network = virshCommandOptNetwork(ctl, cmd, NULL))) + goto cleanup; + + /* try the list with flags support (0.10.2 and later) */ + if ((ret = virNetworkListAllPorts(network, + &list->ports, + flags)) < 0) + goto cleanup; + + list->nports = ret; + + /* sort the list */ + if (list->ports && list->nports) + qsort(list->ports, list->nports, + sizeof(*list->ports), virshNetworkPortSorter); + + success = true; + + cleanup: + if (!success) { + virshNetworkPortListFree(list); + list = NULL; + } + + if (network) + virNetworkFree(network); + + return list; +} + +/* + * "net-list" command + */ +static const vshCmdInfo info_network_port_list[] = { + {.name = "help", + .data = N_("list network ports") + }, + {.name = "desc", + .data = N_("Returns list of network ports.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_network_port_list[] = { + VIRSH_COMMON_OPT_NETWORK_FULL(0), + {.name = "uuid", + .type = VSH_OT_BOOL, + .help = N_("list uuid's only") + }, + {.name = "table", + .type = VSH_OT_BOOL, + .help = N_("list table (default)") + }, + {.name = NULL} +}; + +#define FILTER(NAME, FLAG) \ + if (vshCommandOptBool(cmd, NAME)) \ + flags |= (FLAG) +static bool +cmdNetworkPortList(vshControl *ctl, const vshCmd *cmd) +{ + virshNetworkPortListPtr list = NULL; + size_t i; + bool ret = false; + bool optTable = vshCommandOptBool(cmd, "table"); + bool optUUID = vshCommandOptBool(cmd, "uuid"); + char uuid[VIR_UUID_STRING_BUFLEN]; + unsigned int flags = 0; + vshTablePtr table = NULL; + + if (optTable + optUUID > 1) { + vshError(ctl, "%s", + _("Only one argument from --table and --uuid " + "may be specified.")); + return false; + } + + if (!optUUID) + optTable = true; + + if (!(list = virshNetworkPortListCollect(ctl, cmd, flags))) + return false; + + if (optTable) { + table = vshTableNew(_("UUID"), NULL); + if (!table) + goto cleanup; + } + + for (i = 0; i < list->nports; i++) { + virNetworkPortPtr port = list->ports[i]; + + if (virNetworkPortGetUUIDString(port, uuid) < 0) { + vshError(ctl, "%s", _("Failed to get network's UUID")); + goto cleanup; + } + if (optTable) { + if (vshTableRowAppend(table, uuid, NULL) < 0) + goto cleanup; + } else if (optUUID) { + vshPrint(ctl, "%s\n", uuid); + } + } + + if (optTable) + vshTablePrintToStdout(table, ctl); + + ret = true; + cleanup: + vshTableFree(table); + virshNetworkPortListFree(list); + return ret; +} +#undef FILTER + + const vshCmdDef networkCmds[] = { {.name = "net-autostart", .handler = cmdNetworkAutostart, @@ -1528,5 +1901,29 @@ const vshCmdDef networkCmds[] = { .info = info_network_uuid, .flags = 0 }, + {.name = "net-port-list", + .handler = cmdNetworkPortList, + .opts = opts_network_port_list, + .info = info_network_port_list, + .flags = 0 + }, + {.name = "net-port-create", + .handler = cmdNetworkPortCreate, + .opts = opts_network_port_create, + .info = info_network_port_create, + .flags = 0 + }, + {.name = "net-port-dumpxml", + .handler = cmdNetworkPortDumpXML, + .opts = opts_network_port_dumpxml, + .info = info_network_port_dumpxml, + .flags = 0 + }, + {.name = "net-port-delete", + .handler = cmdNetworkPortDelete, + .opts = opts_network_port_delete, + .info = info_network_port_delete, + .flags = 0 + }, {.name = NULL} }; diff --git a/tools/virsh-network.h b/tools/virsh-network.h index 9c86eb5bc9..18b8e4365f 100644 --- a/tools/virsh-network.h +++ b/tools/virsh-network.h @@ -27,6 +27,11 @@ virNetworkPtr virshCommandOptNetworkBy(vshControl *ctl, const vshCmd *cmd, const char **name, unsigned int flags); +virNetworkPortPtr +virshCommandOptNetworkPort(vshControl *ctl, const vshCmd *cmd, + virNetworkPtr net, + const char **name); + /* default is lookup by Name and UUID */ # define virshCommandOptNetwork(_ctl, _cmd, _name) \ virshCommandOptNetworkBy(_ctl, _cmd, _name, \ -- 2.20.1

The virNetworkObjPtr state will need to maintain a record of all virNetworkPortDefPtr objects associated with the network. Record these in a hash and add APIs for manipulating them. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/virnetworkobj.c | 303 +++++++++++++++++++++++++++++++++++++++ src/conf/virnetworkobj.h | 34 +++++ src/libvirt_private.syms | 6 + 3 files changed, 343 insertions(+) diff --git a/src/conf/virnetworkobj.c b/src/conf/virnetworkobj.c index c9336e0472..47c142998e 100644 --- a/src/conf/virnetworkobj.c +++ b/src/conf/virnetworkobj.c @@ -58,6 +58,8 @@ struct _virNetworkObj { /* Immutable pointer, self locking APIs */ virMacMapPtr macmap; + + virHashTablePtr ports; /* uuid -> virNetworkPortDefPtr */ }; struct _virNetworkObjList { @@ -86,6 +88,17 @@ virNetworkObjOnceInit(void) VIR_ONCE_GLOBAL_INIT(virNetworkObj); +static int +virNetworkObjLoadAllPorts(virNetworkObjPtr net, + const char *stateDir); + + +static void +virNetworkObjPortFree(void *val, const void *key ATTRIBUTE_UNUSED) +{ + virNetworkPortDefFree(val); +} + virNetworkObjPtr virNetworkObjNew(void) { @@ -106,6 +119,10 @@ virNetworkObjNew(void) virBitmapSetBitExpand(obj->classIdMap, 2) < 0) goto error; + if (!(obj->ports = virHashCreate(10, + virNetworkObjPortFree))) + goto error; + virObjectLock(obj); return obj; @@ -458,6 +475,7 @@ virNetworkObjDispose(void *opaque) { virNetworkObjPtr obj = opaque; + virHashFree(obj->ports); virNetworkDefFree(obj->def); virNetworkDefFree(obj->newDef); virBitmapFree(obj->classIdMap); @@ -1072,9 +1090,16 @@ virNetworkObjLoadAllState(virNetworkObjListPtr nets, continue; obj = virNetworkLoadState(nets, stateDir, entry->d_name); + + if (obj && + virNetworkObjLoadAllPorts(obj, stateDir) < 0) { + virNetworkObjEndAPI(&obj); + goto cleanup; + } virNetworkObjEndAPI(&obj); } + cleanup: VIR_DIR_CLOSE(dir); return ret; } @@ -1584,3 +1609,281 @@ virNetworkObjListPrune(virNetworkObjListPtr nets, virHashRemoveSet(nets->objs, virNetworkObjListPruneHelper, &data); virObjectRWUnlock(nets); } + + +char * +virNetworkObjGetPortStatusDir(virNetworkObjPtr net, + const char *stateDir) +{ + char *ret; + ignore_value(virAsprintf(&ret, "%s/%s/ports", stateDir, net->def->name)); + return ret; +} + +int +virNetworkObjAddPort(virNetworkObjPtr net, + virNetworkPortDefPtr portdef, + const char *stateDir) +{ + int ret = -1; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + char *dir = NULL; + + virUUIDFormat(portdef->uuid, uuidstr); + + if (virHashLookup(net->ports, uuidstr)) { + virReportError(VIR_ERR_NETWORK_PORT_EXIST, + _("Network port with UUID %s already exists"), + uuidstr); + goto cleanup; + } + + if (!(dir = virNetworkObjGetPortStatusDir(net, stateDir))) + goto cleanup; + + if (virHashAddEntry(net->ports, uuidstr, portdef) < 0) + goto cleanup; + + if (virNetworkPortDefSaveStatus(portdef, dir) < 0) { + virHashRemoveEntry(net->ports, uuidstr); + goto cleanup; + } + + ret = 0; + + cleanup: + return ret; +} + + +virNetworkPortDefPtr +virNetworkObjLookupPort(virNetworkObjPtr net, + const unsigned char *uuid) +{ + virNetworkPortDefPtr ret = NULL; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + virUUIDFormat(uuid, uuidstr); + + if (!(ret = virHashLookup(net->ports, uuidstr))) { + virReportError(VIR_ERR_NO_NETWORK_PORT, + _("Network port with UUID %s does not exist"), + uuidstr); + goto cleanup; + } + + cleanup: + return ret; +} + + +int +virNetworkObjDeletePort(virNetworkObjPtr net, + const unsigned char *uuid, + const char *stateDir) +{ + int ret = -1; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + char *dir = NULL; + virNetworkPortDefPtr portdef; + + virUUIDFormat(uuid, uuidstr); + + if (!(portdef = virHashLookup(net->ports, uuidstr))) { + virReportError(VIR_ERR_NO_NETWORK_PORT, + _("Network port with UUID %s does not exist"), + uuidstr); + goto cleanup; + } + + if (!(dir = virNetworkObjGetPortStatusDir(net, stateDir))) + goto cleanup; + + if (virNetworkPortDefDeleteStatus(portdef, dir) < 0) + goto cleanup; + + if (virHashRemoveEntry(net->ports, uuidstr) < 0) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FREE(dir); + return ret; +} + + +int +virNetworkObjDeleteAllPorts(virNetworkObjPtr net, + const char *stateDir) +{ + char *dir; + DIR *dh; + struct dirent *de; + int rc; + int ret = -1; + + if (!(dir = virNetworkObjGetPortStatusDir(net, stateDir))) + goto cleanup; + + if ((rc = virDirOpenIfExists(&dh, dir)) <= 0) { + ret = rc; + goto cleanup; + } + + while ((rc = virDirRead(dh, &de, dir)) > 0) { + char *file = NULL; + + if (!virStringStripSuffix(de->d_name, ".xml")) + continue; + + if (virAsprintf(&file, "%s/%s.xml", dir, de->d_name) < 0) + goto cleanup; + + if (unlink(file) < 0 && errno != ENOENT) + VIR_WARN("Unable to delete %s", file); + + VIR_FREE(file); + } + + virHashRemoveAll(net->ports); + + ret = 0; + cleanup: + return ret; +} + + +typedef struct _virNetworkObjPortListExportData virNetworkObjPortListExportData; +typedef virNetworkObjPortListExportData *virNetworkObjPortListExportDataPtr; +struct _virNetworkObjPortListExportData { + virNetworkPtr net; + virNetworkDefPtr def; + virNetworkPortPtr *ports; + virNetworkPortListFilter filter; + int nports; + bool error; +}; + +static int +virNetworkObjPortListExportCallback(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *opaque) +{ + virNetworkObjPortListExportDataPtr data = opaque; + virNetworkPortDefPtr def = payload; + virNetworkPortPtr port; + + if (data->error) + return 0; + + if (data->filter && + !data->filter(data->net->conn, data->def, def)) + goto cleanup; + + if (!data->ports) { + data->nports++; + goto cleanup; + } + + if (!(port = virGetNetworkPort(data->net, def->uuid))) { + data->error = true; + goto cleanup; + } + + data->ports[data->nports++] = port; + + cleanup: + return 0; +} + + +int +virNetworkObjPortListExport(virNetworkPtr net, + virNetworkObjPtr obj, + virNetworkPortPtr **ports, + virNetworkPortListFilter filter) +{ + virNetworkObjPortListExportData data = { + net, obj->def, NULL, filter, 0, false, + }; + int ret = -1; + + *ports = NULL; + + if (ports && VIR_ALLOC_N(data.ports, virHashSize(obj->ports) + 1) < 0) + goto cleanup; + + virHashForEach(obj->ports, virNetworkObjPortListExportCallback, &data); + + if (data.error) + goto cleanup; + + if (data.ports) { + /* trim the array to the final size */ + ignore_value(VIR_REALLOC_N(data.ports, data.nports + 1)); + *ports = data.ports; + data.ports = NULL; + } + + ret = data.nports; + cleanup: + while (data.ports && data.nports) + virObjectUnref(data.ports[--data.nports]); + + VIR_FREE(data.ports); + return ret; +} + + +static int +virNetworkObjLoadAllPorts(virNetworkObjPtr net, + const char *stateDir) +{ + char *dir; + DIR *dh = NULL; + struct dirent *de; + int ret = -1; + int rc; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virNetworkPortDefPtr portdef = NULL; + + if (!(dir = virNetworkObjGetPortStatusDir(net, stateDir))) + goto cleanup; + + if ((rc = virDirOpenIfExists(&dh, dir)) <= 0) { + ret = rc; + goto cleanup; + } + + while ((rc = virDirRead(dh, &de, dir)) > 0) { + char *file = NULL; + + if (!virStringStripSuffix(de->d_name, ".xml")) + continue; + + if (virAsprintf(&file, "%s/%s.xml", dir, de->d_name) < 0) + goto cleanup; + + portdef = virNetworkPortDefParseFile(file); + VIR_FREE(file); + file = NULL; + + if (!portdef) { + VIR_WARN("Cannot parse port %s", file); + continue; + } + + virUUIDFormat(portdef->uuid, uuidstr); + if (virHashAddEntry(net->ports, uuidstr, portdef) < 0) + goto cleanup; + + portdef = NULL; + } + + ret = 0; + cleanup: + VIR_DIR_CLOSE(dh); + virNetworkPortDefFree(portdef); + return ret; +} diff --git a/src/conf/virnetworkobj.h b/src/conf/virnetworkobj.h index 9c8f141cd9..9d9b29fc25 100644 --- a/src/conf/virnetworkobj.h +++ b/src/conf/virnetworkobj.h @@ -23,6 +23,7 @@ # include "internal.h" # include "network_conf.h" +# include "virnetworkportdef.h" typedef struct _virNetworkObj virNetworkObj; typedef virNetworkObj *virNetworkObjPtr; @@ -156,6 +157,39 @@ void virNetworkObjRemoveInactive(virNetworkObjListPtr nets, virNetworkObjPtr net); +int +virNetworkObjAddPort(virNetworkObjPtr net, + virNetworkPortDefPtr portdef, + const char *stateDir); + +char * +virNetworkObjGetPortStatusDir(virNetworkObjPtr net, + const char *stateDir); + +virNetworkPortDefPtr +virNetworkObjLookupPort(virNetworkObjPtr net, + const unsigned char *uuid); + +int +virNetworkObjDeletePort(virNetworkObjPtr net, + const unsigned char *uuid, + const char *stateDir); + +int +virNetworkObjDeleteAllPorts(virNetworkObjPtr net, + const char *stateDir); + +typedef bool +(*virNetworkPortListFilter)(virConnectPtr conn, + virNetworkDefPtr def, + virNetworkPortDefPtr portdef); + +int +virNetworkObjPortListExport(virNetworkPtr net, + virNetworkObjPtr obj, + virNetworkPortPtr **ports, + virNetworkPortListFilter filter); + int virNetworkObjSaveStatus(const char *statusDir, virNetworkObjPtr net) ATTRIBUTE_RETURN_CHECK; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bcd49904b2..ca81b2c4fc 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1030,9 +1030,12 @@ virInterfaceObjSetActive; # conf/virnetworkobj.h +virNetworkObjAddPort; virNetworkObjAssignDef; virNetworkObjBridgeInUse; +virNetworkObjDeleteAllPorts; virNetworkObjDeleteConfig; +virNetworkObjDeletePort; virNetworkObjEndAPI; virNetworkObjFindByName; virNetworkObjFindByUUID; @@ -1043,6 +1046,7 @@ virNetworkObjGetFloorSum; virNetworkObjGetMacMap; virNetworkObjGetNewDef; virNetworkObjGetPersistentDef; +virNetworkObjGetPortStatusDir; virNetworkObjGetRadvdPid; virNetworkObjIsActive; virNetworkObjIsAutostart; @@ -1055,9 +1059,11 @@ virNetworkObjListNumOfNetworks; virNetworkObjListPrune; virNetworkObjLoadAllConfigs; virNetworkObjLoadAllState; +virNetworkObjLookupPort; virNetworkObjMacMgrAdd; virNetworkObjMacMgrDel; virNetworkObjNew; +virNetworkObjPortListExport; virNetworkObjRemoveInactive; virNetworkObjReplacePersistentDef; virNetworkObjSaveStatus; -- 2.20.1

This initial implementation just wires up the APIs and does tracking of the port XML definitions. It is not yet integrated into the resource allocation logic. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/network/bridge_driver.c | 400 ++++++++++++++++++++++++++++++++++++ 1 file changed, 400 insertions(+) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 27e70c984c..d1979e1f73 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -2775,6 +2775,8 @@ networkStartNetwork(virNetworkDriverStatePtr driver, VIR_DEBUG("Beginning network startup process"); + virNetworkObjDeleteAllPorts(obj, driver->stateDir); + VIR_DEBUG("Setting current network def as transient"); if (virNetworkObjSetDefTransient(obj, true) < 0) goto cleanup; @@ -3952,6 +3954,9 @@ networkDestroy(virNetworkPtr net) if ((ret = networkShutdownNetwork(driver, obj)) < 0) goto cleanup; + + virNetworkObjDeleteAllPorts(obj, driver->stateDir); + /* @def replaced in virNetworkObjUnsetDefTransient*/ def = virNetworkObjGetDef(obj); @@ -5540,6 +5545,394 @@ networkBandwidthUpdate(virDomainNetDefPtr iface, } +static virNetworkPortPtr +networkPortLookupByUUID(virNetworkPtr net, + const unsigned char *uuid) +{ + virNetworkObjPtr obj; + virNetworkDefPtr def; + virNetworkPortDefPtr portdef = NULL; + virNetworkPortPtr ret = NULL; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(uuid, uuidstr); + + if (!(obj = networkObjFromNetwork(net))) + return ret; + + def = virNetworkObjGetDef(obj); + + if (!(portdef = virNetworkObjLookupPort(obj, uuid))) + goto cleanup; + + if (virNetworkPortLookupByUUIDEnsureACL(net->conn, def, portdef) < 0) + goto cleanup; + + if (!virNetworkObjIsActive(obj)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("network '%s' is not active"), + def->name); + goto cleanup; + } + + ret = virGetNetworkPort(net, uuid); + + cleanup: + virNetworkObjEndAPI(&obj); + return ret; +} + + +static virNetworkPortPtr +networkPortCreateXML(virNetworkPtr net, + const char *xmldesc, + unsigned int flags) +{ + virNetworkDriverStatePtr driver = networkGetDriver(); + virNetworkObjPtr obj; + virNetworkDefPtr def; + virNetworkPortDefPtr portdef = NULL; + virNetworkPortPtr ret = NULL; + int rc; + + virCheckFlags(VIR_NETWORK_PORT_CREATE_RECLAIM, NULL); + + if (!(obj = networkObjFromNetwork(net))) + return ret; + + def = virNetworkObjGetDef(obj); + + if (!(portdef = virNetworkPortDefParseString(xmldesc))) + goto cleanup; + + if (virNetworkPortCreateXMLEnsureACL(net->conn, def, portdef) < 0) + goto cleanup; + + if (!virNetworkObjIsActive(obj)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("network '%s' is not active"), + def->name); + goto cleanup; + } + + if (portdef->plugtype == VIR_NETWORK_PORT_PLUG_TYPE_NONE) { + if (flags & VIR_NETWORK_PORT_CREATE_RECLAIM) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("Port reclaim requested but plug type is none")); + goto cleanup; + } + } else { + if (!(flags & VIR_NETWORK_PORT_CREATE_RECLAIM)) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("Port reclaim not requested but plug type is not none")); + goto cleanup; + } + } + + if (flags & VIR_NETWORK_PORT_CREATE_RECLAIM) + rc = networkNotifyPort(obj, portdef); + else + rc = networkAllocatePort(obj, portdef); + if (rc < 0) { + virErrorPtr saved; + saved = virSaveLastError(); + ignore_value(networkReleasePort(obj, portdef)); + virSetError(saved); + virFreeError(saved); + goto cleanup; + } + + if (virNetworkObjAddPort(obj, portdef, driver->stateDir) < 0) { + virNetworkPortDefFree(portdef); + goto cleanup; + } + + ret = virGetNetworkPort(net, portdef->uuid); + cleanup: + virNetworkObjEndAPI(&obj); + return ret; +} + + +static char * +networkPortGetXMLDesc(virNetworkPortPtr port, + unsigned int flags) +{ + virNetworkObjPtr obj; + virNetworkDefPtr def; + virNetworkPortDefPtr portdef = NULL; + char *ret = NULL; + + virCheckFlags(0, NULL); + + if (!(obj = networkObjFromNetwork(port->net))) + return ret; + + def = virNetworkObjGetDef(obj); + + if (!(portdef = virNetworkObjLookupPort(obj, port->uuid))) + goto cleanup; + + if (virNetworkPortGetXMLDescEnsureACL(port->net->conn, def, portdef) < 0) + goto cleanup; + + if (!virNetworkObjIsActive(obj)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("network '%s' is not active"), + def->name); + goto cleanup; + } + + if (!(ret = virNetworkPortDefFormat(portdef))) + goto cleanup; + + cleanup: + virNetworkObjEndAPI(&obj); + return ret; +} + + +static int +networkPortDelete(virNetworkPortPtr port, + unsigned int flags) +{ + virNetworkDriverStatePtr driver = networkGetDriver(); + virNetworkObjPtr obj; + virNetworkDefPtr def; + virNetworkPortDefPtr portdef; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(obj = networkObjFromNetwork(port->net))) + return ret; + + def = virNetworkObjGetDef(obj); + + if (!(portdef = virNetworkObjLookupPort(obj, port->uuid))) + goto cleanup; + + if (virNetworkPortDeleteEnsureACL(port->net->conn, def, portdef) < 0) + goto cleanup; + + if (!virNetworkObjIsActive(obj)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("network '%s' is not active"), + def->name); + goto cleanup; + } + + if (networkReleasePort(obj, portdef) < 0) + goto cleanup; + + virNetworkObjDeletePort(obj, port->uuid, driver->stateDir); + + ret = 0; + cleanup: + virNetworkObjEndAPI(&obj); + return ret; +} + + +static int +networkPortSetParameters(virNetworkPortPtr port, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virNetworkDriverStatePtr driver = networkGetDriver(); + virNetworkObjPtr obj; + virNetworkDefPtr def; + virNetworkPortDefPtr portdef; + virNetDevBandwidthPtr bandwidth = NULL; + char *dir = NULL; + int ret = -1; + size_t i; + + virCheckFlags(0, -1); + + if (!(obj = networkObjFromNetwork(port->net))) + return ret; + + def = virNetworkObjGetDef(obj); + + if (!(portdef = virNetworkObjLookupPort(obj, port->uuid))) + goto cleanup; + + if (virNetworkPortSetParametersEnsureACL(port->net->conn, def, portdef) < 0) + goto cleanup; + + if (!virNetworkObjIsActive(obj)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("network '%s' is not active"), + def->name); + goto cleanup; + } + + if (!(dir = virNetworkObjGetPortStatusDir(obj, driver->stateDir))) + goto cleanup; + + if ((VIR_ALLOC(bandwidth) < 0) || + (VIR_ALLOC(bandwidth->in) < 0) || + (VIR_ALLOC(bandwidth->out) < 0)) + goto cleanup; + + for (i = 0; i < nparams; i++) { + virTypedParameterPtr param = ¶ms[i]; + + if (STREQ(param->field, VIR_NETWORK_PORT_BANDWIDTH_IN_AVERAGE)) { + bandwidth->in->average = param->value.ui; + } else if (STREQ(param->field, VIR_NETWORK_PORT_BANDWIDTH_IN_PEAK)) { + bandwidth->in->peak = param->value.ui; + } else if (STREQ(param->field, VIR_NETWORK_PORT_BANDWIDTH_IN_BURST)) { + bandwidth->in->burst = param->value.ui; + } else if (STREQ(param->field, VIR_NETWORK_PORT_BANDWIDTH_IN_FLOOR)) { + bandwidth->in->floor = param->value.ui; + } else if (STREQ(param->field, VIR_NETWORK_PORT_BANDWIDTH_OUT_AVERAGE)) { + bandwidth->out->average = param->value.ui; + } else if (STREQ(param->field, VIR_NETWORK_PORT_BANDWIDTH_OUT_PEAK)) { + bandwidth->out->peak = param->value.ui; + } else if (STREQ(param->field, VIR_NETWORK_PORT_BANDWIDTH_OUT_BURST)) { + bandwidth->out->burst = param->value.ui; + } + } + + /* average or floor are mandatory, peak and burst are optional. + * So if no average or floor is given, we free inbound/outbound + * here which causes inbound/outbound to not be set. */ + if (!bandwidth->in->average && !bandwidth->in->floor) + VIR_FREE(bandwidth->in); + if (!bandwidth->out->average) + VIR_FREE(bandwidth->out); + + if (networkUpdatePortBandwidth(obj, + &portdef->mac, + &portdef->class_id, + portdef->bandwidth, + bandwidth) < 0) + goto cleanup; + + virNetDevBandwidthFree(portdef->bandwidth); + portdef->bandwidth = bandwidth; + bandwidth = NULL; + + if (virNetworkPortDefSaveStatus(portdef, dir) < 0) + goto cleanup; + + ret = 0; + cleanup: + virNetDevBandwidthFree(bandwidth); + virNetworkObjEndAPI(&obj); + VIR_FREE(dir); + return ret; +} + + +static int +networkPortGetParameters(virNetworkPortPtr port, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + virNetworkObjPtr obj; + virNetworkDefPtr def; + virNetworkPortDefPtr portdef; + int maxparams = 0; + int ret = -1; + + virCheckFlags(0, -1); + + *params = NULL; + *nparams = 0; + + if (!(obj = networkObjFromNetwork(port->net))) + return ret; + + def = virNetworkObjGetDef(obj); + + if (!(portdef = virNetworkObjLookupPort(obj, port->uuid))) + goto cleanup; + + if (virNetworkPortGetParametersEnsureACL(port->net->conn, def, portdef) < 0) + goto cleanup; + + if (!virNetworkObjIsActive(obj)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("network '%s' is not active"), + def->name); + goto cleanup; + } + + if (portdef->bandwidth) { + if ((portdef->bandwidth->in != NULL) && + (virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_IN_AVERAGE, + portdef->bandwidth->in->average) < 0 || + virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_IN_PEAK, + portdef->bandwidth->in->peak) < 0 || + virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_IN_FLOOR, + portdef->bandwidth->in->floor) < 0 || + virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_IN_BURST, + portdef->bandwidth->in->burst) < 0)) + goto cleanup; + + if ((portdef->bandwidth->out != NULL) && + (virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_OUT_AVERAGE, + portdef->bandwidth->out->average) < 0 || + virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_OUT_PEAK, + portdef->bandwidth->out->peak) < 0 || + virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_OUT_BURST, + portdef->bandwidth->out->burst) < 0)) + goto cleanup; + } + + ret = 0; + cleanup: + virNetworkObjEndAPI(&obj); + return ret; +} + + +static int +networkListAllPorts(virNetworkPtr net, + virNetworkPortPtr **ports, + unsigned int flags) +{ + virNetworkObjPtr obj; + virNetworkDefPtr def; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(obj = networkObjFromNetwork(net))) + return ret; + + def = virNetworkObjGetDef(obj); + + if (virNetworkListAllPortsEnsureACL(net->conn, def) < 0) + goto cleanup; + + if (!virNetworkObjIsActive(obj)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("network '%s' is not active"), + def->name); + goto cleanup; + } + + ret = virNetworkObjPortListExport(net, obj, ports, + virNetworkListAllPortsCheckACL); + + cleanup: + virNetworkObjEndAPI(&obj); + return ret; +} + + static virNetworkDriver networkDriver = { .name = "bridge", .connectNumOfNetworks = networkConnectNumOfNetworks, /* 0.2.0 */ @@ -5564,6 +5957,13 @@ static virNetworkDriver networkDriver = { .networkIsActive = networkIsActive, /* 0.7.3 */ .networkIsPersistent = networkIsPersistent, /* 0.7.3 */ .networkGetDHCPLeases = networkGetDHCPLeases, /* 1.2.6 */ + .networkPortLookupByUUID = networkPortLookupByUUID, /* 5.3.0 */ + .networkPortCreateXML = networkPortCreateXML, /* 5.3.0 */ + .networkPortGetXMLDesc = networkPortGetXMLDesc, /* 5.3.0 */ + .networkPortDelete = networkPortDelete, /* 5.3.0 */ + .networkListAllPorts = networkListAllPorts, /* 5.3.0 */ + .networkPortGetParameters = networkPortGetParameters, /* 5.3.0 */ + .networkPortSetParameters = networkPortSetParameters, /* 5.3.0 */ }; -- 2.20.1

When starting up it is important to notify the network driver of any NICs which are used by running guests so that it can account for any resources they are using. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/libxl/libxl_driver.c | 30 ++++++++++++++++++++++++++++++ src/lxc/lxc_process.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index 42221cb925..f9b5341ac0 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -352,6 +352,34 @@ libxlAutostartDomain(virDomainObjPtr vm, return ret; } + +static void +libxlReconnectNotifyNets(virDomainDefPtr def) +{ + size_t i; + virConnectPtr conn = NULL; + + for (i = 0; i < def->nnets; i++) { + virDomainNetDefPtr net = def->nets[i]; + /* keep others from trying to use the macvtap device name, but + * don't return error if this happens, since that causes the + * domain to be unceremoniously killed, which would be *very* + * impolite. + */ + if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT) + ignore_value(virNetDevMacVLanReserveName(net->ifname, false)); + + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + if (!conn && !(conn = virGetConnectNetwork())) + continue; + virDomainNetNotifyActualDevice(conn, def, net); + } + } + + virObjectUnref(conn); +} + + /* * Reconnect to running domains that were previously started/created * with libxenlight driver. @@ -424,6 +452,8 @@ libxlReconnectDomain(virDomainObjPtr vm, /* Enable domain death events */ libxl_evenable_domain_death(cfg->ctx, vm->def->id, 0, &priv->deathW); + libxlReconnectNotifyNets(vm->def); + /* now that we know it's reconnected call the hook if present */ if (virHookPresent(VIR_HOOK_DRIVER_LIBXL) && STRNEQ("Domain-0", vm->def->name)) { diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c index 3a33418aca..397b9fd36c 100644 --- a/src/lxc/lxc_process.c +++ b/src/lxc/lxc_process.c @@ -1643,6 +1643,34 @@ virLXCProcessAutostartAll(virLXCDriverPtr driver) virObjectUnref(conn); } + +static void +virLXCProcessReconnectNotifyNets(virDomainDefPtr def) +{ + size_t i; + virConnectPtr conn = NULL; + + for (i = 0; i < def->nnets; i++) { + virDomainNetDefPtr net = def->nets[i]; + /* keep others from trying to use the macvtap device name, but + * don't return error if this happens, since that causes the + * domain to be unceremoniously killed, which would be *very* + * impolite. + */ + if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT) + ignore_value(virNetDevMacVLanReserveName(net->ifname, false)); + + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + if (!conn && !(conn = virGetConnectNetwork())) + continue; + virDomainNetNotifyActualDevice(conn, def, net); + } + } + + virObjectUnref(conn); +} + + static int virLXCProcessReconnectDomain(virDomainObjPtr vm, void *opaque) @@ -1689,6 +1717,8 @@ virLXCProcessReconnectDomain(virDomainObjPtr vm, vm->def, vm->pid) < 0) goto error; + virLXCProcessReconnectNotifyNets(vm->def); + /* now that we know it's reconnected call the hook if present */ if (virHookPresent(VIR_HOOK_DRIVER_LXC)) { char *xml = virDomainDefFormat(vm->def, driver->caps, 0); -- 2.20.1

The various steps involved in reconnecting to a domain may cause updates to the virDomainObj struct that need to be reflected in the saved status file. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/libxl/libxl_driver.c | 3 +++ src/lxc/lxc_process.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index f9b5341ac0..760aef021f 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -454,6 +454,9 @@ libxlReconnectDomain(virDomainObjPtr vm, libxlReconnectNotifyNets(vm->def); + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, cfg->caps) < 0) + VIR_WARN("Cannot update XML for running Xen guest %s", vm->def->name); + /* now that we know it's reconnected call the hook if present */ if (virHookPresent(VIR_HOOK_DRIVER_LIBXL) && STRNEQ("Domain-0", vm->def->name)) { diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c index 397b9fd36c..95dfb091d8 100644 --- a/src/lxc/lxc_process.c +++ b/src/lxc/lxc_process.c @@ -1677,6 +1677,7 @@ virLXCProcessReconnectDomain(virDomainObjPtr vm, { virLXCDriverPtr driver = opaque; virLXCDomainObjPrivatePtr priv; + virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver); int ret = -1; virObjectLock(vm); @@ -1719,6 +1720,9 @@ virLXCProcessReconnectDomain(virDomainObjPtr vm, virLXCProcessReconnectNotifyNets(vm->def); + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) + VIR_WARN("Cannot update XML for running LXC guest %s", vm->def->name); + /* now that we know it's reconnected call the hook if present */ if (virHookPresent(VIR_HOOK_DRIVER_LXC)) { char *xml = virDomainDefFormat(vm->def, driver->caps, 0); @@ -1739,6 +1743,7 @@ virLXCProcessReconnectDomain(virDomainObjPtr vm, ret = 0; cleanup: + virObjectUnref(cfg); virObjectUnlock(vm); return ret; -- 2.20.1

The portid will be the UUID of the virNetworkPort object associated with the network interface when a guest is running. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- docs/formatdomain.html.in | 8 +++++++ docs/schemas/domaincommon.rng | 5 +++++ src/conf/domain_conf.c | 21 +++++++++++++++++++ src/conf/domain_conf.h | 4 ++++ .../net-virtio-network-portgroup.xml | 6 +++--- 5 files changed, 41 insertions(+), 3 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index d27ab07cbb..5e35d57665 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -5309,6 +5309,14 @@ information for different classes of network connections. <span class="since">Since 0.9.4</span>. </p> + <p> + When a guest is running and interface of type <code>network</code> + may include a <code>portid</code> attribute. This provides the UUID + of an associated virNetworkPortPtr object that records the association + between the domain interface and the network. This attribute is + read-only since port objects are create and deleted automatically + during startup and shutdown. <span class="since">Since 5.1.0</span>/ + </p> <p> Also, similar to <code>direct</code> network connections (described below), a connection of type <code>network</code> may diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 111b85c36f..ecdde43c3a 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2591,6 +2591,11 @@ <ref name="deviceName"/> </attribute> </optional> + <optional> + <attribute name="portid"> + <ref name="UUID"/> + </attribute> + </optional> </define> <define name="interface-bridge-attributes"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 1be78e1e26..2b199b6325 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -11406,6 +11406,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_AUTOFREE(char *) type = NULL; VIR_AUTOFREE(char *) network = NULL; VIR_AUTOFREE(char *) portgroup = NULL; + VIR_AUTOFREE(char *) portid = NULL; VIR_AUTOFREE(char *) bridge = NULL; VIR_AUTOFREE(char *) dev = NULL; VIR_AUTOFREE(char *) ifname = NULL; @@ -11483,6 +11484,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, virXMLNodeNameEqual(cur, "source")) { network = virXMLPropString(cur, "network"); portgroup = virXMLPropString(cur, "portgroup"); + if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) + portid = virXMLPropString(cur, "portid"); } else if (!internal && def->type == VIR_DOMAIN_NET_TYPE_INTERNAL && virXMLNodeNameEqual(cur, "source")) { @@ -11695,6 +11698,13 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, "specified with <interface type='network'/>")); goto error; } + if (portid && + virUUIDParse(portid, def->data.network.portid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to parse port id '%s'"), portid); + goto error; + } + VIR_STEAL_PTR(def->data.network.name, network); VIR_STEAL_PTR(def->data.network.portgroup, portgroup); VIR_STEAL_PTR(def->data.network.actual, actual); @@ -24979,6 +24989,11 @@ virDomainActualNetDefContentsFormat(virBufferPtr buf, def->data.network.name); virBufferEscapeString(buf, " portgroup='%s'", def->data.network.portgroup); + if (virUUIDIsValid(def->data.network.portid)) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(def->data.network.portid, uuidstr); + virBufferAsprintf(buf, " portid='%s'", uuidstr); + } } if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE || actualType == VIR_DOMAIN_NET_TYPE_NETWORK) { @@ -25282,6 +25297,12 @@ virDomainNetDefFormat(virBufferPtr buf, def->data.network.name); virBufferEscapeString(buf, " portgroup='%s'", def->data.network.portgroup); + if (virUUIDIsValid(def->data.network.portid) && + !(flags & (VIR_DOMAIN_DEF_FORMAT_INACTIVE))) { + char portidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(def->data.network.portid, portidstr); + virBufferEscapeString(buf, " portid='%s'", portidstr); + } sourceLines++; break; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index be43b31123..8d4c753a3f 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -969,6 +969,7 @@ struct _virDomainNetDef { struct { char *name; char *portgroup; + unsigned char portid[VIR_UUID_BUFLEN]; /* actual has info about the currently used physical * device (if the network is of type * bridge/private/vepa/passthrough). This is saved in the @@ -976,6 +977,9 @@ struct _virDomainNetDef { * since it needs to be re-allocated whenever the domain * is restarted. It is also never shown to the user, and * the user cannot specify it in XML documents. + * + * This information is populated from the virNetworkPort + * object associated with the portid UUID above. */ virDomainActualNetDefPtr actual; } network; diff --git a/tests/qemuxml2argvdata/net-virtio-network-portgroup.xml b/tests/qemuxml2argvdata/net-virtio-network-portgroup.xml index 54a0eb7229..3d6cd02a73 100644 --- a/tests/qemuxml2argvdata/net-virtio-network-portgroup.xml +++ b/tests/qemuxml2argvdata/net-virtio-network-portgroup.xml @@ -24,7 +24,7 @@ <controller type='pci' index='0' model='pci-root'/> <interface type='network' trustGuestRxFilters='yes'> <mac address='00:11:22:33:44:55'/> - <source network='rednet' portgroup='bob'/> + <source network='rednet' portgroup='bob' portid='873be5a0-6b8a-4a0b-a1df-ef3659cd64bc'/> <vlan> <tag id='4095'/> </vlan> @@ -35,7 +35,7 @@ </interface> <interface type='network' trustGuestRxFilters='no'> <mac address='10:11:22:33:44:55'/> - <source network='blue' portgroup='sam'/> + <source network='blue' portgroup='sam' portid='b884cc7e-b347-4a17-9e78-4f672a67794e'/> <virtualport> <parameters instanceid='09b11c53-8b5c-4eeb-8f00-d84eaa0aaa4f' interfaceid='09b11c53-8b5c-4eeb-8f00-d84eaa0aaa4f'/> </virtualport> @@ -43,7 +43,7 @@ </interface> <interface type='network'> <mac address='22:11:22:33:44:55'/> - <source network='blue' portgroup='sam'/> + <source network='blue' portgroup='sam' portid='c5fc8eb4-cb69-4fea-8f8b-65e00e6d1dba'/> <virtualport type='802.1Qbh'> <parameters profileid='testhis99'/> </virtualport> -- 2.20.1

Change the domain conf so invoke the new network port public APIs instead of the network callbacks. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 231 +++++++++++++++++++++++++----------- src/conf/domain_conf.h | 26 ---- src/libvirt_private.syms | 1 - src/network/bridge_driver.c | 202 ------------------------------- 4 files changed, 161 insertions(+), 299 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2b199b6325..3256971214 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -30684,45 +30684,86 @@ virDomainNetDefActualToNetworkPort(virDomainDefPtr dom, return NULL; } -static virDomainNetAllocateActualDeviceImpl netAllocate; -static virDomainNetNotifyActualDeviceImpl netNotify; -static virDomainNetReleaseActualDeviceImpl netRelease; -static virDomainNetBandwidthUpdateImpl netBandwidthUpdate; - -void -virDomainNetSetDeviceImpl(virDomainNetAllocateActualDeviceImpl allocate, - virDomainNetNotifyActualDeviceImpl notify, - virDomainNetReleaseActualDeviceImpl release, - virDomainNetBandwidthUpdateImpl bandwidthUpdate) -{ - netAllocate = allocate; - netNotify = notify; - netRelease = release; - netBandwidthUpdate = bandwidthUpdate; -} - -int -virDomainNetAllocateActualDevice(virConnectPtr conn, - virDomainDefPtr dom, - virDomainNetDefPtr iface) +static int +virDomainNetCreatePort(virConnectPtr conn, + virDomainDefPtr dom, + virDomainNetDefPtr iface, + unsigned int flags) { virNetworkPtr net = NULL; int ret = -1; + virNetworkPortDefPtr portdef = NULL; + virNetworkPortPtr port = NULL; + char *portxml = NULL; + virErrorPtr saved; - if (!netAllocate) { - virReportError(VIR_ERR_NO_SUPPORT, "%s", - _("Virtual networking driver is not available")); + if (!(net = virNetworkLookupByName(conn, iface->data.network.name))) return -1; + + if (flags & VIR_NETWORK_PORT_CREATE_RECLAIM) { + virDomainNetType actualType = virDomainNetGetActualType(iface); + + /* Older libvirtd uses actualType==network, but we now + * just use actualType==bridge, as nothing needs to + * distinguish the two cases, and this simplifies virt + * drive code */ + if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK) { + iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + actualType = VIR_DOMAIN_NET_TYPE_BRIDGE; + } + + if (!(portdef = virDomainNetDefActualToNetworkPort(dom, iface))) + goto cleanup; + } else { + if (!(portdef = virDomainNetDefToNetworkPort(dom, iface))) + goto cleanup; } - if (!(net = virNetworkLookupByName(conn, iface->data.network.name))) - return -1; + if (!(portxml = virNetworkPortDefFormat(portdef))) + goto cleanup; + + virNetworkPortDefFree(portdef); + portdef = NULL; + + if (!(port = virNetworkPortCreateXML(net, portxml, flags))) + goto cleanup; - ret = netAllocate(net, dom, iface); + VIR_FREE(portxml); + if (!(portxml = virNetworkPortGetXMLDesc(port, 0))) + goto deleteport; + + if (!(portdef = virNetworkPortDefParseString(portxml))) + goto deleteport; + + if (virDomainNetDefActualFromNetworkPort(iface, portdef) < 0) + goto deleteport; + + virNetworkPortGetUUID(port, iface->data.network.portid); + + ret = 0; + cleanup: + virNetworkPortDefFree(portdef); + VIR_FREE(portxml); + virObjectUnref(port); virObjectUnref(net); return ret; + + deleteport: + saved = virSaveLastError(); + virNetworkPortDelete(port, 0); + virSetError(saved); + virFreeError(saved); + goto cleanup; +} + +int +virDomainNetAllocateActualDevice(virConnectPtr conn, + virDomainDefPtr dom, + virDomainNetDefPtr iface) +{ + return virDomainNetCreatePort(conn, dom, iface, 0); } void @@ -30730,80 +30771,130 @@ virDomainNetNotifyActualDevice(virConnectPtr conn, virDomainDefPtr dom, virDomainNetDefPtr iface) { - virDomainNetType actualType = virDomainNetGetActualType(iface); - virNetworkPtr net = NULL; - - if (!netNotify) - return; - - if (!(net = virNetworkLookupByName(conn, iface->data.network.name))) - return; - - /* Older libvirtd uses actualType==network, but we now - * just use actualType==bridge, as nothing needs to - * distinguish the two cases, and this simplifies virt - * drive code */ - if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK) { - iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE; - actualType = VIR_DOMAIN_NET_TYPE_BRIDGE; + if (!virUUIDIsValid(iface->data.network.portid)) { + if (virDomainNetCreatePort(conn, dom, iface, + VIR_NETWORK_PORT_CREATE_RECLAIM) < 0) + return; } - - if (netNotify(net, dom, iface) < 0) - goto cleanup; - if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_BRIDGE) { /* * NB: we can't notify the guest of any MTU change anyway, * so there is no point in trying to learn the actualMTU * (final arg to virNetDevTapReattachBridge()) */ - if (virNetDevTapReattachBridge(iface->ifname, - iface->data.network.actual->data.bridge.brname, - &iface->mac, dom->uuid, - virDomainNetGetActualVirtPortProfile(iface), - virDomainNetGetActualVlan(iface), - iface->mtu, NULL) < 0) - goto cleanup; + ignore_value(virNetDevTapReattachBridge(iface->ifname, + iface->data.network.actual->data.bridge.brname, + &iface->mac, dom->uuid, + virDomainNetGetActualVirtPortProfile(iface), + virDomainNetGetActualVlan(iface), + iface->mtu, NULL)); } - - cleanup: - virObjectUnref(net); } int virDomainNetReleaseActualDevice(virConnectPtr conn, - virDomainDefPtr dom, + virDomainDefPtr dom ATTRIBUTE_UNUSED, virDomainNetDefPtr iface) { virNetworkPtr net = NULL; - int ret; - - if (!netRelease) - return 0; + virNetworkPortPtr port = NULL; + int ret = -1; if (!(net = virNetworkLookupByName(conn, iface->data.network.name))) - return -1; + goto cleanup; + + if (!(port = virNetworkPortLookupByUUID(net, iface->data.network.portid))) + goto cleanup; - ret = netRelease(net, dom, iface); + if (virNetworkPortDelete(port, 0) < 0) + goto cleanup; + cleanup: + virObjectUnref(port); virObjectUnref(net); return ret; } +static int +virDomainNetBandwidthToTypedParams(virNetDevBandwidthPtr bandwidth, + virTypedParameterPtr *params, + int *nparams) +{ + int maxparams = 0; + + if ((bandwidth->in != NULL) && + (virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_IN_AVERAGE, + bandwidth->in->average) < 0 || + virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_IN_PEAK, + bandwidth->in->peak) < 0 || + virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_IN_FLOOR, + bandwidth->in->floor) < 0 || + virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_IN_BURST, + bandwidth->in->burst) < 0)) + goto error; + + if ((bandwidth->out != NULL) && + (virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_OUT_AVERAGE, + bandwidth->out->average) < 0 || + virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_OUT_PEAK, + bandwidth->out->peak) < 0 || + virTypedParamsAddUInt(params, nparams, &maxparams, + VIR_NETWORK_PORT_BANDWIDTH_OUT_BURST, + bandwidth->out->burst) < 0)) + goto error; + + return 0; + + error: + virTypedParamsFree(*params, *nparams); + *params = NULL; + *nparams = 0; + return -1; +} + + int virDomainNetBandwidthUpdate(virDomainNetDefPtr iface, virNetDevBandwidthPtr newBandwidth) { - if (!netBandwidthUpdate) { - virReportError(VIR_ERR_NO_SUPPORT, "%s", - _("Virtual networking driver is not available")); - return -1; - } + virNetworkPtr net = NULL; + virNetworkPortPtr port = NULL; + virTypedParameterPtr params = NULL; + int nparams = 0; + virConnectPtr conn = NULL; + int ret = -1; + + if (!(conn = virGetConnectNetwork())) + goto cleanup; + + if (!(net = virNetworkLookupByName(conn, iface->data.network.name))) + goto cleanup; + + if (!(port = virNetworkPortLookupByUUID(net, iface->data.network.portid))) + goto cleanup; + + if (virDomainNetBandwidthToTypedParams(newBandwidth, ¶ms, &nparams) < 0) + goto cleanup; - return netBandwidthUpdate(iface, newBandwidth); + if (virNetworkPortSetParameters(port, params, nparams, 0) < 0) + goto cleanup; + + ret = 0; + cleanup: + virObjectUnref(conn); + virTypedParamsFree(params, nparams); + virObjectUnref(port); + virObjectUnref(net); + return ret; } /* virDomainNetResolveActualType: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 8d4c753a3f..325ae58350 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3568,32 +3568,6 @@ virNetworkPortDefPtr virDomainNetDefActualToNetworkPort(virDomainDefPtr dom, virDomainNetDefPtr iface); -typedef int -(*virDomainNetAllocateActualDeviceImpl)(virNetworkPtr net, - virDomainDefPtr dom, - virDomainNetDefPtr iface); - -typedef int -(*virDomainNetNotifyActualDeviceImpl)(virNetworkPtr net, - virDomainDefPtr dom, - virDomainNetDefPtr iface); - -typedef int -(*virDomainNetReleaseActualDeviceImpl)(virNetworkPtr net, - virDomainDefPtr dom, - virDomainNetDefPtr iface); - -typedef int -(*virDomainNetBandwidthUpdateImpl)(virDomainNetDefPtr iface, - virNetDevBandwidthPtr newBandwidth); - - -void -virDomainNetSetDeviceImpl(virDomainNetAllocateActualDeviceImpl allocate, - virDomainNetNotifyActualDeviceImpl notify, - virDomainNetReleaseActualDeviceImpl release, - virDomainNetBandwidthUpdateImpl bandwidthUpdate); - int virDomainNetAllocateActualDevice(virConnectPtr conn, virDomainDefPtr dom, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ca81b2c4fc..3394ba0f75 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -487,7 +487,6 @@ virDomainNetReleaseActualDevice; virDomainNetRemove; virDomainNetRemoveHostdev; virDomainNetResolveActualType; -virDomainNetSetDeviceImpl; virDomainNetSetModelString; virDomainNetTypeFromString; virDomainNetTypeSharesHostView; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index d1979e1f73..8eeb26c076 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -4708,52 +4708,6 @@ networkAllocatePort(virNetworkObjPtr obj, } -static int -networkAllocateActualDevice(virNetworkPtr net, - virDomainDefPtr dom, - virDomainNetDefPtr iface) -{ - virNetworkDriverStatePtr driver = networkGetDriver(); - virNetworkPortDefPtr port = NULL; - virNetworkObjPtr obj; - int ret = -1; - - obj = virNetworkObjFindByName(driver->networks, net->name); - if (!obj) { - virReportError(VIR_ERR_NO_NETWORK, - _("no network with matching name '%s'"), - net->name); - return -1; - } - - if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Expected an interface for a virtual network")); - goto cleanup; - } - - if (!(port = virDomainNetDefToNetworkPort(dom, iface))) - goto cleanup; - - if (networkAllocatePort(obj, port) < 0) - goto cleanup; - - VIR_DEBUG("Populating net def"); - if (virDomainNetDefActualFromNetworkPort(iface, port) < 0) - goto cleanup; - - ret = 0; - cleanup: - if (ret < 0) { - virDomainActualNetDefFree(iface->data.network.actual); - iface->data.network.actual = NULL; - } - virNetworkPortDefFree(port); - virNetworkObjEndAPI(&obj); - return ret; -} - - /* networkNotifyPort: * @obj: the network to notify * @port: the port definition to notify @@ -4909,54 +4863,6 @@ networkNotifyPort(virNetworkObjPtr obj, } -static int -networkNotifyActualDevice(virNetworkPtr net, - virDomainDefPtr dom, - virDomainNetDefPtr iface) -{ - virNetworkDriverStatePtr driver = networkGetDriver(); - virNetworkObjPtr obj; - virNetworkDefPtr netdef; - virNetworkPortDefPtr port = NULL; - int ret = -1; - - obj = virNetworkObjFindByName(driver->networks, net->name); - if (!obj) { - virReportError(VIR_ERR_NO_NETWORK, - _("no network with matching name '%s'"), - net->name); - goto cleanup; - } - - if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Expected an interface for a virtual network")); - goto cleanup; - } - - netdef = virNetworkObjGetDef(obj); - - if (!virNetworkObjIsActive(obj)) { - virReportError(VIR_ERR_OPERATION_INVALID, - _("network '%s' is not active"), - netdef->name); - goto cleanup; - } - - if (!(port = virDomainNetDefActualToNetworkPort(dom, iface))) - goto cleanup; - - if (networkNotifyPort(obj, port) < 0) - goto cleanup; - - ret = 0; - cleanup: - virNetworkObjEndAPI(&obj); - virNetworkPortDefFree(port); - return ret; -} - - /* networkReleasePort: * @obj: the network to release from * @port: the port definition to release @@ -5072,65 +4978,6 @@ networkReleasePort(virNetworkObjPtr obj, } -/* networkReleaseActualDevice: - * @dom: domain definition that @iface belongs to - * @iface: a domain's NetDef (interface definition) - * - * Given a domain <interface> element that previously had its <actual> - * element filled in (and possibly a physical device allocated to it), - * free up the physical device for use by someone else, and free the - * virDomainActualNetDef. - * - * Returns 0 on success, -1 on failure. - */ -static int -networkReleaseActualDevice(virNetworkPtr net, - virDomainDefPtr dom, - virDomainNetDefPtr iface) -{ - virNetworkDriverStatePtr driver = networkGetDriver(); - virNetworkObjPtr obj; - virNetworkPortDefPtr port = NULL; - int ret = -1; - - obj = virNetworkObjFindByName(driver->networks, net->name); - if (!obj) { - virReportError(VIR_ERR_NO_NETWORK, - _("no network with matching name '%s'"), - net->name); - goto cleanup; - } - - - if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Expected an interface for a virtual network")); - goto cleanup; - } - - if (iface->data.network.actual == NULL) { - ret = 0; - goto cleanup; - } - - if (!(port = virDomainNetDefActualToNetworkPort(dom, iface))) - goto cleanup; - - if (networkReleasePort(obj, port) < 0) - goto cleanup; - - ret = 0; - cleanup: - virNetworkObjEndAPI(&obj); - if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK) { - virDomainActualNetDefFree(iface->data.network.actual); - iface->data.network.actual = NULL; - } - virNetworkPortDefFree(port); - return ret; -} - - /** * networkCheckBandwidth: * @net: network QoS @@ -5503,48 +5350,6 @@ networkUpdatePortBandwidth(virNetworkObjPtr obj, } -static int -networkBandwidthUpdate(virDomainNetDefPtr iface, - virNetDevBandwidthPtr newBandwidth) -{ - virNetworkDriverStatePtr driver = networkGetDriver(); - virNetworkObjPtr obj = NULL; - virNetDevBandwidthPtr oldBandwidth = virDomainNetGetActualBandwidth(iface); - int ret = -1; - - if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Expected an interface for a virtual network")); - return -1; - } - - if (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_BRIDGE || - iface->data.network.actual->data.bridge.brname == NULL) { - /* This is not an interface that's plugged into a bridge. - * We don't care. Thus from our POV bandwidth change is allowed. */ - return 0; - } - - obj = virNetworkObjFindByName(driver->networks, iface->data.network.name); - if (!obj) { - virReportError(VIR_ERR_NO_NETWORK, - _("no network with matching name '%s'"), - iface->data.network.name); - return ret; - } - - ret = networkUpdatePortBandwidth(obj, - &iface->mac, - iface->data.network.actual ? - &iface->data.network.actual->class_id : NULL, - newBandwidth, - oldBandwidth); - - virNetworkObjEndAPI(&obj); - return ret; -} - - static virNetworkPortPtr networkPortLookupByUUID(virNetworkPtr net, const unsigned char *uuid) @@ -6001,12 +5806,5 @@ networkRegister(void) return -1; if (virRegisterStateDriver(&networkStateDriver) < 0) return -1; - - virDomainNetSetDeviceImpl( - networkAllocateActualDevice, - networkNotifyActualDevice, - networkReleaseActualDevice, - networkBandwidthUpdate); - return 0; } -- 2.20.1
participants (3)
-
Daniel P. Berrangé
-
Laine Stump
-
Michal Privoznik