webhooks_worker.py 3.0 KB

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