/*
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
** See the GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see .
**/
#include "vmware_vm.h"
#include "zbxcommon.h"
#if defined(HAVE_LIBXML2) && defined(HAVE_LIBCURL)
#include "vmware_shmem.h"
#include "zbxtime.h"
#include "zbxstr.h"
#include "zbxxml.h"
#ifdef HAVE_LIBXML2
# include
#endif
#include "zbxalgo.h"
#include "zbxjson.h"
#include "zbxnum.h"
#define ZBX_VMPROPMAP(property) \
{property, ZBX_XPATH_PROP_OBJECT(ZBX_VMWARE_SOAP_VM) ZBX_XPATH_PROP_NAME_NODE(property), NULL, 0}
static int vmware_service_get_vm_snapshot(void *xml_node, char **jstr);
static zbx_vmware_propmap_t vm_propmap[] = {
ZBX_VMPROPMAP("summary.config.numCpu"), /* ZBX_VMWARE_VMPROP_CPU_NUM */
ZBX_VMPROPMAP("summary.quickStats.overallCpuUsage"), /* ZBX_VMWARE_VMPROP_CPU_USAGE */
ZBX_VMPROPMAP("summary.config.name"), /* ZBX_VMWARE_VMPROP_NAME */
ZBX_VMPROPMAP("summary.config.memorySizeMB"), /* ZBX_VMWARE_VMPROP_MEMORY_SIZE */
ZBX_VMPROPMAP("summary.quickStats.balloonedMemory"), /* ZBX_VMWARE_VMPROP_MEMORY_SIZE_BALLOONED */
ZBX_VMPROPMAP("summary.quickStats.compressedMemory"), /* ZBX_VMWARE_VMPROP_MEMORY_SIZE_COMPRESSED */
ZBX_VMPROPMAP("summary.quickStats.swappedMemory"), /* ZBX_VMWARE_VMPROP_MEMORY_SIZE_SWAPPED */
ZBX_VMPROPMAP("summary.quickStats.guestMemoryUsage"), /* ZBX_VMWARE_VMPROP_MEMORY_SIZE_USAGE_GUEST */
ZBX_VMPROPMAP("summary.quickStats.hostMemoryUsage"), /* ZBX_VMWARE_VMPROP_MEMORY_SIZE_USAGE_HOST */
ZBX_VMPROPMAP("summary.quickStats.privateMemory"), /* ZBX_VMWARE_VMPROP_MEMORY_SIZE_PRIVATE */
ZBX_VMPROPMAP("summary.quickStats.sharedMemory"), /* ZBX_VMWARE_VMPROP_MEMORY_SIZE_SHARED */
ZBX_VMPROPMAP("summary.runtime.powerState"), /* ZBX_VMWARE_VMPROP_POWER_STATE */
ZBX_VMPROPMAP("summary.storage.committed"), /* ZBX_VMWARE_VMPROP_STORAGE_COMMITED */
ZBX_VMPROPMAP("summary.storage.unshared"), /* ZBX_VMWARE_VMPROP_STORAGE_UNSHARED */
ZBX_VMPROPMAP("summary.storage.uncommitted"), /* ZBX_VMWARE_VMPROP_STORAGE_UNCOMMITTED */
ZBX_VMPROPMAP("summary.quickStats.uptimeSeconds"), /* ZBX_VMWARE_VMPROP_UPTIME */
ZBX_VMPROPMAP("guest.ipAddress"), /* ZBX_VMWARE_VMPROP_IPADDRESS */
ZBX_VMPROPMAP("guest.hostName"), /* ZBX_VMWARE_VMPROP_GUESTHOSTNAME */
ZBX_VMPROPMAP("guest.guestFamily"), /* ZBX_VMWARE_VMPROP_GUESTFAMILY */
ZBX_VMPROPMAP("guest.guestFullName"), /* ZBX_VMWARE_VMPROP_GUESTFULLNAME */
ZBX_VMPROPMAP("parent"), /* ZBX_VMWARE_VMPROP_FOLDER */
{"layoutExsnapshot", /* ZBX_VMWARE_VMPROP_SNAPSHOT */
ZBX_XPATH_PROP_OBJECT(ZBX_VMWARE_SOAP_VM) ZBX_XPATH_PROP_NAME_NODE("snapshot"),
vmware_service_get_vm_snapshot, 0},
{"datastore", ZBX_XPATH_PROP_OBJECT(ZBX_VMWARE_SOAP_VM)/* ZBX_VMWARE_VMPROP_DATASTOREID */
ZBX_XPATH_PROP_NAME_NODE("datastore") ZBX_XPATH_LN("ManagedObjectReference"), NULL, 0},
ZBX_VMPROPMAP("summary.runtime.consolidationNeeded"), /* ZBX_VMWARE_VMPROP_CONSOLIDATION_NEEDED */
ZBX_VMPROPMAP("resourcePool"), /* ZBX_VMWARE_VMPROP_RESOURCEPOOL */
ZBX_VMPROPMAP("guest.toolsVersion"), /* ZBX_VMWARE_VMPROP_TOOLS_VERSION */
ZBX_VMPROPMAP("guest.toolsRunningStatus"), /* ZBX_VMWARE_VMPROP_TOOLS_RUNNING_STATUS */
ZBX_VMPROPMAP("guest.guestState") /* ZBX_VMWARE_VMPROP_STATE */
};
#undef ZBX_VMPROPMAP
#define ZBX_XPATH_GET_OBJECT_NAME(object, id) \
ZBX_XPATH_PROP_OBJECT_ID(object, "[text()='" id "']") "/" \
ZBX_XPATH_PROP_NAME_NODE("name")
#define ZBX_XPATH_GET_FOLDER_NAME(id) \
ZBX_XPATH_GET_OBJECT_NAME(ZBX_VMWARE_SOAP_FOLDER, id)
#define ZBX_XPATH_GET_FOLDER_PARENTID(id) \
ZBX_XPATH_PROP_OBJECT_ID(ZBX_VMWARE_SOAP_FOLDER, "[text()='" id "']") "/" \
ZBX_XPATH_PROP_NAME_NODE("parent") "[@type='Folder']"
/******************************************************************************
* *
* Purpose: frees shared resources allocated to store virtual machine *
* *
* Parameters: vm - [IN] *
* *
******************************************************************************/
void vmware_vm_shared_free(zbx_vmware_vm_t *vm)
{
zbx_vector_vmware_dev_ptr_clear_ext(&vm->devs, vmware_shmem_dev_free);
zbx_vector_vmware_dev_ptr_destroy(&vm->devs);
zbx_vector_vmware_fs_ptr_clear_ext(&vm->file_systems, vmware_shmem_fs_free);
zbx_vector_vmware_fs_ptr_destroy(&vm->file_systems);
zbx_vector_vmware_custom_attr_ptr_clear_ext(&vm->custom_attrs, vmware_shmem_custom_attr_free);
zbx_vector_vmware_custom_attr_ptr_destroy(&vm->custom_attrs);
zbx_vector_str_clear_ext(&vm->alarm_ids, vmware_shared_strfree);
zbx_vector_str_destroy(&vm->alarm_ids);
if (NULL != vm->uuid)
vmware_shared_strfree(vm->uuid);
if (NULL != vm->id)
vmware_shared_strfree(vm->id);
vmware_shmem_props_free(vm->props, ZBX_VMWARE_VMPROPS_NUM);
vmware_shmem_vm_free(vm);
}
/******************************************************************************
* *
* Purpose: frees resources allocated to store vm device object *
* *
* Parameters: dev - [IN] vm device *
* *
******************************************************************************/
static void vmware_dev_free(zbx_vmware_dev_t *dev)
{
zbx_free(dev->instance);
zbx_free(dev->label);
vmware_props_free(dev->props, ZBX_VMWARE_DEV_PROPS_NUM);
zbx_free(dev);
}
/******************************************************************************
* *
* Purpose: frees resources allocated to store vm file system object *
* *
* Parameters: fs - [IN] file system *
* *
******************************************************************************/
static void vmware_fs_free(zbx_vmware_fs_t *fs)
{
zbx_free(fs->path);
zbx_free(fs);
}
/******************************************************************************
* *
* Purpose: frees resources allocated to store vm custom attributes *
* *
* Parameters: ca - [IN] custom attribute *
* *
******************************************************************************/
static void vmware_custom_attr_free(zbx_vmware_custom_attr_t *ca)
{
zbx_free(ca->name);
zbx_free(ca->value);
zbx_free(ca);
}
/******************************************************************************
* *
* Purpose: frees resources allocated to store virtual machine *
* *
* Parameters: vm - [IN] *
* *
******************************************************************************/
void vmware_vm_free(zbx_vmware_vm_t *vm)
{
zbx_vector_vmware_dev_ptr_clear_ext(&vm->devs, vmware_dev_free);
zbx_vector_vmware_dev_ptr_destroy(&vm->devs);
zbx_vector_vmware_fs_ptr_clear_ext(&vm->file_systems, vmware_fs_free);
zbx_vector_vmware_fs_ptr_destroy(&vm->file_systems);
zbx_vector_vmware_custom_attr_ptr_clear_ext(&vm->custom_attrs, vmware_custom_attr_free);
zbx_vector_vmware_custom_attr_ptr_destroy(&vm->custom_attrs);
zbx_vector_str_clear_ext(&vm->alarm_ids, zbx_str_free);
zbx_vector_str_destroy(&vm->alarm_ids);
zbx_free(vm->uuid);
zbx_free(vm->id);
vmware_props_free(vm->props, ZBX_VMWARE_VMPROPS_NUM);
zbx_free(vm);
}
/******************************************************************************
* *
* Purpose: gets list of ip for virtual machine network interface *
* *
* Parameters: details - [IN] xml document containing vm data *
* guestnet_node - [IN] xml node containing list of guest ips *
* mac_addr - [IN] mac address of network interface *
* *
* Return value: json with array of ip *
* *
******************************************************************************/
static char *vmware_vm_get_nic_device_ips(xmlDoc *details, xmlNode *guestnet_node, const char *mac_addr)
{
char xpath[VMWARE_SHORT_STR_LEN], *val = NULL;
zbx_vector_str_t ips;
zbx_vector_str_create(&ips);
zbx_snprintf(xpath, sizeof(xpath), "*[*[local-name()='macAddress']/text()='%s']" ZBX_XPATH_LN("ipAddress") ,
mac_addr);
if (SUCCEED == zbx_xml_node_read_values(details, guestnet_node, xpath, &ips))
{
struct zbx_json json_data;
zbx_json_initarray(&json_data, VMWARE_SHORT_STR_LEN);
for (int i = 0; i < ips.values_num; i++)
zbx_json_addstring(&json_data, NULL, ips.values[i], ZBX_JSON_TYPE_STRING);
zbx_json_close(&json_data);
val = zbx_strdup(val, json_data.buffer);
zbx_json_free(&json_data);
}
else
zabbix_log(LOG_LEVEL_DEBUG, "%s() empty list of guest ips for mac:%s", __func__, mac_addr);
zbx_vector_str_clear_ext(&ips, zbx_str_free);
zbx_vector_str_destroy(&ips);
return val;
}
/*********************************************************************************
* *
* Purpose: gets virtual machine network interface devices' additional *
* properties (props member of zbx_vmware_dev_t) *
* *
* Parameters: details - [IN] xml document containing virtual machine data *
* node - [IN] xml document node that corresponds to given *
* network interface device *
* guestnet_node - [IN] xml node containing list of guest ips *
* *
*********************************************************************************/
static char **vmware_vm_get_nic_device_props(xmlDoc *details, xmlNode *node, xmlNode *guestnet_node)
{
char **props;
props = (char **)zbx_malloc(NULL, sizeof(char *) * ZBX_VMWARE_DEV_PROPS_NUM);
props[ZBX_VMWARE_DEV_PROPS_IFMAC] = zbx_xml_node_read_value(details, node, ZBX_XNN("macAddress"));
props[ZBX_VMWARE_DEV_PROPS_IFCONNECTED] = zbx_xml_node_read_value(details, node,
ZBX_XNN("connectable") ZBX_XPATH_LN("connected"));
props[ZBX_VMWARE_DEV_PROPS_IFTYPE] = zbx_xml_node_read_prop(node, "type");
props[ZBX_VMWARE_DEV_PROPS_IFBACKINGDEVICE] = zbx_xml_node_read_value(details, node,
ZBX_XNN("backing") ZBX_XPATH_LN("deviceName"));
props[ZBX_VMWARE_DEV_PROPS_IFDVSWITCH_UUID] = zbx_xml_node_read_value(details, node,
ZBX_XNN("backing") ZBX_XPATH_LN2("port", "switchUuid"));
props[ZBX_VMWARE_DEV_PROPS_IFDVSWITCH_PORTGROUP] = zbx_xml_node_read_value(details, node,
ZBX_XNN("backing") ZBX_XPATH_LN2("port", "portgroupKey"));
props[ZBX_VMWARE_DEV_PROPS_IFDVSWITCH_PORT] = zbx_xml_node_read_value(details, node,
ZBX_XNN("backing") ZBX_XPATH_LN2("port", "portKey"));
props[ZBX_VMWARE_DEV_PROPS_IFIPS] = vmware_vm_get_nic_device_ips(details, guestnet_node,
props[ZBX_VMWARE_DEV_PROPS_IFMAC]);
return props;
}
/******************************************************************************
* *
* Purpose: gets virtual machine network interface devices *
* *
* Parameters: vm - [OUT] *
* details - [IN] xml document containing virtual machine data *
* *
* Comments: The network interface devices are taken from vm device list *
* filtered by macAddress key. *
* *
******************************************************************************/
static void vmware_vm_get_nic_devices(zbx_vmware_vm_t *vm, xmlDoc *details)
{
xmlXPathContext *xpathCtx;
xmlXPathObject *xpathObj;
xmlNodeSetPtr nodeset;
xmlNode *guestnet_node;
int nics = 0;
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
xpathCtx = xmlXPathNewContext(details);
if (NULL == (xpathObj = xmlXPathEvalExpression((const xmlChar *)ZBX_XPATH_VM_HARDWARE("device")
"[*[local-name()='macAddress']]", xpathCtx)))
{
goto clean;
}
if (0 != xmlXPathNodeSetIsEmpty(xpathObj->nodesetval))
goto clean;
nodeset = xpathObj->nodesetval;
guestnet_node = zbx_xml_doc_get(details, ZBX_XPATH_PROP_NAME("guest.net"));
zbx_vector_vmware_dev_ptr_reserve(&vm->devs, (size_t)(nodeset->nodeNr + vm->devs.values_alloc));
for (int i = 0; i < nodeset->nodeNr; i++)
{
char *key;
zbx_vmware_dev_t *dev;
if (NULL == (key = zbx_xml_node_read_value(details, nodeset->nodeTab[i], "*[local-name()='key']")))
continue;
dev = (zbx_vmware_dev_t *)zbx_malloc(NULL, sizeof(zbx_vmware_dev_t));
dev->type = ZBX_VMWARE_DEV_TYPE_NIC;
dev->instance = key;
dev->label = zbx_xml_node_read_value(details, nodeset->nodeTab[i],
"*[local-name()='deviceInfo']/*[local-name()='label']");
dev->props = vmware_vm_get_nic_device_props(details, nodeset->nodeTab[i], guestnet_node);
zbx_vector_vmware_dev_ptr_append(&vm->devs, dev);
nics++;
}
clean:
xmlXPathFreeObject(xpathObj);
xmlXPathFreeContext(xpathCtx);
zabbix_log(LOG_LEVEL_DEBUG, "End of %s() found:%d", __func__, nics);
}
/******************************************************************************
* *
* Purpose: gets virtual machine virtual disk devices *
* *
* Parameters: vm - [OUT] *
* details - [IN] xml document containing virtual machine data *
* *
******************************************************************************/
static void vmware_vm_get_disk_devices(zbx_vmware_vm_t *vm, xmlDoc *details)
{
xmlXPathContext *xpathCtx;
xmlXPathObject *xpathObj;
xmlNodeSetPtr nodeset;
int disks = 0;
char *xpath = NULL;
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
xpathCtx = xmlXPathNewContext(details);
/* select all hardware devices of VirtualDisk type */
if (NULL == (xpathObj = xmlXPathEvalExpression((const xmlChar *)ZBX_XPATH_VM_HARDWARE("device")
"[string(@*[local-name()='type'])='VirtualDisk']", xpathCtx)))
{
goto clean;
}
if (0 != xmlXPathNodeSetIsEmpty(xpathObj->nodesetval))
goto clean;
nodeset = xpathObj->nodesetval;
zbx_vector_vmware_dev_ptr_reserve(&vm->devs, (size_t)(nodeset->nodeNr + vm->devs.values_alloc));
for (int i = 0; i < nodeset->nodeNr; i++)
{
zbx_vmware_dev_t *dev;
char *unitNumber = NULL, *controllerKey = NULL, *busNumber = NULL,
*controllerLabel = NULL, *controllerType = NULL,
*scsiCtlrUnitNumber = NULL;
xmlXPathObject *xpathObjController = NULL;
do
{
if (NULL == (unitNumber = zbx_xml_node_read_value(details, nodeset->nodeTab[i],
"*[local-name()='unitNumber']")))
{
break;
}
if (NULL == (controllerKey = zbx_xml_node_read_value(details, nodeset->nodeTab[i],
"*[local-name()='controllerKey']")))
{
break;
}
/* find the controller (parent) device */
xpath = zbx_dsprintf(xpath, ZBX_XPATH_VM_HARDWARE("device")
"[*[local-name()='key']/text()='%s']", controllerKey);
if (NULL == (xpathObjController = xmlXPathEvalExpression((const xmlChar *)xpath, xpathCtx)))
break;
if (0 != xmlXPathNodeSetIsEmpty(xpathObjController->nodesetval))
break;
if (NULL == (busNumber = zbx_xml_node_read_value(details,
xpathObjController->nodesetval->nodeTab[0], "*[local-name()='busNumber']")))
{
break;
}
/* scsiCtlrUnitNumber property is simply used to determine controller type. */
/* For IDE controllers it is not set. */
scsiCtlrUnitNumber = zbx_xml_node_read_value(details, xpathObjController->nodesetval->nodeTab[0],
"*[local-name()='scsiCtlrUnitNumber']");
dev = (zbx_vmware_dev_t *)zbx_malloc(NULL, sizeof(zbx_vmware_dev_t));
dev->type = ZBX_VMWARE_DEV_TYPE_DISK;
dev->props = NULL;
/* the virtual disk instance has format : */
/* where controller type is either ide, sata or scsi depending on the controller type */
dev->label = zbx_xml_node_read_value(details, nodeset->nodeTab[i],
"*[local-name()='deviceInfo']/*[local-name()='label']");
controllerLabel = zbx_xml_node_read_value(details, xpathObjController->nodesetval->nodeTab[0],
"*[local-name()='deviceInfo']/*[local-name()='label']");
if (NULL != scsiCtlrUnitNumber ||
(NULL != controllerLabel && NULL != strstr(controllerLabel, "SCSI")))
{
controllerType = "scsi";
}
else if (NULL != controllerLabel && NULL != strstr(controllerLabel, "SATA"))
{
controllerType = "sata";
}
else
{
controllerType = "ide";
}
dev->instance = zbx_dsprintf(NULL, "%s%s:%s", controllerType, busNumber, unitNumber);
zbx_vector_vmware_dev_ptr_append(&vm->devs, dev);
disks++;
}
while (0);
xmlXPathFreeObject(xpathObjController);
zbx_free(controllerLabel);
zbx_free(scsiCtlrUnitNumber);
zbx_free(busNumber);
zbx_free(unitNumber);
zbx_free(controllerKey);
}
clean:
zbx_free(xpath);
if (NULL != xpathObj)
xmlXPathFreeObject(xpathObj);
xmlXPathFreeContext(xpathCtx);
zabbix_log(LOG_LEVEL_DEBUG, "End of %s() found:%d", __func__, disks);
}
/******************************************************************************
* *
* Purpose: gets parameters of virtual machine disks *
* *
* Parameters: vm - [OUT] *
* details - [IN] xml document containing virtual machine data *
* *
******************************************************************************/
static void vmware_vm_get_file_systems(zbx_vmware_vm_t *vm, xmlDoc *details)
{
# define ZBX_XPATH_VM_GUESTDISKS() \
"/*/*/*/*/*/*[local-name()='propSet'][*[local-name()='name'][text()='guest.disk']]" \
"/*/*[local-name()='GuestDiskInfo']"
xmlXPathContext *xpathCtx;
xmlXPathObject *xpathObj;
xmlNodeSetPtr nodeset;
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
xpathCtx = xmlXPathNewContext(details);
if (NULL == (xpathObj = xmlXPathEvalExpression((const xmlChar *)ZBX_XPATH_VM_GUESTDISKS(), xpathCtx)))
goto clean;
if (0 != xmlXPathNodeSetIsEmpty(xpathObj->nodesetval))
goto clean;
nodeset = xpathObj->nodesetval;
zbx_vector_vmware_fs_ptr_reserve(&vm->file_systems, (size_t)(nodeset->nodeNr + vm->file_systems.values_alloc));
for (int i = 0; i < nodeset->nodeNr; i++)
{
zbx_vmware_fs_t *fs;
char *value;
if (NULL == (value = zbx_xml_node_read_value(details, nodeset->nodeTab[i],
"*[local-name()='diskPath']")))
{
continue;
}
fs = (zbx_vmware_fs_t *)zbx_malloc(NULL, sizeof(zbx_vmware_fs_t));
memset(fs, 0, sizeof(zbx_vmware_fs_t));
fs->path = value;
if (NULL != (value = zbx_xml_node_read_value(details, nodeset->nodeTab[i],
"*[local-name()='capacity']")))
{
ZBX_STR2UINT64(fs->capacity, value);
zbx_free(value);
}
if (NULL != (value = zbx_xml_node_read_value(details, nodeset->nodeTab[i],
"*[local-name()='freeSpace']")))
{
ZBX_STR2UINT64(fs->free_space, value);
zbx_free(value);
}
zbx_vector_vmware_fs_ptr_append(&vm->file_systems, fs);
}
clean:
xmlXPathFreeObject(xpathObj);
xmlXPathFreeContext(xpathCtx);
zabbix_log(LOG_LEVEL_DEBUG, "End of %s() found:%d", __func__, vm->file_systems.values_num);
# undef ZBX_XPATH_VM_GUESTDISKS
}
/******************************************************************************
* *
* Purpose: gets custom attributes data of virtual machine *
* *
* Parameters: vm - [OUT] *
* details - [IN] xml document containing virtual machine data *
* *
******************************************************************************/
static void vmware_vm_get_custom_attrs(zbx_vmware_vm_t *vm, xmlDoc *details)
{
# define ZBX_XPATH_VM_CUSTOM_FIELD_VALUES() \
ZBX_XPATH_PROP_NAME("customValue") ZBX_XPATH_LN("CustomFieldValue")
xmlXPathContext *xpathCtx;
xmlXPathObject *xpathObj;
xmlNodeSetPtr nodeset;
xmlNode *node;
char *value;
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
xpathCtx = xmlXPathNewContext(details);
if (NULL == (xpathObj = xmlXPathEvalExpression((const xmlChar *)ZBX_XPATH_VM_CUSTOM_FIELD_VALUES(), xpathCtx)))
goto clean;
if (0 != xmlXPathNodeSetIsEmpty(xpathObj->nodesetval))
goto clean;
if (NULL == (node = zbx_xml_doc_get(details, ZBX_XPATH_PROP_NAME("availableField"))))
goto clean;
nodeset = xpathObj->nodesetval;
zbx_vector_vmware_custom_attr_ptr_reserve(&vm->custom_attrs, (size_t)nodeset->nodeNr);
for (int i = 0; i < nodeset->nodeNr; i++)
{
char xpath[MAX_STRING_LEN];
zbx_vmware_custom_attr_t *attr;
if (NULL == (value = zbx_xml_node_read_value(details, nodeset->nodeTab[i], ZBX_XNN("key"))))
continue;
zbx_snprintf(xpath, sizeof(xpath),
ZBX_XNN("CustomFieldDef") "[" ZBX_XNN("key") "=%s][1]/" ZBX_XNN("name"), value);
zbx_free(value);
if (NULL == (value = zbx_xml_node_read_value(details, node, xpath)))
continue;
attr = (zbx_vmware_custom_attr_t *)zbx_malloc(NULL, sizeof(zbx_vmware_custom_attr_t));
attr->name = value;
value = NULL;
if (NULL == (attr->value = zbx_xml_node_read_value(details, nodeset->nodeTab[i], ZBX_XNN("value"))))
attr->value = zbx_strdup(NULL, "");
zbx_vector_vmware_custom_attr_ptr_append(&vm->custom_attrs, attr);
}
zbx_vector_vmware_custom_attr_ptr_sort(&vm->custom_attrs, vmware_custom_attr_compare_name);
clean:
xmlXPathFreeObject(xpathObj);
xmlXPathFreeContext(xpathCtx);
zabbix_log(LOG_LEVEL_DEBUG, "End of %s() attributes:%d", __func__, vm->custom_attrs.values_num);
# undef ZBX_XPATH_VM_CUSTOM_FIELD_VALUES
}
/******************************************************************************
* *
* Purpose: gets virtual machine data *
* *
* Parameters: service - [IN] vmware service *
* easyhandle - [IN] CURL handle *
* vmid - [IN] virtual machine id *
* propmap - [IN] xpaths of properties to read *
* props_num - [IN] number of properties to read *
* cq_prop - [IN] soap part of query with cq property *
* xdoc - [OUT] reference to output xml document *
* error - [OUT] error message in case of failure *
* *
* Return value: SUCCEED - operation has completed successfully *
* FAIL - operation has failed *
* *
******************************************************************************/
static int vmware_service_get_vm_data(zbx_vmware_service_t *service, CURL *easyhandle, const char *vmid,
const zbx_vmware_propmap_t *propmap, int props_num, const char *cq_prop, xmlDoc **xdoc, char **error)
{
# define ZBX_POST_VMWARE_VM_STATUS_EX \
ZBX_POST_VSPHERE_HEADER \
"" \
"%s" \
"" \
"" \
"VirtualMachine" \
"config.hardware" \
"config.uuid" \
"config.instanceUuid"\
"guest.disk" \
"customValue" \
"availableField" \
"triggeredAlarmState"\
"guest.net" \
"%s" \
"%s" \
"" \
"" \
"Folder" \
"name" \
"parent" \
"" \
"" \
"%s" \
""\
"vm" \
"VirtualMachine" \
"parent" \
"false" \
"" \
"fl" \
"" \
"" \
""\
"fl" \
"Folder" \
"parent" \
"false" \
"" \
"fl" \
"" \
"" \
"" \
"" \
"" \
"" \
ZBX_POST_VSPHERE_FOOTER
char *tmp, props[ZBX_VMWARE_VMPROPS_NUM * 150], *vmid_esc;
int ret;
zabbix_log(LOG_LEVEL_DEBUG, "In %s() vmid:'%s'", __func__, vmid);
props[0] = '\0';
for (int i = 0; i < props_num; i++)
{
zbx_strscat(props, "");
zbx_strscat(props, propmap[i].name);
zbx_strscat(props, "");
}
vmid_esc = zbx_xml_escape_dyn(vmid);
tmp = zbx_dsprintf(NULL, ZBX_POST_VMWARE_VM_STATUS_EX,
get_vmware_service_objects()[service->type].property_collector, props, cq_prop, vmid_esc);
zbx_free(vmid_esc);
ret = zbx_soap_post(__func__, easyhandle, tmp, xdoc, NULL, error);
zbx_str_free(tmp);
zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
return ret;
# undef ZBX_POST_VMWARE_VM_STATUS_EX
}
/******************************************************************************
* *
* Purpose: converts vm folder id to chain of folder names divided by '/' *
* *
* Parameters: xdoc - [IN] xml with all vm details *
* vm_folder - [IN/OUT] vm property with folder id *
* *
* Return value: SUCCEED - operation has completed successfully *
* FAIL - operation has failed *
* *
******************************************************************************/
static int vmware_service_get_vm_folder(xmlDoc *xdoc, char **vm_folder)
{
char tmp[MAX_STRING_LEN], *id, *fl, *folder = NULL;
zabbix_log(LOG_LEVEL_DEBUG, "In %s() folder id:'%s'", __func__, *vm_folder);
id = zbx_strdup(NULL, *vm_folder);
do
{
char *id_esc;
id_esc = zbx_xml_escape_dyn(id);
zbx_free(id);
zbx_snprintf(tmp, sizeof(tmp), ZBX_XPATH_GET_FOLDER_NAME("%s"), id_esc);
if (NULL == (fl = zbx_xml_doc_read_value(xdoc , tmp)))
{
zbx_free(folder);
zbx_free(id_esc);
return FAIL;
}
zbx_snprintf(tmp, sizeof(tmp), ZBX_XPATH_GET_FOLDER_PARENTID("%s"), id_esc);
zbx_free(id_esc);
id = zbx_xml_doc_read_value(xdoc , tmp);
if (NULL == folder) /* we always resolve the first 'Folder' name */
{
folder = fl;
fl = NULL;
}
else if (NULL != id) /* we do not include the last default 'Folder' */
folder = zbx_dsprintf(folder, "%s/%s", fl, folder);
zbx_free(fl);
}
while (NULL != id);
zbx_free(*vm_folder);
*vm_folder = folder;
zabbix_log(LOG_LEVEL_DEBUG, "End of %s(): vm folder:%s", __func__, *vm_folder);
return SUCCEED;
}
/******************************************************************************
* *
* Purpose: collects info about snapshot disk size *
* *
* Parameters: xdoc - [IN] xml document with all details *
* key - [IN] id of snapshot disk *
* layout_node - [IN] xml node with snapshot disk info *
* sz - [OUT] size of snapshot disk *
* usz - [OUT] uniquesize of snapshot disk *
* *
******************************************************************************/
static void vmware_vm_snapshot_disksize(xmlDoc *xdoc, const char *key, xmlNode *layout_node, zbx_uint64_t *sz,
zbx_uint64_t *usz)
{
char *value, xpath[MAX_STRING_LEN];
zbx_snprintf(xpath, sizeof(xpath), ZBX_XNN("file") "[" ZBX_XNN("key") "='%s' and " ZBX_XNN("accessible")
"='true'][1]/" ZBX_XNN("size"), key);
if (NULL != (value = zbx_xml_node_read_value(xdoc, layout_node, xpath)))
{
if (SUCCEED != zbx_is_uint64(value, sz))
*sz = 0;
zbx_free(value);
}
else
{
zbx_snprintf(xpath, sizeof(xpath), ZBX_XNN("file") "[" ZBX_XNN("key") "='%s'][1]/" ZBX_XNN("size"),
key); /* snapshot version < 6 */
if (NULL != (value = zbx_xml_node_read_value(xdoc, layout_node, xpath)))
{
if (SUCCEED != zbx_is_uint64(value, sz))
*sz = 0;
zbx_free(value);
*usz = 0;
return;
}
*sz = 0;
}
zbx_snprintf(xpath, sizeof(xpath), ZBX_XNN("file") "[" ZBX_XNN("key") "='%s' and " ZBX_XNN("accessible")
"='true'][1]/" ZBX_XNN("uniqueSize"), key);
if (NULL != (value = zbx_xml_node_read_value(xdoc, layout_node, xpath)))
{
if (SUCCEED != zbx_is_uint64(value, usz))
*usz = 0;
zbx_free(value);
}
else
{
*usz = 0;
}
}
/******************************************************************************
* *
* Purpose: collects info about snapshots and creates json *
* *
* Parameters: xdoc - [IN] xml document with all details *
* snap_node - [IN] xml node with snapshot info *
* layout_node - [IN] xml node with snapshot disk info *
* disks_used - [IN/OUT] processed disk ids *
* size - [IN/OUT] total size of all snapshots *
* uniquesize - [IN/OUT] total uniquesize of all snapshots *
* count - [IN/OUT] total number of all snapshots *
* latestdate - [OUT] date of last snapshot *
* oldestdate - [OUT] date of oldest snapshot *
* json_data - [OUT] json with info about snapshot *
* *
* Return value: SUCCEED - operation has completed successfully *
* FAIL - operation has failed *
* *
******************************************************************************/
static int vmware_vm_snapshot_collect(xmlDoc *xdoc, xmlNode *snap_node, xmlNode *layout_node,
zbx_vector_uint64_t *disks_used, zbx_uint64_t *size, zbx_uint64_t *uniquesize, zbx_uint64_t *count,
char **latestdate, char **oldestdate, struct zbx_json *json_data)
{
int ret = FAIL;
char *value, xpath[MAX_STRING_LEN], *name, *desc, *crtime;
zbx_vector_str_t ids;
zbx_uint64_t snap_size, snap_usize;
xmlNode *next_node;
zabbix_log(LOG_LEVEL_DEBUG, "In %s() count:" ZBX_FS_UI64, __func__, *count);
zbx_vector_str_create(&ids);
if (NULL == (value = zbx_xml_node_read_value(xdoc, snap_node, ZBX_XNN("snapshot"))))
{
zabbix_log(LOG_LEVEL_WARNING, "%s() snapshot empty", __func__);
goto out;
}
zbx_snprintf(xpath, sizeof(xpath), ZBX_XNN("snapshot") "[" ZBX_XNN("key") "='%s'][1]" ZBX_XPATH_LN("disk")
ZBX_XPATH_LN("chain") ZBX_XPATH_LN("fileKey"), value);
if (FAIL == zbx_xml_node_read_values(xdoc, layout_node, xpath, &ids))
{
zabbix_log(LOG_LEVEL_WARNING, "%s() empty list of fileKey", __func__);
zbx_free(value);
goto out;
}
zbx_snprintf(xpath, sizeof(xpath), ZBX_XNN("snapshot") "[" ZBX_XNN("key") "='%s'][1]"
ZBX_XPATH_LN("dataKey"), value);
zbx_free(value);
if (NULL == (value = zbx_xml_node_read_value(xdoc, layout_node, xpath)))
{
zabbix_log(LOG_LEVEL_WARNING, "%s() dataKey empty", __func__);
goto out;
}
if (0 <= atoi(value))
{
vmware_vm_snapshot_disksize(xdoc, value, layout_node, &snap_size, &snap_usize);
}
else
{
snap_size = 0;
snap_usize = 0;
}
zbx_free(value);
for (int i = 0; i < ids.values_num; i++)
{
zbx_uint64_t dsize, dusize, disk_id = (unsigned int)atoi(ids.values[i]);
if (FAIL != zbx_vector_uint64_search(disks_used, disk_id, ZBX_DEFAULT_UINT64_COMPARE_FUNC))
continue;
zbx_vector_uint64_append(disks_used, disk_id);
vmware_vm_snapshot_disksize(xdoc, ids.values[i], layout_node, &dsize, &dusize);
snap_size += dsize;
snap_usize += dusize;
}
name = zbx_xml_node_read_value(xdoc, snap_node, ZBX_XNN("name"));
desc = zbx_xml_node_read_value(xdoc, snap_node, ZBX_XNN("description"));
crtime = zbx_xml_node_read_value(xdoc, snap_node, ZBX_XNN("createTime"));
zbx_json_addobject(json_data, NULL);
zbx_json_addstring(json_data, "name", ZBX_NULL2EMPTY_STR(name), ZBX_JSON_TYPE_STRING);
zbx_json_addstring(json_data, "description", ZBX_NULL2EMPTY_STR(desc), ZBX_JSON_TYPE_STRING);
zbx_json_addstring(json_data, "createtime", ZBX_NULL2EMPTY_STR(crtime), ZBX_JSON_TYPE_STRING);
zbx_json_adduint64(json_data, "size", snap_size);
zbx_json_adduint64(json_data, "uniquesize", snap_usize);
zbx_json_close(json_data);
if (NULL != oldestdate)
*oldestdate = zbx_strdup(NULL, crtime);
if (NULL != (next_node = zbx_xml_node_get(xdoc, snap_node, ZBX_XNN("childSnapshotList"))))
{
ret = vmware_vm_snapshot_collect(xdoc, next_node, layout_node, disks_used, size, uniquesize, count,
latestdate, NULL, json_data);
}
else
{
*latestdate = crtime;
crtime = NULL;
ret = SUCCEED;
}
*count += 1;
*size += snap_size;
*uniquesize += snap_usize;
zbx_free(name);
zbx_free(desc);
zbx_free(crtime);
out:
zbx_vector_str_clear_ext(&ids, zbx_str_free);
zbx_vector_str_destroy(&ids);
zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
return ret;
}
/******************************************************************************
* *
* Purpose: creates json with info about vm snapshots *
* *
* Parameters: xml_node - [IN] xml node with last vm snapshot *
* jstr - [OUT] json with vm snapshot info *
* *
******************************************************************************/
static int vmware_service_get_vm_snapshot(void *xml_node, char **jstr)
{
xmlNode *root_node, *layout_node, *node = (xmlNode *)xml_node;
xmlDoc *xdoc = node->doc;
struct zbx_json json_data;
int ret = FAIL;
char *latestdate = NULL, *oldestdate = NULL;
zbx_uint64_t count, size, uniquesize;
zbx_vector_uint64_t disks_used;
time_t xml_time, now = time(NULL), latest_age = 0, oldest_age = 0;
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
zbx_json_init(&json_data, ZBX_JSON_STAT_BUF_LEN);
zbx_vector_uint64_create(&disks_used);
if (NULL == (root_node = zbx_xml_node_get(xdoc, node, ZBX_XNN("rootSnapshotList"))))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s() rootSnapshotList empty", __func__);
goto out;
}
if (NULL == (layout_node = zbx_xml_doc_get(xdoc, ZBX_XPATH_PROP_NAME("layoutEx"))))
{
zabbix_log(LOG_LEVEL_WARNING, "%s() layoutEx empty", __func__);
goto out;
}
zbx_json_addarray(&json_data, "snapshot");
count = 0;
size = 0;
uniquesize = 0;
if (FAIL == (ret = vmware_vm_snapshot_collect(xdoc, root_node, layout_node, &disks_used, &size, &uniquesize,
&count, &latestdate, &oldestdate, &json_data)))
{
goto out;
}
if (SUCCEED == zbx_iso8601_utc(ZBX_NULL2EMPTY_STR(latestdate), &xml_time))
latest_age = now - xml_time;
if (SUCCEED == zbx_iso8601_utc(ZBX_NULL2EMPTY_STR(oldestdate), &xml_time))
oldest_age = now - xml_time;
zbx_json_close(&json_data);
zbx_json_adduint64(&json_data, "count", count);
zbx_json_addstring(&json_data, "latestdate", ZBX_NULL2EMPTY_STR(latestdate), ZBX_JSON_TYPE_STRING);
zbx_json_addint64(&json_data, "latestage", latest_age);
zbx_json_addstring(&json_data, "oldestdate", ZBX_NULL2EMPTY_STR(oldestdate), ZBX_JSON_TYPE_STRING);
zbx_json_addint64(&json_data, "oldestage", oldest_age);
zbx_json_adduint64(&json_data, "size", size);
zbx_json_adduint64(&json_data, "uniquesize", uniquesize);
zbx_json_close(&json_data);
*jstr = zbx_strdup(NULL, json_data.buffer);
out:
zbx_free(latestdate);
zbx_free(oldestdate);
zbx_vector_uint64_destroy(&disks_used);
zbx_json_free(&json_data);
zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, FAIL == ret ? zbx_result_string(ret) :
ZBX_NULL2EMPTY_STR(*jstr));
return ret;
}
/******************************************************************************
* *
* Purpose: creates virtual machine object *
* *
* Parameters: service - [IN] vmware service *
* easyhandle - [IN] CURL handle *
* id - [IN] virtual machine id *
* rpools - [IN/OUT] vector with all Resource Pools *
* cq_values - [IN/OUT] vector with custom query entries *
* alarms_data - [IN/OUT] all alarms with cache *
* error - [OUT] error message in case of failure *
* *
* Return value: The created virtual machine object or NULL if an error was *
* detected. *
* *
******************************************************************************/
zbx_vmware_vm_t *vmware_service_create_vm(zbx_vmware_service_t *service, CURL *easyhandle,
const char *id, zbx_vector_vmware_resourcepool_ptr_t *rpools, zbx_vector_cq_value_ptr_t *cq_values,
zbx_vmware_alarms_data_t *alarms_data, char **error)
{
# define ZBX_XPATH_VM_UUID() \
"/*/*/*/*/*/*[local-name()='propSet'][*[local-name()='name'][text()='config.uuid']]" \
"/*[local-name()='val']"
# define ZBX_XPATH_VM_INSTANCE_UUID() \
"/*/*/*/*/*/*[local-name()='propSet'][*[local-name()='name'][text()='config.instanceUuid']]" \
"/*[local-name()='val']"
zbx_vmware_vm_t *vm;
char *value, *cq_prop;
xmlDoc *details = NULL;
zbx_vector_cq_value_ptr_t cqvs;
const char *uuid_xpath[3] = {NULL, ZBX_XPATH_VM_UUID(), ZBX_XPATH_VM_INSTANCE_UUID()};
int ret;
zabbix_log(LOG_LEVEL_DEBUG, "In %s() vmid:'%s'", __func__, id);
vm = (zbx_vmware_vm_t *)zbx_malloc(NULL, sizeof(zbx_vmware_vm_t));
memset(vm, 0, sizeof(zbx_vmware_vm_t));
zbx_vector_vmware_dev_ptr_create(&vm->devs);
zbx_vector_vmware_fs_ptr_create(&vm->file_systems);
zbx_vector_vmware_custom_attr_ptr_create(&vm->custom_attrs);
zbx_vector_cq_value_ptr_create(&cqvs);
cq_prop = vmware_cq_prop_soap_request(cq_values, ZBX_VMWARE_SOAP_VM, id, &cqvs);
ret = vmware_service_get_vm_data(service, easyhandle, id, vm_propmap, ZBX_VMWARE_VMPROPS_NUM, cq_prop,
&details, error);
zbx_str_free(cq_prop);
if (FAIL == ret)
goto out;
if (NULL == (value = zbx_xml_doc_read_value(details, uuid_xpath[service->type])))
{
ret = FAIL;
goto out;
}
vm->uuid = value;
vm->id = zbx_strdup(NULL, id);
if (NULL == (vm->props = xml_read_props(details, vm_propmap, ZBX_VMWARE_VMPROPS_NUM)))
goto out;
if (NULL != vm->props[ZBX_VMWARE_VMPROP_FOLDER] &&
SUCCEED != vmware_service_get_vm_folder(details, &vm->props[ZBX_VMWARE_VMPROP_FOLDER]))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot find vm folder name for id:%s", __func__,
vm->props[ZBX_VMWARE_VMPROP_FOLDER]);
}
if (NULL != vm->props[ZBX_VMWARE_VMPROP_SNAPSHOT])
{
struct zbx_json_parse jp;
char count[ZBX_MAX_UINT64_LEN];
if (SUCCEED == zbx_json_open(vm->props[ZBX_VMWARE_VMPROP_SNAPSHOT], &jp) &&
SUCCEED == zbx_json_value_by_name(&jp, "count", count, sizeof(count), NULL))
{
vm->snapshot_count = (unsigned int)atoi(count);
}
}
else
{
vm->props[ZBX_VMWARE_VMPROP_SNAPSHOT] = zbx_strdup(NULL, "{\"snapshot\":[],\"count\":0,"
"\"latestdate\":null,\"latestage\":0,\"oldestdate\":null,\"oldestage\":0,"
"\"size\":0,\"uniquesize\":0}");
}
if (NULL != vm->props[ZBX_VMWARE_VMPROP_RESOURCEPOOL])
{
int i;
zbx_vmware_resourcepool_t rpool_cmp;
rpool_cmp.id = vm->props[ZBX_VMWARE_VMPROP_RESOURCEPOOL];
if (FAIL != (i = zbx_vector_vmware_resourcepool_ptr_bsearch(rpools, &rpool_cmp,
ZBX_DEFAULT_STR_PTR_COMPARE_FUNC)))
{
rpools->values[i]->vm_num += 1;
}
}
vmware_vm_get_nic_devices(vm, details);
vmware_vm_get_disk_devices(vm, details);
vmware_vm_get_file_systems(vm, details);
vmware_vm_get_custom_attrs(vm, details);
if (0 != cqvs.values_num)
vmware_service_cq_prop_value(__func__, details, &cqvs);
zbx_vector_str_create(&vm->alarm_ids);
ret = vmware_service_get_alarms_data(__func__, service, easyhandle, details, NULL, &vm->alarm_ids, alarms_data,
error);
out:
zbx_vector_cq_value_ptr_destroy(&cqvs);
zbx_xml_doc_free(details);
if (SUCCEED != ret)
{
vmware_vm_free(vm);
vm = NULL;
}
zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
return vm;
# undef ZBX_XPATH_VM_UUID
# undef ZBX_XPATH_VM_INSTANCE_UUID
}
#endif /* defined(HAVE_LIBXML2) && defined(HAVE_LIBCURL) */