New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle connection to IPv6 hosts #51
Handle connection to IPv6 hosts #51
Conversation
runner_service/utils.py
Outdated
| try: | ||
| s.connect((self.host, self.port)) | ||
| if ipaddress.ip_address(self.host) == 4: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, unless I'm missing something this will raise ValueError if you have FQDN specified in host. I think we should use something like this:
ipv6_addr = True
try:
ipaddress.IPv6Address(self.host)
except ipaddress.AddressValueError:
ipv6_addr = False
if ipv6_addr:
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s.connect((self.host, self.port, 0, 0))
else:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.host, self.port))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Marcin is there any better solution how to support all possible combination of host specification (FQDN, IPv4 or IPv6)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to start off with socket.getaddrinfo. The way it works, is it returns a list of 5-element tuples that can be used to pass to socket.socket.
- If you use it with FQDN it will return ipv6 and/or ipv4 addresses in the order that is determined by DNS resolving configuration in your OS i.e.:
>>> pp(socket.getaddrinfo("localhost", 22, 0, socket.SOCK_STREAM))
[(<AddressFamily.AF_INET6: 10>,
<SocketKind.SOCK_STREAM: 1>,
6,
'',
('::1', 22, 0, 0)),
(<AddressFamily.AF_INET: 2>,
<SocketKind.SOCK_STREAM: 1>,
6,
'',
('127.0.0.1', 22))]
- if you use ipv4 it will limit the results to ipv4 addresses only, i.e.:
>>> pp(socket.getaddrinfo("127.0.0.1", 22, 0, socket.SOCK_STREAM))
[(<AddressFamily.AF_INET: 2>,
<SocketKind.SOCK_STREAM: 1>,
6,
'',
('127.0.0.1', 22))]
- analogical scenario for ipv6:
>>> pp(socket.getaddrinfo("::1", 22, 0, socket.SOCK_STREAM))
[(<AddressFamily.AF_INET6: 10>,
<SocketKind.SOCK_STREAM: 1>,
6,
'',
('::1', 22, 0, 0))]
- for non-resolvable hosts it will return the
socket.gaierror:
>>> socket.getaddrinfo("imaginary.host.address", 22, 0, socket.SOCK_STREAM)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.7/socket.py", line 752, in getaddrinfo
for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -2] Name or service not known
runner_service/utils.py
Outdated
| @@ -206,9 +207,25 @@ def timeout_handler(): | |||
| raise SSHTimeout | |||
|
|
|||
| socket.setdefaulttimeout(self.timeout) | |||
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |||
| socket.getaddrinfo(self.host, self.port, 0, 0, socket.SOL_TCP) | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you didn't understood my previous comment - if you use the return value of the socket.getaddrinfo you can actually get rid of all the if-ology below. Also, it's probably better to use socket.socket as a context manager. Smth like this should do:
try:
family, _, _, _, addr = socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM)[0]
except socket.gaierror:
raise HostNotFound
with socket.socket(family, socket.SOCK_STREAM) as s:
try:
s.connect(addr)
except ConnectionRefusedError:
raise SSHNotAccessible
except socket.timeout:
raise SSHTimeout3e66bc8
to
7f51e6f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems OK to me
|
LGTM |
runner_service/utils.py
Outdated
| @@ -4,6 +4,7 @@ | |||
| import socket | |||
| import sys | |||
| import getpass | |||
| import ipaddress | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not needed anymore
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 if we not using it, please remove
runner_service/utils.py
Outdated
| raise SSHTimeout | ||
| else: | ||
| s.shutdown(socket.SHUT_RDWR) | ||
| s.close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're using the context manager we don't need call s.close() by ourselves anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
runner_service/utils.py
Outdated
| try: | ||
| s.connect((self.host, self.port)) | ||
| family, type, proto, canonname, sockaddr = socket.getaddrinfo(self.host, self.port, 0, 0, socket.SOL_TCP)[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type, proto, canonname, - we don't actually use any of these, so it's better to replace them with _, _, _, - it's a convention.
Also, since we're explicitly asking for a SOCK_STREAM type of socket during creation, it would be nice to pass that to the type argument, so (self.host, self.port, 0, socket.SOCK_STREAM, socket.SOL_TCP) would be better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 again! if we're not using the vars, use _, to prevent flake errors
runner_service/utils.py
Outdated
| s.shutdown(socket.SHUT_RDWR) | ||
| s.close() | ||
|
|
||
| with socket.socket(family, socket.SOCK_STREAM) as s: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We were specifying socket.SOL_TCP during socket.getaddrinfo as protocol, so let's do the same here: (family, socket.SOCK_STREAM, socket.SOL_TCP)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense to me
This fix handles adding IPv6 hosts to inventory, which currently fails with the error: Failed to add host to inventory: SSH error - <host-ip> not found; check DNS or /etc/hosts
7f51e6f
to
46b8787
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm - thanks!
|
Looks good, thanks Dana! |
|
@tinez out of curiosity , why socket.create_connection is not used here? |
|
@dominikholler simply haven't thought of that... but indeed it seems it would also work and even make the code a bit more simple. |
|
Resolves #49 |
This fix handles adding IPv6 hosts to inventory, which currently fails with the error:
Failed to add host to inventory: SSH error - not found; check DNS or /etc/hosts
@pcuzner @jmolmo please review