diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 1905d6f85d5b933240f019f6d19223398c5b98b2..4c4a9bd08b24153ef7deba954d33e0fb854275a8 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1428,8 +1428,11 @@ virNetDevVethDelete; # virnetdevvportprofile.h virNetDevVPortProfileAssociate; +virNetDevVPortProfileCheckComplete; +virNetDevVPortProfileCheckNoExtras; virNetDevVPortProfileDisassociate; virNetDevVPortProfileEqual; +virNetDevVPortProfileMerge3; virNetDevVPortProfileOpTypeFromString; virNetDevVPortProfileOpTypeToString; diff --git a/src/util/virnetdevvportprofile.c b/src/util/virnetdevvportprofile.c index 6db04f7f117176bbeb92668532595a761e687bfa..e686fd961b3b6b6aabea2c3685e889751a3e689a 100644 --- a/src/util/virnetdevvportprofile.c +++ b/src/util/virnetdevvportprofile.c @@ -120,6 +120,319 @@ virNetDevVPortProfileEqual(virNetDevVPortProfilePtr a, virNetDevVPortProfilePtr return true; } +/* virNetDevVPortProfileCheckComplete() checks that all attributes + * required for the type of virtport are specified. When + * generateMissing is true, any missing attribute that can be + * autogenerated, will be (instanceid, interfaceid). If virtport == + * NULL or virtPortType == NONE, then the result is always 0 + * (success). If a required attribute is missing, an error is logged + * and -1 is returned. + */ +int +virNetDevVPortProfileCheckComplete(virNetDevVPortProfilePtr virtport, + bool generateMissing) +{ + const char *missing = NULL; + + if (!virtport || virtport->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE) + return 0; + + switch (virtport->virtPortType) { + case VIR_NETDEV_VPORT_PROFILE_8021QBG: + if (!virtport->managerID_specified) { + missing = "managerid"; + } else if (!virtport->typeID_specified) { + missing = "typeid"; + } else if (!virtport->typeIDVersion_specified) { + missing = "typeidversion"; + } else if (!virtport->instanceID_specified) { + if (generateMissing) { + if (virUUIDGenerate(virtport->instanceID) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot generate a random uuid for instanceid")); + return -1; + } + virtport->instanceID_specified = true; + } else { + missing = "instanceid"; + } + } + break; + + case VIR_NETDEV_VPORT_PROFILE_8021QBH: + if (!virtport->profileID[0]) + missing = "profileid"; + break; + + case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH: + /* profileid is optional for openvswitch */ + if (!virtport->interfaceID_specified) { + if (generateMissing) { + if (virUUIDGenerate(virtport->interfaceID) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot generate a random uuid for interfaceid")); + return -1; + } + virtport->interfaceID_specified = true; + } else { + missing = "interfaceid"; + } + } + break; + } + + if (missing) { + virReportError(VIR_ERR_XML_ERROR, + _("missing %s in "), missing, + virNetDevVPortTypeToString(virtport->virtPortType)); + return -1; + } + + return 0; +} + +/* virNetDevVPortProfileCheckNoExtras() checks that there are no + * attributes specified in this virtport that are inappropriate for + * the type. if virtport == NULL or virtPortType == NONE, then the + * result is always 0 (success). If an extra attribute is present, + * an error is logged and -1 is returned. + */ +int +virNetDevVPortProfileCheckNoExtras(virNetDevVPortProfilePtr virtport) +{ + const char *extra = NULL; + + if (!virtport || virtport->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE) + return 0; + + switch (virtport->virtPortType) { + case VIR_NETDEV_VPORT_PROFILE_8021QBG: + if (virtport->profileID[0]) + extra = "profileid"; + else if (virtport->interfaceID_specified) + extra = "interfaceid"; + break; + + case VIR_NETDEV_VPORT_PROFILE_8021QBH: + if (virtport->managerID_specified) + extra = "managerid"; + else if (virtport->typeID_specified) + extra = "typeid"; + else if (virtport->typeIDVersion_specified) + extra = "typeidversion"; + else if (virtport->instanceID_specified) + extra = "instanceid"; + else if (virtport->interfaceID_specified) + extra = "interfaceid"; + break; + + case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH: + if (virtport->managerID_specified) + extra = "managerid"; + else if (virtport->typeID_specified) + extra = "typeid"; + else if (virtport->typeIDVersion_specified) + extra = "typeidversion"; + else if (virtport->instanceID_specified) + extra = "instanceid"; + break; + } + + if (extra) { + virReportError(VIR_ERR_XML_ERROR, + _("extra %s unsupported in "), + extra, + virNetDevVPortTypeToString(virtport->virtPortType)); + return -1; + } + + return 0; +} + +/* virNetDevVPortProfileMerge() - merge the attributes in mods into + * orig. If anything that is set in mods has already been set in orig + * *and doesn't match*, log an error and return -1, otherwise return 0. + */ +static int +virNetDevVPortProfileMerge(virNetDevVPortProfilePtr orig, + virNetDevVPortProfilePtr mods) +{ + enum virNetDevVPortProfile otype; + + if (!orig || !mods) + return 0; + + otype = orig->virtPortType; + + if (mods->virtPortType != VIR_NETDEV_VPORT_PROFILE_NONE) { + if (otype != VIR_NETDEV_VPORT_PROFILE_NONE && + otype != mods->virtPortType) { + virReportError(VIR_ERR_XML_ERROR, + _("attempt to merge virtualports " + "with mismatched types (%s and %s)"), + virNetDevVPortTypeToString(otype), + virNetDevVPortTypeToString(mods->virtPortType)); + return -1; + } + otype = orig->virtPortType = mods->virtPortType; + } + + if (mods->managerID_specified && + (otype == VIR_NETDEV_VPORT_PROFILE_8021QBG || + otype == VIR_NETDEV_VPORT_PROFILE_NONE)) { + if (orig->managerID_specified && + (orig->managerID != mods->managerID)) { + virReportError(VIR_ERR_XML_ERROR, + _("attempt to merge virtualports " + "with mismatched managerids (%d and %d)"), + orig->managerID, mods->managerID); + return -1; + } + orig->managerID = mods->managerID; + orig->managerID_specified = true; + } + + if (mods->typeID_specified && + (otype == VIR_NETDEV_VPORT_PROFILE_8021QBG || + otype == VIR_NETDEV_VPORT_PROFILE_NONE)) { + if (orig->typeID_specified && + (orig->typeID != mods->typeID)) { + virReportError(VIR_ERR_XML_ERROR, + _("attempt to merge virtualports " + "with mismatched typeids (%d and %d)"), + orig->typeID, mods->typeID); + return -1; + } + orig->typeID = mods->typeID; + orig->typeID_specified = true; + } + + if (mods->typeIDVersion_specified && + (otype == VIR_NETDEV_VPORT_PROFILE_8021QBG || + otype == VIR_NETDEV_VPORT_PROFILE_NONE)) { + if (orig->typeIDVersion_specified && + (orig->typeIDVersion != mods->typeIDVersion)) { + virReportError(VIR_ERR_XML_ERROR, + _("attempt to merge virtualports with " + "mismatched typeidversions (%d and %d)"), + orig->typeIDVersion, mods->typeIDVersion); + return -1; + } + orig->typeIDVersion = mods->typeIDVersion; + orig->typeIDVersion_specified = true; + } + + if (mods->instanceID_specified && + (otype == VIR_NETDEV_VPORT_PROFILE_8021QBG || + otype == VIR_NETDEV_VPORT_PROFILE_NONE)) { + if (orig->instanceID_specified && + memcmp(orig->instanceID, mods->instanceID, + sizeof(orig->instanceID))) { + char origuuid[VIR_UUID_STRING_BUFLEN]; + char modsuuid[VIR_UUID_STRING_BUFLEN]; + + virReportError(VIR_ERR_XML_ERROR, + _("attempt to merge virtualports with " + "mismatched instanceids ('%s' and '%s')"), + virUUIDFormat(orig->instanceID, origuuid), + virUUIDFormat(mods->instanceID, modsuuid)); + return -1; + } + memcpy(orig->instanceID, mods->instanceID, sizeof(orig->instanceID)); + orig->instanceID_specified = true; + } + + if (mods->interfaceID_specified && + (otype == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH || + otype == VIR_NETDEV_VPORT_PROFILE_NONE)) { + if (orig->interfaceID_specified && + memcmp(orig->interfaceID, mods->interfaceID, + sizeof(orig->interfaceID))) { + char origuuid[VIR_UUID_STRING_BUFLEN]; + char modsuuid[VIR_UUID_STRING_BUFLEN]; + + virReportError(VIR_ERR_XML_ERROR, + _("attempt to merge virtualports with " + "mismatched interfaceids ('%s' and '%s')"), + virUUIDFormat(orig->interfaceID, origuuid), + virUUIDFormat(mods->interfaceID, modsuuid)); + return -1; + } + memcpy(orig->interfaceID, mods->interfaceID, sizeof(orig->interfaceID)); + orig->interfaceID_specified = true; + } + + if (mods->profileID[0] && + (otype == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH || + otype == VIR_NETDEV_VPORT_PROFILE_8021QBH || + otype == VIR_NETDEV_VPORT_PROFILE_NONE)) { + if (orig->profileID[0] && + STRNEQ(orig->profileID, mods->profileID)) { + virReportError(VIR_ERR_XML_ERROR, + _("attempt to merge virtualports with " + "mismatched profileids ('%s' and '%s')"), + orig->profileID, mods->profileID); + return -1; + } + if (virStrcpyStatic(orig->profileID, mods->profileID)) { + /* this should never happen - it indicates mods->profileID + * isn't properly null terminated. */ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("corrupted profileid string")); + return -1; + } + } + return 0; +} + +/* virNetDevVPortProfileMerge3() - create a new virNetDevVPortProfile + * that is a combination of the three input profiles. fromInterface is + * highest priority and fromPortgroup is lowest. As lower priority + * objects' attributes are merged in, if the attribute is unset in the + * result object, it is set from the lower priority object, but if it + * is already set in the result and the lower priority object wants to + * change it, that is an error. + */ + +int virNetDevVPortProfileMerge3(virNetDevVPortProfilePtr *result, + virNetDevVPortProfilePtr fromInterface, + virNetDevVPortProfilePtr fromNetwork, + virNetDevVPortProfilePtr fromPortgroup) +{ + int ret = -1; + *result = NULL; + + if ((!fromInterface || (fromInterface->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE)) && + (!fromNetwork || (fromNetwork->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE)) && + (!fromPortgroup || (fromPortgroup->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE))) { + return 0; + } + + /* at least one of the source profiles is non-empty */ + if (VIR_ALLOC(*result) < 0) { + virReportOOMError(); + return ret; + } + + /* start with the interface's profile. There are no pointers in a + * virtualPortProfile, so a shallow copy is sufficient. + */ + if (fromInterface) + **result = *fromInterface; + + if (virNetDevVPortProfileMerge(*result, fromNetwork) < 0) + goto error; + if (virNetDevVPortProfileMerge(*result, fromPortgroup) < 0) + goto error; + + ret = 0; + +error: + if (ret < 0) + VIR_FREE(*result); + return ret; +} + #if WITH_VIRTUALPORT diff --git a/src/util/virnetdevvportprofile.h b/src/util/virnetdevvportprofile.h index 3c16bfed843146895b33402f9251303bf80cc56c..d23c284202a236507ce7b1baa9a856a6c0eaf949 100644 --- a/src/util/virnetdevvportprofile.h +++ b/src/util/virnetdevvportprofile.h @@ -58,18 +58,24 @@ VIR_ENUM_DECL(virNetDevVPortProfileOp) typedef struct _virNetDevVPortProfile virNetDevVPortProfile; typedef virNetDevVPortProfile *virNetDevVPortProfilePtr; struct _virNetDevVPortProfile { - enum virNetDevVPortProfile virtPortType; + int virtPortType; /* enum virNetDevVPortProfile */ /* these members are used when virtPortType == 802.1Qbg */ uint8_t managerID; + bool managerID_specified; uint32_t typeID; /* 24 bit valid */ + bool typeID_specified; uint8_t typeIDVersion; + bool typeIDVersion_specified; unsigned char instanceID[VIR_UUID_BUFLEN]; + bool instanceID_specified; /* this member is used when virtPortType == 802.1Qbh|openvswitch */ + /* this is a null-terminated character string */ char profileID[LIBVIRT_IFLA_VF_PORT_PROFILE_MAX]; /* this member is used when virtPortType == openvswitch */ unsigned char interfaceID[VIR_UUID_BUFLEN]; + bool interfaceID_specified; /* NB - if virtPortType == NONE, any/all of the items could be used */ }; @@ -77,6 +83,15 @@ struct _virNetDevVPortProfile { bool virNetDevVPortProfileEqual(virNetDevVPortProfilePtr a, virNetDevVPortProfilePtr b); +int virNetDevVPortProfileCheckComplete(virNetDevVPortProfilePtr virtport, + bool generateMissing); +int virNetDevVPortProfileCheckNoExtras(virNetDevVPortProfilePtr virtport); + +int virNetDevVPortProfileMerge3(virNetDevVPortProfilePtr *result, + virNetDevVPortProfilePtr fromInterface, + virNetDevVPortProfilePtr fromNetwork, + virNetDevVPortProfilePtr fromPortgroup); + int virNetDevVPortProfileAssociate(const char *ifname, const virNetDevVPortProfilePtr virtPort, const virMacAddrPtr macaddr,