@ -98,14 +98,20 @@ except ImportError:
# Python 3
# Python 3
import http . client as httplib
import http . client as httplib
import ansible . module_utils . six . moves . urllib . request as urllib_request
import ansible . module_utils . six . moves . urllib . error as urllib_error
try :
try :
import urllib2
# python3
HAS_URLLIB2 = True
import urllib . request as urllib_request
except :
from urllib . request import AbstractHTTPHandler
HAS_URLLIB2 = False
except ImportError :
# python2
import urllib2 as urllib_request
from urllib2 import AbstractHTTPHandler
try :
try :
import urlparse
from ansible . module_utils . six . moves . urllib . parse import urlparse , urlun parse
HAS_URLPARSE = True
HAS_URLPARSE = True
except :
except :
HAS_URLPARSE = False
HAS_URLPARSE = False
@ -140,7 +146,8 @@ if HAS_SSL:
PROTOCOL = ssl . PROTOCOL_TLSv1
PROTOCOL = ssl . PROTOCOL_TLSv1
if not HAS_SSLCONTEXT and HAS_SSL :
if not HAS_SSLCONTEXT and HAS_SSL :
try :
try :
import ctypes , ctypes . util
import ctypes
import ctypes . util
except ImportError :
except ImportError :
# python 2.4 (likely rhel5 which doesn't have tls1.1 support in its openssl)
# python 2.4 (likely rhel5 which doesn't have tls1.1 support in its openssl)
pass
pass
@ -159,7 +166,6 @@ if not HAS_SSLCONTEXT and HAS_SSL:
del libssl
del libssl
HAS_MATCH_HOSTNAME = True
HAS_MATCH_HOSTNAME = True
try :
try :
from ssl import match_hostname , CertificateError
from ssl import match_hostname , CertificateError
@ -308,18 +314,22 @@ zKPZsZ2miVGclicJHzm5q080b1p/sZtuKIEZk6vZqEg=
# Exceptions
# Exceptions
#
#
class ConnectionError ( Exception ) :
class ConnectionError ( Exception ) :
""" Failed to connect to the server """
""" Failed to connect to the server """
pass
pass
class ProxyError ( ConnectionError ) :
class ProxyError ( ConnectionError ) :
""" Failure to connect because of a proxy """
""" Failure to connect because of a proxy """
pass
pass
class SSLValidationError ( ConnectionError ) :
class SSLValidationError ( ConnectionError ) :
""" Failure to connect due to SSL validation failing """
""" Failure to connect due to SSL validation failing """
pass
pass
class NoSSLError ( SSLValidationError ) :
class NoSSLError ( SSLValidationError ) :
""" Needed to connect to an HTTPS url but no ssl library available to verify the certificate """
""" Needed to connect to an HTTPS url but no ssl library available to verify the certificate """
pass
pass
@ -327,7 +337,7 @@ class NoSSLError(SSLValidationError):
# Some environments (Google Compute Engine's CoreOS deploys) do not compile
# Some environments (Google Compute Engine's CoreOS deploys) do not compile
# against openssl and thus do not have any HTTPS support.
# against openssl and thus do not have any HTTPS support.
CustomHTTPSConnection = CustomHTTPSHandler = None
CustomHTTPSConnection = CustomHTTPSHandler = None
if hasattr ( httplib , ' HTTPSConnection ' ) and hasattr ( urllib 2 , ' HTTPSHandler ' ) :
if hasattr ( httplib , ' HTTPSConnection ' ) and hasattr ( urllib _request , ' HTTPSHandler ' ) :
class CustomHTTPSConnection ( httplib . HTTPSConnection ) :
class CustomHTTPSConnection ( httplib . HTTPSConnection ) :
def __init__ ( self , * args , * * kwargs ) :
def __init__ ( self , * args , * * kwargs ) :
httplib . HTTPSConnection . __init__ ( self , * args , * * kwargs )
httplib . HTTPSConnection . __init__ ( self , * args , * * kwargs )
@ -355,16 +365,18 @@ if hasattr(httplib, 'HTTPSConnection') and hasattr(urllib2, 'HTTPSHandler'):
if HAS_SSLCONTEXT :
if HAS_SSLCONTEXT :
self . sock = self . context . wrap_socket ( sock , server_hostname = server_hostname )
self . sock = self . context . wrap_socket ( sock , server_hostname = server_hostname )
elif HAS_URLLIB3_SNI_SUPPORT :
elif HAS_URLLIB3_SNI_SUPPORT :
self . sock = ssl_wrap_socket ( sock , keyfile = self . key_file , cert_reqs = ssl . CERT_NONE , certfile = self . cert_file , ssl_version = PROTOCOL , server_hostname = server_hostname )
self . sock = ssl_wrap_socket ( sock , keyfile = self . key_file , cert_reqs = ssl . CERT_NONE , certfile = self . cert_file , ssl_version = PROTOCOL ,
server_hostname = server_hostname )
else :
else :
self . sock = ssl . wrap_socket ( sock , keyfile = self . key_file , certfile = self . cert_file , ssl_version = PROTOCOL )
self . sock = ssl . wrap_socket ( sock , keyfile = self . key_file , certfile = self . cert_file , ssl_version = PROTOCOL )
class CustomHTTPSHandler ( urllib 2 . HTTPSHandler ) :
class CustomHTTPSHandler ( urllib _request . HTTPSHandler ) :
def https_open ( self , req ) :
def https_open ( self , req ) :
return self . do_open ( CustomHTTPSConnection , req )
return self . do_open ( CustomHTTPSConnection , req )
https_request = urllib2 . AbstractHTTPHandler . do_request_
https_request = AbstractHTTPHandler . do_request_
def generic_urlparse ( parts ) :
def generic_urlparse ( parts ) :
'''
'''
@ -424,7 +436,8 @@ def generic_urlparse(parts):
generic_parts [ ' port ' ] = None
generic_parts [ ' port ' ] = None
return generic_parts
return generic_parts
class RequestWithMethod ( urllib2 . Request ) :
class RequestWithMethod ( urllib_request . Request ) :
'''
'''
Workaround for using DELETE / PUT / etc with urllib2
Workaround for using DELETE / PUT / etc with urllib2
Originally contained in library / net_infrastructure / dnsmadeeasy
Originally contained in library / net_infrastructure / dnsmadeeasy
@ -434,13 +447,13 @@ class RequestWithMethod(urllib2.Request):
if headers is None :
if headers is None :
headers = { }
headers = { }
self . _method = method . upper ( )
self . _method = method . upper ( )
urllib 2 . Request . __init__ ( self , url , data , headers )
urllib _request . Request . __init__ ( self , url , data , headers )
def get_method ( self ) :
def get_method ( self ) :
if self . _method :
if self . _method :
return self . _method
return self . _method
else :
else :
return urllib 2 . Request . get_method ( self )
return urllib _request . Request . get_method ( self )
def RedirectHandlerFactory ( follow_redirects = None , validate_certs = True ) :
def RedirectHandlerFactory ( follow_redirects = None , validate_certs = True ) :
@ -450,7 +463,7 @@ def RedirectHandlerFactory(follow_redirects=None, validate_certs=True):
where ` ` open_url ` ` or ` ` fetch_url ` ` are used multiple times in a module .
where ` ` open_url ` ` or ` ` fetch_url ` ` are used multiple times in a module .
"""
"""
class RedirectHandler ( urllib 2 . HTTPRedirectHandler ) :
class RedirectHandler ( urllib _request . HTTPRedirectHandler ) :
""" This is an implementation of a RedirectHandler to match the
""" This is an implementation of a RedirectHandler to match the
functionality provided by httplib2 . It will utilize the value of
functionality provided by httplib2 . It will utilize the value of
` ` follow_redirects ` ` that is passed into ` ` RedirectHandlerFactory ` `
` ` follow_redirects ` ` that is passed into ` ` RedirectHandlerFactory ` `
@ -460,12 +473,12 @@ def RedirectHandlerFactory(follow_redirects=None, validate_certs=True):
def redirect_request ( self , req , fp , code , msg , hdrs , newurl ) :
def redirect_request ( self , req , fp , code , msg , hdrs , newurl ) :
handler = maybe_add_ssl_handler ( newurl , validate_certs )
handler = maybe_add_ssl_handler ( newurl , validate_certs )
if handler :
if handler :
urllib 2 . _opener . add_handler ( handler )
urllib _request . _opener . add_handler ( handler )
if follow_redirects == ' urllib2 ' :
if follow_redirects == ' urllib2 ' :
return urllib 2 . HTTPRedirectHandler . redirect_request ( self , req , fp , code , msg , hdrs , newurl )
return urllib _request . HTTPRedirectHandler . redirect_request ( self , req , fp , code , msg , hdrs , newurl )
elif follow_redirects in [ ' no ' , ' none ' , False ] :
elif follow_redirects in [ ' no ' , ' none ' , False ] :
raise urllib 2 . HTTPError ( newurl , code , msg , hdrs , fp )
raise urllib _error . HTTPError ( newurl , code , msg , hdrs , fp )
do_redirect = False
do_redirect = False
if follow_redirects in [ ' all ' , ' yes ' , True ] :
if follow_redirects in [ ' all ' , ' yes ' , True ] :
@ -481,13 +494,12 @@ def RedirectHandlerFactory(follow_redirects=None, validate_certs=True):
newheaders = dict ( ( k , v ) for k , v in req . headers . items ( )
newheaders = dict ( ( k , v ) for k , v in req . headers . items ( )
if k . lower ( ) not in ( " content-length " , " content-type " )
if k . lower ( ) not in ( " content-length " , " content-type " )
)
)
return urllib 2 . Request ( newurl ,
return urllib _request . Request ( newurl ,
headers = newheaders ,
headers = newheaders ,
origin_req_host = req . get_origin_req_host ( ) ,
origin_req_host = req . get_origin_req_host ( ) ,
unverifiable = True )
unverifiable = True )
else :
else :
raise urllib2 . HTTPError ( req . get_full_url ( ) , code , msg , hdrs ,
raise urllib_error . HTTPError ( req . get_full_url ( ) , code , msg , hdrs , fp )
fp )
return RedirectHandler
return RedirectHandler
@ -519,7 +531,7 @@ def build_ssl_validation_error(hostname, port, paths):
raise SSLValidationError ( ' ' . join ( msg ) % ( hostname , port , " , " . join ( paths ) ) )
raise SSLValidationError ( ' ' . join ( msg ) % ( hostname , port , " , " . join ( paths ) ) )
class SSLValidationHandler ( urllib 2 . BaseHandler ) :
class SSLValidationHandler ( urllib _request . BaseHandler ) :
'''
'''
A custom handler class for SSL validation .
A custom handler class for SSL validation .
@ -606,7 +618,7 @@ class SSLValidationHandler(urllib2.BaseHandler):
env_no_proxy = os . environ . get ( ' no_proxy ' )
env_no_proxy = os . environ . get ( ' no_proxy ' )
if env_no_proxy :
if env_no_proxy :
env_no_proxy = env_no_proxy . split ( ' , ' )
env_no_proxy = env_no_proxy . split ( ' , ' )
netloc = urlparse . urlparse ( url ) . netloc
netloc = urlparse ( url ) . netloc
for host in env_no_proxy :
for host in env_no_proxy :
if netloc . endswith ( host ) or netloc . split ( ' : ' ) [ 0 ] . endswith ( host ) :
if netloc . endswith ( host ) or netloc . split ( ' : ' ) [ 0 ] . endswith ( host ) :
@ -637,7 +649,7 @@ class SSLValidationHandler(urllib2.BaseHandler):
try :
try :
s = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
s = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
if https_proxy :
if https_proxy :
proxy_parts = generic_urlparse ( urlparse . urlparse ( https_proxy ) )
proxy_parts = generic_urlparse ( urlparse ( https_proxy ) )
port = proxy_parts . get ( ' port ' ) or 443
port = proxy_parts . get ( ' port ' ) or 443
s . connect ( ( proxy_parts . get ( ' hostname ' ) , port ) )
s . connect ( ( proxy_parts . get ( ' hostname ' ) , port ) )
if proxy_parts . get ( ' scheme ' ) == ' http ' :
if proxy_parts . get ( ' scheme ' ) == ' http ' :
@ -690,13 +702,15 @@ class SSLValidationHandler(urllib2.BaseHandler):
https_request = http_request
https_request = http_request
def maybe_add_ssl_handler ( url , validate_certs ) :
def maybe_add_ssl_handler ( url , validate_certs ) :
# FIXME: change the following to use the generic_urlparse function
# FIXME: change the following to use the generic_urlparse function
# to remove the indexed references for 'parsed'
# to remove the indexed references for 'parsed'
parsed = urlparse . urlparse ( url )
parsed = urlparse ( url )
if parsed [ 0 ] == ' https ' and validate_certs :
if parsed [ 0 ] == ' https ' and validate_certs :
if not HAS_SSL :
if not HAS_SSL :
raise NoSSLError ( ' SSL validation is not available in your version of python. You can use validate_certs=False, however this is unsafe and not recommended ' )
raise NoSSLError ( ' SSL validation is not available in your version of python. You can use validate_certs=False, '
' however this is unsafe and not recommended ' )
# do the cert validation
# do the cert validation
netloc = parsed [ 1 ]
netloc = parsed [ 1 ]
@ -712,13 +726,15 @@ def maybe_add_ssl_handler(url, validate_certs):
# add it to the list of handlers
# add it to the list of handlers
return SSLValidationHandler ( hostname , port )
return SSLValidationHandler ( hostname , port )
# Rewrite of fetch_url to not require the module environment
def open_url ( url , data = None , headers = None , method = None , use_proxy = True ,
def open_url ( url , data = None , headers = None , method = None , use_proxy = True ,
force = False , last_mod_time = None , timeout = 10 , validate_certs = True ,
force = False , last_mod_time = None , timeout = 10 , validate_certs = True ,
url_username = None , url_password = None , http_agent = None ,
url_username = None , url_password = None , http_agent = None ,
force_basic_auth = False , follow_redirects = ' urllib2 ' ) :
force_basic_auth = False , follow_redirects = ' urllib2 ' ) :
'''
'''
Fetches a file from an HTTP / FTP server using urllib2
Fetches a file from an HTTP / FTP server using urllib2
Does not require the module environment
'''
'''
handlers = [ ]
handlers = [ ]
ssl_handler = maybe_add_ssl_handler ( url , validate_certs )
ssl_handler = maybe_add_ssl_handler ( url , validate_certs )
@ -727,7 +743,7 @@ def open_url(url, data=None, headers=None, method=None, use_proxy=True,
# FIXME: change the following to use the generic_urlparse function
# FIXME: change the following to use the generic_urlparse function
# to remove the indexed references for 'parsed'
# to remove the indexed references for 'parsed'
parsed = urlparse . urlparse ( url )
parsed = urlparse ( url )
if parsed [ 0 ] != ' ftp ' :
if parsed [ 0 ] != ' ftp ' :
username = url_username
username = url_username
@ -749,10 +765,10 @@ def open_url(url, data=None, headers=None, method=None, use_proxy=True,
parsed [ 1 ] = netloc
parsed [ 1 ] = netloc
# reconstruct url without credentials
# reconstruct url without credentials
url = url parse. url unparse( parsed )
url = url unparse( parsed )
if username and not force_basic_auth :
if username and not force_basic_auth :
passman = urllib 2 . HTTPPasswordMgrWithDefaultRealm ( )
passman = urllib _request . HTTPPasswordMgrWithDefaultRealm ( )
# this creates a password manager
# this creates a password manager
passman . add_password ( None , netloc , username , password )
passman . add_password ( None , netloc , username , password )
@ -760,7 +776,7 @@ def open_url(url, data=None, headers=None, method=None, use_proxy=True,
# because we have put None at the start it will always
# because we have put None at the start it will always
# use this username/password combination for urls
# use this username/password combination for urls
# for which `theurl` is a super-url
# for which `theurl` is a super-url
authhandler = urllib 2 . HTTPBasicAuthHandler ( passman )
authhandler = urllib _request . HTTPBasicAuthHandler ( passman )
# create the AuthHandler
# create the AuthHandler
handlers . append ( authhandler )
handlers . append ( authhandler )
@ -781,7 +797,7 @@ def open_url(url, data=None, headers=None, method=None, use_proxy=True,
headers [ " Authorization " ] = basic_auth_header ( username , password )
headers [ " Authorization " ] = basic_auth_header ( username , password )
if not use_proxy :
if not use_proxy :
proxyhandler = urllib 2 . ProxyHandler ( { } )
proxyhandler = urllib _request . ProxyHandler ( { } )
handlers . append ( proxyhandler )
handlers . append ( proxyhandler )
if HAS_SSLCONTEXT and not validate_certs :
if HAS_SSLCONTEXT and not validate_certs :
@ -791,7 +807,7 @@ def open_url(url, data=None, headers=None, method=None, use_proxy=True,
context . options | = ssl . OP_NO_SSLv3
context . options | = ssl . OP_NO_SSLv3
context . verify_mode = ssl . CERT_NONE
context . verify_mode = ssl . CERT_NONE
context . check_hostname = False
context . check_hostname = False
handlers . append ( urllib 2 . HTTPSHandler ( context = context ) )
handlers . append ( urllib _request . HTTPSHandler ( context = context ) )
# pre-2.6 versions of python cannot use the custom https
# pre-2.6 versions of python cannot use the custom https
# handler, since the socket class is lacking create_connection.
# handler, since the socket class is lacking create_connection.
@ -801,15 +817,15 @@ def open_url(url, data=None, headers=None, method=None, use_proxy=True,
handlers . append ( RedirectHandlerFactory ( follow_redirects , validate_certs ) )
handlers . append ( RedirectHandlerFactory ( follow_redirects , validate_certs ) )
opener = urllib 2 . build_opener ( * handlers )
opener = urllib _request . build_opener ( * handlers )
urllib 2 . install_opener ( opener )
urllib _request . install_opener ( opener )
if method :
if method :
if method . upper ( ) not in ( ' OPTIONS ' , ' GET ' , ' HEAD ' , ' POST ' , ' PUT ' , ' DELETE ' , ' TRACE ' , ' CONNECT ' , ' PATCH ' ) :
if method . upper ( ) not in ( ' OPTIONS ' , ' GET ' , ' HEAD ' , ' POST ' , ' PUT ' , ' DELETE ' , ' TRACE ' , ' CONNECT ' , ' PATCH ' ) :
raise ConnectionError ( ' invalid HTTP request method; %s ' % method . upper ( ) )
raise ConnectionError ( ' invalid HTTP request method; %s ' % method . upper ( ) )
request = RequestWithMethod ( url , method . upper ( ) , data )
request = RequestWithMethod ( url , method . upper ( ) , data )
else :
else :
request = urllib 2 . Request ( url , data )
request = urllib _request . Request ( url , data )
# add the custom agent header, to help prevent issues
# add the custom agent header, to help prevent issues
# with sites that block the default urllib agent string
# with sites that block the default urllib agent string
@ -836,16 +852,18 @@ def open_url(url, data=None, headers=None, method=None, use_proxy=True,
# have a timeout parameter
# have a timeout parameter
urlopen_args . append ( timeout )
urlopen_args . append ( timeout )
r = urllib 2 . urlopen ( * urlopen_args )
r = urllib _request . urlopen ( * urlopen_args )
return r
return r
#
#
# Module-related functions
# Module-related functions
#
#
def basic_auth_header ( username , password ) :
def basic_auth_header ( username , password ) :
return " Basic %s " % base64 . b64encode ( " %s : %s " % ( username , password ) )
return " Basic %s " % base64 . b64encode ( " %s : %s " % ( username , password ) )
def url_argument_spec ( ) :
def url_argument_spec ( ) :
'''
'''
Creates an argument spec that can be used with any module
Creates an argument spec that can be used with any module
@ -863,15 +881,14 @@ def url_argument_spec():
)
)
def fetch_url ( module , url , data = None , headers = None , method = None ,
def fetch_url ( module , url , data = None , headers = None , method = None ,
use_proxy = True , force = False , last_mod_time = None , timeout = 10 ) :
use_proxy = True , force = False , last_mod_time = None , timeout = 10 ) :
'''
'''
Fetches a file from an HTTP / FTP server using urllib2 . Requires the module environment
Fetches a file from an HTTP / FTP server using urllib2 . Requires the module environment
'''
'''
if not HAS_URLLIB2 :
if not HAS_URLPARSE :
module . fail_json ( msg = ' urllib2 is not installed ' )
elif not HAS_URLPARSE :
module . fail_json ( msg = ' urlparse is not installed ' )
module . fail_json ( msg = ' urlparse is not installed ' )
# Get validate_certs from the module params
# Get validate_certs from the module params
@ -904,7 +921,7 @@ def fetch_url(module, url, data=None, headers=None, method=None,
except ( ConnectionError , ValueError ) :
except ( ConnectionError , ValueError ) :
e = get_exception ( )
e = get_exception ( )
module . fail_json ( msg = str ( e ) )
module . fail_json ( msg = str ( e ) )
except urllib 2 . HTTPError :
except urllib _error . HTTPError :
e = get_exception ( )
e = get_exception ( )
try :
try :
body = e . read ( )
body = e . read ( )
@ -912,7 +929,7 @@ def fetch_url(module, url, data=None, headers=None, method=None,
body = ' '
body = ' '
info . update ( dict ( msg = str ( e ) , body = body , * * e . info ( ) ) )
info . update ( dict ( msg = str ( e ) , body = body , * * e . info ( ) ) )
info [ ' status ' ] = e . code
info [ ' status ' ] = e . code
except urllib 2 . URLError :
except urllib _error . URLError :
e = get_exception ( )
e = get_exception ( )
code = int ( getattr ( e , ' code ' , - 1 ) )
code = int ( getattr ( e , ' code ' , - 1 ) )
info . update ( dict ( msg = " Request failed: %s " % str ( e ) , status = code ) )
info . update ( dict ( msg = " Request failed: %s " % str ( e ) , status = code ) )