3232from ...constants import DEFAULT_RETRIES , RETRY_DELAY
3333from ..reader import ConcurrentRWBuffer
3434from ...exceptions import UnknownHostError , AuthenticationError , \
35- ConnectionError , Timeout
35+ ConnectionError , Timeout , NoIPv6AddressFoundError
3636from ...output import HostOutput , HostOutputBuffers , BufferData
3737
3838
@@ -166,7 +166,9 @@ def __init__(self, host,
166166 proxy_host = None ,
167167 proxy_port = None ,
168168 _auth_thread_pool = True ,
169- identity_auth = True ):
169+ identity_auth = True ,
170+ ipv6_only = False ,
171+ ):
170172 self ._auth_thread_pool = _auth_thread_pool
171173 self .host = host
172174 self .user = user if user else getuser ()
@@ -183,6 +185,7 @@ def __init__(self, host,
183185 self .pkey = _validate_pkey_path (pkey , self .host )
184186 self .identity_auth = identity_auth
185187 self ._keepalive_greenlet = None
188+ self .ipv6_only = ipv6_only
186189 self ._init ()
187190
188191 def _init (self ):
@@ -254,25 +257,37 @@ def _connect_init_session_retry(self, retries):
254257 self ._connect (self ._host , self ._port , retries = retries )
255258 return self ._init_session (retries = retries )
256259
260+ def _get_addr_info (self , host , port ):
261+ addr_info = socket .getaddrinfo (host , port , proto = socket .IPPROTO_TCP )
262+ if self .ipv6_only :
263+ filtered = [addr for addr in addr_info if addr [0 ] is socket .AF_INET6 ]
264+ if not filtered :
265+ raise NoIPv6AddressFoundError (
266+ "Requested IPv6 only and no IPv6 addresses found for host %s from "
267+ "address list %s" , host , [addr for _ , _ , _ , _ , addr in addr_info ])
268+ addr_info = filtered
269+ return addr_info
270+
257271 def _connect (self , host , port , retries = 1 ):
258- self .sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
259- if self .timeout :
260- self .sock .settimeout (self .timeout )
261- logger .debug ("Connecting to %s:%s" , host , port )
262272 try :
263- self .sock . connect (( host , port ) )
273+ addr_info = self ._get_addr_info ( host , port )
264274 except sock_gaierror as ex :
265275 logger .error ("Could not resolve host '%s' - retry %s/%s" ,
266276 host , retries , self .num_retries )
267277 if retries < self .num_retries :
268278 sleep (self .retry_delay )
269279 return self ._connect (host , port , retries = retries + 1 )
270- ex = UnknownHostError ("Unknown host %s - %s - retry %s/%s" ,
271- host , str (ex .args [1 ]), retries ,
272- self .num_retries )
273- ex .host = host
274- ex .port = port
275- raise ex
280+ unknown_ex = UnknownHostError ("Unknown host %s - %s - retry %s/%s" ,
281+ host , str (ex .args [1 ]), retries ,
282+ self .num_retries )
283+ raise unknown_ex from ex
284+ family , _type , proto , _ , sock_addr = addr_info [0 ]
285+ self .sock = socket .socket (family , _type )
286+ if self .timeout :
287+ self .sock .settimeout (self .timeout )
288+ logger .debug ("Connecting to %s:%s" , host , port )
289+ try :
290+ self .sock .connect (sock_addr )
276291 except sock_error as ex :
277292 logger .error ("Error connecting to host '%s:%s' - retry %s/%s" ,
278293 host , port , retries , self .num_retries )
@@ -387,8 +402,8 @@ def read_stderr(self, stderr_buffer, timeout=None):
387402 """Read standard error buffer.
388403 Returns a generator of line by line output.
389404
390- :param stdout_buffer : Buffer to read from.
391- :type stdout_buffer : :py:class:`pssh.clients.reader.ConcurrentRWBuffer`
405+ :param stderr_buffer : Buffer to read from.
406+ :type stderr_buffer : :py:class:`pssh.clients.reader.ConcurrentRWBuffer`
392407 :rtype: generator
393408 """
394409 logger .debug ("Reading from stderr buffer, timeout=%s" , timeout )
0 commit comments