nautobot 2.4.4__py3-none-any.whl → 2.4.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. nautobot/__init__.py +19 -3
  2. nautobot/core/api/mixins.py +10 -0
  3. nautobot/core/celery/__init__.py +5 -3
  4. nautobot/core/celery/encoders.py +2 -2
  5. nautobot/core/forms/fields.py +21 -5
  6. nautobot/core/forms/utils.py +1 -0
  7. nautobot/core/jobs/__init__.py +3 -2
  8. nautobot/core/jobs/bulk_actions.py +1 -1
  9. nautobot/core/management/commands/generate_test_data.py +1 -1
  10. nautobot/core/models/name_color_content_types.py +9 -0
  11. nautobot/core/models/validators.py +7 -0
  12. nautobot/core/settings.py +0 -14
  13. nautobot/core/settings.yaml +0 -28
  14. nautobot/core/tables.py +6 -1
  15. nautobot/core/templates/generic/object_retrieve.html +1 -1
  16. nautobot/core/testing/__init__.py +2 -0
  17. nautobot/core/testing/api.py +18 -0
  18. nautobot/core/testing/mixins.py +9 -0
  19. nautobot/core/tests/nautobot_config.py +0 -2
  20. nautobot/core/tests/runner.py +17 -140
  21. nautobot/core/tests/test_api.py +4 -4
  22. nautobot/core/tests/test_authentication.py +83 -4
  23. nautobot/core/tests/test_forms.py +11 -8
  24. nautobot/core/tests/test_graphql.py +9 -0
  25. nautobot/core/tests/test_jobs.py +33 -27
  26. nautobot/core/ui/object_detail.py +31 -0
  27. nautobot/dcim/factory.py +2 -0
  28. nautobot/dcim/filters/__init__.py +5 -0
  29. nautobot/dcim/forms.py +17 -1
  30. nautobot/dcim/migrations/0068_alter_softwareimagefile_download_url.py +19 -0
  31. nautobot/dcim/migrations/0069_softwareimagefile_external_integration.py +25 -0
  32. nautobot/dcim/models/devices.py +9 -2
  33. nautobot/dcim/tables/devices.py +1 -0
  34. nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +4 -0
  35. nautobot/dcim/tests/test_api.py +74 -31
  36. nautobot/dcim/tests/test_filters.py +2 -0
  37. nautobot/dcim/tests/test_jobs.py +4 -6
  38. nautobot/dcim/tests/test_models.py +65 -0
  39. nautobot/dcim/tests/test_views.py +3 -0
  40. nautobot/extras/choices.py +8 -3
  41. nautobot/extras/forms/forms.py +7 -3
  42. nautobot/extras/jobs.py +181 -103
  43. nautobot/extras/management/utils.py +13 -2
  44. nautobot/extras/models/datasources.py +4 -1
  45. nautobot/extras/models/jobs.py +20 -17
  46. nautobot/extras/plugins/marketplace_manifest.yml +18 -0
  47. nautobot/extras/tables.py +29 -34
  48. nautobot/extras/templates/extras/inc/panel_changelog.html +1 -1
  49. nautobot/extras/templates/extras/inc/panel_jobhistory.html +1 -1
  50. nautobot/extras/templates/extras/status.html +1 -37
  51. nautobot/extras/test_jobs/atomic_transaction.py +6 -6
  52. nautobot/extras/test_jobs/fail.py +75 -1
  53. nautobot/extras/tests/integration/test_notes.py +1 -1
  54. nautobot/extras/tests/test_api.py +23 -8
  55. nautobot/extras/tests/test_changelog.py +4 -4
  56. nautobot/extras/tests/test_customfields.py +3 -0
  57. nautobot/extras/tests/test_datasources.py +64 -54
  58. nautobot/extras/tests/test_jobs.py +69 -62
  59. nautobot/extras/tests/test_models.py +1 -1
  60. nautobot/extras/tests/test_plugins.py +19 -13
  61. nautobot/extras/tests/test_relationships.py +14 -5
  62. nautobot/extras/tests/test_tags.py +2 -2
  63. nautobot/extras/tests/test_views.py +15 -6
  64. nautobot/extras/urls.py +1 -30
  65. nautobot/extras/views.py +17 -55
  66. nautobot/ipam/forms.py +15 -0
  67. nautobot/ipam/querysets.py +6 -0
  68. nautobot/ipam/tables.py +6 -2
  69. nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -41
  70. nautobot/ipam/templates/ipam/rir.html +1 -43
  71. nautobot/ipam/templates/ipam/service.html +2 -46
  72. nautobot/ipam/templates/ipam/service_edit.html +1 -17
  73. nautobot/ipam/templates/ipam/service_retrieve.html +7 -0
  74. nautobot/ipam/tests/migration/__init__.py +0 -0
  75. nautobot/ipam/tests/migration/test_migrations.py +510 -0
  76. nautobot/ipam/tests/test_api.py +66 -36
  77. nautobot/ipam/tests/test_filters.py +0 -10
  78. nautobot/ipam/tests/test_models.py +16 -0
  79. nautobot/ipam/tests/test_views.py +44 -2
  80. nautobot/ipam/urls.py +2 -67
  81. nautobot/ipam/utils/migrations.py +185 -152
  82. nautobot/ipam/utils/testing.py +177 -0
  83. nautobot/ipam/views.py +119 -198
  84. nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +43 -5
  85. nautobot/project-static/docs/code-reference/nautobot/apps/models.html +47 -0
  86. nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +18 -0
  87. nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +35 -0
  88. nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +63 -0
  89. nautobot/project-static/docs/development/apps/api/testing.html +0 -87
  90. nautobot/project-static/docs/development/apps/migration/dependency-updates.html +1 -1
  91. nautobot/project-static/docs/development/core/best-practices.html +3 -3
  92. nautobot/project-static/docs/development/core/getting-started.html +78 -107
  93. nautobot/project-static/docs/development/core/release-checklist.html +1 -1
  94. nautobot/project-static/docs/development/core/style-guide.html +1 -1
  95. nautobot/project-static/docs/development/core/testing.html +24 -198
  96. nautobot/project-static/docs/development/jobs/index.html +27 -14
  97. nautobot/project-static/docs/media/user-guide/administration/getting-started/nautobot-cloud.png +0 -0
  98. nautobot/project-static/docs/objects.inv +0 -0
  99. nautobot/project-static/docs/overview/application_stack.html +1 -1
  100. nautobot/project-static/docs/release-notes/version-2.4.html +409 -1
  101. nautobot/project-static/docs/requirements.txt +1 -1
  102. nautobot/project-static/docs/search/search_index.json +1 -1
  103. nautobot/project-static/docs/sitemap.xml +290 -290
  104. nautobot/project-static/docs/sitemap.xml.gz +0 -0
  105. nautobot/project-static/docs/user-guide/administration/configuration/settings.html +2 -48
  106. nautobot/project-static/docs/user-guide/administration/guides/permissions.html +71 -0
  107. nautobot/project-static/docs/user-guide/administration/installation/http-server.html +3 -1
  108. nautobot/project-static/docs/user-guide/administration/installation/index.html +257 -16
  109. nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +1 -1
  110. nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +2 -2
  111. nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +4 -0
  112. nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +11 -11
  113. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +8 -8
  114. nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +1 -0
  115. nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +40 -25
  116. nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +4 -4
  117. nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +1 -1
  118. nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +77 -5
  119. nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +1 -1
  120. nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +0 -1
  121. nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +1 -1
  122. nautobot/project-static/docs/user-guide/index.html +89 -2
  123. nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +207 -122
  124. nautobot/virtualization/forms.py +20 -0
  125. nautobot/virtualization/templates/virtualization/clustergroup.html +1 -39
  126. nautobot/virtualization/templates/virtualization/clustertype.html +1 -0
  127. nautobot/virtualization/tests/test_api.py +14 -3
  128. nautobot/virtualization/tests/test_views.py +10 -2
  129. nautobot/virtualization/urls.py +10 -93
  130. nautobot/virtualization/views.py +33 -72
  131. {nautobot-2.4.4.dist-info → nautobot-2.4.6.dist-info}/METADATA +8 -7
  132. {nautobot-2.4.4.dist-info → nautobot-2.4.6.dist-info}/RECORD +137 -132
  133. {nautobot-2.4.4.dist-info → nautobot-2.4.6.dist-info}/WHEEL +1 -1
  134. nautobot/core/tests/performance_baselines.yml +0 -8900
  135. nautobot/ipam/tests/test_migrations.py +0 -462
  136. /nautobot/ipam/templates/ipam/{namespace_ipaddresses.html → namespace_ip_addresses.html} +0 -0
  137. {nautobot-2.4.4.dist-info → nautobot-2.4.6.dist-info}/LICENSE.txt +0 -0
  138. {nautobot-2.4.4.dist-info → nautobot-2.4.6.dist-info}/NOTICE +0 -0
  139. {nautobot-2.4.4.dist-info → nautobot-2.4.6.dist-info}/entry_points.txt +0 -0
@@ -5851,9 +5851,9 @@
5851
5851
  <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
5852
5852
 
5853
5853
  <li class="md-nav__item">
5854
- <a href="#configuration" class="md-nav__link">
5854
+ <a href="#configuring-a-webhook" class="md-nav__link">
5855
5855
  <span class="md-ellipsis">
5856
- Configuration
5856
+ Configuring a Webhook
5857
5857
  </span>
5858
5858
  </a>
5859
5859
 
@@ -5870,26 +5870,26 @@
5870
5870
  <ul class="md-nav__list">
5871
5871
 
5872
5872
  <li class="md-nav__item">
5873
- <a href="#available-context" class="md-nav__link">
5873
+ <a href="#available-context-variables" class="md-nav__link">
5874
5874
  <span class="md-ellipsis">
5875
- Available Context
5875
+ Available Context Variables
5876
5876
  </span>
5877
5877
  </a>
5878
5878
 
5879
5879
  </li>
5880
5880
 
5881
- <li class="md-nav__item">
5881
+ </ul>
5882
+ </nav>
5883
+
5884
+ </li>
5885
+
5886
+ <li class="md-nav__item">
5882
5887
  <a href="#default-request-body" class="md-nav__link">
5883
5888
  <span class="md-ellipsis">
5884
5889
  Default Request Body
5885
5890
  </span>
5886
5891
  </a>
5887
5892
 
5888
- </li>
5889
-
5890
- </ul>
5891
- </nav>
5892
-
5893
5893
  </li>
5894
5894
 
5895
5895
  <li class="md-nav__item">
@@ -5902,9 +5902,9 @@
5902
5902
  </li>
5903
5903
 
5904
5904
  <li class="md-nav__item">
5905
- <a href="#troubleshooting" class="md-nav__link">
5905
+ <a href="#troubleshooting-webhooks" class="md-nav__link">
5906
5906
  <span class="md-ellipsis">
5907
- Troubleshooting
5907
+ Troubleshooting Webhooks
5908
5908
  </span>
5909
5909
  </a>
5910
5910
 
@@ -9200,9 +9200,9 @@
9200
9200
  <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
9201
9201
 
9202
9202
  <li class="md-nav__item">
9203
- <a href="#configuration" class="md-nav__link">
9203
+ <a href="#configuring-a-webhook" class="md-nav__link">
9204
9204
  <span class="md-ellipsis">
9205
- Configuration
9205
+ Configuring a Webhook
9206
9206
  </span>
9207
9207
  </a>
9208
9208
 
@@ -9219,26 +9219,26 @@
9219
9219
  <ul class="md-nav__list">
9220
9220
 
9221
9221
  <li class="md-nav__item">
9222
- <a href="#available-context" class="md-nav__link">
9222
+ <a href="#available-context-variables" class="md-nav__link">
9223
9223
  <span class="md-ellipsis">
9224
- Available Context
9224
+ Available Context Variables
9225
9225
  </span>
9226
9226
  </a>
9227
9227
 
9228
9228
  </li>
9229
9229
 
9230
- <li class="md-nav__item">
9230
+ </ul>
9231
+ </nav>
9232
+
9233
+ </li>
9234
+
9235
+ <li class="md-nav__item">
9231
9236
  <a href="#default-request-body" class="md-nav__link">
9232
9237
  <span class="md-ellipsis">
9233
9238
  Default Request Body
9234
9239
  </span>
9235
9240
  </a>
9236
9241
 
9237
- </li>
9238
-
9239
- </ul>
9240
- </nav>
9241
-
9242
9242
  </li>
9243
9243
 
9244
9244
  <li class="md-nav__item">
@@ -9251,9 +9251,9 @@
9251
9251
  </li>
9252
9252
 
9253
9253
  <li class="md-nav__item">
9254
- <a href="#troubleshooting" class="md-nav__link">
9254
+ <a href="#troubleshooting-webhooks" class="md-nav__link">
9255
9255
  <span class="md-ellipsis">
9256
- Troubleshooting
9256
+ Troubleshooting Webhooks
9257
9257
  </span>
9258
9258
  </a>
9259
9259
 
@@ -9278,114 +9278,199 @@
9278
9278
 
9279
9279
 
9280
9280
  <h1 id="webhooks">Webhooks<a class="headerlink" href="#webhooks" title="Permanent link">&para;</a></h1>
9281
- <p>A webhook is a mechanism for conveying to some external system a change that took place in Nautobot. For example, you may want to notify a monitoring system whenever the status of a device is updated in Nautobot. This can be done by creating a webhook for the device model in Nautobot and identifying the webhook receiver. When Nautobot detects a change to a device, an HTTP request containing the details of the change and who made it be sent to the specified receiver. Webhooks are configured in the web UI under Extensibility &gt; Webhooks.</p>
9282
- <h2 id="configuration">Configuration<a class="headerlink" href="#configuration" title="Permanent link">&para;</a></h2>
9283
- <ul>
9284
- <li><strong>Name</strong> - A unique name for the webhook. The name is not included with outbound messages.</li>
9285
- <li><strong>Object type(s)</strong> - The type or types of Nautobot object that will trigger the webhook.</li>
9286
- <li><strong>Enabled</strong> - If unchecked, the webhook will be inactive.</li>
9287
- <li><strong>Events</strong> - A webhook may trigger on any combination of create, update, and delete events. At least one event type must be selected.</li>
9288
- <li><strong>HTTP method</strong> - The type of HTTP request to send. Options include <code>GET</code>, <code>POST</code>, <code>PUT</code>, <code>PATCH</code>, and <code>DELETE</code>.</li>
9289
- <li><strong>URL</strong> - The fuly-qualified URL of the request to be sent. This may specify a destination port number if needed.</li>
9290
- <li><strong>HTTP content type</strong> - The value of the request's <code>Content-Type</code> header. (Defaults to <code>application/json</code>)</li>
9291
- <li><strong>Additional headers</strong> - Any additional headers to include with the request (optional). Add one header per line in the format <code>Name: Value</code>. Jinja2 templating is supported for this field (see below).</li>
9292
- <li><strong>Body template</strong> - The content of the request being sent (optional). Jinja2 templating is supported for this field (see below). If blank, Nautobot will populate the request body with a raw dump of the webhook context. (If the HTTP content-type is set to <code>application/json</code>, this will be formatted as a JSON object.)</li>
9293
- <li><strong>Secret</strong> - A secret string used to prove authenticity of the request (optional). This will append a <code>X-Hook-Signature</code> header to the request, consisting of a HMAC (SHA-512) hex digest of the request body using the secret as the key.</li>
9294
- <li><strong>SSL verification</strong> - Uncheck this option to disable validation of the receiver's SSL certificate. (Disable with caution!)</li>
9295
- <li><strong>CA file path</strong> - The file path to a particular certificate authority (CA) file to use when validating the receiver's SSL certificate (optional).</li>
9296
- </ul>
9281
+ <p>A webhook is a mechanism for notifying an external system when a change occurs in Nautobot. For example, you might want to alert a monitoring tool whenever a device's status is updated. This can be achieved by creating a webhook for the device model and specifying a receiver URL. When Nautobot detects a change, it sends an HTTP request with details about the event and the user who triggered it.</p>
9282
+ <p>Webhooks are managed via the web UI under <strong>Extensibility &gt; Webhooks</strong>.</p>
9283
+ <h2 id="configuring-a-webhook">Configuring a Webhook<a class="headerlink" href="#configuring-a-webhook" title="Permanent link">&para;</a></h2>
9284
+ <p>When setting up a webhook, you need to define the following parameters:</p>
9285
+ <table>
9286
+ <thead>
9287
+ <tr>
9288
+ <th>Parameter</th>
9289
+ <th>Description</th>
9290
+ </tr>
9291
+ </thead>
9292
+ <tbody>
9293
+ <tr>
9294
+ <td><strong>Name</strong></td>
9295
+ <td>A unique name for the webhook (not included in outgoing messages).</td>
9296
+ </tr>
9297
+ <tr>
9298
+ <td><strong>Object type(s)</strong></td>
9299
+ <td>The type(s) of Nautobot objects that trigger the webhook.</td>
9300
+ </tr>
9301
+ <tr>
9302
+ <td><strong>Enabled</strong></td>
9303
+ <td>Indicates whether the webhook is active.</td>
9304
+ </tr>
9305
+ <tr>
9306
+ <td><strong>Events</strong></td>
9307
+ <td>Select one or more events: <code>create</code>, <code>update</code>, or <code>delete</code>.</td>
9308
+ </tr>
9309
+ <tr>
9310
+ <td><strong>HTTP method</strong></td>
9311
+ <td>The type of HTTP request (<code>GET</code>, <code>POST</code>, <code>PUT</code>, <code>PATCH</code>, <code>DELETE</code>).</td>
9312
+ </tr>
9313
+ <tr>
9314
+ <td><strong>URL</strong></td>
9315
+ <td>The fully qualified URL of the receiver. You can specify a port if needed.</td>
9316
+ </tr>
9317
+ <tr>
9318
+ <td><strong>HTTP content type</strong></td>
9319
+ <td>Sets the <code>Content-Type</code> header (default: <code>application/json</code>).</td>
9320
+ </tr>
9321
+ <tr>
9322
+ <td><strong>Additional headers</strong></td>
9323
+ <td>Custom headers, one per line (<code>Name: Value</code>). Supports Jinja2 templating.</td>
9324
+ </tr>
9325
+ <tr>
9326
+ <td><strong>Body template</strong></td>
9327
+ <td>Custom request body. Supports Jinja2 templating. If empty, Nautobot sends the default payload.</td>
9328
+ </tr>
9329
+ <tr>
9330
+ <td><strong>Secret</strong></td>
9331
+ <td>A secret string used for HMAC (SHA-512) authentication. The webhook request includes an <code>X-Hook-Signature</code> header.</td>
9332
+ </tr>
9333
+ <tr>
9334
+ <td><strong>SSL verification</strong></td>
9335
+ <td>If unchecked, Nautobot skips SSL certificate validation (use with caution).</td>
9336
+ </tr>
9337
+ <tr>
9338
+ <td><strong>CA file path</strong></td>
9339
+ <td>Specifies a custom CA file for SSL validation.</td>
9340
+ </tr>
9341
+ </tbody>
9342
+ </table>
9297
9343
  <h2 id="jinja2-template-support">Jinja2 Template Support<a class="headerlink" href="#jinja2-template-support" title="Permanent link">&para;</a></h2>
9298
- <p><a href="https://jinja.palletsprojects.com/">Jinja2 templating</a> is supported for the <code>additional_headers</code> and <code>body_template</code> fields. This enables the user to convey object data in the request headers as well as to craft a customized request body. Request content can be crafted to enable the direct interaction with external systems by ensuring the outgoing message is in a format the receiver expects and understands.</p>
9299
- <p>For example, you might create a Nautobot webhook to <a href="https://api.slack.com/messaging/webhooks">trigger a Slack message</a> any time an IP address is created. You can accomplish this using the following configuration:</p>
9300
- <ul>
9301
- <li>Object type: IPAM &gt; IP address</li>
9302
- <li>HTTP method: <code>POST</code></li>
9303
- <li>URL: Slack incoming webhook URL</li>
9304
- <li>HTTP content type: <code>application/json</code></li>
9305
- <li>Body template: <code>{"text": "IP address {{ data['address'] }} was created by {{ username }}!"}</code></li>
9306
- </ul>
9307
- <h3 id="available-context">Available Context<a class="headerlink" href="#available-context" title="Permanent link">&para;</a></h3>
9308
- <p>The following data is available as context for Jinja2 templates:</p>
9344
+ <p><a href="https://jinja.palletsprojects.com/">Jinja2 templating</a> is supported for the <code>additional_headers</code> and <code>body_template</code> fields. It allows you to customize headers and request bodies dynamically. This is useful for formatting webhook payloads to match external system expectations.</p>
9345
+ <p>Example: Trigger a Slack message when a new IP address is created.</p>
9346
+ <p><strong>Webhook Configuration:</strong></p>
9309
9347
  <ul>
9310
- <li><code>event</code> - The type of event which triggered the webhook: created, updated, or deleted.</li>
9311
- <li><code>model</code> - The Nautobot model which triggered the change.</li>
9312
- <li><code>timestamp</code> - The time at which the event occurred (in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> format).</li>
9313
- <li><code>username</code> - The name of the user account associated with the change.</li>
9314
- <li><code>request_id</code> - The unique request ID. This may be used to correlate multiple changes associated with a single request.</li>
9315
- <li><code>data</code> - A serialized representation of the object <em>after</em> the change was made. This is typically equivalent to the model's representation in Nautobot's REST API.</li>
9316
- <li><code>snapshots</code> - snapshots of the serialized object state both before and after the change was made; provided as a dictionary with keys named <code>prechange</code>, <code>postchange</code> and <code>differences</code>.</li>
9348
+ <li><strong>Object type</strong>: IPAM &gt; IP address</li>
9349
+ <li><strong>HTTP method</strong>: <code>POST</code></li>
9350
+ <li><strong>URL</strong>: Slack incoming webhook URL</li>
9351
+ <li><strong>HTTP content type</strong>: <code>application/json</code></li>
9352
+ <li><strong>Body template</strong>:</li>
9317
9353
  </ul>
9318
- <h3 id="default-request-body">Default Request Body<a class="headerlink" href="#default-request-body" title="Permanent link">&para;</a></h3>
9319
- <p>If no body template is specified, the request body will be populated with a JSON object containing the context data. For example, a newly created Location might appear as follows:</p>
9320
- <div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>{
9321
- <a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a> &quot;event&quot;: &quot;created&quot;,
9322
- <a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a> &quot;timestamp&quot;: &quot;2023-02-14 12:34:56.000000+00:00&quot;,
9323
- <a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a> &quot;model&quot;: &quot;location&quot;,
9324
- <a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a> &quot;username&quot;: &quot;admin&quot;,
9325
- <a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a> &quot;request_id&quot;: &quot;fab0a4fb-52ba-4cb4-9756-4e6a3ac05332&quot;,
9326
- <a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a> &quot;data&quot;: {
9327
- <a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> &quot;id&quot;: &quot;5e4f9a91-372b-46df-a50a-c26357475bee&quot;,
9328
- <a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a> &quot;display&quot;: &quot;Campus A&quot;,
9329
- <a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a> &quot;url&quot;: &quot;/api/dcim/locations/5e4f9a91-372b-46df-a50a-c26357475bee/&quot;,
9330
- <a id="__codelineno-0-11" name="__codelineno-0-11" href="#__codelineno-0-11"></a> &quot;name&quot;: &quot;Campus A&quot;,
9331
- <a id="__codelineno-0-12" name="__codelineno-0-12" href="#__codelineno-0-12"></a> &quot;status&quot;: {
9332
- <a id="__codelineno-0-13" name="__codelineno-0-13" href="#__codelineno-0-13"></a> &quot;display&quot;: &quot;Active&quot;,
9333
- <a id="__codelineno-0-14" name="__codelineno-0-14" href="#__codelineno-0-14"></a> &quot;id&quot;: &quot;363a431c-c784-40b5-8513-758cafd174ad&quot;,
9334
- <a id="__codelineno-0-15" name="__codelineno-0-15" href="#__codelineno-0-15"></a> &quot;url&quot;: &quot;/api/extras/statuses/363a431c-c784-40b5-8513-758cafd174ad/&quot;,
9335
- <a id="__codelineno-0-16" name="__codelineno-0-16" href="#__codelineno-0-16"></a> &quot;name&quot;: &quot;Active&quot;,
9336
- <a id="__codelineno-0-17" name="__codelineno-0-17" href="#__codelineno-0-17"></a> &quot;created&quot;: &quot;2023-02-14T00:00:00Z&quot;,
9337
- <a id="__codelineno-0-18" name="__codelineno-0-18" href="#__codelineno-0-18"></a> &quot;last_updated&quot;: &quot;2023-02-14T19:40:13.216150Z&quot;
9338
- <a id="__codelineno-0-19" name="__codelineno-0-19" href="#__codelineno-0-19"></a> },
9339
- <a id="__codelineno-0-20" name="__codelineno-0-20" href="#__codelineno-0-20"></a> ...
9340
- <a id="__codelineno-0-21" name="__codelineno-0-21" href="#__codelineno-0-21"></a> },
9341
- <a id="__codelineno-0-22" name="__codelineno-0-22" href="#__codelineno-0-22"></a> &quot;snapshots&quot;: {
9342
- <a id="__codelineno-0-23" name="__codelineno-0-23" href="#__codelineno-0-23"></a> &quot;prechange&quot;: null,
9343
- <a id="__codelineno-0-24" name="__codelineno-0-24" href="#__codelineno-0-24"></a> &quot;postchange&quot;: {
9344
- <a id="__codelineno-0-25" name="__codelineno-0-25" href="#__codelineno-0-25"></a> &quot;id&quot;: &quot;5e4f9a91-372b-46df-a50a-c26357475bee&quot;,
9345
- <a id="__codelineno-0-26" name="__codelineno-0-26" href="#__codelineno-0-26"></a> &quot;asn&quot;: null,
9346
- <a id="__codelineno-0-27" name="__codelineno-0-27" href="#__codelineno-0-27"></a> &quot;url&quot;: &quot;/api/dcim/locations/5e4f9a91-372b-46df-a50a-c26357475bee/&quot;,
9347
- <a id="__codelineno-0-28" name="__codelineno-0-28" href="#__codelineno-0-28"></a> &quot;name&quot;: &quot;Campus A&quot;,
9348
- <a id="__codelineno-0-29" name="__codelineno-0-29" href="#__codelineno-0-29"></a> ...
9349
- <a id="__codelineno-0-30" name="__codelineno-0-30" href="#__codelineno-0-30"></a> },
9350
- <a id="__codelineno-0-31" name="__codelineno-0-31" href="#__codelineno-0-31"></a> &quot;differences&quot;: {
9351
- <a id="__codelineno-0-32" name="__codelineno-0-32" href="#__codelineno-0-32"></a> &quot;removed&quot;: null,
9352
- <a id="__codelineno-0-33" name="__codelineno-0-33" href="#__codelineno-0-33"></a> &quot;added&quot;: {
9353
- <a id="__codelineno-0-34" name="__codelineno-0-34" href="#__codelineno-0-34"></a> &quot;id&quot;: &quot;5e4f9a91-372b-46df-a50a-c26357475bee&quot;,
9354
- <a id="__codelineno-0-35" name="__codelineno-0-35" href="#__codelineno-0-35"></a> &quot;asn&quot;: null,
9355
- <a id="__codelineno-0-36" name="__codelineno-0-36" href="#__codelineno-0-36"></a> &quot;url&quot;: &quot;/api/dcim/locations/5e4f9a91-372b-46df-a50a-c26357475bee/&quot;,
9356
- <a id="__codelineno-0-37" name="__codelineno-0-37" href="#__codelineno-0-37"></a> &quot;name&quot;: &quot;Campus A&quot;,
9357
- <a id="__codelineno-0-38" name="__codelineno-0-38" href="#__codelineno-0-38"></a> ...
9358
- <a id="__codelineno-0-39" name="__codelineno-0-39" href="#__codelineno-0-39"></a> }
9359
- <a id="__codelineno-0-40" name="__codelineno-0-40" href="#__codelineno-0-40"></a> }
9360
- <a id="__codelineno-0-41" name="__codelineno-0-41" href="#__codelineno-0-41"></a> }
9361
- <a id="__codelineno-0-42" name="__codelineno-0-42" href="#__codelineno-0-42"></a>}
9354
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="p">{</span><span class="nt">&quot;text&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;IP address {{ data[&#39;address&#39;] }} was created by {{ username }}!&quot;</span><span class="p">}</span>
9355
+ </code></pre></div>
9356
+ <h3 id="available-context-variables">Available Context Variables<a class="headerlink" href="#available-context-variables" title="Permanent link">&para;</a></h3>
9357
+ <table>
9358
+ <thead>
9359
+ <tr>
9360
+ <th>Variable</th>
9361
+ <th>Description</th>
9362
+ </tr>
9363
+ </thead>
9364
+ <tbody>
9365
+ <tr>
9366
+ <td><code>event</code></td>
9367
+ <td>The event type (<code>created</code>, <code>updated</code>, <code>deleted</code>).</td>
9368
+ </tr>
9369
+ <tr>
9370
+ <td><code>model</code></td>
9371
+ <td>The Nautobot model triggering the event.</td>
9372
+ </tr>
9373
+ <tr>
9374
+ <td><code>timestamp</code></td>
9375
+ <td>The event timestamp in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> format.</td>
9376
+ </tr>
9377
+ <tr>
9378
+ <td><code>username</code></td>
9379
+ <td>The user who triggered the event.</td>
9380
+ </tr>
9381
+ <tr>
9382
+ <td><code>request_id</code></td>
9383
+ <td>A unique request ID for correlation of multiple changes associated with a single request.</td>
9384
+ </tr>
9385
+ <tr>
9386
+ <td><code>data</code></td>
9387
+ <td>A serialized representation of the object <em>after</em> the change.</td>
9388
+ </tr>
9389
+ <tr>
9390
+ <td><code>snapshots</code></td>
9391
+ <td>Contains <code>prechange</code>, <code>postchange</code>, and <code>differences</code> snapshots.</td>
9392
+ </tr>
9393
+ </tbody>
9394
+ </table>
9395
+ <h2 id="default-request-body">Default Request Body<a class="headerlink" href="#default-request-body" title="Permanent link">&para;</a></h2>
9396
+ <p>If no body template is provided, Nautobot sends a default JSON payload:</p>
9397
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="p">{</span>
9398
+ <a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a><span class="w"> </span><span class="nt">&quot;event&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;created&quot;</span><span class="p">,</span>
9399
+ <a id="__codelineno-1-3" name="__codelineno-1-3" href="#__codelineno-1-3"></a><span class="w"> </span><span class="nt">&quot;timestamp&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;2023-02-14T12:34:56.000000+00:00&quot;</span><span class="p">,</span>
9400
+ <a id="__codelineno-1-4" name="__codelineno-1-4" href="#__codelineno-1-4"></a><span class="w"> </span><span class="nt">&quot;model&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;location&quot;</span><span class="p">,</span>
9401
+ <a id="__codelineno-1-5" name="__codelineno-1-5" href="#__codelineno-1-5"></a><span class="w"> </span><span class="nt">&quot;username&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;admin&quot;</span><span class="p">,</span>
9402
+ <a id="__codelineno-1-6" name="__codelineno-1-6" href="#__codelineno-1-6"></a><span class="w"> </span><span class="nt">&quot;request_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;fab0a4fb-52ba-4cb4-9756-4e6a3ac05332&quot;</span><span class="p">,</span>
9403
+ <a id="__codelineno-1-7" name="__codelineno-1-7" href="#__codelineno-1-7"></a><span class="w"> </span><span class="nt">&quot;data&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
9404
+ <a id="__codelineno-1-8" name="__codelineno-1-8" href="#__codelineno-1-8"></a><span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;5e4f9a91-372b-46df-a50a-c26357475bee&quot;</span><span class="p">,</span>
9405
+ <a id="__codelineno-1-9" name="__codelineno-1-9" href="#__codelineno-1-9"></a><span class="w"> </span><span class="nt">&quot;display&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Campus A&quot;</span><span class="p">,</span>
9406
+ <a id="__codelineno-1-10" name="__codelineno-1-10" href="#__codelineno-1-10"></a><span class="w"> </span><span class="nt">&quot;url&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;/api/dcim/locations/5e4f9a91-372b-46df-a50a-c26357475bee/&quot;</span><span class="p">,</span>
9407
+ <a id="__codelineno-1-11" name="__codelineno-1-11" href="#__codelineno-1-11"></a><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Campus A&quot;</span><span class="p">,</span>
9408
+ <a id="__codelineno-1-12" name="__codelineno-1-12" href="#__codelineno-1-12"></a><span class="w"> </span><span class="nt">&quot;status&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
9409
+ <a id="__codelineno-1-13" name="__codelineno-1-13" href="#__codelineno-1-13"></a><span class="w"> </span><span class="nt">&quot;display&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Active&quot;</span><span class="p">,</span>
9410
+ <a id="__codelineno-1-14" name="__codelineno-1-14" href="#__codelineno-1-14"></a><span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;363a431c-c784-40b5-8513-758cafd174ad&quot;</span><span class="p">,</span>
9411
+ <a id="__codelineno-1-15" name="__codelineno-1-15" href="#__codelineno-1-15"></a><span class="w"> </span><span class="nt">&quot;url&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;/api/extras/statuses/363a431c-c784-40b5-8513-758cafd174ad/&quot;</span><span class="p">,</span>
9412
+ <a id="__codelineno-1-16" name="__codelineno-1-16" href="#__codelineno-1-16"></a><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Active&quot;</span><span class="p">,</span>
9413
+ <a id="__codelineno-1-17" name="__codelineno-1-17" href="#__codelineno-1-17"></a><span class="w"> </span><span class="nt">&quot;created&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;2023-02-14T00:00:00Z&quot;</span><span class="p">,</span>
9414
+ <a id="__codelineno-1-18" name="__codelineno-1-18" href="#__codelineno-1-18"></a><span class="w"> </span><span class="nt">&quot;last_updated&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;2023-02-14T19:40:13.216150Z&quot;</span>
9415
+ <a id="__codelineno-1-19" name="__codelineno-1-19" href="#__codelineno-1-19"></a><span class="w"> </span><span class="p">},</span>
9416
+ <a id="__codelineno-1-20" name="__codelineno-1-20" href="#__codelineno-1-20"></a><span class="w"> </span><span class="err">...</span>
9417
+ <a id="__codelineno-1-21" name="__codelineno-1-21" href="#__codelineno-1-21"></a><span class="w"> </span><span class="p">},</span>
9418
+ <a id="__codelineno-1-22" name="__codelineno-1-22" href="#__codelineno-1-22"></a><span class="w"> </span><span class="nt">&quot;snapshots&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
9419
+ <a id="__codelineno-1-23" name="__codelineno-1-23" href="#__codelineno-1-23"></a><span class="w"> </span><span class="nt">&quot;prechange&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span>
9420
+ <a id="__codelineno-1-24" name="__codelineno-1-24" href="#__codelineno-1-24"></a><span class="w"> </span><span class="nt">&quot;postchange&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
9421
+ <a id="__codelineno-1-25" name="__codelineno-1-25" href="#__codelineno-1-25"></a><span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;5e4f9a91-372b-46df-a50a-c26357475bee&quot;</span><span class="p">,</span>
9422
+ <a id="__codelineno-1-26" name="__codelineno-1-26" href="#__codelineno-1-26"></a><span class="w"> </span><span class="nt">&quot;asn&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span>
9423
+ <a id="__codelineno-1-27" name="__codelineno-1-27" href="#__codelineno-1-27"></a><span class="w"> </span><span class="nt">&quot;url&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;/api/dcim/locations/5e4f9a91-372b-46df-a50a-c26357475bee/&quot;</span><span class="p">,</span>
9424
+ <a id="__codelineno-1-28" name="__codelineno-1-28" href="#__codelineno-1-28"></a><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Campus A&quot;</span><span class="p">,</span>
9425
+ <a id="__codelineno-1-29" name="__codelineno-1-29" href="#__codelineno-1-29"></a><span class="w"> </span><span class="err">...</span>
9426
+ <a id="__codelineno-1-30" name="__codelineno-1-30" href="#__codelineno-1-30"></a><span class="w"> </span><span class="p">},</span>
9427
+ <a id="__codelineno-1-31" name="__codelineno-1-31" href="#__codelineno-1-31"></a><span class="w"> </span><span class="nt">&quot;differences&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
9428
+ <a id="__codelineno-1-32" name="__codelineno-1-32" href="#__codelineno-1-32"></a><span class="w"> </span><span class="nt">&quot;removed&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span>
9429
+ <a id="__codelineno-1-33" name="__codelineno-1-33" href="#__codelineno-1-33"></a><span class="w"> </span><span class="nt">&quot;added&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
9430
+ <a id="__codelineno-1-34" name="__codelineno-1-34" href="#__codelineno-1-34"></a><span class="w"> </span><span class="nt">&quot;id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;5e4f9a91-372b-46df-a50a-c26357475bee&quot;</span><span class="p">,</span>
9431
+ <a id="__codelineno-1-35" name="__codelineno-1-35" href="#__codelineno-1-35"></a><span class="w"> </span><span class="nt">&quot;asn&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span>
9432
+ <a id="__codelineno-1-36" name="__codelineno-1-36" href="#__codelineno-1-36"></a><span class="w"> </span><span class="nt">&quot;url&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;/api/dcim/locations/5e4f9a91-372b-46df-a50a-c26357475bee/&quot;</span><span class="p">,</span>
9433
+ <a id="__codelineno-1-37" name="__codelineno-1-37" href="#__codelineno-1-37"></a><span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Campus A&quot;</span><span class="p">,</span>
9434
+ <a id="__codelineno-1-38" name="__codelineno-1-38" href="#__codelineno-1-38"></a><span class="w"> </span><span class="err">...</span>
9435
+ <a id="__codelineno-1-39" name="__codelineno-1-39" href="#__codelineno-1-39"></a><span class="w"> </span><span class="p">}</span>
9436
+ <a id="__codelineno-1-40" name="__codelineno-1-40" href="#__codelineno-1-40"></a><span class="w"> </span><span class="p">}</span>
9437
+ <a id="__codelineno-1-41" name="__codelineno-1-41" href="#__codelineno-1-41"></a><span class="w"> </span><span class="p">}</span>
9438
+ <a id="__codelineno-1-42" name="__codelineno-1-42" href="#__codelineno-1-42"></a><span class="p">}</span>
9362
9439
  </code></pre></div>
9363
9440
  <h2 id="webhook-processing">Webhook Processing<a class="headerlink" href="#webhook-processing" title="Permanent link">&para;</a></h2>
9364
- <p>When a change is detected, any resulting webhooks are placed into a Redis queue for processing. This allows the user's request to complete without needing to wait for the outgoing webhook(s) to be processed. The webhooks are then extracted from the queue by the <code>celery worker</code> process and HTTP requests are sent to their respective destinations.</p>
9365
- <p>A request is considered successful if the response has a 2XX status code; otherwise, the request is marked as having failed. Failed requests may be retried manually via the admin UI.</p>
9366
- <h2 id="troubleshooting">Troubleshooting<a class="headerlink" href="#troubleshooting" title="Permanent link">&para;</a></h2>
9367
- <p>To assist with verifying that the content of outgoing webhooks is rendered correctly, Nautobot provides a simple HTTP listener that can be run locally to receive and display webhook requests. First, modify the target URL of the desired webhook to <code>http://localhost:9000/</code>. This will instruct Nautobot to send the request to the local server on TCP port 9000. Then, start the webhook receiver service from the Nautobot root directory:</p>
9368
- <div class="highlight"><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>nautobot-server webhook_receiver
9441
+ <p>When Nautobot detects a relevant change, it queues the webhook in Redis. This ensures that webhook processing does not delay the original request. A Celery worker then dequeues and sends the HTTP request to the specified receiver.</p>
9442
+ <p>A webhook request is considered successful if the receiver responds with a <code>2XX</code> status code. Failed requests can be retried manually via the admin UI.</p>
9443
+ <h2 id="troubleshooting-webhooks">Troubleshooting Webhooks<a class="headerlink" href="#troubleshooting-webhooks" title="Permanent link">&para;</a></h2>
9444
+ <p>To inspect outgoing webhooks, you can use a local HTTP listener. Nautobot provides a built-in webhook receiver that logs incoming requests:</p>
9445
+ <ol>
9446
+ <li>Set the webhook URL to <code>http://localhost:9000/</code>.</li>
9447
+ <li>
9448
+ <p>Start the webhook receiver:</p>
9449
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a>nautobot-server<span class="w"> </span>webhook_receiver
9369
9450
  </code></pre></div>
9370
9451
  <p>Example output:</p>
9371
- <div class="highlight"><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a>Listening on port http://localhost:9000. Stop with CONTROL-C.
9452
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a>Listening<span class="w"> </span>on<span class="w"> </span>port<span class="w"> </span>http://localhost:9000.<span class="w"> </span>Stop<span class="w"> </span>with<span class="w"> </span>CONTROL-C.
9372
9453
  </code></pre></div>
9373
- <p>You can test the receiver itself by sending any HTTP request to it. For example:</p>
9374
- <div class="highlight"><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a>curl -X POST http://localhost:9000 --data &#39;{&quot;foo&quot;: &quot;bar&quot;}&#39;
9454
+ </li>
9455
+ <li>
9456
+ <p>Send a test request:</p>
9457
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a>curl<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span>http://localhost:9000<span class="w"> </span>--data<span class="w"> </span><span class="s1">&#39;{&quot;foo&quot;: &quot;bar&quot;}&#39;</span>
9375
9458
  </code></pre></div>
9376
- <p>The server will print output similar to the following:</p>
9377
- <div class="highlight"><pre><span></span><code><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a>[1] Tue, 07 Apr 2020 17:44:02 GMT 127.0.0.1 &quot;POST / HTTP/1.1&quot; 200 -
9378
- <a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a>Host: localhost:9000
9379
- <a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a>User-Agent: curl/7.58.0
9380
- <a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a>Accept: */*
9381
- <a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a>Content-Length: 14
9382
- <a id="__codelineno-4-6" name="__codelineno-4-6" href="#__codelineno-4-6"></a>Content-Type: application/x-www-form-urlencoded
9383
- <a id="__codelineno-4-7" name="__codelineno-4-7" href="#__codelineno-4-7"></a>
9384
- <a id="__codelineno-4-8" name="__codelineno-4-8" href="#__codelineno-4-8"></a>{&quot;foo&quot;: &quot;bar&quot;}
9385
- <a id="__codelineno-4-9" name="__codelineno-4-9" href="#__codelineno-4-9"></a>------------
9459
+ <p>The listener will output:</p>
9460
+ <div class="highlight"><pre><span></span><code><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="o">[</span><span class="m">1</span><span class="o">]</span><span class="w"> </span>Tue,<span class="w"> </span><span class="m">07</span><span class="w"> </span>Apr<span class="w"> </span><span class="m">2020</span><span class="w"> </span><span class="m">17</span>:44:02<span class="w"> </span>GMT<span class="w"> </span><span class="m">127</span>.0.0.1<span class="w"> </span><span class="s2">&quot;POST / HTTP/1.1&quot;</span><span class="w"> </span><span class="m">200</span><span class="w"> </span>-
9461
+ <a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a>Host:<span class="w"> </span>localhost:9000
9462
+ <a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a>Content-Type:<span class="w"> </span>application/json
9463
+ <a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a>Content-Length:<span class="w"> </span><span class="m">14</span>
9464
+ <a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a>
9465
+ <a id="__codelineno-5-6" name="__codelineno-5-6" href="#__codelineno-5-6"></a><span class="o">{</span><span class="s2">&quot;foo&quot;</span>:<span class="w"> </span><span class="s2">&quot;bar&quot;</span><span class="o">}</span>
9386
9466
  </code></pre></div>
9387
- <p>Note that <code>webhook_receiver</code> does not actually <em>do</em> anything with the information received: It merely prints the request headers and body for inspection.</p>
9388
- <p>Now, when the Nautobot webhook is triggered and processed, you should see its headers and content appear in the terminal where the webhook receiver is listening. If you don't, check that the <code>celery worker</code> process is running.</p>
9467
+ </li>
9468
+ </ol>
9469
+ <blockquote>
9470
+ <p><strong>Alternative Testing Tools:</strong>
9471
+ Instead of using the built-in webhook receiver, you can test webhooks with external services like <a href="https://beeceptor.com/">Beeceptor</a> or <a href="https://pipedream.com/requestbin">Pipedream RequestBin</a>. These tools let you inspect webhook payloads and troubleshoot integration issues.</p>
9472
+ </blockquote>
9473
+ <p>If a webhook does not trigger as expected, ensure that the <strong>Celery worker</strong> process is running and check the Nautobot logs for errors.</p>
9389
9474
 
9390
9475
 
9391
9476
 
@@ -69,6 +69,16 @@ class ClusterTypeFilterForm(NautobotFilterForm):
69
69
  clusters = DynamicModelMultipleChoiceField(queryset=Cluster.objects.all(), to_field_name="name", required=False)
70
70
 
71
71
 
72
+ class ClusterTypeBulkEditForm(NautobotBulkEditForm):
73
+ pk = forms.ModelMultipleChoiceField(queryset=ClusterType.objects.all(), widget=forms.MultipleHiddenInput())
74
+ description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
75
+
76
+ class Meta:
77
+ nullable_fields = [
78
+ "description",
79
+ ]
80
+
81
+
72
82
  #
73
83
  # Cluster groups
74
84
  #
@@ -89,6 +99,16 @@ class ClusterGroupFilterForm(NautobotFilterForm):
89
99
  clusters = DynamicModelMultipleChoiceField(queryset=Cluster.objects.all(), to_field_name="name", required=False)
90
100
 
91
101
 
102
+ class ClusterGroupBulkEditForm(NautobotBulkEditForm):
103
+ pk = forms.ModelMultipleChoiceField(queryset=ClusterGroup.objects.all(), widget=forms.MultipleHiddenInput())
104
+ description = forms.CharField(max_length=CHARFIELD_MAX_LENGTH, required=False)
105
+
106
+ class Meta:
107
+ nullable_fields = [
108
+ "description",
109
+ ]
110
+
111
+
92
112
  #
93
113
  # Clusters
94
114
  #
@@ -1,40 +1,2 @@
1
1
  {% extends 'generic/object_retrieve.html' %}
2
- {% load helpers %}
3
-
4
- {% block content_left_page %}
5
- <div class="panel panel-default">
6
- <div class="panel-heading">
7
- <strong>Cluster Group</strong>
8
- </div>
9
- <table class="table table-hover panel-body attr-table">
10
- <tr>
11
- <td>Description</td>
12
- <td>{{ object.description|placeholder }}</td>
13
- </tr>
14
- <tr>
15
- <td>Clusters</td>
16
- <td>
17
- <a href="{% url 'virtualization:cluster_list' %}?cluster_group={{ object.name }}">{{ cluster_table.rows|length }}</a>
18
- </td>
19
- </tr>
20
- </table>
21
- </div>
22
- {% endblock content_left_page %}
23
-
24
- {% block content_right_page %}
25
- <div class="panel panel-default">
26
- <div class="panel-heading">
27
- <strong>Clusters</strong>
28
- </div>
29
- {% include 'inc/table.html' with table=cluster_table %}
30
- {% if perms.virtualization.add_cluster %}
31
- <div class="panel-footer text-right noprint">
32
- <a href="{% url 'virtualization:cluster_add' %}?cluster_group={{ object.pk }}" class="btn btn-xs btn-primary">
33
- <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add cluster
34
- </a>
35
- </div>
36
- {% endif %}
37
- </div>
38
- {% include 'inc/paginator.html' with paginator=cluster_table.paginator page=cluster_table.page %}
39
- <div class="row"></div>
40
- {% endblock content_right_page %}
2
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -1 +1,2 @@
1
1
  {% extends 'generic/object_retrieve.html' %}
2
+ {% comment %}3.0 TODO: remove this template, which only exists for backward compatibility with 2.4 and earlier{% endcomment %}
@@ -239,7 +239,7 @@ class VirtualMachineTest(APIViewTestCases.APIViewTestCase):
239
239
  schema = ConfigContextSchema.objects.create(
240
240
  name="Schema 1", data_schema={"type": "object", "properties": {"A": {"type": "integer"}}}
241
241
  )
242
- self.add_permissions("virtualization.change_virtualmachine")
242
+ self.add_permissions("virtualization.change_virtualmachine", "extras.view_configcontextschema")
243
243
 
244
244
  patch_data = {"local_config_context_schema": str(schema.pk)}
245
245
 
@@ -346,7 +346,12 @@ class VMInterfaceTest(APIViewTestCases.APIViewTestCase):
346
346
 
347
347
  def test_untagged_vlan_requires_mode(self):
348
348
  """Test that when an `untagged_vlan` is specified, `mode` is also required."""
349
- self.add_permissions("virtualization.add_vminterface")
349
+ self.add_permissions(
350
+ "virtualization.add_vminterface",
351
+ "virtualization.view_virtualmachine",
352
+ "extras.view_status",
353
+ "ipam.view_vlan",
354
+ )
350
355
 
351
356
  # This will fail.
352
357
  url = self._get_list_url()
@@ -361,7 +366,13 @@ class VMInterfaceTest(APIViewTestCases.APIViewTestCase):
361
366
  )
362
367
 
363
368
  def test_tagged_vlan_raise_error_if_mode_not_set_to_tagged(self):
364
- self.add_permissions("virtualization.add_vminterface", "virtualization.change_vminterface")
369
+ self.add_permissions(
370
+ "virtualization.add_vminterface",
371
+ "virtualization.change_vminterface",
372
+ "virtualization.view_virtualmachine",
373
+ "extras.view_status",
374
+ "ipam.view_vlan",
375
+ )
365
376
  vlan = VLAN.objects.get(name="VLAN 1")
366
377
  virtualmachine = VirtualMachine.objects.first()
367
378
  with self.subTest("On create, assert 400 status."):