88import threading
99from contextlib import closing
1010from wsgiref .simple_server import make_server
11+ import base64
12+ import sys
1113
1214from . import core
1315try :
@@ -118,7 +120,46 @@ def write_to_textfile(path, registry):
118120 os .rename (tmppath , path )
119121
120122
121- def push_to_gateway (gateway , job , registry , grouping_key = None , timeout = None ):
123+ def default_handler (url , method , timeout , headers , data ):
124+ '''Default handler that implements HTTP/HTTPS connections.
125+
126+ Used by the push_to_gateway functions. Can be re-used by other handlers.'''
127+ def handle ():
128+ request = Request (url , data = data )
129+ request .get_method = lambda : method
130+ for k , v in headers :
131+ request .add_header (k , v )
132+ resp = build_opener (HTTPHandler ).open (request , timeout = timeout )
133+ if resp .code >= 400 :
134+ raise IOError ("error talking to pushgateway: {0} {1}" .format (
135+ resp .code , resp .msg ))
136+
137+ return handle
138+
139+
140+ def basic_auth_handler (url , method , timeout , headers , data , username = None , password = None ):
141+ '''Handler that implements HTTP/HTTPS connections with Basic Auth.
142+
143+ Sets auth headers using supplied 'username' and 'password', if set.
144+ Used by the push_to_gateway functions. Can be re-used by other handlers.'''
145+ def handle ():
146+ '''Handler that implements HTTP Basic Auth.
147+ '''
148+ if username is not None and password is not None :
149+ if sys .version_info >= (3 ,0 ):
150+ auth_value = bytes ('{0}:{1}' .format (username , password ), 'utf8' )
151+ auth_token = str (base64 .b64encode (auth_value ), 'utf8' )
152+ else :
153+ auth_value = '{0}:{1}' .format (username , password )
154+ auth_token = base64 .b64encode (auth_value )
155+ auth_header = "Basic {0}" .format (auth_token )
156+ headers .append (['Authorization' , auth_header ])
157+ default_handler (url , method , timeout , headers , data )()
158+
159+ return handle
160+
161+
162+ def push_to_gateway (gateway , job , registry , grouping_key = None , timeout = None , handler = default_handler ):
122163 '''Push metrics to the given pushgateway.
123164
124165 `gateway` the url for your push gateway. Either of the form
@@ -130,13 +171,37 @@ def push_to_gateway(gateway, job, registry, grouping_key=None, timeout=None):
130171 Defaults to None
131172 `timeout` is how long push will attempt to connect before giving up.
132173 Defaults to None
174+ `handler` is an optional function which can be provided to perform
175+ requests to the 'gateway'.
176+ Defaults to None, in which case an http or https request
177+ will be carried out by a default handler.
178+ If not None, the argument must be a function which accepts
179+ the following arguments:
180+ url, method, timeout, headers, and content
181+ May be used to implement additional functionality not
182+ supported by the built-in default handler (such as SSL
183+ client certicates, and HTTP authentication mechanisms).
184+ 'url' is the URL for the request, the 'gateway' argument
185+ described earlier will form the basis of this URL.
186+ 'method' is the HTTP method which should be used when
187+ carrying out the request.
188+ 'timeout' requests not successfully completed after this
189+ many seconds should be aborted. If timeout is None, then
190+ the handler should not set a timeout.
191+ 'headers' is a list of ("header-name","header-value") tuples
192+ which must be passed to the pushgateway in the form of HTTP
193+ request headers.
194+ The function should raise an exception (e.g. IOError) on
195+ failure.
196+ 'content' is the data which should be used to form the HTTP
197+ Message Body.
133198
134199 This overwrites all metrics with the same job and grouping_key.
135200 This uses the PUT HTTP method.'''
136- _use_gateway ('PUT' , gateway , job , registry , grouping_key , timeout )
201+ _use_gateway ('PUT' , gateway , job , registry , grouping_key , timeout , handler )
137202
138203
139- def pushadd_to_gateway (gateway , job , registry , grouping_key = None , timeout = None ):
204+ def pushadd_to_gateway (gateway , job , registry , grouping_key = None , timeout = None , handler = default_handler ):
140205 '''PushAdd metrics to the given pushgateway.
141206
142207 `gateway` the url for your push gateway. Either of the form
@@ -148,13 +213,19 @@ def pushadd_to_gateway(gateway, job, registry, grouping_key=None, timeout=None):
148213 Defaults to None
149214 `timeout` is how long push will attempt to connect before giving up.
150215 Defaults to None
216+ `handler` is an optional function which can be provided to perform
217+ requests to the 'gateway'.
218+ Defaults to None, in which case an http or https request
219+ will be carried out by a default handler.
220+ See the 'prometheus_client.push_to_gateway' documentation
221+ for implementation requirements.
151222
152223 This replaces metrics with the same name, job and grouping_key.
153224 This uses the POST HTTP method.'''
154- _use_gateway ('POST' , gateway , job , registry , grouping_key , timeout )
225+ _use_gateway ('POST' , gateway , job , registry , grouping_key , timeout , handler )
155226
156227
157- def delete_from_gateway (gateway , job , grouping_key = None , timeout = None ):
228+ def delete_from_gateway (gateway , job , grouping_key = None , timeout = None , handler = default_handler ):
158229 '''Delete metrics from the given pushgateway.
159230
160231 `gateway` the url for your push gateway. Either of the form
@@ -165,14 +236,21 @@ def delete_from_gateway(gateway, job, grouping_key=None, timeout=None):
165236 Defaults to None
166237 `timeout` is how long delete will attempt to connect before giving up.
167238 Defaults to None
239+ `handler` is an optional function which can be provided to perform
240+ requests to the 'gateway'.
241+ Defaults to None, in which case an http or https request
242+ will be carried out by a default handler.
243+ See the 'prometheus_client.push_to_gateway' documentation
244+ for implementation requirements.
168245
169246 This deletes metrics with the given job and grouping_key.
170247 This uses the DELETE HTTP method.'''
171- _use_gateway ('DELETE' , gateway , job , None , grouping_key , timeout )
248+ _use_gateway ('DELETE' , gateway , job , None , grouping_key , timeout , handler )
172249
173250
174- def _use_gateway (method , gateway , job , registry , grouping_key , timeout ):
175- if not (gateway .startswith ('http://' ) or gateway .startswith ('https://' )):
251+ def _use_gateway (method , gateway , job , registry , grouping_key , timeout , handler ):
252+ gateway_url = urlparse (gateway )
253+ if not gateway_url .scheme :
176254 gateway = 'http://{0}' .format (gateway )
177255 url = '{0}/metrics/job/{1}' .format (gateway , quote_plus (job ))
178256
@@ -185,13 +263,9 @@ def _use_gateway(method, gateway, job, registry, grouping_key, timeout):
185263 url = url + '' .join (['/{0}/{1}' .format (quote_plus (str (k )), quote_plus (str (v )))
186264 for k , v in sorted (grouping_key .items ())])
187265
188- request = Request (url , data = data )
189- request .add_header ('Content-Type' , CONTENT_TYPE_LATEST )
190- request .get_method = lambda : method
191- resp = build_opener (HTTPHandler ).open (request , timeout = timeout )
192- if resp .code >= 400 :
193- raise IOError ("error talking to pushgateway: {0} {1}" .format (
194- resp .code , resp .msg ))
266+ headers = [('Content-Type' , CONTENT_TYPE_LATEST )]
267+ handler (url = url , method = method , timeout = timeout ,
268+ headers = headers , data = data )()
195269
196270def instance_ip_grouping_key ():
197271 '''Grouping key with instance set to the IP Address of this host.'''
0 commit comments