|
|
|
@ -13,7 +13,7 @@ DOCUMENTATION = r"""
|
|
|
|
requirements:
|
|
|
|
requirements:
|
|
|
|
- python >= 2.10
|
|
|
|
- python >= 2.10
|
|
|
|
description:
|
|
|
|
description:
|
|
|
|
- Reads inventory data from the Hetzner Cloud API.
|
|
|
|
- Reads inventory data from the Hetzner Cloud API. Server Groups are given by the <service> label. Servers need two labels: service and stage.
|
|
|
|
extends_documentation_fragment:
|
|
|
|
extends_documentation_fragment:
|
|
|
|
- constructed
|
|
|
|
- constructed
|
|
|
|
- inventory_cache
|
|
|
|
- inventory_cache
|
|
|
|
@ -27,11 +27,11 @@ DOCUMENTATION = r"""
|
|
|
|
required: true
|
|
|
|
required: true
|
|
|
|
env:
|
|
|
|
env:
|
|
|
|
- name: HETZNER_CLOUD_TOKEN
|
|
|
|
- name: HETZNER_CLOUD_TOKEN
|
|
|
|
stage:
|
|
|
|
label_selector:
|
|
|
|
description: The Hetzner Cloud stage.
|
|
|
|
description: Filter servers by this label selector.
|
|
|
|
required: true
|
|
|
|
required: true
|
|
|
|
env:
|
|
|
|
env:
|
|
|
|
- name: HETZNER_CLOUD_STAGE
|
|
|
|
- name: HETZNER_LABEL_SELECTOR
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import json
|
|
|
|
import json
|
|
|
|
@ -41,25 +41,43 @@ from ansible.module_utils.urls import open_url
|
|
|
|
from ansible.module_utils._text import to_native
|
|
|
|
from ansible.module_utils._text import to_native
|
|
|
|
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
|
|
|
|
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
|
|
|
|
from ansible.release import __version__
|
|
|
|
from ansible.release import __version__
|
|
|
|
|
|
|
|
from ansible.utils.display import Display
|
|
|
|
|
|
|
|
|
|
|
|
class MyHcloudAPI:
|
|
|
|
class MyHcloudAPI:
|
|
|
|
|
|
|
|
|
|
|
|
BASE = "https://api.hetzner.cloud"
|
|
|
|
BASE = "https://api.hetzner.cloud"
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, token, stage):
|
|
|
|
def __init__(self, token, label_selector):
|
|
|
|
self.token = token
|
|
|
|
self.token = token
|
|
|
|
self.stage = stage
|
|
|
|
self.label_selector = label_selector
|
|
|
|
|
|
|
|
|
|
|
|
def get_servers(self):
|
|
|
|
def get_servers(self):
|
|
|
|
api_url = "%s/v1/servers?label_selector=stage=" % self.BASE + self.stage
|
|
|
|
display = Display()
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
response = open_url(
|
|
|
|
servers = []
|
|
|
|
api_url,
|
|
|
|
|
|
|
|
headers={"Authorization": "Bearer " + self.token},
|
|
|
|
# pagination with page_size per window, repeat until last page is reached
|
|
|
|
)
|
|
|
|
page = 1
|
|
|
|
result = json.loads(response.read())
|
|
|
|
page_size = 20
|
|
|
|
return result["servers"]
|
|
|
|
while page > 0:
|
|
|
|
|
|
|
|
api_url = "%s/v1/servers?label_selector=" % self.BASE + self.label_selector + "&per_page=" + str(page_size) + "&page=" + str(page)
|
|
|
|
|
|
|
|
#display.display(api_url)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
response = open_url(
|
|
|
|
|
|
|
|
api_url,
|
|
|
|
|
|
|
|
headers={"Authorization": "Bearer " + self.token},
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
result = json.loads(response.read())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
servers += result["servers"]
|
|
|
|
|
|
|
|
#for server in result["servers"]:
|
|
|
|
|
|
|
|
# display.display(server["name"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if result["meta"]["pagination"]["page"] == result["meta"]["pagination"]["last_page"]:
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
page += 1
|
|
|
|
|
|
|
|
return servers
|
|
|
|
except ValueError:
|
|
|
|
except ValueError:
|
|
|
|
raise AnsibleError("Incorrect JSON payload")
|
|
|
|
raise AnsibleError("Incorrect JSON payload")
|
|
|
|
except Exception as e:
|
|
|
|
except Exception as e:
|
|
|
|
@ -77,7 +95,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
|
|
|
|
|
|
|
|
|
|
|
def _read_servers_from_API(self):
|
|
|
|
def _read_servers_from_API(self):
|
|
|
|
servers = MyHcloudAPI(
|
|
|
|
servers = MyHcloudAPI(
|
|
|
|
self.get_option("api_token"), self.get_option("stage")
|
|
|
|
self.get_option("api_token"), self.get_option("label_selector")
|
|
|
|
).get_servers()
|
|
|
|
).get_servers()
|
|
|
|
return servers
|
|
|
|
return servers
|
|
|
|
|
|
|
|
|
|
|
|
@ -115,22 +133,29 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
|
|
|
if cache_needs_update:
|
|
|
|
if cache_needs_update:
|
|
|
|
self._cache[cache_key] = servers
|
|
|
|
self._cache[cache_key] = servers
|
|
|
|
|
|
|
|
|
|
|
|
self.populate(self.get_option("stage"), servers)
|
|
|
|
self.populate(servers)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def populate(self, servers):
|
|
|
|
|
|
|
|
display = Display()
|
|
|
|
|
|
|
|
|
|
|
|
def populate(self, stage, servers):
|
|
|
|
|
|
|
|
# Add a default top group 'hcloud'
|
|
|
|
# Add a default top group 'hcloud'
|
|
|
|
self.inventory.add_group(group="hcloud")
|
|
|
|
self.inventory.add_group(group="hcloud")
|
|
|
|
|
|
|
|
|
|
|
|
# Add a default top group 'stage_XYZ'
|
|
|
|
|
|
|
|
self.inventory.add_group(group="stage_" + stage)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for server in servers:
|
|
|
|
for server in servers:
|
|
|
|
serverName = server["name"]
|
|
|
|
serverName = server["name"]
|
|
|
|
serverLabels = server["labels"]
|
|
|
|
serverLabels = server["labels"]
|
|
|
|
serverService = serverLabels["service"]
|
|
|
|
serverStage = serverLabels["stage"]
|
|
|
|
|
|
|
|
serverService = serverLabels["service"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
display.display("server:<" + serverName + ">, stage=<" + serverStage + ">, service=<" + serverService + ">")
|
|
|
|
|
|
|
|
|
|
|
|
self.inventory.add_group(group=serverService)
|
|
|
|
self.inventory.add_group(group=serverService)
|
|
|
|
|
|
|
|
self.inventory.add_group(group="stage_" + serverStage)
|
|
|
|
|
|
|
|
|
|
|
|
self.inventory.add_host(serverName, group="hcloud")
|
|
|
|
self.inventory.add_host(serverName, group="hcloud")
|
|
|
|
self.inventory.add_host(serverName, group=serverService)
|
|
|
|
self.inventory.add_host(serverName, group=serverService)
|
|
|
|
self.inventory.add_host(serverName, group="stage_" + stage)
|
|
|
|
self.inventory.add_host(serverName, group="stage_" + serverStage)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO get the private server ip for the given stage
|
|
|
|
|
|
|
|
# self.inventory.set_variable(serverName, 'stage_server_ip', server["public_net"]["ipv4"]["ip"])
|
|
|
|
|
|
|
|
# self.inventory.set_variable(serverName, 'stage_private_server_ip', ...)
|
|
|
|
|