feat(inventory): handle servers that dont work with connect_with setting (#187)

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.
This commit is contained in:
Julian Tölle 2023-02-01 10:41:36 +01:00 committed by GitHub
parent 83094a121d
commit bfe0f7a575
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 17 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- inventory plugin - Log warning instead of crashing when some servers do not work with global connect_with setting.

View file

@ -37,7 +37,11 @@ DOCUMENTATION = r'''
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:
@ -237,22 +241,14 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
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:
@ -278,6 +274,39 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
# 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())