mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Brook inventory (#15264)
* Initial work on Brook.io dynamic inventory * Handle error cases in Brook.io dynamic inventory * Remove defaults from brook.ini * Update Brook.io dynamic inventory for libbrookv0.3 Use authentication api to obtain a valid JWT from an API Token. * Remove defaults from brook.ini
This commit is contained in:
parent
2f411c9aa9
commit
574163c949
2 changed files with 302 additions and 0 deletions
40
contrib/inventory/brook.ini
Normal file
40
contrib/inventory/brook.ini
Normal file
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright 2016 Doalitic.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
# The Brook.io inventory script has the following dependencies:
|
||||
# 1. A working Brook.io account
|
||||
# See https://brook.io
|
||||
# 2. A valid token generated through the 'API token' panel of Brook.io
|
||||
# 3. The libbrook python libray.
|
||||
# See https://github.com/doalitic/libbrook
|
||||
#
|
||||
# Author: Francisco Ros <fjros@doalitic.com>
|
||||
|
||||
[brook]
|
||||
# Valid API token (required).
|
||||
# E.g. 'Aed342a12A60433697281FeEe1a4037C'
|
||||
#
|
||||
api_token =
|
||||
|
||||
# Project id within Brook.io, as obtained from the project settings (optional). If provided, the
|
||||
# generated inventory will just include the hosts that belong to such project. Otherwise, it will
|
||||
# include all hosts in projects the requesting user has access to. The response includes groups
|
||||
# 'project_x', being 'x' the project name.
|
||||
# E.g. '2e8e099e1bc34cc0979d97ac34e9577b'
|
||||
#
|
||||
project_id =
|
262
contrib/inventory/brook.py
Executable file
262
contrib/inventory/brook.py
Executable file
|
@ -0,0 +1,262 @@
|
|||
#!/usr/bin/env python
|
||||
# Copyright 2016 Doalitic.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
"""
|
||||
Brook.io external inventory script
|
||||
==================================
|
||||
|
||||
Generates inventory that Ansible can understand by making API requests to Brook.io via the libbrook
|
||||
library. Hence, such dependency must be installed in the system to run this script.
|
||||
|
||||
The default configuration file is named 'brook.ini' and is located alongside this script. You can
|
||||
choose any other file by setting the BROOK_INI_PATH environment variable.
|
||||
|
||||
If param 'project_id' is left blank in 'brook.ini', the inventory includes all the instances in
|
||||
projects where the requesting user belongs. Otherwise, only instances from the given project are
|
||||
included, provided the requesting user belongs to it.
|
||||
|
||||
The following variables are established for every host. They can be retrieved from the hostvars
|
||||
dictionary.
|
||||
- brook_name: str
|
||||
- brook_description: str
|
||||
- brook_project: str
|
||||
- brook_template: str
|
||||
- brook_region: str
|
||||
- brook_status: str
|
||||
- brook_tags: list(str)
|
||||
- brook_internal_ips: list(str)
|
||||
- brook_external_ips: list(str)
|
||||
- brook_created_at
|
||||
- brook_updated_at
|
||||
- ansible_ssh_host
|
||||
|
||||
Instances are grouped by the following categories:
|
||||
- tag:
|
||||
A group is created for each tag. E.g. groups 'tag_foo' and 'tag_bar' are created if there exist
|
||||
instances with tags 'foo' and/or 'bar'.
|
||||
- project:
|
||||
A group is created for each project. E.g. group 'project_test' is created if a project named
|
||||
'test' exist.
|
||||
- status:
|
||||
A group is created for each instance state. E.g. groups 'status_RUNNING' and 'status_PENDING'
|
||||
are created if there are instances in running and pending state.
|
||||
|
||||
Examples:
|
||||
Execute uname on all instances in project 'test'
|
||||
$ ansible -i brook.py project_test -m shell -a "/bin/uname -a"
|
||||
|
||||
Install nginx on all debian web servers tagged with 'www'
|
||||
$ ansible -i brook.py tag_www -m apt -a "name=nginx state=present"
|
||||
|
||||
Run site.yml playbook on web servers
|
||||
$ ansible-playbook -i brook.py site.yml -l tag_www
|
||||
|
||||
Support:
|
||||
This script is tested on Python 2.7 and 3.4. It may work on other versions though.
|
||||
|
||||
Author: Francisco Ros <fjros@doalitic.com>
|
||||
Version: 0.1
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
try:
|
||||
from ConfigParser import SafeConfigParser as ConfigParser
|
||||
except ImportError:
|
||||
from configparser import ConfigParser
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
try:
|
||||
import libbrook
|
||||
except:
|
||||
print('Brook.io inventory script requires libbrook. See https://github.com/doalitic/libbrook')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class BrookInventory:
|
||||
|
||||
_API_ENDPOINT = 'https://api.brook.io'
|
||||
|
||||
def __init__(self):
|
||||
self._configure_from_file()
|
||||
self.client = self.get_api_client()
|
||||
self.inventory = self.get_inventory()
|
||||
|
||||
def _configure_from_file(self):
|
||||
"""Initialize from .ini file.
|
||||
|
||||
Configuration file is assumed to be named 'brook.ini' and to be located on the same
|
||||
directory than this file, unless the environment variable BROOK_INI_PATH says otherwise.
|
||||
"""
|
||||
|
||||
brook_ini_default_path = \
|
||||
os.path.join(os.path.dirname(os.path.realpath(__file__)), 'brook.ini')
|
||||
brook_ini_path = os.environ.get('BROOK_INI_PATH', brook_ini_default_path)
|
||||
|
||||
config = ConfigParser(defaults={
|
||||
'api_token': '',
|
||||
'project_id': ''
|
||||
})
|
||||
config.read(brook_ini_path)
|
||||
self.api_token = config.get('brook', 'api_token')
|
||||
self.project_id = config.get('brook', 'project_id')
|
||||
|
||||
if not self.api_token:
|
||||
print('You must provide (at least) your Brook.io API token to generate the dynamic '
|
||||
'inventory.')
|
||||
sys.exit(1)
|
||||
|
||||
def get_api_client(self):
|
||||
"""Authenticate user via the provided credentials and return the corresponding API client.
|
||||
"""
|
||||
|
||||
# Get JWT token from API token
|
||||
#
|
||||
unauthenticated_client = libbrook.ApiClient(host=self._API_ENDPOINT)
|
||||
auth_api = libbrook.AuthApi(unauthenticated_client)
|
||||
api_token = libbrook.AuthTokenRequest()
|
||||
api_token.token = self.api_token
|
||||
jwt = auth_api.auth_token(token=api_token)
|
||||
|
||||
# Create authenticated API client
|
||||
#
|
||||
return libbrook.ApiClient(host=self._API_ENDPOINT,
|
||||
header_name='Authorization',
|
||||
header_value='Bearer %s' % jwt.token)
|
||||
|
||||
def get_inventory(self):
|
||||
"""Generate Ansible inventory.
|
||||
"""
|
||||
|
||||
groups = dict()
|
||||
meta = dict()
|
||||
meta['hostvars'] = dict()
|
||||
|
||||
instances_api = libbrook.InstancesApi(self.client)
|
||||
projects_api = libbrook.ProjectsApi(self.client)
|
||||
templates_api = libbrook.TemplatesApi(self.client)
|
||||
|
||||
# If no project is given, get all projects the requesting user has access to
|
||||
#
|
||||
if not self.project_id:
|
||||
projects = [project.id for project in projects_api.index_projects()]
|
||||
else:
|
||||
projects = [self.project_id]
|
||||
|
||||
# Build inventory from instances in all projects
|
||||
#
|
||||
for project_id in projects:
|
||||
project = projects_api.show_project(project_id=project_id)
|
||||
for instance in instances_api.index_instances(project_id=project_id):
|
||||
# Get template used for this instance
|
||||
template = templates_api.show_template(template_id=instance.template)
|
||||
|
||||
# Update hostvars
|
||||
try:
|
||||
meta['hostvars'][instance.name] = \
|
||||
self.hostvars(project, instance, template, instances_api)
|
||||
except libbrook.rest.ApiException:
|
||||
continue
|
||||
|
||||
# Group by project
|
||||
project_group = 'project_%s' % project.name
|
||||
if project_group in groups.keys():
|
||||
groups[project_group].append(instance.name)
|
||||
else:
|
||||
groups[project_group] = [instance.name]
|
||||
|
||||
# Group by status
|
||||
status_group = 'status_%s' % meta['hostvars'][instance.name]['brook_status']
|
||||
if status_group in groups.keys():
|
||||
groups[status_group].append(instance.name)
|
||||
else:
|
||||
groups[status_group] = [instance.name]
|
||||
|
||||
# Group by tags
|
||||
tags = meta['hostvars'][instance.name]['brook_tags']
|
||||
for tag in tags:
|
||||
tag_group = 'tag_%s' % tag
|
||||
if tag_group in groups.keys():
|
||||
groups[tag_group].append(instance.name)
|
||||
else:
|
||||
groups[tag_group] = [instance.name]
|
||||
|
||||
groups['_meta'] = meta
|
||||
return groups
|
||||
|
||||
def hostvars(self, project, instance, template, api):
|
||||
"""Return the hostvars dictionary for the given instance.
|
||||
|
||||
Raise libbrook.rest.ApiException if it cannot retrieve all required information from the
|
||||
Brook.io API.
|
||||
"""
|
||||
|
||||
hostvars = instance.to_dict()
|
||||
hostvars['brook_name'] = hostvars.pop('name')
|
||||
hostvars['brook_description'] = hostvars.pop('description')
|
||||
hostvars['brook_project'] = hostvars.pop('project')
|
||||
hostvars['brook_template'] = hostvars.pop('template')
|
||||
hostvars['brook_region'] = hostvars.pop('region')
|
||||
hostvars['brook_created_at'] = hostvars.pop('created_at')
|
||||
hostvars['brook_updated_at'] = hostvars.pop('updated_at')
|
||||
del hostvars['id']
|
||||
del hostvars['key']
|
||||
del hostvars['provider']
|
||||
del hostvars['image']
|
||||
|
||||
# Substitute identifiers for names
|
||||
#
|
||||
hostvars['brook_project'] = project.name
|
||||
hostvars['brook_template'] = template.name
|
||||
|
||||
# Retrieve instance state
|
||||
#
|
||||
status = api.status_instance(project_id=project.id, instance_id=instance.id)
|
||||
hostvars.update({'brook_status': status.state})
|
||||
|
||||
# Retrieve instance tags
|
||||
#
|
||||
tags = api.instance_tags(project_id=project.id, instance_id=instance.id)
|
||||
hostvars.update({'brook_tags': tags})
|
||||
|
||||
# Retrieve instance addresses
|
||||
#
|
||||
addresses = api.instance_addresses(project_id=project.id, instance_id=instance.id)
|
||||
internal_ips = [address.address for address in addresses if address.scope == 'internal']
|
||||
external_ips = [address.address for address in addresses
|
||||
if address.address and address.scope == 'external']
|
||||
hostvars.update({'brook_internal_ips': internal_ips})
|
||||
hostvars.update({'brook_external_ips': external_ips})
|
||||
try:
|
||||
hostvars.update({'ansible_ssh_host': external_ips[0]})
|
||||
except IndexError:
|
||||
raise libbrook.rest.ApiException(status='502', reason='Instance without public IP')
|
||||
|
||||
return hostvars
|
||||
|
||||
|
||||
# Run the script
|
||||
#
|
||||
brook = BrookInventory()
|
||||
print(json.dumps(brook.inventory))
|
Loading…
Reference in a new issue