summaryrefslogtreecommitdiffstats
path: root/inventory
diff options
context:
space:
mode:
Diffstat (limited to 'inventory')
-rw-r--r--inventory/byo/hosts2
-rwxr-xr-xinventory/libvirt/hosts/libvirt_generic.py95
-rwxr-xr-xinventory/multi_ec2.py161
-rw-r--r--inventory/multi_ec2.yaml.example16
-rw-r--r--inventory/openshift-ansible-inventory.spec29
5 files changed, 214 insertions, 89 deletions
diff --git a/inventory/byo/hosts b/inventory/byo/hosts
index 728eec8aa..9a1cbce29 100644
--- a/inventory/byo/hosts
+++ b/inventory/byo/hosts
@@ -17,7 +17,7 @@ ansible_ssh_user=root
deployment_type=enterprise
# Pre-release registry URL
-openshift_registry_url=docker-buildvm-rhose.usersys.redhat.com:5000/openshift3_beta/ose-${component}:${version}
+oreg_url=docker-buildvm-rhose.usersys.redhat.com:5000/openshift3_beta/ose-${component}:${version}
# Pre-release additional repo
#openshift_additional_repos=[{'id': 'ose-devel', 'name': 'ose-devel', 'baseurl': 'http://buildvm-devops.usersys.redhat.com/puddle/build/OpenShiftEnterprise/3.0/latest/RH7-RHOSE-3.0/$basearch/os', 'enabled': 1, 'gpgcheck': 0}]
diff --git a/inventory/libvirt/hosts/libvirt_generic.py b/inventory/libvirt/hosts/libvirt_generic.py
index 4652f112e..1c9c17308 100755
--- a/inventory/libvirt/hosts/libvirt_generic.py
+++ b/inventory/libvirt/hosts/libvirt_generic.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python2
-"""
+'''
libvirt external inventory script
=================================
@@ -12,7 +12,7 @@ To use this, copy this file over /etc/ansible/hosts and chmod +x the file.
This, more or less, allows you to keep one central database containing
info about all of your managed instances.
-"""
+'''
# (c) 2015, Jason DeTiberus <jdetiber@redhat.com>
#
@@ -36,9 +36,7 @@ info about all of your managed instances.
import argparse
import ConfigParser
import os
-import re
import sys
-from time import time
import libvirt
import xml.etree.ElementTree as ET
@@ -49,8 +47,11 @@ except ImportError:
class LibvirtInventory(object):
+ ''' libvirt dynamic inventory '''
def __init__(self):
+ ''' Main execution path '''
+
self.inventory = dict() # A list of groups and the hosts in that group
self.cache = dict() # Details about hosts in the inventory
@@ -59,13 +60,15 @@ class LibvirtInventory(object):
self.parse_cli_args()
if self.args.host:
- print self.json_format_dict(self.get_host_info(), self.args.pretty)
+ print _json_format_dict(self.get_host_info(), self.args.pretty)
elif self.args.list:
- print self.json_format_dict(self.get_inventory(), self.args.pretty)
+ print _json_format_dict(self.get_inventory(), self.args.pretty)
else: # default action with no options
- print self.json_format_dict(self.get_inventory(), self.args.pretty)
+ print _json_format_dict(self.get_inventory(), self.args.pretty)
def read_settings(self):
+ ''' Reads the settings from the libvirt.ini file '''
+
config = ConfigParser.SafeConfigParser()
config.read(
os.path.dirname(os.path.realpath(__file__)) + '/libvirt.ini'
@@ -73,6 +76,8 @@ class LibvirtInventory(object):
self.libvirt_uri = config.get('libvirt', 'uri')
def parse_cli_args(self):
+ ''' Command line argument processing '''
+
parser = argparse.ArgumentParser(
description='Produce an Ansible Inventory file based on libvirt'
)
@@ -96,25 +101,27 @@ class LibvirtInventory(object):
self.args = parser.parse_args()
def get_host_info(self):
+ ''' Get variables about a specific host '''
+
inventory = self.get_inventory()
if self.args.host in inventory['_meta']['hostvars']:
return inventory['_meta']['hostvars'][self.args.host]
def get_inventory(self):
+ ''' Construct the inventory '''
+
inventory = dict(_meta=dict(hostvars=dict()))
conn = libvirt.openReadOnly(self.libvirt_uri)
if conn is None:
- print "Failed to open connection to %s" % libvirt_uri
+ print "Failed to open connection to %s" % self.libvirt_uri
sys.exit(1)
domains = conn.listAllDomains()
if domains is None:
- print "Failed to list domains for connection %s" % libvirt_uri
+ print "Failed to list domains for connection %s" % self.libvirt_uri
sys.exit(1)
- arp_entries = self.parse_arp_entries()
-
for domain in domains:
hostvars = dict(libvirt_name=domain.name(),
libvirt_id=domain.ID(),
@@ -130,21 +137,30 @@ class LibvirtInventory(object):
hostvars['libvirt_status'] = 'running'
root = ET.fromstring(domain.XMLDesc())
- ns = {'ansible': 'https://github.com/ansible/ansible'}
- for tag_elem in root.findall('./metadata/ansible:tags/ansible:tag', ns):
+ ansible_ns = {'ansible': 'https://github.com/ansible/ansible'}
+ for tag_elem in root.findall('./metadata/ansible:tags/ansible:tag', ansible_ns):
tag = tag_elem.text
- self.push(inventory, "tag_%s" % tag, domain_name)
- self.push(hostvars, 'libvirt_tags', tag)
+ _push(inventory, "tag_%s" % tag, domain_name)
+ _push(hostvars, 'libvirt_tags', tag)
# TODO: support more than one network interface, also support
# interface types other than 'network'
interface = root.find("./devices/interface[@type='network']")
if interface is not None:
+ source_elem = interface.find('source')
mac_elem = interface.find('mac')
- if mac_elem is not None:
- mac = mac_elem.get('address')
- if mac in arp_entries:
- ip_address = arp_entries[mac]['ip_address']
+ if source_elem is not None and \
+ mac_elem is not None:
+ # Adding this to disable pylint check specifically
+ # ignoring libvirt-python versions that
+ # do not include DHCPLeases
+ # This is needed until we upgrade the build bot to
+ # RHEL7 (>= 1.2.6 libvirt)
+ # pylint: disable=no-member
+ dhcp_leases = conn.networkLookupByName(source_elem.get('network')) \
+ .DHCPLeases(mac_elem.get('address'))
+ if len(dhcp_leases) > 0:
+ ip_address = dhcp_leases[0]['ipaddr']
hostvars['ansible_ssh_host'] = ip_address
hostvars['libvirt_ip_address'] = ip_address
@@ -152,28 +168,23 @@ class LibvirtInventory(object):
return inventory
- def parse_arp_entries(self):
- arp_entries = dict()
- with open('/proc/net/arp', 'r') as f:
- # throw away the header
- f.readline()
-
- for line in f:
- ip_address, _, _, mac, _, device = line.strip().split()
- arp_entries[mac] = dict(ip_address=ip_address, device=device)
-
- return arp_entries
-
- def push(self, my_dict, key, element):
- if key in my_dict:
- my_dict[key].append(element)
- else:
- my_dict[key] = [element]
-
- 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 _push(my_dict, key, element):
+ '''
+ Push element to the my_dict[key] list.
+ After having initialized my_dict[key] if it dosn't exist.
+ '''
+
+ if key in my_dict:
+ my_dict[key].append(element)
+ else:
+ my_dict[key] = [element]
+
+def _json_format_dict(data, pretty=False):
+ ''' Serialize data to a JSON formated str '''
+
+ if pretty:
+ return json.dumps(data, sort_keys=True, indent=2)
+ else:
+ return json.dumps(data)
LibvirtInventory()
diff --git a/inventory/multi_ec2.py b/inventory/multi_ec2.py
index b99212dd5..f8196aefd 100755
--- a/inventory/multi_ec2.py
+++ b/inventory/multi_ec2.py
@@ -13,7 +13,8 @@ import subprocess
import json
import errno
import fcntl
-
+import tempfile
+import copy
CONFIG_FILE_NAME = 'multi_ec2.yaml'
DEFAULT_CACHE_PATH = os.path.expanduser('~/.ansible/tmp/multi_ec2_inventory.cache')
@@ -25,8 +26,14 @@ class MultiEc2(object):
Stores a json hash of resources in result.
'''
- def __init__(self):
- self.args = None
+ def __init__(self, args=None):
+ # Allow args to be passed when called as a library
+ if not args:
+ self.args = {}
+ else:
+ self.args = args
+
+ self.cache_path = DEFAULT_CACHE_PATH
self.config = None
self.all_ec2_results = {}
self.result = {}
@@ -43,8 +50,15 @@ class MultiEc2(object):
else:
self.config_file = None # expect env vars
- self.parse_cli_args()
+ def run(self):
+ '''This method checks to see if the local
+ cache is valid for the inventory.
+
+ if the cache is valid; return cache
+ else the credentials are loaded from multi_ec2.yaml or from the env
+ and we attempt to get the inventory from the provider specified.
+ '''
# load yaml
if self.config_file and os.path.isfile(self.config_file):
self.config = self.load_yaml_config()
@@ -69,15 +83,14 @@ class MultiEc2(object):
raise RuntimeError("Could not find valid ec2 credentials in the environment.")
# Set the default cache path but if its defined we'll assign it.
- self.cache_path = DEFAULT_CACHE_PATH
if self.config.has_key('cache_location'):
self.cache_path = self.config['cache_location']
- if self.args.refresh_cache:
+ if self.args.get('refresh_cache', None):
self.get_inventory()
self.write_to_cache()
# if its a host query, fetch and do not cache
- elif self.args.host:
+ elif self.args.get('host', None):
self.get_inventory()
elif not self.is_cache_valid():
# go fetch the inventories and cache them if cache is expired
@@ -118,9 +131,9 @@ class MultiEc2(object):
"and that it is executable. (%s)" % provider)
cmds = [provider]
- if self.args.host:
+ if self.args.get('host', None):
cmds.append("--host")
- cmds.append(self.args.host)
+ cmds.append(self.args.get('host', None))
else:
cmds.append('--list')
@@ -128,6 +141,54 @@ class MultiEc2(object):
return subprocess.Popen(cmds, stderr=subprocess.PIPE, \
stdout=subprocess.PIPE, env=env)
+
+ @staticmethod
+ def generate_config(config_data):
+ """Generate the ec2.ini file in as a secure temp file.
+ Once generated, pass it to the ec2.py as an environment variable.
+ """
+ fildes, tmp_file_path = tempfile.mkstemp(prefix='multi_ec2.ini.')
+ for section, values in config_data.items():
+ os.write(fildes, "[%s]\n" % section)
+ for option, value in values.items():
+ os.write(fildes, "%s = %s\n" % (option, value))
+ os.close(fildes)
+ return tmp_file_path
+
+ def run_provider(self):
+ '''Setup the provider call with proper variables
+ and call self.get_provider_tags.
+ '''
+ try:
+ all_results = []
+ tmp_file_paths = []
+ processes = {}
+ for account in self.config['accounts']:
+ env = account['env_vars']
+ if account.has_key('provider_config'):
+ tmp_file_paths.append(MultiEc2.generate_config(account['provider_config']))
+ env['EC2_INI_PATH'] = tmp_file_paths[-1]
+ name = account['name']
+ provider = account['provider']
+ processes[name] = self.get_provider_tags(provider, env)
+
+ # for each process collect stdout when its available
+ for name, process in processes.items():
+ out, err = process.communicate()
+ all_results.append({
+ "name": name,
+ "out": out.strip(),
+ "err": err.strip(),
+ "code": process.returncode
+ })
+
+ finally:
+ # Clean up the mkstemp file
+ for tmp_file in tmp_file_paths:
+ os.unlink(tmp_file)
+
+ return all_results
+
def get_inventory(self):
"""Create the subprocess to fetch tags from a provider.
Host query:
@@ -138,46 +199,61 @@ class MultiEc2(object):
Query all of the different accounts for their tags. Once completed
store all of their results into one merged updated hash.
"""
- processes = {}
- for account in self.config['accounts']:
- 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
- })
+ provider_results = self.run_provider()
# process --host results
- if not self.args.host:
+ # For any 0 result, return it
+ if self.args.get('host', None):
+ count = 0
+ for results in provider_results:
+ if results['code'] == 0 and results['err'] == '' and results['out'] != '{}':
+ self.result = json.loads(results['out'])
+ count += 1
+ if count > 1:
+ raise RuntimeError("Found > 1 results for --host %s. \
+ This is an invalid state." % self.args.get('host', None))
+ # process --list results
+ else:
# For any non-zero, raise an error on it
- for result in all_results:
+ for result in provider_results:
if result['code'] != 0:
raise RuntimeError(result['err'])
else:
self.all_ec2_results[result['name']] = json.loads(result['out'])
+
+ # Check if user wants extra vars in yaml by
+ # having hostvars and all_group defined
+ for acc_config in self.config['accounts']:
+ self.apply_account_config(acc_config)
+
+ # Build results by merging all dictionaries
values = self.all_ec2_results.values()
values.insert(0, self.result)
for result in values:
MultiEc2.merge_destructively(self.result, result)
- 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 apply_account_config(self, acc_config):
+ ''' Apply account config settings
+ '''
+ if not acc_config.has_key('hostvars') and not acc_config.has_key('all_group'):
+ return
+
+ results = self.all_ec2_results[acc_config['name']]
+ # Update each hostvar with the newly desired key: value
+ for host_property, value in acc_config['hostvars'].items():
+ # Verify the account results look sane
+ # by checking for these keys ('_meta' and 'hostvars' exist)
+ if results.has_key('_meta') and results['_meta'].has_key('hostvars'):
+ for data in results['_meta']['hostvars'].values():
+ data[str(host_property)] = str(value)
+
+ # Add this group
+ results["%s_%s" % (host_property, value)] = \
+ copy.copy(results[acc_config['all_group']])
+
+ # store the results back into all_ec2_results
+ self.all_ec2_results[acc_config['name']] = results
+
@staticmethod
def merge_destructively(input_a, input_b):
"merges b into input_a"
@@ -191,7 +267,7 @@ class MultiEc2(object):
elif isinstance(input_a[key], list) and isinstance(input_b[key], list):
for result in input_b[key]:
if result not in input_a[key]:
- input_a[key].input_append(result)
+ input_a[key].append(result)
# a is a list and not b
elif isinstance(input_a[key], list):
if input_b[key] not in input_a[key]:
@@ -226,7 +302,7 @@ class MultiEc2(object):
help='List instances (default: True)')
parser.add_argument('--host', action='store', default=False,
help='Get all the variables about a specific instance')
- self.args = parser.parse_args()
+ self.args = parser.parse_args().__dict__
def write_to_cache(self):
''' Writes data in JSON format to a file '''
@@ -276,4 +352,7 @@ class MultiEc2(object):
if __name__ == "__main__":
- print MultiEc2().result_str()
+ MEC2 = MultiEc2()
+ MEC2.parse_cli_args()
+ MEC2.run()
+ print MEC2.result_str()
diff --git a/inventory/multi_ec2.yaml.example b/inventory/multi_ec2.yaml.example
index d8361a49f..99f157b11 100644
--- a/inventory/multi_ec2.yaml.example
+++ b/inventory/multi_ec2.yaml.example
@@ -5,14 +5,28 @@ cache_location: ~/.ansible/tmp/multi_ec2_inventory.cache
accounts:
- name: aws1
provider: aws/hosts/ec2.py
+ provider_config:
+ ec2:
+ regions: all
+ regions_exclude: us-gov-west-1,cn-north-1
+ destination_variable: public_dns_name
+ route53: False
+ cache_path: ~/.ansible/tmp
+ cache_max_age: 300
+ vpc_destination_variable: ip_address
env_vars:
AWS_ACCESS_KEY_ID: XXXXXXXXXXXXXXXXXXXX
AWS_SECRET_ACCESS_KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ all_group: ec2
+ hostvars:
+ cloud: aws
+ account: aws1
- - name: aws2
+- name: aws2
provider: aws/hosts/ec2.py
env_vars:
AWS_ACCESS_KEY_ID: XXXXXXXXXXXXXXXXXXXX
AWS_SECRET_ACCESS_KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ EC2_INI_PATH: /etc/ansible/ec2.ini
cache_max_age: 60
diff --git a/inventory/openshift-ansible-inventory.spec b/inventory/openshift-ansible-inventory.spec
index 8267e16f6..cd2332549 100644
--- a/inventory/openshift-ansible-inventory.spec
+++ b/inventory/openshift-ansible-inventory.spec
@@ -1,6 +1,6 @@
Summary: OpenShift Ansible Inventories
Name: openshift-ansible-inventory
-Version: 0.0.2
+Version: 0.0.7
Release: 1%{?dist}
License: ASL 2.0
URL: https://github.com/openshift/openshift-ansible
@@ -25,18 +25,39 @@ mkdir -p %{buildroot}/usr/share/ansible/inventory/gce
cp -p multi_ec2.py %{buildroot}/usr/share/ansible/inventory
cp -p multi_ec2.yaml.example %{buildroot}/etc/ansible/multi_ec2.yaml
-cp -p aws/ec2.py aws/ec2.ini %{buildroot}/usr/share/ansible/inventory/aws
-cp -p gce/gce.py %{buildroot}/usr/share/ansible/inventory/gce
+cp -p aws/hosts/ec2.py %{buildroot}/usr/share/ansible/inventory/aws
+cp -p gce/hosts/gce.py %{buildroot}/usr/share/ansible/inventory/gce
%files
%config(noreplace) /etc/ansible/*
%dir /usr/share/ansible/inventory
/usr/share/ansible/inventory/multi_ec2.py*
/usr/share/ansible/inventory/aws/ec2.py*
-%config(noreplace) /usr/share/ansible/inventory/aws/ec2.ini
/usr/share/ansible/inventory/gce/gce.py*
%changelog
+* Fri May 15 2015 Kenny Woodson <kwoodson@redhat.com> 0.0.7-1
+- Making multi_ec2 into a library (kwoodson@redhat.com)
+
+* Wed May 13 2015 Thomas Wiest <twiest@redhat.com> 0.0.6-1
+- Added support for grouping and a bug fix. (kwoodson@redhat.com)
+
+* Tue May 12 2015 Thomas Wiest <twiest@redhat.com> 0.0.5-1
+- removed ec2.ini from the openshift-ansible-inventory.spec file so that we're
+ not dictating what the ec2.ini file should look like. (twiest@redhat.com)
+- Added capability to pass in ec2.ini file. (kwoodson@redhat.com)
+
+* Thu May 07 2015 Thomas Wiest <twiest@redhat.com> 0.0.4-1
+- Fixed a bug due to renaming of variables. (kwoodson@redhat.com)
+
+* Thu May 07 2015 Thomas Wiest <twiest@redhat.com> 0.0.3-1
+- fixed build problems with openshift-ansible-inventory.spec
+ (twiest@redhat.com)
+- Allow option in multi_ec2 to set cache location. (kwoodson@redhat.com)
+- Add ansible_connection=local to localhost in inventory (jdetiber@redhat.com)
+- Adding refresh-cache option and cleanup for pylint. Also updated for
+ aws/hosts/ being added. (kwoodson@redhat.com)
+
* Thu Mar 26 2015 Thomas Wiest <twiest@redhat.com> 0.0.2-1
- added the ability to have a config file in /etc/openshift_ansible to
multi_ec2.py. (twiest@redhat.com)