Skip to content

Commit

Permalink
feat(inventory): handle servers that dont work with connect_with setting
Browse files Browse the repository at this point in the history
Currently if any of the servers in the inventory do not work with the
selected `connect_with` mode, the script exits and returns 0 servers.
This can happen for example if one of your servers does not have a public
ipv4 address, but you set `connect_with: public_ipv4` (default).

This commit changes the behaviour to log a warning message, and just skip
setting `ansible_host` for this server. This server will not be reachable
by ansible by default, but users can use `compose` to override the
`ansible_host` that we set based on the other variables.
  • Loading branch information
apricote committed Jan 31, 2023
1 parent b491741 commit 12983df
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- inventory plugin - Log warning instead of crashing when some servers do not work with global connect_with setting.
65 changes: 47 additions & 18 deletions plugins/inventory/hcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@
type: str
required: false
connect_with:
description: Connect to the server using the value from this field.
description: |
Connect to the server using the value from this field. This sets the `ansible_host`
variable to the value indicated, if that value is available. If you need further
customization, like falling back to private ipv4 if the server has no public ipv4,
you can use `compose` top-level key.
default: public_ipv4
type: str
choices:
Expand Down Expand Up @@ -127,7 +131,6 @@
except ImportError:
HAS_HCLOUD = False


class InventoryModule(BaseInventoryPlugin, Constructable):
NAME = 'hetzner.hcloud.hcloud'

Expand Down Expand Up @@ -238,22 +241,14 @@ def _set_server_attributes(self, server):
if server_private_network.network.id == self.network.id:
self.inventory.set_variable(server.name, "private_ipv4", to_native(server_private_network.ip))

if self.get_option("connect_with") == "public_ipv4":
self.inventory.set_variable(server.name, "ansible_host", to_native(server.public_net.ipv4.ip))
if self.get_option("connect_with") == "public_ipv6":
self.inventory.set_variable(server.name, "ansible_host", to_native(self._first_ipv6_address(server.public_net.ipv6.ip)))
elif self.get_option("connect_with") == "hostname":
self.inventory.set_variable(server.name, "ansible_host", to_native(server.name))
elif self.get_option("connect_with") == "ipv4_dns_ptr":
self.inventory.set_variable(server.name, "ansible_host", to_native(server.public_net.ipv4.dns_ptr))
elif self.get_option("connect_with") == "private_ipv4":
if self.get_option("network"):
for server_private_network in server.private_net:
if server_private_network.network.id == self.network.id:
self.inventory.set_variable(server.name, "ansible_host", to_native(server_private_network.ip))
else:
raise AnsibleError(
"You can only connect via private IPv4 if you specify a network")
try:
self.inventory.set_variable(server.name, "ansible_host", self._get_server_ansible_host(server))
except AnsibleError as e:
# Log warning that for this host can not be connected to, using the
# method specified in `connect_with`. Users might use `compose` to
# override the connection method, or implement custom logic, so we
# do not need to abort if nothing matched.
self.display.v("[hcloud] %s" % e, server.name)

# Server Type
if server.server_type is not None:
Expand All @@ -279,6 +274,40 @@ def _set_server_attributes(self, server):
# Labels
self.inventory.set_variable(server.name, "labels", dict(server.labels))

def _get_server_ansible_host(self, server):
if self.get_option("connect_with") == "public_ipv4":
if server.public_net.ipv4:
return to_native(server.public_net.ipv4.ip)
else:
raise AnsibleError("Server has no public ipv4, but connect_with=public_ipv4 was specified")

if self.get_option("connect_with") == "public_ipv6":
if server.public_net.ipv6:
return to_native(self._first_ipv6_address(server.public_net.ipv6.ip))
else:
raise AnsibleError("Server has no public ipv6, but connect_with=public_ipv6 was specified")

elif self.get_option("connect_with") == "hostname":
# every server has a name, no need to guard this
return to_native(server.name)

elif self.get_option("connect_with") == "ipv4_dns_ptr":
if server.public_net.ipv4:
return to_native(server.public_net.ipv4.dns_ptr)
else:
raise AnsibleError("Server has no public ipv4, but connect_with=ipv4_dns_ptr was specified")

elif self.get_option("connect_with") == "private_ipv4":
if self.get_option("network"):
for server_private_network in server.private_net:
if server_private_network.network.id == self.network.id:
return to_native(server_private_network.ip)

else:
raise AnsibleError(
"You can only connect via private IPv4 if you specify a network")


def _first_ipv6_address(self, network):
return next(IPv6Network(network).hosts())

Expand Down

0 comments on commit 12983df

Please sign in to comment.