diff options
| author | Kenny Woodson <kwoodson@redhat.com> | 2014-12-12 14:31:19 -0500 | 
|---|---|---|
| committer | Kenny Woodson <kwoodson@redhat.com> | 2014-12-12 14:31:19 -0500 | 
| commit | dc548678b40dc400b62614df846e25a50d656b39 (patch) | |
| tree | c7e0bc651162a8f56c95ebd07bd0a874fe51f573 /inventory | |
| parent | 95ae631e6e5f2230a912d65d7add0178e7878079 (diff) | |
| download | openshift-dc548678b40dc400b62614df846e25a50d656b39.tar.gz openshift-dc548678b40dc400b62614df846e25a50d656b39.tar.bz2 openshift-dc548678b40dc400b62614df846e25a50d656b39.tar.xz openshift-dc548678b40dc400b62614df846e25a50d656b39.zip | |
First version of meta inventory.
Diffstat (limited to 'inventory')
| -rw-r--r-- | inventory/.gitignore | 1 | ||||
| -rwxr-xr-x | inventory/meta.py | 183 | ||||
| -rw-r--r-- | inventory/meta.yaml.example | 15 | 
3 files changed, 199 insertions, 0 deletions
| diff --git a/inventory/.gitignore b/inventory/.gitignore new file mode 100644 index 000000000..055263611 --- /dev/null +++ b/inventory/.gitignore @@ -0,0 +1 @@ +meta.yaml diff --git a/inventory/meta.py b/inventory/meta.py new file mode 100755 index 000000000..c13a0295c --- /dev/null +++ b/inventory/meta.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python + +from time import time +import argparse +import yaml +import os +import sys +import pdb +import subprocess +import json +import pprint + + +class MetaInventory(object): + +    def __init__(self): +        self.config = None +        self.results = {} +        self.result = {} +        self.cache_path_cache = os.path.expanduser('~/.ansible/tmp/meta-inventory.cache') + +        self.parse_cli_args() + +        # load yaml +        self.load_yaml_config() + +        # if its a host query, fetch and do not cache +        if self.args.host: +            self.get_inventory() +        elif not self.is_cache_valid(): +            # go fetch the inventories and cache them if cache is expired +            self.get_inventory() +            self.write_to_cache() +        else: +            # get data from disk +            self.get_inventory_from_cache() + +    def load_yaml_config(self,conf_file=os.path.join(os.getcwd(),'meta.yaml')): +        """Load a yaml config file with credentials to query the +        respective cloud for inventory. +        """ +        config = None +        with open(conf_file) as conf: +          self.config = yaml.safe_load(conf) + +    def get_provider_tags(self,provider, env={}): +        """Call <provider> and query all of the tags that are usuable +        by ansible.  If environment is empty use the default env. +        """ +        if not env: +            env = os.environ + +        # check to see if provider exists +        if not os.path.isfile(os.path.join(os.getcwd(),provider)): +            raise RuntimeError("Unkown provider: %s" % provider) + +        cmds = [provider] +        if self.args.host: +            cmds.append("--host") +            cmds.append(self.args.host) +        else: +            cmds.append('--list') + +        cmds.append('--refresh-cache') + +        return subprocess.Popen(cmds, stderr=subprocess.PIPE, \ +                                stdout=subprocess.PIPE, env=env) +    def get_inventory(self): +        """Create the subprocess to fetch tags from a provider. +        Host query: +        Query to return a specific host.  If > 1 queries have +        results then fail. + +        List query: +        Query all of the different clouds for their tags.  Once completed +        store all of their results into one merged updated hash. +        """ +        processes = {} +        for account in self.config['clouds']: +            env = account['env_vars'] +            name = account['name'] +            provider = account['provider'] +            processes[name] = self.get_provider_tags(provider, env) + +        # for each process collect stdout when its available +        all_results = [] +        for name, process in processes.items(): +            out, err = process.communicate() +            all_results.append({ +                "name": name, +                "out": out.strip(), +                "err": err.strip(), +                "code": process.returncode +            }) + +        if not self.args.host: +            # For any non-zero, raise an error on it +            for result in all_results: +                if result['code'] != 0: +                    raise RuntimeError(result['err']) +                else: +                    self.results[result['name']] = json.loads(result['out']) +            self.merge() +        else: +            # For any 0 result, return it +            count = 0 +            for results in all_results: +                if results['code'] == 0 and results['err'] == '' and results['out'] != '{}': +                    self.result = json.loads(out) +                    count += 1 +                if count > 1: +                    raise RuntimeError("Found > 1 results for --host %s. \ +                                       This is an invalid state." % self.args.host) + +    def merge(self): +        """Merge the results into a single hash.  Duplicate keys are placed +        into a list. +        """ +        for name, cloud_result in self.results.items(): +            for k,v in cloud_result.items(): +                if self.result.has_key(k): +                    # need to combine into a list +                    if isinstance(self.result[k], list): +                        self.result[k].append(v) +                    else: +                        self.result[k] = [self.result[k],v] +                else: +                    self.result[k] = [v] + +        self.result = self.json_format_dict(self.result) + +    def is_cache_valid(self): +        ''' Determines if the cache files have expired, or if it is still valid ''' + +        if os.path.isfile(self.cache_path_cache): +            mod_time = os.path.getmtime(self.cache_path_cache) +            current_time = time() +            if (mod_time + self.config['cache_max_age']) > current_time: +                #if os.path.isfile(self.cache_path_index): +                return True + +        return False + +    def parse_cli_args(self): +        ''' Command line argument processing ''' + +        parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on a provider') +        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') +        self.args = parser.parse_args() + +    def write_to_cache(self): +        ''' Writes data in JSON format to a file ''' + +        json_data = self.json_format_dict(self.result, True) +        with open(self.cache_path_cache, 'w') as cache: +            cache.write(json_data) + +    def get_inventory_from_cache(self): +        ''' Reads the inventory from the cache file and returns it as a JSON +        object ''' + +        with open(self.cache_path_cache, 'r') as cache: +            self.result = json.loads(cache.read()) + +    def json_format_dict(self, data, pretty=False): +        ''' Converts a dict to a JSON object and dumps it as a formatted +        string ''' + +        if pretty: +            return json.dumps(data, sort_keys=True, indent=2) +        else: +            return json.dumps(data) + + +if __name__ == "__main__": +    mi = MetaInventory() +    #print mi.result +    pp = pprint.PrettyPrinter(indent=2) +    pp.pprint(mi.result) + diff --git a/inventory/meta.yaml.example b/inventory/meta.yaml.example new file mode 100644 index 000000000..bba26f016 --- /dev/null +++ b/inventory/meta.yaml.example @@ -0,0 +1,15 @@ +# meta inventory configs +clouds: +  - name: aws1 +    provider: aws/ec2.py +    env_vars: +      AWS_ACCESS_KEY_ID: XXXXXXXXXXXXXXXXXXXX +      AWS_SECRET_ACCESS_KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + +  - name: aws2 +    provider: aws/ec2.py +    env_vars: +      AWS_ACCESS_KEY_ID: XXXXXXXXXXXXXXXXXXXX +      AWS_SECRET_ACCESS_KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + +cache_max_age: 60 | 
