Created
June 15, 2021 12:04
-
-
Save ElioLopez/e72db875f9af4205e49784c045ccd3f6 to your computer and use it in GitHub Desktop.
tower 3.8.3 configuration files openshift (located on /var/lib/awx/vendor/awx_ansible_collections/ansible_collections/community/vmware/scripts/inventory/)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Ansible VMware external inventory script settings | |
[defaults] | |
# If true (the default), return only guest VMs. If false, also return host | |
# systems in the results. | |
guests_only = True | |
# Specify an alternate group name for guest VMs. If not defined, defaults to | |
# the basename of the inventory script + "_vm", e.g. "vmware_vm". | |
#vm_group = vm_group_name | |
# Specify an alternate group name for host systems when guests_only=false. | |
# If not defined, defaults to the basename of the inventory script + "_hw", | |
# e.g. "vmware_hw". | |
#hw_group = hw_group_name | |
# Specify the number of seconds to use the inventory cache before it is | |
# considered stale. If not defined, defaults to 0 seconds. | |
#cache_max_age = 3600 | |
# Specify the directory used for storing the inventory cache. If not defined, | |
# caching will be disabled. | |
#cache_dir = ~/.cache/ansible | |
# Specify a prefix filter. Any VMs with names beginning with this string will | |
# not be returned. | |
# prefix_filter = test_ | |
# Specify a cluster filter list (colon delimited). Only clusters matching by | |
# name will be scanned for virtualmachines | |
#clusters = cluster1,cluster2 | |
[auth] | |
# Specify hostname or IP address of vCenter/ESXi server. A port may be | |
# included with the hostname, e.g.: vcenter.example.com:8443. This setting | |
# may also be defined via the VMWARE_HOST environment variable. | |
host = vcenter.example.com | |
# Specify a username to access the vCenter host. This setting may also be | |
# defined with the VMWARE_USER environment variable. | |
user = ihasaccess | |
# Specify a password to access the vCenter host. This setting may also be | |
# defined with the VMWARE_PASSWORD environment variable. | |
password = ssshverysecret | |
# Force SSL certificate checking by default or ignore self-signed certs. | |
#sslcheck=True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
from __future__ import absolute_import, division, print_function | |
__metaclass__ = type | |
''' | |
VMware Inventory Script | |
======================= | |
Retrieve information about virtual machines from a vCenter server or | |
standalone ESX host. When `group_by=false` (in the INI file), host systems | |
are also returned in addition to VMs. | |
This script will attempt to read configuration from an INI file with the same | |
base filename if present, or `vmware.ini` if not. It is possible to create | |
symlinks to the inventory script to support multiple configurations, e.g.: | |
* `vmware.py` (this script) | |
* `vmware.ini` (default configuration, will be read by `vmware.py`) | |
* `vmware_test.py` (symlink to `vmware.py`) | |
* `vmware_test.ini` (test configuration, will be read by `vmware_test.py`) | |
* `vmware_other.py` (symlink to `vmware.py`, will read `vmware.ini` since no | |
`vmware_other.ini` exists) | |
The path to an INI file may also be specified via the `VMWARE_INI` environment | |
variable, in which case the filename matching rules above will not apply. | |
Host and authentication parameters may be specified via the `VMWARE_HOST`, | |
`VMWARE_USER` and `VMWARE_PASSWORD` environment variables; these options will | |
take precedence over options present in the INI file. An INI file is not | |
required if these options are specified using environment variables. | |
''' | |
import json | |
import logging | |
import optparse | |
import os | |
import ssl | |
import sys | |
import time | |
from ansible.module_utils.common._collections_compat import MutableMapping | |
from ansible.module_utils.six import integer_types, text_type, string_types | |
from ansible.module_utils.six.moves import configparser | |
# Disable logging message trigged by pSphere/suds. | |
try: | |
from logging import NullHandler | |
except ImportError: | |
from logging import Handler | |
class NullHandler(Handler): | |
def emit(self, record): | |
pass | |
logging.getLogger('psphere').addHandler(NullHandler()) | |
logging.getLogger('suds').addHandler(NullHandler()) | |
from psphere.client import Client | |
from psphere.errors import ObjectNotFoundError | |
from psphere.managedobjects import HostSystem, VirtualMachine, ManagedObject, Network, ClusterComputeResource | |
from suds.sudsobject import Object as SudsObject | |
class VMwareInventory(object): | |
def __init__(self, guests_only=None): | |
self.config = configparser.SafeConfigParser() | |
if os.environ.get('VMWARE_INI', ''): | |
config_files = [os.environ['VMWARE_INI']] | |
else: | |
config_files = [os.path.abspath(sys.argv[0]).rstrip('.py') + '.ini', 'vmware.ini'] | |
for config_file in config_files: | |
if os.path.exists(config_file): | |
self.config.read(config_file) | |
break | |
# Retrieve only guest VMs, or include host systems? | |
if guests_only is not None: | |
self.guests_only = guests_only | |
elif self.config.has_option('defaults', 'guests_only'): | |
self.guests_only = self.config.getboolean('defaults', 'guests_only') | |
else: | |
self.guests_only = True | |
# Read authentication information from VMware environment variables | |
# (if set), otherwise from INI file. | |
auth_host = os.environ.get('VMWARE_HOST') | |
if not auth_host and self.config.has_option('auth', 'host'): | |
auth_host = self.config.get('auth', 'host') | |
auth_user = os.environ.get('VMWARE_USER') | |
if not auth_user and self.config.has_option('auth', 'user'): | |
auth_user = self.config.get('auth', 'user') | |
auth_password = os.environ.get('VMWARE_PASSWORD') | |
if not auth_password and self.config.has_option('auth', 'password'): | |
auth_password = self.config.get('auth', 'password') | |
sslcheck = os.environ.get('VMWARE_SSLCHECK') | |
if not sslcheck and self.config.has_option('auth', 'sslcheck'): | |
sslcheck = self.config.get('auth', 'sslcheck') | |
if not sslcheck: | |
sslcheck = True | |
else: | |
if sslcheck.lower() in ['no', 'false']: | |
sslcheck = False | |
else: | |
sslcheck = True | |
# Limit the clusters being scanned | |
self.filter_clusters = os.environ.get('VMWARE_CLUSTERS') | |
if not self.filter_clusters and self.config.has_option('defaults', 'clusters'): | |
self.filter_clusters = self.config.get('defaults', 'clusters') | |
if self.filter_clusters: | |
self.filter_clusters = [x.strip() for x in self.filter_clusters.split(',') if x.strip()] | |
# Override certificate checks | |
if not sslcheck: | |
if hasattr(ssl, '_create_unverified_context'): | |
ssl._create_default_https_context = ssl._create_unverified_context | |
# Create the VMware client connection. | |
self.client = Client(auth_host, auth_user, auth_password) | |
def _put_cache(self, name, value): | |
''' | |
Saves the value to cache with the name given. | |
''' | |
if self.config.has_option('defaults', 'cache_dir'): | |
cache_dir = os.path.expanduser(self.config.get('defaults', 'cache_dir')) | |
if not os.path.exists(cache_dir): | |
os.makedirs(cache_dir) | |
cache_file = os.path.join(cache_dir, name) | |
with open(cache_file, 'w') as cache: | |
json.dump(value, cache) | |
def _get_cache(self, name, default=None): | |
''' | |
Retrieves the value from cache for the given name. | |
''' | |
if self.config.has_option('defaults', 'cache_dir'): | |
cache_dir = self.config.get('defaults', 'cache_dir') | |
cache_file = os.path.join(cache_dir, name) | |
if os.path.exists(cache_file): | |
if self.config.has_option('defaults', 'cache_max_age'): | |
cache_max_age = self.config.getint('defaults', 'cache_max_age') | |
else: | |
cache_max_age = 0 | |
cache_stat = os.stat(cache_file) | |
if (cache_stat.st_mtime + cache_max_age) >= time.time(): | |
with open(cache_file) as cache: | |
return json.load(cache) | |
return default | |
def _flatten_dict(self, d, parent_key='', sep='_'): | |
''' | |
Flatten nested dicts by combining keys with a separator. Lists with | |
only string items are included as is; any other lists are discarded. | |
''' | |
items = [] | |
for k, v in d.items(): | |
if k.startswith('_'): | |
continue | |
new_key = parent_key + sep + k if parent_key else k | |
if isinstance(v, MutableMapping): | |
items.extend(self._flatten_dict(v, new_key, sep).items()) | |
elif isinstance(v, (list, tuple)): | |
if all([isinstance(x, string_types) for x in v]): | |
items.append((new_key, v)) | |
else: | |
items.append((new_key, v)) | |
return dict(items) | |
def _get_obj_info(self, obj, depth=99, seen=None): | |
''' | |
Recursively build a data structure for the given pSphere object (depth | |
only applies to ManagedObject instances). | |
''' | |
seen = seen or set() | |
if isinstance(obj, ManagedObject): | |
try: | |
obj_unicode = text_type(getattr(obj, 'name')) | |
except AttributeError: | |
obj_unicode = () | |
if obj in seen: | |
return obj_unicode | |
seen.add(obj) | |
if depth <= 0: | |
return obj_unicode | |
d = {} | |
for attr in dir(obj): | |
if attr.startswith('_'): | |
continue | |
try: | |
val = getattr(obj, attr) | |
obj_info = self._get_obj_info(val, depth - 1, seen) | |
if obj_info != (): | |
d[attr] = obj_info | |
except Exception as e: | |
pass | |
return d | |
elif isinstance(obj, SudsObject): | |
d = {} | |
for key, val in iter(obj): | |
obj_info = self._get_obj_info(val, depth, seen) | |
if obj_info != (): | |
d[key] = obj_info | |
return d | |
elif isinstance(obj, (list, tuple)): | |
l = [] | |
for val in iter(obj): | |
obj_info = self._get_obj_info(val, depth, seen) | |
if obj_info != (): | |
l.append(obj_info) | |
return l | |
elif isinstance(obj, (type(None), bool, float) + string_types + integer_types): | |
return obj | |
else: | |
return () | |
def _get_host_info(self, host, prefix='vmware'): | |
''' | |
Return a flattened dict with info about the given host system. | |
''' | |
host_info = { | |
'name': host.name, | |
} | |
for attr in ('datastore', 'network', 'vm'): | |
try: | |
value = getattr(host, attr) | |
host_info['%ss' % attr] = self._get_obj_info(value, depth=0) | |
except AttributeError: | |
host_info['%ss' % attr] = [] | |
for k, v in self._get_obj_info(host.summary, depth=0).items(): | |
if isinstance(v, MutableMapping): | |
for k2, v2 in v.items(): | |
host_info[k2] = v2 | |
elif k != 'host': | |
host_info[k] = v | |
try: | |
host_info['ipAddress'] = host.config.network.vnic[0].spec.ip.ipAddress | |
except Exception as e: | |
print(e, file=sys.stderr) | |
host_info = self._flatten_dict(host_info, prefix) | |
if ('%s_ipAddress' % prefix) in host_info: | |
host_info['ansible_ssh_host'] = host_info['%s_ipAddress' % prefix] | |
return host_info | |
def _get_vm_info(self, vm, prefix='vmware'): | |
''' | |
Return a flattened dict with info about the given virtual machine. | |
''' | |
vm_info = { | |
'name': vm.name, | |
} | |
for attr in ('datastore', 'network'): | |
try: | |
value = getattr(vm, attr) | |
vm_info['%ss' % attr] = self._get_obj_info(value, depth=0) | |
except AttributeError: | |
vm_info['%ss' % attr] = [] | |
try: | |
vm_info['resourcePool'] = self._get_obj_info(vm.resourcePool, depth=0) | |
except AttributeError: | |
vm_info['resourcePool'] = '' | |
try: | |
vm_info['guestState'] = vm.guest.guestState | |
except AttributeError: | |
vm_info['guestState'] = '' | |
for k, v in self._get_obj_info(vm.summary, depth=0).items(): | |
if isinstance(v, MutableMapping): | |
for k2, v2 in v.items(): | |
if k2 == 'host': | |
k2 = 'hostSystem' | |
vm_info[k2] = v2 | |
elif k != 'vm': | |
vm_info[k] = v | |
vm_info = self._flatten_dict(vm_info, prefix) | |
if ('%s_ipAddress' % prefix) in vm_info: | |
vm_info['ansible_ssh_host'] = vm_info['%s_ipAddress' % prefix] | |
return vm_info | |
def _add_host(self, inv, parent_group, host_name): | |
''' | |
Add the host to the parent group in the given inventory. | |
''' | |
p_group = inv.setdefault(parent_group, []) | |
if isinstance(p_group, dict): | |
group_hosts = p_group.setdefault('hosts', []) | |
else: | |
group_hosts = p_group | |
if host_name not in group_hosts: | |
group_hosts.append(host_name) | |
def _add_child(self, inv, parent_group, child_group): | |
''' | |
Add a child group to a parent group in the given inventory. | |
''' | |
if parent_group != 'all': | |
p_group = inv.setdefault(parent_group, {}) | |
if not isinstance(p_group, dict): | |
inv[parent_group] = {'hosts': p_group} | |
p_group = inv[parent_group] | |
group_children = p_group.setdefault('children', []) | |
if child_group not in group_children: | |
group_children.append(child_group) | |
inv.setdefault(child_group, []) | |
def get_inventory(self, meta_hostvars=True): # noqa # pylint: disable=too-complex | |
''' | |
Reads the inventory from cache or VMware API via pSphere. | |
''' | |
# Use different cache names for guests only vs. all hosts. | |
if self.guests_only: | |
cache_name = '__inventory_guests__' | |
else: | |
cache_name = '__inventory_all__' | |
inv = self._get_cache(cache_name, None) | |
if inv is not None: | |
return inv | |
inv = {'all': {'hosts': []}} | |
if meta_hostvars: | |
inv['_meta'] = {'hostvars': {}} | |
default_group = os.path.basename(sys.argv[0]).rstrip('.py') | |
if not self.guests_only: | |
if self.config.has_option('defaults', 'hw_group'): | |
hw_group = self.config.get('defaults', 'hw_group') | |
else: | |
hw_group = default_group + '_hw' | |
if self.config.has_option('defaults', 'vm_group'): | |
vm_group = self.config.get('defaults', 'vm_group') | |
else: | |
vm_group = default_group + '_vm' | |
if self.config.has_option('defaults', 'prefix_filter'): | |
prefix_filter = self.config.get('defaults', 'prefix_filter') | |
else: | |
prefix_filter = None | |
if self.filter_clusters: | |
# Loop through clusters and find hosts: | |
hosts = [] | |
for cluster in ClusterComputeResource.all(self.client): | |
if cluster.name in self.filter_clusters: | |
for host in cluster.host: | |
hosts.append(host) | |
else: | |
# Get list of all physical hosts | |
hosts = HostSystem.all(self.client) | |
# Loop through physical hosts: | |
for host in hosts: | |
if not self.guests_only: | |
self._add_host(inv, 'all', host.name) | |
self._add_host(inv, hw_group, host.name) | |
host_info = self._get_host_info(host) | |
if meta_hostvars: | |
inv['_meta']['hostvars'][host.name] = host_info | |
self._put_cache(host.name, host_info) | |
# Loop through all VMs on physical host. | |
for vm in host.vm: | |
if prefix_filter: | |
if vm.name.startswith(prefix_filter): | |
continue | |
self._add_host(inv, 'all', vm.name) | |
self._add_host(inv, vm_group, vm.name) | |
vm_info = self._get_vm_info(vm) | |
if meta_hostvars: | |
inv['_meta']['hostvars'][vm.name] = vm_info | |
self._put_cache(vm.name, vm_info) | |
# Group by resource pool. | |
vm_resourcePool = vm_info.get('vmware_resourcePool', None) | |
if vm_resourcePool: | |
self._add_child(inv, vm_group, 'resource_pools') | |
self._add_child(inv, 'resource_pools', vm_resourcePool) | |
self._add_host(inv, vm_resourcePool, vm.name) | |
# Group by datastore. | |
for vm_datastore in vm_info.get('vmware_datastores', []): | |
self._add_child(inv, vm_group, 'datastores') | |
self._add_child(inv, 'datastores', vm_datastore) | |
self._add_host(inv, vm_datastore, vm.name) | |
# Group by network. | |
for vm_network in vm_info.get('vmware_networks', []): | |
self._add_child(inv, vm_group, 'networks') | |
self._add_child(inv, 'networks', vm_network) | |
self._add_host(inv, vm_network, vm.name) | |
# Group by guest OS. | |
vm_guestId = vm_info.get('vmware_guestId', None) | |
if vm_guestId: | |
self._add_child(inv, vm_group, 'guests') | |
self._add_child(inv, 'guests', vm_guestId) | |
self._add_host(inv, vm_guestId, vm.name) | |
# Group all VM templates. | |
vm_template = vm_info.get('vmware_template', False) | |
if vm_template: | |
self._add_child(inv, vm_group, 'templates') | |
self._add_host(inv, 'templates', vm.name) | |
self._put_cache(cache_name, inv) | |
return inv | |
def get_host(self, hostname): | |
''' | |
Read info about a specific host or VM from cache or VMware API. | |
''' | |
inv = self._get_cache(hostname, None) | |
if inv is not None: | |
return inv | |
if not self.guests_only: | |
try: | |
host = HostSystem.get(self.client, name=hostname) | |
inv = self._get_host_info(host) | |
except ObjectNotFoundError: | |
pass | |
if inv is None: | |
try: | |
vm = VirtualMachine.get(self.client, name=hostname) | |
inv = self._get_vm_info(vm) | |
except ObjectNotFoundError: | |
pass | |
if inv is not None: | |
self._put_cache(hostname, inv) | |
return inv or {} | |
def main(): | |
parser = optparse.OptionParser() | |
parser.add_option('--list', action='store_true', dest='list', | |
default=False, help='Output inventory groups and hosts') | |
parser.add_option('--host', dest='host', default=None, metavar='HOST', | |
help='Output variables only for the given hostname') | |
# Additional options for use when running the script standalone, but never | |
# used by Ansible. | |
parser.add_option('--pretty', action='store_true', dest='pretty', | |
default=False, help='Output nicely-formatted JSON') | |
parser.add_option('--include-host-systems', action='store_true', | |
dest='include_host_systems', default=False, | |
help='Include host systems in addition to VMs') | |
parser.add_option('--no-meta-hostvars', action='store_false', | |
dest='meta_hostvars', default=True, | |
help='Exclude [\'_meta\'][\'hostvars\'] with --list') | |
options, args = parser.parse_args() | |
if options.include_host_systems: | |
vmware_inventory = VMwareInventory(guests_only=False) | |
else: | |
vmware_inventory = VMwareInventory() | |
if options.host is not None: | |
inventory = vmware_inventory.get_host(options.host) | |
else: | |
inventory = vmware_inventory.get_inventory(options.meta_hostvars) | |
json_kwargs = {} | |
if options.pretty: | |
json_kwargs.update({'indent': 4, 'sort_keys': True}) | |
json.dump(inventory, sys.stdout, **json_kwargs) | |
if __name__ == '__main__': | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Ansible VMware external inventory script settings | |
[vmware] | |
# The resolvable hostname or ip address of the vsphere | |
server=vcenter | |
# The port for the vsphere API | |
#port=443 | |
# The username with access to the vsphere API. This setting | |
# may also be defined via the VMWARE_USERNAME environment variable. | |
username=administrator@vsphere.local | |
# The password for the vsphere API. This setting | |
# may also be defined via the VMWARE_PASSWORD environment variable. | |
password=vmware | |
# Verify the server's SSL certificate | |
#validate_certs = True | |
# Specify the number of seconds to use the inventory cache before it is | |
# considered stale. If not defined, defaults to 0 seconds. | |
#cache_max_age = 3600 | |
# Specify the directory used for storing the inventory cache. If not defined, | |
# caching will be disabled. | |
#cache_path = ~/.cache/ansible | |
# Max object level refers to the level of recursion the script will delve into | |
# the objects returned from pyvomi to find serializable facts. The default | |
# level of 0 is sufficient for most tasks and will be the most performant. | |
# Beware that the recursion can exceed python's limit (causing traceback), | |
# cause sluggish script performance and return huge blobs of facts. | |
# If you do not know what you are doing, leave this set to 1. | |
#max_object_level=1 | |
# Lower the keynames for facts to make addressing them easier. | |
#lower_var_keys=True | |
# Don't retrieve and process some VMware attribute keys | |
# Default values permit to sanitize inventory meta and to improve a little bit | |
# performance by removing non-common group attributes. | |
#skip_keys = declaredalarmstate,disabledmethod,dynamicproperty,dynamictype,environmentbrowser,managedby,parent,childtype,resourceconfig | |
# Host alias for objects in the inventory. VMware allows duplicate VM names | |
# so they can not be considered unique. Use this setting to alter the alias | |
# returned for the hosts. Any atributes for the guest can be used to build | |
# this alias. The default combines the config name and the config uuid and | |
# expects that the ansible_host will be set by the host_pattern. | |
#alias_pattern={{ config.name + '_' + config.uuid }} | |
# Host pattern is the value set for ansible_host and ansible_ssh_host, which | |
# needs to be a hostname or ipaddress the ansible controlhost can reach. | |
#host_pattern={{ guest.ipaddress }} | |
# Host filters are a comma separated list of jinja patterns to remove | |
# non-matching hosts from the final result. | |
# EXAMPLES: | |
# host_filters={{ config.guestid == 'rhel7_64Guest' }} | |
# host_filters={{ config.cpuhotremoveenabled != False }},{{ runtime.maxmemoryusage >= 512 }} | |
# host_filters={{ config.cpuhotremoveenabled != False }},{{ runtime.maxmemoryusage >= 512 }} | |
# host_filters={{ runtime.powerstate == "poweredOn" }} | |
# host_filters={{ guest.gueststate == "notRunning" }} | |
# The default value is powerstate of virtual machine equal to "poweredOn". (Changed in version 2.5) | |
# Runtime state does not require to have vmware tools installed as compared to "guest.gueststate" | |
#host_filters={{ runtime.powerstate == "poweredOn" }} | |
# Groupby patterns enable the user to create groups via any possible jinja | |
# expression. The resulting value will the groupname and the host will be added | |
# to that group. Be careful to not make expressions that simply return True/False | |
# because those values will become the literal group name. The patterns can be | |
# comma delimited to create as many groups as necessary | |
#groupby_patterns={{ guest.guestid }},{{ 'templates' if config.template else 'guests'}} | |
# Group by custom fields will use VMware custom fields to generate hostgroups | |
# based on {{ custom_field_group_prefix }} + field_name + _ + field_value | |
# Set groupby_custom_field to True will enable this feature | |
# If custom field value is comma separated, multiple groups are created. | |
# Warning: This required max_object_level to be set to 2 or greater. | |
#groupby_custom_field = False | |
# You can customize prefix used by custom field hostgroups generation here. | |
# vmware_tag_ prefix is the default and consistent with ec2_tag_ | |
#custom_field_group_prefix = vmware_tag_ | |
# You can blacklist custom fields so that they are not included in the | |
# groupby_custom_field option. This is useful when you have custom fields that | |
# have values that are unique to individual hosts. Timestamps for example. | |
# The groupby_custom_field_excludes option should be a comma separated list of custom | |
# field keys to be blacklisted. | |
#groupby_custom_field_excludes=<custom_field_1>,<custom_field_2>,<custom_field_3> | |
# The script attempts to recurse into virtualmachine objects and serialize | |
# all available data. The serialization is comprehensive but slow. If the | |
# vcenter environment is large and the desired properties are known, create | |
# a 'properties' section in this config and make an arbitrary list of | |
# key=value settings where the value is a path to a specific property. If | |
# If this feature is enabled, be sure to fetch every property that is used | |
# in the jinja expressions defined above. For performance tuning, reduce | |
# the number of properties to the smallest amount possible and limit the | |
# use of properties that are not direct attributes of vim.VirtualMachine | |
#[properties] | |
#prop01=name | |
#prop02=config.cpuHotAddEnabled | |
#prop03=config.cpuHotRemoveEnabled | |
#prop04=config.instanceUuid | |
#prop05=config.hardware.numCPU | |
#prop06=config.template | |
#prop07=config.name | |
#prop08=guest.hostName | |
#prop09=guest.ipAddress | |
#prop10=guest.guestId | |
#prop11=guest.guestState | |
#prop12=runtime.maxMemoryUsage | |
# In order to populate `customValue` (virtual machine's custom attributes) inside hostvars, | |
# uncomment following property. Please see - https://github.com/ansible/ansible/issues/41395 | |
#prop13=customValue |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
# | |
# Copyright (C): 2017, Ansible Project | |
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | |
# Requirements | |
# - pyvmomi >= 6.0.0.2016.4 | |
# TODO: | |
# * more jq examples | |
# * optional folder hierarchy | |
from __future__ import (absolute_import, division, print_function) | |
__metaclass__ = type | |
""" | |
$ jq '._meta.hostvars[].config' data.json | head | |
{ | |
"alternateguestname": "", | |
"instanceuuid": "5035a5cd-b8e8-d717-e133-2d383eb0d675", | |
"memoryhotaddenabled": false, | |
"guestfullname": "Red Hat Enterprise Linux 7 (64-bit)", | |
"changeversion": "2016-05-16T18:43:14.977925Z", | |
"uuid": "4235fc97-5ddb-7a17-193b-9a3ac97dc7b4", | |
"cpuhotremoveenabled": false, | |
"vpmcenabled": false, | |
"firmware": "bios", | |
""" | |
import atexit | |
import datetime | |
import itertools | |
import json | |
import os | |
import re | |
import ssl | |
import sys | |
import uuid | |
from time import time | |
from jinja2 import Environment | |
from ansible.module_utils.six import integer_types, PY3 | |
from ansible.module_utils.six.moves import configparser | |
try: | |
import argparse | |
except ImportError: | |
sys.exit('Error: This inventory script required "argparse" python module. Please install it or upgrade to python-2.7') | |
try: | |
from pyVmomi import vim, vmodl | |
from pyVim.connect import SmartConnect, Disconnect | |
except ImportError: | |
sys.exit("ERROR: This inventory script required 'pyVmomi' Python module, it was not able to load it") | |
def regex_match(s, pattern): | |
'''Custom filter for regex matching''' | |
reg = re.compile(pattern) | |
if reg.match(s): | |
return True | |
else: | |
return False | |
def select_chain_match(inlist, key, pattern): | |
'''Get a key from a list of dicts, squash values to a single list, then filter''' | |
outlist = [x[key] for x in inlist] | |
outlist = list(itertools.chain(*outlist)) | |
outlist = [x for x in outlist if regex_match(x, pattern)] | |
return outlist | |
class VMwareMissingHostException(Exception): | |
pass | |
class VMWareInventory(object): | |
__name__ = 'VMWareInventory' | |
guest_props = False | |
instances = [] | |
debug = False | |
load_dumpfile = None | |
write_dumpfile = None | |
maxlevel = 1 | |
lowerkeys = True | |
config = None | |
cache_max_age = None | |
cache_path_cache = None | |
cache_path_index = None | |
cache_dir = None | |
server = None | |
port = None | |
username = None | |
password = None | |
validate_certs = True | |
host_filters = [] | |
skip_keys = [] | |
groupby_patterns = [] | |
groupby_custom_field_excludes = [] | |
safe_types = [bool, str, float, None] + list(integer_types) | |
iter_types = [dict, list] | |
bad_types = ['Array', 'disabledMethod', 'declaredAlarmState'] | |
vimTableMaxDepth = { | |
"vim.HostSystem": 2, | |
"vim.VirtualMachine": 2, | |
} | |
custom_fields = {} | |
# use jinja environments to allow for custom filters | |
env = Environment() | |
env.filters['regex_match'] = regex_match | |
env.filters['select_chain_match'] = select_chain_match | |
# translation table for attributes to fetch for known vim types | |
vimTable = { | |
vim.Datastore: ['_moId', 'name'], | |
vim.ResourcePool: ['_moId', 'name'], | |
vim.HostSystem: ['_moId', 'name'], | |
} | |
@staticmethod | |
def _empty_inventory(): | |
return {"_meta": {"hostvars": {}}} | |
def __init__(self, load=True): | |
self.inventory = VMWareInventory._empty_inventory() | |
if load: | |
# Read settings and parse CLI arguments | |
self.parse_cli_args() | |
self.read_settings() | |
# Check the cache | |
cache_valid = self.is_cache_valid() | |
# Handle Cache | |
if self.args.refresh_cache or not cache_valid: | |
self.do_api_calls_update_cache() | |
else: | |
self.debugl('loading inventory from cache') | |
self.inventory = self.get_inventory_from_cache() | |
def debugl(self, text): | |
if self.args.debug: | |
try: | |
text = str(text) | |
except UnicodeEncodeError: | |
text = text.encode('utf-8') | |
print('%s %s' % (datetime.datetime.now(), text)) | |
def show(self): | |
# Data to print | |
self.debugl('dumping results') | |
data_to_print = None | |
if self.args.host: | |
data_to_print = self.get_host_info(self.args.host) | |
elif self.args.list: | |
# Display list of instances for inventory | |
data_to_print = self.inventory | |
return json.dumps(data_to_print, indent=2) | |
def is_cache_valid(self): | |
''' Determines if the cache files have expired, or if it is still valid ''' | |
valid = False | |
if os.path.isfile(self.cache_path_cache): | |
mod_time = os.path.getmtime(self.cache_path_cache) | |
current_time = time() | |
if (mod_time + self.cache_max_age) > current_time: | |
valid = True | |
return valid | |
def do_api_calls_update_cache(self): | |
''' Get instances and cache the data ''' | |
self.inventory = self.instances_to_inventory(self.get_instances()) | |
self.write_to_cache(self.inventory) | |
def write_to_cache(self, data): | |
''' Dump inventory to json file ''' | |
with open(self.cache_path_cache, 'w') as f: | |
f.write(json.dumps(data, indent=2)) | |
def get_inventory_from_cache(self): | |
''' Read in jsonified inventory ''' | |
jdata = None | |
with open(self.cache_path_cache, 'r') as f: | |
jdata = f.read() | |
return json.loads(jdata) | |
def read_settings(self): | |
''' Reads the settings from the vmware_inventory.ini file ''' | |
scriptbasename = __file__ | |
scriptbasename = os.path.basename(scriptbasename) | |
scriptbasename = scriptbasename.replace('.py', '') | |
defaults = {'vmware': { | |
'server': '', | |
'port': 443, | |
'username': '', | |
'password': '', | |
'validate_certs': True, | |
'ini_path': os.path.join(os.path.dirname(__file__), '%s.ini' % scriptbasename), | |
'cache_name': 'ansible-vmware', | |
'cache_path': '~/.ansible/tmp', | |
'cache_max_age': 3600, | |
'max_object_level': 1, | |
'skip_keys': 'declaredalarmstate,' | |
'disabledmethod,' | |
'dynamicproperty,' | |
'dynamictype,' | |
'environmentbrowser,' | |
'managedby,' | |
'parent,' | |
'childtype,' | |
'resourceconfig', | |
'alias_pattern': '{{ config.name + "_" + config.uuid }}', | |
'host_pattern': '{{ guest.ipaddress }}', | |
'host_filters': '{{ runtime.powerstate == "poweredOn" }}', | |
'groupby_patterns': '{{ guest.guestid }},{{ "templates" if config.template else "guests"}}', | |
'lower_var_keys': True, | |
'custom_field_group_prefix': 'vmware_tag_', | |
'groupby_custom_field_excludes': '', | |
'groupby_custom_field': False} | |
} | |
if PY3: | |
config = configparser.ConfigParser() | |
else: | |
config = configparser.SafeConfigParser() | |
# where is the config? | |
vmware_ini_path = os.environ.get('VMWARE_INI_PATH', defaults['vmware']['ini_path']) | |
vmware_ini_path = os.path.expanduser(os.path.expandvars(vmware_ini_path)) | |
config.read(vmware_ini_path) | |
if 'vmware' not in config.sections(): | |
config.add_section('vmware') | |
# apply defaults | |
for k, v in defaults['vmware'].items(): | |
if not config.has_option('vmware', k): | |
config.set('vmware', k, str(v)) | |
# where is the cache? | |
self.cache_dir = os.path.expanduser(config.get('vmware', 'cache_path')) | |
if self.cache_dir and not os.path.exists(self.cache_dir): | |
os.makedirs(self.cache_dir) | |
# set the cache filename and max age | |
cache_name = config.get('vmware', 'cache_name') | |
self.cache_path_cache = self.cache_dir + "/%s.cache" % cache_name | |
self.debugl('cache path is %s' % self.cache_path_cache) | |
self.cache_max_age = int(config.getint('vmware', 'cache_max_age')) | |
# mark the connection info | |
self.server = os.environ.get('VMWARE_SERVER', config.get('vmware', 'server')) | |
self.debugl('server is %s' % self.server) | |
self.port = int(os.environ.get('VMWARE_PORT', config.get('vmware', 'port'))) | |
self.username = os.environ.get('VMWARE_USERNAME', config.get('vmware', 'username')) | |
self.debugl('username is %s' % self.username) | |
self.password = os.environ.get('VMWARE_PASSWORD', config.get('vmware', 'password', raw=True)) | |
self.validate_certs = os.environ.get('VMWARE_VALIDATE_CERTS', config.get('vmware', 'validate_certs')) | |
if self.validate_certs in ['no', 'false', 'False', False]: | |
self.validate_certs = False | |
self.debugl('cert validation is %s' % self.validate_certs) | |
# behavior control | |
self.maxlevel = int(config.get('vmware', 'max_object_level')) | |
self.debugl('max object level is %s' % self.maxlevel) | |
self.lowerkeys = config.get('vmware', 'lower_var_keys') | |
if type(self.lowerkeys) != bool: | |
if str(self.lowerkeys).lower() in ['yes', 'true', '1']: | |
self.lowerkeys = True | |
else: | |
self.lowerkeys = False | |
self.debugl('lower keys is %s' % self.lowerkeys) | |
self.skip_keys = list(config.get('vmware', 'skip_keys').split(',')) | |
self.debugl('skip keys is %s' % self.skip_keys) | |
temp_host_filters = list(config.get('vmware', 'host_filters').split('}},')) | |
for host_filter in temp_host_filters: | |
host_filter = host_filter.rstrip() | |
if host_filter != "": | |
if not host_filter.endswith("}}"): | |
host_filter += "}}" | |
self.host_filters.append(host_filter) | |
self.debugl('host filters are %s' % self.host_filters) | |
temp_groupby_patterns = list(config.get('vmware', 'groupby_patterns').split('}},')) | |
for groupby_pattern in temp_groupby_patterns: | |
groupby_pattern = groupby_pattern.rstrip() | |
if groupby_pattern != "": | |
if not groupby_pattern.endswith("}}"): | |
groupby_pattern += "}}" | |
self.groupby_patterns.append(groupby_pattern) | |
self.debugl('groupby patterns are %s' % self.groupby_patterns) | |
temp_groupby_custom_field_excludes = config.get('vmware', 'groupby_custom_field_excludes') | |
self.groupby_custom_field_excludes = [x.strip('"') for x in [y.strip("'") for y in temp_groupby_custom_field_excludes.split(",")]] | |
self.debugl('groupby exclude strings are %s' % self.groupby_custom_field_excludes) | |
# Special feature to disable the brute force serialization of the | |
# virtual machine objects. The key name for these properties does not | |
# matter because the values are just items for a larger list. | |
if config.has_section('properties'): | |
self.guest_props = [] | |
for prop in config.items('properties'): | |
self.guest_props.append(prop[1]) | |
# save the config | |
self.config = config | |
def parse_cli_args(self): | |
''' Command line argument processing ''' | |
parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on PyVmomi') | |
parser.add_argument('--debug', action='store_true', default=False, | |
help='show debug info') | |
parser.add_argument('--list', action='store_true', default=True, | |
help='List instances (default: True)') | |
parser.add_argument('--host', action='store', | |
help='Get all the variables about a specific instance') | |
parser.add_argument('--refresh-cache', action='store_true', default=False, | |
help='Force refresh of cache by making API requests to VSphere (default: False - use cache files)') | |
parser.add_argument('--max-instances', default=None, type=int, | |
help='maximum number of instances to retrieve') | |
self.args = parser.parse_args() | |
def get_instances(self): | |
''' Get a list of vm instances with pyvmomi ''' | |
kwargs = {'host': self.server, | |
'user': self.username, | |
'pwd': self.password, | |
'port': int(self.port)} | |
if self.validate_certs and hasattr(ssl, 'SSLContext'): | |
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) | |
context.verify_mode = ssl.CERT_REQUIRED | |
context.check_hostname = True # noqa # pylint: disable=assigning-non-slot | |
kwargs['sslContext'] = context | |
elif self.validate_certs and not hasattr(ssl, 'SSLContext'): | |
sys.exit('pyVim does not support changing verification mode with python < 2.7.9. Either update ' | |
'python or use validate_certs=false.') | |
elif not self.validate_certs and hasattr(ssl, 'SSLContext'): | |
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) | |
context.verify_mode = ssl.CERT_NONE | |
context.check_hostname = False # noqa # pylint: disable=assigning-non-slot | |
kwargs['sslContext'] = context | |
elif not self.validate_certs and not hasattr(ssl, 'SSLContext'): | |
# Python 2.7.9 < or RHEL/CentOS 7.4 < | |
pass | |
return self._get_instances(kwargs) | |
def _get_instances(self, inkwargs): | |
''' Make API calls ''' | |
instances = [] | |
si = None | |
try: | |
si = SmartConnect(**inkwargs) | |
except ssl.SSLError as connection_error: | |
if '[SSL: CERTIFICATE_VERIFY_FAILED]' in str(connection_error) and self.validate_certs: | |
sys.exit("Unable to connect to ESXi server due to %s, " | |
"please specify validate_certs=False and try again" % connection_error) | |
except Exception as exc: | |
self.debugl("Unable to connect to ESXi server due to %s" % exc) | |
sys.exit("Unable to connect to ESXi server due to %s" % exc) | |
self.debugl('retrieving all instances') | |
if not si: | |
sys.exit("Could not connect to the specified host using specified " | |
"username and password") | |
atexit.register(Disconnect, si) | |
content = si.RetrieveContent() | |
# Create a search container for virtualmachines | |
self.debugl('creating containerview for virtualmachines') | |
container = content.rootFolder | |
viewType = [vim.VirtualMachine] | |
recursive = True | |
containerView = content.viewManager.CreateContainerView(container, viewType, recursive) | |
children = containerView.view | |
for child in children: | |
# If requested, limit the total number of instances | |
if self.args.max_instances: | |
if len(instances) >= self.args.max_instances: | |
break | |
instances.append(child) | |
self.debugl("%s total instances in container view" % len(instances)) | |
if self.args.host: | |
instances = [x for x in instances if x.name == self.args.host] | |
instance_tuples = [] | |
for instance in instances: | |
if self.guest_props: | |
ifacts = self.facts_from_proplist(instance) | |
else: | |
ifacts = self.facts_from_vobj(instance) | |
instance_tuples.append((instance, ifacts)) | |
self.debugl('facts collected for all instances') | |
try: | |
cfm = content.customFieldsManager | |
if cfm is not None and cfm.field: | |
for f in cfm.field: | |
if not f.managedObjectType or f.managedObjectType == vim.VirtualMachine: | |
self.custom_fields[f.key] = f.name | |
self.debugl('%d custom fields collected' % len(self.custom_fields)) | |
except vmodl.RuntimeFault as exc: | |
self.debugl("Unable to gather custom fields due to %s" % exc.msg) | |
except IndexError as exc: | |
self.debugl("Unable to gather custom fields due to %s" % exc) | |
return instance_tuples | |
def instances_to_inventory(self, instances): # noqa # pylint: disable=too-complex | |
''' Convert a list of vm objects into a json compliant inventory ''' | |
self.debugl('re-indexing instances based on ini settings') | |
inventory = VMWareInventory._empty_inventory() | |
inventory['all'] = {} | |
inventory['all']['hosts'] = [] | |
for idx, instance in enumerate(instances): | |
# make a unique id for this object to avoid vmware's | |
# numerous uuid's which aren't all unique. | |
thisid = str(uuid.uuid4()) | |
idata = instance[1] | |
# Put it in the inventory | |
inventory['all']['hosts'].append(thisid) | |
inventory['_meta']['hostvars'][thisid] = idata.copy() | |
inventory['_meta']['hostvars'][thisid]['ansible_uuid'] = thisid | |
# Make a map of the uuid to the alias the user wants | |
name_mapping = self.create_template_mapping( | |
inventory, | |
self.config.get('vmware', 'alias_pattern') | |
) | |
# Make a map of the uuid to the ssh hostname the user wants | |
host_mapping = self.create_template_mapping( | |
inventory, | |
self.config.get('vmware', 'host_pattern') | |
) | |
# Reset the inventory keys | |
for k, v in name_mapping.items(): | |
if not host_mapping or k not in host_mapping: | |
continue | |
# set ansible_host (2.x) | |
try: | |
inventory['_meta']['hostvars'][k]['ansible_host'] = host_mapping[k] | |
# 1.9.x backwards compliance | |
inventory['_meta']['hostvars'][k]['ansible_ssh_host'] = host_mapping[k] | |
except Exception: | |
continue | |
if k == v: | |
continue | |
# add new key | |
inventory['all']['hosts'].append(v) | |
inventory['_meta']['hostvars'][v] = inventory['_meta']['hostvars'][k] | |
# cleanup old key | |
inventory['all']['hosts'].remove(k) | |
inventory['_meta']['hostvars'].pop(k, None) | |
self.debugl('pre-filtered hosts:') | |
for i in inventory['all']['hosts']: | |
self.debugl(' * %s' % i) | |
# Apply host filters | |
for hf in self.host_filters: | |
if not hf: | |
continue | |
self.debugl('filter: %s' % hf) | |
filter_map = self.create_template_mapping(inventory, hf, dtype='boolean') | |
for k, v in filter_map.items(): | |
if not v: | |
# delete this host | |
inventory['all']['hosts'].remove(k) | |
inventory['_meta']['hostvars'].pop(k, None) | |
self.debugl('post-filter hosts:') | |
for i in inventory['all']['hosts']: | |
self.debugl(' * %s' % i) | |
# Create groups | |
for gbp in self.groupby_patterns: | |
groupby_map = self.create_template_mapping(inventory, gbp) | |
for k, v in groupby_map.items(): | |
if v not in inventory: | |
inventory[v] = {} | |
inventory[v]['hosts'] = [] | |
if k not in inventory[v]['hosts']: | |
inventory[v]['hosts'].append(k) | |
if self.config.get('vmware', 'groupby_custom_field'): | |
for k, v in inventory['_meta']['hostvars'].items(): | |
if 'customvalue' in v: | |
for tv in v['customvalue']: | |
newkey = None | |
field_name = self.custom_fields[tv['key']] if tv['key'] in self.custom_fields else tv['key'] | |
if field_name in self.groupby_custom_field_excludes: | |
continue | |
values = [] | |
keylist = map(lambda x: x.strip(), tv['value'].split(',')) | |
for kl in keylist: | |
try: | |
newkey = "%s%s_%s" % (self.config.get('vmware', 'custom_field_group_prefix'), str(field_name), kl) | |
newkey = newkey.strip() | |
except Exception as e: | |
self.debugl(e) | |
values.append(newkey) | |
for tag in values: | |
if not tag: | |
continue | |
if tag not in inventory: | |
inventory[tag] = {} | |
inventory[tag]['hosts'] = [] | |
if k not in inventory[tag]['hosts']: | |
inventory[tag]['hosts'].append(k) | |
return inventory | |
def create_template_mapping(self, inventory, pattern, dtype='string'): | |
''' Return a hash of uuid to templated string from pattern ''' | |
mapping = {} | |
for k, v in inventory['_meta']['hostvars'].items(): | |
t = self.env.from_string(pattern) | |
newkey = None | |
try: | |
newkey = t.render(v) | |
newkey = newkey.strip() | |
except Exception as e: | |
self.debugl(e) | |
if not newkey: | |
continue | |
if dtype == 'integer': | |
newkey = int(newkey) | |
elif dtype == 'boolean': | |
if newkey.lower() == 'false': | |
newkey = False | |
elif newkey.lower() == 'true': | |
newkey = True | |
elif dtype == 'string': | |
pass | |
mapping[k] = newkey | |
return mapping | |
def facts_from_proplist(self, vm): # noqa # pylint: disable=too-complex | |
'''Get specific properties instead of serializing everything''' | |
rdata = {} | |
for prop in self.guest_props: | |
self.debugl('getting %s property for %s' % (prop, vm.name)) | |
key = prop | |
if self.lowerkeys: | |
key = key.lower() | |
if '.' not in prop: | |
# props without periods are direct attributes of the parent | |
vm_property = getattr(vm, prop) | |
if isinstance(vm_property, vim.CustomFieldsManager.Value.Array): | |
temp_vm_property = [] | |
for vm_prop in vm_property: | |
temp_vm_property.append({'key': vm_prop.key, | |
'value': vm_prop.value}) | |
rdata[key] = temp_vm_property | |
else: | |
rdata[key] = vm_property | |
else: | |
# props with periods are subkeys of parent attributes | |
parts = prop.split('.') | |
total = len(parts) - 1 | |
# pointer to the current object | |
val = None | |
# pointer to the current result key | |
lastref = rdata | |
for idx, x in enumerate(parts): | |
if isinstance(val, dict): | |
if x in val: | |
val = val.get(x) | |
elif x.lower() in val: | |
val = val.get(x.lower()) | |
else: | |
# if the val wasn't set yet, get it from the parent | |
if not val: | |
try: | |
val = getattr(vm, x) | |
except AttributeError as e: | |
self.debugl(e) | |
else: | |
# in a subkey, get the subprop from the previous attrib | |
try: | |
val = getattr(val, x) | |
except AttributeError as e: | |
self.debugl(e) | |
# make sure it serializes | |
val = self._process_object_types(val) | |
# lowercase keys if requested | |
if self.lowerkeys: | |
x = x.lower() | |
# change the pointer or set the final value | |
if idx != total: | |
if x not in lastref: | |
lastref[x] = {} | |
lastref = lastref[x] | |
else: | |
lastref[x] = val | |
if self.args.debug: | |
self.debugl("For %s" % vm.name) | |
for key in list(rdata.keys()): | |
if isinstance(rdata[key], dict): | |
for ikey in list(rdata[key].keys()): | |
self.debugl("Property '%s.%s' has value '%s'" % (key, ikey, rdata[key][ikey])) | |
else: | |
self.debugl("Property '%s' has value '%s'" % (key, rdata[key])) | |
return rdata | |
def facts_from_vobj(self, vobj, level=0): | |
''' Traverse a VM object and return a json compliant data structure ''' | |
# pyvmomi objects are not yet serializable, but may be one day ... | |
# https://github.com/vmware/pyvmomi/issues/21 | |
# WARNING: | |
# Accessing an object attribute will trigger a SOAP call to the remote. | |
# Increasing the attributes collected or the depth of recursion greatly | |
# increases runtime duration and potentially memory+network utilization. | |
if level == 0: | |
try: | |
self.debugl("get facts for %s" % vobj.name) | |
except Exception as e: | |
self.debugl(e) | |
rdata = {} | |
methods = dir(vobj) | |
methods = [str(x) for x in methods if not x.startswith('_')] | |
methods = [x for x in methods if x not in self.bad_types] | |
methods = [x for x in methods if not x.lower() in self.skip_keys] | |
methods = sorted(methods) | |
for method in methods: | |
# Attempt to get the method, skip on fail | |
try: | |
methodToCall = getattr(vobj, method) | |
except Exception as e: | |
continue | |
# Skip callable methods | |
if callable(methodToCall): | |
continue | |
if self.lowerkeys: | |
method = method.lower() | |
rdata[method] = self._process_object_types( | |
methodToCall, | |
thisvm=vobj, | |
inkey=method, | |
) | |
return rdata | |
def _process_object_types(self, vobj, thisvm=None, inkey='', level=0): # noqa # pylint: disable=too-complex | |
''' Serialize an object ''' | |
rdata = {} | |
if type(vobj).__name__ in self.vimTableMaxDepth and level >= self.vimTableMaxDepth[type(vobj).__name__]: | |
return rdata | |
if vobj is None: | |
rdata = None | |
elif type(vobj) in self.vimTable: | |
rdata = {} | |
for key in self.vimTable[type(vobj)]: | |
try: | |
rdata[key] = getattr(vobj, key) | |
except Exception as e: | |
self.debugl(e) | |
elif issubclass(type(vobj), str) or isinstance(vobj, str): | |
if vobj.isalnum(): | |
rdata = vobj | |
else: | |
rdata = vobj.encode('utf-8').decode('utf-8') | |
elif issubclass(type(vobj), bool) or isinstance(vobj, bool): | |
rdata = vobj | |
elif issubclass(type(vobj), integer_types) or isinstance(vobj, integer_types): | |
rdata = vobj | |
elif issubclass(type(vobj), float) or isinstance(vobj, float): | |
rdata = vobj | |
elif issubclass(type(vobj), list) or issubclass(type(vobj), tuple): | |
rdata = [] | |
try: | |
vobj = sorted(vobj) | |
except Exception: | |
pass | |
for idv, vii in enumerate(vobj): | |
if level + 1 <= self.maxlevel: | |
vid = self._process_object_types( | |
vii, | |
thisvm=thisvm, | |
inkey=inkey + '[' + str(idv) + ']', | |
level=(level + 1) | |
) | |
if vid: | |
rdata.append(vid) | |
elif issubclass(type(vobj), dict): | |
pass | |
elif issubclass(type(vobj), object): | |
methods = dir(vobj) | |
methods = [str(x) for x in methods if not x.startswith('_')] | |
methods = [x for x in methods if x not in self.bad_types] | |
methods = [x for x in methods if not inkey + '.' + x.lower() in self.skip_keys] | |
methods = sorted(methods) | |
for method in methods: | |
# Attempt to get the method, skip on fail | |
try: | |
methodToCall = getattr(vobj, method) | |
except Exception as e: | |
continue | |
if callable(methodToCall): | |
continue | |
if self.lowerkeys: | |
method = method.lower() | |
if level + 1 <= self.maxlevel: | |
try: | |
rdata[method] = self._process_object_types( | |
methodToCall, | |
thisvm=thisvm, | |
inkey=inkey + '.' + method, | |
level=(level + 1) | |
) | |
except vim.fault.NoPermission: | |
self.debugl("Skipping method %s (NoPermission)" % method) | |
else: | |
pass | |
return rdata | |
def get_host_info(self, host): | |
''' Return hostvars for a single host ''' | |
if host in self.inventory['_meta']['hostvars']: | |
return self.inventory['_meta']['hostvars'][host] | |
elif self.args.host and self.inventory['_meta']['hostvars']: | |
match = None | |
for k, v in self.inventory['_meta']['hostvars'].items(): | |
if self.inventory['_meta']['hostvars'][k]['name'] == self.args.host: | |
match = k | |
break | |
if match: | |
return self.inventory['_meta']['hostvars'][match] | |
else: | |
raise VMwareMissingHostException('%s not found' % host) | |
else: | |
raise VMwareMissingHostException('%s not found' % host) | |
if __name__ == "__main__": | |
# Run the script | |
print(VMWareInventory().show()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment