webhooks_worker.py 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import logging
  2. import requests
  3. from django_rq import job
  4. from jinja2.exceptions import TemplateError
  5. from .choices import ObjectChangeActionChoices
  6. from .webhooks import generate_signature
  7. logger = logging.getLogger('netbox.webhooks_worker')
  8. @job('default')
  9. def process_webhook(webhook, data, model_name, event, timestamp, username, request_id):
  10. """
  11. Make a POST request to the defined Webhook
  12. """
  13. context = {
  14. 'event': dict(ObjectChangeActionChoices)[event].lower(),
  15. 'timestamp': timestamp,
  16. 'model': model_name,
  17. 'username': username,
  18. 'request_id': request_id,
  19. 'data': data
  20. }
  21. # Build the headers for the HTTP request
  22. headers = {
  23. 'Content-Type': webhook.http_content_type,
  24. }
  25. try:
  26. headers.update(webhook.render_headers(context))
  27. except (TemplateError, ValueError) as e:
  28. logger.error("Error parsing HTTP headers for webhook {}: {}".format(webhook, e))
  29. raise e
  30. # Render the request body
  31. try:
  32. body = webhook.render_body(context)
  33. except TemplateError as e:
  34. logger.error("Error rendering request body for webhook {}: {}".format(webhook, e))
  35. raise e
  36. # Prepare the HTTP request
  37. params = {
  38. 'method': webhook.http_method,
  39. 'url': webhook.payload_url,
  40. 'headers': headers,
  41. 'data': body,
  42. }
  43. logger.info(
  44. "Sending {} request to {} ({} {})".format(
  45. params['method'], params['url'], context['model'], context['event']
  46. )
  47. )
  48. logger.debug(params)
  49. try:
  50. prepared_request = requests.Request(**params).prepare()
  51. except requests.exceptions.RequestException as e:
  52. logger.error("Error forming HTTP request: {}".format(e))
  53. raise e
  54. # If a secret key is defined, sign the request with a hash of the key and its content
  55. if webhook.secret != '':
  56. prepared_request.headers['X-Hook-Signature'] = generate_signature(prepared_request.body, webhook.secret)
  57. # Send the request
  58. with requests.Session() as session:
  59. session.verify = webhook.ssl_verification
  60. if webhook.ca_file_path:
  61. session.verify = webhook.ca_file_path
  62. response = session.send(prepared_request)
  63. if 200 <= response.status_code <= 299:
  64. logger.info("Request succeeded; response status {}".format(response.status_code))
  65. return 'Status {} returned, webhook successfully processed.'.format(response.status_code)
  66. else:
  67. logger.warning("Request failed; response status {}: {}".format(response.status_code, response.content))
  68. raise requests.exceptions.RequestException(
  69. "Status {} returned with content '{}', webhook FAILED to process.".format(
  70. response.status_code, response.content
  71. )
  72. )