mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Add ability to get vmware_guest_facts using vsphere schema output (#47446)
This commit is contained in:
parent
7f50f467fe
commit
f2495ef0d3
3 changed files with 188 additions and 23 deletions
|
@ -1,12 +1,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright: (c) 2015, Joseph Callen <jcallen () csc.com>
|
# Copyright: (c) 2015, Joseph Callen <jcallen () csc.com>
|
||||||
# Copyright: (c) 2018, Ansible Project
|
# Copyright: (c) 2018, Ansible Project
|
||||||
|
# Copyright: (c) 2018, James E. King III (@jeking3) <jking@apache.org>
|
||||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
import atexit
|
import atexit
|
||||||
|
import ansible.module_utils.common._collections_compat as collections_compat
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import ssl
|
import ssl
|
||||||
|
@ -27,11 +30,13 @@ except ImportError:
|
||||||
PYVMOMI_IMP_ERR = None
|
PYVMOMI_IMP_ERR = None
|
||||||
try:
|
try:
|
||||||
from pyVim import connect
|
from pyVim import connect
|
||||||
from pyVmomi import vim, vmodl
|
from pyVmomi import vim, vmodl, VmomiSupport
|
||||||
HAS_PYVMOMI = True
|
HAS_PYVMOMI = True
|
||||||
|
HAS_PYVMOMIJSON = hasattr(VmomiSupport, 'VmomiJSONEncoder')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
PYVMOMI_IMP_ERR = traceback.format_exc()
|
PYVMOMI_IMP_ERR = traceback.format_exc()
|
||||||
HAS_PYVMOMI = False
|
HAS_PYVMOMI = False
|
||||||
|
HAS_PYVMOMIJSON = False
|
||||||
|
|
||||||
from ansible.module_utils._text import to_text, to_native
|
from ansible.module_utils._text import to_text, to_native
|
||||||
from ansible.module_utils.six import integer_types, iteritems, string_types, raise_from
|
from ansible.module_utils.six import integer_types, iteritems, string_types, raise_from
|
||||||
|
@ -1274,3 +1279,104 @@ class PyVmomi(object):
|
||||||
return f
|
return f
|
||||||
|
|
||||||
self.module.fail_json(msg="No vmdk file found for path specified [%s]" % vmdk_path)
|
self.module.fail_json(msg="No vmdk file found for path specified [%s]" % vmdk_path)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Conversion to JSON
|
||||||
|
#
|
||||||
|
|
||||||
|
def _deepmerge(self, d, u):
|
||||||
|
"""
|
||||||
|
Deep merges u into d.
|
||||||
|
|
||||||
|
Credit:
|
||||||
|
https://bit.ly/2EDOs1B (stackoverflow question 3232943)
|
||||||
|
License:
|
||||||
|
cc-by-sa 3.0 (https://creativecommons.org/licenses/by-sa/3.0/)
|
||||||
|
Changes:
|
||||||
|
using collections_compat for compatibility
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- d (dict): dict to merge into
|
||||||
|
- u (dict): dict to merge into d
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict, with u merged into d
|
||||||
|
"""
|
||||||
|
for k, v in iteritems(u):
|
||||||
|
if isinstance(v, collections_compat.Mapping):
|
||||||
|
d[k] = self._deepmerge(d.get(k, {}), v)
|
||||||
|
else:
|
||||||
|
d[k] = v
|
||||||
|
return d
|
||||||
|
|
||||||
|
def _extract(self, data, remainder):
|
||||||
|
"""
|
||||||
|
This is used to break down dotted properties for extraction.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- data (dict): result of _jsonify on a property
|
||||||
|
- remainder: the remainder of the dotted property to select
|
||||||
|
|
||||||
|
Return:
|
||||||
|
dict
|
||||||
|
"""
|
||||||
|
result = dict()
|
||||||
|
if '.' not in remainder:
|
||||||
|
result[remainder] = data[remainder]
|
||||||
|
return result
|
||||||
|
key, remainder = remainder.split('.', 1)
|
||||||
|
result[key] = self._extract(data[key], remainder)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _jsonify(self, obj):
|
||||||
|
"""
|
||||||
|
Convert an object from pyVmomi into JSON.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- obj (object): vim object
|
||||||
|
|
||||||
|
Return:
|
||||||
|
dict
|
||||||
|
"""
|
||||||
|
return json.loads(json.dumps(obj, cls=VmomiSupport.VmomiJSONEncoder,
|
||||||
|
sort_keys=True, strip_dynamic=True))
|
||||||
|
|
||||||
|
def to_json(self, obj, properties=None):
|
||||||
|
"""
|
||||||
|
Convert a vSphere (pyVmomi) Object into JSON. This is a deep
|
||||||
|
transformation. The list of properties is optional - if not
|
||||||
|
provided then all properties are deeply converted. The resulting
|
||||||
|
JSON is sorted to improve human readability.
|
||||||
|
|
||||||
|
Requires upstream support from pyVmomi > 6.7.1
|
||||||
|
(https://github.com/vmware/pyvmomi/pull/732)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- obj (object): vim object
|
||||||
|
- properties (list, optional): list of properties following
|
||||||
|
the property collector specification, for example:
|
||||||
|
["config.hardware.memoryMB", "name", "overallStatus"]
|
||||||
|
default is a complete object dump, which can be large
|
||||||
|
|
||||||
|
Return:
|
||||||
|
dict
|
||||||
|
"""
|
||||||
|
if not HAS_PYVMOMIJSON:
|
||||||
|
self.module.fail_json(msg='The installed version of pyvmomi lacks JSON output support; need pyvmomi>6.7.1')
|
||||||
|
|
||||||
|
result = dict()
|
||||||
|
if properties:
|
||||||
|
for prop in properties:
|
||||||
|
try:
|
||||||
|
if '.' in prop:
|
||||||
|
key, remainder = prop.split('.', 1)
|
||||||
|
tmp = dict()
|
||||||
|
tmp[key] = self._extract(self._jsonify(getattr(obj, key)), remainder)
|
||||||
|
self._deepmerge(result, tmp)
|
||||||
|
else:
|
||||||
|
result[prop] = self._jsonify(getattr(obj, prop))
|
||||||
|
except (AttributeError, KeyError):
|
||||||
|
self.module.fail_json(msg="Property '{0}' not found.".format(prop))
|
||||||
|
else:
|
||||||
|
result = self._jsonify(obj)
|
||||||
|
return result
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# This module is also sponsored by E.T.A.I. (www.etai.fr)
|
# This module is also sponsored by E.T.A.I. (www.etai.fr)
|
||||||
|
# Copyright (C) 2018 James E. King III (@jeking3) <jking@apache.org>
|
||||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
|
@ -25,7 +26,7 @@ version_added: 2.3
|
||||||
author:
|
author:
|
||||||
- Loic Blot (@nerzhul) <loic.blot@unix-experience.fr>
|
- Loic Blot (@nerzhul) <loic.blot@unix-experience.fr>
|
||||||
notes:
|
notes:
|
||||||
- Tested on vSphere 5.5
|
- Tested on vSphere 5.5, 6.7
|
||||||
requirements:
|
requirements:
|
||||||
- "python >= 2.6"
|
- "python >= 2.6"
|
||||||
- PyVmomi
|
- PyVmomi
|
||||||
|
@ -79,6 +80,32 @@ options:
|
||||||
default: 'no'
|
default: 'no'
|
||||||
type: bool
|
type: bool
|
||||||
version_added: '2.8'
|
version_added: '2.8'
|
||||||
|
schema:
|
||||||
|
description:
|
||||||
|
- Specify the output schema desired.
|
||||||
|
- The 'summary' output schema is the legacy output from the module
|
||||||
|
- The 'vsphere' output schema is the vSphere API class definition
|
||||||
|
which requires pyvmomi>6.7.1
|
||||||
|
choices: ['summary', 'vsphere']
|
||||||
|
default: 'summary'
|
||||||
|
type: str
|
||||||
|
version_added: '2.8'
|
||||||
|
properties:
|
||||||
|
description:
|
||||||
|
- Specify the properties to retrieve.
|
||||||
|
- If not specified, all properties are retrieved (deeply).
|
||||||
|
- Results are returned in a structure identical to the vsphere API.
|
||||||
|
- 'Example:'
|
||||||
|
- ' properties: ['
|
||||||
|
- ' "config.hardware.memoryMB",'
|
||||||
|
- ' "config.hardware.numCPU",'
|
||||||
|
- ' "guest.disk",'
|
||||||
|
- ' "overallStatus"'
|
||||||
|
- ' ]'
|
||||||
|
- Only valid when C(schema) is C(vsphere).
|
||||||
|
type: list
|
||||||
|
required: False
|
||||||
|
version_added: '2.8'
|
||||||
extends_documentation_fragment: vmware.documentation
|
extends_documentation_fragment: vmware.documentation
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@ -93,6 +120,19 @@ EXAMPLES = '''
|
||||||
uuid: 421e4592-c069-924d-ce20-7e7533fab926
|
uuid: 421e4592-c069-924d-ce20-7e7533fab926
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
register: facts
|
register: facts
|
||||||
|
|
||||||
|
- name: Gather some facts from a guest using the vSphere API output schema
|
||||||
|
vmware_guest_facts:
|
||||||
|
hostname: "{{ vcenter_hostname }}"
|
||||||
|
username: "{{ vcenter_username }}"
|
||||||
|
password: "{{ vcenter_password }}"
|
||||||
|
validate_certs: no
|
||||||
|
datacenter: "{{ datacenter_name }}"
|
||||||
|
name: "{{ vm_name }}"
|
||||||
|
schema: "vsphere"
|
||||||
|
properties: ["config.hardware.memoryMB", "guest.disk", "overallStatus"]
|
||||||
|
delegate_to: localhost
|
||||||
|
register: facts
|
||||||
'''
|
'''
|
||||||
|
|
||||||
RETURN = """
|
RETURN = """
|
||||||
|
@ -168,11 +208,6 @@ except ImportError:
|
||||||
HAS_VCLOUD = False
|
HAS_VCLOUD = False
|
||||||
|
|
||||||
|
|
||||||
class PyVmomiHelper(PyVmomi):
|
|
||||||
def __init__(self, module):
|
|
||||||
super(PyVmomiHelper, self).__init__(module)
|
|
||||||
|
|
||||||
|
|
||||||
class VmwareTag(VmwareRestClient):
|
class VmwareTag(VmwareRestClient):
|
||||||
def __init__(self, module):
|
def __init__(self, module):
|
||||||
super(VmwareTag, self).__init__(module)
|
super(VmwareTag, self).__init__(module)
|
||||||
|
@ -189,7 +224,9 @@ def main():
|
||||||
use_instance_uuid=dict(type='bool', default=False),
|
use_instance_uuid=dict(type='bool', default=False),
|
||||||
folder=dict(type='str'),
|
folder=dict(type='str'),
|
||||||
datacenter=dict(type='str', required=True),
|
datacenter=dict(type='str', required=True),
|
||||||
tags=dict(type='bool', default=False)
|
tags=dict(type='bool', default=False),
|
||||||
|
schema=dict(type='str', choices=['summary', 'vsphere'], default='summary'),
|
||||||
|
properties=dict(type='list')
|
||||||
)
|
)
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
required_one_of=[['name', 'uuid']])
|
required_one_of=[['name', 'uuid']])
|
||||||
|
@ -199,14 +236,21 @@ def main():
|
||||||
# so we should leave the input folder path unmodified
|
# so we should leave the input folder path unmodified
|
||||||
module.params['folder'] = module.params['folder'].rstrip('/')
|
module.params['folder'] = module.params['folder'].rstrip('/')
|
||||||
|
|
||||||
pyv = PyVmomiHelper(module)
|
if module.params['schema'] != 'vsphere' and module.params.get('properties'):
|
||||||
|
module.fail_json(msg="The option 'properties' is only valid when the schema is 'vsphere'")
|
||||||
|
|
||||||
|
pyv = PyVmomi(module)
|
||||||
# Check if the VM exists before continuing
|
# Check if the VM exists before continuing
|
||||||
vm = pyv.get_vm()
|
vm = pyv.get_vm()
|
||||||
|
|
||||||
# VM already exists
|
# VM already exists
|
||||||
if vm:
|
if vm:
|
||||||
try:
|
try:
|
||||||
instance = pyv.gather_facts(vm)
|
if module.params['schema'] == 'summary':
|
||||||
|
instance = pyv.gather_facts(vm)
|
||||||
|
else:
|
||||||
|
instance = pyv.to_json(vm, module.params['properties'])
|
||||||
|
|
||||||
if module.params.get('tags'):
|
if module.params.get('tags'):
|
||||||
if not HAS_VCLOUD:
|
if not HAS_VCLOUD:
|
||||||
module.fail_json(msg="Unable to find 'vCloud Suite SDK' Python library which is required."
|
module.fail_json(msg="Unable to find 'vCloud Suite SDK' Python library which is required."
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
# Test code for the vmware_guest_facts module.
|
# Test code for the vmware_guest_facts module.
|
||||||
# Copyright: (c) 2017, Abhijeet Kasurde <akasurde@redhat.com>
|
# Copyright: (c) 2017, Abhijeet Kasurde <akasurde@redhat.com>
|
||||||
|
# Copyright: (c) 2018, James E. King III (@jeking3) <jking@apache.org>
|
||||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
---
|
||||||
- name: store the vcenter container ip
|
- name: store the vcenter container ip
|
||||||
set_fact:
|
set_fact:
|
||||||
vcsim: "{{ lookup('env', 'vcenter_host') }}"
|
vcsim: "{{ lookup('env', 'vcenter_host') }}"
|
||||||
|
@ -62,7 +63,7 @@
|
||||||
folder: "{{ vm1 | dirname }}"
|
folder: "{{ vm1 | dirname }}"
|
||||||
register: guest_facts_0001
|
register: guest_facts_0001
|
||||||
|
|
||||||
- debug: msg="{{ guest_facts_0001 }}"
|
- debug: var=guest_facts_0001
|
||||||
|
|
||||||
- assert:
|
- assert:
|
||||||
that:
|
that:
|
||||||
|
@ -98,7 +99,25 @@
|
||||||
uuid: "{{ vm1_uuid }}"
|
uuid: "{{ vm1_uuid }}"
|
||||||
register: guest_facts_0002
|
register: guest_facts_0002
|
||||||
|
|
||||||
- debug: msg="{{ guest_facts_0002 }}"
|
- debug: var=guest_facts_0002
|
||||||
|
|
||||||
|
- name: "Get specific details about virtual machines using the vsphere output schema"
|
||||||
|
vmware_guest_facts:
|
||||||
|
validate_certs: False
|
||||||
|
hostname: "{{ vcsim }}"
|
||||||
|
username: "{{ vcsim_instance['json']['username'] }}"
|
||||||
|
password: "{{ vcsim_instance['json']['password'] }}"
|
||||||
|
datacenter: "{{ dc1 | basename }}"
|
||||||
|
uuid: "{{ vm1_uuid }}"
|
||||||
|
schema: vsphere
|
||||||
|
properties:
|
||||||
|
- config.hardware.memoryMB
|
||||||
|
- guest
|
||||||
|
- name
|
||||||
|
- summary.runtime.connectionState
|
||||||
|
register: guest_facts_0002b
|
||||||
|
|
||||||
|
- debug: var=guest_facts_0002b
|
||||||
|
|
||||||
- assert:
|
- assert:
|
||||||
that:
|
that:
|
||||||
|
@ -106,14 +125,10 @@
|
||||||
- "guest_facts_0002['instance']['hw_product_uuid'] is defined"
|
- "guest_facts_0002['instance']['hw_product_uuid'] is defined"
|
||||||
- "guest_facts_0002['instance']['hw_product_uuid'] == vm1_uuid"
|
- "guest_facts_0002['instance']['hw_product_uuid'] == vm1_uuid"
|
||||||
- "guest_facts_0002['instance']['hw_cores_per_socket'] is defined"
|
- "guest_facts_0002['instance']['hw_cores_per_socket'] is defined"
|
||||||
- "guest_facts_0001['instance']['hw_datastores'] is defined"
|
- "guest_facts_0002b['instance']['config']['hardware']['memoryMB'] is defined"
|
||||||
- "guest_facts_0001['instance']['hw_esxi_host'] == h1 | basename"
|
- "guest_facts_0002b['instance']['config']['hardware']['numCoresPerSocket'] is not defined"
|
||||||
- "guest_facts_0001['instance']['hw_files'] is defined"
|
- "guest_facts_0002b['instance']['guest']['toolsVersion'] is defined"
|
||||||
- "guest_facts_0001['instance']['hw_guest_ha_state'] is defined"
|
- "guest_facts_0002b['instance']['overallStatus'] is not defined"
|
||||||
- "guest_facts_0001['instance']['hw_is_template'] is defined"
|
|
||||||
- "guest_facts_0001['instance']['hw_folder'] is defined"
|
|
||||||
- "guest_facts_0001['instance']['guest_question'] is defined"
|
|
||||||
- "guest_facts_0001['instance']['guest_consolidation_needed'] is defined"
|
|
||||||
|
|
||||||
# Testcase 0003: Get details about virtual machines without snapshots using UUID
|
# Testcase 0003: Get details about virtual machines without snapshots using UUID
|
||||||
- name: get empty list of snapshots from virtual machine using UUID
|
- name: get empty list of snapshots from virtual machine using UUID
|
||||||
|
@ -126,7 +141,7 @@
|
||||||
uuid: "{{ vm1_uuid }}"
|
uuid: "{{ vm1_uuid }}"
|
||||||
register: guest_facts_0003
|
register: guest_facts_0003
|
||||||
|
|
||||||
- debug: msg="{{ guest_facts_0003 }}"
|
- debug: var=guest_facts_0003
|
||||||
|
|
||||||
- assert:
|
- assert:
|
||||||
that:
|
that:
|
||||||
|
@ -169,7 +184,7 @@
|
||||||
# uuid: "{{ vm1_uuid }}"
|
# uuid: "{{ vm1_uuid }}"
|
||||||
# register: guest_facts_0004
|
# register: guest_facts_0004
|
||||||
|
|
||||||
#- debug: msg="{{ guest_facts_0004 }}"
|
#- debug: var=guest_facts_0004
|
||||||
|
|
||||||
#- assert:
|
#- assert:
|
||||||
# that:
|
# that:
|
||||||
|
|
Loading…
Reference in a new issue