mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
allow for non standard hostnames
* Changed parse_addresses to throw exceptions instead of passing None * Switched callers to trap and pass through the original values. * Added very verbose notice * Look at deprecating this and possibly validate at plugin instead fixes #13608
This commit is contained in:
parent
593d80c63d
commit
75e94e0cba
5 changed files with 49 additions and 29 deletions
|
@ -109,7 +109,12 @@ class Inventory(object):
|
||||||
pass
|
pass
|
||||||
elif isinstance(host_list, list):
|
elif isinstance(host_list, list):
|
||||||
for h in host_list:
|
for h in host_list:
|
||||||
|
try:
|
||||||
(host, port) = parse_address(h, allow_ranges=False)
|
(host, port) = parse_address(h, allow_ranges=False)
|
||||||
|
except AnsibleError as e:
|
||||||
|
display.vvv("Unable to parse address from hostname, leaving unchanged: %s" % to_string(e))
|
||||||
|
host = h
|
||||||
|
port = None
|
||||||
all.add_host(Host(host, port))
|
all.add_host(Host(host, port))
|
||||||
elif self._loader.path_exists(host_list):
|
elif self._loader.path_exists(host_list):
|
||||||
#TODO: switch this to a plugin loader and a 'condition' per plugin on which it should be tried, restoring 'inventory pllugins'
|
#TODO: switch this to a plugin loader and a 'condition' per plugin on which it should be tried, restoring 'inventory pllugins'
|
||||||
|
@ -228,15 +233,13 @@ class Inventory(object):
|
||||||
# If it doesn't, it could still be a single pattern. This accounts for
|
# If it doesn't, it could still be a single pattern. This accounts for
|
||||||
# non-separator uses of colons: IPv6 addresses and [x:y] host ranges.
|
# non-separator uses of colons: IPv6 addresses and [x:y] host ranges.
|
||||||
else:
|
else:
|
||||||
|
try:
|
||||||
(base, port) = parse_address(pattern, allow_ranges=True)
|
(base, port) = parse_address(pattern, allow_ranges=True)
|
||||||
if base:
|
|
||||||
patterns = [pattern]
|
patterns = [pattern]
|
||||||
|
except:
|
||||||
# The only other case we accept is a ':'-separated list of patterns.
|
# The only other case we accept is a ':'-separated list of patterns.
|
||||||
# This mishandles IPv6 addresses, and is retained only for backwards
|
# This mishandles IPv6 addresses, and is retained only for backwards
|
||||||
# compatibility.
|
# compatibility.
|
||||||
|
|
||||||
else:
|
|
||||||
patterns = re.findall(
|
patterns = re.findall(
|
||||||
r'''(?: # We want to match something comprising:
|
r'''(?: # We want to match something comprising:
|
||||||
[^\s:\[\]] # (anything other than whitespace or ':[]'
|
[^\s:\[\]] # (anything other than whitespace or ':[]'
|
||||||
|
|
|
@ -23,7 +23,7 @@ import ast
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible import constants as C
|
from ansible import constants as C
|
||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError, AnsibleParserError
|
||||||
from ansible.inventory.host import Host
|
from ansible.inventory.host import Host
|
||||||
from ansible.inventory.group import Group
|
from ansible.inventory.group import Group
|
||||||
from ansible.inventory.expand_hosts import detect_range
|
from ansible.inventory.expand_hosts import detect_range
|
||||||
|
@ -264,9 +264,12 @@ class InventoryParser(object):
|
||||||
# Can the given hostpattern be parsed as a host with an optional port
|
# Can the given hostpattern be parsed as a host with an optional port
|
||||||
# specification?
|
# specification?
|
||||||
|
|
||||||
|
try:
|
||||||
(pattern, port) = parse_address(hostpattern, allow_ranges=True)
|
(pattern, port) = parse_address(hostpattern, allow_ranges=True)
|
||||||
if not pattern:
|
except:
|
||||||
self._raise_error("Can't parse '%s' as host[:port]" % hostpattern)
|
# not a recognizable host pattern
|
||||||
|
pattern = hostpattern
|
||||||
|
port = None
|
||||||
|
|
||||||
# Once we have separated the pattern, we expand it into list of one or
|
# Once we have separated the pattern, we expand it into list of one or
|
||||||
# more hostnames, depending on whether it contains any [x:y] ranges.
|
# more hostnames, depending on whether it contains any [x:y] ranges.
|
||||||
|
|
|
@ -20,6 +20,7 @@ from __future__ import (absolute_import, division, print_function)
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.errors import AnsibleParserError, AnsibleError
|
||||||
|
|
||||||
# Components that match a numeric or alphanumeric begin:end or begin:end:step
|
# Components that match a numeric or alphanumeric begin:end or begin:end:step
|
||||||
# range expression inside square brackets.
|
# range expression inside square brackets.
|
||||||
|
@ -162,6 +163,7 @@ patterns = {
|
||||||
$
|
$
|
||||||
'''.format(label=label), re.X|re.I|re.UNICODE
|
'''.format(label=label), re.X|re.I|re.UNICODE
|
||||||
),
|
),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse_address(address, allow_ranges=False):
|
def parse_address(address, allow_ranges=False):
|
||||||
|
@ -183,8 +185,8 @@ def parse_address(address, allow_ranges=False):
|
||||||
# First, we extract the port number if one is specified.
|
# First, we extract the port number if one is specified.
|
||||||
|
|
||||||
port = None
|
port = None
|
||||||
for type in ['bracketed_hostport', 'hostport']:
|
for matching in ['bracketed_hostport', 'hostport']:
|
||||||
m = patterns[type].match(address)
|
m = patterns[matching].match(address)
|
||||||
if m:
|
if m:
|
||||||
(address, port) = m.groups()
|
(address, port) = m.groups()
|
||||||
port = int(port)
|
port = int(port)
|
||||||
|
@ -194,22 +196,20 @@ def parse_address(address, allow_ranges=False):
|
||||||
# numeric ranges, or a hostname with alphanumeric ranges.
|
# numeric ranges, or a hostname with alphanumeric ranges.
|
||||||
|
|
||||||
host = None
|
host = None
|
||||||
for type in ['ipv4', 'ipv6', 'hostname']:
|
for matching in ['ipv4', 'ipv6', 'hostname']:
|
||||||
m = patterns[type].match(address)
|
m = patterns[matching].match(address)
|
||||||
if m:
|
if m:
|
||||||
host = address
|
host = address
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# If it isn't any of the above, we don't understand it.
|
# If it isn't any of the above, we don't understand it.
|
||||||
|
|
||||||
if not host:
|
if not host:
|
||||||
return (None, None)
|
raise AnsibleError("Not a valid network hostname: %s" % address)
|
||||||
|
|
||||||
# If we get to this point, we know that any included ranges are valid. If
|
|
||||||
# the caller is prepared to handle them, all is well. Otherwise we treat
|
|
||||||
# it as a parse failure.
|
|
||||||
|
|
||||||
|
# If we get to this point, we know that any included ranges are valid.
|
||||||
|
# If the caller is prepared to handle them, all is well.
|
||||||
|
# Otherwise we treat it as a parse failure.
|
||||||
if not allow_ranges and '[' in host:
|
if not allow_ranges and '[' in host:
|
||||||
return (None, None)
|
raise AnsibleParserError("Detected range in host but was asked to ignore ranges")
|
||||||
|
|
||||||
return (host, port)
|
return (host, port)
|
||||||
|
|
|
@ -53,9 +53,13 @@ class ActionModule(ActionBase):
|
||||||
new_name = self._task.args.get('name', self._task.args.get('hostname', None))
|
new_name = self._task.args.get('name', self._task.args.get('hostname', None))
|
||||||
display.vv("creating host via 'add_host': hostname=%s" % new_name)
|
display.vv("creating host via 'add_host': hostname=%s" % new_name)
|
||||||
|
|
||||||
|
try:
|
||||||
name, port = parse_address(new_name, allow_ranges=False)
|
name, port = parse_address(new_name, allow_ranges=False)
|
||||||
if not name:
|
except:
|
||||||
raise AnsibleError("Invalid inventory hostname: %s" % new_name)
|
# not a parsable hostname, but might still be usable
|
||||||
|
name = new_name
|
||||||
|
port = None
|
||||||
|
|
||||||
if port:
|
if port:
|
||||||
self._task.args['ansible_ssh_port'] = port
|
self._task.args['ansible_ssh_port'] = port
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,12 @@ class TestParseAddress(unittest.TestCase):
|
||||||
for t in self.tests:
|
for t in self.tests:
|
||||||
test = self.tests[t]
|
test = self.tests[t]
|
||||||
|
|
||||||
|
try:
|
||||||
(host, port) = parse_address(t)
|
(host, port) = parse_address(t)
|
||||||
|
except:
|
||||||
|
host = None
|
||||||
|
port = None
|
||||||
|
|
||||||
assert host == test[0]
|
assert host == test[0]
|
||||||
assert port == test[1]
|
assert port == test[1]
|
||||||
|
|
||||||
|
@ -79,6 +84,11 @@ class TestParseAddress(unittest.TestCase):
|
||||||
for t in self.range_tests:
|
for t in self.range_tests:
|
||||||
test = self.range_tests[t]
|
test = self.range_tests[t]
|
||||||
|
|
||||||
|
try:
|
||||||
(host, port) = parse_address(t, allow_ranges=True)
|
(host, port) = parse_address(t, allow_ranges=True)
|
||||||
|
except:
|
||||||
|
host = None
|
||||||
|
port = None
|
||||||
|
|
||||||
assert host == test[0]
|
assert host == test[0]
|
||||||
assert port == test[1]
|
assert port == test[1]
|
||||||
|
|
Loading…
Reference in a new issue