summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/hrobot/plugins/inventory
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
commit975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch)
tree89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/community/hrobot/plugins/inventory
parentInitial commit. (diff)
downloadansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz
ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/community/hrobot/plugins/inventory')
-rw-r--r--ansible_collections/community/hrobot/plugins/inventory/robot.py191
1 files changed, 191 insertions, 0 deletions
diff --git a/ansible_collections/community/hrobot/plugins/inventory/robot.py b/ansible_collections/community/hrobot/plugins/inventory/robot.py
new file mode 100644
index 000000000..1d5af4537
--- /dev/null
+++ b/ansible_collections/community/hrobot/plugins/inventory/robot.py
@@ -0,0 +1,191 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2019 Oleksandr Stepanov <alexandrst88@gmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r"""
+ name: robot
+ author:
+ - Oleksandr Stepanov (@alexandrst88)
+ short_description: Hetzner Robot inventory source
+ version_added: 1.1.0
+ description:
+ - Reads servers from Hetzner Robot API.
+ - Uses a YAML configuration file that ends with C(robot.yml) or C(robot.yaml).
+ - The inventory plugin adds all values from U(https://robot.your-server.de/doc/webservice/en.html#get-server)
+ prepended with C(hrobot_) to the server's inventory.
+ For example, the variable C(hrobot_dc) contains the data center the server is located in.
+ extends_documentation_fragment:
+ - ansible.builtin.constructed
+ - ansible.builtin.inventory_cache
+ - community.hrobot.robot
+ notes:
+ - The I(hetzner_user) and I(hetzner_password) options can be templated.
+ options:
+ plugin:
+ description: Token that ensures this is a source file for the plugin.
+ required: true
+ choices: ["community.hrobot.robot"]
+ hetzner_user:
+ env:
+ - name: HROBOT_API_USER
+ hetzner_password:
+ env:
+ - name: HROBOT_API_PASSWORD
+ filters:
+ description:
+ - A dictionary of filter value pairs.
+ - Available filters are listed here are keys of server like C(status) or C(server_ip).
+ - See U(https://robot.your-server.de/doc/webservice/en.html#get-server) for all values that can be used.
+ type: dict
+ default: {}
+"""
+
+EXAMPLES = r"""
+# Fetch all hosts in Hetzner Robot
+plugin: community.hrobot.robot
+# Filters all servers in ready state
+filters:
+ status: ready
+
+# Example showing encrypted credentials
+# (This assumes that Mozilla sops was used to encrypt keys/hetzner.sops.yaml, which contains two values
+# hetzner_username and hetzner_password. Needs the community.sops collection to decode that file.)
+plugin: community.hrobot.robot
+hetzner_user: '{{ (lookup("community.sops.sops", "keys/hetzner.sops.yaml") | from_yaml).hetzner_username }}'
+hetzner_password: '{{ (lookup("community.sops.sops", "keys/hetzner.sops.yaml") | from_yaml).hetzner_password }}'
+
+# Example using constructed features to create groups
+plugin: community.hrobot.robot
+filters:
+ status: ready
+ traffic: unlimited
+# keyed_groups may be used to create custom groups
+strict: false
+keyed_groups:
+ # Add e.g. groups for every data center
+ - key: hrobot_dc
+ separator: ""
+# Use the IP address to connect to the host
+compose:
+ server_name_ip: hrobot_server_name ~ '-' ~ hrobot_server_ip
+"""
+
+from ansible.errors import AnsibleError
+from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
+from ansible.template import Templar
+from ansible.utils.display import Display
+
+from ansible_collections.community.hrobot.plugins.module_utils.robot import (
+ BASE_URL,
+ PluginException,
+ plugin_open_url_json,
+)
+
+display = Display()
+
+
+class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
+
+ NAME = 'community.hrobot.robot'
+
+ def verify_file(self, path):
+ ''' return true/false if this is possibly a valid file for this plugin to consume '''
+ valid = False
+ if super(InventoryModule, self).verify_file(path):
+ # base class verifies that file exists and is readable by current user
+ if path.endswith(('robot.yaml', 'robot.yml')):
+ valid = True
+ else:
+ display.debug("robot inventory filename must end with 'robot.yml' or 'robot.yaml'")
+ return valid
+
+ def parse(self, inventory, loader, path, cache=True):
+ super(InventoryModule, self).parse(inventory, loader, path)
+ servers = {}
+ config = self._read_config_data(path)
+ self.load_cache_plugin()
+ cache_key = self.get_cache_key(path)
+
+ self.templar = Templar(loader=loader)
+
+ # cache may be True or False at this point to indicate if the inventory is being refreshed
+ # get the user's cache option too to see if we should save the cache if it is changing
+ user_cache_setting = self.get_option('cache')
+
+ # read if the user has caching enabled and the cache isn't being refreshed
+ attempt_to_read_cache = user_cache_setting and cache
+ # update if the user has caching enabled and the cache is being refreshed; update this value to True if the cache has expired below
+ cache_needs_update = user_cache_setting and not cache
+
+ # attempt to read the cache if inventory isn't being refreshed and the user has caching enabled
+ if attempt_to_read_cache:
+ try:
+ servers = self._cache[cache_key]
+ except KeyError:
+ # This occurs if the cache_key is not in the cache or if the cache_key expired, so the cache needs to be updated
+ cache_needs_update = True
+ elif not cache_needs_update:
+ servers = self.get_servers()
+
+ if cache_needs_update:
+ servers = self.get_servers()
+
+ # set the cache
+ self._cache[cache_key] = servers
+
+ self.populate(servers)
+
+ def populate(self, servers):
+ filters = self.get_option('filters')
+ strict = self.get_option('strict')
+ server_lists = []
+ for server in servers:
+ s = server['server']
+ server_name = s.get('server_name') or s.get('server_ip') or str(s['server_number'])
+ matched = self.filter(s, filters)
+ if not matched:
+ continue
+
+ if server_name in server_lists:
+ display.warning('Two of your Hetzner servers use the same server name ({0}). '
+ 'Please make sure that your server names are unique. '
+ 'Only the first server named {0} will be included in the inventory.'.format(server_name))
+ continue
+
+ self.inventory.add_host(server_name)
+ server_lists.append(server_name)
+ if 'server_ip' in s:
+ self.inventory.set_variable(server_name, 'ansible_host', s['server_ip'])
+ for hostvar, hostval in s.items():
+ self.inventory.set_variable(server_name, "{0}_{1}".format('hrobot', hostvar), hostval)
+
+ # Composed variables
+ server_vars = self.inventory.get_host(server_name).get_vars()
+ self._set_composite_vars(self.get_option('compose'), server_vars, server_name, strict=strict)
+
+ # Complex groups based on jinja2 conditionals, hosts that meet the conditional are added to group
+ self._add_host_to_composed_groups(self.get_option('groups'), server, server_name, strict=strict)
+
+ # Create groups based on variable values and add the corresponding hosts to it
+ self._add_host_to_keyed_groups(self.get_option('keyed_groups'), server, server_name, strict=strict)
+
+ def filter(self, server, filters):
+ matched = True
+ for key, value in filters.items():
+ if server.get(key) != value:
+ matched = False
+ break
+ return matched
+
+ def get_servers(self):
+ try:
+ return plugin_open_url_json(self, '{0}/server'.format(BASE_URL), templar=self.templar)[0]
+ except PluginException as e:
+ raise AnsibleError(e.error_message)