mirror of
https://github.com/thegeeklab/ansible-later.git
synced 2024-11-16 10:00:39 +00:00
188 lines
8.3 KiB
Python
188 lines
8.3 KiB
Python
# Copyright (c) 2018 Ansible Project
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
from __future__ import (absolute_import, division, print_function)
|
|
|
|
__metaclass__ = type
|
|
|
|
DOCUMENTATION = '''
|
|
name: tower
|
|
plugin_type: inventory
|
|
author:
|
|
- Matthew Jones (@matburt)
|
|
- Yunfan Zhang (@YunfanZhang42)
|
|
short_description: Ansible dynamic inventory plugin for Ansible Tower.
|
|
version_added: "2.7"
|
|
description:
|
|
- Reads inventories from Ansible Tower.
|
|
- Supports reading configuration from both YAML config file and environment variables.
|
|
- If reading from the YAML file, the file name must end with tower.(yml|yaml) or tower_inventory.(yml|yaml),
|
|
the path in the command would be /path/to/tower_inventory.(yml|yaml). If some arguments in the config file
|
|
are missing, this plugin will try to fill in missing arguments by reading from environment variables.
|
|
- If reading configurations from environment variables, the path in the command must be @tower_inventory.
|
|
options:
|
|
plugin:
|
|
description: the name of this plugin, it should always be set to 'tower'
|
|
for this plugin to recognize it as it's own.
|
|
env:
|
|
- name: ANSIBLE_INVENTORY_ENABLED
|
|
required: True
|
|
choices: ['tower']
|
|
host:
|
|
description: The network address of your Ansible Tower host.
|
|
type: string
|
|
env:
|
|
- name: TOWER_HOST
|
|
required: True
|
|
username:
|
|
description: The user that you plan to use to access inventories on Ansible Tower.
|
|
type: string
|
|
env:
|
|
- name: TOWER_USERNAME
|
|
required: True
|
|
password:
|
|
description: The password for your Ansible Tower user.
|
|
type: string
|
|
env:
|
|
- name: TOWER_PASSWORD
|
|
required: True
|
|
inventory_id:
|
|
description: The ID of the Ansible Tower inventory that you wish to import.
|
|
type: string
|
|
env:
|
|
- name: TOWER_INVENTORY
|
|
required: True
|
|
verify_ssl:
|
|
description: Specify whether Ansible should verify the SSL certificate of Ansible Tower host.
|
|
type: bool
|
|
default: True
|
|
env:
|
|
- name: TOWER_VERIFY_SSL
|
|
required: False
|
|
'''
|
|
|
|
EXAMPLES = '''
|
|
# Before you execute the following commands, you should make sure this file is in your plugin path,
|
|
# and you enabled this plugin.
|
|
|
|
# Example for using tower_inventory.yml file
|
|
|
|
plugin: tower
|
|
host: your_ansible_tower_server_network_address
|
|
username: your_ansible_tower_username
|
|
password: your_ansible_tower_password
|
|
inventory_id: the_ID_of_targeted_ansible_tower_inventory
|
|
# Then you can run the following command.
|
|
# If some of the arguments are missing, Ansible will attempt to read them from environment variables.
|
|
# ansible-inventory -i /path/to/tower_inventory.yml --list
|
|
|
|
# Example for reading from environment variables:
|
|
|
|
# Set environment variables:
|
|
# export TOWER_HOST=YOUR_TOWER_HOST_ADDRESS
|
|
# export TOWER_USERNAME=YOUR_TOWER_USERNAME
|
|
# export TOWER_PASSWORD=YOUR_TOWER_PASSWORD
|
|
# export TOWER_INVENTORY=THE_ID_OF_TARGETED_INVENTORY
|
|
# Read the inventory specified in TOWER_INVENTORY from Ansible Tower, and list them.
|
|
# The inventory path must always be @tower_inventory if you are reading all settings from environment variables.
|
|
# ansible-inventory -i @tower_inventory --list
|
|
'''
|
|
|
|
import re
|
|
import os
|
|
import json
|
|
from ansible.module_utils import six
|
|
from ansible.module_utils.urls import Request, urllib_error, ConnectionError, socket, httplib
|
|
from ansible.module_utils._text import to_native
|
|
from ansible.errors import AnsibleParserError
|
|
from ansible.plugins.inventory import BaseInventoryPlugin
|
|
|
|
# Python 2/3 Compatibility
|
|
try:
|
|
from urlparse import urljoin
|
|
except ImportError:
|
|
from urllib.parse import urljoin
|
|
|
|
|
|
class InventoryModule(BaseInventoryPlugin):
|
|
NAME = 'tower'
|
|
# Stays backward compatible with tower inventory script.
|
|
# If the user supplies '@tower_inventory' as path, the plugin will read from environment variables.
|
|
no_config_file_supplied = False
|
|
|
|
def read_tower_inventory(self, tower_host, tower_user, tower_pass, inventory, verify_ssl=True):
|
|
if not re.match('(?:http|https)://', tower_host):
|
|
tower_host = 'https://{tower_host}'.format(tower_host=tower_host)
|
|
inventory_id = inventory.replace('/', '')
|
|
inventory_url = '/api/v2/inventories/{inv_id}/script/?hostvars=1&towervars=1&all=1'.format(inv_id=inventory_id)
|
|
inventory_url = urljoin(tower_host, inventory_url)
|
|
|
|
request_handler = Request(url_username=tower_user,
|
|
url_password=tower_pass,
|
|
force_basic_auth=True,
|
|
validate_certs=verify_ssl)
|
|
|
|
try:
|
|
response = request_handler.get(inventory_url)
|
|
except (ConnectionError, urllib_error.URLError, socket.error, httplib.HTTPException) as e:
|
|
error_msg = 'Connection to remote host failed: {err}'.format(err=e)
|
|
# If Tower gives a readable error message, display that message to the user.
|
|
if callable(getattr(e, 'read', None)):
|
|
error_msg += ' with message: {err_msg}'.format(err_msg=e.read())
|
|
raise AnsibleParserError(to_native(error_msg))
|
|
|
|
# Attempt to parse JSON.
|
|
try:
|
|
return json.loads(response.read())
|
|
except (ValueError, TypeError) as e:
|
|
# If the JSON parse fails, print the ValueError
|
|
raise AnsibleParserError(to_native('Failed to parse json from host: {err}'.format(err=e)))
|
|
|
|
def verify_file(self, path):
|
|
if path.endswith('@tower_inventory'):
|
|
self.no_config_file_supplied = True
|
|
return True
|
|
elif super(InventoryModule, self).verify_file(path):
|
|
return path.endswith(('tower_inventory.yml', 'tower_inventory.yaml', 'tower.yml', 'tower.yaml'))
|
|
else:
|
|
return False
|
|
|
|
def parse(self, inventory, loader, path, cache=True):
|
|
super(InventoryModule, self).parse(inventory, loader, path)
|
|
if not self.no_config_file_supplied and os.path.isfile(path):
|
|
self._read_config_data(path)
|
|
# Read inventory from tower server.
|
|
# Note the environment variables will be handled automatically by InventoryManager.
|
|
inventory = self.read_tower_inventory(self.get_option('host'),
|
|
self.get_option('username'),
|
|
self.get_option('password'),
|
|
self.get_option('inventory_id'),
|
|
verify_ssl=self.get_option('verify_ssl'))
|
|
# To start with, create all the groups.
|
|
for group_name in inventory:
|
|
if group_name != '_meta':
|
|
self.inventory.add_group(group_name)
|
|
|
|
# Then, create all hosts and add the host vars.
|
|
all_hosts = inventory['_meta']['hostvars']
|
|
for host_name, host_vars in six.iteritems(all_hosts):
|
|
self.inventory.add_host(host_name)
|
|
for var_name, var_value in six.iteritems(host_vars):
|
|
self.inventory.set_variable(host_name, var_name, var_value)
|
|
|
|
# Lastly, create to group-host and group-group relationships, and set group vars.
|
|
for group_name, group_content in six.iteritems(inventory):
|
|
if group_name != 'all' and group_name != '_meta':
|
|
# First add hosts to groups
|
|
for host_name in group_content.get('hosts', []):
|
|
self.inventory.add_host(host_name, group_name)
|
|
# Then add the parent-children group relationships.
|
|
for child_group_name in group_content.get('children', []):
|
|
self.inventory.add_child(group_name, child_group_name)
|
|
# Set the group vars. Note we should set group var for 'all', but not '_meta'.
|
|
if group_name != '_meta':
|
|
for var_name, var_value in six.iteritems(group_content.get('vars', {})):
|
|
self.inventory.set_variable(group_name, var_name, var_value)
|
|
# Clean up the inventory.
|
|
self.inventory.reconcile_inventory()
|