#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright: (c) 2020, sky-joker # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = r''' --- module: vmware_host_logbundle short_description: Fetch logbundle file from ESXi description: - This module can be used to fetch logbundle file from ESXi. author: - sky-joker (@sky-joker) options: esxi_hostname: description: - Name of the host system to fetch the logbundle. type: str required: true dest: description: - file destination on localhost, path must be exist. type: str required: true manifests: description: - Logs to include in the logbundle file. - Refer to the id key of the M(community.vmware.vmware_host_logbundle_info) module for values that can be specified in the manifest. default: - System:Base - System:CoreDumps - System:EsxImage - System:IOFilter - System:LoadESX - System:Modules - System:RDMA - System:ResourceGroups - System:TPM - System:VFlash - System:VMTools - System:VmiofPlugins - System:ntp - System:uwstats - Fcd:Catalog - VirtualMachines:CoreDumps - VirtualMachines:VirtualMachineStats - VirtualMachines:base - VirtualMachines:base - VirtualMachines:diskinfo - VirtualMachines:logs - Storage:FCoE - Storage:Multipathing - Storage:NAS - Storage:VSAN - Storage:VSANHealth - Storage:VSANIscsiTarget - Storage:VSANPerfStats - Storage:VSANPerfSvc - Storage:VSANTraces - Storage:VVOL - Storage:base - Storage:iodm - Storage:iscsi - FeatureStateSwitch:FeatureStateSwitch - Userworld:HostAgent - Userworld:ProcessInformation - Configuration:System - Logs:System - hostProfiles:SystemImageCacheHostProfile - hostProfiles:hostProfiles - FileSystem:VMFSDiskDump - FileSystem:base - ActiveDirectory:base - CIM:base - Hardware:base - Hardware:usb - Installer:base - Network:base - Network:dvs - Network:lacp - Network:nscd - Network:tcpip - IntegrityChecks:md5sums type: list elements: str required: false performance_data: description: - Gather performance data for ESXi. type: dict required: false suboptions: duration: description: - Duration for which performance data is gathered. type: int default: 300 interval: description: - Interval for which performance data is gathered. type: int default: 5 extends_documentation_fragment: - community.vmware.vmware.documentation ''' EXAMPLES = r''' - name: fetch logbundle file from ESXi community.vmware.vmware_host_logbundle: hostname: "{{ vcenter_hostname }}" username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" esxi_hostname: "{{ esxi_hostname }}" dest: ./esxi-log.tgz - name: fetch logbundle file from ESXi with manifests community.vmware.vmware_host_logbundle: hostname: "{{ vcenter_hostname }}" username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" esxi_hostname: "{{ esxi_hostname }}" dest: ./esxi-log.tgz manifests: - System:Base - VirtualMachines:VirtualMachineStats ''' RETURN = r''' dest: description: saved path of a logbundle file for ESXi returned: on success type: str sample: { "changed": true, "dest": "./esxi-log.tgz", "failed": false, "gid": 0, "group": "root", "mode": "0644", "owner": "root", "size": 25783140, "state": "file", "uid": 0 } ''' try: from pyVmomi import vim except ImportError: pass import xml.etree.ElementTree as ET from ansible_collections.community.vmware.plugins.module_utils.vmware import PyVmomi, vmware_argument_spec from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.urls import fetch_url class VMwareHostLogbundle(PyVmomi): def __init__(self, module): super(VMwareHostLogbundle, self).__init__(module) self.esxi_hostname = self.params['esxi_hostname'] self.dest = self.params['dest'] self.manifests = self.params['manifests'] self.performance_data = self.params['performance_data'] if not self.dest.endswith('.tgz'): self.dest = self.dest + '.tgz' def generate_req_headers(self, url): # get ticket req = vim.SessionManager.HttpServiceRequestSpec(method='httpGet', url=url) ticket = self.content.sessionManager.AcquireGenericServiceTicket(req) headers = { 'Content-Type': 'application/octet-stream', 'Cookie': 'vmware_cgi_ticket=%s' % ticket.id } return headers def validate_manifests(self): url = 'https://' + self.esxi_hostname + '/cgi-bin/vm-support.cgi?listmanifests=1' headers = self.generate_req_headers(url) manifests = [] try: resp, info = fetch_url(self.module, method='GET', headers=headers, url=url) if info['status'] != 200: self.module.fail_json(msg="failed to fetch manifests from %s: %s" % (url, info['msg'])) manifest_list = ET.fromstring(resp.read()) for manifest in manifest_list[0]: manifests.append(manifest.attrib['id']) except Exception as e: self.module.fail_json(msg="Failed to fetch manifests from %s: %s" % (url, e)) for manifest in self.manifests: validate_manifest_result = [m for m in manifests if m == manifest] if not validate_manifest_result: self.module.fail_json(msg="%s is a manifest that cannot be specified." % manifest) def get_logbundle(self): self.validate_manifests() url = 'https://' + self.esxi_hostname + '/cgi-bin/vm-support.cgi?manifests=' + '&'.join(self.manifests) if self.performance_data: duration = self.performance_data.get('duration') interval = self.performance_data.get('interval') url = url + '&performance=true&duration=%s&interval=%s' % (duration, interval) headers = self.generate_req_headers(url) try: resp, info = fetch_url(self.module, method='GET', headers=headers, url=url) if info['status'] != 200: self.module.fail_json(msg="failed to fetch logbundle from %s: %s" % (url, info['msg'])) with open(self.dest, 'wb') as local_file: local_file.write(resp.read()) except Exception as e: self.module.fail_json(msg="Failed to fetch logbundle from %s: %s" % (url, e)) self.module.exit_json(changed=True, dest=self.dest) def main(): argument_spec = vmware_argument_spec() argument_spec.update( esxi_hostname=dict(type='str', required=True), dest=dict(type='str', required=True), manifests=dict(type='list', elements='str', default=['System:Base', 'System:CoreDumps', 'System:EsxImage', 'System:IOFilter', 'System:LoadESX', 'System:Modules', 'System:RDMA', 'System:ResourceGroups', 'System:TPM', 'System:VFlash', 'System:VMTools', 'System:VmiofPlugins', 'System:ntp', 'System:uwstats', 'Fcd:Catalog', 'VirtualMachines:CoreDumps', 'VirtualMachines:VirtualMachineStats', 'VirtualMachines:base', 'VirtualMachines:base', 'VirtualMachines:diskinfo', 'VirtualMachines:logs', 'Storage:FCoE', 'Storage:Multipathing', 'Storage:NAS', 'Storage:VSAN', 'Storage:VSANHealth', 'Storage:VSANIscsiTarget', 'Storage:VSANPerfStats', 'Storage:VSANPerfSvc', 'Storage:VSANTraces', 'Storage:VVOL', 'Storage:base', 'Storage:iodm', 'Storage:iscsi', 'FeatureStateSwitch:FeatureStateSwitch', 'Userworld:HostAgent', 'Userworld:ProcessInformation', 'Configuration:System', 'Logs:System', 'hostProfiles:SystemImageCacheHostProfile', 'hostProfiles:hostProfiles', 'FileSystem:VMFSDiskDump', 'FileSystem:base', 'ActiveDirectory:base', 'CIM:base', 'Hardware:base', 'Hardware:usb', 'Installer:base', 'Network:base', 'Network:dvs', 'Network:lacp', 'Network:nscd', 'Network:tcpip', 'IntegrityChecks:md5sums']), performance_data=dict(type='dict', required=False, options=dict( duration=dict(type='int', default=300), interval=dict(type='int', default=5) )) ) module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) vmware_host_logbundle_mgr = VMwareHostLogbundle(module) vmware_host_logbundle_mgr.get_logbundle() if __name__ == "__main__": main()