From b8fa5fd071e9f552f40bd6a5566e836e588ab181 Mon Sep 17 00:00:00 2001 From: Matthias Bolte Date: Sun, 5 Aug 2012 22:11:50 +0200 Subject: [PATCH] esx: Implement network driver An ESX server has one or more PhysicalNics that represent the actual hardware NICs. Those can be listed via the interface driver. A libvirt virtual network is mapped to a HostVirtualSwitch. On the physical side a HostVirtualSwitch can be connected to PhysicalNics. On the virtual side a HostVirtualSwitch has HostPortGroups that are mapped to libvirt virtual network's portgroups. Typically there is HostPortGroups named 'VM Network' that is used to connect virtual machines to a HostVirtualSwitch. A second HostPortGroup typically named 'Management Network' is used to connect the hypervisor itself to the HostVirtualSwitch. This one is not mapped to a libvirt virtual network's portgroup. There can be more HostPortGroups than those typical two on a HostVirtualSwitch. +---------------+-------------------+ ...---| | | +-------------+ | HostPortGroup | |---| PhysicalNic | | VM Network | | | vmnic0 | ...---| | | +-------------+ +---------------+ HostVirtualSwitch | | vSwitch0 | +---------------+ | | HostPortGroup | | ...---| Management | | | Network | | +---------------+-------------------+ The virtual counterparts of the PhysicalNic is the HostVirtualNic for the hypervisor and the VirtualEthernetCard for the virtual machines that are grouped into HostPortGroups. +---------------------+ +---------------+---... | VirtualEthernetCard |---| | +---------------------+ | HostPortGroup | +---------------------+ | VM Network | | VirtualEthernetCard |---| | +---------------------+ +---------------+ | +---------------+ +---------------------+ | HostPortGroup | | HostVirtualNic |---| Management | +---------------------+ | Network | +---------------+---... The currently implemented network driver can list, define and undefine HostVirtualSwitches including HostPortGroups for virtual machines. Existing HostVirtualSwitches cannot be edited yet. This will be added in a followup patch. --- po/POTFILES.in | 1 + src/conf/network_conf.c | 3 +- src/conf/network_conf.h | 3 + src/esx/esx_network_driver.c | 867 ++++++++++++++++++++++++++++++++- src/esx/esx_vi.c | 171 +++++++ src/esx/esx_vi.h | 14 + src/esx/esx_vi_generator.input | 151 ++++++ src/esx/esx_vi_generator.py | 5 + 8 files changed, 1212 insertions(+), 3 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index 37a00ee5a2..e617952929 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -27,6 +27,7 @@ src/cpu/cpu_x86.c src/datatypes.c src/driver.c src/esx/esx_driver.c +src/esx/esx_network_driver.c src/esx/esx_storage_driver.c src/esx/esx_util.c src/esx/esx_vi.c diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index eb92d930af..a3714d945e 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -47,8 +47,6 @@ #define MAX_BRIDGE_ID 256 #define VIR_FROM_THIS VIR_FROM_NETWORK -VIR_ENUM_DECL(virNetworkForward) - VIR_ENUM_IMPL(virNetworkForward, VIR_NETWORK_FORWARD_LAST, "none", "nat", "route", "bridge", "private", "vepa", "passthrough" ) @@ -967,6 +965,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) goto error; } VIR_FREE(tmp); + def->uuid_specified = true; } /* Parse network domain information */ diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 1c640a9506..a95b382313 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -148,6 +148,7 @@ typedef struct _virNetworkDef virNetworkDef; typedef virNetworkDef *virNetworkDefPtr; struct _virNetworkDef { unsigned char uuid[VIR_UUID_BUFLEN]; + bool uuid_specified; char *name; char *bridge; /* Name of bridge device */ @@ -289,4 +290,6 @@ int virNetworkObjIsDuplicate(virNetworkObjListPtr doms, void virNetworkObjLock(virNetworkObjPtr obj); void virNetworkObjUnlock(virNetworkObjPtr obj); +VIR_ENUM_DECL(virNetworkForward) + #endif /* __NETWORK_CONF_H__ */ diff --git a/src/esx/esx_network_driver.c b/src/esx/esx_network_driver.c index 2e0e40bb15..b42f1d8468 100644 --- a/src/esx/esx_network_driver.c +++ b/src/esx/esx_network_driver.c @@ -4,7 +4,7 @@ * host networks * * Copyright (C) 2010-2011 Red Hat, Inc. - * Copyright (C) 2010 Matthias Bolte + * Copyright (C) 2010-2012 Matthias Bolte * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,11 +24,13 @@ #include +#include "md5.h" #include "internal.h" #include "util.h" #include "memory.h" #include "logging.h" #include "uuid.h" +#include "network_conf.h" #include "esx_private.h" #include "esx_network_driver.h" #include "esx_vi.h" @@ -37,6 +39,12 @@ #define VIR_FROM_THIS VIR_FROM_ESX +/* + * The UUID of a network is the MD5 sum of it's key. Therefore, verify that + * UUID and MD5 sum match in size, because we rely on that. + */ +verify(MD5_DIGEST_SIZE == VIR_UUID_BUFLEN); + static virDrvOpenStatus @@ -67,10 +75,867 @@ esxNetworkClose(virConnectPtr conn) +static int +esxNumberOfNetworks(virConnectPtr conn) +{ + esxPrivate *priv = conn->networkPrivateData; + esxVI_HostVirtualSwitch *hostVirtualSwitchList = NULL; + esxVI_HostVirtualSwitch *hostVirtualSwitch = NULL; + int count = 0; + + if (esxVI_EnsureSession(priv->primary) < 0 || + esxVI_LookupHostVirtualSwitchList(priv->primary, + &hostVirtualSwitchList) < 0) { + return -1; + } + + for (hostVirtualSwitch = hostVirtualSwitchList; hostVirtualSwitch != NULL; + hostVirtualSwitch = hostVirtualSwitch->_next) { + ++count; + } + + esxVI_HostVirtualSwitch_Free(&hostVirtualSwitchList); + + return count; +} + + + +static int +esxListNetworks(virConnectPtr conn, char **const names, int maxnames) +{ + bool success = false; + esxPrivate *priv = conn->networkPrivateData; + esxVI_HostVirtualSwitch *hostVirtualSwitchList = NULL; + esxVI_HostVirtualSwitch *hostVirtualSwitch = NULL; + int count = 0; + int i; + + if (maxnames == 0) { + return 0; + } + + if (esxVI_EnsureSession(priv->primary) < 0 || + esxVI_LookupHostVirtualSwitchList(priv->primary, + &hostVirtualSwitchList) < 0) { + return -1; + } + + for (hostVirtualSwitch = hostVirtualSwitchList; hostVirtualSwitch != NULL; + hostVirtualSwitch = hostVirtualSwitch->_next) { + names[count] = strdup(hostVirtualSwitch->name); + + if (names[count] == NULL) { + virReportOOMError(); + goto cleanup; + } + + ++count; + } + + success = true; + + cleanup: + if (! success) { + for (i = 0; i < count; ++i) { + VIR_FREE(names[i]); + } + + count = -1; + } + + esxVI_HostVirtualSwitch_Free(&hostVirtualSwitchList); + + return count; +} + + + +static int +esxNumberOfDefinedNetworks(virConnectPtr conn ATTRIBUTE_UNUSED) +{ + /* ESX networks are always active */ + return 0; +} + + + +static int +esxListDefinedNetworks(virConnectPtr conn ATTRIBUTE_UNUSED, + char **const names ATTRIBUTE_UNUSED, + int maxnames ATTRIBUTE_UNUSED) +{ + /* ESX networks are always active */ + return 0; +} + + + +static virNetworkPtr +esxNetworkLookupByUUID(virConnectPtr conn, const unsigned char *uuid) +{ + virNetworkPtr network = NULL; + esxPrivate *priv = conn->networkPrivateData; + esxVI_HostVirtualSwitch *hostVirtualSwitchList = NULL; + esxVI_HostVirtualSwitch *hostVirtualSwitch = NULL; + unsigned char md5[MD5_DIGEST_SIZE]; /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + char uuid_string[VIR_UUID_STRING_BUFLEN] = ""; + + if (esxVI_EnsureSession(priv->primary) < 0 || + esxVI_LookupHostVirtualSwitchList(priv->primary, + &hostVirtualSwitchList) < 0) { + return NULL; + } + + for (hostVirtualSwitch = hostVirtualSwitchList; hostVirtualSwitch != NULL; + hostVirtualSwitch = hostVirtualSwitch->_next) { + md5_buffer(hostVirtualSwitch->key, strlen(hostVirtualSwitch->key), md5); + + if (memcmp(uuid, md5, VIR_UUID_BUFLEN) == 0) { + break; + } + } + + if (hostVirtualSwitch == NULL) { + virUUIDFormat(uuid, uuid_string); + + virReportError(VIR_ERR_NO_NETWORK, + _("Could not find HostVirtualSwitch with UUID '%s'"), + uuid_string); + + goto cleanup; + } + + network = virGetNetwork(conn, hostVirtualSwitch->name, uuid); + + cleanup: + esxVI_HostVirtualSwitch_Free(&hostVirtualSwitchList); + + return network; +} + + + +static virNetworkPtr +esxNetworkLookupByName(virConnectPtr conn, const char *name) +{ + virNetworkPtr network = NULL; + esxPrivate *priv = conn->networkPrivateData; + esxVI_HostVirtualSwitch *hostVirtualSwitch = NULL; + unsigned char md5[MD5_DIGEST_SIZE]; /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + + if (esxVI_EnsureSession(priv->primary) < 0 || + esxVI_LookupHostVirtualSwitchByName(priv->primary, name, + &hostVirtualSwitch, + esxVI_Occurrence_RequiredItem) < 0) { + return NULL; + } + + /* + * HostVirtualSwitch doesn't have a UUID, but we can use the key property + * as source for a UUID. The key is unique per host and cannot change + * during the lifetime of the HostVirtualSwitch. + * + * The MD5 sum of the key can be used as UUID, assuming MD5 is considered + * to be collision-free enough for this use case. + */ + md5_buffer(hostVirtualSwitch->key, strlen(hostVirtualSwitch->key), md5); + + network = virGetNetwork(conn, hostVirtualSwitch->name, md5); + + esxVI_HostVirtualSwitch_Free(&hostVirtualSwitch); + + return network; +} + + + +static int +esxBandwidthToShapingPolicy(virNetDevBandwidthPtr bandwidth, + esxVI_HostNetworkTrafficShapingPolicy **shapingPolicy) +{ + int result = -1; + + if (shapingPolicy == NULL || *shapingPolicy != NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + } + + if (bandwidth->in == NULL || bandwidth->out == NULL || + bandwidth->in->average != bandwidth->out->average || + bandwidth->in->peak != bandwidth->out->peak || + bandwidth->in->burst != bandwidth->out->burst) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Different inbound and outbound bandwidth is unsupported")); + return -1; + } + + if (bandwidth->in->average == 0 && bandwidth->in->peak == 0 && + bandwidth->in->burst == 0) { + return 0; + } + + if (esxVI_HostNetworkTrafficShapingPolicy_Alloc(shapingPolicy) < 0) { + goto cleanup; + } + + (*shapingPolicy)->enabled = esxVI_Boolean_True; + + if (bandwidth->in->average > 0) { + if (esxVI_Long_Alloc(&(*shapingPolicy)->averageBandwidth) < 0) { + goto cleanup; + } + + /* Scale kilobytes per second to bits per second */ + (*shapingPolicy)->averageBandwidth->value = bandwidth->in->average * 8 * 1000; + } + + if (bandwidth->in->peak > 0) { + if (esxVI_Long_Alloc(&(*shapingPolicy)->peakBandwidth) < 0) { + goto cleanup; + } + + /* Scale kilobytes per second to bits per second */ + (*shapingPolicy)->peakBandwidth->value = bandwidth->in->peak * 8 * 1000; + } + + if (bandwidth->in->burst > 0) { + if (esxVI_Long_Alloc(&(*shapingPolicy)->burstSize) < 0) { + goto cleanup; + } + + /* Scale kilobytes to bytes */ + (*shapingPolicy)->burstSize->value = bandwidth->in->burst * 1024; + } + + result = 0; + + cleanup: + if (result < 0) { + esxVI_HostNetworkTrafficShapingPolicy_Free(shapingPolicy); + } + + return result; +} + + + +static virNetworkPtr +esxNetworkDefineXML(virConnectPtr conn, const char *xml) +{ + virNetworkPtr network = NULL; + esxPrivate *priv = conn->networkPrivateData; + virNetworkDefPtr def = NULL; + esxVI_HostVirtualSwitch *hostVirtualSwitch = NULL; + esxVI_HostPortGroup *hostPortGroupList = NULL; + esxVI_HostPortGroup *hostPortGroup = NULL; + esxVI_HostVirtualSwitchSpec *hostVirtualSwitchSpec = NULL; + esxVI_HostVirtualSwitchBondBridge *hostVirtualSwitchBondBridge = NULL; + esxVI_PhysicalNic *physicalNicList = NULL; + esxVI_PhysicalNic *physicalNic = NULL; + esxVI_HostPortGroupSpec *hostPortGroupSpec = NULL; + int i; + + unsigned char md5[MD5_DIGEST_SIZE]; /* MD5_DIGEST_SIZE = VIR_UUID_BUFLEN = 16 */ + + if (esxVI_EnsureSession(priv->primary) < 0) { + return NULL; + } + + /* Parse network XML */ + def = virNetworkDefParseString(xml); + + if (def == NULL) { + return NULL; + } + + /* Check if an existing HostVirtualSwitch should be edited */ + if (esxVI_LookupHostVirtualSwitchByName(priv->primary, def->name, + &hostVirtualSwitch, + esxVI_Occurrence_OptionalItem) < 0) { + goto cleanup; + } + + if (hostVirtualSwitch != NULL) { + /* FIXME */ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("HostVirtualSwitch already exists, editing existing " + "ones is not supported yet")); + goto cleanup; + } + + /* UUID is derived from the HostVirtualSwitch's key and cannot be specified */ + if (def->uuid_specified) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Cannot use predefined UUID")); + goto cleanup; + } + + /* FIXME: Add support for NAT */ + if (def->forwardType != VIR_NETWORK_FORWARD_NONE && + def->forwardType != VIR_NETWORK_FORWARD_BRIDGE) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported forward mode '%s'"), + virNetworkForwardTypeToString(def->forwardType)); + goto cleanup; + } + + /* Verify that specified HostPortGroups don't exist already */ + if (def->nPortGroups > 0) { + if (esxVI_LookupHostPortGroupList(priv->primary, &hostPortGroupList) < 0) { + goto cleanup; + } + + for (i = 0; i < def->nPortGroups; ++i) { + for (hostPortGroup = hostPortGroupList; hostPortGroup != NULL; + hostPortGroup = hostPortGroup->_next) { + if (STREQ(def->portGroups[i].name, hostPortGroup->spec->name)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("HostPortGroup with name '%s' exists already"), + def->portGroups[i].name); + goto cleanup; + } + } + } + } + + /* Create HostVirtualSwitch */ + if (esxVI_HostVirtualSwitchSpec_Alloc(&hostVirtualSwitchSpec) < 0 || + esxVI_Int_Alloc(&hostVirtualSwitchSpec->numPorts) < 0) { + goto cleanup; + } + + if (def->forwardType != VIR_NETWORK_FORWARD_NONE && def->nForwardIfs > 0) { + if (esxVI_HostVirtualSwitchBondBridge_Alloc + (&hostVirtualSwitchBondBridge) < 0) { + goto cleanup; + } + + hostVirtualSwitchSpec->bridge = + (esxVI_HostVirtualSwitchBridge *)hostVirtualSwitchBondBridge; + + /* Lookup PhysicalNic list and match by name to get key */ + if (esxVI_LookupPhysicalNicList(priv->primary, &physicalNicList) < 0) { + goto cleanup; + } + + for (i = 0; i < def->nForwardIfs; ++i) { + bool found = false; + + for (physicalNic = physicalNicList; physicalNic != NULL; + physicalNic = physicalNic->_next) { + if (STREQ(def->forwardIfs[i].dev, physicalNic->device)) { + if (esxVI_String_AppendValueToList + (&hostVirtualSwitchBondBridge->nicDevice, + physicalNic->key) < 0) { + goto cleanup; + } + + found = true; + break; + } + } + + if (! found) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find PhysicalNic with name '%s'"), + def->forwardIfs[i].dev); + goto cleanup; + } + } + } + + hostVirtualSwitchSpec->numPorts->value = 128; + + if (def->bandwidth != NULL) { + if (esxVI_HostNetworkPolicy_Alloc(&hostVirtualSwitchSpec->policy) < 0) { + goto cleanup; + } + + if (esxBandwidthToShapingPolicy + (def->bandwidth, + &hostVirtualSwitchSpec->policy->shapingPolicy) < 0) { + goto cleanup; + } + } + + if (esxVI_AddVirtualSwitch + (priv->primary, + priv->primary->hostSystem->configManager->networkSystem, + def->name, hostVirtualSwitchSpec) < 0) { + goto cleanup; + } + + /* Create HostPortGroup(s) */ + for (i = 0; i < def->nPortGroups; ++i) { + esxVI_HostPortGroupSpec_Free(&hostPortGroupSpec); + + if (esxVI_HostPortGroupSpec_Alloc(&hostPortGroupSpec) < 0 || + esxVI_HostNetworkPolicy_Alloc(&hostPortGroupSpec->policy) < 0 || + esxVI_Int_Alloc(&hostPortGroupSpec->vlanId) < 0 || + esxVI_String_DeepCopyValue(&hostPortGroupSpec->name, + def->portGroups[i].name) < 0 || + esxVI_String_DeepCopyValue(&hostPortGroupSpec->vswitchName, + def->name) < 0) { + goto cleanup; + } + + hostPortGroupSpec->vlanId->value = 0; + + if (def->portGroups[i].bandwidth != NULL) { + if (esxBandwidthToShapingPolicy + (def->portGroups[i].bandwidth, + &hostPortGroupSpec->policy->shapingPolicy) < 0) { + goto cleanup; + } + } + + if (esxVI_AddPortGroup + (priv->primary, + priv->primary->hostSystem->configManager->networkSystem, + hostPortGroupSpec) < 0) { + goto cleanup; + } + } + + /* Lookup created HostVirtualSwitch to get the UUID */ + if (esxVI_LookupHostVirtualSwitchByName(priv->primary, def->name, + &hostVirtualSwitch, + esxVI_Occurrence_RequiredItem) < 0) { + goto cleanup; + } + + md5_buffer(hostVirtualSwitch->key, strlen(hostVirtualSwitch->key), md5); + + network = virGetNetwork(conn, hostVirtualSwitch->name, md5); + + cleanup: + virNetworkDefFree(def); + esxVI_HostVirtualSwitch_Free(&hostVirtualSwitch); + esxVI_HostPortGroup_Free(&hostPortGroupList); + esxVI_HostVirtualSwitchSpec_Free(&hostVirtualSwitchSpec); + esxVI_PhysicalNic_Free(&physicalNicList); + esxVI_HostPortGroupSpec_Free(&hostPortGroupSpec); + + return network; +} + + + +static int +esxNetworkUndefine(virNetworkPtr network) +{ + int result = -1; + esxPrivate *priv = network->conn->networkPrivateData; + esxVI_HostVirtualSwitch *hostVirtualSwitch = NULL; + esxVI_HostPortGroup *hostPortGroupList = NULL; + esxVI_String *hostPortGroupKey = NULL; + esxVI_HostPortGroup *hostPortGroup = NULL; + esxVI_HostPortGroupPort *hostPortGroupPort = NULL; + + if (esxVI_EnsureSession(priv->primary) < 0) { + return -1; + } + + /* Lookup HostVirtualSwitch and HostPortGroup list*/ + if (esxVI_LookupHostVirtualSwitchByName(priv->primary, network->name, + &hostVirtualSwitch, + esxVI_Occurrence_RequiredItem) < 0 || + esxVI_LookupHostPortGroupList(priv->primary, &hostPortGroupList) < 0) { + goto cleanup; + } + + /* Verify that the HostVirtualSwitch is connected to virtual machines only */ + for (hostPortGroupKey = hostVirtualSwitch->portgroup; + hostPortGroupKey != NULL; hostPortGroupKey = hostPortGroupKey->_next) { + bool found = false; + + for (hostPortGroup = hostPortGroupList; hostPortGroup != NULL; + hostPortGroup = hostPortGroup->_next) { + if (STREQ(hostPortGroupKey->value, hostPortGroup->key)) { + for (hostPortGroupPort = hostPortGroup->port; + hostPortGroupPort != NULL; + hostPortGroupPort = hostPortGroupPort->_next) { + if (STRNEQ(hostPortGroupPort->type, "virtualMachine")) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("Cannot undefine HostVirtualSwitch that has a '%s' port"), + hostPortGroupPort->type); + goto cleanup; + } + } + + found = true; + break; + } + } + + if (! found) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find HostPortGroup for key '%s'"), + hostPortGroupKey->value); + goto cleanup; + } + } + + /* Remove all HostPortGroups from the HostVirtualSwitch */ + for (hostPortGroupKey = hostVirtualSwitch->portgroup; + hostPortGroupKey != NULL; hostPortGroupKey = hostPortGroupKey->_next) { + bool found = false; + + for (hostPortGroup = hostPortGroupList; hostPortGroup != NULL; + hostPortGroup = hostPortGroup->_next) { + if (STREQ(hostPortGroupKey->value, hostPortGroup->key)) { + if (esxVI_RemovePortGroup + (priv->primary, + priv->primary->hostSystem->configManager->networkSystem, + hostPortGroup->spec->name) < 0) { + goto cleanup; + } + + found = true; + break; + } + } + + if (! found) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find HostPortGroup for key '%s'"), + hostPortGroupKey->value); + goto cleanup; + } + } + + /* Finally, remove HostVirtualSwitch itself */ + if (esxVI_RemoveVirtualSwitch + (priv->primary, + priv->primary->hostSystem->configManager->networkSystem, + network->name) < 0) { + goto cleanup; + } + + result = 0; + + cleanup: + esxVI_HostVirtualSwitch_Free(&hostVirtualSwitch); + esxVI_HostPortGroup_Free(&hostPortGroupList); + + return result; +} + + + +static int +esxShapingPolicyToBandwidth(esxVI_HostNetworkTrafficShapingPolicy *shapingPolicy, + virNetDevBandwidthPtr *bandwidth) +{ + if (bandwidth == NULL || *bandwidth != NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + } + + if (shapingPolicy == NULL || shapingPolicy->enabled != esxVI_Boolean_True) { + return 0; + } + + if (VIR_ALLOC(*bandwidth) < 0 || + VIR_ALLOC((*bandwidth)->in) < 0 || + VIR_ALLOC((*bandwidth)->out) < 0) { + virReportOOMError(); + return -1; + } + + if (shapingPolicy->averageBandwidth != NULL) { + /* Scale bits per second to kilobytes per second */ + (*bandwidth)->in->average = shapingPolicy->averageBandwidth->value / 8 / 1000; + (*bandwidth)->out->average = shapingPolicy->averageBandwidth->value / 8 / 1000; + } + + if (shapingPolicy->peakBandwidth != NULL) { + /* Scale bits per second to kilobytes per second */ + (*bandwidth)->in->peak = shapingPolicy->peakBandwidth->value / 8 / 1000; + (*bandwidth)->out->peak = shapingPolicy->peakBandwidth->value / 8 / 1000; + } + + if (shapingPolicy->burstSize != NULL) { + /* Scale bytes to kilobytes */ + (*bandwidth)->in->burst = shapingPolicy->burstSize->value / 1024; + (*bandwidth)->out->burst = shapingPolicy->burstSize->value / 1024; + } + + return 0; +} + + + +static char * +esxNetworkGetXMLDesc(virNetworkPtr network_, unsigned int flags) +{ + char *xml = NULL; + esxPrivate *priv = network_->conn->networkPrivateData; + esxVI_HostVirtualSwitch *hostVirtualSwitch = NULL; + int count = 0; + esxVI_PhysicalNic *physicalNicList = NULL; + esxVI_PhysicalNic *physicalNic = NULL; + esxVI_String *physicalNicKey = NULL; + esxVI_HostPortGroup *hostPortGroupList = NULL; + esxVI_HostPortGroup *hostPortGroup = NULL; + esxVI_String *propertyNameList = NULL; + esxVI_ObjectContent *networkList = NULL; + esxVI_ObjectContent *network = NULL; + esxVI_String *networkNameList = NULL; + esxVI_String *hostPortGroupKey = NULL; + esxVI_String *networkName = NULL; + virNetworkDefPtr def; + + if (esxVI_EnsureSession(priv->primary) < 0) { + return NULL; + } + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + goto cleanup; + } + + /* Lookup HostVirtualSwitch */ + if (esxVI_LookupHostVirtualSwitchByName(priv->primary, network_->name, + &hostVirtualSwitch, + esxVI_Occurrence_RequiredItem) < 0) { + goto cleanup; + } + + md5_buffer(hostVirtualSwitch->key, strlen(hostVirtualSwitch->key), def->uuid); + + def->name = strdup(hostVirtualSwitch->name); + + if (def->name == NULL) { + virReportOOMError(); + goto cleanup; + } + + def->forwardType = VIR_NETWORK_FORWARD_NONE; + + /* Count PhysicalNics on HostVirtualSwitch */ + count = 0; + + for (physicalNicKey = hostVirtualSwitch->pnic; + physicalNicKey != NULL; physicalNicKey = physicalNicKey->_next) { + ++count; + } + + if (count > 0) { + def->forwardType = VIR_NETWORK_FORWARD_BRIDGE; + + if (VIR_ALLOC_N(def->forwardIfs, count) < 0) { + virReportOOMError(); + goto cleanup; + } + + /* Find PhysicalNic by key */ + if (esxVI_LookupPhysicalNicList(priv->primary, &physicalNicList) < 0) { + goto cleanup; + } + + for (physicalNicKey = hostVirtualSwitch->pnic; + physicalNicKey != NULL; physicalNicKey = physicalNicKey->_next) { + bool found = false; + + for (physicalNic = physicalNicList; physicalNic != NULL; + physicalNic = physicalNic->_next) { + if (STREQ(physicalNicKey->value, physicalNic->key)) { + def->forwardIfs[def->nForwardIfs].dev = strdup(physicalNic->device); + + if (def->forwardIfs[def->nForwardIfs].dev == NULL) { + virReportOOMError(); + goto cleanup; + } + + ++def->nForwardIfs; + + found = true; + break; + } + } + + if (! found) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find PhysicalNic with key '%s'"), + physicalNicKey->value); + goto cleanup; + } + } + } + + /* Count HostPortGroups on HostVirtualSwitch */ + count = 0; + + for (hostPortGroupKey = hostVirtualSwitch->portgroup; + hostPortGroupKey != NULL; hostPortGroupKey = hostPortGroupKey->_next) { + ++count; + } + + if (count > 0) { + if (VIR_ALLOC_N(def->portGroups, count) < 0) { + virReportOOMError(); + goto cleanup; + } + + /* Lookup Network list and create name list */ + if (esxVI_String_AppendValueToList(&propertyNameList, "name") < 0 || + esxVI_LookupNetworkList(priv->primary, propertyNameList, + &networkList) < 0) { + goto cleanup; + } + + for (network = networkList; network != NULL; network = network->_next) { + char *tmp = NULL; + + if (esxVI_GetStringValue(network, "name", &tmp, + esxVI_Occurrence_RequiredItem) < 0 || + esxVI_String_AppendValueToList(&networkNameList, tmp) < 0) { + goto cleanup; + } + } + + /* Find HostPortGroup by key */ + if (esxVI_LookupHostPortGroupList(priv->primary, &hostPortGroupList) < 0) { + goto cleanup; + } + + for (hostPortGroupKey = hostVirtualSwitch->portgroup; + hostPortGroupKey != NULL; hostPortGroupKey = hostPortGroupKey->_next) { + bool found = false; + + for (hostPortGroup = hostPortGroupList; hostPortGroup != NULL; + hostPortGroup = hostPortGroup->_next) { + if (STREQ(hostPortGroupKey->value, hostPortGroup->key)) { + /* Find Network for HostPortGroup, there might be none */ + for (networkName = networkNameList; networkName != NULL; + networkName = networkName->_next) { + if (STREQ(networkName->value, hostPortGroup->spec->name)) { + def->portGroups[def->nPortGroups].name = strdup(networkName->value); + + if (def->portGroups[def->nPortGroups].name == NULL) { + virReportOOMError(); + goto cleanup; + } + + if (hostPortGroup->spec->policy != NULL) { + if (esxShapingPolicyToBandwidth + (hostPortGroup->spec->policy->shapingPolicy, + &def->portGroups[def->nPortGroups].bandwidth) < 0) { + ++def->nPortGroups; + goto cleanup; + } + } + + ++def->nPortGroups; + break; + } + } + + found = true; + break; + } + } + + if (! found) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not find HostPortGroup with key '%s'"), + hostPortGroupKey->value); + goto cleanup; + } + } + } + + if (hostVirtualSwitch->spec->policy != NULL) { + if (esxShapingPolicyToBandwidth + (hostVirtualSwitch->spec->policy->shapingPolicy, + &def->bandwidth) < 0) { + goto cleanup; + } + } + + xml = virNetworkDefFormat(def, flags); + + cleanup: + esxVI_HostVirtualSwitch_Free(&hostVirtualSwitch); + esxVI_PhysicalNic_Free(&physicalNicList); + esxVI_HostPortGroup_Free(&hostPortGroupList); + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&networkList); + esxVI_String_Free(&networkNameList); + virNetworkDefFree(def); + + return xml; +} + + + +static int +esxNetworkGetAutostart(virNetworkPtr network ATTRIBUTE_UNUSED, + int *autostart) +{ + /* ESX networks are always active */ + *autostart = 1; + + return 0; +} + + + +static int +esxNetworkSetAutostart(virNetworkPtr network ATTRIBUTE_UNUSED, + int autostart) +{ + /* Just accept autostart activation, but fail on autostart deactivation */ + autostart = (autostart != 0); + + if (! autostart) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot deactivate network autostart")); + return -1; + } + + return 0; +} + + + +static int +esxNetworkIsActive(virNetworkPtr network ATTRIBUTE_UNUSED) +{ + /* ESX networks are always active */ + return 1; +} + + + +static int +esxNetworkIsPersistent(virNetworkPtr network ATTRIBUTE_UNUSED) +{ + /* ESX has no concept of transient networks, so all of them are persistent */ + return 1; +} + + + static virNetworkDriver esxNetworkDriver = { .name = "ESX", .open = esxNetworkOpen, /* 0.7.6 */ .close = esxNetworkClose, /* 0.7.6 */ + .numOfNetworks = esxNumberOfNetworks, /* 0.10.0 */ + .listNetworks = esxListNetworks, /* 0.10.0 */ + .numOfDefinedNetworks = esxNumberOfDefinedNetworks, /* 0.10.0 */ + .listDefinedNetworks = esxListDefinedNetworks, /* 0.10.0 */ + .networkLookupByUUID = esxNetworkLookupByUUID, /* 0.10.0 */ + .networkLookupByName = esxNetworkLookupByName, /* 0.10.0 */ + .networkDefineXML = esxNetworkDefineXML, /* 0.10.0 */ + .networkUndefine = esxNetworkUndefine, /* 0.10.0 */ + .networkGetXMLDesc = esxNetworkGetXMLDesc, /* 0.10.0 */ + .networkGetAutostart = esxNetworkGetAutostart, /* 0.10.0 */ + .networkSetAutostart = esxNetworkSetAutostart, /* 0.10.0 */ + .networkIsActive = esxNetworkIsActive, /* 0.10.0 */ + .networkIsPersistent = esxNetworkIsPersistent, /* 0.10.0 */ }; diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c index f3a9e913f0..65e1d9afa7 100644 --- a/src/esx/esx_vi.c +++ b/src/esx/esx_vi.c @@ -783,6 +783,7 @@ ESX_VI__TEMPLATE__FREE(Context, esxVI_SelectionSpec_Free(&item->selectSet_hostSystemToDatastore); esxVI_SelectionSpec_Free(&item->selectSet_computeResourceToHost); esxVI_SelectionSpec_Free(&item->selectSet_computeResourceToParentToParent); + esxVI_SelectionSpec_Free(&item->selectSet_datacenterToNetwork); }) int @@ -1927,6 +1928,13 @@ esxVI_BuildSelectSetCollection(esxVI_Context *ctx) return -1; } + /* Datacenter -> network (Network) */ + if (esxVI_BuildSelectSet(&ctx->selectSet_datacenterToNetwork, + "datacenterToNetwork", + "Datacenter", "network", NULL) < 0) { + return -1; + } + return 0; } @@ -2094,6 +2102,15 @@ esxVI_LookupObjectContentByType(esxVI_Context *ctx, type, root->type); goto cleanup; } + } else if (STREQ(root->type, "Datacenter")) { + if (STREQ(type, "Network")) { + objectSpec->selectSet = ctx->selectSet_datacenterToNetwork; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Invalid lookup of '%s' from '%s'"), + type, root->type); + goto cleanup; + } } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid lookup from '%s'"), root->type); @@ -4090,6 +4107,160 @@ esxVI_LookupPhysicalNicByMACAddress(esxVI_Context *ctx, const char *mac, +int +esxVI_LookupHostVirtualSwitchList(esxVI_Context *ctx, + esxVI_HostVirtualSwitch **hostVirtualSwitchList) +{ + int result = -1; + esxVI_String *propertyNameList = NULL; + esxVI_ObjectContent *hostSystem = NULL; + esxVI_DynamicProperty *dynamicProperty = NULL; + + if (hostVirtualSwitchList == NULL || *hostVirtualSwitchList != NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + } + + if (esxVI_String_AppendValueToList(&propertyNameList, + "config.network.vswitch") < 0 || + esxVI_LookupHostSystemProperties(ctx, propertyNameList, + &hostSystem) < 0) { + goto cleanup; + } + + for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, "config.network.vswitch")) { + if (esxVI_HostVirtualSwitch_CastListFromAnyType + (dynamicProperty->val, hostVirtualSwitchList) < 0) { + goto cleanup; + } + } else { + VIR_WARN("Unexpected '%s' property", dynamicProperty->name); + } + } + + result = 0; + + cleanup: + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&hostSystem); + + return result; +} + + + +int +esxVI_LookupHostVirtualSwitchByName(esxVI_Context *ctx, const char *name, + esxVI_HostVirtualSwitch **hostVirtualSwitch, + esxVI_Occurrence occurrence) +{ + int result = -1; + esxVI_HostVirtualSwitch *hostVirtualSwitchList = NULL; + esxVI_HostVirtualSwitch *candidate = NULL; + + if (hostVirtualSwitch == NULL || *hostVirtualSwitch != NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + } + + if (esxVI_LookupHostVirtualSwitchList(ctx, &hostVirtualSwitchList) < 0) { + goto cleanup; + } + + /* Search for a matching HostVirtualSwitch */ + for (candidate = hostVirtualSwitchList; candidate != NULL; + candidate = candidate->_next) { + if (STREQ(candidate->name, name)) { + if (esxVI_HostVirtualSwitch_DeepCopy(hostVirtualSwitch, + candidate) < 0) { + goto cleanup; + } + + /* Found HostVirtualSwitch with matching name */ + result = 0; + + goto cleanup; + } + } + + if (*hostVirtualSwitch == NULL && + occurrence != esxVI_Occurrence_OptionalItem) { + virReportError(VIR_ERR_NO_NETWORK, + _("Could not find HostVirtualSwitch with name '%s'"), + name); + goto cleanup; + } + + result = 0; + + cleanup: + esxVI_HostVirtualSwitch_Free(&hostVirtualSwitchList); + + return result; +} + + + +int +esxVI_LookupHostPortGroupList(esxVI_Context *ctx, + esxVI_HostPortGroup **hostPortGroupList) +{ + int result = -1; + esxVI_String *propertyNameList = NULL; + esxVI_ObjectContent *hostSystem = NULL; + esxVI_DynamicProperty *dynamicProperty = NULL; + + if (hostPortGroupList == NULL || *hostPortGroupList != NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + } + + if (esxVI_String_AppendValueToList(&propertyNameList, + "config.network.portgroup") < 0 || + esxVI_LookupHostSystemProperties(ctx, propertyNameList, + &hostSystem) < 0) { + goto cleanup; + } + + for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL; + dynamicProperty = dynamicProperty->_next) { + if (STREQ(dynamicProperty->name, "config.network.portgroup")) { + if (esxVI_HostPortGroup_CastListFromAnyType + (dynamicProperty->val, hostPortGroupList) < 0) { + goto cleanup; + } + + break; + } else { + VIR_WARN("Unexpected '%s' property", dynamicProperty->name); + } + } + + result = 0; + + cleanup: + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&hostSystem); + + return result; +} + + + +int +esxVI_LookupNetworkList(esxVI_Context *ctx, esxVI_String *propertyNameList, + esxVI_ObjectContent **networkList) +{ + return esxVI_LookupObjectContentByType(ctx, ctx->datacenter->_reference, + "Network", propertyNameList, + networkList, + esxVI_Occurrence_OptionalList); +} + + + int esxVI_HandleVirtualMachineQuestion (esxVI_Context *ctx, esxVI_ManagedObjectReference *virtualMachine, diff --git a/src/esx/esx_vi.h b/src/esx/esx_vi.h index 597013c535..12394e7271 100644 --- a/src/esx/esx_vi.h +++ b/src/esx/esx_vi.h @@ -227,6 +227,7 @@ struct _esxVI_Context { esxVI_SelectionSpec *selectSet_hostSystemToDatastore; esxVI_SelectionSpec *selectSet_computeResourceToHost; esxVI_SelectionSpec *selectSet_computeResourceToParentToParent; + esxVI_SelectionSpec *selectSet_datacenterToNetwork; bool hasQueryVirtualDiskUuid; bool hasSessionIsActive; }; @@ -492,6 +493,19 @@ int esxVI_LookupPhysicalNicByMACAddress(esxVI_Context *ctx, const char *mac, esxVI_PhysicalNic **physicalNic, esxVI_Occurrence occurrence); +int esxVI_LookupHostVirtualSwitchList + (esxVI_Context *ctx, esxVI_HostVirtualSwitch **hostVirtualSwitchList); + +int esxVI_LookupHostVirtualSwitchByName(esxVI_Context *ctx, const char *name, + esxVI_HostVirtualSwitch **hostVirtualSwitch, + esxVI_Occurrence occurrence); + +int esxVI_LookupHostPortGroupList(esxVI_Context *ctx, + esxVI_HostPortGroup **hostPortGroupList); + +int esxVI_LookupNetworkList(esxVI_Context *ctx, esxVI_String *propertyNameList, + esxVI_ObjectContent **networkList); + int esxVI_HandleVirtualMachineQuestion (esxVI_Context *ctx, esxVI_ManagedObjectReference *virtualMachine, esxVI_VirtualMachineQuestionInfo *questionInfo, bool autoAnswer, diff --git a/src/esx/esx_vi_generator.input b/src/esx/esx_vi_generator.input index 5572b36077..c4a3e56998 100644 --- a/src/esx/esx_vi_generator.input +++ b/src/esx/esx_vi_generator.input @@ -338,12 +338,138 @@ object HostNasVolume extends HostFileSystemVolume end +object HostNetOffloadCapabilities + Boolean csumOffload o + Boolean tcpSegmentation o + Boolean zeroCopyXmit o +end + + +object HostNetworkPolicy + HostNetworkSecurityPolicy security o + HostNicTeamingPolicy nicTeaming o + HostNetOffloadCapabilities offloadPolicy o + HostNetworkTrafficShapingPolicy shapingPolicy o +end + + +object HostNetworkSecurityPolicy + Boolean allowPromiscuous o + Boolean macChanges o + Boolean forgedTransmits o +end + + +object HostNetworkTrafficShapingPolicy + Boolean enabled o + Long averageBandwidth o + Long peakBandwidth o + Long burstSize o +end + + +object HostNicFailureCriteria + String checkSpeed o + Int speed o + Boolean checkDuplex o + Boolean fullDuplex o + Boolean checkErrorPercent o + Int percentage o + Boolean checkBeacon o +end + + +object HostNicOrderPolicy + String activeNic ol + String standbyNic ol +end + + +object HostNicTeamingPolicy + String policy o + Boolean reversePolicy o + Boolean notifySwitches o + Boolean rollingOrder o + HostNicFailureCriteria failureCriteria o + HostNicOrderPolicy nicOrder o +end + + +object HostPortGroup + String key o + HostPortGroupPort port ol + String vswitch o + HostNetworkPolicy computedPolicy r + HostPortGroupSpec spec r +end + + +object HostPortGroupPort + String key o + String mac ol + String type r +end + + +object HostPortGroupSpec + String name r + Int vlanId r + String vswitchName r + HostNetworkPolicy policy r +end + + object HostScsiDiskPartition String diskName r Int partition r end +object HostVirtualSwitch + String name r + String key r + Int numPorts r + Int numPortsAvailable r + Int mtu o + String portgroup ol + String pnic ol + HostVirtualSwitchSpec spec r +end + + +object HostVirtualSwitchAutoBridge extends HostVirtualSwitchBridge + String excludedNicDevice ol +end + + +object HostVirtualSwitchBeaconConfig + Int interval r +end + + +object HostVirtualSwitchBondBridge extends HostVirtualSwitchBridge + String nicDevice rl + HostVirtualSwitchBeaconConfig beacon o +end + + +object HostVirtualSwitchBridge +end + + +object HostVirtualSwitchSimpleBridge extends HostVirtualSwitchBridge + String nicDevice r +end + + +object HostVirtualSwitchSpec + Int numPorts r + HostVirtualSwitchBridge bridge o + HostNetworkPolicy policy o + Int mtu o +end + + object HostVmfsVolume extends HostFileSystemVolume Int blockSizeMb r Int maxBlocks r @@ -805,6 +931,19 @@ end # Methods # +method AddPortGroup + ManagedObjectReference _this r + HostPortGroupSpec portgrp r +end + + +method AddVirtualSwitch + ManagedObjectReference _this r + String vswitchName r + HostVirtualSwitchSpec spec o +end + + method AnswerVM ManagedObjectReference _this r String questionId r @@ -981,12 +1120,24 @@ method RegisterVM_Task returns ManagedObjectReference r end +method RemovePortGroup + ManagedObjectReference _this r + String pgName r +end + + method RemoveSnapshot_Task returns ManagedObjectReference r ManagedObjectReference _this r Boolean removeChildren r end +method RemoveVirtualSwitch + ManagedObjectReference _this r + String vswitchName r +end + + method RetrieveProperties returns ObjectContent ol ManagedObjectReference _this:propertyCollector r PropertyFilterSpec specSet rl diff --git a/src/esx/esx_vi_generator.py b/src/esx/esx_vi_generator.py index 596bd1681e..b49db70f1e 100755 --- a/src/esx/esx_vi_generator.py +++ b/src/esx/esx_vi_generator.py @@ -1520,6 +1520,11 @@ additional_object_features = { "AutoStartDefaults" : Object.FEATURE__AN Object.FEATURE__ANY_TYPE, "HostDatastoreBrowserSearchResults" : Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE, + "HostPortGroup" : Object.FEATURE__LIST | + Object.FEATURE__ANY_TYPE, + "HostVirtualSwitch" : Object.FEATURE__DEEP_COPY | + Object.FEATURE__LIST | + Object.FEATURE__ANY_TYPE, "ManagedObjectReference" : Object.FEATURE__ANY_TYPE, "ObjectContent" : Object.FEATURE__DEEP_COPY, "PhysicalNic" : Object.FEATURE__DEEP_COPY |