webhooks_worker.py 2.9 KB

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