/* ** 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 <https://www.gnu.org/licenses/>. **/ #include "config.h" #include "vmware_vm.h" #include "vmware_shmem.h" #include "zbxtime.h" #include "zbxstr.h" #include "zbxxml.h" #ifdef HAVE_LIBXML2 # include <libxml/xpath.h> #endif #include "zbxalgo.h" #include "zbxjson.h" #include "zbxnum.h" #if defined(HAVE_LIBXML2) && defined(HAVE_LIBCURL) #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 */ {"layoutEx</ns0:pathSet><ns0:pathSet>snapshot", /* 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 <controller type><busNumber>:<unitNumber> */ /* 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 \ "<ns0:RetrievePropertiesEx>" \ "<ns0:_this type=\"PropertyCollector\">%s</ns0:_this>" \ "<ns0:specSet>" \ "<ns0:propSet>" \ "<ns0:type>VirtualMachine</ns0:type>" \ "<ns0:pathSet>config.hardware</ns0:pathSet>" \ "<ns0:pathSet>config.uuid</ns0:pathSet>" \ "<ns0:pathSet>config.instanceUuid</ns0:pathSet>"\ "<ns0:pathSet>guest.disk</ns0:pathSet>" \ "<ns0:pathSet>customValue</ns0:pathSet>" \ "<ns0:pathSet>availableField</ns0:pathSet>" \ "<ns0:pathSet>triggeredAlarmState</ns0:pathSet>"\ "<ns0:pathSet>guest.net</ns0:pathSet>" \ "%s" \ "%s" \ "</ns0:propSet>" \ "<ns0:propSet>" \ "<ns0:type>Folder</ns0:type>" \ "<ns0:pathSet>name</ns0:pathSet>" \ "<ns0:pathSet>parent</ns0:pathSet>" \ "</ns0:propSet>" \ "<ns0:objectSet>" \ "<ns0:obj type=\"VirtualMachine\">%s</ns0:obj>" \ "<ns0:selectSet xsi:type=\"ns0:TraversalSpec\">"\ "<ns0:name>vm</ns0:name>" \ "<ns0:type>VirtualMachine</ns0:type>" \ "<ns0:path>parent</ns0:path>" \ "<ns0:skip>false</ns0:skip>" \ "<ns0:selectSet>" \ "<ns0:name>fl</ns0:name>" \ "</ns0:selectSet>" \ "</ns0:selectSet>" \ "<ns0:selectSet xsi:type=\"ns0:TraversalSpec\">"\ "<ns0:name>fl</ns0:name>" \ "<ns0:type>Folder</ns0:type>" \ "<ns0:path>parent</ns0:path>" \ "<ns0:skip>false</ns0:skip>" \ "<ns0:selectSet>" \ "<ns0:name>fl</ns0:name>" \ "</ns0:selectSet>" \ "</ns0:selectSet>" \ "</ns0:objectSet>" \ "</ns0:specSet>" \ "<ns0:options/>" \ "</ns0:RetrievePropertiesEx>" \ 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, "<ns0:pathSet>"); zbx_strscat(props, propmap[i].name); zbx_strscat(props, "</ns0:pathSet>"); } 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) */