forward-netbox 2.0.2__tar.gz → 2.0.3__tar.gz

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 (262) hide show
  1. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/PKG-INFO +3 -2
  2. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/README.md +2 -1
  3. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/__init__.py +1 -1
  4. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_sync.py +32 -538
  5. forward_netbox-2.0.3/forward_netbox/utilities/fast_bootstrap_executor.py +81 -0
  6. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync.py +7 -0
  7. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_reporting.py +9 -2
  8. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/pyproject.toml +1 -1
  9. forward_netbox-2.0.2/forward_netbox/utilities/fast_bootstrap_executor.py +0 -248
  10. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/LICENSE +0 -0
  11. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/api/__init__.py +0 -0
  12. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/api/serializers.py +0 -0
  13. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/api/urls.py +0 -0
  14. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/api/views.py +0 -0
  15. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/choices.py +0 -0
  16. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/exceptions.py +0 -0
  17. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/filtersets.py +0 -0
  18. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/forms.py +0 -0
  19. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/jobs.py +0 -0
  20. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/management/__init__.py +0 -0
  21. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/management/commands/__init__.py +0 -0
  22. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/management/commands/forward_apic_cimc_readiness_audit.py +0 -0
  23. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/management/commands/forward_blocker_audit.py +0 -0
  24. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/management/commands/forward_collection_gap_alert.py +0 -0
  25. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/management/commands/forward_device_scope_reconciliation_audit.py +0 -0
  26. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/management/commands/forward_module_readiness.py +0 -0
  27. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/management/commands/forward_pushdown_profile.py +0 -0
  28. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/management/commands/forward_query_diff_coverage_audit.py +0 -0
  29. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/management/commands/forward_seed_ui_harness.py +0 -0
  30. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/management/commands/forward_validation_org_query_audit.py +0 -0
  31. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0001_initial.py +0 -0
  32. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0002_add_coalesce_fields.py +0 -0
  33. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0003_forwardingestionissue_context_fields.py +0 -0
  34. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0004_forwardingestion_baseline_fields.py +0 -0
  35. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0005_validation_and_reporting.py +0 -0
  36. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0006_alter_forwardnqemap_netbox_model.py +0 -0
  37. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0007_forwardingestion_persisted_change_counters.py +0 -0
  38. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0008_alter_forwardnqemap_netbox_model.py +0 -0
  39. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0009_alter_forwardnqemap_netbox_model.py +0 -0
  40. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0010_forwardvalidationrun_override_applied_and_more.py +0 -0
  41. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0011_forwardingestion_change_request_id.py +0 -0
  42. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0012_forwardnqemap_query_reference.py +0 -0
  43. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0013_execution_run_step.py +0 -0
  44. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0014_execution_step_fetch_scope.py +0 -0
  45. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0015_execution_step_apply_engine.py +0 -0
  46. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0016_execution_step_query_metrics.py +0 -0
  47. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0017_execution_step_row_counters.py +0 -0
  48. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0018_execution_run_reconciliation_events.py +0 -0
  49. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0019_forwardexecutionstep_query_parameters.py +0 -0
  50. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0020_execution_step_operation.py +0 -0
  51. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0021_add_fhrpgroup_nqe_map_choice.py +0 -0
  52. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0022_alter_forwardnqemap_netbox_model.py +0 -0
  53. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0023_split_ip_address_maps.py +0 -0
  54. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0024_forwarddeviceanalysis.py +0 -0
  55. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0025_device_analysis_netboxmodel.py +0 -0
  56. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0026_forwarddeviceanalysis_collection_result.py +0 -0
  57. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0027_alter_forwardexecutionrun_backend.py +0 -0
  58. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/0028_remove_forwardexecutionstep_run_and_more.py +0 -0
  59. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/migrations/__init__.py +0 -0
  60. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/models.py +0 -0
  61. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/navigation.py +0 -0
  62. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/__init__.py +0 -0
  63. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_apic_cimc_inventory.nqe +0 -0
  64. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_apic_nodes.nqe +0 -0
  65. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_app_profiles.nqe +0 -0
  66. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_bridge_domains.nqe +0 -0
  67. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_command_inventory.nqe +0 -0
  68. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_contracts.nqe +0 -0
  69. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_endpoint_groups.nqe +0 -0
  70. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_fabrics.nqe +0 -0
  71. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_filters.nqe +0 -0
  72. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_l3outs.nqe +0 -0
  73. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_nodes.nqe +0 -0
  74. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_pods.nqe +0 -0
  75. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_static_port_bindings.nqe +0 -0
  76. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_tenants.nqe +0 -0
  77. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_aci_vrfs.nqe +0 -0
  78. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_bgp_address_families.nqe +0 -0
  79. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_bgp_peer_address_families.nqe +0 -0
  80. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_bgp_peers.nqe +0 -0
  81. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_device_analysis.nqe +0 -0
  82. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_device_feature_tags.nqe +0 -0
  83. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_device_feature_tags_with_rules.nqe +0 -0
  84. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_device_models.nqe +0 -0
  85. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_device_models_with_netbox_aliases.nqe +0 -0
  86. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_device_types.nqe +0 -0
  87. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_device_vendors.nqe +0 -0
  88. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_devices.nqe +0 -0
  89. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_devices_with_netbox_aliases.nqe +0 -0
  90. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_hsrp_groups.nqe +0 -0
  91. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_inferred_interface_cables.nqe +0 -0
  92. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_interfaces.nqe +0 -0
  93. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_inventory_items.nqe +0 -0
  94. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_ip_addresses_ipv4.nqe +0 -0
  95. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_ip_addresses_ipv6.nqe +0 -0
  96. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_ip_addresses_unassignable_diagnostics.nqe +0 -0
  97. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_locations.nqe +0 -0
  98. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_mac_addresses.nqe +0 -0
  99. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_modules.nqe +0 -0
  100. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_ospf_areas.nqe +0 -0
  101. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_ospf_instances.nqe +0 -0
  102. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_ospf_interfaces.nqe +0 -0
  103. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_peering_sessions.nqe +0 -0
  104. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_platforms.nqe +0 -0
  105. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_prefixes_ipv4.nqe +0 -0
  106. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_prefixes_ipv6.nqe +0 -0
  107. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_routing_import_diagnostics.nqe +0 -0
  108. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_virtual_chassis.nqe +0 -0
  109. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_vlans.nqe +0 -0
  110. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/forward_vrfs.nqe +0 -0
  111. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/queries/netbox_utilities.nqe +0 -0
  112. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/signals.py +0 -0
  113. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tables.py +0 -0
  114. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/template_content.py +0 -0
  115. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/forwarddriftpolicy.html +0 -0
  116. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/forwardingestion.html +0 -0
  117. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/forwardnqemap.html +0 -0
  118. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/forwardsource.html +0 -0
  119. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/forwardsync.html +0 -0
  120. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/forwardsync_dependency_preview.html +0 -0
  121. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/forwardsync_drift_report.html +0 -0
  122. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/forwardsync_health.html +0 -0
  123. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/forwardsync_module_readiness.html +0 -0
  124. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/forwardsync_scope_reconciliation.html +0 -0
  125. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/forwardvalidationrun.html +0 -0
  126. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/inc/device_analysis_panel.html +0 -0
  127. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/inc/diff.html +0 -0
  128. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/inc/execution_insights.html +0 -0
  129. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/inc/logs_pending.html +0 -0
  130. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/inc/merge_form.html +0 -0
  131. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/inc/validation_force_allow_form.html +0 -0
  132. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/partials/change_explainability.html +0 -0
  133. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/partials/ingestion_all.html +0 -0
  134. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/partials/ingestion_merge_button.html +0 -0
  135. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/partials/ingestion_progress.html +0 -0
  136. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/partials/ingestion_statistics.html +0 -0
  137. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/partials/ingestion_status.html +0 -0
  138. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/partials/job_logs.html +0 -0
  139. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/partials/object_tabs.html +0 -0
  140. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/partials/stage_switcher_buttons.html +0 -0
  141. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templates/forward_netbox/partials/validation_force_allow_button.html +0 -0
  142. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templatetags/__init__.py +0 -0
  143. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/templatetags/forward_netbox_helpers.py +0 -0
  144. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/__init__.py +0 -0
  145. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/fixtures/aci_command_inventory_expected.json +0 -0
  146. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/fixtures/aci_discovery_expected.json +0 -0
  147. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/scenarios.py +0 -0
  148. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_api_usage.py +0 -0
  149. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_api_views.py +0 -0
  150. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_apic_cimc_readiness_audit_command.py +0 -0
  151. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_apply_engine.py +0 -0
  152. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_branching.py +0 -0
  153. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_bulk_adapter_parity.py +0 -0
  154. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_bulk_merge.py +0 -0
  155. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_bulk_merge_scale.py +0 -0
  156. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_device_scope_reconciliation_audit_command.py +0 -0
  157. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_device_scope_tagging.py +0 -0
  158. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_forms.py +0 -0
  159. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_forward_api.py +0 -0
  160. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_health.py +0 -0
  161. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_ingestion_merge.py +0 -0
  162. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_interface_naming.py +0 -0
  163. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_ipaddress_dup_global.py +0 -0
  164. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_issue_rendering.py +0 -0
  165. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_job_compat.py +0 -0
  166. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_job_liveness.py +0 -0
  167. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_jobs.py +0 -0
  168. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_log_export.py +0 -0
  169. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_logging.py +0 -0
  170. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_models.py +0 -0
  171. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_module_readiness.py +0 -0
  172. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_plugin_integrations.py +0 -0
  173. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_primary_ip.py +0 -0
  174. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_primary_ip_integration.py +0 -0
  175. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_query_binding.py +0 -0
  176. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_query_diff_coverage_audit_command.py +0 -0
  177. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_query_registry.py +0 -0
  178. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_release_readiness_audit.py +0 -0
  179. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_runtime_dependency_check.py +0 -0
  180. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_scope_matched_tags.py +0 -0
  181. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_scope_module_ui.py +0 -0
  182. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_sensitive_content.py +0 -0
  183. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_stability_hardening.py +0 -0
  184. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_sync_aci.py +0 -0
  185. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_sync_facade.py +0 -0
  186. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_sync_orchestration.py +0 -0
  187. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_sync_runner_contracts.py +0 -0
  188. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_sync_state.py +0 -0
  189. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_synthetic_scenarios.py +0 -0
  190. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/tests/test_validation_org_query_audit_command.py +0 -0
  191. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/urls.py +0 -0
  192. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/__init__.py +0 -0
  193. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/api_usage.py +0 -0
  194. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/apply_engine.py +0 -0
  195. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/apply_engine_bulk.py +0 -0
  196. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/apply_engine_decision.py +0 -0
  197. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/branch_budget.py +0 -0
  198. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/branching.py +0 -0
  199. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/bulk_merge.py +0 -0
  200. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/change_explainability.py +0 -0
  201. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/density_learning.py +0 -0
  202. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/device_analysis.py +0 -0
  203. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/direct_changes.py +0 -0
  204. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/drift_report.py +0 -0
  205. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/execution_ledger.py +0 -0
  206. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/execution_telemetry.py +0 -0
  207. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/fetch_artifacts.py +0 -0
  208. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/forward_api.py +0 -0
  209. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/forward_api_impl.py +0 -0
  210. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/health.py +0 -0
  211. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/health_apply_fetch.py +0 -0
  212. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/health_checks.py +0 -0
  213. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/health_summary_blocks.py +0 -0
  214. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/ingestion_issues.py +0 -0
  215. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/ingestion_merge.py +0 -0
  216. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/ingestion_presentation.py +0 -0
  217. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/interface_naming.py +0 -0
  218. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/job_compat.py +0 -0
  219. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/job_liveness.py +0 -0
  220. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/json_safe.py +0 -0
  221. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/logging.py +0 -0
  222. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/merge.py +0 -0
  223. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/model_contracts.py +0 -0
  224. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/model_validation.py +0 -0
  225. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/module_readiness.py +0 -0
  226. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/multi_branch_lifecycle.py +0 -0
  227. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/plugin_integrations/__init__.py +0 -0
  228. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/plugin_integrations/registry.py +0 -0
  229. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/primary_ip.py +0 -0
  230. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/query_binding.py +0 -0
  231. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/query_binding_resolution.py +0 -0
  232. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/query_diagnostics.py +0 -0
  233. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/query_fetch.py +0 -0
  234. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/query_fetch_execution.py +0 -0
  235. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/query_registry.py +0 -0
  236. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/runtime_guidance.py +0 -0
  237. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/scope_reconciliation.py +0 -0
  238. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sensitive_content.py +0 -0
  239. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/single_branch_executor.py +0 -0
  240. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/snapshot_freshness.py +0 -0
  241. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/support_bundle_archive.py +0 -0
  242. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_aci.py +0 -0
  243. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_cable.py +0 -0
  244. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_contracts.py +0 -0
  245. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_core_models.py +0 -0
  246. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_device.py +0 -0
  247. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_events.py +0 -0
  248. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_execution.py +0 -0
  249. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_facade.py +0 -0
  250. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_interface.py +0 -0
  251. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_inventory_module.py +0 -0
  252. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_ipam.py +0 -0
  253. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_orchestration.py +0 -0
  254. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_primitives.py +0 -0
  255. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_routing.py +0 -0
  256. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_routing_impl.py +0 -0
  257. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_runner_adapters.py +0 -0
  258. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_runner_contracts.py +0 -0
  259. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/sync_state.py +0 -0
  260. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/validation.py +0 -0
  261. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/utilities/validation_org_query.py +0 -0
  262. {forward_netbox-2.0.2 → forward_netbox-2.0.3}/forward_netbox/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forward-netbox
3
- Version: 2.0.2
3
+ Version: 2.0.3
4
4
  Summary: NetBox plugin to sync Forward data into NetBox via built-in NQE queries
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -31,7 +31,8 @@ Forward 26.6 is the baseline for async NQE.
31
31
 
32
32
  | Plugin Release | NetBox Version | Status |
33
33
  | --- | --- | --- |
34
- | `v2.0.2` | `4.6.3` required; needs netbox-branching `1.1.0+` | Current release; Patch: apply_device_scope_tags now works with multiple include tags in `any` match mode each device is tagged with exactly the include tag(s) it carries (resolved per-device at fetch time), instead of skipping. Also silences the spurious `Skipping untagged VLAN 1` warning (VID 1 is NetBox's implicit access default and is intentionally not imported). No engine/schema changes; drop-in from `2.0.1`, no org republish |
34
+ | `v2.0.3` | `4.6.3` required; needs netbox-branching `1.1.0+` | Current release; Patch: (1) module-sync readiness warnings no longer flood the log — the per-row `module bay does not exist; run forward_module_readiness` skip is capped to a few examples plus a suppressed-count summary (was up to 20 near-identical lines per sync); (2) fixes the release `CI` gate (`CHANGELOG matches README`) that had been red since v1.7.2 the generator no longer depends on git tag-date timing; (3) removes dead executor code (`ForwardFastBootstrapExecutor.run`) and refreshes stale internal docs. No engine/schema changes; drop-in from `2.0.2` |
35
+ | `v2.0.2` | `4.6.3` required; needs netbox-branching `1.1.0+` | Superseded by `v2.0.3`; Patch: apply_device_scope_tags now works with multiple include tags in `any` match mode — each device is tagged with exactly the include tag(s) it carries (resolved per-device at fetch time), instead of skipping. Also silences the spurious `Skipping untagged VLAN 1` warning (VID 1 is NetBox's implicit access default and is intentionally not imported). No engine/schema changes; drop-in from `2.0.1`, no org republish |
35
36
  | `v2.0.1` | `4.6.3` required; needs netbox-branching `1.1.0+` | Superseded by `v2.0.2`; Patch: fixes two 2.0.0 regressions an operator hits immediately — a false `netbox_branching is not installed; syncs will fail` startup warning (the dependency check used the wrong distribution name), and a 500 on the Sync list page (`KeyError: 'available'` from a removed execution-ledger summary). No engine or data changes; drop-in upgrade from `2.0.0` |
36
37
  | `v2.0.0` | `4.6.3` required; needs netbox-branching `1.1.0+` | Superseded by `v2.0.1`; Breaking 2.0 — single-branch is the only execution path. Removed the per-shard branching/fast-bootstrap/resumable executor, 10k-change budget sharding, and the execution-ledger run-history; dropped the backend/max-changes/scheduler-overlap selectors |
37
38
  | `v1.7.2` | `4.6.3` required (4.5.x dropped); needs netbox-branching `1.1.0+` | Superseded by `v2.0.0`; Collection-gap diagnostics: per-reason backfill breakdown + staleness, growth/trend escalation, per-device collection result, ACI delete safety valve, opt-in auto-tag |
@@ -8,7 +8,8 @@ Forward 26.6 is the baseline for async NQE.
8
8
 
9
9
  | Plugin Release | NetBox Version | Status |
10
10
  | --- | --- | --- |
11
- | `v2.0.2` | `4.6.3` required; needs netbox-branching `1.1.0+` | Current release; Patch: apply_device_scope_tags now works with multiple include tags in `any` match mode each device is tagged with exactly the include tag(s) it carries (resolved per-device at fetch time), instead of skipping. Also silences the spurious `Skipping untagged VLAN 1` warning (VID 1 is NetBox's implicit access default and is intentionally not imported). No engine/schema changes; drop-in from `2.0.1`, no org republish |
11
+ | `v2.0.3` | `4.6.3` required; needs netbox-branching `1.1.0+` | Current release; Patch: (1) module-sync readiness warnings no longer flood the log — the per-row `module bay does not exist; run forward_module_readiness` skip is capped to a few examples plus a suppressed-count summary (was up to 20 near-identical lines per sync); (2) fixes the release `CI` gate (`CHANGELOG matches README`) that had been red since v1.7.2 the generator no longer depends on git tag-date timing; (3) removes dead executor code (`ForwardFastBootstrapExecutor.run`) and refreshes stale internal docs. No engine/schema changes; drop-in from `2.0.2` |
12
+ | `v2.0.2` | `4.6.3` required; needs netbox-branching `1.1.0+` | Superseded by `v2.0.3`; Patch: apply_device_scope_tags now works with multiple include tags in `any` match mode — each device is tagged with exactly the include tag(s) it carries (resolved per-device at fetch time), instead of skipping. Also silences the spurious `Skipping untagged VLAN 1` warning (VID 1 is NetBox's implicit access default and is intentionally not imported). No engine/schema changes; drop-in from `2.0.1`, no org republish |
12
13
  | `v2.0.1` | `4.6.3` required; needs netbox-branching `1.1.0+` | Superseded by `v2.0.2`; Patch: fixes two 2.0.0 regressions an operator hits immediately — a false `netbox_branching is not installed; syncs will fail` startup warning (the dependency check used the wrong distribution name), and a 500 on the Sync list page (`KeyError: 'available'` from a removed execution-ledger summary). No engine or data changes; drop-in upgrade from `2.0.0` |
13
14
  | `v2.0.0` | `4.6.3` required; needs netbox-branching `1.1.0+` | Superseded by `v2.0.1`; Breaking 2.0 — single-branch is the only execution path. Removed the per-shard branching/fast-bootstrap/resumable executor, 10k-change budget sharding, and the execution-ledger run-history; dropped the backend/max-changes/scheduler-overlap selectors |
14
15
  | `v1.7.2` | `4.6.3` required (4.5.x dropped); needs netbox-branching `1.1.0+` | Superseded by `v2.0.0`; Collection-gap diagnostics: per-reason backfill breakdown + staleness, growth/trend escalation, per-device collection result, ACI delete safety valve, opt-in auto-tag |
@@ -8,7 +8,7 @@ class NetboxForwardConfig(PluginConfig):
8
8
  name = "forward_netbox"
9
9
  verbose_name = "NetBox Forward Plugin"
10
10
  description = "Sync Forward data into NetBox using built-in NQE queries."
11
- version = "2.0.2"
11
+ version = "2.0.3"
12
12
  base_url = "forward"
13
13
  min_version = "4.6.3"
14
14
 
@@ -4,9 +4,7 @@ import tempfile
4
4
  from unittest.mock import Mock
5
5
  from unittest.mock import patch
6
6
 
7
- from core.exceptions import SyncError
8
7
  from core.models import ObjectChange
9
- from core.models import ObjectType
10
8
  from dcim.models import Cable
11
9
  from dcim.models import Device
12
10
  from dcim.models import DeviceRole
@@ -23,7 +21,6 @@ from dcim.models import VirtualChassis
23
21
  from dcim.models.device_components import ModuleBay
24
22
  from dcim.models.modules import ModuleType
25
23
  from django.apps import apps
26
- from django.contrib.auth import get_user_model
27
24
  from django.contrib.contenttypes.models import ContentType
28
25
  from django.core.exceptions import ValidationError
29
26
  from django.db import connection
@@ -53,7 +50,6 @@ from forward_netbox.models import ForwardIngestion
53
50
  from forward_netbox.models import ForwardIngestionIssue
54
51
  from forward_netbox.models import ForwardSource
55
52
  from forward_netbox.models import ForwardSync
56
- from forward_netbox.models import ForwardValidationRun
57
53
  from forward_netbox.signals import seed_builtin_nqe_maps
58
54
  from forward_netbox.utilities.apply_engine import ADAPTER_MODELS_WITHOUT_BLOCKER
59
55
  from forward_netbox.utilities.apply_engine import ADAPTER_REQUIRED_MODELS
@@ -79,17 +75,11 @@ from forward_netbox.utilities.branch_budget import shard_fetch_capability_for_mo
79
75
  from forward_netbox.utilities.branch_budget import shard_fetch_contract
80
76
  from forward_netbox.utilities.branch_budget import SHARD_FETCH_MODEL_CONTRACTS
81
77
  from forward_netbox.utilities.density_learning import update_density_learning
82
- from forward_netbox.utilities.direct_changes import object_changes_for_ingestion
83
78
  from forward_netbox.utilities.execution_telemetry import build_plan_preview
84
- from forward_netbox.utilities.fast_bootstrap_executor import (
85
- ForwardFastBootstrapExecutor,
86
- )
87
79
  from forward_netbox.utilities.forward_api import LATEST_PROCESSED_SNAPSHOT
88
- from forward_netbox.utilities.logging import SyncLogging
89
80
  from forward_netbox.utilities.query_diagnostics import (
90
81
  summarize_ipaddress_parent_prefix_rows,
91
82
  )
92
- from forward_netbox.utilities.query_fetch import ForwardModelResult
93
83
  from forward_netbox.utilities.query_fetch import ForwardQueryContext
94
84
  from forward_netbox.utilities.query_fetch import ForwardQueryFetcher
95
85
  from forward_netbox.utilities.query_registry import QuerySpec
@@ -584,534 +574,6 @@ class ForwardBranchBudgetPlanTest(TestCase):
584
574
  self.assertEqual(profile["dcim.device"]["sample_count"], 1)
585
575
 
586
576
 
587
- class ForwardFastBootstrapExecutorTest(TestCase):
588
- SNAPSHOT_ID = "snapshot-1"
589
-
590
- def setUp(self):
591
- self.source = ForwardSource.objects.create(
592
- name="source-fast-bootstrap",
593
- type="saas",
594
- url="https://fwd.app",
595
- parameters={
596
- "username": "user@example.com",
597
- "password": "secret",
598
- "verify": True,
599
- "timeout": 1200,
600
- "network_id": "test-network",
601
- },
602
- )
603
- self.sync = ForwardSync.objects.create(
604
- name="sync-fast-bootstrap",
605
- source=self.source,
606
- parameters={
607
- "snapshot_id": LATEST_PROCESSED_SNAPSHOT,
608
- "dcim.site": True,
609
- "enable_bulk_orm": False,
610
- },
611
- )
612
-
613
- @patch("forward_netbox.utilities.fast_bootstrap_executor.ForwardSyncRunner")
614
- @patch("forward_netbox.utilities.fast_bootstrap_executor.ForwardQueryFetcher")
615
- def test_run_creates_branchless_baseline_ingestion(
616
- self,
617
- mock_fetcher_class,
618
- mock_runner_class,
619
- ):
620
- logger = SyncLogging()
621
- context = ForwardQueryContext(
622
- network_id="test-network",
623
- snapshot_selector=LATEST_PROCESSED_SNAPSHOT,
624
- snapshot_id=self.SNAPSHOT_ID,
625
- snapshot_info={"state": "PROCESSED"},
626
- snapshot_metrics={},
627
- query_parameters={},
628
- maps=[],
629
- )
630
- workload = BranchWorkload(
631
- model_string="dcim.site",
632
- label="sites",
633
- upsert_rows=[{"name": "Site 1", "slug": "site-1"}],
634
- delete_rows=[{"name": "Old Site", "slug": "old-site"}],
635
- sync_mode="full",
636
- coalesce_fields=[["slug"]],
637
- query_name="Forward Sites",
638
- execution_mode="query_id",
639
- execution_value="FQ_sites",
640
- )
641
- model_result = ForwardModelResult(
642
- model_string="dcim.site",
643
- query_name="Forward Sites",
644
- execution_mode="query_id",
645
- execution_value="FQ_sites",
646
- sync_mode="full",
647
- row_count=1,
648
- delete_count=1,
649
- snapshot_id=self.SNAPSHOT_ID,
650
- )
651
- fetcher = mock_fetcher_class.return_value
652
- fetcher.resolve_context.return_value = context
653
- fetcher.fetch_workloads.return_value = [workload]
654
- fetcher.model_results = [model_result]
655
- runner = mock_runner_class.return_value
656
- runner._model_coalesce_fields = {}
657
-
658
- def apply_rows(model_string, rows):
659
- for _row in rows:
660
- logger.increment_statistics(model_string)
661
-
662
- def delete_rows(model_string, rows):
663
- for _row in rows:
664
- logger.increment_statistics(model_string)
665
-
666
- runner._apply_model_rows.side_effect = apply_rows
667
- runner._delete_model_rows.side_effect = delete_rows
668
-
669
- ingestions = ForwardFastBootstrapExecutor(
670
- self.sync,
671
- Mock(),
672
- logger,
673
- user=Mock(),
674
- ).run()
675
-
676
- self.assertEqual(len(ingestions), 1)
677
- ingestion = ingestions[0]
678
- self.assertIsNone(ingestion.branch)
679
- self.assertTrue(ingestion.baseline_ready)
680
- self.assertEqual(ingestion.snapshot_id, self.SNAPSHOT_ID)
681
- self.assertEqual(ingestion.sync_mode, "full")
682
- self.assertEqual(ingestion.applied_change_count, 2)
683
- self.assertEqual(ingestion.failed_change_count, 0)
684
- self.assertEqual(ingestion.model_results, [model_result.as_dict()])
685
- self.assertEqual(ingestion.model_results[0]["apply_engine"], "adapter")
686
- self.assertEqual(
687
- ForwardValidationRun.objects.get(sync=self.sync),
688
- ingestion.validation_run,
689
- )
690
- self.assertIsNotNone(ingestion.change_request_id)
691
- runner._apply_model_rows.assert_called_once_with(
692
- "dcim.site",
693
- workload.upsert_rows,
694
- )
695
- runner._delete_model_rows.assert_called_once_with(
696
- "dcim.site",
697
- workload.delete_rows,
698
- )
699
-
700
- @patch("forward_netbox.utilities.fast_bootstrap_executor.ForwardSyncRunner")
701
- @patch("forward_netbox.utilities.fast_bootstrap_executor.ForwardQueryFetcher")
702
- def test_run_skips_preflight_when_source_disables_it(
703
- self,
704
- mock_fetcher_class,
705
- mock_runner_class,
706
- ):
707
- self.source.parameters["query_preflight_enabled"] = False
708
- self.source.save(update_fields=["parameters"])
709
-
710
- logger = SyncLogging()
711
- context = ForwardQueryContext(
712
- network_id="test-network",
713
- snapshot_selector=LATEST_PROCESSED_SNAPSHOT,
714
- snapshot_id=self.SNAPSHOT_ID,
715
- snapshot_info={"state": "PROCESSED"},
716
- snapshot_metrics={},
717
- query_parameters={},
718
- maps=[],
719
- )
720
- workload = BranchWorkload(
721
- model_string="dcim.site",
722
- label="sites",
723
- upsert_rows=[{"name": "Site 1", "slug": "site-1"}],
724
- delete_rows=[],
725
- sync_mode="full",
726
- coalesce_fields=[["slug"]],
727
- query_name="Forward Sites",
728
- execution_mode="query_id",
729
- execution_value="FQ_sites",
730
- )
731
- fetcher = mock_fetcher_class.return_value
732
- fetcher.resolve_context.return_value = context
733
- fetcher.fetch_workloads.return_value = [workload]
734
- fetcher.model_results = []
735
- runner = mock_runner_class.return_value
736
- runner._model_coalesce_fields = {}
737
-
738
- ForwardFastBootstrapExecutor(
739
- self.sync,
740
- Mock(),
741
- logger,
742
- user=Mock(),
743
- ).run()
744
-
745
- self.assertFalse(fetcher.run_preflight.called)
746
- fetcher.fetch_workloads.assert_called_once()
747
-
748
- @patch("forward_netbox.utilities.fast_bootstrap_executor.ForwardSyncRunner")
749
- @patch("forward_netbox.utilities.fast_bootstrap_executor.ForwardQueryFetcher")
750
- def test_run_applies_fast_bootstrap_workloads_in_dependency_order(
751
- self,
752
- mock_fetcher_class,
753
- mock_runner_class,
754
- ):
755
- logger = SyncLogging()
756
- context = ForwardQueryContext(
757
- network_id="test-network",
758
- snapshot_selector=LATEST_PROCESSED_SNAPSHOT,
759
- snapshot_id=self.SNAPSHOT_ID,
760
- snapshot_info={"state": "PROCESSED"},
761
- snapshot_metrics={},
762
- query_parameters={},
763
- maps=[],
764
- )
765
- mac_workload = BranchWorkload(
766
- model_string="dcim.macaddress",
767
- label="mac addresses",
768
- upsert_rows=[
769
- {
770
- "device": "device-1",
771
- "interface": "Ethernet1",
772
- "mac": "00:11:22:33:44:55",
773
- "mac_address": "00:11:22:33:44:55",
774
- }
775
- ],
776
- sync_mode="full",
777
- coalesce_fields=[["mac_address"]],
778
- query_name="Forward MAC Addresses",
779
- execution_mode="query_id",
780
- execution_value="FQ_macs",
781
- )
782
- interface_workload = BranchWorkload(
783
- model_string="dcim.interface",
784
- label="interfaces",
785
- upsert_rows=[
786
- {
787
- "device": "device-1",
788
- "name": "Ethernet1",
789
- "type": "other",
790
- "enabled": True,
791
- }
792
- ],
793
- sync_mode="full",
794
- coalesce_fields=[["device", "name"]],
795
- query_name="Forward Interfaces",
796
- execution_mode="query_id",
797
- execution_value="FQ_interfaces",
798
- )
799
- fetcher = mock_fetcher_class.return_value
800
- fetcher.resolve_context.return_value = context
801
- fetcher.fetch_workloads.return_value = [mac_workload, interface_workload]
802
- fetcher.model_results = [
803
- ForwardModelResult(
804
- model_string="dcim.macaddress",
805
- query_name="Forward MAC Addresses",
806
- execution_mode="query_id",
807
- execution_value="FQ_macs",
808
- sync_mode="full",
809
- row_count=1,
810
- snapshot_id=self.SNAPSHOT_ID,
811
- ),
812
- ForwardModelResult(
813
- model_string="dcim.interface",
814
- query_name="Forward Interfaces",
815
- execution_mode="query_id",
816
- execution_value="FQ_interfaces",
817
- sync_mode="full",
818
- row_count=1,
819
- snapshot_id=self.SNAPSHOT_ID,
820
- ),
821
- ]
822
- runner = mock_runner_class.return_value
823
- runner._model_coalesce_fields = {}
824
-
825
- ForwardFastBootstrapExecutor(
826
- self.sync,
827
- Mock(),
828
- logger,
829
- user=Mock(),
830
- ).run()
831
-
832
- self.assertEqual(
833
- [call.args[0] for call in runner._apply_model_rows.call_args_list],
834
- ["dcim.interface", "dcim.macaddress"],
835
- )
836
-
837
- @patch("forward_netbox.utilities.fast_bootstrap_executor.ForwardQueryFetcher")
838
- def test_run_records_direct_netbox_changes_for_fast_bootstrap(
839
- self,
840
- mock_fetcher_class,
841
- ):
842
- logger = SyncLogging()
843
- context = ForwardQueryContext(
844
- network_id="test-network",
845
- snapshot_selector=LATEST_PROCESSED_SNAPSHOT,
846
- snapshot_id=self.SNAPSHOT_ID,
847
- snapshot_info={"state": "PROCESSED"},
848
- snapshot_metrics={},
849
- query_parameters={},
850
- maps=[],
851
- )
852
- workload = BranchWorkload(
853
- model_string="dcim.site",
854
- label="sites",
855
- upsert_rows=[{"name": "Site 2", "slug": "site-2"}],
856
- delete_rows=[],
857
- sync_mode="full",
858
- coalesce_fields=[["slug"]],
859
- query_name="Forward Sites",
860
- execution_mode="query_id",
861
- execution_value="FQ_sites",
862
- )
863
- model_result = ForwardModelResult(
864
- model_string="dcim.site",
865
- query_name="Forward Sites",
866
- execution_mode="query_id",
867
- execution_value="FQ_sites",
868
- sync_mode="full",
869
- row_count=1,
870
- delete_count=0,
871
- snapshot_id=self.SNAPSHOT_ID,
872
- )
873
- fetcher = mock_fetcher_class.return_value
874
- fetcher.resolve_context.return_value = context
875
- fetcher.fetch_workloads.return_value = [workload]
876
- fetcher.model_results = [model_result]
877
- user = get_user_model().objects.create_user(username="fast-bootstrap-user")
878
-
879
- ingestions = ForwardFastBootstrapExecutor(
880
- self.sync,
881
- Mock(),
882
- logger,
883
- user=user,
884
- ).run()
885
-
886
- ingestion = ingestions[0]
887
- ingestion.refresh_from_db()
888
- site_type = ObjectType.objects.get_for_model(Site)
889
- self.assertTrue(Site.objects.filter(slug="site-2").exists())
890
- self.assertTrue(
891
- ObjectChange.objects.filter(
892
- request_id=ingestion.change_request_id,
893
- changed_object_type=site_type,
894
- action="create",
895
- ).exists()
896
- )
897
- self.assertEqual(ingestion.applied_change_count, 1)
898
- self.assertEqual(ingestion.created_change_count, 1)
899
- self.assertEqual(ingestion.updated_change_count, 0)
900
- self.assertEqual(ingestion.deleted_change_count, 0)
901
- self.assertEqual(object_changes_for_ingestion(ingestion).count(), 1)
902
-
903
- @patch("forward_netbox.utilities.fast_bootstrap_executor.ForwardSyncRunner")
904
- @patch("forward_netbox.utilities.fast_bootstrap_executor.ForwardQueryFetcher")
905
- def test_run_does_not_mark_baseline_ready_when_issues_exist(
906
- self,
907
- mock_fetcher_class,
908
- mock_runner_class,
909
- ):
910
- logger = SyncLogging()
911
- context = ForwardQueryContext(
912
- network_id="test-network",
913
- snapshot_selector=LATEST_PROCESSED_SNAPSHOT,
914
- snapshot_id=self.SNAPSHOT_ID,
915
- snapshot_info={"state": "PROCESSED"},
916
- snapshot_metrics={},
917
- query_parameters={},
918
- maps=[],
919
- )
920
- workload = BranchWorkload(
921
- model_string="dcim.site",
922
- label="sites",
923
- upsert_rows=[{"name": "Site 1", "slug": "site-1"}],
924
- delete_rows=[],
925
- sync_mode="full",
926
- coalesce_fields=[["slug"]],
927
- query_name="Forward Sites",
928
- execution_mode="query_id",
929
- execution_value="FQ_sites",
930
- )
931
- model_result = ForwardModelResult(
932
- model_string="dcim.site",
933
- query_name="Forward Sites",
934
- execution_mode="query_id",
935
- execution_value="FQ_sites",
936
- sync_mode="full",
937
- row_count=1,
938
- delete_count=0,
939
- snapshot_id=self.SNAPSHOT_ID,
940
- )
941
- fetcher = mock_fetcher_class.return_value
942
- fetcher.resolve_context.return_value = context
943
- fetcher.fetch_workloads.return_value = [workload]
944
- fetcher.model_results = [model_result]
945
- runner = mock_runner_class.return_value
946
- runner._model_coalesce_fields = {}
947
-
948
- def apply_rows(model_string, _rows):
949
- ingestion = ForwardIngestion.objects.get(sync=self.sync)
950
- ingestion.issues.create(
951
- model=model_string,
952
- message="Unable to apply site row.",
953
- exception="validation failed",
954
- )
955
- logger.increment_statistics(model_string, outcome="failed")
956
-
957
- runner._apply_model_rows.side_effect = apply_rows
958
-
959
- with self.assertRaisesRegex(
960
- SyncError,
961
- "Forward fast bootstrap completed with issues",
962
- ):
963
- ForwardFastBootstrapExecutor(
964
- self.sync,
965
- Mock(),
966
- logger,
967
- user=Mock(),
968
- ).run()
969
-
970
- ingestion = ForwardIngestion.objects.get(sync=self.sync)
971
- self.assertFalse(ingestion.baseline_ready)
972
- self.assertEqual(ingestion.issues.count(), 1)
973
- self.assertEqual(ingestion.applied_change_count, 0)
974
- self.assertEqual(ingestion.failed_change_count, 1)
975
-
976
- @patch("forward_netbox.utilities.fast_bootstrap_executor.ForwardSyncRunner")
977
- @patch("forward_netbox.utilities.fast_bootstrap_executor.ForwardQueryFetcher")
978
- def test_run_marks_baseline_ready_when_only_optional_model_issues_exist(
979
- self,
980
- mock_fetcher_class,
981
- mock_runner_class,
982
- ):
983
- logger = SyncLogging()
984
- context = ForwardQueryContext(
985
- network_id="test-network",
986
- snapshot_selector=LATEST_PROCESSED_SNAPSHOT,
987
- snapshot_id=self.SNAPSHOT_ID,
988
- snapshot_info={"state": "PROCESSED"},
989
- snapshot_metrics={},
990
- query_parameters={},
991
- maps=[],
992
- )
993
- workload = BranchWorkload(
994
- model_string="netbox_routing.bgppeer",
995
- label="bgp peers",
996
- upsert_rows=[{"device": "device-1"}],
997
- delete_rows=[],
998
- sync_mode="full",
999
- coalesce_fields=[["device"]],
1000
- query_name="Forward BGP Peers",
1001
- execution_mode="query_id",
1002
- execution_value="FQ_bgp",
1003
- )
1004
- model_result = ForwardModelResult(
1005
- model_string="netbox_routing.bgppeer",
1006
- query_name="Forward BGP Peers",
1007
- execution_mode="query_id",
1008
- execution_value="FQ_bgp",
1009
- sync_mode="full",
1010
- row_count=1,
1011
- delete_count=0,
1012
- snapshot_id=self.SNAPSHOT_ID,
1013
- )
1014
- fetcher = mock_fetcher_class.return_value
1015
- fetcher.resolve_context.return_value = context
1016
- fetcher.fetch_workloads.return_value = [workload]
1017
- fetcher.model_results = [model_result]
1018
- runner = mock_runner_class.return_value
1019
- runner._model_coalesce_fields = {}
1020
-
1021
- def apply_rows(model_string, _rows):
1022
- ingestion = ForwardIngestion.objects.get(sync=self.sync)
1023
- ingestion.issues.create(
1024
- model=model_string,
1025
- message="Unable to apply optional BGP row.",
1026
- exception="validation failed",
1027
- )
1028
- logger.increment_statistics(model_string, outcome="failed")
1029
-
1030
- runner._apply_model_rows.side_effect = apply_rows
1031
-
1032
- ingestions = ForwardFastBootstrapExecutor(
1033
- self.sync,
1034
- Mock(),
1035
- logger,
1036
- user=Mock(),
1037
- ).run()
1038
-
1039
- ingestion = ingestions[0]
1040
- ingestion.refresh_from_db()
1041
- self.assertTrue(ingestion.baseline_ready)
1042
- self.assertEqual(ingestion.issues.count(), 1)
1043
- self.assertEqual(ingestion.failed_change_count, 1)
1044
-
1045
- @patch("forward_netbox.utilities.fast_bootstrap_executor.ForwardSyncRunner")
1046
- @patch("forward_netbox.utilities.fast_bootstrap_executor.ForwardQueryFetcher")
1047
- def test_run_marks_baseline_ready_when_only_dependency_skip_issues_exist(
1048
- self,
1049
- mock_fetcher_class,
1050
- mock_runner_class,
1051
- ):
1052
- logger = SyncLogging()
1053
- context = ForwardQueryContext(
1054
- network_id="test-network",
1055
- snapshot_selector=LATEST_PROCESSED_SNAPSHOT,
1056
- snapshot_id=self.SNAPSHOT_ID,
1057
- snapshot_info={"state": "PROCESSED"},
1058
- snapshot_metrics={},
1059
- query_parameters={},
1060
- maps=[],
1061
- )
1062
- workload = BranchWorkload(
1063
- model_string="ipam.ipaddress",
1064
- label="ip addresses",
1065
- upsert_rows=[],
1066
- delete_rows=[{"address": "192.0.2.1/32"}],
1067
- sync_mode="full",
1068
- coalesce_fields=[["address"]],
1069
- query_name="Forward IP Addresses",
1070
- execution_mode="query_id",
1071
- execution_value="FQ_ip",
1072
- )
1073
- model_result = ForwardModelResult(
1074
- model_string="ipam.ipaddress",
1075
- query_name="Forward IP Addresses",
1076
- execution_mode="query_id",
1077
- execution_value="FQ_ip",
1078
- sync_mode="full",
1079
- row_count=0,
1080
- delete_count=1,
1081
- snapshot_id=self.SNAPSHOT_ID,
1082
- )
1083
- fetcher = mock_fetcher_class.return_value
1084
- fetcher.resolve_context.return_value = context
1085
- fetcher.fetch_workloads.return_value = [workload]
1086
- fetcher.model_results = [model_result]
1087
- runner = mock_runner_class.return_value
1088
- runner._model_coalesce_fields = {}
1089
-
1090
- def delete_rows(model_string, _rows):
1091
- ingestion = ForwardIngestion.objects.get(sync=self.sync)
1092
- ingestion.issues.create(
1093
- model=model_string,
1094
- message="Skipping delete for `ipam.ipaddress` due to protected dependencies.",
1095
- exception="ForwardDependencySkipError",
1096
- )
1097
- logger.increment_statistics(model_string, outcome="skipped")
1098
-
1099
- runner._delete_model_rows.side_effect = delete_rows
1100
-
1101
- ingestions = ForwardFastBootstrapExecutor(
1102
- self.sync,
1103
- Mock(),
1104
- logger,
1105
- user=Mock(),
1106
- ).run()
1107
-
1108
- ingestion = ingestions[0]
1109
- ingestion.refresh_from_db()
1110
- self.assertTrue(ingestion.baseline_ready)
1111
- self.assertEqual(ingestion.issues.count(), 1)
1112
- self.assertEqual(ingestion.failed_change_count, 0)
1113
-
1114
-
1115
577
  class ForwardSyncRunnerTest(TestCase):
1116
578
  def setUp(self):
1117
579
  self.source = ForwardSource.objects.create(
@@ -2492,6 +1954,38 @@ class ForwardSyncRunnerTest(TestCase):
2492
1954
  "`missing-interface` after the first 20.",
2493
1955
  )
2494
1956
 
1957
+ def test_apply_dcim_module_caps_missing_module_bay_warnings(self):
1958
+ # A systemic readiness gap (module sync enabled before the device-type
1959
+ # module bays exist) skips every module row, so the per-row detail is
1960
+ # capped low (SKIP_WARNING_DETAIL_LIMITS) and the rest are summarized.
1961
+ self._create_device("device-module-readiness")
1962
+ logger = Mock()
1963
+ runner = ForwardSyncRunner(
1964
+ sync=self.sync, ingestion=None, client=None, logger_=logger
1965
+ )
1966
+ limit = ForwardSyncRunner.SKIP_WARNING_DETAIL_LIMITS["missing-module-bay"]
1967
+ rows = [
1968
+ {"device": "device-module-readiness", "module_bay": f"module {index}"}
1969
+ for index in range(limit + 2)
1970
+ ]
1971
+
1972
+ runner._apply_model_rows("dcim.module", rows)
1973
+
1974
+ warning_messages = [call.args[0] for call in logger.log_warning.call_args_list]
1975
+ # `limit` per-row detail lines + one suppressed-count summary.
1976
+ self.assertEqual(len(warning_messages), limit + 1)
1977
+ self.assertTrue(
1978
+ all(
1979
+ "forward_module_readiness" in message
1980
+ for message in warning_messages[:limit]
1981
+ )
1982
+ )
1983
+ self.assertEqual(
1984
+ warning_messages[-1],
1985
+ f"Suppressed 2 additional dcim.module skip warnings for "
1986
+ f"`missing-module-bay` after the first {limit}.",
1987
+ )
1988
+
2495
1989
  def test_dependency_lookup_cache_primes_only_exact_interface_pairs(self):
2496
1990
  device_1 = self._create_device("device-pair-1")
2497
1991
  device_2 = self._create_device("device-pair-2")