@ -69,8 +69,8 @@ Examples:
$ contrib / inventory / gce . py - - host my_instance
$ contrib / inventory / gce . py - - host my_instance
Author : Eric Johnson < erjohnso @google.com >
Author : Eric Johnson < erjohnso @google.com >
Contributors : Matt Hite < mhite @hotmail.com >
Contributors : Matt Hite < mhite @hotmail.com > , Tom Melendez < supertom @google.com >
Version : 0.0 . 2
Version : 0.0 . 3
'''
'''
__requires__ = [ ' pycrypto>=2.6 ' ]
__requires__ = [ ' pycrypto>=2.6 ' ]
@ -89,6 +89,9 @@ USER_AGENT_VERSION="v2"
import sys
import sys
import os
import os
import argparse
import argparse
from time import time
import ConfigParser
import ConfigParser
import logging
import logging
@ -107,8 +110,57 @@ except:
sys . exit ( " GCE inventory script requires libcloud >= 0.13 " )
sys . exit ( " GCE inventory script requires libcloud >= 0.13 " )
class CloudInventoryCache ( object ) :
def __init__ ( self , cache_name = ' ansible-cloud-cache ' , cache_path = ' /tmp ' ,
cache_max_age = 300 ) :
cache_dir = os . path . expanduser ( cache_path )
if not os . path . exists ( cache_dir ) :
os . makedirs ( cache_dir )
self . cache_path_cache = os . path . join ( cache_dir , cache_name )
self . cache_max_age = cache_max_age
def is_valid ( self , max_age = None ) :
''' Determines if the cache files have expired, or if it is still valid '''
if max_age is None :
max_age = self . cache_max_age
if os . path . isfile ( self . cache_path_cache ) :
mod_time = os . path . getmtime ( self . cache_path_cache )
current_time = time ( )
if ( mod_time + max_age ) > current_time :
return True
return False
def get_all_data_from_cache ( self , filename = ' ' ) :
''' Reads the JSON inventory from the cache file. Returns Python dictionary. '''
data = ' '
if not filename :
filename = self . cache_path_cache
with open ( filename , ' r ' ) as cache :
data = cache . read ( )
return json . loads ( data )
def write_to_cache ( self , data , filename = ' ' ) :
''' Writes data to file as JSON. Returns True. '''
if not filename :
filename = self . cache_path_cache
json_data = json . dumps ( data )
with open ( filename , ' w ' ) as cache :
cache . write ( json_data )
return True
class GceInventory ( object ) :
class GceInventory ( object ) :
def __init__ ( self ) :
def __init__ ( self ) :
# Cache object
self . cache = None
# dictionary containing inventory read from disk
self . inventory = { }
# Read settings and parse CLI arguments
# Read settings and parse CLI arguments
self . parse_cli_args ( )
self . parse_cli_args ( )
self . config = self . get_config ( )
self . config = self . get_config ( )
@ -117,22 +169,36 @@ class GceInventory(object):
if self . ip_type :
if self . ip_type :
self . ip_type = self . ip_type . lower ( )
self . ip_type = self . ip_type . lower ( )
# Cache management
start_inventory_time = time ( )
cache_used = False
if self . args . refresh_cache or not self . cache . is_valid ( ) :
self . do_api_calls_update_cache ( )
else :
self . load_inventory_from_cache ( )
cache_used = True
self . inventory [ ' _meta ' ] [ ' stats ' ] = { ' use_cache ' : True }
self . inventory [ ' _meta ' ] [ ' stats ' ] = {
' inventory_load_time ' : time ( ) - start_inventory_time ,
' cache_used ' : cache_used
}
# Just display data for specific host
# Just display data for specific host
if self . args . host :
if self . args . host :
print ( self . json_format_dict ( self . node_to_dict (
print ( self . json_format_dict (
self . get_instance ( self . args . host ) ) ,
self . inventory [ ' _meta ' ] [ ' hostvars ' ] [ self . args . host ] ,
pretty = self . args . pretty ) )
pretty = self . args . pretty ) )
sys . exit ( 0 )
else :
# Otherwise, assume user wants all instances grouped
zones = self . parse_env_zones ( )
zones = self . parse_env_zones ( )
print ( self . json_format_dict ( self . inventory ,
# Otherwise, assume user wants all instances grouped
pretty = self . args . pretty ) )
print ( self . json_format_dict ( self . group_instances ( zones ) ,
pretty = self . args . pretty ) )
sys . exit ( 0 )
sys . exit ( 0 )
def get_config ( self ) :
def get_config ( self ) :
"""
"""
Reads the settings from the gce . ini file .
Populates a SafeConfigParser object with defaults and
Populates a SafeConfigParser object with defaults and
attempts to read an . ini - style configuration from the filename
attempts to read an . ini - style configuration from the filename
specified in GCE_INI_PATH . If the environment variable is
specified in GCE_INI_PATH . If the environment variable is
@ -153,11 +219,15 @@ class GceInventory(object):
' gce_project_id ' : ' ' ,
' gce_project_id ' : ' ' ,
' libcloud_secrets ' : ' ' ,
' libcloud_secrets ' : ' ' ,
' inventory_ip_type ' : ' ' ,
' inventory_ip_type ' : ' ' ,
' cache_path ' : ' ~/.ansible/tmp ' ,
' cache_max_age ' : ' 300 '
} )
} )
if ' gce ' not in config . sections ( ) :
if ' gce ' not in config . sections ( ) :
config . add_section ( ' gce ' )
config . add_section ( ' gce ' )
if ' inventory ' not in config . sections ( ) :
if ' inventory ' not in config . sections ( ) :
config . add_section ( ' inventory ' )
config . add_section ( ' inventory ' )
if ' cache ' not in config . sections ( ) :
config . add_section ( ' cache ' )
config . read ( gce_ini_path )
config . read ( gce_ini_path )
@ -173,6 +243,14 @@ class GceInventory(object):
if states :
if states :
self . instance_states = states . split ( ' , ' )
self . instance_states = states . split ( ' , ' )
# Caching
cache_path = config . get ( ' cache ' , ' cache_path ' )
cache_max_age = config . getint ( ' cache ' , ' cache_max_age ' )
# TOOD(supertom): support project-specific caches
cache_name = ' ansible-gce.cache '
self . cache = CloudInventoryCache ( cache_path = cache_path ,
cache_max_age = cache_max_age ,
cache_name = cache_name )
return config
return config
def get_inventory_options ( self ) :
def get_inventory_options ( self ) :
@ -252,6 +330,9 @@ class GceInventory(object):
help = ' Get all information about an instance ' )
help = ' Get all information about an instance ' )
parser . add_argument ( ' --pretty ' , action = ' store_true ' , default = False ,
parser . add_argument ( ' --pretty ' , action = ' store_true ' , default = False ,
help = ' Pretty format (default: False) ' )
help = ' Pretty format (default: False) ' )
parser . add_argument (
' --refresh-cache ' , action = ' store_true ' , default = False ,
help = ' Force refresh of cache by making API requests (default: False - use cache files) ' )
self . args = parser . parse_args ( )
self . args = parser . parse_args ( )
@ -290,12 +371,24 @@ class GceInventory(object):
' ansible_ssh_host ' : ssh_host
' ansible_ssh_host ' : ssh_host
}
}
def get_instance ( self , instance_name ) :
def load_inventory_from_cache ( self ) :
''' Gets details about a specific instance '''
''' Loads inventory from JSON on disk. '''
try :
try :
return self . driver . ex_get_node ( instance_name )
self . inventory = self . cache . get_all_data_from_cache ( )
hosts = self . inventory [ ' _meta ' ] [ ' hostvars ' ]
except Exception as e :
except Exception as e :
return None
print (
" Invalid inventory file %s . Please rebuild with -refresh-cache option. "
% ( self . cache . cache_path_cache ) )
raise
def do_api_calls_update_cache ( self ) :
''' Do API calls and save data in cache. '''
zones = self . parse_env_zones ( )
data = self . group_instances ( zones )
self . cache . write_to_cache ( data )
self . inventory = data
def group_instances ( self , zones = None ) :
def group_instances ( self , zones = None ) :
''' Group all instances '''
''' Group all instances '''
@ -369,6 +462,6 @@ class GceInventory(object):
else :
else :
return json . dumps ( data )
return json . dumps ( data )
# Run the script
# Run the script
GceInventory ( )
if __name__ == ' __main__ ' :
GceInventory ( )