[libvirt] [PATCHv2 0/9] network: physical device abstraction aka 'virtual switch'

This series deprecates patches 04/10 - 10/10 of the previous series of the same name, posted here: https://www.redhat.com/archives/libvir-list/2011-July/msg00149.html This patch is in response to the following bug reports: https://bugzilla.redhat.com/show_bug.cgi?id=643947 (RHEL) https://bugzilla.redhat.com/show_bug.cgi?id=636106 (upstream) The first 4 patches of the earlier series were reasonable cleanups in their own right, and were ACKed, so I pushed them earlier. I reorganized the remaining 6 patches accord to advice from Dan Berrange and Eric Blake. Some notes about correlation between v1 and v2: [PATCHv2 1/9] util: define MAX A new patch [PATCHv2 2/9] conf: put virtPortProfile struct / functions in a common location Was 05/10 in v1: https://www.redhat.com/archives/libvir-list/2011-July/msg00154.html [PATCHv2 3/9] conf: virDomainNetDef points to (rather than contains) virtPortProfile This was also part of 05/10 in v1. I separated it out to make review of pure code movement (in 2/9) easier. [PATCHv2 4/9] conf: support abstracted interface info in domain interface XML [PATCHv2 5/9] conf: support abstracted interface info in network XML These two were 06/10 and 07/10 in v1: https://www.redhat.com/archives/libvir-list/2011-July/msg00155.html https://www.redhat.com/archives/libvir-list/2011-July/msg00156.html However, they were split differently. In v1 it was split into RNG changes vs. parser changes with network and domain combined in each patch. In v2, I reorganized (based on Dan's advice) to have one patch containing all domain XML changes, from RNG up through parser, test cases, and documentation, and one patch for network XML changes. [PATCHv2 6/9] network: separate Start/Shutdown functions for new network types This was patch 08/10 in v1: https://www.redhat.com/archives/libvir-list/2011-July/msg00157.html It is unchanged from the original. [PATCHv2 7/9] qemu: use virDomainNetGetActual*() functions where appropriate [PATCHv2 8/9] qemu: use virDomainNetGetActual*() in qemuDomainXMLToNative These two were patch 09/10 in v1: https://www.redhat.com/archives/libvir-list/2011-July/msg00158.html In v2, I just split out one piece of code that was more complicated than just replacing direct data references with helper functions, and modified the commit message to be more clear about the "NOP" nature of the change. [PATCHv2 9/9] network: internal API functions to manage assignment of physdev to guest This was 10/10 in v1: https://www.redhat.com/archives/libvir-list/2011-July/msg00159.html The main modification here was to remove all of the #if WITH_NETWORK from qemu_*.c, and instead define static inline NOP functions for the functions I'd previously eliminated references to with #if WITH_NETWORK (in the case that libvirt is build --without-network). This works out very well, except that make syntax-check doesn't like ATTRIBUTE_UNUSED in the inline static functions (I think we should change syntax-check rather than going back to the old #if WITH_NETWORK). Comments about other changes I've made *within* each patch are in the responses to the original patches, as well as in the annotations to the new patches.

If util.h is going to have a MIN, it may as well also have MAX. --- src/util/util.h | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/src/util/util.h b/src/util/util.h index e8197be..af8b15d 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -35,6 +35,9 @@ # ifndef MIN # define MIN(a, b) ((a) < (b) ? (a) : (b)) # endif +# ifndef MAX +# define MAX(a, b) ((a) > (b) ? (a) : (b)) +# endif ssize_t saferead(int fd, void *buf, size_t count) ATTRIBUTE_RETURN_CHECK; ssize_t safewrite(int fd, const void *buf, size_t count) -- 1.7.3.4

On 07/20/2011 12:21 AM, Laine Stump wrote:
If util.h is going to have a MIN, it may as well also have MAX. --- src/util/util.h | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/src/util/util.h b/src/util/util.h index e8197be..af8b15d 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -35,6 +35,9 @@ # ifndef MIN # define MIN(a, b) ((a)< (b) ? (a) : (b)) # endif +# ifndef MAX +# define MAX(a, b) ((a)> (b) ? (a) : (b)) +# endif
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

virtPortProfiles are currently only used in the domain XML, but will soon also be used in the network XML. To prepare for that change, this patch moves the structure definition into util/network.h and the parse and format functions into util/network.c (I decided that this was a better choice than macvtap.h/c for something that needed to always be available on all platforms). --- docs/schemas/Makefile.am | 1 + docs/schemas/domain.rng | 45 +--------- docs/schemas/networkcommon.rng | 50 ++++++++++ libvirt.spec.in | 1 + mingw32-libvirt.spec.in | 1 + src/conf/domain_conf.c | 188 ------------------------------------- src/libvirt_private.syms | 2 + src/util/macvtap.c | 6 +- src/util/macvtap.h | 36 +------- src/util/network.c | 200 ++++++++++++++++++++++++++++++++++++++++ src/util/network.h | 46 +++++++++ 11 files changed, 308 insertions(+), 268 deletions(-) create mode 100644 docs/schemas/networkcommon.rng diff --git a/docs/schemas/Makefile.am b/docs/schemas/Makefile.am index 5ef7737..75a0e73 100644 --- a/docs/schemas/Makefile.am +++ b/docs/schemas/Makefile.am @@ -8,6 +8,7 @@ schema_DATA = \ domainsnapshot.rng \ interface.rng \ network.rng \ + networkcommon.rng \ nodedev.rng \ nwfilter.rng \ secret.rng \ diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 8a4e3fe..2f656ed 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -7,6 +7,7 @@ <include href='basictypes.rng'/> <include href='storageencryption.rng'/> + <include href='networkcommon.rng'/> <!-- description element, maybe placed anywhere under the root @@ -1172,45 +1173,6 @@ </optional> </interleave> </define> - <define name="virtualPortProfile"> - <choice> - <group> - <element name="virtualport"> - <attribute name="type"> - <value>802.1Qbg</value> - </attribute> - <element name="parameters"> - <attribute name="managerid"> - <ref name="uint8range"/> - </attribute> - <attribute name="typeid"> - <ref name="uint24range"/> - </attribute> - <attribute name="typeidversion"> - <ref name="uint8range"/> - </attribute> - <optional> - <attribute name="instanceid"> - <ref name="UUID"/> - </attribute> - </optional> - </element> - </element> - </group> - <group> - <element name="virtualport"> - <attribute name="type"> - <value>802.1Qbh</value> - </attribute> - <element name="parameters"> - <attribute name="profileid"> - <ref name="virtualPortProfileID"/> - </attribute> - </element> - </element> - </group> - </choice> - </define> <!-- An emulator description is just a path to the binary used for the task --> @@ -2515,9 +2477,4 @@ <param name="pattern">[a-zA-Z0-9_\.:]+</param> </data> </define> - <define name="virtualPortProfileID"> - <data type="string"> - <param name="maxLength">39</param> - </data> - </define> </grammar> diff --git a/docs/schemas/networkcommon.rng b/docs/schemas/networkcommon.rng new file mode 100644 index 0000000..0251813 --- /dev/null +++ b/docs/schemas/networkcommon.rng @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<!-- network-related definitions used in multiple grammars --> +<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> + + <define name="virtualPortProfileID"> + <data type="string"> + <param name="maxLength">39</param> + </data> + </define> + + <define name="virtualPortProfile"> + <choice> + <group> + <element name="virtualport"> + <attribute name="type"> + <value>802.1Qbg</value> + </attribute> + <element name="parameters"> + <attribute name="managerid"> + <ref name="uint8range"/> + </attribute> + <attribute name="typeid"> + <ref name="uint24range"/> + </attribute> + <attribute name="typeidversion"> + <ref name="uint8range"/> + </attribute> + <optional> + <attribute name="instanceid"> + <ref name="UUID"/> + </attribute> + </optional> + </element> + </element> + </group> + <group> + <element name="virtualport"> + <attribute name="type"> + <value>802.1Qbh</value> + </attribute> + <element name="parameters"> + <attribute name="profileid"> + <ref name="virtualPortProfileID"/> + </attribute> + </element> + </element> + </group> + </choice> + </define> +</grammar> diff --git a/libvirt.spec.in b/libvirt.spec.in index 667a1ba..6cbd9ac 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -1077,6 +1077,7 @@ fi %{_datadir}/libvirt/schemas/storageencryption.rng %{_datadir}/libvirt/schemas/nwfilter.rng %{_datadir}/libvirt/schemas/basictypes.rng +%{_datadir}/libvirt/schemas/networkcommon.rng %{_datadir}/libvirt/cpu_map.xml diff --git a/mingw32-libvirt.spec.in b/mingw32-libvirt.spec.in index a8a39da..3958d33 100644 --- a/mingw32-libvirt.spec.in +++ b/mingw32-libvirt.spec.in @@ -109,6 +109,7 @@ rm -rf $RPM_BUILD_ROOT %{_mingw32_datadir}/libvirt/schemas/secret.rng %{_mingw32_datadir}/libvirt/schemas/storageencryption.rng %{_mingw32_datadir}/libvirt/schemas/basictypes.rng +%{_mingw32_datadir}/libvirt/schemas/networkcommon.rng %{_mingw32_datadir}/libvirt/cpu_map.xml diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 3c3ab39..0af8860 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -467,11 +467,6 @@ VIR_ENUM_IMPL(virDomainSeclabel, VIR_DOMAIN_SECLABEL_LAST, "dynamic", "static") -VIR_ENUM_IMPL(virVirtualPort, VIR_VIRTUALPORT_TYPE_LAST, - "none", - "802.1Qbg", - "802.1Qbh") - VIR_ENUM_IMPL(virDomainClockOffset, VIR_DOMAIN_CLOCK_OFFSET_LAST, "utc", "localtime", @@ -2591,146 +2586,6 @@ cleanup: } -static int -virVirtualPortProfileParamsParseXML(xmlNodePtr node, - virVirtualPortProfileParamsPtr virtPort) -{ - int ret = -1; - char *virtPortType; - char *virtPortManagerID = NULL; - char *virtPortTypeID = NULL; - char *virtPortTypeIDVersion = NULL; - char *virtPortInstanceID = NULL; - char *virtPortProfileID = NULL; - xmlNodePtr cur = node->children; - const char *msg = NULL; - - virtPortType = virXMLPropString(node, "type"); - if (!virtPortType) - return -1; - - while (cur != NULL) { - if (xmlStrEqual(cur->name, BAD_CAST "parameters")) { - - virtPortManagerID = virXMLPropString(cur, "managerid"); - virtPortTypeID = virXMLPropString(cur, "typeid"); - virtPortTypeIDVersion = virXMLPropString(cur, "typeidversion"); - virtPortInstanceID = virXMLPropString(cur, "instanceid"); - virtPortProfileID = virXMLPropString(cur, "profileid"); - - break; - } - - cur = cur->next; - } - - virtPort->virtPortType = VIR_VIRTUALPORT_NONE; - - switch (virVirtualPortTypeFromString(virtPortType)) { - - case VIR_VIRTUALPORT_8021QBG: - if (virtPortManagerID != NULL && virtPortTypeID != NULL && - virtPortTypeIDVersion != NULL) { - unsigned int val; - - if (virStrToLong_ui(virtPortManagerID, NULL, 0, &val)) { - msg = _("cannot parse value of managerid parameter"); - goto err_exit; - } - - if (val > 0xff) { - msg = _("value of managerid out of range"); - goto err_exit; - } - - virtPort->u.virtPort8021Qbg.managerID = (uint8_t)val; - - if (virStrToLong_ui(virtPortTypeID, NULL, 0, &val)) { - msg = _("cannot parse value of typeid parameter"); - goto err_exit; - } - - if (val > 0xffffff) { - msg = _("value for typeid out of range"); - goto err_exit; - } - - virtPort->u.virtPort8021Qbg.typeID = (uint32_t)val; - - if (virStrToLong_ui(virtPortTypeIDVersion, NULL, 0, &val)) { - msg = _("cannot parse value of typeidversion parameter"); - goto err_exit; - } - - if (val > 0xff) { - msg = _("value of typeidversion out of range"); - goto err_exit; - } - - virtPort->u.virtPort8021Qbg.typeIDVersion = (uint8_t)val; - - if (virtPortInstanceID != NULL) { - if (virUUIDParse(virtPortInstanceID, - virtPort->u.virtPort8021Qbg.instanceID)) { - msg = _("cannot parse instanceid parameter as a uuid"); - goto err_exit; - } - } else { - if (virUUIDGenerate(virtPort->u.virtPort8021Qbg.instanceID)) { - msg = _("cannot generate a random uuid for instanceid"); - goto err_exit; - } - } - - virtPort->virtPortType = VIR_VIRTUALPORT_8021QBG; - ret = 0; - } else { - msg = _("a parameter is missing for 802.1Qbg description"); - goto err_exit; - } - break; - - case VIR_VIRTUALPORT_8021QBH: - if (virtPortProfileID != NULL) { - if (virStrcpyStatic(virtPort->u.virtPort8021Qbh.profileID, - virtPortProfileID) != NULL) { - virtPort->virtPortType = VIR_VIRTUALPORT_8021QBH; - ret = 0; - } else { - msg = _("profileid parameter too long"); - goto err_exit; - } - } else { - msg = _("profileid parameter is missing for 802.1Qbh descripion"); - goto err_exit; - } - break; - - - default: - case VIR_VIRTUALPORT_NONE: - case VIR_VIRTUALPORT_TYPE_LAST: - msg = _("unknown virtualport type"); - goto err_exit; - break; - } - -err_exit: - - if (msg) - virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", msg); - - VIR_FREE(virtPortManagerID); - VIR_FREE(virtPortTypeID); - VIR_FREE(virtPortTypeIDVersion); - VIR_FREE(virtPortInstanceID); - VIR_FREE(virtPortProfileID); - VIR_FREE(virtPortType); - - return ret; -} - - /* Parse the XML definition for a network interface * @param node XML nodeset to parse for net definition * @return 0 on success, -1 on failure @@ -5319,49 +5174,6 @@ virDomainChrTargetTypeToString(int deviceType, return type; } -static void -virVirtualPortProfileFormat(virBufferPtr buf, - virVirtualPortProfileParamsPtr virtPort, - const char *indent) -{ - char uuidstr[VIR_UUID_STRING_BUFLEN]; - - if (virtPort->virtPortType == VIR_VIRTUALPORT_NONE) - return; - - virBufferAsprintf(buf, "%s<virtualport type='%s'>\n", - indent, - virVirtualPortTypeToString(virtPort->virtPortType)); - - switch (virtPort->virtPortType) { - case VIR_VIRTUALPORT_NONE: - case VIR_VIRTUALPORT_TYPE_LAST: - break; - - case VIR_VIRTUALPORT_8021QBG: - virUUIDFormat(virtPort->u.virtPort8021Qbg.instanceID, - uuidstr); - virBufferAsprintf(buf, - "%s <parameters managerid='%d' typeid='%d' " - "typeidversion='%d' instanceid='%s'/>\n", - indent, - virtPort->u.virtPort8021Qbg.managerID, - virtPort->u.virtPort8021Qbg.typeID, - virtPort->u.virtPort8021Qbg.typeIDVersion, - uuidstr); - break; - - case VIR_VIRTUALPORT_8021QBH: - virBufferAsprintf(buf, - "%s <parameters profileid='%s'/>\n", - indent, - virtPort->u.virtPort8021Qbh.profileID); - break; - } - - virBufferAsprintf(buf, "%s</virtualport>\n", indent); -} - int virDomainDiskIndexByName(virDomainDefPtr def, const char *name) { virDomainDiskDefPtr vdisk; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3e3b1dd..fba596e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -715,6 +715,8 @@ virSocketParseAddr; virSocketParseIpv4Addr; virSocketParseIpv6Addr; virSocketSetPort; +virVirtualPortProfileFormat; +virVirtualPortProfileParamsParseXML; # network_conf.h diff --git a/src/util/macvtap.c b/src/util/macvtap.c index 30343c8..052f76b 100644 --- a/src/util/macvtap.c +++ b/src/util/macvtap.c @@ -50,6 +50,7 @@ #include "util.h" #include "macvtap.h" +#include "network.h" VIR_ENUM_IMPL(virMacvtapMode, VIR_MACVTAP_MODE_LAST, "vepa", @@ -1089,7 +1090,7 @@ vpAssociatePortProfileId(const char *macvtap_ifname, VIR_DEBUG("%s: VM OPERATION: %s", __FUNCTION__, virVMOperationTypeToString(vmOp)); - if (vmOp == VIR_VM_OP_NO_OP) + if (!virtPort || vmOp == VIR_VM_OP_NO_OP) return 0; switch (virtPort->virtPortType) { @@ -1145,6 +1146,9 @@ vpDisassociatePortProfileId(const char *macvtap_ifname, VIR_DEBUG("%s: VM OPERATION: %s", __FUNCTION__, virVMOperationTypeToString(vmOp)); + if (!virtPort) + return 0; + switch (virtPort->virtPortType) { case VIR_VIRTUALPORT_NONE: case VIR_VIRTUALPORT_TYPE_LAST: diff --git a/src/util/macvtap.h b/src/util/macvtap.h index 1b85989..8e8613d 100644 --- a/src/util/macvtap.h +++ b/src/util/macvtap.h @@ -25,15 +25,6 @@ # include <config.h> - -enum virVirtualPortType { - VIR_VIRTUALPORT_NONE, - VIR_VIRTUALPORT_8021QBG, - VIR_VIRTUALPORT_8021QBH, - - VIR_VIRTUALPORT_TYPE_LAST, -}; - /* the mode type for macvtap devices */ enum virMacvtapMode { VIR_MACVTAP_MODE_VEPA, @@ -44,31 +35,6 @@ enum virMacvtapMode { VIR_MACVTAP_MODE_LAST, }; - -# ifdef IFLA_VF_PORT_PROFILE_MAX -# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX IFLA_VF_PORT_PROFILE_MAX -# else -# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX 40 -# endif - -/* profile data for macvtap (VEPA) */ -typedef struct _virVirtualPortProfileParams virVirtualPortProfileParams; -typedef virVirtualPortProfileParams *virVirtualPortProfileParamsPtr; -struct _virVirtualPortProfileParams { - enum virVirtualPortType virtPortType; - union { - struct { - uint8_t managerID; - uint32_t typeID; /* 24 bit valid */ - uint8_t typeIDVersion; - unsigned char instanceID[VIR_UUID_BUFLEN]; - } virtPort8021Qbg; - struct { - char profileID[LIBVIRT_IFLA_VF_PORT_PROFILE_MAX]; - } virtPort8021Qbh; - } u; -}; - enum virVMOperationType { VIR_VM_OP_CREATE, VIR_VM_OP_SAVE, @@ -85,6 +51,7 @@ enum virVMOperationType { # if WITH_MACVTAP # include "internal.h" +# include "network.h" int openMacvtapTap(const char *ifname, const unsigned char *macaddress, @@ -119,7 +86,6 @@ int vpDisassociatePortProfileId(const char *macvtap_ifname, # endif /* WITH_MACVTAP */ -VIR_ENUM_DECL(virVirtualPort) VIR_ENUM_DECL(virVMOperation) VIR_ENUM_DECL(virMacvtapMode) diff --git a/src/util/network.c b/src/util/network.c index eb16e0c..a323b26 100644 --- a/src/util/network.c +++ b/src/util/network.c @@ -12,6 +12,7 @@ #include <arpa/inet.h> #include "memory.h" +#include "uuid.h" #include "network.h" #include "util.h" #include "virterror_internal.h" @@ -674,3 +675,202 @@ virSocketAddrPrefixToNetmask(unsigned int prefix, error: return result; } + +/* virtualPortProfile utilities */ + +VIR_ENUM_IMPL(virVirtualPort, VIR_VIRTUALPORT_TYPE_LAST, + "none", + "802.1Qbg", + "802.1Qbh") + +int +virVirtualPortProfileParamsParseXML(xmlNodePtr node, + virVirtualPortProfileParamsPtr virtPort) +{ + int ret = -1; + char *virtPortType; + char *virtPortManagerID = NULL; + char *virtPortTypeID = NULL; + char *virtPortTypeIDVersion = NULL; + char *virtPortInstanceID = NULL; + char *virtPortProfileID = NULL; + xmlNodePtr cur = node->children; + + virtPortType = virXMLPropString(node, "type"); + if (!virtPortType) { + virSocketError(VIR_ERR_XML_ERROR, "%s", + _("missing virtualportprofile type")); + goto error; + } + + while (cur != NULL) { + if (xmlStrEqual(cur->name, BAD_CAST "parameters")) { + + virtPortManagerID = virXMLPropString(cur, "managerid"); + virtPortTypeID = virXMLPropString(cur, "typeid"); + virtPortTypeIDVersion = virXMLPropString(cur, "typeidversion"); + virtPortInstanceID = virXMLPropString(cur, "instanceid"); + virtPortProfileID = virXMLPropString(cur, "profileid"); + + break; + } + + cur = cur->next; + } + + virtPort->virtPortType = VIR_VIRTUALPORT_NONE; + + switch (virVirtualPortTypeFromString(virtPortType)) { + + case VIR_VIRTUALPORT_8021QBG: + if (virtPortManagerID != NULL && virtPortTypeID != NULL && + virtPortTypeIDVersion != NULL) { + unsigned int val; + + if (virStrToLong_ui(virtPortManagerID, NULL, 0, &val)) { + virSocketError(VIR_ERR_XML_ERROR, "%s", + _("cannot parse value of managerid parameter")); + goto error; + } + + if (val > 0xff) { + virSocketError(VIR_ERR_XML_ERROR, "%s", + _("value of managerid out of range")); + goto error; + } + + virtPort->u.virtPort8021Qbg.managerID = (uint8_t)val; + + if (virStrToLong_ui(virtPortTypeID, NULL, 0, &val)) { + virSocketError(VIR_ERR_XML_ERROR, "%s", + _("cannot parse value of typeid parameter")); + goto error; + } + + if (val > 0xffffff) { + virSocketError(VIR_ERR_XML_ERROR, "%s", + _("value for typeid out of range")); + goto error; + } + + virtPort->u.virtPort8021Qbg.typeID = (uint32_t)val; + + if (virStrToLong_ui(virtPortTypeIDVersion, NULL, 0, &val)) { + virSocketError(VIR_ERR_XML_ERROR, "%s", + _("cannot parse value of typeidversion parameter")); + goto error; + } + + if (val > 0xff) { + virSocketError(VIR_ERR_XML_ERROR, "%s", + _("value of typeidversion out of range")); + goto error; + } + + virtPort->u.virtPort8021Qbg.typeIDVersion = (uint8_t)val; + + if (virtPortInstanceID != NULL) { + if (virUUIDParse(virtPortInstanceID, + virtPort->u.virtPort8021Qbg.instanceID)) { + virSocketError(VIR_ERR_XML_ERROR, "%s", + _("cannot parse instanceid parameter as a uuid")); + goto error; + } + } else { + if (virUUIDGenerate(virtPort->u.virtPort8021Qbg.instanceID)) { + virSocketError(VIR_ERR_XML_ERROR, "%s", + _("cannot generate a random uuid for instanceid")); + goto error; + } + } + + virtPort->virtPortType = VIR_VIRTUALPORT_8021QBG; + ret = 0; + } else { + virSocketError(VIR_ERR_XML_ERROR, "%s", + _("a parameter is missing for 802.1Qbg description")); + goto error; + } + break; + + case VIR_VIRTUALPORT_8021QBH: + if (virtPortProfileID != NULL) { + if (virStrcpyStatic(virtPort->u.virtPort8021Qbh.profileID, + virtPortProfileID) != NULL) { + virtPort->virtPortType = VIR_VIRTUALPORT_8021QBH; + ret = 0; + } else { + virSocketError(VIR_ERR_XML_ERROR, "%s", + _("profileid parameter too long")); + goto error; + } + } else { + virSocketError(VIR_ERR_XML_ERROR, "%s", + _("profileid parameter is missing for 802.1Qbh descripion")); + goto error; + } + break; + + + default: + case VIR_VIRTUALPORT_NONE: + case VIR_VIRTUALPORT_TYPE_LAST: + virSocketError(VIR_ERR_XML_ERROR, "%s", + _("unknown virtualport type")); + goto error; + break; + } + +error: + VIR_FREE(virtPortManagerID); + VIR_FREE(virtPortTypeID); + VIR_FREE(virtPortTypeIDVersion); + VIR_FREE(virtPortInstanceID); + VIR_FREE(virtPortProfileID); + VIR_FREE(virtPortType); + + return ret; +} + +void +virVirtualPortProfileFormat(virBufferPtr buf, + virVirtualPortProfileParamsPtr virtPort, + const char *indent) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + if (!virtPort || virtPort->virtPortType == VIR_VIRTUALPORT_NONE) + return; + + virBufferAsprintf(buf, "%s<virtualport type='%s'>\n", + indent, + virVirtualPortTypeToString(virtPort->virtPortType)); + + switch (virtPort->virtPortType) { + case VIR_VIRTUALPORT_NONE: + case VIR_VIRTUALPORT_TYPE_LAST: + break; + + case VIR_VIRTUALPORT_8021QBG: + virUUIDFormat(virtPort->u.virtPort8021Qbg.instanceID, + uuidstr); + virBufferAsprintf(buf, + "%s <parameters managerid='%d' typeid='%d' " + "typeidversion='%d' instanceid='%s'/>\n", + indent, + virtPort->u.virtPort8021Qbg.managerID, + virtPort->u.virtPort8021Qbg.typeID, + virtPort->u.virtPort8021Qbg.typeIDVersion, + uuidstr); + break; + + case VIR_VIRTUALPORT_8021QBH: + virBufferAsprintf(buf, + "%s <parameters profileid='%s'/>\n", + indent, + virtPort->u.virtPort8021Qbh.profileID); + break; + } + + virBufferAsprintf(buf, "%s</virtualport>\n", indent); +} diff --git a/src/util/network.h b/src/util/network.h index ed0b78c..0a8fc03 100644 --- a/src/util/network.h +++ b/src/util/network.h @@ -12,6 +12,8 @@ # define __VIR_NETWORK_H__ # include "internal.h" +# include "buf.h" +# include "util.h" # include <sys/types.h> # include <sys/socket.h> @@ -20,6 +22,7 @@ # endif # include <netdb.h> # include <netinet/in.h> +# include <xml.h> typedef struct { union { @@ -90,4 +93,47 @@ int virSocketAddrPrefixToNetmask(unsigned int prefix, virSocketAddrPtr netmask, int family); +/* virtualPortProfile utilities */ +# ifdef IFLA_VF_PORT_PROFILE_MAX +# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX IFLA_VF_PORT_PROFILE_MAX +# else +# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX 40 +# endif + +enum virVirtualPortType { + VIR_VIRTUALPORT_NONE, + VIR_VIRTUALPORT_8021QBG, + VIR_VIRTUALPORT_8021QBH, + + VIR_VIRTUALPORT_TYPE_LAST, +}; + +VIR_ENUM_DECL(virVirtualPort) + +/* profile data for macvtap (VEPA) */ +typedef struct _virVirtualPortProfileParams virVirtualPortProfileParams; +typedef virVirtualPortProfileParams *virVirtualPortProfileParamsPtr; +struct _virVirtualPortProfileParams { + enum virVirtualPortType virtPortType; + union { + struct { + uint8_t managerID; + uint32_t typeID; /* 24 bit valid */ + uint8_t typeIDVersion; + unsigned char instanceID[VIR_UUID_BUFLEN]; + } virtPort8021Qbg; + struct { + char profileID[LIBVIRT_IFLA_VF_PORT_PROFILE_MAX]; + } virtPort8021Qbh; + } u; +}; + +int +virVirtualPortProfileParamsParseXML(xmlNodePtr node, + virVirtualPortProfileParamsPtr virtPort); +void +virVirtualPortProfileFormat(virBufferPtr buf, + virVirtualPortProfileParamsPtr virtPort, + const char *indent); + #endif /* __VIR_NETWORK_H__ */ -- 1.7.3.4

On 07/20/2011 12:21 AM, Laine Stump wrote:
virtPortProfiles are currently only used in the domain XML, but will soon also be used in the network XML. To prepare for that change, this patch moves the structure definition into util/network.h and the parse and format functions into util/network.c (I decided that this was a better choice than macvtap.h/c for something that needed to always be available on all platforms). --- docs/schemas/Makefile.am | 1 + docs/schemas/domain.rng | 45 +--------- docs/schemas/networkcommon.rng | 50 ++++++++++ libvirt.spec.in | 1 + mingw32-libvirt.spec.in | 1 + src/conf/domain_conf.c | 188 ------------------------------------- src/libvirt_private.syms | 2 + src/util/macvtap.c | 6 +- src/util/macvtap.h | 36 +------- src/util/network.c | 200 ++++++++++++++++++++++++++++++++++++++++ src/util/network.h | 46 +++++++++ 11 files changed, 308 insertions(+), 268 deletions(-) create mode 100644 docs/schemas/networkcommon.rng
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

The virtPortProfile in the domain interface struct is now a separately allocated object *pointed to by* (rather than contained in) the main virDomainNetDef object. This is done to make it easier to figure out when a virtualPortProfile has/hasn't been specified in a particular config. --- src/conf/domain_conf.c | 17 ++++++++--------- src/conf/domain_conf.h | 2 +- src/qemu/qemu_command.c | 4 ++-- src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_migration.c | 4 ++-- src/qemu/qemu_process.c | 2 +- src/util/network.c | 15 ++++++++++++--- src/util/network.h | 2 +- 8 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0af8860..24f9b00 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -770,6 +770,7 @@ void virDomainNetDefFree(virDomainNetDefPtr def) case VIR_DOMAIN_NET_TYPE_DIRECT: VIR_FREE(def->data.direct.linkdev); + VIR_FREE(def->data.direct.virtPortProfile); break; case VIR_DOMAIN_NET_TYPE_USER: @@ -2617,8 +2618,7 @@ virDomainNetDefParseXML(virCapsPtr caps, char *devaddr = NULL; char *mode = NULL; virNWFilterHashTablePtr filterparams = NULL; - virVirtualPortProfileParams virtPort; - bool virtPortParsed = false; + virVirtualPortProfileParamsPtr virtPort = NULL; xmlNodePtr oldnode = ctxt->node; int ret; @@ -2664,12 +2664,11 @@ virDomainNetDefParseXML(virCapsPtr caps, xmlStrEqual(cur->name, BAD_CAST "source")) { dev = virXMLPropString(cur, "dev"); mode = virXMLPropString(cur, "mode"); - } else if (!virtPortParsed && + } else if ((virtPort == NULL) && (def->type == VIR_DOMAIN_NET_TYPE_DIRECT) && xmlStrEqual(cur->name, BAD_CAST "virtualport")) { - if (virVirtualPortProfileParamsParseXML(cur, &virtPort)) + if (virVirtualPortProfileParamsParseXML(cur, &virtPort) < 0) goto error; - virtPortParsed = true; } else if ((network == NULL) && ((def->type == VIR_DOMAIN_NET_TYPE_SERVER) || (def->type == VIR_DOMAIN_NET_TYPE_CLIENT) || @@ -2853,9 +2852,8 @@ virDomainNetDefParseXML(virCapsPtr caps, } else def->data.direct.mode = VIR_MACVTAP_MODE_VEPA; - if (virtPortParsed) - def->data.direct.virtPortProfile = virtPort; - + def->data.direct.virtPortProfile = virtPort; + virtPort = NULL; def->data.direct.linkdev = dev; dev = NULL; @@ -2962,6 +2960,7 @@ cleanup: VIR_FREE(port); VIR_FREE(ifname); VIR_FREE(dev); + VIR_FREE(virtPort); VIR_FREE(script); VIR_FREE(bridge); VIR_FREE(model); @@ -8601,7 +8600,7 @@ virDomainNetDefFormat(virBufferPtr buf, virBufferAsprintf(buf, " mode='%s'", virMacvtapModeTypeToString(def->data.direct.mode)); virBufferAddLit(buf, "/>\n"); - virVirtualPortProfileFormat(buf, &def->data.direct.virtPortProfile, + virVirtualPortProfileFormat(buf, def->data.direct.virtPortProfile, " "); break; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 7a3d72b..69e74dc 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -381,7 +381,7 @@ struct _virDomainNetDef { struct { char *linkdev; int mode; /* enum virMacvtapMode from util/macvtap.h */ - virVirtualPortProfileParams virtPortProfile; + virVirtualPortProfileParamsPtr virtPortProfile; } direct; } data; struct { diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index a3bce4a..f456e25 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -127,7 +127,7 @@ qemuPhysIfaceConnect(virDomainDefPtr def, rc = openMacvtapTap(net->ifname, net->mac, net->data.direct.linkdev, net->data.direct.mode, vnet_hdr, def->uuid, - &net->data.direct.virtPortProfile, &res_ifname, + net->data.direct.virtPortProfile, &res_ifname, vmop, driver->stateDir); if (rc >= 0) { virDomainAuditNetDevice(def, net, res_ifname, true); @@ -150,7 +150,7 @@ qemuPhysIfaceConnect(virDomainDefPtr def, VIR_FORCE_CLOSE(rc); delMacvtap(net->ifname, net->mac, net->data.direct.linkdev, net->data.direct.mode, - &net->data.direct.virtPortProfile, + net->data.direct.virtPortProfile, driver->stateDir); VIR_FREE(net->ifname); } diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 0eae661..6c22f4c 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1616,7 +1616,7 @@ int qemuDomainDetachNetDevice(struct qemud_driver *driver, if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) { delMacvtap(detach->ifname, detach->mac, detach->data.direct.linkdev, detach->data.direct.mode, - &detach->data.direct.virtPortProfile, + detach->data.direct.virtPortProfile, driver->stateDir); VIR_FREE(detach->ifname); } diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index dfa80e3..84fe05a 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -2341,7 +2341,7 @@ qemuMigrationVPAssociatePortProfiles(virDomainDefPtr def) { if (vpAssociatePortProfileId(net->ifname, net->mac, net->data.direct.linkdev, - &net->data.direct.virtPortProfile, + net->data.direct.virtPortProfile, def->uuid, VIR_VM_OP_MIGRATE_IN_FINISH) != 0) goto err_exit; @@ -2358,7 +2358,7 @@ err_exit: vpDisassociatePortProfileId(net->ifname, net->mac, net->data.direct.linkdev, - &net->data.direct.virtPortProfile, + net->data.direct.virtPortProfile, VIR_VM_OP_MIGRATE_IN_FINISH); } } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 448b06e..5121241 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3017,7 +3017,7 @@ void qemuProcessStop(struct qemud_driver *driver, if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { delMacvtap(net->ifname, net->mac, net->data.direct.linkdev, net->data.direct.mode, - &net->data.direct.virtPortProfile, driver->stateDir); + net->data.direct.virtPortProfile, driver->stateDir); VIR_FREE(net->ifname); } } diff --git a/src/util/network.c b/src/util/network.c index a323b26..c35668d 100644 --- a/src/util/network.c +++ b/src/util/network.c @@ -685,7 +685,7 @@ VIR_ENUM_IMPL(virVirtualPort, VIR_VIRTUALPORT_TYPE_LAST, int virVirtualPortProfileParamsParseXML(xmlNodePtr node, - virVirtualPortProfileParamsPtr virtPort) + virVirtualPortProfileParamsPtr *def) { int ret = -1; char *virtPortType; @@ -694,8 +694,14 @@ virVirtualPortProfileParamsParseXML(xmlNodePtr node, char *virtPortTypeIDVersion = NULL; char *virtPortInstanceID = NULL; char *virtPortProfileID = NULL; + virVirtualPortProfileParamsPtr virtPort = NULL; xmlNodePtr cur = node->children; + if (VIR_ALLOC(virtPort) < 0) { + virReportOOMError(); + return -1; + } + virtPortType = virXMLPropString(node, "type"); if (!virtPortType) { virSocketError(VIR_ERR_XML_ERROR, "%s", @@ -785,7 +791,7 @@ virVirtualPortProfileParamsParseXML(xmlNodePtr node, } virtPort->virtPortType = VIR_VIRTUALPORT_8021QBG; - ret = 0; + } else { virSocketError(VIR_ERR_XML_ERROR, "%s", _("a parameter is missing for 802.1Qbg description")); @@ -798,7 +804,6 @@ virVirtualPortProfileParamsParseXML(xmlNodePtr node, if (virStrcpyStatic(virtPort->u.virtPort8021Qbh.profileID, virtPortProfileID) != NULL) { virtPort->virtPortType = VIR_VIRTUALPORT_8021QBH; - ret = 0; } else { virSocketError(VIR_ERR_XML_ERROR, "%s", _("profileid parameter too long")); @@ -821,7 +826,11 @@ virVirtualPortProfileParamsParseXML(xmlNodePtr node, break; } + ret = 0; + *def = virtPort; + virtPort = NULL; error: + VIR_FREE(virtPort); VIR_FREE(virtPortManagerID); VIR_FREE(virtPortTypeID); VIR_FREE(virtPortTypeIDVersion); diff --git a/src/util/network.h b/src/util/network.h index 0a8fc03..b69ee3d 100644 --- a/src/util/network.h +++ b/src/util/network.h @@ -130,7 +130,7 @@ struct _virVirtualPortProfileParams { int virVirtualPortProfileParamsParseXML(xmlNodePtr node, - virVirtualPortProfileParamsPtr virtPort); + virVirtualPortProfileParamsPtr *virtPort); void virVirtualPortProfileFormat(virBufferPtr buf, virVirtualPortProfileParamsPtr virtPort, -- 1.7.3.4

On 07/20/2011 12:21 AM, Laine Stump wrote:
The virtPortProfile in the domain interface struct is now a separately allocated object *pointed to by* (rather than contained in) the main virDomainNetDef object. This is done to make it easier to figure out when a virtualPortProfile has/hasn't been specified in a particular config. --- src/conf/domain_conf.c | 17 ++++++++--------- src/conf/domain_conf.h | 2 +- src/qemu/qemu_command.c | 4 ++-- src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_migration.c | 4 ++-- src/qemu/qemu_process.c | 2 +- src/util/network.c | 15 ++++++++++++--- src/util/network.h | 2 +- 8 files changed, 28 insertions(+), 20 deletions(-)
ACK. Splitting this did make reviewing 2 and 3 easier. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

the domain XML <interface> element is updated in the following ways: 1) <virtualportprofile> can be specified when source type='network' (previously it was only valid for source type='direct') (Since virtualPortProfile is going to be used in both the domain and network RNG, its RNG definition was moved into a separate file that will be included by both.) 2) A new attribute "portgroup" has been added to the <source> element. When source type='network' (the only time portgroup is recognized), extra configuration information will be taken from the <portgroup> element of the given name. 3) Each virDomainNetDef now also potentially has a virDomainActualNetDef which is a private object (never exported/imported via the public API, and not defined in the RNG) that is used to maintain information about the physical device that was actually used for a NetDef that was of type VIR_DOMAIN_NET_TYPE_NETWORK. The virDomainActualNetDef will only be parsed/formatted if the parse/format function is called with the VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET flag set (which is only needed when saving/loading a running domain's state info to the stateDir). --- The internal flag created a lot of discussion on the list, and what was finally decided on was to leave the existing VIR_DOMAIN_XML_INTERNAL_STATUS in the regular flags (and also put my new VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET there), but put in a compile-time protection against re-using those bits for the public API, and a runtime check to make sure nobody calls the public API with those bits on. Eric took care of this in a separate patch, which was pushed on Monday. Comments on other changes from V1 are here: https://www.redhat.com/archives/libvir-list/2011-July/msg01289.html https://www.redhat.com/archives/libvir-list/2011-July/msg01290.html docs/formatdomain.html.in | 61 ++++- docs/schemas/domain.rng | 8 + src/conf/domain_conf.c | 284 +++++++++++++++++++- src/conf/domain_conf.h | 40 +++ src/libvirt_private.syms | 7 +- src/libxl/libxl_driver.c | 4 +- .../qemuxml2argv-net-virtio-network-portgroup.xml | 33 +++ tests/qemuxml2xmltest.c | 1 + 8 files changed, 411 insertions(+), 27 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-virtio-network-portgroup.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index a54ee6a..8f42ba9 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1369,24 +1369,55 @@ <p> <strong><em> This is the recommended config for general guest connectivity on - hosts with dynamic / wireless networking configs + hosts with dynamic / wireless networking configs (or multi-host + environments where the host hardware details are described + separately in a <code><network></code> + definition <span class="since">Since 0.9.4</span>). </em></strong> </p> <p> - Provides a virtual network using a bridge device in the host. - Depending on the virtual network configuration, the network may be - totally isolated, NAT'ing to an explicit network device, or NAT'ing to - the default route. DHCP and DNS are provided on the virtual network in - all cases and the IP range can be determined by examining the virtual - network config with '<code>virsh net-dumpxml [networkname]</code>'. - There is one virtual network called 'default' setup out - of the box which does NAT'ing to the default route and has an IP range of - <code>192.168.122.0/255.255.255.0</code>. Each guest will have an - associated tun device created with a name of vnetN, which can also be - overridden with the <target> element (see + + Provides a connection whose details are described by the named + network definition. Depending on the virtual network's "forward + mode" configuration, the network may be totally isolated + (no <code><forward></code> element given), NAT'ing to an + explicit network device or to the default route + (<code><forward mode='nat'></code>), routed with no NAT + (<code><forward mode='route'/></code>), or connected + directly to one of the host's network interfaces (via macvtap) + or bridge devices ((<code><forward + mode='bridge|private|vepa|passthrough'/></code> <span class="since">Since + 0.9.4</span>) + </p> + <p> + For networks with a forward mode of bridge, private, vepa, and + passthrough, it is assumed that the host has any necessary DNS + and DHCP services already setup outside the scope of libvirt. In + the case of isolated, nat, and routed networks, DHCP and DNS are + provided on the virtual network by libvirt, and the IP range can + be determined by examining the virtual network config with + '<code>virsh net-dumpxml [networkname]</code>'. There is one + virtual network called 'default' setup out of the box which does + NAT'ing to the default route and has an IP range + of <code>192.168.122.0/255.255.255.0</code>. Each guest will + have an associated tun device created with a name of vnetN, + which can also be overridden with the <target> element + (see <a href="#elementsNICSTargetOverride">overriding the target element</a>). </p> + <p> + When the source of an interface is a network, + a <code>portgroup</code> can be specified along with the name of + the network; one network may have multiple portgroups defined, + with each portgroup containing slightly different configuration + information for different classes of network + connections. <span class="since">Since 0.9.4</span>). Also, + similar to <code>direct</code> network connections (described + below), a connection of type <code>network</code> may specify + a <code>virtportprofile</code> element, with configuration data + to be forwarded to a vepa or 802.1Qbh compliant switch. + </p> <pre> ... @@ -1396,9 +1427,13 @@ </interface> ... <interface type='network'> - <source network='default'/> + <source network='default' portgroup='engineering'/> <target dev='vnet7'/> <mac address="00:11:22:33:44:55"/> + <virtualport type='802.1Qbg'> + <parameters managerid='11' typeid='1193047' typeidversion='2' instanceid='09b11c53-8b5c-4eeb-8f00-d84eaa0aaa4f'/> + </virtualport> + </interface> </devices> ...</pre> diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 2f656ed..07c63bd 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1026,8 +1026,16 @@ <attribute name="network"> <ref name="deviceName"/> </attribute> + <optional> + <attribute name="portgroup"> + <ref name="deviceName"/> + </attribute> + </optional> <empty/> </element> + <optional> + <ref name="virtualPortProfile"/> + </optional> <ref name="interface-options"/> </interleave> </group> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 24f9b00..4c58633 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -56,10 +56,11 @@ * verify that it doesn't overflow an unsigned int when shifting */ verify(VIR_DOMAIN_VIRT_LAST <= 32); -/* Private flag used internally by virDomainSaveStatus and - * virDomainObjParseFile. */ +/* Private flags used internally by virDomainSaveStatus and + * virDomainLoadStatus. */ typedef enum { VIR_DOMAIN_XML_INTERNAL_STATUS = (1<<16), /* dump internal domain status information */ + VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET = (1<<17), /* dump/parse <actual> element */ } virDomainXMLInternalFlags; VIR_ENUM_IMPL(virDomainTaint, VIR_DOMAIN_TAINT_LAST, @@ -734,6 +735,25 @@ void virDomainFSDefFree(virDomainFSDefPtr def) VIR_FREE(def); } +void +virDomainActualNetDefFree(virDomainActualNetDefPtr def) +{ + if (!def) + return; + + switch (def->type) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: + VIR_FREE(def->data.bridge.brname); + break; + case VIR_DOMAIN_NET_TYPE_DIRECT: + VIR_FREE(def->data.direct.linkdev); + VIR_FREE(def->data.direct.virtPortProfile); + break; + default: + break; + } +} + void virDomainNetDefFree(virDomainNetDefPtr def) { if (!def) @@ -756,6 +776,9 @@ void virDomainNetDefFree(virDomainNetDefPtr def) case VIR_DOMAIN_NET_TYPE_NETWORK: VIR_FREE(def->data.network.name); + VIR_FREE(def->data.network.portgroup); + VIR_FREE(def->data.network.virtPortProfile); + virDomainActualNetDefFree(def->data.network.actual); break; case VIR_DOMAIN_NET_TYPE_BRIDGE: @@ -2586,6 +2609,80 @@ cleanup: goto cleanup; } +static int +virDomainActualNetDefParseXML(xmlNodePtr node, + xmlXPathContextPtr ctxt, + virDomainActualNetDefPtr *def) +{ + virDomainActualNetDefPtr actual = NULL; + int ret = -1; + xmlNodePtr save_ctxt = ctxt->node; + char *type = NULL; + char *mode = NULL; + + if (VIR_ALLOC(actual) < 0) { + virReportOOMError(); + return -1; + } + + ctxt->node = node; + + type = virXMLPropString(node, "type"); + if (!type) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing type attribute in interface's <actual> element")); + goto error; + } + if ((int)(actual->type = virDomainNetTypeFromString(type)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown type '%s' in interface's <actual> element"), type); + goto error; + } + if (actual->type != VIR_DOMAIN_NET_TYPE_BRIDGE && + actual->type != VIR_DOMAIN_NET_TYPE_DIRECT) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported type '%s' in interface's <actual> element"), + type); + goto error; + } + + if (actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { + actual->data.bridge.brname = virXPathString("string(./source[1]/@bridge)", ctxt); + } else if (actual->type == VIR_DOMAIN_NET_TYPE_DIRECT) { + xmlNodePtr virtPortNode; + + actual->data.direct.linkdev = virXPathString("string(./source[1]/@dev)", ctxt); + + mode = virXPathString("string(./source[1]/@mode)", ctxt); + if (mode) { + int m; + if ((m = virMacvtapModeTypeFromString(mode)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("Unkown mode '%s' in interface <actual> element"), + mode); + goto error; + } + actual->data.direct.mode = m; + } + + virtPortNode = virXPathNode("./virtualport", ctxt); + if (virtPortNode && + virVirtualPortProfileParamsParseXML(virtPortNode, + &actual->data.direct.virtPortProfile) < 0) { + goto error; + } + } + + *def = actual; + actual = NULL; + ret = 0; +error: + VIR_FREE(type); + VIR_FREE(mode); + + ctxt->node = save_ctxt; + return ret; +} /* Parse the XML definition for a network interface * @param node XML nodeset to parse for net definition @@ -2603,6 +2700,7 @@ virDomainNetDefParseXML(virCapsPtr caps, char *macaddr = NULL; char *type = NULL; char *network = NULL; + char *portgroup = NULL; char *bridge = NULL; char *dev = NULL; char *ifname = NULL; @@ -2619,6 +2717,7 @@ virDomainNetDefParseXML(virCapsPtr caps, char *mode = NULL; virNWFilterHashTablePtr filterparams = NULL; virVirtualPortProfileParamsPtr virtPort = NULL; + virDomainActualNetDefPtr actual = NULL; xmlNodePtr oldnode = ctxt->node; int ret; @@ -2650,6 +2749,7 @@ virDomainNetDefParseXML(virCapsPtr caps, (def->type == VIR_DOMAIN_NET_TYPE_NETWORK) && (xmlStrEqual(cur->name, BAD_CAST "source"))) { network = virXMLPropString(cur, "network"); + portgroup = virXMLPropString(cur, "portgroup"); } else if ((internal == NULL) && (def->type == VIR_DOMAIN_NET_TYPE_INTERNAL) && (xmlStrEqual(cur->name, BAD_CAST "source"))) { @@ -2665,7 +2765,8 @@ virDomainNetDefParseXML(virCapsPtr caps, dev = virXMLPropString(cur, "dev"); mode = virXMLPropString(cur, "mode"); } else if ((virtPort == NULL) && - (def->type == VIR_DOMAIN_NET_TYPE_DIRECT) && + ((def->type == VIR_DOMAIN_NET_TYPE_DIRECT) || + (def->type == VIR_DOMAIN_NET_TYPE_NETWORK)) && xmlStrEqual(cur->name, BAD_CAST "virtualport")) { if (virVirtualPortProfileParamsParseXML(cur, &virtPort) < 0) goto error; @@ -2713,6 +2814,12 @@ virDomainNetDefParseXML(virCapsPtr caps, if (virDomainDeviceBootParseXML(cur, &def->bootIndex, bootMap)) goto error; + } else if ((actual == NULL) && + (flags & VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET) && + (def->type == VIR_DOMAIN_NET_TYPE_NETWORK) && + xmlStrEqual(cur->name, BAD_CAST "actual")) { + if (virDomainActualNetDefParseXML(cur, ctxt, &actual) < 0) + goto error; } } cur = cur->next; @@ -2761,6 +2868,12 @@ virDomainNetDefParseXML(virCapsPtr caps, } def->data.network.name = network; network = NULL; + def->data.network.portgroup = portgroup; + portgroup = NULL; + def->data.network.virtPortProfile = virtPort; + virtPort = NULL; + def->data.network.actual = actual; + actual = NULL; break; case VIR_DOMAIN_NET_TYPE_ETHERNET: @@ -2956,11 +3069,13 @@ cleanup: ctxt->node = oldnode; VIR_FREE(macaddr); VIR_FREE(network); + VIR_FREE(portgroup); VIR_FREE(address); VIR_FREE(port); VIR_FREE(ifname); VIR_FREE(dev); VIR_FREE(virtPort); + virDomainActualNetDefFree(actual); VIR_FREE(script); VIR_FREE(bridge); VIR_FREE(model); @@ -6772,7 +6887,8 @@ virDomainDefPtr virDomainDefParseNode(virCapsPtr caps, } ctxt->node = root; - def = virDomainDefParseXML(caps, xml, root, ctxt, expectedVirtTypes, flags); + def = virDomainDefParseXML(caps, xml, root, ctxt, expectedVirtTypes, + flags); cleanup: xmlXPathFreeContext(ctxt); @@ -6803,7 +6919,8 @@ virDomainObjParseNode(virCapsPtr caps, } ctxt->node = root; - obj = virDomainObjParseXML(caps, xml, ctxt, expectedVirtTypes, flags); + obj = virDomainObjParseXML(caps, xml, ctxt, expectedVirtTypes, + flags); cleanup: xmlXPathFreeContext(ctxt); @@ -8529,6 +8646,67 @@ virDomainFSDefFormat(virBufferPtr buf, } static int +virDomainActualNetDefFormat(virBufferPtr buf, + virDomainActualNetDefPtr def) +{ + int ret = -1; + const char *type; + const char *mode; + + if (!def) + return 0; + + type = virDomainNetTypeToString(def->type); + if (!type) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected net type %d"), def->type); + return ret; + } + + if (def->type != VIR_DOMAIN_NET_TYPE_BRIDGE && + def->type != VIR_DOMAIN_NET_TYPE_DIRECT) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected net type %s"), type); + goto error; + } + virBufferAsprintf(buf, " <actual type='%s'>\n", type); + + switch (def->type) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: + if (def->data.bridge.brname) { + virBufferEscapeString(buf, " <source bridge='%s'/>\n", + def->data.bridge.brname); + } + break; + + case VIR_DOMAIN_NET_TYPE_DIRECT: + virBufferAddLit(buf, " <source"); + if (def->data.direct.linkdev) + virBufferEscapeString(buf, " dev='%s'", + def->data.direct.linkdev); + + mode = virMacvtapModeTypeToString(def->data.direct.mode); + if (!mode) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected source mode %d"), + def->data.direct.mode); + return ret; + } + virBufferAsprintf(buf, " mode='%s'/>\n", mode); + virVirtualPortProfileFormat(buf, def->data.direct.virtPortProfile, + " "); + break; + default: + break; + } + virBufferAddLit(buf, " </actual>\n"); + + ret = 0; +error: + return ret; +} + +static int virDomainNetDefFormat(virBufferPtr buf, virDomainNetDefPtr def, unsigned int flags) @@ -8551,8 +8729,18 @@ virDomainNetDefFormat(virBufferPtr buf, switch (def->type) { case VIR_DOMAIN_NET_TYPE_NETWORK: - virBufferEscapeString(buf, " <source network='%s'/>\n", + virBufferEscapeString(buf, " <source network='%s'", def->data.network.name); + if (def->data.network.portgroup) { + virBufferEscapeString(buf, " portgroup='%s'", + def->data.network.portgroup); + } + virBufferAddLit(buf, "/>\n"); + virVirtualPortProfileFormat(buf, def->data.network.virtPortProfile, + " "); + if ((flags & VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET) && + (virDomainActualNetDefFormat(buf, def->data.network.actual) < 0)) + return -1; break; case VIR_DOMAIN_NET_TYPE_ETHERNET: @@ -9437,9 +9625,11 @@ virDomainHostdevDefFormat(virBufferPtr buf, VIR_DOMAIN_XML_INACTIVE | \ VIR_DOMAIN_XML_UPDATE_CPU) -verify((VIR_DOMAIN_XML_INTERNAL_STATUS & DUMPXML_FLAGS) == 0); +verify(((VIR_DOMAIN_XML_INTERNAL_STATUS | + VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET) + & DUMPXML_FLAGS) == 0); -/* This internal version can accept VIR_DOMAIN_XML_INTERNAL_STATUS, +/* This internal version can accept VIR_DOMAIN_XML_INTERNAL_*, * whereas the public version cannot. */ static char * virDomainDefFormatInternal(virDomainDefPtr def, @@ -9451,7 +9641,10 @@ virDomainDefFormatInternal(virDomainDefPtr def, const char *type = NULL; int n, allones = 1; - virCheckFlags(DUMPXML_FLAGS | VIR_DOMAIN_XML_INTERNAL_STATUS, NULL); + virCheckFlags(DUMPXML_FLAGS | + VIR_DOMAIN_XML_INTERNAL_STATUS | + VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET, + NULL); if (!(type = virDomainVirtTypeToString(def->virtType))) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, @@ -10008,7 +10201,10 @@ int virDomainSaveStatus(virCapsPtr caps, const char *statusDir, virDomainObjPtr obj) { - unsigned int flags = VIR_DOMAIN_XML_SECURE|VIR_DOMAIN_XML_INTERNAL_STATUS; + unsigned int flags = (VIR_DOMAIN_XML_SECURE | + VIR_DOMAIN_XML_INTERNAL_STATUS | + VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET); + int ret = -1; char *xml; @@ -10099,7 +10295,8 @@ static virDomainObjPtr virDomainLoadStatus(virCapsPtr caps, goto error; if (!(obj = virDomainObjParseFile(caps, statusFile, expectedVirtTypes, - VIR_DOMAIN_XML_INTERNAL_STATUS))) + VIR_DOMAIN_XML_INTERNAL_STATUS | + VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET))) goto error; virUUIDFormat(obj->def->uuid, uuidstr); @@ -11096,3 +11293,68 @@ virDomainStateReasonFromString(virDomainState state, const char *reason) return -1; } + + +/* Some access functions to gloss over the difference between NetDef + * (<interface>) and ActualNetDef (<actual>). If the NetDef has an + * ActualNetDef, return the requested value from the ActualNetDef, + * otherwise return the value from the NetDef. + */ + +int +virDomainNetGetActualType(virDomainNetDefPtr iface) +{ + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) + return iface->type; + if (!iface->data.network.actual) + return iface->type; + return iface->data.network.actual->type; +} + +char * +virDomainNetGetActualBridgeName(virDomainNetDefPtr iface) +{ + if (iface->type == VIR_DOMAIN_NET_TYPE_BRIDGE) + return iface->data.bridge.brname; + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) + return NULL; + if (!iface->data.network.actual) + return NULL; + return iface->data.network.actual->data.bridge.brname; +} + +char * +virDomainNetGetActualDirectDev(virDomainNetDefPtr iface) +{ + if (iface->type == VIR_DOMAIN_NET_TYPE_DIRECT) + return iface->data.direct.linkdev; + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) + return NULL; + if (!iface->data.network.actual) + return NULL; + return iface->data.network.actual->data.direct.linkdev; +} + +int +virDomainNetGetActualDirectMode(virDomainNetDefPtr iface) +{ + if (iface->type == VIR_DOMAIN_NET_TYPE_DIRECT) + return iface->data.direct.mode; + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) + return 0; + if (!iface->data.network.actual) + return 0; + return iface->data.network.actual->data.direct.mode; +} + +virVirtualPortProfileParamsPtr +virDomainNetGetActualDirectVirtPortProfile(virDomainNetDefPtr iface) +{ + if (iface->type == VIR_DOMAIN_NET_TYPE_DIRECT) + return iface->data.direct.virtPortProfile; + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) + return NULL; + if (!iface->data.network.actual) + return NULL; + return iface->data.network.actual->data.direct.virtPortProfile; +} diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 69e74dc..9e9db41 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -343,6 +343,27 @@ enum virDomainNetVirtioTxModeType { VIR_DOMAIN_NET_VIRTIO_TX_MODE_LAST, }; +/* Config that was actually used to bring up interface, after + * resolving network reference. This is private data, only used within + * libvirt, but still must maintain backward compatibility, because + * different versions of libvirt may read the same data file. + */ +typedef struct _virDomainActualNetDef virDomainActualNetDef; +typedef virDomainActualNetDef *virDomainActualNetDefPtr; +struct _virDomainActualNetDef { + enum virDomainNetType type; + union { + struct { + char *brname; + } bridge; + struct { + char *linkdev; + int mode; /* enum virMacvtapMode from util/macvtap.h */ + virVirtualPortProfileParamsPtr virtPortProfile; + } direct; + } data; +}; + /* Stores the virtual network interface configuration */ typedef struct _virDomainNetDef virDomainNetDef; typedef virDomainNetDef *virDomainNetDefPtr; @@ -369,6 +390,17 @@ struct _virDomainNetDef { } socket; /* any of NET_CLIENT or NET_SERVER or NET_MCAST */ struct { char *name; + char *portgroup; + virVirtualPortProfileParamsPtr virtPortProfile; + /* actual has info about the currently used physical + * device (if the network is of type + * bridge/private/vepa/passthrough). This is saved in the + * domain state, but never written to persistent config, + * 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. + */ + virDomainActualNetDefPtr actual; } network; struct { char *brname; @@ -1338,6 +1370,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def); void virDomainDiskHostDefFree(virDomainDiskHostDefPtr def); void virDomainControllerDefFree(virDomainControllerDefPtr def); void virDomainFSDefFree(virDomainFSDefPtr def); +void virDomainActualNetDefFree(virDomainActualNetDefPtr def); void virDomainNetDefFree(virDomainNetDefPtr def); void virDomainSmartcardDefFree(virDomainSmartcardDefPtr def); void virDomainChrDefFree(virDomainChrDefPtr def); @@ -1448,6 +1481,13 @@ int virDomainNetIndexByMac(virDomainDefPtr def, const unsigned char *mac); int virDomainNetInsert(virDomainDefPtr def, virDomainNetDefPtr net); int virDomainNetRemoveByMac(virDomainDefPtr def, const unsigned char *mac); +int virDomainNetGetActualType(virDomainNetDefPtr iface); +char *virDomainNetGetActualBridgeName(virDomainNetDefPtr iface); +char *virDomainNetGetActualDirectDev(virDomainNetDefPtr iface); +int virDomainNetGetActualDirectMode(virDomainNetDefPtr iface); +virVirtualPortProfileParamsPtr +virDomainNetGetActualDirectVirtPortProfile(virDomainNetDefPtr iface); + int virDomainControllerInsert(virDomainDefPtr def, virDomainControllerDefPtr controller); void virDomainControllerInsertPreAlloced(virDomainDefPtr def, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fba596e..63e8aea 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -223,6 +223,7 @@ virDomainAuditVcpu; # domain_conf.h virDiskNameToBusDeviceIndex; virDiskNameToIndex; +virDomainActualNetDefFree; virDomainAssignDef; virDomainChrConsoleTargetTypeFromString; virDomainChrConsoleTargetTypeToString; @@ -329,6 +330,11 @@ virDomainLoadAllConfigs; virDomainMemballoonModelTypeFromString; virDomainMemballoonModelTypeToString; virDomainNetDefFree; +virDomainNetGetActualBridgeName; +virDomainNetGetActualDirectDev; +virDomainNetGetActualDirectMode; +virDomainNetGetActualType; +virDomainNetGetActualDirectVirtPortProfile; virDomainNetIndexByMac; virDomainNetInsert; virDomainNetRemoveByMac; @@ -743,7 +749,6 @@ virNetworkSaveConfig; virNetworkSetBridgeMacAddr; virNetworkSetBridgeName; - # node_device_conf.h virNodeDevCapTypeToString; virNodeDevCapsDefFree; diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index cc37d05..8c983f2 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -707,7 +707,7 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm, } vm->def->id = domid; - if ((dom_xml = virDomainDefFormat(vm->def, 0)) == NULL) + if ((dom_xml = virDomainDefFormat(vm->def)) == NULL) goto error; if (libxl_userdata_store(&priv->ctx, domid, "libvirt-xml", @@ -1852,7 +1852,7 @@ libxlDoDomainSave(libxlDriverPrivatePtr driver, virDomainObjPtr vm, goto cleanup; } - if ((xml = virDomainDefFormat(vm->def, 0)) == NULL) + if ((xml = virDomainDefFormat(vm->def)) == NULL) goto cleanup; xml_len = strlen(xml) + 1; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-virtio-network-portgroup.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-virtio-network-portgroup.xml new file mode 100644 index 0000000..0f6e076 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-virtio-network-portgroup.xml @@ -0,0 +1,33 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219136</memory> + <currentMemory>219136</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' unit='0'/> + </disk> + <controller type='ide' index='0'/> + <interface type='network'> + <mac address='00:11:22:33:44:55'/> + <source network='rednet' portgroup='bob'/> + <virtualport type='802.1Qbg'> + <parameters managerid='11' typeid='1193047' typeidversion='2' instanceid='09b11c53-8b5c-4eeb-8f00-d84eaa0aaa4f'/> + </virtualport> + <model type='virtio'/> + </interface> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index f22872f..6b1fbf5 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -158,6 +158,7 @@ mymain(void) DO_TEST("net-virtio-device"); DO_TEST("net-eth"); DO_TEST("net-eth-ifname"); + DO_TEST("net-virtio-network-portgroup"); DO_TEST("sound"); DO_TEST("serial-vc"); -- 1.7.3.4

On 07/20/2011 12:21 AM, Laine Stump wrote:
the domain XML<interface> element is updated in the following ways:
1)<virtualportprofile> can be specified when source type='network' (previously it was only valid for source type='direct')
(Since virtualPortProfile is going to be used in both the domain and network RNG, its RNG definition was moved into a separate file that will be included by both.)
2) A new attribute "portgroup" has been added to the<source> element. When source type='network' (the only time portgroup is recognized), extra configuration information will be taken from the <portgroup> element of the given name.
3) Each virDomainNetDef now also potentially has a virDomainActualNetDef which is a private object (never exported/imported via the public API, and not defined in the RNG) that is used to maintain information about the physical device that was actually used for a NetDef that was of type VIR_DOMAIN_NET_TYPE_NETWORK.
The virDomainActualNetDef will only be parsed/formatted if the parse/format function is called with the VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET flag set (which is only needed when saving/loading a running domain's state info to the stateDir). ---
The internal flag created a lot of discussion on the list, and what was finally decided on was to leave the existing VIR_DOMAIN_XML_INTERNAL_STATUS in the regular flags (and also put my new VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET there), but put in a compile-time protection against re-using those bits for the public API, and a runtime check to make sure nobody calls the public API with those bits on. Eric took care of this in a separate patch, which was pushed on Monday.
And in case it isn't obvious: If we ever add a new public bit in the future such that the compile-time test fails, we simply move the internal bits to another free location and recompile - since they are internal, they do not leak over RPC, so there are no cross-version dependencies and thus can be changed at will. Meanwhile, if a newer version of libvirt adds new bits and talks to an older version, the runtime check guarantees that the new bits will be rejected as unknown rather than accidentally turning on the internal behavior of the older version.
<p> - Provides a virtual network using a bridge device in the host.
...
- overridden with the<target> element (see + + Provides a connection whose details are described by the named
Why the blank line between <p> and the start of the paragraph? [and I really hate it that thunderbird is back to its habits of munging quoted text in my emails again - for a while there, I was running a version where the problem went away, but the latest distro upgrade made it resurface]
+void +virDomainActualNetDefFree(virDomainActualNetDefPtr def)
Add this to cfg.mk's list of free-like functions.
+{ + if (!def) + return; + + switch (def->type) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: + VIR_FREE(def->data.bridge.brname); + break; + case VIR_DOMAIN_NET_TYPE_DIRECT: + VIR_FREE(def->data.direct.linkdev); + VIR_FREE(def->data.direct.virtPortProfile); + break; + default: + break; + } +}
Memory leak of def itself.
+static int +virDomainActualNetDefParseXML(xmlNodePtr node, + xmlXPathContextPtr ctxt, + virDomainActualNetDefPtr *def) +{ + virDomainActualNetDefPtr actual = NULL; + int ret = -1; + xmlNodePtr save_ctxt = ctxt->node; + char *type = NULL; + char *mode = NULL; + + if (VIR_ALLOC(actual)< 0) { + virReportOOMError(); + return -1; + } + + ctxt->node = node; + + type = virXMLPropString(node, "type"); + if (!type) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing type attribute in interface's<actual> element")); + goto error; + } + if ((int)(actual->type = virDomainNetTypeFromString(type))< 0) {
This cast is not needed if you tweak domain_conf.h.
+ + *def = actual; + actual = NULL; + ret = 0; +error: + VIR_FREE(type); + VIR_FREE(mode); + + ctxt->node = save_ctxt; + return ret;
Memory leak of actual on error cases.
@@ -6772,7 +6887,8 @@ virDomainDefPtr virDomainDefParseNode(virCapsPtr caps, }
ctxt->node = root; - def = virDomainDefParseXML(caps, xml, root, ctxt, expectedVirtTypes, flags); + def = virDomainDefParseXML(caps, xml, root, ctxt, expectedVirtTypes, + flags);
Your change and then undo made for a spurious hunk.
cleanup: xmlXPathFreeContext(ctxt); @@ -6803,7 +6919,8 @@ virDomainObjParseNode(virCapsPtr caps, }
ctxt->node = root; - obj = virDomainObjParseXML(caps, xml, ctxt, expectedVirtTypes, flags); + obj = virDomainObjParseXML(caps, xml, ctxt, expectedVirtTypes, + flags);
and another one.
@@ -10008,7 +10201,10 @@ int virDomainSaveStatus(virCapsPtr caps, const char *statusDir, virDomainObjPtr obj) { - unsigned int flags = VIR_DOMAIN_XML_SECURE|VIR_DOMAIN_XML_INTERNAL_STATUS; + unsigned int flags = (VIR_DOMAIN_XML_SECURE | + VIR_DOMAIN_XML_INTERNAL_STATUS | + VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET); + int ret = -1; char *xml;
@@ -10099,7 +10295,8 @@ static virDomainObjPtr virDomainLoadStatus(virCapsPtr caps, goto error;
if (!(obj = virDomainObjParseFile(caps, statusFile, expectedVirtTypes, - VIR_DOMAIN_XML_INTERNAL_STATUS))) + VIR_DOMAIN_XML_INTERNAL_STATUS | + VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET)))
Thinking out loud here - it looks like we never use _INTERNAL_STATUS or _INTERNAL_ACTUAL_NET in isolation - as of this patch, it is always both or neither. Maybe we could have let _INTERNAL_STATUS be the key on whether to also output/parse <actual> elements rather than adding a new flag. On the other hand, if we ever change our mind and decide that it makes sense to let the user do 'virsh dumpxml dom --actual' to see which resources actually got used, then it is easier to change VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET into a public flag, than it is if we lump all internal actions under a single _INTERNAL_STATUS flags. So no change necessary for now.
+++ b/src/conf/domain_conf.h @@ -343,6 +343,27 @@ enum virDomainNetVirtioTxModeType { VIR_DOMAIN_NET_VIRTIO_TX_MODE_LAST, };
+/* Config that was actually used to bring up interface, after + * resolving network reference. This is private data, only used within + * libvirt, but still must maintain backward compatibility, because + * different versions of libvirt may read the same data file. + */ +typedef struct _virDomainActualNetDef virDomainActualNetDef; +typedef virDomainActualNetDef *virDomainActualNetDefPtr; +struct _virDomainActualNetDef { + enum virDomainNetType type;
Per the earlier cast comment, use int here, to match most other _virDomain*Def typed unions.
@@ -743,7 +749,6 @@ virNetworkSaveConfig; virNetworkSetBridgeMacAddr; virNetworkSetBridgeName;
- # node_device_conf.h
A spurious whitespace change.
+++ b/src/libxl/libxl_driver.c @@ -707,7 +707,7 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm, }
vm->def->id = domid; - if ((dom_xml = virDomainDefFormat(vm->def, 0)) == NULL) + if ((dom_xml = virDomainDefFormat(vm->def)) == NULL)
Oops, that's a compile failure, caused by over-undoing your now-abandoned idea of adding a parameter. ACK with this squashed in: diff --git i/cfg.mk w/cfg.mk index 773a3df..f2a951d 100644 --- i/cfg.mk +++ w/cfg.mk @@ -97,6 +97,7 @@ useless_free_options = \ --name=virCommandFree \ --name=virConfFreeList \ --name=virConfFreeValue \ + --name=virDomainActualNetDefFree \ --name=virDomainChrDefFree \ --name=virDomainChrSourceDefFree \ --name=virDomainControllerDefFree \ diff --git i/src/conf/domain_conf.c w/src/conf/domain_conf.c index e6d07d1..f4a42db 100644 --- i/src/conf/domain_conf.c +++ w/src/conf/domain_conf.c @@ -752,6 +752,8 @@ virDomainActualNetDefFree(virDomainActualNetDefPtr def) default: break; } + + VIR_FREE(def); } void virDomainNetDefFree(virDomainNetDefPtr def) @@ -2633,7 +2635,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, _("missing type attribute in interface's <actual> element")); goto error; } - if ((int)(actual->type = virDomainNetTypeFromString(type)) < 0) { + if ((actual->type = virDomainNetTypeFromString(type)) < 0) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, _("unknown type '%s' in interface's <actual> element"), type); goto error; @@ -2679,6 +2681,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, error: VIR_FREE(type); VIR_FREE(mode); + virDomainActualNetDefFree(actual); ctxt->node = save_ctxt; return ret; @@ -6887,8 +6890,7 @@ virDomainDefPtr virDomainDefParseNode(virCapsPtr caps, } ctxt->node = root; - def = virDomainDefParseXML(caps, xml, root, ctxt, expectedVirtTypes, - flags); + def = virDomainDefParseXML(caps, xml, root, ctxt, expectedVirtTypes, flags); cleanup: xmlXPathFreeContext(ctxt); @@ -6919,8 +6921,7 @@ virDomainObjParseNode(virCapsPtr caps, } ctxt->node = root; - obj = virDomainObjParseXML(caps, xml, ctxt, expectedVirtTypes, - flags); + obj = virDomainObjParseXML(caps, xml, ctxt, expectedVirtTypes, flags); cleanup: xmlXPathFreeContext(ctxt); diff --git i/src/conf/domain_conf.h w/src/conf/domain_conf.h index 9e9db41..11d27f3 100644 --- i/src/conf/domain_conf.h +++ w/src/conf/domain_conf.h @@ -351,7 +351,7 @@ enum virDomainNetVirtioTxModeType { typedef struct _virDomainActualNetDef virDomainActualNetDef; typedef virDomainActualNetDef *virDomainActualNetDefPtr; struct _virDomainActualNetDef { - enum virDomainNetType type; + int type; /* enum virDomainNetType */ union { struct { char *brname; diff --git i/src/libvirt_private.syms w/src/libvirt_private.syms index f03951f..306550b 100644 --- i/src/libvirt_private.syms +++ w/src/libvirt_private.syms @@ -743,6 +743,7 @@ virNetworkSaveConfig; virNetworkSetBridgeMacAddr; virNetworkSetBridgeName; + # node_device_conf.h virNodeDevCapTypeToString; virNodeDevCapsDefFree; diff --git i/src/libxl/libxl_driver.c w/src/libxl/libxl_driver.c index fa3f6a8..e84fa36 100644 --- i/src/libxl/libxl_driver.c +++ w/src/libxl/libxl_driver.c @@ -707,7 +707,7 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm, } vm->def->id = domid; - if ((dom_xml = virDomainDefFormat(vm->def)) == NULL) + if ((dom_xml = virDomainDefFormat(vm->def, 0)) == NULL) goto error; if (libxl_userdata_store(&priv->ctx, domid, "libvirt-xml", @@ -1852,7 +1852,7 @@ libxlDoDomainSave(libxlDriverPrivatePtr driver, virDomainObjPtr vm, goto cleanup; } - if ((xml = virDomainDefFormat(vm->def)) == NULL) + if ((xml = virDomainDefFormat(vm->def, 0)) == NULL) goto cleanup; xml_len = strlen(xml) + 1; -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 07/20/2011 05:54 PM, Eric Blake wrote:
On 07/20/2011 12:21 AM, Laine Stump wrote:
<p> - Provides a virtual network using a bridge device in the host.
...
- overridden with the<target> element (see + + Provides a connection whose details are described by the named
Why the blank line between <p> and the start of the paragraph?
I usually put a few blank lines in between what I'm changing and the surrounding text to minimize the amount of changed lines due to re-flowing. I then close up the blank lines before I'm done. OR I don't - this time I forgot.
[and I really hate it that thunderbird is back to its habits of munging quoted text in my emails again - for a while there, I was running a version where the problem went away, but the latest distro upgrade made it resurface]
I've been very bothered lately by Thunderbird's belief that it's okay to mess around with my text however it sees fit. For example, I can't get it to honor extra indenting whitespace I put at the beginning of lines when I'm putting XML examples in my message.
+void +virDomainActualNetDefFree(virDomainActualNetDefPtr def)
Add this to cfg.mk's list of free-like functions.
+{ + if (!def) + return; + + switch (def->type) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: + VIR_FREE(def->data.bridge.brname); + break; + case VIR_DOMAIN_NET_TYPE_DIRECT: + VIR_FREE(def->data.direct.linkdev); + VIR_FREE(def->data.direct.virtPortProfile); + break; + default: + break; + } +}
Memory leak of def itself.
Derp!
+static int +virDomainActualNetDefParseXML(xmlNodePtr node, + xmlXPathContextPtr ctxt, + virDomainActualNetDefPtr *def) +{ + virDomainActualNetDefPtr actual = NULL; + int ret = -1; + xmlNodePtr save_ctxt = ctxt->node; + char *type = NULL; + char *mode = NULL; + + if (VIR_ALLOC(actual)< 0) { + virReportOOMError(); + return -1; + } + + ctxt->node = node; + + type = virXMLPropString(node, "type"); + if (!type) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing type attribute in interface's<actual> element")); + goto error; + } + if ((int)(actual->type = virDomainNetTypeFromString(type))< 0) {
This cast is not needed if you tweak domain_conf.h.
+ + *def = actual; + actual = NULL; + ret = 0; +error: + VIR_FREE(type); + VIR_FREE(mode); + + ctxt->node = save_ctxt; + return ret;
Memory leak of actual on error cases.
Double derp! (This previously didn't leak because I allocated directly into the parent object, and let the caller's destructor cleanup on failure).
@@ -6772,7 +6887,8 @@ virDomainDefPtr virDomainDefParseNode(virCapsPtr caps, }
ctxt->node = root; - def = virDomainDefParseXML(caps, xml, root, ctxt, expectedVirtTypes, flags); + def = virDomainDefParseXML(caps, xml, root, ctxt, expectedVirtTypes, + flags);
Your change and then undo made for a spurious hunk.
cleanup: xmlXPathFreeContext(ctxt); @@ -6803,7 +6919,8 @@ virDomainObjParseNode(virCapsPtr caps, }
ctxt->node = root; - obj = virDomainObjParseXML(caps, xml, ctxt, expectedVirtTypes, flags); + obj = virDomainObjParseXML(caps, xml, ctxt, expectedVirtTypes, + flags);
and another one.
@@ -10008,7 +10201,10 @@ int virDomainSaveStatus(virCapsPtr caps, const char *statusDir, virDomainObjPtr obj) { - unsigned int flags = VIR_DOMAIN_XML_SECURE|VIR_DOMAIN_XML_INTERNAL_STATUS; + unsigned int flags = (VIR_DOMAIN_XML_SECURE | + VIR_DOMAIN_XML_INTERNAL_STATUS | + VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET); + int ret = -1; char *xml;
@@ -10099,7 +10295,8 @@ static virDomainObjPtr virDomainLoadStatus(virCapsPtr caps, goto error;
if (!(obj = virDomainObjParseFile(caps, statusFile, expectedVirtTypes, - VIR_DOMAIN_XML_INTERNAL_STATUS))) + VIR_DOMAIN_XML_INTERNAL_STATUS | + VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET)))
Thinking out loud here - it looks like we never use _INTERNAL_STATUS or _INTERNAL_ACTUAL_NET in isolation - as of this patch, it is always both or neither. Maybe we could have let _INTERNAL_STATUS be the key on whether to also output/parse <actual> elements rather than adding a new flag. On the other hand, if we ever change our mind and decide that it makes sense to let the user do 'virsh dumpxml dom --actual' to see which resources actually got used, then it is easier to change VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET into a public flag, than it is if we lump all internal actions under a single _INTERNAL_STATUS flags. So no change necessary for now.
+++ b/src/conf/domain_conf.h @@ -343,6 +343,27 @@ enum virDomainNetVirtioTxModeType { VIR_DOMAIN_NET_VIRTIO_TX_MODE_LAST, };
+/* Config that was actually used to bring up interface, after + * resolving network reference. This is private data, only used within + * libvirt, but still must maintain backward compatibility, because + * different versions of libvirt may read the same data file. + */ +typedef struct _virDomainActualNetDef virDomainActualNetDef; +typedef virDomainActualNetDef *virDomainActualNetDefPtr; +struct _virDomainActualNetDef { + enum virDomainNetType type;
Per the earlier cast comment, use int here, to match most other _virDomain*Def typed unions.
Okay. But the original struct this was copied from (virDomainNetDef) uses the enum directly.
@@ -743,7 +749,6 @@ virNetworkSaveConfig; virNetworkSetBridgeMacAddr; virNetworkSetBridgeName;
- # node_device_conf.h
A spurious whitespace change.
+++ b/src/libxl/libxl_driver.c @@ -707,7 +707,7 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm, }
vm->def->id = domid; - if ((dom_xml = virDomainDefFormat(vm->def, 0)) == NULL) + if ((dom_xml = virDomainDefFormat(vm->def)) == NULL)
Oops, that's a compile failure, caused by over-undoing your now-abandoned idea of adding a parameter.
ACK with this squashed in:
Squashed and pushed. Thanks!

The network XML is updated in the following ways: 1) The <forward> element can now contain a list of forward interfaces: <forward .... > <interface dev='eth10'/> <interface dev='eth11'/> <interface dev='eth12'/> <interface dev='eth13'/> </forward> The first of these takes the place of the dev attribute that is normally in <forward> - on when defining a network you can specify either one, and on output both will be present. If you specify both, they must match. 2) In addition to forward modes of 'nat' and 'route', these new modes are supported: private, passthrough, vepa - when this network is referenced by a domain interface, it will have the same effect as if the interface had specified, e.g.: <interface type='direct'> <source mode='${mode}' dev='${dev}> ... </interface> where ${mode} is one of the three new modes, and ${dev} is an interface selected from the list given in <forward>. bridge - if a <forward> dev (or multiple devs) is defined, and forward mode is 'bridge' this is just like the modes 'private', 'passthrough', and 'vepa' above. If there is no forward dev specified but a bridge name is given (e.g. "<bridge name='br0'/>"), then guest interfaces using this network will use libvirt's "host bridge" mode, equivalent to this: <interface type='bridge'> <source bridge='${bridge-name}'/> ... </interface> 3) A network can have multiple <portgroup> elements, which may be selected by the guest interface definition (by adding "portgroup='${name}'" in the <source> element along with the network name). Currently a portgroup can only contain a virtportprofile, but the intent is that other configuration items may be put there int the future (e.g. bandwidth config). When building a guest's interface, if the <interface> XML itself has no virtportprofile, and if the requested network has a portgroup with name matching the name given in the <interface> (or if one of the network's portgroups is marked with the "default='yes'" attribute), the virtportprofile from that portgroup will be used by the interface. 4) A network can have a virtportprofile defined at the top level, which will be used by a guest interface when connecting in one of the 'direct' modes if the guest interface XML itself hasn't specified any virtportprofile, and if there are also no matching portgroups on the network. --- Comments on other changes from V1 are here: https://www.redhat.com/archives/libvir-list/2011-July/msg01289.html https://www.redhat.com/archives/libvir-list/2011-July/msg01290.html Note that I now have text cases for both this and the domain XML changes, and documentation for the domain XML changes. I am still missing documentation for the network XML, and am working on that while you're reviewing this code. docs/schemas/network.rng | 37 +++ src/conf/network_conf.c | 334 +++++++++++++++++--- src/conf/network_conf.h | 41 +++- src/libvirt_private.syms | 1 + src/network/bridge_driver.c | 85 ++++-- tests/networkxml2xmlin/8021Qbh-net.xml | 14 + tests/networkxml2xmlin/direct-net.xml | 7 + tests/networkxml2xmlin/host-bridge-net.xml | 6 + tests/networkxml2xmlin/vepa-net.xml | 22 ++ tests/networkxml2xmlout/8021Qbh-net.xml | 14 + tests/networkxml2xmlout/direct-net.xml | 7 + tests/networkxml2xmlout/host-bridge-net.xml | 6 + tests/networkxml2xmlout/nat-network-dns-hosts.xml | 4 +- .../nat-network-dns-txt-record.xml | 4 +- tests/networkxml2xmlout/nat-network.xml | 4 +- tests/networkxml2xmlout/routed-network.xml | 4 +- tests/networkxml2xmlout/vepa-net.xml | 22 ++ tests/networkxml2xmltest.c | 4 + 18 files changed, 542 insertions(+), 74 deletions(-) create mode 100644 tests/networkxml2xmlin/8021Qbh-net.xml create mode 100644 tests/networkxml2xmlin/direct-net.xml create mode 100644 tests/networkxml2xmlin/host-bridge-net.xml create mode 100644 tests/networkxml2xmlin/vepa-net.xml create mode 100644 tests/networkxml2xmlout/8021Qbh-net.xml create mode 100644 tests/networkxml2xmlout/direct-net.xml create mode 100644 tests/networkxml2xmlout/host-bridge-net.xml create mode 100644 tests/networkxml2xmlout/vepa-net.xml diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index 6d9f06b..1adb553 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -7,6 +7,7 @@ </start> <include href='basictypes.rng'/> + <include href='networkcommon.rng'/> <define name="network"> @@ -77,12 +78,48 @@ <choice> <value>nat</value> <value>route</value> + <value>bridge</value> + <value>passthrough</value> + <value>private</value> + <value>vepa</value> </choice> </attribute> </optional> + <zeroOrMore> + <element name='interface'> + <attribute name='dev'> + <ref name='deviceName'/> + </attribute> + </element> + </zeroOrMore> </element> </optional> + <!-- <virtualport> element --> + <optional> + <ref name="virtualPortProfile"/> + </optional> + + <!-- <portgroup> elements --> + <zeroOrMore> + <element name="portgroup"> + <attribute name="name"> + <ref name="deviceName"/> + </attribute> + <optional> + <attribute name="default"> + <choice> + <value>yes</value> + <value>no</value> + </choice> + </attribute> + </optional> + <optional> + <ref name="virtualPortProfile"/> + </optional> + </element> + </zeroOrMore> + <!-- <domain> element --> <optional> <element name="domain"> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index ae479bf..c3e7d6a 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -50,7 +50,7 @@ VIR_ENUM_DECL(virNetworkForward) VIR_ENUM_IMPL(virNetworkForward, VIR_NETWORK_FORWARD_LAST, - "none", "nat", "route" ) + "none", "nat", "route", "bridge", "private", "vepa", "passthrough" ) #define virNetworkReportError(code, ...) \ virReportErrorHelper(VIR_FROM_NETWORK, code, __FILE__, \ @@ -87,6 +87,19 @@ virNetworkObjPtr virNetworkFindByName(const virNetworkObjListPtr nets, } +static void +virPortGroupDefClear(virPortGroupDefPtr def) +{ + VIR_FREE(def->name); + VIR_FREE(def->virtPortProfile); +} + +static void +virNetworkForwardIfDefClear(virNetworkForwardIfDefPtr def) +{ + VIR_FREE(def->dev); +} + static void virNetworkIpDefClear(virNetworkIpDefPtr def) { int ii; @@ -135,14 +148,23 @@ void virNetworkDefFree(virNetworkDefPtr def) VIR_FREE(def->name); VIR_FREE(def->bridge); - VIR_FREE(def->forwardDev); VIR_FREE(def->domain); + for (ii = 0 ; ii < def->nForwardIfs && def->forwardIfs ; ii++) { + virNetworkForwardIfDefClear(&def->forwardIfs[ii]); + } + VIR_FREE(def->forwardIfs); + for (ii = 0 ; ii < def->nips && def->ips ; ii++) { virNetworkIpDefClear(&def->ips[ii]); } VIR_FREE(def->ips); + for (ii = 0; ii < def->nPortGroups && def->portGroups; ii++) { + virPortGroupDefClear(&def->portGroups[ii]); + } + VIR_FREE(def->portGroups); + virNetworkDNSDefFree(def->dns); VIR_FREE(def); @@ -735,14 +757,63 @@ error: return result; } +static int +virNetworkPortGroupParseXML(virPortGroupDefPtr def, + xmlNodePtr node, + xmlXPathContextPtr ctxt) +{ + /* + * virPortGroupDef object is already allocated as part of an array. + * On failure clear it out, but don't free it. + */ + + xmlNodePtr save; + xmlNodePtr virtPortNode; + char *isDefault = NULL; + + int result = -1; + + save = ctxt->node; + ctxt->node = node; + + /* grab raw data from XML */ + def->name = virXPathString("string(./@name)", ctxt); + isDefault = virXPathString("string(./@default)", ctxt); + def->isDefault = isDefault && STRCASEEQ(isDefault, "yes"); + + virtPortNode = virXPathNode("./virtualport", ctxt); + if (virtPortNode && + (virVirtualPortProfileParamsParseXML(virtPortNode, + &def->virtPortProfile) < 0)) { + goto error; + } + + result = 0; +error: + if (result < 0) { + virPortGroupDefClear(def); + } + VIR_FREE(isDefault); + + ctxt->node = save; + return result; +} + static virNetworkDefPtr virNetworkDefParseXML(xmlXPathContextPtr ctxt) { virNetworkDefPtr def; char *tmp; + char *stp = NULL; xmlNodePtr *ipNodes = NULL; + xmlNodePtr *portGroupNodes = NULL; + xmlNodePtr *forwardIfNodes = NULL; xmlNodePtr dnsNode = NULL; - int nIps; + xmlNodePtr virtPortNode = NULL; + xmlNodePtr forwardNode = NULL; + int nIps, nPortGroups, nForwardIfs; + char *forwardDev = NULL; + xmlNodePtr save = ctxt->node; if (VIR_ALLOC(def) < 0) { virReportOOMError(); @@ -779,9 +850,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) /* Parse bridge information */ def->bridge = virXPathString("string(./bridge[1]/@name)", ctxt); - tmp = virXPathString("string(./bridge[1]/@stp)", ctxt); - def->stp = (tmp && STREQ(tmp, "off")) ? 0 : 1; - VIR_FREE(tmp); + stp = virXPathString("string(./bridge[1]/@stp)", ctxt); if (virXPathULong("string(./bridge[1]/@delay)", ctxt, &def->delay) < 0) def->delay = 0; @@ -805,6 +874,36 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) goto error; } + virtPortNode = virXPathNode("./virtualport", ctxt); + if (virtPortNode && + (virVirtualPortProfileParamsParseXML(virtPortNode, + &def->virtPortProfile) < 0)) { + goto error; + } + + nPortGroups = virXPathNodeSet("./portgroup", ctxt, &portGroupNodes); + if (nPortGroups < 0) + goto error; + + if (nPortGroups > 0) { + int ii; + + /* allocate array to hold all the portgroups */ + if (VIR_ALLOC_N(def->portGroups, nPortGroups) < 0) { + virReportOOMError(); + goto error; + } + /* parse each portgroup */ + for (ii = 0; ii < nPortGroups; ii++) { + int ret = virNetworkPortGroupParseXML(&def->portGroups[ii], + portGroupNodes[ii], ctxt); + if (ret < 0) + goto error; + def->nPortGroups++; + } + } + VIR_FREE(portGroupNodes); + nIps = virXPathNodeSet("./ip", ctxt, &ipNodes); if (nIps < 0) goto error; @@ -828,17 +927,16 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) } VIR_FREE(ipNodes); - /* IPv4 forwarding setup */ - if (virXPathBoolean("count(./forward) > 0", ctxt)) { - if (def->nips == 0) { - virNetworkReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Forwarding requested, but no IP address provided")); - goto error; - } - tmp = virXPathString("string(./forward[1]/@mode)", ctxt); + forwardNode = virXPathNode("./forward", ctxt); + if (!forwardNode) { + def->forwardType = VIR_NETWORK_FORWARD_NONE; + def->stp = (stp && STREQ(stp, "off")) ? 0 : 1; + } else { + ctxt->node = forwardNode; + tmp = virXPathString("string(./@mode)", ctxt); if (tmp) { if ((def->forwardType = virNetworkForwardTypeFromString(tmp)) < 0) { - virNetworkReportError(VIR_ERR_INTERNAL_ERROR, + virNetworkReportError(VIR_ERR_XML_ERROR, _("unknown forwarding type '%s'"), tmp); VIR_FREE(tmp); goto error; @@ -848,17 +946,116 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) def->forwardType = VIR_NETWORK_FORWARD_NAT; } + forwardDev = virXPathString("string(./@dev)", ctxt); - def->forwardDev = virXPathString("string(./forward[1]/@dev)", ctxt); - } else { - def->forwardType = VIR_NETWORK_FORWARD_NONE; - } + /* all of these modes can use a pool of physical interfaces */ + nForwardIfs = virXPathNodeSet("./interface", ctxt, &forwardIfNodes); + if (nForwardIfs < 0) + goto error; + + if ((nForwardIfs > 0) || forwardDev) { + int ii; + /* allocate array to hold all the portgroups */ + if (VIR_ALLOC_N(def->forwardIfs, MAX(nForwardIfs, 1)) < 0) { + virReportOOMError(); + goto error; + } + + if (forwardDev) { + def->forwardIfs[0].usageCount = 0; + def->forwardIfs[0].dev = forwardDev; + forwardDev = NULL; + def->nForwardIfs++; + } + + /* parse each forwardIf */ + for (ii = 0; ii < nForwardIfs; ii++) { + forwardDev = virXMLPropString(forwardIfNodes[ii], "dev"); + if (!forwardDev) { + virNetworkReportError(VIR_ERR_XML_ERROR, + _("Missing required dev attribute in network '%s' forward interface element"), + def->name); + goto error; + } + + if ((ii == 0) && (def->nForwardIfs == 1)) { + /* both forwardDev and an interface element are present. + * If they don't match, it's an error. */ + if (STRNEQ(forwardDev, def->forwardIfs[0].dev)) { + virNetworkReportError(VIR_ERR_XML_ERROR, + _("forward dev '%s' must match first interface element dev '%s' in network '%s'"), + def->forwardIfs[0].dev, + forwardDev, def->name); + goto error; + } + VIR_FREE(forwardDev); + continue; + } + + def->forwardIfs[ii].dev = forwardDev; + forwardDev = NULL; + def->forwardIfs[ii].usageCount = 0; + def->nForwardIfs++; + } + } + VIR_FREE(forwardIfNodes); + + switch (def->forwardType) { + case VIR_NETWORK_FORWARD_ROUTE: + case VIR_NETWORK_FORWARD_NAT: + /* It's pointless to specify L3 forwarding without specifying + * the network we're on. + */ + if (def->nips == 0) { + virNetworkReportError(VIR_ERR_XML_ERROR, + _("%s forwarding requested, but no IP address provided for network '%s'"), + virNetworkForwardTypeToString(def->forwardType), + def->name); + goto error; + } + if (def->nForwardIfs > 1) { + virNetworkReportError(VIR_ERR_XML_ERROR, + _("multiple forwarding interfaces specified for network '%s', only one is supported"), + def->name); + goto error; + } + def->stp = (stp && STREQ(stp, "off")) ? 0 : 1; + break; + case VIR_NETWORK_FORWARD_PRIVATE: + case VIR_NETWORK_FORWARD_VEPA: + case VIR_NETWORK_FORWARD_PASSTHROUGH: + if (def->bridge) { + virNetworkReportError(VIR_ERR_XML_ERROR, + _("bridge name not allowed in %s mode (network '%s'"), + virNetworkForwardTypeToString(def->forwardType), + def->name); + goto error; + } + /* fall through to next case */ + case VIR_NETWORK_FORWARD_BRIDGE: + if (def->delay || stp) { + virNetworkReportError(VIR_ERR_XML_ERROR, + _("bridge delay/stp options only allowed in route, nat, and isolated mode, not in %s (network '%s')"), + virNetworkForwardTypeToString(def->forwardType), + def->name); + goto error; + } + break; + } + } + VIR_FREE(stp); + ctxt->node = save; return def; error: + VIR_FREE(stp); virNetworkDefFree(def); VIR_FREE(ipNodes); + VIR_FREE(portGroupNodes); + VIR_FREE(forwardIfNodes); + VIR_FREE(forwardDev); + ctxt->node = save; return NULL; } @@ -1043,6 +1240,19 @@ error: return result; } +static void +virPortGroupDefFormat(virBufferPtr buf, + const virPortGroupDefPtr def) +{ + virBufferAsprintf(buf, " <portgroup name='%s'", def->name); + if (def->isDefault) { + virBufferAddLit(buf, " default='yes'"); + } + virBufferAddLit(buf, ">\n"); + virVirtualPortProfileFormat(buf, def->virtPortProfile, " "); + virBufferAddLit(buf, " </portgroup>\n"); +} + char *virNetworkDefFormat(const virNetworkDefPtr def) { virBuffer buf = VIR_BUFFER_INITIALIZER; @@ -1058,24 +1268,48 @@ char *virNetworkDefFormat(const virNetworkDefPtr def) virBufferAsprintf(&buf, " <uuid>%s</uuid>\n", uuidstr); if (def->forwardType != VIR_NETWORK_FORWARD_NONE) { + const char *dev = virNetworkDefForwardIf(def, 0); const char *mode = virNetworkForwardTypeToString(def->forwardType); - if (mode) { - if (def->forwardDev) { - virBufferEscapeString(&buf, " <forward dev='%s'", - def->forwardDev); - } else { - virBufferAddLit(&buf, " <forward"); + + if (!mode) { + virNetworkReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown forward type %d in network '%s'"), + def->forwardType, def->name); + goto error; + } + virBufferAddLit(&buf, " <forward"); + if (dev) + virBufferEscapeString(&buf, " dev='%s'", dev); + virBufferAsprintf(&buf, " mode='%s'%s>\n", mode, + def->nForwardIfs ? "" : "/"); + + if (def->nForwardIfs) { + for (ii = 0; ii < def->nForwardIfs; ii++) { + if (def->forwardIfs[ii].dev) { + virBufferEscapeString(&buf, " <interface dev='%s'/>\n", + def->forwardIfs[ii].dev); + } } - virBufferAsprintf(&buf, " mode='%s'/>\n", mode); + virBufferAddLit(&buf, " </forward>\n"); } } - virBufferAddLit(&buf, " <bridge"); - if (def->bridge) - virBufferEscapeString(&buf, " name='%s'", def->bridge); - virBufferAsprintf(&buf, " stp='%s' delay='%ld' />\n", - def->stp ? "on" : "off", - def->delay); + if (def->forwardType == VIR_NETWORK_FORWARD_NONE || + def->forwardType == VIR_NETWORK_FORWARD_NAT || + def->forwardType == VIR_NETWORK_FORWARD_ROUTE) { + + virBufferAddLit(&buf, " <bridge"); + if (def->bridge) + virBufferEscapeString(&buf, " name='%s'", def->bridge); + virBufferAsprintf(&buf, " stp='%s' delay='%ld' />\n", + def->stp ? "on" : "off", + def->delay); + } else if (def->forwardType == VIR_NETWORK_FORWARD_BRIDGE && + def->bridge) { + virBufferEscapeString(&buf, " <bridge name='%s' />\n", def->bridge); + } + + if (def->mac_specified) { char macaddr[VIR_MAC_STRING_BUFLEN]; virFormatMacAddr(def->mac, macaddr); @@ -1093,6 +1327,11 @@ char *virNetworkDefFormat(const virNetworkDefPtr def) goto error; } + virVirtualPortProfileFormat(&buf, def->virtPortProfile, " "); + + for (ii = 0; ii < def->nPortGroups; ii++) + virPortGroupDefFormat(&buf, &def->portGroups[ii]); + virBufferAddLit(&buf, "</network>\n"); if (virBufferError(&buf)) @@ -1107,6 +1346,22 @@ char *virNetworkDefFormat(const virNetworkDefPtr def) return NULL; } +virPortGroupDefPtr virPortGroupFindByName(virNetworkDefPtr net, + const char *portgroup) +{ + int ii; + for (ii = 0; ii < net->nPortGroups; ii++) { + if (portgroup) { + if (STREQ(portgroup, net->portGroups[ii].name)) + return &net->portGroups[ii]; + } else { + if (net->portGroups[ii].isDefault) + return &net->portGroups[ii]; + } + } + return NULL; +} + int virNetworkSaveXML(const char *configDir, virNetworkDefPtr def, const char *xml) @@ -1209,11 +1464,16 @@ virNetworkObjPtr virNetworkLoadConfig(virNetworkObjListPtr nets, goto error; } - /* Generate a bridge if none is specified, but don't check for collisions - * if a bridge is hardcoded, so the network is at least defined - */ - if (virNetworkSetBridgeName(nets, def, 0)) - goto error; + if (def->forwardType == VIR_NETWORK_FORWARD_NONE || + def->forwardType == VIR_NETWORK_FORWARD_NAT || + def->forwardType == VIR_NETWORK_FORWARD_ROUTE) { + + /* Generate a bridge if none is specified, but don't check for collisions + * if a bridge is hardcoded, so the network is at least defined. + */ + if (virNetworkSetBridgeName(nets, def, 0)) + goto error; + } if (!(net = virNetworkAssignDef(nets, def))) goto error; diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 5edcf27..92cc03e 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -33,11 +33,14 @@ # include "network.h" # include "util.h" -/* 2 possible types of forwarding */ enum virNetworkForwardType { VIR_NETWORK_FORWARD_NONE = 0, VIR_NETWORK_FORWARD_NAT, VIR_NETWORK_FORWARD_ROUTE, + VIR_NETWORK_FORWARD_BRIDGE, + VIR_NETWORK_FORWARD_PRIVATE, + VIR_NETWORK_FORWARD_VEPA, + VIR_NETWORK_FORWARD_PASSTHROUGH, VIR_NETWORK_FORWARD_LAST, }; @@ -107,6 +110,21 @@ struct _virNetworkIpDef { virSocketAddr bootserver; }; +typedef struct _virNetworkForwardIfDef virNetworkForwardIfDef; +typedef virNetworkForwardIfDef *virNetworkForwardIfDefPtr; +struct _virNetworkForwardIfDef { + char *dev; /* name of device */ + int usageCount; /* how many guest interfaces are bound to this device? */ +}; + +typedef struct _virPortGroupDef virPortGroupDef; +typedef virPortGroupDef *virPortGroupDefPtr; +struct _virPortGroupDef { + char *name; + bool isDefault; + virVirtualPortProfileParamsPtr virtPortProfile; +}; + typedef struct _virNetworkDef virNetworkDef; typedef virNetworkDef *virNetworkDefPtr; struct _virNetworkDef { @@ -121,12 +139,21 @@ struct _virNetworkDef { bool mac_specified; int forwardType; /* One of virNetworkForwardType constants */ - char *forwardDev; /* Destination device for forwarding */ + + /* If there are multiple forward devices (i.e. a pool of + * interfaces), they will be listed here. + */ + size_t nForwardIfs; + virNetworkForwardIfDefPtr forwardIfs; size_t nips; virNetworkIpDefPtr ips; /* ptr to array of IP addresses on this network */ virNetworkDNSDefPtr dns; /* ptr to dns related configuration */ + virVirtualPortProfileParamsPtr virtPortProfile; + + size_t nPortGroups; + virPortGroupDefPtr portGroups; }; typedef struct _virNetworkObj virNetworkObj; @@ -179,6 +206,16 @@ virNetworkDefPtr virNetworkDefParseNode(xmlDocPtr xml, char *virNetworkDefFormat(const virNetworkDefPtr def); +static inline const char * +virNetworkDefForwardIf(const virNetworkDefPtr def, size_t n) +{ + return ((def->forwardIfs && (def->nForwardIfs > n)) + ? def->forwardIfs[n].dev : NULL); +} + +virPortGroupDefPtr virPortGroupFindByName(virNetworkDefPtr net, + const char *portgroup); + virNetworkIpDefPtr virNetworkDefGetIpByIndex(const virNetworkDefPtr def, int family, size_t n); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 63e8aea..1b6d1b0 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -748,6 +748,7 @@ virNetworkRemoveInactive; virNetworkSaveConfig; virNetworkSetBridgeMacAddr; virNetworkSetBridgeName; +virPortGroupFindByName; # node_device_conf.h virNodeDevCapTypeToString; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 0a12bc0..4459146 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -898,6 +898,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, virNetworkIpDefPtr ipdef) { int prefix = virNetworkIpDefPrefix(ipdef); + const char *forwardIf = virNetworkDefForwardIf(network->def, 0); if (prefix < 0) { networkReportError(VIR_ERR_INTERNAL_ERROR, @@ -911,7 +912,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, &ipdef->address, prefix, network->def->bridge, - network->def->forwardDev) < 0) { + forwardIf) < 0) { networkReportError(VIR_ERR_SYSTEM_ERROR, _("failed to add iptables rule to allow forwarding from '%s'"), network->def->bridge); @@ -925,7 +926,7 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, &ipdef->address, prefix, network->def->bridge, - network->def->forwardDev) < 0) { + forwardIf) < 0) { networkReportError(VIR_ERR_SYSTEM_ERROR, _("failed to add iptables rule to allow forwarding to '%s'"), network->def->bridge); @@ -959,11 +960,12 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, if (iptablesAddForwardMasquerade(driver->iptables, &ipdef->address, prefix, - network->def->forwardDev, + forwardIf, NULL) < 0) { networkReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to enable masquerading to '%s'"), - network->def->forwardDev ? network->def->forwardDev : NULL); + _("failed to add iptables rule to enable masquerading%s%s"), + forwardIf ? " to " : "", + forwardIf ? forwardIf : ""); goto masqerr3; } @@ -971,11 +973,12 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, if (iptablesAddForwardMasquerade(driver->iptables, &ipdef->address, prefix, - network->def->forwardDev, + forwardIf, "udp") < 0) { networkReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to enable UDP masquerading to '%s'"), - network->def->forwardDev ? network->def->forwardDev : NULL); + _("failed to add iptables rule to enable UDP masquerading%s%s"), + forwardIf ? " to " : "", + forwardIf ? forwardIf : ""); goto masqerr4; } @@ -983,11 +986,12 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, if (iptablesAddForwardMasquerade(driver->iptables, &ipdef->address, prefix, - network->def->forwardDev, + forwardIf, "tcp") < 0) { networkReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to enable TCP masquerading to '%s'"), - network->def->forwardDev ? network->def->forwardDev : NULL); + _("failed to add iptables rule to enable TCP masquerading%s%s"), + forwardIf ? " to " : "", + forwardIf ? forwardIf : ""); goto masqerr5; } @@ -997,26 +1001,26 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, iptablesRemoveForwardMasquerade(driver->iptables, &ipdef->address, prefix, - network->def->forwardDev, + forwardIf, "udp"); masqerr4: iptablesRemoveForwardMasquerade(driver->iptables, &ipdef->address, prefix, - network->def->forwardDev, + forwardIf, NULL); masqerr3: iptablesRemoveForwardAllowRelatedIn(driver->iptables, &ipdef->address, prefix, network->def->bridge, - network->def->forwardDev); + forwardIf); masqerr2: iptablesRemoveForwardAllowOut(driver->iptables, &ipdef->address, prefix, network->def->bridge, - network->def->forwardDev); + forwardIf); masqerr1: return -1; } @@ -1027,34 +1031,35 @@ networkRemoveMasqueradingIptablesRules(struct network_driver *driver, virNetworkIpDefPtr ipdef) { int prefix = virNetworkIpDefPrefix(ipdef); + const char *forwardIf = virNetworkDefForwardIf(network->def, 0); if (prefix >= 0) { iptablesRemoveForwardMasquerade(driver->iptables, &ipdef->address, prefix, - network->def->forwardDev, + forwardIf, "tcp"); iptablesRemoveForwardMasquerade(driver->iptables, &ipdef->address, prefix, - network->def->forwardDev, + forwardIf, "udp"); iptablesRemoveForwardMasquerade(driver->iptables, &ipdef->address, prefix, - network->def->forwardDev, + forwardIf, NULL); iptablesRemoveForwardAllowRelatedIn(driver->iptables, &ipdef->address, prefix, network->def->bridge, - network->def->forwardDev); + forwardIf); iptablesRemoveForwardAllowOut(driver->iptables, &ipdef->address, prefix, network->def->bridge, - network->def->forwardDev); + forwardIf); } } @@ -1064,6 +1069,7 @@ networkAddRoutingIptablesRules(struct network_driver *driver, virNetworkIpDefPtr ipdef) { int prefix = virNetworkIpDefPrefix(ipdef); + const char *forwardIf = virNetworkDefForwardIf(network->def, 0); if (prefix < 0) { networkReportError(VIR_ERR_INTERNAL_ERROR, @@ -1077,7 +1083,7 @@ networkAddRoutingIptablesRules(struct network_driver *driver, &ipdef->address, prefix, network->def->bridge, - network->def->forwardDev) < 0) { + forwardIf) < 0) { networkReportError(VIR_ERR_SYSTEM_ERROR, _("failed to add iptables rule to allow routing from '%s'"), network->def->bridge); @@ -1089,7 +1095,7 @@ networkAddRoutingIptablesRules(struct network_driver *driver, &ipdef->address, prefix, network->def->bridge, - network->def->forwardDev) < 0) { + forwardIf) < 0) { networkReportError(VIR_ERR_SYSTEM_ERROR, _("failed to add iptables rule to allow routing to '%s'"), network->def->bridge); @@ -1103,7 +1109,7 @@ routeerr2: &ipdef->address, prefix, network->def->bridge, - network->def->forwardDev); + forwardIf); routeerr1: return -1; } @@ -1114,19 +1120,20 @@ networkRemoveRoutingIptablesRules(struct network_driver *driver, virNetworkIpDefPtr ipdef) { int prefix = virNetworkIpDefPrefix(ipdef); + const char *forwardIf = virNetworkDefForwardIf(network->def, 0); if (prefix >= 0) { iptablesRemoveForwardAllowIn(driver->iptables, &ipdef->address, prefix, network->def->bridge, - network->def->forwardDev); + forwardIf); iptablesRemoveForwardAllowOut(driver->iptables, &ipdef->address, prefix, network->def->bridge, - network->def->forwardDev); + forwardIf); } } @@ -2171,10 +2178,18 @@ static virNetworkPtr networkCreate(virConnectPtr conn, const char *xml) { if (virNetworkObjIsDuplicate(&driver->networks, def, 1) < 0) goto cleanup; - if (virNetworkSetBridgeName(&driver->networks, def, 1)) - goto cleanup; + /* Only the three L3 network types that are configured by libvirt + * need to have a bridge device name / mac address provided + */ + if (def->forwardType == VIR_NETWORK_FORWARD_NONE || + def->forwardType == VIR_NETWORK_FORWARD_NAT || + def->forwardType == VIR_NETWORK_FORWARD_ROUTE) { - virNetworkSetBridgeMacAddr(def); + if (virNetworkSetBridgeName(&driver->networks, def, 1)) + goto cleanup; + + virNetworkSetBridgeMacAddr(def); + } if (!(network = virNetworkAssignDef(&driver->networks, def))) @@ -2216,10 +2231,18 @@ static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) { if (virNetworkObjIsDuplicate(&driver->networks, def, 0) < 0) goto cleanup; - if (virNetworkSetBridgeName(&driver->networks, def, 1)) - goto cleanup; + /* Only the three L3 network types that are configured by libvirt + * need to have a bridge device name / mac address provided + */ + if (def->forwardType == VIR_NETWORK_FORWARD_NONE || + def->forwardType == VIR_NETWORK_FORWARD_NAT || + def->forwardType == VIR_NETWORK_FORWARD_ROUTE) { - virNetworkSetBridgeMacAddr(def); + if (virNetworkSetBridgeName(&driver->networks, def, 1)) + goto cleanup; + + virNetworkSetBridgeMacAddr(def); + } if (!(network = virNetworkAssignDef(&driver->networks, def))) diff --git a/tests/networkxml2xmlin/8021Qbh-net.xml b/tests/networkxml2xmlin/8021Qbh-net.xml new file mode 100644 index 0000000..2d779dc --- /dev/null +++ b/tests/networkxml2xmlin/8021Qbh-net.xml @@ -0,0 +1,14 @@ +<network> + <name>8021Qbh-net</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8c</uuid> + <forward mode="private" dev="eth1"> + <interface dev="eth1"/> + <interface dev="eth2"/> + <interface dev="eth3"/> + <interface dev="eth4"/> + <interface dev="eth5"/> + </forward> + <virtualport type="802.1Qbh"> + <parameters profileid="spongebob24"/> + </virtualport> +</network> diff --git a/tests/networkxml2xmlin/direct-net.xml b/tests/networkxml2xmlin/direct-net.xml new file mode 100644 index 0000000..d73c454 --- /dev/null +++ b/tests/networkxml2xmlin/direct-net.xml @@ -0,0 +1,7 @@ +<network> + <name>direct-net</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8f</uuid> + <forward mode="bridge"> + <interface dev="eth10"/> + </forward> +</network> diff --git a/tests/networkxml2xmlin/host-bridge-net.xml b/tests/networkxml2xmlin/host-bridge-net.xml new file mode 100644 index 0000000..960bc2d --- /dev/null +++ b/tests/networkxml2xmlin/host-bridge-net.xml @@ -0,0 +1,6 @@ +<network> + <name>host-bridge-net</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8e</uuid> + <forward mode="bridge"/> + <bridge name="br0"/> +</network> diff --git a/tests/networkxml2xmlin/vepa-net.xml b/tests/networkxml2xmlin/vepa-net.xml new file mode 100644 index 0000000..b1a40c6 --- /dev/null +++ b/tests/networkxml2xmlin/vepa-net.xml @@ -0,0 +1,22 @@ +<network> + <name>vepa-net</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8b</uuid> + <forward mode="vepa"> + <interface dev="eth1"/> + <interface dev="eth2"/> + <interface dev="eth3"/> + </forward> + <virtualport type="802.1Qbg"> + <parameters managerid="11" typeid="1193047" typeidversion="2" instanceid="b153fa89-1b87-9719-ec12-99e0054fb844"/> + </virtualport> + <portgroup name="bob" default="yes"> + <virtualport type="802.1Qbg"> + <parameters managerid="12" typeid="2193047" typeidversion="3" instanceid="5d00e0ba-e15c-959c-fbb6-b595b0655735"/> + </virtualport> + </portgroup> + <portgroup name="alice"> + <virtualport type="802.1Qbg"> + <parameters managerid="13" typeid="3193047" typeidversion="4" instanceid="70bf45f9-01a8-f5ee-3c0f-e25a0a2e44a6"/> + </virtualport> + </portgroup> +</network> diff --git a/tests/networkxml2xmlout/8021Qbh-net.xml b/tests/networkxml2xmlout/8021Qbh-net.xml new file mode 100644 index 0000000..d4d5b4b --- /dev/null +++ b/tests/networkxml2xmlout/8021Qbh-net.xml @@ -0,0 +1,14 @@ +<network> + <name>8021Qbh-net</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8c</uuid> + <forward dev='eth1' mode='private'> + <interface dev='eth1'/> + <interface dev='eth2'/> + <interface dev='eth3'/> + <interface dev='eth4'/> + <interface dev='eth5'/> + </forward> + <virtualport type='802.1Qbh'> + <parameters profileid='spongebob24'/> + </virtualport> +</network> diff --git a/tests/networkxml2xmlout/direct-net.xml b/tests/networkxml2xmlout/direct-net.xml new file mode 100644 index 0000000..56cd707 --- /dev/null +++ b/tests/networkxml2xmlout/direct-net.xml @@ -0,0 +1,7 @@ +<network> + <name>direct-net</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8f</uuid> + <forward dev='eth10' mode='bridge'> + <interface dev='eth10'/> + </forward> +</network> diff --git a/tests/networkxml2xmlout/host-bridge-net.xml b/tests/networkxml2xmlout/host-bridge-net.xml new file mode 100644 index 0000000..84992a9 --- /dev/null +++ b/tests/networkxml2xmlout/host-bridge-net.xml @@ -0,0 +1,6 @@ +<network> + <name>host-bridge-net</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8e</uuid> + <forward mode='bridge'/> + <bridge name='br0' /> +</network> diff --git a/tests/networkxml2xmlout/nat-network-dns-hosts.xml b/tests/networkxml2xmlout/nat-network-dns-hosts.xml index 9a83fed..b26fa03 100644 --- a/tests/networkxml2xmlout/nat-network-dns-hosts.xml +++ b/tests/networkxml2xmlout/nat-network-dns-hosts.xml @@ -1,7 +1,9 @@ <network> <name>default</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9c</uuid> - <forward dev='eth0' mode='nat'/> + <forward dev='eth0' mode='nat'> + <interface dev='eth0'/> + </forward> <bridge name='virbr0' stp='on' delay='0' /> <dns> <host ip='192.168.122.1'> diff --git a/tests/networkxml2xmlout/nat-network-dns-txt-record.xml b/tests/networkxml2xmlout/nat-network-dns-txt-record.xml index bd16976..f767f48 100644 --- a/tests/networkxml2xmlout/nat-network-dns-txt-record.xml +++ b/tests/networkxml2xmlout/nat-network-dns-txt-record.xml @@ -1,7 +1,9 @@ <network> <name>default</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> - <forward dev='eth1' mode='nat'/> + <forward dev='eth1' mode='nat'> + <interface dev='eth1'/> + </forward> <bridge name='virbr0' stp='on' delay='0' /> <dns> <txt name='example' value='example value' /> diff --git a/tests/networkxml2xmlout/nat-network.xml b/tests/networkxml2xmlout/nat-network.xml index eb71d9e..02d6849 100644 --- a/tests/networkxml2xmlout/nat-network.xml +++ b/tests/networkxml2xmlout/nat-network.xml @@ -1,7 +1,9 @@ <network> <name>default</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> - <forward dev='eth1' mode='nat'/> + <forward dev='eth1' mode='nat'> + <interface dev='eth1'/> + </forward> <bridge name='virbr0' stp='on' delay='0' /> <ip address='192.168.122.1' netmask='255.255.255.0'> <dhcp> diff --git a/tests/networkxml2xmlout/routed-network.xml b/tests/networkxml2xmlout/routed-network.xml index 3aa8109..9235e15 100644 --- a/tests/networkxml2xmlout/routed-network.xml +++ b/tests/networkxml2xmlout/routed-network.xml @@ -1,7 +1,9 @@ <network> <name>local</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid> - <forward dev='eth1' mode='route'/> + <forward dev='eth1' mode='route'> + <interface dev='eth1'/> + </forward> <bridge name='virbr1' stp='on' delay='0' /> <mac address='12:34:56:78:9A:BC'/> <ip address='192.168.122.1' netmask='255.255.255.0'> diff --git a/tests/networkxml2xmlout/vepa-net.xml b/tests/networkxml2xmlout/vepa-net.xml new file mode 100644 index 0000000..af13d0f --- /dev/null +++ b/tests/networkxml2xmlout/vepa-net.xml @@ -0,0 +1,22 @@ +<network> + <name>vepa-net</name> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8b</uuid> + <forward dev='eth1' mode='vepa'> + <interface dev='eth1'/> + <interface dev='eth2'/> + <interface dev='eth3'/> + </forward> + <virtualport type='802.1Qbg'> + <parameters managerid='11' typeid='1193047' typeidversion='2' instanceid='b153fa89-1b87-9719-ec12-99e0054fb844'/> + </virtualport> + <portgroup name='bob' default='yes'> + <virtualport type='802.1Qbg'> + <parameters managerid='12' typeid='2193047' typeidversion='3' instanceid='5d00e0ba-e15c-959c-fbb6-b595b0655735'/> + </virtualport> + </portgroup> + <portgroup name='alice'> + <virtualport type='802.1Qbg'> + <parameters managerid='13' typeid='3193047' typeidversion='4' instanceid='70bf45f9-01a8-f5ee-3c0f-e25a0a2e44a6'/> + </virtualport> + </portgroup> +</network> diff --git a/tests/networkxml2xmltest.c b/tests/networkxml2xmltest.c index 065166d..f354b0d 100644 --- a/tests/networkxml2xmltest.c +++ b/tests/networkxml2xmltest.c @@ -88,6 +88,10 @@ mymain(void) DO_TEST("netboot-proxy-network"); DO_TEST("nat-network-dns-txt-record"); DO_TEST("nat-network-dns-hosts"); + DO_TEST("8021Qbh-net"); + DO_TEST("direct-net"); + DO_TEST("host-bridge-net"); + DO_TEST("vepa-net"); return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE); } -- 1.7.3.4

On 07/20/2011 12:21 AM, Laine Stump wrote:
The network XML is updated in the following ways:
1) The<forward> element can now contain a list of forward interfaces:
<forward ....> <interface dev='eth10'/> <interface dev='eth11'/> <interface dev='eth12'/> <interface dev='eth13'/> </forward>
The first of these takes the place of the dev attribute that is normally in<forward> - on when defining a network you can specify either one, and on output both will be present. If you specify both, they must match.
Note that I now have text cases for both this and the domain XML changes, and documentation for the domain XML changes. I am still missing documentation for the network XML, and am working on that while you're reviewing this code.
And you know I'll bug you for it if it hasn't shown up by next week :)
+ case VIR_NETWORK_FORWARD_PASSTHROUGH: + if (def->bridge) { + virNetworkReportError(VIR_ERR_XML_ERROR, + _("bridge name not allowed in %s mode (network '%s'"), + virNetworkForwardTypeToString(def->forwardType), + def->name); + goto error; + } + /* fall through to next case */ + case VIR_NETWORK_FORWARD_BRIDGE:
We'll soon find out whether Coverity understands this comment. Hopefully yes.
+++ b/src/libvirt_private.syms @@ -748,6 +748,7 @@ virNetworkRemoveInactive; virNetworkSaveConfig; virNetworkSetBridgeMacAddr; virNetworkSetBridgeName; +virPortGroupFindByName;
# node_device_conf.h
Simple merge conflict here if you applied my squash changes from patch 4/9.
@@ -959,11 +960,12 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, if (iptablesAddForwardMasquerade(driver->iptables, &ipdef->address, prefix, - network->def->forwardDev, + forwardIf, NULL)< 0) { networkReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to enable masquerading to '%s'"), - network->def->forwardDev ? network->def->forwardDev : NULL); + _("failed to add iptables rule to enable masquerading%s%s"), + forwardIf ? " to " : "", + forwardIf ? forwardIf : "");
Translators don't like this. Not to mention that " to " is not marked for translation, so you'd get a mixed-language result. Better to spell out two possible sentences.
goto masqerr3; }
@@ -971,11 +973,12 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, if (iptablesAddForwardMasquerade(driver->iptables, &ipdef->address, prefix, - network->def->forwardDev, + forwardIf, "udp")< 0) { networkReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to enable UDP masquerading to '%s'"), - network->def->forwardDev ? network->def->forwardDev : NULL); + _("failed to add iptables rule to enable UDP masquerading%s%s"), + forwardIf ? " to " : "", + forwardIf ? forwardIf : "");
Likewise. ACK with this squashed in (or something similar, if you don't like my abuse of the fact that printf ignores an unused argument on one of the two strings): diff --git i/src/network/bridge_driver.c w/src/network/bridge_driver.c index 4459146..402f447 100644 --- i/src/network/bridge_driver.c +++ w/src/network/bridge_driver.c @@ -963,9 +963,10 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, forwardIf, NULL) < 0) { networkReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to enable masquerading%s%s"), - forwardIf ? " to " : "", - forwardIf ? forwardIf : ""); + forwardIf ? + _("failed to add iptables rule to enable masquerading to %s") : + _("failed to add iptables rule to enable masquerading"), + forwardIf); goto masqerr3; } @@ -976,9 +977,10 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, forwardIf, "udp") < 0) { networkReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to enable UDP masquerading%s%s"), - forwardIf ? " to " : "", - forwardIf ? forwardIf : ""); + forwardIf ? + _("failed to add iptables rule to enable UDP masquerading to %s") : + _("failed to add iptables rule to enable UDP masquerading"), + forwardIf); goto masqerr4; } @@ -989,9 +991,10 @@ networkAddMasqueradingIptablesRules(struct network_driver *driver, forwardIf, "tcp") < 0) { networkReportError(VIR_ERR_SYSTEM_ERROR, - _("failed to add iptables rule to enable TCP masquerading%s%s"), - forwardIf ? " to " : "", - forwardIf ? forwardIf : ""); + forwardIf ? + _("failed to add iptables rule to enable TCP masquerading to %s") : + _("failed to add iptables rule to enable TCP masquerading"), + forwardIf); goto masqerr5; } -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Wed, Jul 20, 2011 at 02:21:55AM -0400, Laine Stump wrote:
The network XML is updated in the following ways: [...] diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index 6d9f06b..1adb553 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -7,6 +7,7 @@ </start>
<include href='basictypes.rng'/> + <include href='networkcommon.rng'/>
Modularity is nice but you forgot networkcommon.rng in the patch, right ? Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On 07/21/2011 02:44 AM, Daniel Veillard wrote:
On Wed, Jul 20, 2011 at 02:21:55AM -0400, Laine Stump wrote:
The network XML is updated in the following ways: [...] diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index 6d9f06b..1adb553 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -7,6 +7,7 @@ </start>
<include href='basictypes.rng'/> +<include href='networkcommon.rng'/> Modularity is nice but you forgot networkcommon.rng in the patch, right ?
networkcommon.rng was added in PATCH 2/9 - it's used by both network.rng and domain.rng. (I had actually forgotten to "git add" the new file in the previous version of the patches, but this time I remembered - I just doublechecked)

On Thu, Jul 21, 2011 at 03:14:30AM -0400, Laine Stump wrote:
On 07/21/2011 02:44 AM, Daniel Veillard wrote:
On Wed, Jul 20, 2011 at 02:21:55AM -0400, Laine Stump wrote:
The network XML is updated in the following ways: [...] diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index 6d9f06b..1adb553 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -7,6 +7,7 @@ </start>
<include href='basictypes.rng'/> +<include href='networkcommon.rng'/> Modularity is nice but you forgot networkcommon.rng in the patch, right ?
networkcommon.rng was added in PATCH 2/9 - it's used by both network.rng and domain.rng.
(I had actually forgotten to "git add" the new file in the previous version of the patches, but this time I remembered - I just doublechecked)
Yeah, I noticed but after sending the mail ... sorry :-) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Previously all networks were composed of bridge devices created and managed by libvirt, and the same operations needed to be done for all of them when they were started and stopped (create and start the bridge device, configure its MAC address and IP address, add iptables rules). The new network types are (for now at least) managed outside of libvirt, and the network object is used only to contain information about the network, which is then used as each individual guest connects itself. This means that when starting/stopping one of these new networks, we really want to do nothing, aside from marking the network as active/inactive. This has been setup as toplevel Start/Shutdown functions that do the small bit of common stuff, then have a switch statement to execute network type-specific start/shutdown code, then do a bit more common code. The type-specific functions called for the new host bridge and macvtap based types are currently empty. In the future these functions may actually do something, and we will surely add more functions that are similarly patterned. Once everything has settled, we can make a table of "sub-driver" function pointers for each network type, and store a pointer to that table in the network object, then we can replace the switch statements with calls to functions in the table. The final step in this will be to add a new table (and corresponding new functions) for new network types as they are added. --- Unchanged from V1. src/network/bridge_driver.c | 188 +++++++++++++++++++++++++++++++------------ 1 files changed, 138 insertions(+), 50 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 4459146..8767af9 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -97,10 +97,22 @@ static void networkDriverUnlock(struct network_driver *driver) static int networkShutdown(void); -static int networkStartNetworkDaemon(struct network_driver *driver, +static int networkStartNetwork(struct network_driver *driver, + virNetworkObjPtr network); + +static int networkShutdownNetwork(struct network_driver *driver, + virNetworkObjPtr network); + +static int networkStartNetworkVirtual(struct network_driver *driver, virNetworkObjPtr network); -static int networkShutdownNetworkDaemon(struct network_driver *driver, +static int networkShutdownNetworkVirtual(struct network_driver *driver, + virNetworkObjPtr network); + +static int networkStartNetworkExternal(struct network_driver *driver, + virNetworkObjPtr network); + +static int networkShutdownNetworkExternal(struct network_driver *driver, virNetworkObjPtr network); static void networkReloadIptablesRules(struct network_driver *driver); @@ -252,9 +264,10 @@ networkAutostartConfigs(struct network_driver *driver) { for (i = 0 ; i < driver->networks.count ; i++) { virNetworkObjLock(driver->networks.objs[i]); if (driver->networks.objs[i]->autostart && - !virNetworkObjIsActive(driver->networks.objs[i]) && - networkStartNetworkDaemon(driver, driver->networks.objs[i]) < 0) { + !virNetworkObjIsActive(driver->networks.objs[i])) { + if (networkStartNetwork(driver, driver->networks.objs[i]) < 0) { /* failed to start but already logged */ + } } virNetworkObjUnlock(driver->networks.objs[i]); } @@ -1695,7 +1708,7 @@ networkAddAddrToBridge(struct network_driver *driver, } static int -networkStartNetworkDaemon(struct network_driver *driver, +networkStartNetworkVirtual(struct network_driver *driver, virNetworkObjPtr network) { int ii, err; @@ -1704,12 +1717,6 @@ networkStartNetworkDaemon(struct network_driver *driver, virNetworkIpDefPtr ipdef; char *macTapIfName = NULL; - if (virNetworkObjIsActive(network)) { - networkReportError(VIR_ERR_OPERATION_INVALID, - "%s", _("network is already active")); - return -1; - } - /* Check to see if any network IP collides with an existing route */ if (networkCheckRouteCollision(network) < 0) return -1; @@ -1811,26 +1818,10 @@ networkStartNetworkDaemon(struct network_driver *driver, if (v6present && networkStartRadvd(network) < 0) goto err4; - /* Persist the live configuration now we have bridge info */ - if (virNetworkSaveConfig(NETWORK_STATE_DIR, network->def) < 0) { - goto err5; - } - VIR_FREE(macTapIfName); - VIR_INFO("Starting up network '%s'", network->def->name); - network->active = 1; return 0; - err5: - if (!save_err) - save_err = virSaveLastError(); - - if (network->radvdPid > 0) { - kill(network->radvdPid, SIGTERM); - network->radvdPid = -1; - } - err4: if (!save_err) save_err = virSaveLastError(); @@ -1882,25 +1873,11 @@ networkStartNetworkDaemon(struct network_driver *driver, return -1; } - -static int networkShutdownNetworkDaemon(struct network_driver *driver, +static int networkShutdownNetworkVirtual(struct network_driver *driver, virNetworkObjPtr network) { int err; - char *stateFile; - char *macTapIfName; - - VIR_INFO("Shutting down network '%s'", network->def->name); - - if (!virNetworkObjIsActive(network)) - return 0; - - stateFile = virNetworkConfigFile(NETWORK_STATE_DIR, network->def->name); - if (!stateFile) - return -1; - - unlink(stateFile); - VIR_FREE(stateFile); + char ebuf[1024]; if (network->radvdPid > 0) { char *radvdpidbase; @@ -1918,10 +1895,8 @@ static int networkShutdownNetworkDaemon(struct network_driver *driver, if (network->dnsmasqPid > 0) kill(network->dnsmasqPid, SIGTERM); - char ebuf[1024]; - if (network->def->mac_specified) { - macTapIfName = networkBridgeDummyNicName(network->def->bridge); + char *macTapIfName = networkBridgeDummyNicName(network->def->bridge); if (!macTapIfName) { virReportOOMError(); } else { @@ -1957,6 +1932,119 @@ static int networkShutdownNetworkDaemon(struct network_driver *driver, kill(network->radvdPid, SIGKILL); network->radvdPid = -1; + return 0; +} + +static int +networkStartNetworkExternal(struct network_driver *driver ATTRIBUTE_UNUSED, + virNetworkObjPtr network ATTRIBUTE_UNUSED) +{ + /* put anything here that needs to be done each time a network of + * type BRIDGE, PRIVATE, VEPA, or PASSTHROUGH is started. On + * failure, undo anything you've done, and return -1. On success + * return 0. + */ + return 0; +} + +static int networkShutdownNetworkExternal(struct network_driver *driver ATTRIBUTE_UNUSED, + virNetworkObjPtr network ATTRIBUTE_UNUSED) +{ + /* put anything here that needs to be done each time a network of + * type BRIDGE, PRIVATE, VEPA, or PASSTHROUGH is shutdown. On + * failure, undo anything you've done, and return -1. On success + * return 0. + */ + return 0; +} + +static int +networkStartNetwork(struct network_driver *driver, + virNetworkObjPtr network) +{ + int ret = 0; + + if (virNetworkObjIsActive(network)) { + networkReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("network is already active")); + return -1; + } + + switch (network->def->forwardType) { + + case VIR_NETWORK_FORWARD_NONE: + case VIR_NETWORK_FORWARD_NAT: + case VIR_NETWORK_FORWARD_ROUTE: + ret = networkStartNetworkVirtual(driver, network); + break; + + case VIR_NETWORK_FORWARD_BRIDGE: + case VIR_NETWORK_FORWARD_PRIVATE: + case VIR_NETWORK_FORWARD_VEPA: + case VIR_NETWORK_FORWARD_PASSTHROUGH: + ret = networkStartNetworkExternal(driver, network); + break; + } + + if (ret < 0) + return ret; + + /* Persist the live configuration now that anything autogenerated + * is setup. + */ + if ((ret = virNetworkSaveConfig(NETWORK_STATE_DIR, network->def)) < 0) { + goto error; + } + + VIR_INFO("Starting up network '%s'", network->def->name); + network->active = 1; + +error: + if (ret < 0) { + virErrorPtr save_err = virSaveLastError(); + int save_errno = errno; + networkShutdownNetwork(driver, network); + virSetError(save_err); + virFreeError(save_err); + errno = save_errno; + } + return ret; +} + +static int networkShutdownNetwork(struct network_driver *driver, + virNetworkObjPtr network) +{ + int ret = 0; + char *stateFile; + + VIR_INFO("Shutting down network '%s'", network->def->name); + + if (!virNetworkObjIsActive(network)) + return 0; + + stateFile = virNetworkConfigFile(NETWORK_STATE_DIR, network->def->name); + if (!stateFile) + return -1; + + unlink(stateFile); + VIR_FREE(stateFile); + + switch (network->def->forwardType) { + + case VIR_NETWORK_FORWARD_NONE: + case VIR_NETWORK_FORWARD_NAT: + case VIR_NETWORK_FORWARD_ROUTE: + ret = networkShutdownNetworkVirtual(driver, network); + break; + + case VIR_NETWORK_FORWARD_BRIDGE: + case VIR_NETWORK_FORWARD_PRIVATE: + case VIR_NETWORK_FORWARD_VEPA: + case VIR_NETWORK_FORWARD_PASSTHROUGH: + ret = networkShutdownNetworkExternal(driver, network); + break; + } + network->active = 0; if (network->newDef) { @@ -1965,7 +2053,7 @@ static int networkShutdownNetworkDaemon(struct network_driver *driver, network->newDef = NULL; } - return 0; + return ret; } @@ -2196,7 +2284,7 @@ static virNetworkPtr networkCreate(virConnectPtr conn, const char *xml) { goto cleanup; def = NULL; - if (networkStartNetworkDaemon(driver, network) < 0) { + if (networkStartNetwork(driver, network) < 0) { virNetworkRemoveInactive(&driver->networks, network); network = NULL; @@ -2398,7 +2486,7 @@ static int networkStart(virNetworkPtr net) { goto cleanup; } - ret = networkStartNetworkDaemon(driver, network); + ret = networkStartNetwork(driver, network); cleanup: if (network) @@ -2427,7 +2515,7 @@ static int networkDestroy(virNetworkPtr net) { goto cleanup; } - ret = networkShutdownNetworkDaemon(driver, network); + ret = networkShutdownNetwork(driver, network); if (!network->persistent) { virNetworkRemoveInactive(&driver->networks, network); -- 1.7.3.4

On 07/20/2011 12:21 AM, Laine Stump wrote:
Previously all networks were composed of bridge devices created and managed by libvirt, and the same operations needed to be done for all of them when they were started and stopped (create and start the bridge device, configure its MAC address and IP address, add iptables rules). The new network types are (for now at least) managed outside of libvirt, and the network object is used only to contain information about the network, which is then used as each individual guest connects itself.
This means that when starting/stopping one of these new networks, we really want to do nothing, aside from marking the network as active/inactive.
This has been setup as toplevel Start/Shutdown functions that do the small bit of common stuff, then have a switch statement to execute network type-specific start/shutdown code, then do a bit more common code. The type-specific functions called for the new host bridge and macvtap based types are currently empty.
In the future these functions may actually do something, and we will surely add more functions that are similarly patterned. Once everything has settled, we can make a table of "sub-driver" function pointers for each network type, and store a pointer to that table in the network object, then we can replace the switch statements with calls to functions in the table.
The final step in this will be to add a new table (and corresponding new functions) for new network types as they are added. --- Unchanged from V1.
V1 was acked, and I didn't spot any major differences.
+error: + if (ret< 0) { + virErrorPtr save_err = virSaveLastError(); + int save_errno = errno;
Oops. virSaveLastError doesn't (currently) preserve errno. Sounds like a separate patch is worthwhile in the meantime, rather than altering this one. ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

The qemu driver accesses fields in the virDomainNetDef directly, but with the advent of the virDomainActualNetDef, some pieces of information may be found in a different place (the ActualNetDef) if the network connection is of type='network' and that network is of forward type='bridge|private|vepa|passthrough'. The previous patch added functions to mask this difference from callers - they hide the decision making process and just pick the value from the proper place. This patch uses those functions in the qemu driver as a first step in making qemu work with the new network types. At this point, the the virDomainActualNetDef is always NULL, so the GetActualX() function will return exactly what the def->X that's being replaced would have returned (ie bisecting is not compromised). There is one place (in qemu_driver.c) where the internal details of the NetDef are directly manipulated by the code, so these GetActual functions cannot be used without extra additional code; that file will be treated in a separate patch. --- Changes from V1: This and the next patch were previously a single patch. I separated them so that this patch would be 100% replacement of direct data access with calls to (currently NOP) helper functions. Some comments on what's changed here: https://www.redhat.com/archives/libvir-list/2011-July/msg00568.html (the most important change is in the wording of the commit log message so that it is no longer misleading.) src/qemu/qemu_command.c | 44 ++++++++++++++++++++++++++------------------ src/qemu/qemu_hotplug.c | 16 +++++++++------- src/qemu/qemu_migration.c | 12 ++++++------ src/qemu/qemu_process.c | 10 ++++++---- 4 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f456e25..172e394 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -125,9 +125,12 @@ qemuPhysIfaceConnect(virDomainDefPtr def, net->model && STREQ(net->model, "virtio")) vnet_hdr = 1; - rc = openMacvtapTap(net->ifname, net->mac, net->data.direct.linkdev, - net->data.direct.mode, vnet_hdr, def->uuid, - net->data.direct.virtPortProfile, &res_ifname, + rc = openMacvtapTap(net->ifname, net->mac, + virDomainNetGetActualDirectDev(net), + virDomainNetGetActualDirectMode(net), + vnet_hdr, def->uuid, + virDomainNetGetActualDirectVirtPortProfile(net), + &res_ifname, vmop, driver->stateDir); if (rc >= 0) { virDomainAuditNetDevice(def, net, res_ifname, true); @@ -148,9 +151,10 @@ qemuPhysIfaceConnect(virDomainDefPtr def, err = virDomainConfNWFilterInstantiate(conn, net); if (err) { VIR_FORCE_CLOSE(rc); - delMacvtap(net->ifname, net->mac, net->data.direct.linkdev, - net->data.direct.mode, - net->data.direct.virtPortProfile, + delMacvtap(net->ifname, net->mac, + virDomainNetGetActualDirectDev(net), + virDomainNetGetActualDirectMode(net), + virDomainNetGetActualDirectVirtPortProfile(net), driver->stateDir); VIR_FREE(net->ifname); } @@ -184,8 +188,9 @@ qemuNetworkIfaceConnect(virDomainDefPtr def, int vnet_hdr = 0; int template_ifname = 0; unsigned char tapmac[VIR_MAC_BUFLEN]; + int actualType = virDomainNetGetActualType(net); - if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK) { int active, fail = 0; virErrorPtr errobj; virNetworkPtr network = virNetworkLookupByName(conn, @@ -218,14 +223,15 @@ qemuNetworkIfaceConnect(virDomainDefPtr def, if (fail) return -1; - } else if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { - if (!(brname = strdup(net->data.bridge.brname))) { + } else if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) { + if (!(brname = strdup(virDomainNetGetActualBridgeName(net)))) { virReportOOMError(); return -1; } } else { qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Network type %d is not supported"), net->type); + _("Network type %d is not supported"), + virDomainNetGetActualType(net)); return -1; } @@ -1829,7 +1835,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, bool is_tap = false; virBuffer buf = VIR_BUFFER_INITIALIZER; - switch (net->type) { + switch (virDomainNetGetActualType(net)) { case VIR_DOMAIN_NET_TYPE_NETWORK: case VIR_DOMAIN_NET_TYPE_BRIDGE: case VIR_DOMAIN_NET_TYPE_DIRECT: @@ -1857,7 +1863,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, case VIR_DOMAIN_NET_TYPE_SERVER: case VIR_DOMAIN_NET_TYPE_MCAST: virBufferAddLit(&buf, "socket"); - switch (net->type) { + switch (virDomainNetGetActualType(net)) { case VIR_DOMAIN_NET_TYPE_CLIENT: virBufferAsprintf(&buf, "%cconnect=%s:%d", type_sep, @@ -3678,6 +3684,7 @@ qemuBuildCommandLine(virConnectPtr conn, char vhostfd_name[50] = ""; int vlan; int bootindex = bootNet; + int actualType; bootNet = 0; if (!bootindex) @@ -3690,8 +3697,9 @@ qemuBuildCommandLine(virConnectPtr conn, else vlan = i; - if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK || - net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { + actualType = virDomainNetGetActualType(net); + if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK || + actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) { int tapfd = qemuNetworkIfaceConnect(def, conn, driver, net, qemuCaps); if (tapfd < 0) @@ -3703,7 +3711,7 @@ qemuBuildCommandLine(virConnectPtr conn, if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", tapfd) >= sizeof(tapfd_name)) goto no_memory; - } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { + } else if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) { int tapfd = qemuPhysIfaceConnect(def, conn, driver, net, qemuCaps, vmop); if (tapfd < 0) @@ -3717,9 +3725,9 @@ qemuBuildCommandLine(virConnectPtr conn, goto no_memory; } - if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK || - net->type == VIR_DOMAIN_NET_TYPE_BRIDGE || - net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { + if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK || + actualType == VIR_DOMAIN_NET_TYPE_BRIDGE || + actualType == VIR_DOMAIN_NET_TYPE_DIRECT) { /* Attempt to use vhost-net mode for these types of network device */ int vhostfd; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 6c22f4c..128b9ea 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -603,6 +603,7 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, virDomainDevicePCIAddress guestAddr; int vlan; bool releaseaddr = false; + int actualType = virDomainNetGetActualType(net); if (!qemuCapsGet(priv->qemuCaps, QEMU_CAPS_HOST_NET_ADD)) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -610,14 +611,14 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, return -1; } - if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE || - net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE || + actualType == VIR_DOMAIN_NET_TYPE_NETWORK) { if ((tapfd = qemuNetworkIfaceConnect(vm->def, conn, driver, net, priv->qemuCaps)) < 0) return -1; if (qemuOpenVhostNet(vm->def, net, priv->qemuCaps, &vhostfd) < 0) goto cleanup; - } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { + } else if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) { if ((tapfd = qemuPhysIfaceConnect(vm->def, conn, driver, net, priv->qemuCaps, VIR_VM_OP_CREATE)) < 0) @@ -1613,10 +1614,11 @@ int qemuDomainDetachNetDevice(struct qemud_driver *driver, virDomainConfNWFilterTeardown(detach); #if WITH_MACVTAP - if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) { - delMacvtap(detach->ifname, detach->mac, detach->data.direct.linkdev, - detach->data.direct.mode, - detach->data.direct.virtPortProfile, + if (virDomainNetGetActualType(detach) == VIR_DOMAIN_NET_TYPE_DIRECT) { + delMacvtap(detach->ifname, detach->mac, + virDomainNetGetActualDirectDev(detach), + virDomainNetGetActualDirectMode(detach), + virDomainNetGetActualDirectVirtPortProfile(detach), driver->stateDir); VIR_FREE(detach->ifname); } diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 84fe05a..721e031 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -2337,11 +2337,11 @@ qemuMigrationVPAssociatePortProfiles(virDomainDefPtr def) { for (i = 0; i < def->nnets; i++) { net = def->nets[i]; - if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { + if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT) { if (vpAssociatePortProfileId(net->ifname, net->mac, - net->data.direct.linkdev, - net->data.direct.virtPortProfile, + virDomainNetGetActualDirectDev(net), + virDomainNetGetActualDirectVirtPortProfile(net), def->uuid, VIR_VM_OP_MIGRATE_IN_FINISH) != 0) goto err_exit; @@ -2354,11 +2354,11 @@ qemuMigrationVPAssociatePortProfiles(virDomainDefPtr def) { err_exit: for (i = 0; i < last_good_net; i++) { net = def->nets[i]; - if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { + if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT) { vpDisassociatePortProfileId(net->ifname, net->mac, - net->data.direct.linkdev, - net->data.direct.virtPortProfile, + virDomainNetGetActualDirectDev(net), + virDomainNetGetActualDirectVirtPortProfile(net), VIR_VM_OP_MIGRATE_IN_FINISH); } } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 5121241..3c57a2a 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3014,10 +3014,12 @@ void qemuProcessStop(struct qemud_driver *driver, def = vm->def; for (i = 0; i < def->nnets; i++) { virDomainNetDefPtr net = def->nets[i]; - if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { - delMacvtap(net->ifname, net->mac, net->data.direct.linkdev, - net->data.direct.mode, - net->data.direct.virtPortProfile, driver->stateDir); + if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT) { + delMacvtap(net->ifname, net->mac, + virDomainNetGetActualDirectDev(net), + virDomainNetGetActualDirectMode(net), + virDomainNetGetActualDirectVirtPortProfile(net), + driver->stateDir); VIR_FREE(net->ifname); } } -- 1.7.3.4

On 07/20/2011 12:21 AM, Laine Stump wrote:
The qemu driver accesses fields in the virDomainNetDef directly, but with the advent of the virDomainActualNetDef, some pieces of information may be found in a different place (the ActualNetDef) if the network connection is of type='network' and that network is of forward type='bridge|private|vepa|passthrough'. The previous patch added functions to mask this difference from callers - they hide the decision making process and just pick the value from the proper place.
This patch uses those functions in the qemu driver as a first step in making qemu work with the new network types. At this point, the the virDomainActualNetDef is always NULL, so the GetActualX() function will return exactly what the def->X that's being replaced would have returned (ie bisecting is not compromised).
Indeed nicer wording. ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

This is the one function outside of domain_conf.c that plays around with (even modifying) the internals of the virDomainNetDef, and thus can't be fixed up simply by replacing direct accesses to the fields of the struct with the GetActual*() access functions. In this case, we need to check if the defined type is "network", and if it is *then* check the actual type; if the actual type is "bridge", then we can at least put the bridgename in a place where it can be used; otherwise (if type isn't "bridge"), we behave exactly as we used to - just null out *everything*. --- src/qemu/qemu_driver.c | 39 +++++++++++++++++++++++++++++++++++++-- 1 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 8870e33..8482c69 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3972,9 +3972,44 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, for (i = 0 ; i < def->nnets ; i++) { virDomainNetDefPtr net = def->nets[i]; int bootIndex = net->bootIndex; - if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK || - net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + int actualType = virDomainNetGetActualType(net); + const char *brname; + VIR_FREE(net->data.network.name); + VIR_FREE(net->data.network.portgroup); + if ((actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) && + (brname = virDomainNetGetActualBridgeName(net))) { + + char *brnamecopy = strdup(brname); + if (!brnamecopy) { + virReportOOMError(); + goto cleanup; + } + + virDomainActualNetDefFree(net->data.network.actual); + + memset(net, 0, sizeof *net); + + net->type = VIR_DOMAIN_NET_TYPE_ETHERNET; + net->data.ethernet.dev = brnamecopy; + net->data.ethernet.script = NULL; + net->data.ethernet.ipaddr = NULL; + } else { + /* actualType is either NETWORK or DIRECT. In either + * case, the best we can do is NULL everything out. + */ + virDomainActualNetDefFree(net->data.network.actual); + memset(net, 0, sizeof *net); + + net->type = VIR_DOMAIN_NET_TYPE_ETHERNET; + net->data.ethernet.dev = NULL; + net->data.ethernet.script = NULL; + net->data.ethernet.ipaddr = NULL; + } + } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { + VIR_FREE(net->data.direct.linkdev); + VIR_FREE(net->data.direct.virtPortProfile); memset(net, 0, sizeof *net); -- 1.7.3.4

On 07/20/2011 12:21 AM, Laine Stump wrote:
This is the one function outside of domain_conf.c that plays around with (even modifying) the internals of the virDomainNetDef, and thus can't be fixed up simply by replacing direct accesses to the fields of the struct with the GetActual*() access functions.
In this case, we need to check if the defined type is "network", and if it is *then* check the actual type; if the actual type is "bridge", then we can at least put the bridgename in a place where it can be used; otherwise (if type isn't "bridge"), we behave exactly as we used to - just null out *everything*. --- src/qemu/qemu_driver.c | 39 +++++++++++++++++++++++++++++++++++++-- 1 files changed, 37 insertions(+), 2 deletions(-)
ACK. It helps when I read the context of that change: /* Since we're just exporting args, we can't do bridge/network/direct * setups, since libvirt will normally create TAP/macvtap devices * directly. We convert those configs into generic 'ethernet' * config and assume the user has suitable 'ifup-qemu' scripts */ and realized that xml-to-native is not modifying an existing persistent or running domain, but operating directly on candidate xml. At the same time, it feels a bit awkward that we can't convert xml directly to native cases where the command line that we use internally depends on an fd number for a file that libvirt opens, rather than a filename counterpart. Is that something where we could add a new API flag, or add a new qemu-specific format string (right now we only take "qemu-kvm", so a new one could be "shell-script"), where we could output the command line with fd numbers as-is as well as outputting preliminary shell code snippets that would explain what files were opened to those fd numbers (something like "exec 17>/dev/tun; qemu -network fd:17")? Just thinking aloud here; I don't know that anyone will have much use for this, so not a high priority. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 07/20/2011 07:34 PM, Eric Blake wrote:
On 07/20/2011 12:21 AM, Laine Stump wrote:
This is the one function outside of domain_conf.c that plays around with (even modifying) the internals of the virDomainNetDef, and thus can't be fixed up simply by replacing direct accesses to the fields of the struct with the GetActual*() access functions.
In this case, we need to check if the defined type is "network", and if it is *then* check the actual type; if the actual type is "bridge", then we can at least put the bridgename in a place where it can be used; otherwise (if type isn't "bridge"), we behave exactly as we used to - just null out *everything*. --- src/qemu/qemu_driver.c | 39 +++++++++++++++++++++++++++++++++++++-- 1 files changed, 37 insertions(+), 2 deletions(-)
ACK. It helps when I read the context of that change:
/* Since we're just exporting args, we can't do bridge/network/direct * setups, since libvirt will normally create TAP/macvtap devices * directly. We convert those configs into generic 'ethernet' * config and assume the user has suitable 'ifup-qemu' scripts */
and realized that xml-to-native is not modifying an existing persistent or running domain, but operating directly on candidate xml.
At the same time, it feels a bit awkward that we can't convert xml directly to native cases where the command line that we use internally depends on an fd number for a file that libvirt opens, rather than a filename counterpart. Is that something where we could add a new API flag, or add a new qemu-specific format string (right now we only take "qemu-kvm", so a new one could be "shell-script"), where we could output the command line with fd numbers as-is as well as outputting preliminary shell code snippets that would explain what files were opened to those fd numbers (something like "exec 17>/dev/tun; qemu -network fd:17")? Just thinking aloud here; I don't know that anyone will have much use for this, so not a high priority.
Yeah, that whole bit of code did look a bit like "almost best effort" to translate, rather than a perfect translation. Since it's not really possible to give a commandline that can be run manually with 100% of the functionality provided when run by libvirt (with all its behind the scenes opening of files, creating tap devices, etc), I guess that's okay. If someone wants to see exactly what the command looks like when libvirt runs it, they can start the domain from libvirt and go look in the log file :-P

The network driver needs to assign physical devices for use by modes that use macvtap, keeping track of which physical devices are in use (and how many instances, when the devices can be shared). Three calls are added: networkAllocateActualDevice - finds a physical device for use by the domain, and sets up the virDomainActualNetDef accordingly. networkNotifyActualDevice - assumes that the domain was already running, but libvirtd was restarted, and needs to be notified by each already-running domain about what interfaces they are using. networkReleaseActualDevice - decrements the usage count of the allocated physical device, and frees the virDomainActualNetDef to avoid later accidentally using the device. bridge_driver.[hc] - the new APIs. When WITH_NETWORK is false, these functions are all defined inline static in the .h file (and become a NOP) to prevent link errors. qemu_(command|driver|hotplug|process).c - add calls to the above APIs in the appropriate places. tests/Makefile.am - need to include libvirt_driver_network.la whenever libvirt_driver_qemu.la is linked, to avoid unreferenced symbols (in functions that are never called by the test programs...) --- Change from V1: rather than making things compile properly when configured with --without-network by dropping #if WITH_NETWORK all over in the .c files, in this version I just redefine the functions in question (network.....()) as static inline in the .h file when WITH_NETWORK is false: https://www.redhat.com/archives/libvir-list/2011-July/msg00534.html This works perfectly, but "make syntax-check" doesn't like the fact that I've put ATTRIBUTE_UNUSED on the args of the function prototype. I think make syntax-check should be changed to allow ATTRIBUTE_UNUSED in a .h file, as long as it's part of a function definition (rather than declaration), but could be convinced otherwise if needed to get this pushed sooner. src/libvirt_network.syms | 3 + src/network/bridge_driver.c | 372 +++++++++++++++++++++++++++++++++++++++++++ src/network/bridge_driver.h | 28 ++++ src/qemu/qemu_command.c | 11 ++ src/qemu/qemu_hotplug.c | 40 ++++-- src/qemu/qemu_process.c | 25 +++- tests/Makefile.am | 15 ++- 7 files changed, 476 insertions(+), 18 deletions(-) diff --git a/src/libvirt_network.syms b/src/libvirt_network.syms index 6be5e45..e402b5f 100644 --- a/src/libvirt_network.syms +++ b/src/libvirt_network.syms @@ -3,4 +3,7 @@ # # bridge_driver.h +networkAllocateActualDevice; networkBuildDhcpDaemonCommandLine; +networkNotifyActualDevice; +networkReleaseActualDevice; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 8767af9..15e0710 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -2715,3 +2715,375 @@ int networkRegister(void) { virRegisterStateDriver(&networkStateDriver); return 0; } + +/********************************************************/ + +/* Private API to deal with logical switch capabilities. + * These functions are exported so that other parts of libvirt can + * call them, but are not part of the public API and not in the + * driver's function table. If we ever have more than one network + * driver, we will need to present these functions via a second + * "backend" function table. + */ + +/* networkAllocateActualDevice: + * @iface: the original NetDef from the domain + * + * Looks up the network reference by iface, 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. + * + * Returns 0 on success, -1 on failure. + */ +int +networkAllocateActualDevice(virDomainNetDefPtr iface) +{ + struct network_driver *driver = driverState; + virNetworkObjPtr network; + virNetworkDefPtr netdef; + int ret = -1; + + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) + return 0; + + virDomainActualNetDefFree(iface->data.network.actual); + iface->data.network.actual = NULL; + + networkDriverLock(driver); + network = virNetworkFindByName(&driver->networks, iface->data.network.name); + networkDriverUnlock(driver); + if (!network) { + networkReportError(VIR_ERR_NO_NETWORK, + _("no network with matching name '%s'"), + iface->data.network.name); + goto cleanup; + } + + netdef = network->def; + if ((netdef->forwardType == VIR_NETWORK_FORWARD_BRIDGE) && + netdef->bridge) { + + /* <forward type='bridge'/> <bridge name='xxx'/> + * is VIR_DOMAIN_NET_TYPE_BRIDGE + */ + + if (VIR_ALLOC(iface->data.network.actual) < 0) { + virReportOOMError(); + goto cleanup; + } + + iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + iface->data.network.actual->data.bridge.brname = strdup(netdef->bridge); + if (!iface->data.network.actual->data.bridge.brname) { + virReportOOMError(); + goto cleanup; + } + + } else if ((netdef->forwardType == VIR_NETWORK_FORWARD_BRIDGE) || + (netdef->forwardType == VIR_NETWORK_FORWARD_PRIVATE) || + (netdef->forwardType == VIR_NETWORK_FORWARD_VEPA) || + (netdef->forwardType == VIR_NETWORK_FORWARD_PASSTHROUGH)) { + virVirtualPortProfileParamsPtr virtport = NULL; + + /* <forward type='bridge|private|vepa|passthrough'> are all + * VIR_DOMAIN_NET_TYPE_DIRECT. + */ + + if (VIR_ALLOC(iface->data.network.actual) < 0) { + virReportOOMError(); + goto cleanup; + } + + /* Set type=direct and appropriate <source mode='xxx'/> */ + iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_DIRECT; + switch (netdef->forwardType) { + case VIR_NETWORK_FORWARD_BRIDGE: + iface->data.network.actual->data.direct.mode = VIR_MACVTAP_MODE_BRIDGE; + break; + case VIR_NETWORK_FORWARD_PRIVATE: + iface->data.network.actual->data.direct.mode = VIR_MACVTAP_MODE_PRIVATE; + break; + case VIR_NETWORK_FORWARD_VEPA: + iface->data.network.actual->data.direct.mode = VIR_MACVTAP_MODE_VEPA; + break; + case VIR_NETWORK_FORWARD_PASSTHROUGH: + iface->data.network.actual->data.direct.mode = VIR_MACVTAP_MODE_PASSTHRU; + break; + } + + /* Find the most specific virtportprofile and copy it */ + if (iface->data.network.virtPortProfile) { + virtport = iface->data.network.virtPortProfile; + } else { + virPortGroupDefPtr portgroup + = virPortGroupFindByName(netdef, iface->data.network.portgroup); + if (portgroup) + virtport = portgroup->virtPortProfile; + else + virtport = netdef->virtPortProfile; + } + if (virtport) { + if (VIR_ALLOC(iface->data.network.actual->data.direct.virtPortProfile) < 0) { + virReportOOMError(); + goto cleanup; + } + /* There are no pointers in a virtualPortProfile, so a shallow copy + * is sufficient + */ + *iface->data.network.actual->data.direct.virtPortProfile = *virtport; + } + /* If there is only a single device, just return it (caller will detect + * any error if exclusive use is required but could be acquired). + */ + if (netdef->nForwardIfs == 0) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + _("network '%s' uses a direct mode, but has no forward dev and no interface pool"), + netdef->name); + goto cleanup; + } else { + int ii; + virNetworkForwardIfDefPtr dev = NULL; + + /* pick an interface from the pool */ + + /* PASSTHROUGH mode, and PRIVATE Mode + 802.1Qbh both require + * exclusive access to a device, so current usageCount must be + * 0. Other modes can share, so just search for the one with + * the lowest usageCount. + */ + if ((netdef->forwardType == VIR_NETWORK_FORWARD_PASSTHROUGH) || + ((netdef->forwardType == VIR_NETWORK_FORWARD_PRIVATE) && + iface->data.network.actual->data.direct.virtPortProfile && + (iface->data.network.actual->data.direct.virtPortProfile->virtPortType + == VIR_VIRTUALPORT_8021QBH))) { + /* pick first dev with 0 usageCount */ + + for (ii = 0; ii < netdef->nForwardIfs; ii++) { + if (netdef->forwardIfs[ii].usageCount == 0) { + dev = &netdef->forwardIfs[ii]; + break; + } + } + } else { + /* pick least used dev */ + dev = &netdef->forwardIfs[0]; + for (ii = 1; ii < netdef->nForwardIfs; ii++) { + if (netdef->forwardIfs[ii].usageCount < dev->usageCount) + dev = &netdef->forwardIfs[ii]; + } + } + /* dev points at the physical device we want to use */ + if (!dev) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + _("network '%s' requires exclusive access to interfaces, but none are available"), + netdef->name); + goto cleanup; + } + iface->data.network.actual->data.direct.linkdev = strdup(dev->dev); + if (!iface->data.network.actual->data.direct.linkdev) { + virReportOOMError(); + goto cleanup; + } + /* we are now assured of success, so mark the allocation */ + dev->usageCount++; + VIR_DEBUG("Using physical device %s, usageCount %d", + dev->dev, dev->usageCount); + } + } + + ret = 0; +cleanup: + if (network) + virNetworkObjUnlock(network); + if (ret < 0) { + virDomainActualNetDefFree(iface->data.network.actual); + iface->data.network.actual = NULL; + } + return ret; +} + +/* networkNotifyActualDevice: + * @iface: the domain's NetDef with an "actual" device already filled in. + * + * 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. + */ +int +networkNotifyActualDevice(virDomainNetDefPtr iface) +{ + struct network_driver *driver = driverState; + virNetworkObjPtr network; + virNetworkDefPtr netdef; + char *actualDev; + int ret = -1; + + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) + return 0; + + if (!iface->data.network.actual || + (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_DIRECT)) { + VIR_DEBUG("Nothing to claim from network %s", iface->data.network.name); + return 0; + } + + networkDriverLock(driver); + network = virNetworkFindByName(&driver->networks, iface->data.network.name); + networkDriverUnlock(driver); + if (!network) { + networkReportError(VIR_ERR_NO_NETWORK, + _("no network with matching name '%s'"), + iface->data.network.name); + goto cleanup; + } + + actualDev = virDomainNetGetActualDirectDev(iface); + if (!actualDev) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("the interface uses a direct mode, but has no source dev")); + goto cleanup; + } + + netdef = network->def; + if (netdef->nForwardIfs == 0) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + _("network '%s' uses a direct mode, but has no forward dev and no interface pool"), + netdef->name); + goto cleanup; + } else { + int ii; + virNetworkForwardIfDefPtr dev = NULL; + + /* find the matching interface in the pool and increment its usageCount */ + + for (ii = 0; ii < netdef->nForwardIfs; ii++) { + if (STREQ(actualDev, netdef->forwardIfs[ii].dev)) { + dev = &netdef->forwardIfs[ii]; + break; + } + } + /* dev points at the physical device we want to use */ + if (!dev) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + _("network '%s' doesn't have dev='%s' in use by domain"), + netdef->name, actualDev); + goto cleanup; + } + + /* PASSTHROUGH mode, and PRIVATE Mode + 802.1Qbh both require + * exclusive access to a device, so current usageCount must be + * 0 in those cases. + */ + if ((dev->usageCount > 0) && + ((netdef->forwardType == VIR_NETWORK_FORWARD_PASSTHROUGH) || + ((netdef->forwardType == VIR_NETWORK_FORWARD_PRIVATE) && + iface->data.network.actual->data.direct.virtPortProfile && + (iface->data.network.actual->data.direct.virtPortProfile->virtPortType + == VIR_VIRTUALPORT_8021QBH)))) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + _("network '%s' claims dev='%s' is already in use by a different domain"), + netdef->name, actualDev); + goto cleanup; + } + /* we are now assured of success, so mark the allocation */ + dev->usageCount++; + VIR_DEBUG("Using physical device %s, usageCount %d", + dev->dev, dev->usageCount); + } + + ret = 0; +cleanup: + if (network) + virNetworkObjUnlock(network); + return ret; +} + + +/* networkReleaseActualDevice: + * @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. + */ +int +networkReleaseActualDevice(virDomainNetDefPtr iface) +{ + struct network_driver *driver = driverState; + virNetworkObjPtr network = NULL; + virNetworkDefPtr netdef; + char *actualDev; + int ret = -1; + + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) + return 0; + + if (!iface->data.network.actual || + (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_DIRECT)) { + VIR_DEBUG("Nothing to release to network %s", iface->data.network.name); + ret = 0; + goto cleanup; + } + + networkDriverLock(driver); + network = virNetworkFindByName(&driver->networks, iface->data.network.name); + networkDriverUnlock(driver); + if (!network) { + networkReportError(VIR_ERR_NO_NETWORK, + _("no network with matching name '%s'"), + iface->data.network.name); + goto cleanup; + } + + actualDev = virDomainNetGetActualDirectDev(iface); + if (!actualDev) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("the interface uses a direct mode, but has no source dev")); + goto cleanup; + } + + netdef = network->def; + if (netdef->nForwardIfs == 0) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + _("network '%s' uses a direct mode, but has no forward dev and no interface pool"), + netdef->name); + goto cleanup; + } else { + int ii; + virNetworkForwardIfDefPtr dev = NULL; + + for (ii = 0; ii < netdef->nForwardIfs; ii++) { + if (STREQ(actualDev, netdef->forwardIfs[ii].dev)) { + dev = &netdef->forwardIfs[ii]; + break; + } + } + /* dev points at the physical device we've been using */ + if (!dev) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + _("network '%s' doesn't have dev='%s' in use by domain"), + netdef->name, actualDev); + goto cleanup; + } + + dev->usageCount--; + VIR_DEBUG("Releasing physical device %s, usageCount %d", + dev->dev, dev->usageCount); + } + + ret = 0; +cleanup: + if (network) + virNetworkObjUnlock(network); + virDomainActualNetDefFree(iface->data.network.actual); + iface->data.network.actual = NULL; + return ret; +} diff --git a/src/network/bridge_driver.h b/src/network/bridge_driver.h index 2896c84..710d862 100644 --- a/src/network/bridge_driver.h +++ b/src/network/bridge_driver.h @@ -29,13 +29,41 @@ # include "internal.h" # include "network_conf.h" +# include "domain_conf.h" # include "command.h" # include "dnsmasq.h" int networkRegister(void); + +#if WITH_NETWORK +int networkAllocateActualDevice(virDomainNetDefPtr iface); +int networkNotifyActualDevice(virDomainNetDefPtr iface); +int networkReleaseActualDevice(virDomainNetDefPtr iface); + int networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, virCommandPtr *cmdout, char *pidfile, dnsmasqContext *dctx); +#else +static inline int +networkAllocateActualDevice(virDomainNetDefPtr iface ATTRIBUTE_UNUSED) +{ return 0; } + +static inline int +networkNotifyActualDevice(virDomainNetDefPtr iface ATTRIBUTE_UNUSED) +{ return 0; } + +static inline int +networkReleaseActualDevice(virDomainNetDefPtr iface ATTRIBUTE_UNUSED) +{ return 0; } + +static inline int +networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network ATTRIBUTE_UNUSED, + virCommandPtr *cmdout ATTRIBUTE_UNUSED, + char *pidfile ATTRIBUTE_UNUSED, + dnsmasqContext *dctx ATTRIBUTE_UNUSED) +{ return 0; } + +#endif typedef char *(*networkDnsmasqLeaseFileNameFunc)(const char *netname); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 172e394..0ae8d67 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -37,6 +37,7 @@ #include "domain_nwfilter.h" #include "domain_audit.h" #include "domain_conf.h" +#include "network/bridge_driver.h" #include <sys/utsname.h> #include <sys/stat.h> @@ -3697,6 +3698,13 @@ qemuBuildCommandLine(virConnectPtr conn, else vlan = i; + /* If appropriate, grab a physical device from the configured + * network's pool of devices, or resolve bridge device name + * to the one defined in the network definition. + */ + if (networkAllocateActualDevice(net) < 0) + goto error; + actualType = virDomainNetGetActualType(net); if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK || actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) { @@ -4693,6 +4701,9 @@ qemuBuildCommandLine(virConnectPtr conn, no_memory: virReportOOMError(); error: + /* free up any resources in the network driver */ + for (i = 0 ; i < def->nnets ; i++) + networkReleaseActualDevice(def->nets[i]); for (i = 0; i <= last_good_net; i++) virDomainConfNWFilterTeardown(def->nets[i]); virBufferFreeAndReset(&rbd_hosts); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 128b9ea..74ce9be 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -39,6 +39,7 @@ #include "files.h" #include "qemu_cgroup.h" #include "locking/domain_lock.h" +#include "network/bridge_driver.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -603,7 +604,8 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, virDomainDevicePCIAddress guestAddr; int vlan; bool releaseaddr = false; - int actualType = virDomainNetGetActualType(net); + bool iface_connected = false; + int actualType; if (!qemuCapsGet(priv->qemuCaps, QEMU_CAPS_HOST_NET_ADD)) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -611,18 +613,28 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, return -1; } + /* If appropriate, grab a physical device from the configured + * network's pool of devices, or resolve bridge device name + * to the one defined in the network definition. + */ + if (networkAllocateActualDevice(net) < 0) + goto cleanup; + + actualType = virDomainNetGetActualType(net); if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE || actualType == VIR_DOMAIN_NET_TYPE_NETWORK) { if ((tapfd = qemuNetworkIfaceConnect(vm->def, conn, driver, net, priv->qemuCaps)) < 0) - return -1; + goto cleanup; + iface_connected = true; if (qemuOpenVhostNet(vm->def, net, priv->qemuCaps, &vhostfd) < 0) goto cleanup; } else if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) { if ((tapfd = qemuPhysIfaceConnect(vm->def, conn, driver, net, priv->qemuCaps, VIR_VM_OP_CREATE)) < 0) - return -1; + goto cleanup; + iface_connected = true; if (qemuOpenVhostNet(vm->def, net, priv->qemuCaps, &vhostfd) < 0) goto cleanup; } @@ -738,16 +750,19 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, vm->def->nets[vm->def->nnets++] = net; cleanup: - if ((ret != 0) && - qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) && - (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && - releaseaddr && - qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, - net->info.addr.pci.slot) < 0) - VIR_WARN("Unable to release PCI address on NIC"); + if (ret < 0) { + if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) && + (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && + releaseaddr && + qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, + net->info.addr.pci.slot) < 0) + VIR_WARN("Unable to release PCI address on NIC"); - if (ret != 0) - virDomainConfNWFilterTeardown(net); + if (iface_connected) + virDomainConfNWFilterTeardown(net); + + networkReleaseActualDevice(net); + } VIR_FREE(nicstr); VIR_FREE(netstr); @@ -1634,6 +1649,7 @@ int qemuDomainDetachNetDevice(struct qemud_driver *driver, } } + networkReleaseActualDevice(detach); if (vm->def->nnets > 1) { memmove(vm->def->nets + i, vm->def->nets + i + 1, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 3c57a2a..8a8beb1 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -56,6 +56,7 @@ #include "domain_audit.h" #include "domain_nwfilter.h" #include "locking/domain_lock.h" +#include "network/bridge_driver.h" #include "uuid.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -2178,6 +2179,19 @@ int qemuProcessStopCPUs(struct qemud_driver *driver, virDomainObjPtr vm, static int +qemuProcessNotifyNets(virDomainDefPtr def) +{ + int ii; + + for (ii = 0 ; ii < def->nnets ; ii++) { + virDomainNetDefPtr net = def->nets[ii]; + if (networkNotifyActualDevice(net) < 0) + return -1; + } + return 0; +} + +static int qemuProcessFiltersInstantiate(virConnectPtr conn, virDomainDefPtr def) { @@ -2379,6 +2393,9 @@ qemuProcessReconnect(void *payload, const void *name ATTRIBUTE_UNUSED, void *opa if (virSecurityManagerReserveLabel(driver->securityManager, obj) < 0) goto error; + if (qemuProcessNotifyNets(obj->def) < 0) + goto error; + if (qemuProcessFiltersInstantiate(conn, obj->def)) goto error; @@ -3010,10 +3027,10 @@ void qemuProcessStop(struct qemud_driver *driver, qemuDomainReAttachHostDevices(driver, vm->def); -#if WITH_MACVTAP def = vm->def; for (i = 0; i < def->nnets; i++) { virDomainNetDefPtr net = def->nets[i]; +#if WITH_MACVTAP if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT) { delMacvtap(net->ifname, net->mac, virDomainNetGetActualDirectDev(net), @@ -3022,8 +3039,12 @@ void qemuProcessStop(struct qemud_driver *driver, driver->stateDir); VIR_FREE(net->ifname); } - } #endif + /* release the physical device (or any other resources used by + * this interface in the network driver + */ + networkReleaseActualDevice(net); + } retry: if ((ret = qemuRemoveCgroup(driver, vm, 0)) < 0) { diff --git a/tests/Makefile.am b/tests/Makefile.am index e9a445e..971ab70 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -314,23 +314,30 @@ EXTRA_DIST += xml2sexprtest.c sexpr2xmltest.c xmconfigtest.c \ endif if WITH_QEMU + +qemu_LDADDS = ../src/libvirt_driver_qemu.la + +if WITH_NETWORK +qemu_LDADDS += ../src/libvirt_driver_network.la +endif + qemuxml2argvtest_SOURCES = \ qemuxml2argvtest.c testutilsqemu.c testutilsqemu.h \ testutils.c testutils.h -qemuxml2argvtest_LDADD = ../src/libvirt_driver_qemu.la $(LDADDS) +qemuxml2argvtest_LDADD = $(qemu_LDADDS) $(LDADDS) qemuxml2xmltest_SOURCES = \ qemuxml2xmltest.c testutilsqemu.c testutilsqemu.h \ testutils.c testutils.h -qemuxml2xmltest_LDADD = ../src/libvirt_driver_qemu.la $(LDADDS) +qemuxml2xmltest_LDADD = $(qemu_LDADDS) $(LDADDS) qemuargv2xmltest_SOURCES = \ qemuargv2xmltest.c testutilsqemu.c testutilsqemu.h \ testutils.c testutils.h -qemuargv2xmltest_LDADD = ../src/libvirt_driver_qemu.la $(LDADDS) +qemuargv2xmltest_LDADD = $(qemu_LDADDS) $(LDADDS) qemuhelptest_SOURCES = qemuhelptest.c testutils.c testutils.h -qemuhelptest_LDADD = ../src/libvirt_driver_qemu.la $(LDADDS) +qemuhelptest_LDADD = $(qemu_LDADDS) $(LDADDS) else EXTRA_DIST += qemuxml2argvtest.c qemuxml2xmltest.c qemuargv2xmltest.c qemuhelptest.c testutilsqemu.c testutilsqemu.h endif -- 1.7.3.4

On 07/20/2011 12:21 AM, Laine Stump wrote: [sorry for running out of time yesterday, and leaving this series hanging on one patch]
The network driver needs to assign physical devices for use by modes that use macvtap, keeping track of which physical devices are in use (and how many instances, when the devices can be shared). Three calls are added:
networkAllocateActualDevice - finds a physical device for use by the domain, and sets up the virDomainActualNetDef accordingly.
networkNotifyActualDevice - assumes that the domain was already running, but libvirtd was restarted, and needs to be notified by each already-running domain about what interfaces they are using.
networkReleaseActualDevice - decrements the usage count of the allocated physical device, and frees the virDomainActualNetDef to avoid later accidentally using the device.
bridge_driver.[hc] - the new APIs. When WITH_NETWORK is false, these functions are all defined inline static in the .h file (and become a NOP) to prevent link errors.
qemu_(command|driver|hotplug|process).c - add calls to the above APIs in the appropriate places.
tests/Makefile.am - need to include libvirt_driver_network.la whenever libvirt_driver_qemu.la is linked, to avoid unreferenced symbols (in functions that are never called by the test programs...)
The joys of static linking - once you need one function in a .o, all others in that file get linked in, along with their dependencies, even if they remain uncalled. I don't know if that means we need to refactor our qemu driver into yet more files, but that's a question for another day; I'm fine with the extra library in tests/Makefile.am for now.
---
Change from V1: rather than making things compile properly when configured with --without-network by dropping #if WITH_NETWORK all over in the .c files, in this version I just redefine the functions in question (network.....()) as static inline in the .h file when WITH_NETWORK is false:
https://www.redhat.com/archives/libvir-list/2011-July/msg00534.html
This works perfectly, but "make syntax-check" doesn't like the fact that I've put ATTRIBUTE_UNUSED on the args of the function prototype. I think make syntax-check should be changed to allow ATTRIBUTE_UNUSED in a .h file, as long as it's part of a function definition (rather than declaration), but could be convinced otherwise if needed to get this pushed sooner.
It would be easy enough to exempt just that one header from the syntax check rule (but don't apply this - see below): diff --git i/cfg.mk w/cfg.mk index f2a951d..651e7e8 100644 --- i/cfg.mk +++ w/cfg.mk @@ -664,6 +664,9 @@ $(srcdir)/src/remote/remote_client_bodies.h: $(srcdir)/src/remote/remote_protoco $(MAKE) -C src remote/remote_client_bodies.h # List all syntax-check exemptions: +exclude_file_name_regexp--sc_avoid_attribute_unused_in_header = \ + ^src/network/bridge_driver\.h$$ + exclude_file_name_regexp--sc_avoid_strcase = ^tools/virsh\.c$$ _src1=libvirt|fdstream|qemu/qemu_monitor|util/(command|util)|xen/xend_internal|rpc/virnetsocket
+ +/* networkAllocateActualDevice: + * @iface: the original NetDef from the domain + * + * Looks up the network reference by iface, 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. + * + * Returns 0 on success, -1 on failure. + */ +int +networkAllocateActualDevice(virDomainNetDefPtr iface) +{ + struct network_driver *driver = driverState; + virNetworkObjPtr network; + virNetworkDefPtr netdef; + int ret = -1; + + if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK)
Uses iface without first checking for NULL, so let's mark that in the .h file.
+ if (virtport) { + if (VIR_ALLOC(iface->data.network.actual->data.direct.virtPortProfile)< 0) { + virReportOOMError(); + goto cleanup; + } + /* There are no pointers in a virtualPortProfile, so a shallow copy + * is sufficient + */ + *iface->data.network.actual->data.direct.virtPortProfile = *virtport;
Will this ever bite us later if we add a pointer to the struct, but forget to update this line of code? Adding a helper function now, even if the helper function just uses bitwise shallow copy for now, might ease maintenance down the road. But it's not worth holding up this patch just for that.
+ } + /* If there is only a single device, just return it (caller will detect + * any error if exclusive use is required but could be acquired).
s/could be/could not be/
int networkRegister(void); + +#if WITH_NETWORK
The cppi syntax-check didn't like this.
+int networkAllocateActualDevice(virDomainNetDefPtr iface);
See comment above about adding attributes.
+#else +static inline int +networkAllocateActualDevice(virDomainNetDefPtr iface ATTRIBUTE_UNUSED)
I don't know of any way to write a regex that avoids ATTRIBUTE_UNUSED but permits static inline. However, it _is_ possible to write a macro rather than use a static inline function, at which point you don't need ATTRIBUTE_UNUSED in the first place! ACK with this squashed in: diff --git i/src/network/bridge_driver.c w/src/network/bridge_driver.c index b36b1f1..99033a2 100644 --- i/src/network/bridge_driver.c +++ w/src/network/bridge_driver.c @@ -2838,7 +2838,7 @@ networkAllocateActualDevice(virDomainNetDefPtr iface) *iface->data.network.actual->data.direct.virtPortProfile = *virtport; } /* If there is only a single device, just return it (caller will detect - * any error if exclusive use is required but could be acquired). + * any error if exclusive use is required but could not be acquired). */ if (netdef->nForwardIfs == 0) { networkReportError(VIR_ERR_INTERNAL_ERROR, diff --git i/src/network/bridge_driver.h w/src/network/bridge_driver.h index 710d862..d409f76 100644 --- i/src/network/bridge_driver.h +++ w/src/network/bridge_driver.h @@ -1,7 +1,7 @@ /* * network_driver.h: core driver methods for managing networks * - * Copyright (C) 2006, 2007 Red Hat, Inc. + * Copyright (C) 2006, 2007, 2011 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -35,35 +35,25 @@ int networkRegister(void); -#if WITH_NETWORK -int networkAllocateActualDevice(virDomainNetDefPtr iface); -int networkNotifyActualDevice(virDomainNetDefPtr iface); -int networkReleaseActualDevice(virDomainNetDefPtr iface); +# if WITH_NETWORK +int networkAllocateActualDevice(virDomainNetDefPtr iface) + ATTRIBUTE_NONNULL(1); +int networkNotifyActualDevice(virDomainNetDefPtr iface) + ATTRIBUTE_NONNULL(1); +int networkReleaseActualDevice(virDomainNetDefPtr iface) + ATTRIBUTE_NONNULL(1); int networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network, virCommandPtr *cmdout, char *pidfile, - dnsmasqContext *dctx); -#else -static inline int -networkAllocateActualDevice(virDomainNetDefPtr iface ATTRIBUTE_UNUSED) -{ return 0; } - -static inline int -networkNotifyActualDevice(virDomainNetDefPtr iface ATTRIBUTE_UNUSED) -{ return 0; } - -static inline int -networkReleaseActualDevice(virDomainNetDefPtr iface ATTRIBUTE_UNUSED) -{ return 0; } - -static inline int -networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network ATTRIBUTE_UNUSED, - virCommandPtr *cmdout ATTRIBUTE_UNUSED, - char *pidfile ATTRIBUTE_UNUSED, - dnsmasqContext *dctx ATTRIBUTE_UNUSED) -{ return 0; } - -#endif + dnsmasqContext *dctx) + ; +# else +/* Define no-op replacements that don't drag in any link dependencies. */ +# define networkAllocateActualDevice(iface) 0 +# define networkNotifyActualDevice(iface) 0 +# define networkReleaseActualDevice(iface) 0 +# define networkBuildDhcpDaemonCommandLine(network, cmdout, pidfile, dctx) 0 +# endif typedef char *(*networkDnsmasqLeaseFileNameFunc)(const char *netname); -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 07/20/2011 02:21 AM, Laine Stump wrote:
This series deprecates patches 04/10 - 10/10 of the previous series of the same name, posted here:
https://www.redhat.com/archives/libvir-list/2011-July/msg00149.html
This patch is in response to the following bug reports:
https://bugzilla.redhat.com/show_bug.cgi?id=643947 (RHEL) https://bugzilla.redhat.com/show_bug.cgi?id=636106 (upstream)
The first 4 patches of the earlier series were reasonable cleanups in their own right, and were ACKed, so I pushed them earlier. I reorganized the remaining 6 patches accord to advice from Dan Berrange and Eric Blake. Some notes about correlation between v1 and v2:
I've pushed the entire series, with the fixes Eric suggested.
participants (3)
-
Daniel Veillard
-
Eric Blake
-
Laine Stump