Last active
March 16, 2023 15:48
-
-
Save tboerger/c37e95f063f604951efc4ece43bf2246 to your computer and use it in GitHub Desktop.
Revisions
-
tboerger revised this gist
May 13, 2020 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -196,6 +196,7 @@ def call_update_cache(self): for vapp in self.gather_vapp_list(): vapp_name = vapp.get('name') self.inventory['server']['children'].append(vapp_name) if not vapp_name in self.inventory.keys(): self.inventory[vapp_name] = { -
tboerger revised this gist
May 13, 2020 . 2 changed files with 213 additions and 136 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -3,168 +3,245 @@ import json import requests import os import argparse import xml.etree.cElementTree as ET from time import time GLOBALS = { 'ansible_become': 'true', 'ansible_python_interpreter': '/usr/bin/python3', 'ansible_ssh_common_args': '-o StrictHostKeyChecking=no -o ProxyCommand="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -W %h:%p [email protected]"' } OVERRIDE = { 'bastion-01': { 'ansible_host': '192.168.1.1', 'ansible_ssh_common_args': '-o StrictHostKeyChecking=no' }, 'bastion-02': { 'ansible_host': '192.168.1.2', 'ansible_ssh_common_args': '-o StrictHostKeyChecking=no' } } class VcdInventory(object): def _empty_inventory(self): return { 'server': { 'hosts': [] }, '_meta': { 'hostvars': {} } } def __init__(self): self.inventory = self._empty_inventory() self.credentials = { 'base_url': '', 'username': '', 'password': '', 'org': '', 'headers': { 'Accept': 'application/*+xml;version=30.0' }, } self.parse_cli_args() self.read_credentials() self.configure_cache() if self.args.refresh_cache: self.call_update_cache() elif not self.is_cache_valid(): self.call_update_cache() if self.args.host: data_to_print = self.host_information() elif self.args.list: if self.inventory == self._empty_inventory(): data_to_print = self.inventory_from_cache() else: data_to_print = self.json_format_dict(self.inventory, True) print(data_to_print) def parse_cli_args(self): parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on vCD') parser.add_argument( '--list', action='store_true', default=True, help='List instances, used as default action as well' ) parser.add_argument( '--host', action='store', default=False, 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' ) self.args = parser.parse_args() def read_credentials(self): self.credentials['base_url'] = os.environ.get('VCD_URL', '') if not self.credentials['base_url'].strip(): print('Missing VCD_URL environment variable!') exit(1) self.credentials['username'] = os.environ.get('VCD_USER', '') if not self.credentials['username'].strip(): print('Missing VCD_USER environment variable!') exit(1) self.credentials['password'] = os.environ.get('VCD_PASSWORD', '') if not self.credentials['password'].strip(): print('Missing VCD_PASSWORD environment variable!') exit(1) self.credentials['org'] = os.environ.get('VCD_ORG', '') if not self.credentials['org'].strip(): print('Missing VCD_ORG environment variable!') exit(1) def configure_cache(self): cache_dir = os.path.expanduser('~/.vcd/cache') if not os.path.exists(cache_dir): os.makedirs(cache_dir) cache_name = self.credentials.get('org') cache_name += '-' + self.credentials.get('username') self.cache_path_file = os.path.join(cache_dir, '%s.cache' % cache_name) self.cache_max_age = 900 def is_cache_valid(self): if os.path.isfile(self.cache_path_file): mod_time = os.path.getmtime(self.cache_path_file) current_time = time() return (mod_time + self.cache_max_age) > current_time return False def write_to_cache(self, data, filename): with open(filename, 'w') as f: f.write(self.json_format_dict(data, True)) def gather_vapp_list(self): url = self.credentials['base_url'] + '/vApps/query' return self.extract_from_tree( url ).findall( '{http://www.vmware.com/vcloud/v1.5}VAppRecord' ) def gather_hosts_from(self, href): return self.extract_from_tree( href ).iter( '{http://www.vmware.com/vcloud/v1.5}Vm' ) def extract_from_tree(self, url): return ET.fromstring( requests.get( url, headers=self.credentials['headers'] ).content ) def merge_available_attrs(self, host): result = { 'ansible_host': self.search_within_attrs( host, '{http://www.vmware.com/vcloud/v1.5}IpAddress', True, '' ) } result.update(GLOBALS) if host.get('name') in OVERRIDE.keys(): result.update(OVERRIDE[host.get('name')]) return result def search_within_attrs(self, root, tag, text, attr): for elem in root.iter(tag): if text: return elem.text else: return elem.get(attr) def call_update_cache(self): self.authenticate_to_api() for vapp in self.gather_vapp_list(): vapp_name = vapp.get('name') if not vapp_name in self.inventory.keys(): self.inventory[vapp_name] = { 'hosts': [] } for host in self.gather_hosts_from(vapp.get('href')): host_name = host.get('name') self.inventory['server']['hosts'].append(host.get('name')) self.inventory[vapp_name]['hosts'].append(host.get('name')) self.inventory['_meta']['hostvars'][host_name] = self.merge_available_attrs(host) self.write_to_cache(self.inventory, self.cache_path_file) def authenticate_to_api(self): url = self.credentials['base_url'] + '/sessions' session = requests.post( url, headers=self.credentials['headers'], auth=(self.credentials['username'] + '@' + self.credentials['org'], self.credentials['password']) ) self.credentials['headers']['x-vcloud-authorization'] = session.headers['x-vcloud-authorization'] def inventory_from_cache(self): with open(self.cache_path_file, 'r') as f: return f.read() def json_format_dict(self, data, pretty=False): if pretty: return json.dumps(data, sort_keys=True, indent=2) else: return json.dumps(data) def host_information(self): self.inventory = json.loads(self.inventory_from_cache()) if self.args.host not in self.inventory['_meta']['hostvars'].keys(): self.call_update_cache() if self.args.host not in self.inventory['_meta']['hostvars'].keys(): return self.json_format_dict({}, True) return self.json_format_dict(self.inventory['_meta']['hostvars'][self.args.host], True) if __name__ == '__main__': VcdInventory() This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -1,14 +1,4 @@ { "_meta": { "hostvars": { "bastion-01": { @@ -66,5 +56,15 @@ "mariadb-01", "mariadb-02" ] }, "server": { "hosts": [ "bastion-01", "bastion-02", "haproxy-01", "haproxy-02", "mariadb-01", "mariadb-02" ] } } -
tboerger created this gist
May 13, 2020 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,170 @@ #!/usr/bin/env python3 import json import requests import os import xml.etree.cElementTree as ET # # Just provide the following environment variables: # VCD_URL - https://vcloud.example.com/api # VCD_USER - username # VCD_PASSWORD - p455w0rd # VCD_ORG - octocat # def main(): config = { 'base_url': '', 'username': '', 'password': '', 'org': '', 'headers': { 'Accept': 'application/*+xml;version=30.0' }, # globals gets merged into all hostvars for every host 'globals': { 'ansible_become': 'true', 'ansible_python_interpreter': '/usr/bin/python3', 'ansible_ssh_common_args': '-o StrictHostKeyChecking=no -o ProxyCommand="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -W %h:%p [email protected]"' }, # override can be used to override specific hostvars 'override': { 'bastion-01': { 'ansible_host': '192.168.1.1', 'ansible_ssh_common_args': '-o StrictHostKeyChecking=no' }, 'bastion-02': { 'ansible_host': '192.168.1.2', 'ansible_ssh_common_args': '-o StrictHostKeyChecking=no' } } } config['base_url'] = os.environ.get('VCD_URL', '') if not config['base_url'].strip(): print('Missing VCD_URL environment variable!') exit(1) config['username'] = os.environ.get('VCD_USER', '') if not config['username'].strip(): print('Missing VCD_USER environment variable!') exit(1) config['password'] = os.environ.get('VCD_PASSWORD', '') if not config['password'].strip(): print('Missing VCD_PASSWORD environment variable!') exit(1) config['org'] = os.environ.get('VCD_ORG', '') if not config['org'].strip(): print('Missing VCD_ORG environment variable!') exit(1) login(config) available = {} server = [] root = { 'server': { 'hosts': server }, '_meta': { 'hostvars': available } } for vapp in vapps(config): for host in hosts(config, vapp.get('href')): host_name = host.get('name') server.append(host.get('name')) available[host_name] = fillup(config, host) groups(root, vapp, host) print(json.dumps(root)) def login(config): url = config['base_url'] + '/sessions' session = requests.post( url, headers=config['headers'], auth=(config['username'] + "@" + config['org'], config['password']) ) config['headers']['x-vcloud-authorization'] = session.headers['x-vcloud-authorization'] def vapps(config): url = config['base_url'] + '/vApps/query' return tree( url, config['headers'], ).findall( '{http://www.vmware.com/vcloud/v1.5}VAppRecord' ) def hosts(config, href): return tree( href, config['headers'], ).iter( '{http://www.vmware.com/vcloud/v1.5}Vm' ) def tree(url, headers): return ET.fromstring( requests.get( url, headers=headers ).content ) def fillup(config, host): result = { 'ansible_host': search( host, '{http://www.vmware.com/vcloud/v1.5}IpAddress', True, '' ) } result.update(config['globals']) if host.get('name') in config['override'].keys(): result.update(config['override'][host.get('name')]) return result def search(root, tag, text, attr): for elem in root.iter(tag): if text: return elem.text else: return elem.get(attr) def groups(root, vapp, host): name = vapp.get('name') if not name in root: root[name] = { 'hosts': [] } root[name]['hosts'].append(search( host, '{http://www.vmware.com/vcloud/v1.5}Vm', False, "name" )) if __name__ == '__main__': main() This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,70 @@ { "server": { "hosts": [ "bastion-01", "bastion-02", "haproxy-01", "haproxy-02", "mariadb-01", "mariadb-02" ] }, "_meta": { "hostvars": { "bastion-01": { "ansible_host": "192.168.1.1", "ansible_become": "true", "ansible_python_interpreter": "/usr/bin/python3", "ansible_ssh_common_args": "-o StrictHostKeyChecking=no" }, "bastion-02": { "ansible_host": "192.168.1.2", "ansible_become": "true", "ansible_python_interpreter": "/usr/bin/python3", "ansible_ssh_common_args": "-o StrictHostKeyChecking=no" }, "haproxy-01": { "ansible_host": "10.10.0.21", "ansible_become": "true", "ansible_python_interpreter": "/usr/bin/python3", "ansible_ssh_common_args": "-o StrictHostKeyChecking=no -o ProxyCommand=\"ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -W %h:%p [email protected]\"" }, "haproxy-02": { "ansible_host": "10.10.0.22", "ansible_become": "true", "ansible_python_interpreter": "/usr/bin/python3", "ansible_ssh_common_args": "-o StrictHostKeyChecking=no -o ProxyCommand=\"ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -W %h:%p [email protected]\"" }, "mariadb-01": { "ansible_host": "10.10.8.1", "ansible_become": "true", "ansible_python_interpreter": "/usr/bin/python3", "ansible_ssh_common_args": "-o StrictHostKeyChecking=no -o ProxyCommand=\"ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -W %h:%p [email protected]\"" }, "mariadb-02": { "ansible_host": "10.10.8.2", "ansible_become": "true", "ansible_python_interpreter": "/usr/bin/python3", "ansible_ssh_common_args": "-o StrictHostKeyChecking=no -o ProxyCommand=\"ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -W %h:%p [email protected]\"" } } }, "bastion": { "hosts": [ "bastion-01", "bastion-02" ] }, "haproxy": { "hosts": [ "haproxy-01", "haproxy-02" ] }, "mariadb": { "hosts": [ "mariadb-01", "mariadb-02" ] } }