diff options
Diffstat (limited to 'inventory')
-rw-r--r-- | inventory/aws/hosts/ec2.ini (renamed from inventory/aws/ec2.ini) | 0 | ||||
-rwxr-xr-x | inventory/aws/hosts/ec2.py (renamed from inventory/aws/ec2.py) | 0 | ||||
-rw-r--r-- | inventory/aws/hosts/hosts | 1 | ||||
-rw-r--r-- | inventory/byo/hosts | 34 | ||||
-rwxr-xr-x | inventory/gce/hosts/gce.py (renamed from inventory/gce/gce.py) | 0 | ||||
-rw-r--r-- | inventory/gce/hosts/hosts | 1 | ||||
-rw-r--r-- | inventory/libvirt/hosts/hosts | 1 | ||||
-rw-r--r-- | inventory/libvirt/hosts/libvirt.ini | 20 | ||||
-rwxr-xr-x | inventory/libvirt/hosts/libvirt_generic.py | 179 | ||||
-rwxr-xr-x | inventory/multi_ec2.py | 110 | ||||
-rw-r--r-- | inventory/multi_ec2.yaml.example | 4 | ||||
-rw-r--r-- | inventory/openshift-ansible-inventory.spec | 50 |
12 files changed, 353 insertions, 47 deletions
diff --git a/inventory/aws/ec2.ini b/inventory/aws/hosts/ec2.ini index eaab0a410..eaab0a410 100644 --- a/inventory/aws/ec2.ini +++ b/inventory/aws/hosts/ec2.ini diff --git a/inventory/aws/ec2.py b/inventory/aws/hosts/ec2.py index f231ff4c2..f231ff4c2 100755 --- a/inventory/aws/ec2.py +++ b/inventory/aws/hosts/ec2.py diff --git a/inventory/aws/hosts/hosts b/inventory/aws/hosts/hosts new file mode 100644 index 000000000..34a4396bd --- /dev/null +++ b/inventory/aws/hosts/hosts @@ -0,0 +1 @@ +localhost ansible_connection=local ansible_sudo=no ansible_python_interpreter=/usr/bin/python2 diff --git a/inventory/byo/hosts b/inventory/byo/hosts new file mode 100644 index 000000000..98dbb4fd8 --- /dev/null +++ b/inventory/byo/hosts @@ -0,0 +1,34 @@ +# This is an example of a bring your own (byo) host inventory + +# Create an OSEv3 group that contains the masters and nodes groups +[OSEv3:children] +masters +nodes + +# Set variables common for all OSEv3 hosts +[OSEv3:vars] +# SSH user, this user should allow ssh based auth without requiring a password +ansible_ssh_user=root + +# If ansible_ssh_user is not root, ansible_sudo must be set to true +#ansible_sudo=true + +# To deploy origin, change deployment_type to origin +deployment_type=enterprise + +# Pre-release registry URL +openshift_registry_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}] + +# Origin copr repo +#openshift_additional_repos=[{'id': 'openshift-origin-copr', 'name': 'OpenShift Origin COPR', 'baseurl': 'https://copr-be.cloud.fedoraproject.org/results/maxamillion/origin-next/epel-7-$basearch/', 'enabled': 1, 'gpgcheck': 1, gpgkey: 'https://copr-be.cloud.fedoraproject.org/results/maxamillion/origin-next/pubkey.gpg'}] + +# host group for masters +[masters] +ose3-master-ansible.test.example.com + +# host group for nodes +[nodes] +ose3-node[1:2]-ansible.test.example.com diff --git a/inventory/gce/gce.py b/inventory/gce/hosts/gce.py index 3403f735e..3403f735e 100755 --- a/inventory/gce/gce.py +++ b/inventory/gce/hosts/gce.py diff --git a/inventory/gce/hosts/hosts b/inventory/gce/hosts/hosts new file mode 100644 index 000000000..34a4396bd --- /dev/null +++ b/inventory/gce/hosts/hosts @@ -0,0 +1 @@ +localhost ansible_connection=local ansible_sudo=no ansible_python_interpreter=/usr/bin/python2 diff --git a/inventory/libvirt/hosts/hosts b/inventory/libvirt/hosts/hosts new file mode 100644 index 000000000..34a4396bd --- /dev/null +++ b/inventory/libvirt/hosts/hosts @@ -0,0 +1 @@ +localhost ansible_connection=local ansible_sudo=no ansible_python_interpreter=/usr/bin/python2 diff --git a/inventory/libvirt/hosts/libvirt.ini b/inventory/libvirt/hosts/libvirt.ini new file mode 100644 index 000000000..62ff204dd --- /dev/null +++ b/inventory/libvirt/hosts/libvirt.ini @@ -0,0 +1,20 @@ +# Ansible libvirt external inventory script settings +# + +[libvirt] + +uri = qemu:///system + +# API calls to libvirt can be slow. For this reason, we cache the results of an API +# call. Set this to the path you want cache files to be written to. Two files +# will be written to this directory: +# - ansible-libvirt.cache +# - ansible-libvirt.index +cache_path = /tmp + +# The number of seconds a cache file is considered valid. After this many +# seconds, a new API call will be made, and the cache file will be updated. +cache_max_age = 900 + + + diff --git a/inventory/libvirt/hosts/libvirt_generic.py b/inventory/libvirt/hosts/libvirt_generic.py new file mode 100755 index 000000000..4652f112e --- /dev/null +++ b/inventory/libvirt/hosts/libvirt_generic.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python2 + +""" +libvirt external inventory script +================================= + +Ansible has a feature where instead of reading from /etc/ansible/hosts +as a text file, it can query external programs to obtain the list +of hosts, groups the hosts are in, and even variables to assign to each host. + +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> +# +# This file is part of Ansible, +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +###################################################################### + +import argparse +import ConfigParser +import os +import re +import sys +from time import time +import libvirt +import xml.etree.ElementTree as ET + +try: + import json +except ImportError: + import simplejson as json + + +class LibvirtInventory(object): + + def __init__(self): + self.inventory = dict() # A list of groups and the hosts in that group + self.cache = dict() # Details about hosts in the inventory + + # Read settings and parse CLI arguments + self.read_settings() + self.parse_cli_args() + + if self.args.host: + print self.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) + else: # default action with no options + print self.json_format_dict(self.get_inventory(), self.args.pretty) + + def read_settings(self): + config = ConfigParser.SafeConfigParser() + config.read( + os.path.dirname(os.path.realpath(__file__)) + '/libvirt.ini' + ) + self.libvirt_uri = config.get('libvirt', 'uri') + + def parse_cli_args(self): + parser = argparse.ArgumentParser( + description='Produce an Ansible Inventory file based on libvirt' + ) + 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' + ) + parser.add_argument( + '--pretty', + action='store_true', + default=False, + help='Pretty format (default: False)' + ) + self.args = parser.parse_args() + + def get_host_info(self): + inventory = self.get_inventory() + if self.args.host in inventory['_meta']['hostvars']: + return inventory['_meta']['hostvars'][self.args.host] + + def get_inventory(self): + 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 + sys.exit(1) + + domains = conn.listAllDomains() + if domains is None: + print "Failed to list domains for connection %s" % 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(), + libvirt_uuid=domain.UUIDString()) + domain_name = domain.name() + + # TODO: add support for guests that are not in a running state + state, _ = domain.state() + # 2 is the state for a running guest + if state != 1: + continue + + 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): + tag = tag_elem.text + self.push(inventory, "tag_%s" % tag, domain_name) + self.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: + 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'] + hostvars['ansible_ssh_host'] = ip_address + hostvars['libvirt_ip_address'] = ip_address + + inventory['_meta']['hostvars'][domain_name] = hostvars + + 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) + +LibvirtInventory() diff --git a/inventory/multi_ec2.py b/inventory/multi_ec2.py index 5dee7972b..b839a33ea 100755 --- a/inventory/multi_ec2.py +++ b/inventory/multi_ec2.py @@ -1,37 +1,58 @@ #!/usr/bin/env python2 +''' + Fetch and combine multiple ec2 account settings into a single + json hash. +''' # vim: expandtab:tabstop=4:shiftwidth=4 from time import time import argparse import yaml import os -import sys -import pdb import subprocess import json -import pprint +CONFIG_FILE_NAME = 'multi_ec2.yaml' + class MultiEc2(object): + ''' + MultiEc2 class: + Opens a yaml config file and reads aws credentials. + Stores a json hash of resources in result. + ''' def __init__(self): + self.args = None self.config = None self.all_ec2_results = {} self.result = {} self.cache_path = os.path.expanduser('~/.ansible/tmp/multi_ec2_inventory.cache') self.file_path = os.path.join(os.path.dirname(os.path.realpath(__file__))) - self.config_file = os.path.join(self.file_path,"multi_ec2.yaml") + + same_dir_config_file = os.path.join(self.file_path, CONFIG_FILE_NAME) + etc_dir_config_file = os.path.join(os.path.sep, 'etc', 'ansible', CONFIG_FILE_NAME) + + # Prefer a file in the same directory, fall back to a file in etc + if os.path.isfile(same_dir_config_file): + self.config_file = same_dir_config_file + elif os.path.isfile(etc_dir_config_file): + self.config_file = etc_dir_config_file + else: + self.config_file = None # expect env vars + self.parse_cli_args() # load yaml - if os.path.isfile(self.config_file): + if self.config_file and os.path.isfile(self.config_file): self.config = self.load_yaml_config() - elif os.environ.has_key("AWS_ACCESS_KEY_ID") and os.environ.has_key("AWS_SECRET_ACCESS_KEY"): + elif os.environ.has_key("AWS_ACCESS_KEY_ID") and \ + os.environ.has_key("AWS_SECRET_ACCESS_KEY"): self.config = {} self.config['accounts'] = [ { 'name': 'default', - 'provider': 'aws/ec2.py', + 'provider': 'aws/hosts/ec2.py', 'env_vars': { 'AWS_ACCESS_KEY_ID': os.environ["AWS_ACCESS_KEY_ID"], 'AWS_SECRET_ACCESS_KEY': os.environ["AWS_SECRET_ACCESS_KEY"], @@ -43,13 +64,9 @@ class MultiEc2(object): else: raise RuntimeError("Could not find valid ec2 credentials in the environment.") - if self.args.cache_only: - # get data from disk - result = self.get_inventory_from_cache() - - if not result: - self.get_inventory() - self.write_to_cache() + if self.args.refresh_cache: + self.get_inventory() + self.write_to_cache() # if its a host query, fetch and do not cache elif self.args.host: self.get_inventory() @@ -61,7 +78,7 @@ class MultiEc2(object): # get data from disk self.get_inventory_from_cache() - def load_yaml_config(self,conf_file=None): + def load_yaml_config(self, conf_file=None): """Load a yaml config file with credentials to query the respective cloud for inventory. """ @@ -75,7 +92,7 @@ class MultiEc2(object): return config - def get_provider_tags(self,provider, env={}): + def get_provider_tags(self, provider, env=None): """Call <provider> and query all of the tags that are usuable by ansible. If environment is empty use the default env. """ @@ -140,7 +157,8 @@ class MultiEc2(object): self.all_ec2_results[result['name']] = json.loads(result['out']) values = self.all_ec2_results.values() values.insert(0, self.result) - [MultiEc2.merge_destructively(self.result, x) for x in values] + for result in values: + MultiEc2.merge_destructively(self.result, result) else: # For any 0 result, return it count = 0 @@ -152,30 +170,30 @@ class MultiEc2(object): raise RuntimeError("Found > 1 results for --host %s. \ This is an invalid state." % self.args.host) @staticmethod - def merge_destructively(a, b): - "merges b into a" - for key in b: - if key in a: - if isinstance(a[key], dict) and isinstance(b[key], dict): - MultiEc2.merge_destructively(a[key], b[key]) - elif a[key] == b[key]: + def merge_destructively(input_a, input_b): + "merges b into input_a" + for key in input_b: + if key in input_a: + if isinstance(input_a[key], dict) and isinstance(input_b[key], dict): + MultiEc2.merge_destructively(input_a[key], input_b[key]) + elif input_a[key] == input_b[key]: pass # same leaf value # both lists so add each element in b to a if it does ! exist - elif isinstance(a[key], list) and isinstance(b[key],list): - for x in b[key]: - if x not in a[key]: - a[key].append(x) + 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) # a is a list and not b - elif isinstance(a[key], list): - if b[key] not in a[key]: - a[key].append(b[key]) - elif isinstance(b[key], list): - a[key] = [a[key]] + [k for k in b[key] if k != a[key]] + elif isinstance(input_a[key], list): + if input_b[key] not in input_a[key]: + input_a[key].append(input_b[key]) + elif isinstance(input_b[key], list): + input_a[key] = [input_a[key]] + [k for k in input_b[key] if k != input_a[key]] else: - a[key] = [a[key],b[key]] + input_a[key] = [input_a[key], input_b[key]] else: - a[key] = b[key] - return a + input_a[key] = input_b[key] + return input_a def is_cache_valid(self): ''' Determines if the cache files have expired, or if it is still valid ''' @@ -191,19 +209,20 @@ class MultiEc2(object): 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('--cache-only', action='store_true', default=False, - help='Fetch cached only instances (default: False)') + parser = argparse.ArgumentParser( + description='Produce an Ansible Inventory file based on a provider') + parser.add_argument('--refresh-cache', action='store_true', default=False, + help='Fetch cached only instances (default: False)') parser.add_argument('--list', action='store_true', default=True, - help='List instances (default: True)') + help='List instances (default: True)') parser.add_argument('--host', action='store', default=False, - help='Get all the variables about a specific instance') + 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) + json_data = MultiEc2.json_format_dict(self.result, True) with open(self.cache_path, 'w') as cache: cache.write(json_data) @@ -219,7 +238,8 @@ class MultiEc2(object): return True - def json_format_dict(self, data, pretty=False): + @classmethod + def json_format_dict(cls, data, pretty=False): ''' Converts a dict to a JSON object and dumps it as a formatted string ''' @@ -229,9 +249,9 @@ class MultiEc2(object): return json.dumps(data) def result_str(self): + '''Return cache string stored in self.result''' return self.json_format_dict(self.result, True) if __name__ == "__main__": - mi = MultiEc2() - print mi.result_str() + print MultiEc2().result_str() diff --git a/inventory/multi_ec2.yaml.example b/inventory/multi_ec2.yaml.example index 0bd505816..91e7c7970 100644 --- a/inventory/multi_ec2.yaml.example +++ b/inventory/multi_ec2.yaml.example @@ -1,13 +1,13 @@ # multi ec2 inventory configs accounts: - name: aws1 - provider: aws/ec2.py + provider: aws/hosts/ec2.py env_vars: AWS_ACCESS_KEY_ID: XXXXXXXXXXXXXXXXXXXX AWS_SECRET_ACCESS_KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - name: aws2 - provider: aws/ec2.py + provider: aws/hosts/ec2.py env_vars: AWS_ACCESS_KEY_ID: XXXXXXXXXXXXXXXXXXXX AWS_SECRET_ACCESS_KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX diff --git a/inventory/openshift-ansible-inventory.spec b/inventory/openshift-ansible-inventory.spec new file mode 100644 index 000000000..8267e16f6 --- /dev/null +++ b/inventory/openshift-ansible-inventory.spec @@ -0,0 +1,50 @@ +Summary: OpenShift Ansible Inventories +Name: openshift-ansible-inventory +Version: 0.0.2 +Release: 1%{?dist} +License: ASL 2.0 +URL: https://github.com/openshift/openshift-ansible +Source0: %{name}-%{version}.tar.gz +Requires: python2 +BuildRequires: python2-devel +BuildArch: noarch + +%description +Ansible Inventories used with the openshift-ansible scripts and playbooks. + +%prep +%setup -q + +%build + +%install +mkdir -p %{buildroot}/etc/ansible +mkdir -p %{buildroot}/usr/share/ansible/inventory +mkdir -p %{buildroot}/usr/share/ansible/inventory/aws +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 + +%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 +* 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) +- Merge pull request #97 from jwhonce/wip/cluster (jhonce@redhat.com) +- gce inventory/playbook updates for node registration changes + (jdetiber@redhat.com) +- Various fixes (jdetiber@redhat.com) + +* Tue Mar 24 2015 Thomas Wiest <twiest@redhat.com> 0.0.1-1 +- new package built with tito + |