#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
VMWARE external inventory script
=================================

shamelessly copied from existing inventory scripts.

This script and it's ini can be used more than once,

i.e. vmware.py/vmware_colo.ini vmware_idf.py/vmware_idf.ini
(script can be link)

so if you don't have clustered vcenter  but multiple esx machines or
just diff clusters you can have an inventory  per each and automatically
group hosts based on file name or specify a group in the ini.

You can also use <SCRIPT_NAME>_HOST|USER|PASSWORD environment variables
to override the ini.
'''

import os
import sys
import time
import ConfigParser
from psphere.client import Client
from psphere.managedobjects import HostSystem

try:
    import json
except ImportError:
    import simplejson as json


def save_cache(cache_item, data, config):
    ''' saves item to cache '''

    if config.has_option('cache', 'dir'):
        dpath = os.path.expanduser(config.get('cache', 'dir'))
        try:
            if not os.path.exists(dpath):
                os.makedirs(dpath)
            if os.path.isdir(dpath):
                cache = open('/'.join([dpath,cache_item]), 'w')
                cache.write(json.dumps(data))
                cache.close()
        except IOError, e:
            pass # not really sure what to do here


def get_cache(cache_item, config):
    ''' returns cached item  '''

    inv = {}
    if config.has_option('cache', 'dir'):
        dpath = os.path.expanduser(config.get('cache', 'dir'))
        try:
            cache = open('/'.join([dpath,cache_item]), 'r')
            inv = json.loads(cache.read())
            cache.close()
        except IOError, e:
            pass # not really sure what to do here

    return inv

def cache_available(cache_item, config):
    ''' checks if we have a 'fresh' cache available for item requested '''

    if config.has_option('cache', 'dir'):
        dpath = os.path.expanduser(config.get('cache', 'dir'))

        try:
            existing = os.stat('/'.join([dpath,cache_item]))
        except:
            # cache doesn't exist or isn't accessible
            return False

        if config.has_option('cache', 'max_age'):
            maxage = config.get('cache', 'max_age')
            fileage = int( time.time() - existing.st_mtime )
            if (maxage > fileage):
                return True

    return False

def get_host_info(host):
    ''' Get variables about a specific host '''

    hostinfo = {
        'vmware_name' : host.name,
    }
    for k in host.capability.__dict__.keys():
        if k.startswith('_'):
           continue
        try:
            hostinfo['vmware_' + k] = str(host.capability[k])
        except:
           continue

    return hostinfo


def get_inventory(client, config):
    ''' Reads the inventory from cache or vmware api '''

    inv = {}

    if cache_available('inventory', config):
        inv = get_cache('inventory',config)
    elif client:
        inv= { 'all': {'hosts': []}, '_meta': { 'hostvars': {} } }
        default_group = os.path.basename(sys.argv[0]).rstrip('.py')

        if config.has_option('defaults', 'guests_only'):
            guests_only = config.get('defaults', 'guests_only')
        else:
            guests_only = True

        if not guests_only:
            if config.has_option('defaults','hw_group'):
                hw_group = config.get('defaults','hw_group')
            else:
                hw_group = default_group + '_hw'
            inv[hw_group] = []

        if config.has_option('defaults','vm_group'):
            vm_group = config.get('defaults','vm_group')
        else:
            vm_group = default_group + '_vm'
        inv[vm_group] = []

        # Loop through physical hosts:
        hosts = HostSystem.all(client)
        for host in hosts:
            if not guests_only:
                inv['all']['hosts'].append(host.name)
                inv[hw_group].append(host.name)
                inv['_meta']['hostvars'][host.name] = get_host_info(host)
                save_cache(vm.name, inv['_meta']['hostvars'][host.name], config)

            for vm in host.vm:
                inv['all']['hosts'].append(vm.name)
                inv[vm_group].append(vm.name)
                inv['_meta']['hostvars'][vm.name] = get_host_info(vm)
                save_cache(vm.name, inv['_meta']['hostvars'][vm.name], config)

        save_cache('inventory', inv, config)

    return json.dumps(inv)

def get_single_host(client, config, hostname):

    inv = {}
    if cache_available(hostname, config):
        inv = get_cache(hostname,config)
    elif client:
        hosts = HostSystem.all(client) #TODO: figure out single host getter
        for host in hosts:
            if hostname == host.name:
                inv = get_host_info(host)
                break
            for vm in host.vm:
                if hostname == vm.name:
                    inv = get_host_info(vm)
                    break
        save_cache(hostname,inv,config)

    return json.dumps(inv)

if __name__ == '__main__':

    inventory = {}
    hostname = None

    if len(sys.argv) > 1:
        if sys.argv[1] == "--host":
            hostname = sys.argv[2]

    # Read config
    config = ConfigParser.SafeConfigParser()
    me = os.path.abspath(sys.argv[0]).rstrip('.py')
    for configfilename in [me + '.ini', 'vmware.ini']:
        if os.path.exists(configfilename):
            config.read(configfilename)
            break

    mename = os.path.basename(me).upper()
    host =  os.getenv('VMWARE_' + mename + '_HOST',os.getenv('VMWARE_HOST', config.get('auth','host')))
    user = os.getenv('VMWARE_' + mename + '_USER', os.getenv('VMWARE_USER', config.get('auth','user')))
    password = os.getenv('VMWARE_' + mename + '_PASSWORD',os.getenv('VMWARE_PASSWORD', config.get('auth','password')))

    try:
        client =  Client( host,user,password )
    except Exception, e:
        client = None
        #print >> STDERR "Unable to login (only cache available): %s", str(e)

    # Actually do the work
    if hostname is None:
        inventory = get_inventory(client, config)
    else:
        inventory = get_single_host(client, config, hostname)

    # Return to ansible
    print inventory
