summaryrefslogtreecommitdiffstats
path: root/deployment/inventory/vsphere/vms/vmware_inventory.py
diff options
context:
space:
mode:
authorValerii Ponomarov <vponomar@redhat.com>2019-12-21 00:09:11 +0530
committerValerii Ponomarov <vponomar@redhat.com>2019-12-21 00:15:18 +0530
commitd28b637d972aa600ed15ef437e4cb59c53e0ca5e (patch)
tree5cefd011a1d3b6d2ebc395227494ed64dc1da286 /deployment/inventory/vsphere/vms/vmware_inventory.py
parent678dd026c8961a713dec15a153ef9da64c6b171b (diff)
Delete 'deployment' dir as obsolete and not supported
'deployment' directory contains obsolete and unsupported approach for deploying OpenShift and GlusterFS storage. Separate deployment approach has already been used for significant amount of time. So, delete local 'directory' as it makes no sense to keep it. Moreover, it may be confusing for people who may try it out and see unpredictable errors. Change-Id: Ibf353500bab59853f597304cb9c1990102c000ef
Diffstat (limited to 'deployment/inventory/vsphere/vms/vmware_inventory.py')
-rwxr-xr-xdeployment/inventory/vsphere/vms/vmware_inventory.py567
1 files changed, 0 insertions, 567 deletions
diff --git a/deployment/inventory/vsphere/vms/vmware_inventory.py b/deployment/inventory/vsphere/vms/vmware_inventory.py
deleted file mode 100755
index 0cc485e7..00000000
--- a/deployment/inventory/vsphere/vms/vmware_inventory.py
+++ /dev/null
@@ -1,567 +0,0 @@
-#!/usr/bin/env python
-
-# Requirements
-# - pyvmomi >= 6.0.0.2016.4
-
-# TODO:
-# * more jq examples
-# * optional folder heirarchy
-
-"""
-$ 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",
-"""
-
-from __future__ import print_function
-
-import argparse
-import atexit
-import datetime
-import jinja2
-import os
-import six
-import ssl
-import sys
-import uuid
-
-from six.moves import configparser
-from time import time
-
-HAS_PYVMOMI = False
-try:
- from pyVim.connect import SmartConnect, Disconnect
- HAS_PYVMOMI = True
-except ImportError:
- pass
-
-try:
- import json
-except ImportError:
- import simplejson as json
-
-
-class VMWareInventory(object):
-
- __name__ = 'VMWareInventory'
-
- 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
- server = None
- port = None
- username = None
- password = None
- host_filters = []
- groupby_patterns = []
-
- bad_types = ['Array', 'disabledMethod', 'declaredAlarmState']
- if (sys.version_info > (3, 0)):
- safe_types = [int, bool, str, float, None]
- else:
- safe_types = [int, long, bool, str, float, None]
- iter_types = [dict, list]
- skip_keys = ['dynamicproperty', 'dynamictype', 'managedby', 'childtype']
-
- def _empty_inventory(self):
- return {"_meta": {"hostvars": {}}}
-
- def __init__(self, load=True):
- self.inventory = self._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.inventory = self.get_inventory_from_cache()
-
- def debugl(self, text):
- if self.args.debug:
- try:
- text = str(text)
- except UnicodeEncodeError:
- text = text.encode('ascii', 'ignore')
- print(text)
-
- def show(self):
- # Data to print
- 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):
- """Determine if the cache files have expired or 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."""
-
- instances = self.get_instances()
- self.instances = instances
- self.inventory = self.instances_to_inventory(instances)
- self.write_to_cache(self.inventory, self.cache_path_cache)
-
- def write_to_cache(self, data, cache_path):
- """Dump inventory to json file."""
- with open(self.cache_path_cache, 'wb') as f:
- f.write(json.dumps(data))
-
- def get_inventory_from_cache(self):
- """Read in jsonified inventory."""
-
- jdata = None
- with open(self.cache_path_cache, 'rb') 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': '',
- '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,
- 'alias_pattern': '{{ config.name + "_" + config.uuid }}',
- 'host_pattern': '{{ guest.ipaddress }}',
- 'host_filters': '{{ guest.gueststate == "running" }}',
- 'groupby_patterns': ('{{ guest.guestid }},{{ "templates" if '
- 'config.template else "guests"}}'),
- 'lower_var_keys': True,
- }}
-
- if six.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)
-
- # 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.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.port = int(os.environ.get(
- 'VMWARE_PORT', config.get('vmware', 'port')))
- self.username = os.environ.get(
- 'VMWARE_USERNAME', config.get('vmware', 'username'))
- self.password = os.environ.get(
- 'VMWARE_PASSWORD', config.get('vmware', 'password'))
-
- # behavior control
- self.maxlevel = int(config.get('vmware', 'max_object_level'))
- 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.host_filters = list(
- config.get('vmware', 'host_filters').split(','))
- self.groupby_patterns = list(
- config.get('vmware', 'groupby_patterns').split(','))
-
- # 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."""
-
- instances = []
- kwargs = {
- 'host': self.server,
- 'user': self.username,
- 'pwd': self.password,
- 'port': int(self.port),
- }
-
- if hasattr(ssl, 'SSLContext'):
- # older ssl libs do not have an SSLContext method:
- # context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
- # AttributeError: 'module' object has no attribute 'SSLContext'
- # older pyvmomi version also do not have an sslcontext kwarg:
- # https://github.com/vmware/pyvmomi/commit/92c1de5056be7c5390ac2a28eb08ad939a4b7cdd
- context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
- context.verify_mode = ssl.CERT_NONE
- kwargs['sslContext'] = context
-
- instances = self._get_instances(kwargs)
- self.debugl("### INSTANCES RETRIEVED")
- return instances
-
- def _get_instances(self, inkwargs):
- """Make API calls."""
-
- instances = []
- si = SmartConnect(**inkwargs)
-
- if not si:
- print("Could not connect to the specified host using specified "
- "username and password")
- return -1
- atexit.register(Disconnect, si)
- content = si.RetrieveContent()
- for child in content.rootFolder.childEntity:
- instances += self._get_instances_from_children(child)
- if self.args.max_instances:
- if len(instances) >= (self.args.max_instances + 1):
- instances = instances[0:(self.args.max_instances + 1)]
- instance_tuples = []
- for instance in sorted(instances):
- ifacts = self.facts_from_vobj(instance)
- instance_tuples.append((instance, ifacts))
- return instance_tuples
-
- def _get_instances_from_children(self, child):
- instances = []
-
- if hasattr(child, 'childEntity'):
- self.debugl("CHILDREN: %s" % child.childEntity)
- instances += self._get_instances_from_children(child.childEntity)
- elif hasattr(child, 'vmFolder'):
- self.debugl("FOLDER: %s" % child)
- instances += self._get_instances_from_children(child.vmFolder)
- elif hasattr(child, 'index'):
- self.debugl("LIST: %s" % child)
- for x in sorted(child):
- self.debugl("LIST_ITEM: %s" % x)
- instances += self._get_instances_from_children(x)
- elif hasattr(child, 'guest'):
- self.debugl("GUEST: %s" % child)
- instances.append(child)
- elif hasattr(child, 'vm'):
- # resource pools
- self.debugl("RESOURCEPOOL: %s" % child.vm)
- if child.vm:
- instances += self._get_instances_from_children(child.vm)
- else:
- self.debugl("ELSE ...")
- try:
- self.debugl(child.__dict__)
- except Exception:
- pass
- self.debugl(child)
- return instances
-
- def instances_to_inventory(self, instances):
- """Convert a list of vm objects into a json compliant inventory."""
-
- inventory = self._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 name 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():
-
- # set ansible_host (2.x)
- inventory['_meta']['hostvars'][k]['ansible_host'] = host_mapping[k]
-
- # 1.9.x backwards compliance
- inventory['_meta']['hostvars'][k]['ansible_ssh_host'] = (
- host_mapping[k])
-
- 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('PREFILTER_HOSTS:')
- for i in inventory['all']['hosts']:
- self.debugl(i)
-
- # Create special host filter removing all the hosts which
- # are not related to the configured cluster.
- if six.PY3:
- ocp_config = configparser.ConfigParser()
- else:
- ocp_config = configparser.SafeConfigParser()
- default_ocp_config = os.path.join(
- os.path.dirname(__file__), '../../../ocp-on-vmware.ini')
- ocp_ini_path = os.environ.get('VMWARE_INI_PATH', default_ocp_config)
- ocp_ini_path = os.path.expanduser(os.path.expandvars(ocp_ini_path))
- ocp_config.read(ocp_ini_path)
- cluster_id_filter = (
- "{{ config.annotation is not none and "
- "'%s' in config.annotation }}") % ocp_config.get(
- 'vmware', 'cluster_id')
- self.host_filters.append(cluster_id_filter)
-
- # 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('POSTFILTER_HOSTS:')
- for i in inventory['all']['hosts']:
- self.debugl(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)
-
- 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 = jinja2.Template(pattern)
- newkey = None
- try:
- newkey = t.render(v)
- newkey = newkey.strip()
- except Exception as e:
- self.debugl(e)
- if not newkey:
- continue
- elif 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_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
-
- rdata = {}
-
- # Do not serialize self
- if hasattr(vobj, '__name__'):
- if vobj.__name__ == 'VMWareInventory':
- return rdata
-
- # Exit early if maxlevel is reached
- if level > self.maxlevel:
- return rdata
-
- # Objects usually have a dict property
- if hasattr(vobj, '__dict__') and not level == 0:
-
- keys = sorted(vobj.__dict__.keys())
- for k in keys:
- v = vobj.__dict__[k]
- # Skip private methods
- if k.startswith('_'):
- continue
-
- if k.lower() in self.skip_keys:
- continue
-
- if self.lowerkeys:
- k = k.lower()
-
- rdata[k] = self._process_object_types(v, level=level)
- else:
- 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 = sorted(methods)
-
- for method in methods:
-
- if method in rdata:
- continue
-
- # Attempt to get the method, skip on fail
- try:
- methodToCall = getattr(vobj, method)
- except Exception:
- continue
-
- # Skip callable methods
- if callable(methodToCall):
- continue
-
- if self.lowerkeys:
- method = method.lower()
-
- rdata[method] = self._process_object_types(
- methodToCall,
- level=((level - 1)
- if method in ('guest', 'net') else level))
-
- return rdata
-
- def _process_object_types(self, vobj, level=0):
- rdata = {}
- self.debugl("PROCESSING: %s" % vobj)
-
- if type(vobj) in self.safe_types:
- try:
- rdata = vobj
- except Exception as e:
- self.debugl(e)
-
- elif hasattr(vobj, 'append'):
- rdata = []
- for vi in sorted(vobj):
- if type(vi) in self.safe_types:
- rdata.append(vi)
- else:
- if (level + 1 <= self.maxlevel):
- vid = self.facts_from_vobj(vi, level=(level + 1))
- if vid:
- rdata.append(vid)
-
- elif hasattr(vobj, '__dict__'):
- if (level + 1 <= self.maxlevel):
- md = None
- md = self.facts_from_vobj(vobj, level=(level + 1))
- if md:
- rdata = md
- elif not vobj or type(vobj) in self.safe_types:
- rdata = vobj
- elif type(vobj) == datetime.datetime:
- rdata = str(vobj)
- else:
- self.debugl("unknown datatype: %s" % type(vobj))
-
- if not rdata:
- rdata = None
- return rdata
-
- def get_host_info(self, host):
- """Return hostvars for a single host."""
- return self.inventory['_meta']['hostvars'][host]
-
-
-if __name__ == "__main__":
- # Run the script
- print(VMWareInventory().show())