[libvirt] [PATCH] esx: Implement network driver
by Matthias Bolte
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 37a00ee..e617952 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 eb92d93..a3714d9 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 1c640a9..a95b382 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 2e0e40b..b42f1d8 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 <matthias.bolte(a)googlemail.com>
+ * Copyright (C) 2010-2012 Matthias Bolte <matthias.bolte(a)googlemail.com>
*
* 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 <config.h>
+#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 f3a9e91..65e1d9a 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);
@@ -4091,6 +4108,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,
esxVI_VirtualMachineQuestionInfo *questionInfo, bool autoAnswer,
diff --git a/src/esx/esx_vi.h b/src/esx/esx_vi.h
index 597013c..12394e7 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 5572b36..c4a3e56 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 596bd16..b49db70 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 |
--
1.7.4.1
12 years, 3 months
[libvirt] [PATCH] esx: Implement interface driver
by Matthias Bolte
Lists available PhysicalNic devices. A PhysicalNic is always active
and can neither be defined nor undefined.
A PhysicalNic is used to bridge a HostVirtualSwitch to the physical
network.
---
This implementation differs from Ata's. It just lists PhysicalNics
instead of listing HostVirtualNics as bridges.
src/esx/esx_interface_driver.c | 239 +++++++++++++++++++++++++++++++++++++++-
src/esx/esx_vi.c | 144 ++++++++++++++++++++++++-
src/esx/esx_vi.h | 13 ++-
src/esx/esx_vi_generator.input | 34 ++++++-
src/esx/esx_vi_generator.py | 5 +-
5 files changed, 430 insertions(+), 5 deletions(-)
diff --git a/src/esx/esx_interface_driver.c b/src/esx/esx_interface_driver.c
index 501409a..a78a744 100644
--- a/src/esx/esx_interface_driver.c
+++ b/src/esx/esx_interface_driver.c
@@ -4,7 +4,7 @@
* host interfaces
*
* Copyright (C) 2010-2011 Red Hat, Inc.
- * Copyright (C) 2010 Matthias Bolte <matthias.bolte(a)googlemail.com>
+ * Copyright (C) 2010-2012 Matthias Bolte <matthias.bolte(a)googlemail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -29,6 +29,8 @@
#include "memory.h"
#include "logging.h"
#include "uuid.h"
+#include "interface_conf.h"
+#include "virsocketaddr.h"
#include "esx_private.h"
#include "esx_interface_driver.h"
#include "esx_vi.h"
@@ -67,10 +69,245 @@ esxInterfaceClose(virConnectPtr conn)
+static int
+esxNumberOfInterfaces(virConnectPtr conn)
+{
+ esxPrivate *priv = conn->interfacePrivateData;
+ esxVI_PhysicalNic *physicalNicList = NULL;
+ esxVI_PhysicalNic *physicalNic = NULL;
+ int count = 0;
+
+ if (esxVI_EnsureSession(priv->primary) < 0 ||
+ esxVI_LookupPhysicalNicList(priv->primary, &physicalNicList) < 0) {
+ return -1;
+ }
+
+ for (physicalNic = physicalNicList; physicalNic != NULL;
+ physicalNic = physicalNic->_next) {
+ ++count;
+ }
+
+ esxVI_PhysicalNic_Free(&physicalNicList);
+
+ return count;
+}
+
+
+
+static int
+esxListInterfaces(virConnectPtr conn, char **const names, int maxnames)
+{
+ bool success = false;
+ esxPrivate *priv = conn->interfacePrivateData;
+ esxVI_PhysicalNic *physicalNicList = NULL;
+ esxVI_PhysicalNic *physicalNic = NULL;
+ int count = 0;
+ int i;
+
+ if (maxnames == 0) {
+ return 0;
+ }
+
+ if (esxVI_EnsureSession(priv->primary) < 0 ||
+ esxVI_LookupPhysicalNicList(priv->primary, &physicalNicList) < 0) {
+ return -1;
+ }
+
+ for (physicalNic = physicalNicList; physicalNic != NULL;
+ physicalNic = physicalNic->_next) {
+ names[count] = strdup(physicalNic->device);
+
+ 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_PhysicalNic_Free(&physicalNicList);
+
+ return count;
+}
+
+
+
+static int
+esxNumberOfDefinedInterfaces(virConnectPtr conn ATTRIBUTE_UNUSED)
+{
+ /* ESX interfaces are always active */
+ return 0;
+}
+
+
+
+static int
+esxListDefinedInterfaces(virConnectPtr conn ATTRIBUTE_UNUSED,
+ char **const names ATTRIBUTE_UNUSED,
+ int maxnames ATTRIBUTE_UNUSED)
+{
+ /* ESX interfaces are always active */
+ return 0;
+}
+
+
+
+static virInterfacePtr
+esxInterfaceLookupByName(virConnectPtr conn, const char *name)
+{
+ virInterfacePtr iface = NULL;
+ esxPrivate *priv = conn->interfacePrivateData;
+ esxVI_PhysicalNic *physicalNic = NULL;
+
+ if (esxVI_EnsureSession(priv->primary) < 0 ||
+ esxVI_LookupPhysicalNicByMACAddress(priv->primary, name, &physicalNic,
+ esxVI_Occurrence_RequiredItem) < 0) {
+ return NULL;
+ }
+
+ iface = virGetInterface(conn, physicalNic->device, physicalNic->mac);
+
+ esxVI_PhysicalNic_Free(&physicalNic);
+
+ return iface;
+}
+
+
+
+static virInterfacePtr
+esxInterfaceLookupByMACString(virConnectPtr conn, const char *mac)
+{
+ virInterfacePtr iface = NULL;
+ esxPrivate *priv = conn->interfacePrivateData;
+ esxVI_PhysicalNic *physicalNic = NULL;
+
+ if (esxVI_EnsureSession(priv->primary) < 0 ||
+ esxVI_LookupPhysicalNicByMACAddress(priv->primary, mac, &physicalNic,
+ esxVI_Occurrence_RequiredItem) < 0) {
+ return NULL;
+ }
+
+ iface = virGetInterface(conn, physicalNic->device, physicalNic->mac);
+
+ esxVI_PhysicalNic_Free(&physicalNic);
+
+ return iface;
+}
+
+
+
+static char *
+esxInterfaceGetXMLDesc(virInterfacePtr iface, unsigned int flags)
+{
+ char *xml = NULL;
+ esxPrivate *priv = iface->conn->interfacePrivateData;
+ esxVI_PhysicalNic *physicalNic = NULL;
+ virInterfaceDef def;
+ bool hasAddress = false;
+ virInterfaceProtocolDefPtr protocols;
+ virInterfaceProtocolDef protocol;
+ virSocketAddr socketAddress;
+ virInterfaceIpDefPtr ips;
+ virInterfaceIpDef ip;
+
+ virCheckFlags(VIR_INTERFACE_XML_INACTIVE, NULL);
+
+ memset(&def, 0, sizeof(def));
+ memset(&protocol, 0, sizeof(protocol));
+ memset(&socketAddress, 0, sizeof(socketAddress));
+ memset(&ip, 0, sizeof(ip));
+
+ if (esxVI_EnsureSession(priv->primary) < 0 ||
+ esxVI_LookupPhysicalNicByMACAddress(priv->primary, iface->mac,
+ &physicalNic,
+ esxVI_Occurrence_RequiredItem) < 0) {
+ return NULL;
+ }
+
+ def.type = VIR_INTERFACE_TYPE_ETHERNET;
+ def.name = physicalNic->device;
+ def.mac = physicalNic->mac;
+ def.startmode = VIR_INTERFACE_START_ONBOOT;
+
+ /* FIXME: Add support for IPv6, requires to use vSphere API 4.0 */
+ if (physicalNic->spec->ip != NULL) {
+ protocol.family = (char *)"ipv4";
+
+ if (physicalNic->spec->ip->dhcp == esxVI_Boolean_True) {
+ protocol.dhcp = 1;
+ }
+
+ if (physicalNic->spec->ip->ipAddress != NULL &&
+ physicalNic->spec->ip->subnetMask != NULL &&
+ strlen(physicalNic->spec->ip->ipAddress) > 0 &&
+ strlen(physicalNic->spec->ip->subnetMask) > 0) {
+ hasAddress = true;
+ }
+
+ if (protocol.dhcp || hasAddress) {
+ protocols = &protocol;
+ def.nprotos = 1;
+ def.protos = &protocols;
+ }
+
+ if (hasAddress &&
+ !(protocol.dhcp && (flags & VIR_INTERFACE_XML_INACTIVE))) {
+ ips = &ip;
+ protocol.nips = 1;
+ protocol.ips = &ips;
+
+ if (virSocketAddrParseIPv4(&socketAddress,
+ physicalNic->spec->ip->subnetMask) < 0) {
+ goto cleanup;
+ }
+
+ ip.address = physicalNic->spec->ip->ipAddress;
+ ip.prefix = virSocketAddrGetNumNetmaskBits(&socketAddress);
+ }
+ }
+
+ xml = virInterfaceDefFormat(&def);
+
+ cleanup:
+ esxVI_PhysicalNic_Free(&physicalNic);
+
+ return xml;
+}
+
+
+
+static int
+esxInterfaceIsActive(virInterfacePtr iface ATTRIBUTE_UNUSED)
+{
+ /* ESX interfaces are always active */
+ return 1;
+}
+
+
+
static virInterfaceDriver esxInterfaceDriver = {
.name = "ESX",
.open = esxInterfaceOpen, /* 0.7.6 */
.close = esxInterfaceClose, /* 0.7.6 */
+ .numOfInterfaces = esxNumberOfInterfaces, /* 0.10.0 */
+ .listInterfaces = esxListInterfaces, /* 0.10.0 */
+ .numOfDefinedInterfaces = esxNumberOfDefinedInterfaces, /* 0.10.0 */
+ .listDefinedInterfaces = esxListDefinedInterfaces, /* 0.10.0 */
+ .interfaceLookupByName = esxInterfaceLookupByName, /* 0.10.0 */
+ .interfaceLookupByMACString = esxInterfaceLookupByMACString, /* 0.10.0 */
+ .interfaceGetXMLDesc = esxInterfaceGetXMLDesc, /* 0.10.0 */
+ .interfaceIsActive = esxInterfaceIsActive, /* 0.10.0 */
};
diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c
index 2c789e1..c4217c9 100644
--- a/src/esx/esx_vi.c
+++ b/src/esx/esx_vi.c
@@ -3,7 +3,7 @@
* esx_vi.c: client for the VMware VI API 2.5 to manage ESX hosts
*
* Copyright (C) 2010-2011 Red Hat, Inc.
- * Copyright (C) 2009-2011 Matthias Bolte <matthias.bolte(a)googlemail.com>
+ * Copyright (C) 2009-2012 Matthias Bolte <matthias.bolte(a)googlemail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -3949,6 +3949,148 @@ esxVI_LookupAutoStartPowerInfoList(esxVI_Context *ctx,
int
+esxVI_LookupPhysicalNicList(esxVI_Context *ctx,
+ esxVI_PhysicalNic **physicalNicList)
+{
+ int result = -1;
+ esxVI_String *propertyNameList = NULL;
+ esxVI_ObjectContent *hostSystem = NULL;
+ esxVI_DynamicProperty *dynamicProperty = NULL;
+
+ if (physicalNicList == NULL || *physicalNicList != NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
+ return -1;
+ }
+
+ if (esxVI_String_AppendValueToList(&propertyNameList,
+ "config.network.pnic") < 0 ||
+ esxVI_LookupHostSystemProperties(ctx, propertyNameList,
+ &hostSystem) < 0) {
+ goto cleanup;
+ }
+
+ for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL;
+ dynamicProperty = dynamicProperty->_next) {
+ if (STREQ(dynamicProperty->name, "config.network.pnic")) {
+ if (esxVI_PhysicalNic_CastListFromAnyType(dynamicProperty->val,
+ physicalNicList) < 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_LookupPhysicalNicByName(esxVI_Context *ctx, const char *name,
+ esxVI_PhysicalNic **physicalNic,
+ esxVI_Occurrence occurrence)
+{
+ int result = -1;
+ esxVI_PhysicalNic *physicalNicList = NULL;
+ esxVI_PhysicalNic *candidate = NULL;
+
+ if (physicalNic == NULL || *physicalNic != NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
+ return -1;
+ }
+
+ if (esxVI_LookupPhysicalNicList(ctx, &physicalNicList) < 0) {
+ goto cleanup;
+ }
+
+ /* Search for a matching physical NIC */
+ for (candidate = physicalNicList; candidate != NULL;
+ candidate = candidate->_next) {
+ if (STRCASEEQ(candidate->device, name)) {
+ if (esxVI_PhysicalNic_DeepCopy(physicalNic, candidate) < 0) {
+ goto cleanup;
+ }
+
+ /* Found physical NIC with matching name */
+ result = 0;
+
+ goto cleanup;
+ }
+ }
+
+ if (*physicalNic == NULL && occurrence != esxVI_Occurrence_OptionalItem) {
+ virReportError(VIR_ERR_NO_INTERFACE,
+ _("Could not find physical NIC with name '%s'"), name);
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ esxVI_PhysicalNic_Free(&physicalNicList);
+
+ return result;
+}
+
+
+
+int
+esxVI_LookupPhysicalNicByMACAddress(esxVI_Context *ctx, const char *mac,
+ esxVI_PhysicalNic **physicalNic,
+ esxVI_Occurrence occurrence)
+{
+ int result = -1;
+ esxVI_PhysicalNic *physicalNicList = NULL;
+ esxVI_PhysicalNic *candidate = NULL;
+
+ if (physicalNic == NULL || *physicalNic != NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument"));
+ return -1;
+ }
+
+ if (esxVI_LookupPhysicalNicList(ctx, &physicalNicList) < 0) {
+ goto cleanup;
+ }
+
+ /* Search for a matching physical NIC */
+ for (candidate = physicalNicList; candidate != NULL;
+ candidate = candidate->_next) {
+ if (STRCASEEQ(candidate->mac, mac)) {
+ if (esxVI_PhysicalNic_DeepCopy(physicalNic, candidate) < 0) {
+ goto cleanup;
+ }
+
+ /* Found physical NIC with matching MAC address */
+ result = 0;
+
+ goto cleanup;
+ }
+ }
+
+ if (*physicalNic == NULL && occurrence != esxVI_Occurrence_OptionalItem) {
+ virReportError(VIR_ERR_NO_INTERFACE,
+ _("Could not find physical NIC with MAC address '%s'"), mac);
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ esxVI_PhysicalNic_Free(&physicalNicList);
+
+ return result;
+}
+
+
+
+int
esxVI_HandleVirtualMachineQuestion
(esxVI_Context *ctx, esxVI_ManagedObjectReference *virtualMachine,
esxVI_VirtualMachineQuestionInfo *questionInfo, bool autoAnswer,
diff --git a/src/esx/esx_vi.h b/src/esx/esx_vi.h
index 4b84be8..597013c 100644
--- a/src/esx/esx_vi.h
+++ b/src/esx/esx_vi.h
@@ -3,7 +3,7 @@
* esx_vi.h: client for the VMware VI API 2.5 to manage ESX hosts
*
* Copyright (C) 2011 Red Hat, Inc.
- * Copyright (C) 2009-2010 Matthias Bolte <matthias.bolte(a)googlemail.com>
+ * Copyright (C) 2009-2012 Matthias Bolte <matthias.bolte(a)googlemail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -481,6 +481,17 @@ int esxVI_LookupAutoStartDefaults(esxVI_Context *ctx,
int esxVI_LookupAutoStartPowerInfoList(esxVI_Context *ctx,
esxVI_AutoStartPowerInfo **powerInfoList);
+int esxVI_LookupPhysicalNicList(esxVI_Context *ctx,
+ esxVI_PhysicalNic **physicalNicList);
+
+int esxVI_LookupPhysicalNicByName(esxVI_Context *ctx, const char *name,
+ esxVI_PhysicalNic **physicalNic,
+ esxVI_Occurrence occurrence);
+
+int esxVI_LookupPhysicalNicByMACAddress(esxVI_Context *ctx, const char *mac,
+ esxVI_PhysicalNic **physicalNic,
+ esxVI_Occurrence occurrence);
+
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 1a67a8c..5572b36 100644
--- a/src/esx/esx_vi_generator.input
+++ b/src/esx/esx_vi_generator.input
@@ -317,6 +317,13 @@ object HostFileSystemVolume
end
+object HostIpConfig
+ Boolean dhcp r
+ String ipAddress o
+ String subnetMask o
+end
+
+
object HostMountInfo
String path o
String accessMode r
@@ -455,6 +462,31 @@ object PerfSampleInfo
end
+object PhysicalNic
+ String key o
+ String device r
+ String pci r
+ String driver o
+ PhysicalNicLinkInfo linkSpeed o
+ PhysicalNicLinkInfo validLinkSpecification ol
+ PhysicalNicSpec spec r
+ Boolean wakeOnLanSupported r
+ String mac r
+end
+
+
+object PhysicalNicLinkInfo
+ Int speedMb r
+ Boolean duplex r
+end
+
+
+object PhysicalNicSpec
+ HostIpConfig ip o
+ PhysicalNicLinkInfo linkSpeed o
+end
+
+
object PropertyChange
String name r
PropertyChangeOp op r
@@ -569,7 +601,7 @@ object TemplateConfigFileQuery extends VmConfigFileQuery
end
-object TraversalSpec extends SelectionSpec
+object TraversalSpec extends SelectionSpec
String type r
String path r
Boolean skip o
diff --git a/src/esx/esx_vi_generator.py b/src/esx/esx_vi_generator.py
index af2d57e..596bd16 100755
--- a/src/esx/esx_vi_generator.py
+++ b/src/esx/esx_vi_generator.py
@@ -3,7 +3,7 @@
#
# esx_vi_generator.py: generates most of the SOAP type mapping code
#
-# Copyright (C) 2010-2011 Matthias Bolte <matthias.bolte(a)googlemail.com>
+# Copyright (C) 2010-2012 Matthias Bolte <matthias.bolte(a)googlemail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -1522,6 +1522,9 @@ additional_object_features = { "AutoStartDefaults" : Object.FEATURE__AN
Object.FEATURE__ANY_TYPE,
"ManagedObjectReference" : Object.FEATURE__ANY_TYPE,
"ObjectContent" : Object.FEATURE__DEEP_COPY,
+ "PhysicalNic" : Object.FEATURE__DEEP_COPY |
+ Object.FEATURE__LIST |
+ Object.FEATURE__ANY_TYPE,
"ResourcePoolResourceUsage" : Object.FEATURE__ANY_TYPE,
"ServiceContent" : Object.FEATURE__DESERIALIZE,
"SharesInfo" : Object.FEATURE__ANY_TYPE,
--
1.7.4.1
12 years, 3 months
[libvirt] [PATCH] esx: Remove redundant checks for esxVI_LookupHostSystemProperties result
by Matthias Bolte
esxVI_LookupHostSystemProperties guarantees that hostSystem is non-NULL.
Remove redundant NULL checks from callers.
Also prefer esxVI_GetStringValue over open-coding the logic.
---
src/esx/esx_driver.c | 82 +++++++++---------------------------------------
src/esx/esx_vi.c | 4 +-
src/esx/esx_vi_types.c | 3 +-
3 files changed, 19 insertions(+), 70 deletions(-)
diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c
index 47957cc..72a7acc 100644
--- a/src/esx/esx_driver.c
+++ b/src/esx/esx_driver.c
@@ -3,7 +3,7 @@
* esx_driver.c: core driver functions for managing VMware ESX hosts
*
* Copyright (C) 2010-2012 Red Hat, Inc.
- * Copyright (C) 2009-2011 Matthias Bolte <matthias.bolte(a)googlemail.com>
+ * Copyright (C) 2009-2012 Matthias Bolte <matthias.bolte(a)googlemail.com>
* Copyright (C) 2009 Maximilian Wilhelm <max(a)rfc2324.org>
*
* This library is free software; you can redistribute it and/or
@@ -467,12 +467,6 @@ esxSupportsLongMode(esxPrivate *priv)
goto cleanup;
}
- if (hostSystem == NULL) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Could not retrieve the HostSystem object"));
- goto cleanup;
- }
-
for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "hardware.cpuFeature")) {
@@ -534,7 +528,7 @@ esxLookupHostSystemBiosUuid(esxPrivate *priv, unsigned char *uuid)
int result = -1;
esxVI_String *propertyNameList = NULL;
esxVI_ObjectContent *hostSystem = NULL;
- esxVI_DynamicProperty *dynamicProperty = NULL;
+ char *uuid_string = NULL;
if (esxVI_EnsureSession(priv->primary) < 0) {
return -1;
@@ -543,41 +537,22 @@ esxLookupHostSystemBiosUuid(esxPrivate *priv, unsigned char *uuid)
if (esxVI_String_AppendValueToList(&propertyNameList,
"hardware.systemInfo.uuid") < 0 ||
esxVI_LookupHostSystemProperties(priv->primary, propertyNameList,
- &hostSystem) < 0) {
- goto cleanup;
- }
-
- if (hostSystem == NULL) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Could not retrieve the HostSystem object"));
+ &hostSystem) < 0 ||
+ esxVI_GetStringValue(hostSystem, "hardware.systemInfo.uuid",
+ &uuid_string, esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
}
- for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL;
- dynamicProperty = dynamicProperty->_next) {
- if (STREQ(dynamicProperty->name, "hardware.systemInfo.uuid")) {
- if (esxVI_AnyType_ExpectType(dynamicProperty->val,
- esxVI_Type_String) < 0) {
- goto cleanup;
- }
-
- if (strlen(dynamicProperty->val->string) > 0) {
- if (virUUIDParse(dynamicProperty->val->string, uuid) < 0) {
- VIR_WARN("Could not parse host UUID from string '%s'",
- dynamicProperty->val->string);
+ if (strlen(uuid_string) > 0) {
+ if (virUUIDParse(uuid_string, uuid) < 0) {
+ VIR_WARN("Could not parse host UUID from string '%s'", uuid_string);
- /* HostSystem has an invalid UUID, ignore it */
- memset(uuid, 0, VIR_UUID_BUFLEN);
- }
- } else {
- /* HostSystem has an empty UUID */
- memset(uuid, 0, VIR_UUID_BUFLEN);
- }
-
- break;
- } else {
- VIR_WARN("Unexpected '%s' property", dynamicProperty->name);
+ /* HostSystem has an invalid UUID, ignore it */
+ memset(uuid, 0, VIR_UUID_BUFLEN);
}
+ } else {
+ /* HostSystem has an empty UUID */
+ memset(uuid, 0, VIR_UUID_BUFLEN);
}
result = 0;
@@ -1176,17 +1151,8 @@ esxSupportsVMotion(esxPrivate *priv)
if (esxVI_String_AppendValueToList(&propertyNameList,
"capability.vmotionSupported") < 0 ||
esxVI_LookupHostSystemProperties(priv->primary, propertyNameList,
- &hostSystem) < 0) {
- goto cleanup;
- }
-
- if (hostSystem == NULL) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Could not retrieve the HostSystem object"));
- goto cleanup;
- }
-
- if (esxVI_GetBoolean(hostSystem, "capability.vmotionSupported",
+ &hostSystem) < 0 ||
+ esxVI_GetBoolean(hostSystem, "capability.vmotionSupported",
&priv->supportsVMotion,
esxVI_Occurrence_RequiredItem) < 0) {
goto cleanup;
@@ -1281,12 +1247,6 @@ esxGetHostname(virConnectPtr conn)
goto cleanup;
}
- if (hostSystem == NULL) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Could not retrieve the HostSystem object"));
- goto cleanup;
- }
-
for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name,
@@ -1379,12 +1339,6 @@ esxNodeGetInfo(virConnectPtr conn, virNodeInfoPtr nodeinfo)
goto cleanup;
}
- if (hostSystem == NULL) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Could not retrieve the HostSystem object"));
- goto cleanup;
- }
-
for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "hardware.cpuInfo.hz")) {
@@ -2702,12 +2656,6 @@ esxDomainGetVcpusFlags(virDomainPtr domain, unsigned int flags)
goto cleanup;
}
- if (hostSystem == NULL) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("Could not retrieve the HostSystem object"));
- goto cleanup;
- }
-
for (dynamicProperty = hostSystem->propSet; dynamicProperty != NULL;
dynamicProperty = dynamicProperty->_next) {
if (STREQ(dynamicProperty->name, "capability.maxSupportedVcpus")) {
diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c
index c4217c9..f3a9e91 100644
--- a/src/esx/esx_vi.c
+++ b/src/esx/esx_vi.c
@@ -4570,8 +4570,8 @@ esxVI_ProductVersionToDefaultVirtualHWVersion(esxVI_ProductVersion productVersio
esxVI_DynamicProperty *dynamicProperty = NULL; \
\
if (ptrptr == NULL || *ptrptr != NULL) { \
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
- _("Invalid argument")); \
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
+ _("Invalid argument")); \
return -1; \
} \
\
diff --git a/src/esx/esx_vi_types.c b/src/esx/esx_vi_types.c
index 36f3196..2236751 100644
--- a/src/esx/esx_vi_types.c
+++ b/src/esx/esx_vi_types.c
@@ -296,7 +296,8 @@
childNode = childNode->next) { \
if (childNode->type != XML_ELEMENT_NODE) { \
virReportError(VIR_ERR_INTERNAL_ERROR, \
- _("Wrong XML element type %d"), childNode->type); \
+ _("Wrong XML element type %d"), \
+ childNode->type); \
goto failure; \
} \
\
--
1.7.4.1
12 years, 3 months
[libvirt] [PATCH] esx: Fix dynamic deep copy
by Matthias Bolte
The static deep copy allocates storage for the copy. The dynamic
version injected the dynamic dispatch after the allocation. This
triggered the invalid argument check in the dynamically dispatched
deep copy call. The deep copy function expects its dest parameter
to be a pointer to a NULL-pointer. This expectation wasn't met due
to the dispatching deep copy doing the allocation before the call.
Fix this by dynamically dispatching to the correct type before the
allocation.
---
src/esx/esx_vi_types.c | 35 ++++++++++++++++++++++++++++++-----
1 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/src/esx/esx_vi_types.c b/src/esx/esx_vi_types.c
index 8750554..36f3196 100644
--- a/src/esx/esx_vi_types.c
+++ b/src/esx/esx_vi_types.c
@@ -621,11 +621,36 @@
#define ESX_VI__TEMPLATE__DYNAMIC_DEEP_COPY(__type, _dispatch, _deep_copy) \
- ESX_VI__TEMPLATE__DEEP_COPY(__type, \
- ESX_VI__TEMPLATE__DISPATCH(src->_type, \
- esxVI_Type_ToString(src->_type), \
- __type, _dispatch, -1) \
- _deep_copy)
+ int \
+ esxVI_##__type##_DeepCopy(esxVI_##__type **dest, esxVI_##__type *src) \
+ { \
+ if (dest == NULL || *dest != NULL) { \
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
+ _("Invalid argument")); \
+ return -1; \
+ } \
+ \
+ if (src == NULL) { \
+ return 0; \
+ } \
+ \
+ ESX_VI__TEMPLATE__DISPATCH(src->_type, \
+ esxVI_Type_ToString(src->_type), \
+ __type, _dispatch, -1) \
+ \
+ if (esxVI_##__type##_Alloc(dest) < 0) { \
+ goto failure; \
+ } \
+ \
+ _deep_copy \
+ \
+ return 0; \
+ \
+ failure: \
+ esxVI_##__type##_Free(dest); \
+ \
+ return -1; \
+ }
--
1.7.4.1
12 years, 3 months
[libvirt] [PATCH] nwfilter: remove target table before renaming it
by Stefan Berger
Remove the target table before renaming a table to it, i.e.,
remove table B before renaming A to B. This makes the
renaming more robust against unconnected left-over tables.
---
src/nwfilter/nwfilter_ebiptables_driver.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
Index: libvirt-firewalld/src/nwfilter/nwfilter_ebiptables_driver.c
===================================================================
--- libvirt-firewalld.orig/src/nwfilter/nwfilter_ebiptables_driver.c
+++ libvirt-firewalld/src/nwfilter/nwfilter_ebiptables_driver.c
@@ -125,12 +125,18 @@ static const char ebiptables_script_func
"}\n";
static const char ebiptables_script_func_rename_chains[] =
+ "rename_chain()\n"
+ "{\n"
+ " $EBT -t nat -F $2\n"
+ " $EBT -t nat -X $2\n"
+ " $EBT -t nat -E $1 $2\n"
+ "}\n"
"rename_chains()\n"
"{\n"
" for tmp in $*; do\n"
" case $tmp in\n"
- " %c*) $EBT -t nat -E $tmp %c${tmp#?} ;;\n"
- " %c*) $EBT -t nat -E $tmp %c${tmp#?} ;;\n"
+ " %c*) rename_chain $tmp %c${tmp#?} ;;\n"
+ " %c*) rename_chain $tmp %c${tmp#?} ;;\n"
" esac\n"
" done\n"
"}\n";
12 years, 3 months
[libvirt] PF interface brought down if guest using a VF in hostdev mode under 802.1Qbh
by Nishank Trivedi (nistrive)
Hi,
In 802.1Qbh case where exists a SR-IOV capable network device, if any of
the virtual functions of this device is used in hostdev mode for the
guest, port-profile disassociate will also cause physical function
interface to go down. This appears to be a bug, but wanted to find if this
was done intentionally for some reasons.
To be more specific, if a network device supports SRIOV and its VF is
being used in pci passthrough mode, when the guest is shutdown or
destroyed following happens -
qemuProcessStop
\_ qemuDomainReAttachHostdevDevices(hostdevs)
\_ qemuDomainHostdevNetConfigRestore(hostdev)
\_ virNetDevPortProfileDisassociate(linkdev)
\_ virNetDevSetOnline(linkdev, false)
In above, qemuDomainHostdevNetConfigRestore() finds out the PF for
provided hostdev (which is VF) and passes it to
virNetDevPortProfileDisassociate() as linkdev. Later, linkdev gets passed
to virNetDevSetOnline() where the interface is brought down by clearing
IFF_UP flag.
However, in macvtap emulation mode,
virNetDevMacVLanDeleteWithVPortProfile() passes VF as linkdev (and -1 as
vf) to virNetDevPortProfileDisassociate(), hence, not affecting PF at all.
Bringing down a PF, when only VF is being brought down is not expected
behavior (unless, I'm missing something here). A way to get around it
would be to check if there exists vf (>=0) in
virNetDevPortProfileDisassociate(), and if so, it should only pass the
interface name of this VF rather than PF itself to virNetDevSetOnline().
If it sounds reasonable, I'll send out a fix.
Thanks,
Nishank
12 years, 3 months
[libvirt] virDomainMemoryStats call
by Parakkal, Navin S
Hi,
Is the array mstats returned by virDomainMemoryStats call cummulative or snapshot of the counter at that particular time ? I was of the opinion that is cummulative like the /proc counters but this counter seems to go backwards ie next snapshot value is less than the current one which makes me think these are snapshot values . I'm using RHEL 6.3 libvirt version 0.9.10.
Regards,
Navin
12 years, 3 months
[libvirt] [PATCH 5/5] Add qemu-agent-command command to virsh
by MATSUDA, Daiki
Add qemu-agent-command command to virsh to support virDomainQemuAgentCommand().
virsh-host.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/tools/virsh-host.c b/tools/virsh-host.c
index d9d09b4..b9180f3 100644
--- a/tools/virsh-host.c
+++ b/tools/virsh-host.c
@@ -633,6 +633,74 @@ cleanup:
}
/*
+ * "qemu-agent-command" command
+ */
+static const vshCmdInfo info_qemu_agent_command[] = {
+ {"help", N_("Qemu Guest Agent Command")},
+ {"desc", N_("Qemu Guest Agent Command")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_qemu_agent_command[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"timeout", VSH_OT_INT, VSH_OFLAG_REQ_OPT, N_("timeout")},
+ {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("command")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdQemuAgentCommand(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ bool ret = false;
+ char *guest_agent_cmd = NULL;
+ char *result = NULL;
+ int timeout = 0;
+ unsigned int flags = 0;
+ const vshCmdOpt *opt = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ bool pad = false;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto cleanup;
+
+ dom = vshCommandOptDomain(ctl, cmd, NULL);
+ if (dom == NULL)
+ goto cleanup;
+
+ while ((opt = vshCommandOptArgv(cmd, opt))) {
+ if (pad)
+ virBufferAddChar(&buf, ' ');
+ pad = true;
+ virBufferAdd(&buf, opt->data, -1);
+ }
+ if (virBufferError(&buf)) {
+ vshPrint(ctl, "%s", _("Failed to collect command"));
+ goto cleanup;
+ }
+ guest_agent_cmd = virBufferContentAndReset(&buf);
+
+ if (vshCommandOptInt(cmd, "timeout", &timeout) < 0) {
+ timeout = 0;
+ }
+
+ if (virDomainQemuAgentCommand(dom, guest_agent_cmd, &result,
+ timeout, flags) < 0)
+ goto cleanup;
+
+ printf("%s\n", result);
+
+ ret = true;
+
+cleanup:
+ VIR_FREE(result);
+ VIR_FREE(guest_agent_cmd);
+ if (dom)
+ virDomainFree(dom);
+
+ return ret;
+}
+/*
* "sysinfo" command
*/
static const vshCmdInfo info_sysinfo[] = {
@@ -832,6 +900,8 @@ static const vshCmdDef hostAndHypervisorCmds[] = {
{"qemu-attach", cmdQemuAttach, opts_qemu_attach, info_qemu_attach, 0},
{"qemu-monitor-command", cmdQemuMonitorCommand, opts_qemu_monitor_command,
info_qemu_monitor_command, 0},
+ {"qemu-agent-command", cmdQemuAgentCommand, opts_qemu_agent_command,
+ info_qemu_agent_command, 0},
{"sysinfo", cmdSysinfo, NULL, info_sysinfo, 0},
{"uri", cmdURI, NULL, info_uri, 0},
{"version", cmdVersion, opts_version, info_version, 0},
12 years, 3 months
[libvirt] [PATCH 4/5] Add virDomainQemuAgentCommand() support function to python module
by MATSUDA, Daiki
Add virDomainQemuAgentCommand() support function to python module.
generator.py | 1 +
libvirt-qemu-override-api.xml | 8 ++++++++
libvirt-qemu-override.c | 31 +++++++++++++++++++++++++++++++
3 files changed, 40 insertions(+)
diff --git a/python/generator.py b/python/generator.py
index 6559ece..3cec12b 100755
--- a/python/generator.py
+++ b/python/generator.py
@@ -431,6 +431,7 @@ skip_impl = (
qemu_skip_impl = (
'virDomainQemuMonitorCommand',
+ 'virDomainQemuAgentCommand',
)
diff --git a/python/libvirt-qemu-override-api.xml b/python/libvirt-qemu-override-api.xml
index d69acea..37759d0 100644
--- a/python/libvirt-qemu-override-api.xml
+++ b/python/libvirt-qemu-override-api.xml
@@ -8,5 +8,13 @@
<arg name='cmd' type='const char *' info='the command which will be passed to QEMU monitor'/>
<arg name='flags' type='unsigned int' info='an OR'ed set of virDomainQemuMonitorCommandFlags'/>
</function>
+ <function name='virDomainQemuAgentCommand' file='python-qemu'>
+ <info>Send a Guest Agent command to domain</info>
+ <return type='str *' info='the command output or None in case of error'/>
+ <arg name='domain' type='virDomainPtr' info='pointer to the domain'/>
+ <arg name='cmd' type='const char *' info='guest agent command on domain'/>
+ <arg name='timeout' type='int' info='timeout seconds'/>
+ <arg name='flags' type='unsigned int' info='execution flags'/>
+ </function>
</symbols>
</api>
diff --git a/python/libvirt-qemu-override.c b/python/libvirt-qemu-override.c
index e532416..195aadc 100644
--- a/python/libvirt-qemu-override.c
+++ b/python/libvirt-qemu-override.c
@@ -82,6 +82,36 @@ libvirt_qemu_virDomainQemuMonitorCommand(PyObject *self ATTRIBUTE_UNUSED,
return py_retval;
}
+static PyObject *
+libvirt_qemu_virDomainQemuAgentCommand(PyObject *self ATTRIBUTE_UNUSED,
+ PyObject *args)
+{
+ PyObject *py_retval;
+ char *result = NULL;
+ virDomainPtr domain;
+ PyObject *pyobj_domain;
+ int timeout;
+ unsigned int flags;
+ char *cmd;
+ int c_retval;
+
+ if (!PyArg_ParseTuple(args, (char *)"Ozii:virDomainQemuAgentCommand",
+ &pyobj_domain, &cmd, &timeout, &flags))
+ return NULL;
+ domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain);
+
+ if (domain == NULL)
+ return VIR_PY_NONE;
+ LIBVIRT_BEGIN_ALLOW_THREADS;
+ c_retval = virDomainQemuAgentCommand(domain, cmd, &result, timeout, flags);
+ LIBVIRT_END_ALLOW_THREADS;
+
+ if (c_retval < 0)
+ return VIR_PY_NONE;
+
+ py_retval = PyString_FromString(result);
+ return py_retval;
+}
/************************************************************************
* *
* The registration stuff *
@@ -90,6 +120,7 @@ libvirt_qemu_virDomainQemuMonitorCommand(PyObject *self ATTRIBUTE_UNUSED,
static PyMethodDef libvirtQemuMethods[] = {
#include "libvirt-qemu-export.c"
{(char *) "virDomainQemuMonitorCommand", libvirt_qemu_virDomainQemuMonitorCommand, METH_VARARGS, NULL},
+ {(char *) "virDomainQemuAgentCommand", libvirt_qemu_virDomainQemuAgentCommand, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL}
};
12 years, 3 months
[libvirt] [PATCH 3/5] add virDomainQemuAgentCommand() to qemu driver and remote driver
by MATSUDA, Daiki
Add virDomainQemuAgentCommand() to qemu driver and remote driver
to support qemuAgentCommand().
daemon/remote.c | 35 ++++++++++++++++++++
include/libvirt/libvirt-qemu.h | 3 +
src/driver.h | 6 +++
src/libvirt-qemu.c | 58 +++++++++++++++++++++++++++++++++
src/libvirt_qemu.syms | 5 ++
src/qemu/qemu_driver.c | 71 +++++++++++++++++++++++++++++++++++++++++
src/qemu_protocol-structs | 10 +++++
src/remote/qemu_protocol.x | 14 +++++++-
src/remote/remote_driver.c | 41 +++++++++++++++++++++++
9 files changed, 242 insertions(+), 1 deletion(-)
diff --git a/daemon/remote.c b/daemon/remote.c
index 832307e..4fd5c9b 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -3948,6 +3948,41 @@ cleanup:
return rv;
}
+static int
+qemuDispatchAgentCommand(virNetServerPtr server ATTRIBUTE_UNUSED,
+ virNetServerClientPtr client ATTRIBUTE_UNUSED,
+ virNetMessagePtr msg ATTRIBUTE_UNUSED,
+ virNetMessageErrorPtr rerr,
+ qemu_agent_command_args *args,
+ qemu_agent_command_ret *ret)
+{
+ virDomainPtr dom = NULL;
+ int rv = -1;
+ struct daemonClientPrivate *priv =
+ virNetServerClientGetPrivateData(client);
+
+ if (!priv->conn) {
+ virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+ goto cleanup;
+ }
+
+ if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+ goto cleanup;
+
+ if (virDomainQemuAgentCommand(dom, args->cmd, &ret->result,
+ args->timeout, args->flags) < 0) {
+ goto cleanup;
+ }
+
+ rv = 0;
+cleanup:
+ if (rv < 0)
+ virNetMessageSaveError(rerr);
+ if (dom)
+ virDomainFree(dom);
+ return rv;
+}
+
/*----- Helpers. -----*/
/* get_nonnull_domain and get_nonnull_network turn an on-wire
diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h
index ffc5ae5..aed1259 100644
--- a/include/libvirt/libvirt-qemu.h
+++ b/include/libvirt/libvirt-qemu.h
@@ -49,6 +49,9 @@ typedef enum {
VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT = 0,
} virDomainQemuAgentCommandTimeoutFlags;
+int virDomainQemuAgentCommand(virDomainPtr domain, const char *cmd,
+ char **result, int timeout, unsigned int flags);
+
# ifdef __cplusplus
}
# endif
diff --git a/src/driver.h b/src/driver.h
index aab9766..c368273 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -689,6 +689,11 @@ typedef int
(*virDrvDomainQemuMonitorCommand)(virDomainPtr domain, const char *cmd,
char **result, unsigned int flags);
+typedef int
+ (*virDrvDomainQemuAgentCommand)(virDomainPtr domain, const char *cmd,
+ char **result, int timeout,
+ unsigned int flags);
+
/* Choice of unsigned int rather than pid_t is intentional. */
typedef virDomainPtr
(*virDrvDomainQemuAttach)(virConnectPtr conn,
@@ -1048,6 +1053,7 @@ struct _virDriver {
virDrvDomainGetDiskErrors domainGetDiskErrors;
virDrvDomainSetMetadata domainSetMetadata;
virDrvDomainGetMetadata domainGetMetadata;
+ virDrvDomainQemuAgentCommand qemuDomainQemuAgentCommand;
};
typedef int
diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c
index 78480bb..0e12425 100644
--- a/src/libvirt-qemu.c
+++ b/src/libvirt-qemu.c
@@ -185,3 +185,61 @@ error:
virDispatchError(conn);
return NULL;
}
+
+/**
+ * virDomainQemuAgentCommand:
+ * @domain: a domain object
+ * @cmd: the guest agent command string
+ * @result: a string returned by @cmd
+ * @timeout: timeout seconds
+ * @flags: execution flags
+ *
+ * Execution Guest Agent Command
+ *
+ * Issue @cmd to the guest agent running in @domain.
+ * If @result is NULL, then don't wait for a result (and @timeout
+ * must be 0). Otherwise, wait for @timeout seconds for a
+ * successful result to be populated into *@result, with
+ * VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK (-1) meaning to block
+ * forever waiting for a result.
+ *
+ * Returns 0 if succeeded, -1 in failing.
+ */
+int
+virDomainQemuAgentCommand(virDomainPtr domain,
+ const char *cmd,
+ char **result,
+ int timeout,
+ unsigned int flags)
+{
+ virConnectPtr conn;
+
+ VIR_DEBUG("domain=%p, cmd=%s, result=%p, timeout=%d, flags=%x",
+ domain, cmd, result, timeout, flags);
+
+ if (!VIR_IS_CONNECTED_DOMAIN(domain)) {
+ virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+ virDispatchError(NULL);
+ return -1;
+ }
+
+ conn = domain->conn;
+
+ virCheckNonNullArgGoto(result, error);
+
+ if (conn->driver->qemuDomainQemuAgentCommand) {
+ int ret;
+ ret = conn->driver->qemuDomainQemuAgentCommand(domain, cmd, result,
+ timeout, flags);
+ if (ret < 0)
+ goto error;
+ return ret;
+ }
+
+ virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+ /* Copy to connection error object for back compatability */
+ virDispatchError(domain->conn);
+ return -1;
+}
diff --git a/src/libvirt_qemu.syms b/src/libvirt_qemu.syms
index 8447730..f968d91 100644
--- a/src/libvirt_qemu.syms
+++ b/src/libvirt_qemu.syms
@@ -19,3 +19,8 @@ LIBVIRT_QEMU_0.9.4 {
global:
virDomainQemuAttach;
} LIBVIRT_QEMU_0.8.3;
+
+LIBVIRT_QEMU_0.10.0 {
+ global:
+ virDomainQemuAgentCommand;
+} LIBVIRT_QEMU_0.9.4;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index dee1268..750577e 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -13204,6 +13204,76 @@ qemuListAllDomains(virConnectPtr conn,
return ret;
}
+static int
+qemuDomainQemuAgentCommand(virDomainPtr domain,
+ const char *cmd,
+ char **result,
+ int timeout,
+ unsigned int flags)
+{
+ struct qemud_driver *driver = domain->conn->privateData;
+ virDomainObjPtr vm;
+ int ret = -1;
+ qemuDomainObjPrivatePtr priv;
+
+ virCheckFlags(0, -1);
+
+ qemuDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, domain->uuid);
+ qemuDriverUnlock(driver);
+
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(domain->uuid, uuidstr);
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"), uuidstr);
+ goto cleanup;
+ }
+
+ priv = vm->privateData;
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto cleanup;
+ }
+
+ if (priv->agentError) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("QEMU guest agent is not available due to an error"));
+ goto cleanup;
+ }
+
+ if (!priv->agent) {
+ qemuReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+ _("QEMU guest agent is not configured"));
+ goto cleanup;
+ }
+
+ if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto endjob;
+ }
+
+ qemuDomainObjEnterAgent(driver, vm);
+ ret = qemuAgentQemuAgentCommand(priv->agent, cmd, result, timeout);
+ qemuDomainObjExitAgent(driver, vm);
+
+endjob:
+ if (qemuDomainObjEndJob(driver, vm) == 0) {
+ vm = NULL;
+ }
+
+cleanup:
+ if (vm)
+ virDomainObjUnlock(vm);
+ return ret;
+}
+
static virDriver qemuDriver = {
.no = VIR_DRV_QEMU,
.name = QEMU_DRIVER_NAME,
@@ -13369,6 +13439,7 @@ static virDriver qemuDriver = {
.domainPMSuspendForDuration = qemuDomainPMSuspendForDuration, /* 0.9.11 */
.domainPMWakeup = qemuDomainPMWakeup, /* 0.9.11 */
.domainGetCPUStats = qemuDomainGetCPUStats, /* 0.9.11 */
+ .qemuDomainQemuAgentCommand = qemuDomainQemuAgentCommand, /* 0.10.0 */
};
diff --git a/src/qemu_protocol-structs b/src/qemu_protocol-structs
index 67968eb..624fa7b 100644
--- a/src/qemu_protocol-structs
+++ b/src/qemu_protocol-structs
@@ -19,7 +19,17 @@ struct qemu_domain_attach_args {
struct qemu_domain_attach_ret {
remote_nonnull_domain dom;
};
+struct qemu_agent_command_args {
+ remote_nonnull_domain dom;
+ remote_nonnull_string cmd;
+ int timeout;
+ u_int flags;
+};
+struct qemu_agent_command_ret {
+ remote_nonnull_string result;
+};
enum qemu_procedure {
QEMU_PROC_MONITOR_COMMAND = 1,
QEMU_PROC_DOMAIN_ATTACH = 2,
+ QEMU_PROC_AGENT_COMMAND = 3,
};
diff --git a/src/remote/qemu_protocol.x b/src/remote/qemu_protocol.x
index c06339c..67c6493 100644
--- a/src/remote/qemu_protocol.x
+++ b/src/remote/qemu_protocol.x
@@ -47,6 +47,17 @@ struct qemu_domain_attach_ret {
remote_nonnull_domain dom;
};
+struct qemu_agent_command_args {
+ remote_nonnull_domain dom;
+ remote_nonnull_string cmd;
+ int timeout;
+ unsigned int flags;
+};
+
+struct qemu_agent_command_ret {
+ remote_nonnull_string result;
+};
+
/* Define the program number, protocol version and procedure numbers here. */
const QEMU_PROGRAM = 0x20008087;
const QEMU_PROTOCOL_VERSION = 1;
@@ -61,5 +72,6 @@ enum qemu_procedure {
* are some exceptions to this rule, e.g. domainDestroy. Other APIs MAY
* be marked as high priority. If in doubt, it's safe to choose low. */
QEMU_PROC_MONITOR_COMMAND = 1, /* skipgen skipgen priority:low */
- QEMU_PROC_DOMAIN_ATTACH = 2 /* autogen autogen priority:low */
+ QEMU_PROC_DOMAIN_ATTACH = 2, /* autogen autogen priority:low */
+ QEMU_PROC_AGENT_COMMAND = 3 /* skipgen skipgen priority:low */
};
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 353a153..6f27f59 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -5191,6 +5191,46 @@ make_nonnull_domain_snapshot (remote_nonnull_domain_snapshot *snapshot_dst, virD
make_nonnull_domain(&snapshot_dst->dom, snapshot_src->domain);
}
+static int
+remoteQemuDomainQemuAgentCommand (virDomainPtr domain, const char *cmd,
+ char **result, int timeout,
+ unsigned int flags)
+{
+ int rv = -1;
+ qemu_agent_command_args args;
+ qemu_agent_command_ret ret;
+ struct private_data *priv = domain->conn->privateData;
+
+ remoteDriverLock(priv);
+
+ make_nonnull_domain(&args.dom, domain);
+ args.cmd = (char *)cmd;
+ args.timeout = timeout;
+ args.flags = flags;
+
+ memset (&ret, 0, sizeof(ret));
+
+ if (call (domain->conn, priv, REMOTE_CALL_QEMU, QEMU_PROC_AGENT_COMMAND,
+ (xdrproc_t) xdr_qemu_agent_command_args, (char *) &args,
+ (xdrproc_t) xdr_qemu_agent_command_ret, (char *) &ret) == -1)
+ goto done;
+
+ *result = strdup(ret.result);
+ if (*result == NULL) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ rv = 0;
+
+cleanup:
+ xdr_free ((xdrproc_t) xdr_qemu_agent_command_ret, (char *) &ret);
+
+done:
+ remoteDriverUnlock(priv);
+ return rv;
+}
+
/*----------------------------------------------------------------------*/
unsigned long remoteVersion(void)
@@ -5368,6 +5408,7 @@ static virDriver remote_driver = {
.domainSetMetadata = remoteDomainSetMetadata, /* 0.9.10 */
.domainGetMetadata = remoteDomainGetMetadata, /* 0.9.10 */
.domainGetHostname = remoteDomainGetHostname, /* 0.10.0 */
+ .qemuDomainQemuAgentCommand = remoteQemuDomainQemuAgentCommand, /* 0.10.0 */
};
static virNetworkDriver network_driver = {
12 years, 3 months