nautobot 2.4.15__py3-none-any.whl → 2.4.17__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.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/apps/utils.py +2 -0
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +3 -3
- nautobot/cloud/views.py +7 -0
- nautobot/core/apps/__init__.py +1 -0
- nautobot/core/celery/__init__.py +2 -1
- nautobot/core/templates/components/panel/panel.html +1 -1
- nautobot/core/templates/inc/paginator.html +3 -3
- nautobot/core/templates/inc/table.html +2 -2
- nautobot/core/templatetags/helpers.py +80 -6
- nautobot/core/testing/mixins.py +1 -1
- nautobot/core/testing/views.py +2 -4
- nautobot/core/ui/bulk_buttons.py +53 -53
- nautobot/core/ui/object_detail.py +9 -4
- nautobot/core/utils/data.py +13 -0
- nautobot/core/utils/deprecation.py +2 -0
- nautobot/dcim/migrations/0073_alter_powerport_power_factor_and_more.py +41 -0
- nautobot/dcim/models/device_component_templates.py +4 -2
- nautobot/dcim/models/device_components.py +3 -2
- nautobot/dcim/templates/dcim/rack_elevation_list.html +4 -4
- nautobot/dcim/views.py +9 -0
- nautobot/extras/models/customfields.py +45 -9
- nautobot/extras/tables.py +12 -0
- nautobot/extras/templates/extras/configcontext_retrieve.html +1 -1
- nautobot/extras/templates/extras/configcontext_update.html +49 -49
- nautobot/extras/templates/extras/configcontextschema_retrieve.html +47 -47
- nautobot/extras/templates/extras/configcontextschema_update.html +18 -18
- nautobot/extras/templates/extras/inc/job_table.html +1 -1
- nautobot/extras/templates/extras/inc/object_contact_header.html +2 -2
- nautobot/extras/templates/extras/marketplace.html +1 -1
- nautobot/extras/templates/extras/note_retrieve.html +53 -53
- nautobot/extras/templates/extras/secretsgroup_retrieve.html +2 -29
- nautobot/extras/templates/extras/tag_retrieve.html +1 -1
- nautobot/extras/templates/extras/tag_update.html +14 -14
- nautobot/extras/templates/extras/team_retrieve.html +1 -1
- nautobot/extras/tests/test_models.py +216 -0
- nautobot/extras/tests/test_plugins.py +12 -0
- nautobot/extras/tests/test_views.py +2 -2
- nautobot/extras/views.py +20 -4
- nautobot/ipam/apps.py +1 -0
- nautobot/ipam/jobs/__init__.py +10 -0
- nautobot/ipam/jobs/cleanup.py +296 -0
- nautobot/ipam/models.py +301 -178
- nautobot/ipam/templates/ipam/inc/ipadress_edit_header.html +3 -3
- nautobot/ipam/templates/ipam/inc/toggle_available.html +2 -2
- nautobot/ipam/templates/ipam/ipaddress_assign.html +1 -1
- nautobot/ipam/templates/ipam/prefix_list.html +1 -1
- nautobot/ipam/templates/ipam/vlan_retrieve.html +1 -77
- nautobot/ipam/tests/test_jobs.py +454 -0
- nautobot/ipam/tests/test_models.py +290 -122
- nautobot/ipam/tests/test_views.py +40 -164
- nautobot/ipam/urls.py +0 -11
- nautobot/ipam/utils/testing.py +9 -4
- nautobot/ipam/views.py +166 -235
- nautobot/project-static/docs/404.html +9 -6
- nautobot/project-static/docs/apps/index.html +9 -6
- nautobot/project-static/docs/apps/nautobot-apps.html +9 -6
- nautobot/project-static/docs/assets/javascripts/bundle.92b07e13.min.js +16 -0
- nautobot/project-static/docs/assets/javascripts/{bundle.50899def.min.js.map → bundle.92b07e13.min.js.map} +2 -2
- nautobot/project-static/docs/assets/javascripts/workers/{search.d50fe291.min.js → search.973d3a69.min.js} +4 -4
- nautobot/project-static/docs/assets/javascripts/workers/{search.d50fe291.min.js.map → search.973d3a69.min.js.map} +1 -1
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +10 -7
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/constants.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/events.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +11 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +11 -8
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +28 -9
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +9 -6
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +69 -7
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +9 -6
- nautobot/project-static/docs/development/apps/api/configuration-view.html +13 -10
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +11 -8
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +13 -10
- nautobot/project-static/docs/development/apps/api/models/global-search.html +10 -7
- nautobot/project-static/docs/development/apps/api/models/graphql.html +18 -15
- nautobot/project-static/docs/development/apps/api/models/index.html +14 -11
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +11 -8
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +15 -12
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +9 -6
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +15 -12
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +9 -6
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +11 -8
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +16 -13
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +12 -10305
- nautobot/project-static/docs/development/apps/api/platform-features/prepopulating-data.html +10722 -0
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +15 -12
- nautobot/project-static/docs/development/apps/api/platform-features/table-extensions.html +14 -11
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +9 -6
- nautobot/project-static/docs/development/apps/api/prometheus.html +15 -12
- nautobot/project-static/docs/development/apps/api/setup.html +9 -6
- nautobot/project-static/docs/development/apps/api/testing.html +9 -6
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +12 -9
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +9 -6
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +9 -6
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +9 -6
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-views.html +20 -17
- nautobot/project-static/docs/development/apps/api/views/base-template.html +9 -6
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +15 -12
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +14 -11
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +9 -6
- nautobot/project-static/docs/development/apps/api/views/index.html +9 -6
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +10 -7
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +24 -21
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +12 -9
- nautobot/project-static/docs/development/apps/api/views/notes.html +10 -7
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +19 -16
- nautobot/project-static/docs/development/apps/api/views/urls.html +11 -8
- nautobot/project-static/docs/development/apps/index.html +9 -6
- nautobot/project-static/docs/development/apps/migration/code-updates.html +19 -16
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +9 -6
- nautobot/project-static/docs/development/apps/migration/from-v1.html +9 -6
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +22 -19
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +9 -6
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +9 -6
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +9 -6
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/best-practices.html +9 -6
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/breadcrumbs-titles.html +14 -11
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/custom-content.html +27 -24
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/index.html +20 -17
- nautobot/project-static/docs/development/apps/migration/ui-component-framework/migration-steps.html +20 -17
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +9 -6
- nautobot/project-static/docs/development/core/application-registry.html +23 -20
- nautobot/project-static/docs/development/core/best-practices.html +23 -20
- nautobot/project-static/docs/development/core/bootstrap-ui.html +9 -6
- nautobot/project-static/docs/development/core/caching.html +9 -6
- nautobot/project-static/docs/development/core/controllers.html +9 -6
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +10 -7
- nautobot/project-static/docs/development/core/generic-views.html +9 -6
- nautobot/project-static/docs/development/core/getting-started.html +9 -6
- nautobot/project-static/docs/development/core/homepage.html +12 -9
- nautobot/project-static/docs/development/core/index.html +9 -6
- nautobot/project-static/docs/development/core/minikube-dev-environment-for-k8s-jobs.html +9 -6
- nautobot/project-static/docs/development/core/model-checklist.html +9 -6
- nautobot/project-static/docs/development/core/model-features.html +11 -8
- nautobot/project-static/docs/development/core/natural-keys.html +21 -18
- nautobot/project-static/docs/development/core/navigation-menu.html +10 -7
- nautobot/project-static/docs/development/core/release-checklist.html +9 -6
- nautobot/project-static/docs/development/core/role-internals.html +9 -6
- nautobot/project-static/docs/development/core/settings.html +9 -6
- nautobot/project-static/docs/development/core/style-guide.html +32 -29
- nautobot/project-static/docs/development/core/templates.html +9 -6
- nautobot/project-static/docs/development/core/testing.html +10 -7
- nautobot/project-static/docs/development/core/ui-component-framework.html +36 -33
- nautobot/project-static/docs/development/core/user-preferences.html +9 -6
- nautobot/project-static/docs/development/index.html +9 -6
- nautobot/project-static/docs/development/jobs/getting-started.html +13 -10
- nautobot/project-static/docs/development/jobs/index.html +9 -6
- nautobot/project-static/docs/development/jobs/installation.html +23 -20
- nautobot/project-static/docs/development/jobs/job-extensions.html +25 -22
- nautobot/project-static/docs/development/jobs/job-logging.html +12 -9
- nautobot/project-static/docs/development/jobs/job-patterns.html +45 -42
- nautobot/project-static/docs/development/jobs/job-structure.html +53 -50
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +23 -20
- nautobot/project-static/docs/development/jobs/testing.html +14 -11
- nautobot/project-static/docs/index.html +9 -6
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/overview/application_stack.html +9 -6
- nautobot/project-static/docs/overview/design_philosophy.html +9 -6
- nautobot/project-static/docs/release-notes/index.html +9 -6
- nautobot/project-static/docs/release-notes/version-1.0.html +9 -6
- nautobot/project-static/docs/release-notes/version-1.1.html +9 -6
- nautobot/project-static/docs/release-notes/version-1.2.html +10 -7
- nautobot/project-static/docs/release-notes/version-1.3.html +9 -6
- nautobot/project-static/docs/release-notes/version-1.4.html +9 -6
- nautobot/project-static/docs/release-notes/version-1.5.html +13 -10
- nautobot/project-static/docs/release-notes/version-1.6.html +9 -6
- nautobot/project-static/docs/release-notes/version-2.0.html +9 -6
- nautobot/project-static/docs/release-notes/version-2.1.html +9 -6
- nautobot/project-static/docs/release-notes/version-2.2.html +9 -6
- nautobot/project-static/docs/release-notes/version-2.3.html +9 -6
- nautobot/project-static/docs/release-notes/version-2.4.html +342 -6
- nautobot/project-static/docs/requirements.txt +2 -2
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +301 -301
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +15 -12
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +9 -6
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +16 -13
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +9 -6
- nautobot/project-static/docs/user-guide/administration/configuration/redis.html +9 -6
- nautobot/project-static/docs/user-guide/administration/configuration/settings.html +11 -8
- nautobot/project-static/docs/user-guide/administration/configuration/time-zones.html +9 -6
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +9 -6
- nautobot/project-static/docs/user-guide/administration/guides/docker.html +9 -6
- nautobot/project-static/docs/user-guide/administration/guides/health-checks.html +9 -6
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +9 -6
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +9 -6
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +9 -6
- nautobot/project-static/docs/user-guide/administration/guides/request-profiling.html +9 -6
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +16 -13
- nautobot/project-static/docs/user-guide/administration/guides/selinux-troubleshooting.html +9 -6
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +9 -6
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +9 -6
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +9 -6
- nautobot/project-static/docs/user-guide/administration/installation/index.html +9 -6
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +9 -6
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +9 -6
- nautobot/project-static/docs/user-guide/administration/installation/services.html +12 -9
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +13 -10
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +10 -7
- nautobot/project-static/docs/user-guide/administration/security/index.html +9 -6
- nautobot/project-static/docs/user-guide/administration/security/notices.html +9 -6
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +9 -6
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +10 -7
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +9 -6
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +9 -6
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +9 -6
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +9 -6
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +9 -6
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +9 -6
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +9 -6
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +15 -12
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloud.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudaccount.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetwork.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudnetworkprefixassignment.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudresourcetype.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservice.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/cloud/cloudservicenetworkassignment.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controller.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/controllermanageddevicegroup.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicefamily.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +13 -10
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/module.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebay.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulebaytemplate.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/modulefamily.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/moduletype.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareimagefile.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/softwareversion.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualdevicecontext.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/extras/contact.html +11 -8
- nautobot/project-static/docs/user-guide/core-data-model/extras/team.html +11 -8
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +41 -41
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +197 -54
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/overview/introduction.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/wireless/index.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/wireless/radioprofile.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/wireless/supporteddatarate.html +9 -6
- nautobot/project-static/docs/user-guide/core-data-model/wireless/wirelessnetwork.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/contacts-and-teams.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +13 -10
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/software-image-files-and-versions.html +9 -6
- nautobot/project-static/docs/user-guide/feature-guides/wireless-networks-and-controllers.html +9 -6
- nautobot/project-static/docs/user-guide/index.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +10 -7
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/events.html +11 -8
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobqueue.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/kubernetes-job-support.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/managing-jobs.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/objectmetadata.html +12 -9
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/rendering-jinja-templates.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/savedview.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +11 -8
- nautobot/project-static/docs/user-guide/platform-functionality/staticgroupassociation.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +9 -6
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +9 -6
- nautobot/project-static/fonts/UFL.txt +96 -96
- nautobot/project-static/js/forms.js +35 -2
- nautobot/virtualization/filters.py +7 -0
- {nautobot-2.4.15.dist-info → nautobot-2.4.17.dist-info}/METADATA +6 -6
- {nautobot-2.4.15.dist-info → nautobot-2.4.17.dist-info}/RECORD +373 -368
- nautobot/project-static/docs/assets/javascripts/bundle.50899def.min.js +0 -16
- {nautobot-2.4.15.dist-info → nautobot-2.4.17.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.15.dist-info → nautobot-2.4.17.dist-info}/NOTICE +0 -0
- {nautobot-2.4.15.dist-info → nautobot-2.4.17.dist-info}/WHEEL +0 -0
- {nautobot-2.4.15.dist-info → nautobot-2.4.17.dist-info}/entry_points.txt +0 -0
|
@@ -406,17 +406,20 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
|
|
|
406
406
|
self.status = self.statuses.first()
|
|
407
407
|
self.status.content_types.add(ContentType.objects.get_for_model(IPAddress))
|
|
408
408
|
self.root = Prefix.objects.create(
|
|
409
|
-
prefix="101.102.0.0/
|
|
409
|
+
prefix="101.102.0.0/16", status=self.status, namespace=self.namespace, type=PrefixTypeChoices.TYPE_CONTAINER
|
|
410
410
|
)
|
|
411
411
|
self.parent = Prefix.objects.create(
|
|
412
|
-
prefix="101.102.
|
|
412
|
+
prefix="101.102.103.0/24",
|
|
413
|
+
status=self.status,
|
|
414
|
+
namespace=self.namespace,
|
|
415
|
+
type=PrefixTypeChoices.TYPE_CONTAINER,
|
|
413
416
|
)
|
|
414
|
-
self.child1 = Prefix.objects.create(prefix="101.102.
|
|
415
|
-
self.child2 = Prefix.objects.create(prefix="101.102.
|
|
417
|
+
self.child1 = Prefix.objects.create(prefix="101.102.103.0/26", status=self.status, namespace=self.namespace)
|
|
418
|
+
self.child2 = Prefix.objects.create(prefix="101.102.103.104/32", status=self.status, namespace=self.namespace)
|
|
416
419
|
|
|
417
420
|
def test_parent_exists_after_model_clean(self):
|
|
418
421
|
prefix = Prefix(
|
|
419
|
-
prefix="101.102.0
|
|
422
|
+
prefix="101.102.1.0/24",
|
|
420
423
|
status=self.status,
|
|
421
424
|
namespace=self.namespace,
|
|
422
425
|
type=PrefixTypeChoices.TYPE_CONTAINER,
|
|
@@ -626,18 +629,18 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
|
|
|
626
629
|
# siblings()
|
|
627
630
|
self.assertEqual(list(self.child1.siblings()), [self.child2])
|
|
628
631
|
self.assertEqual(list(self.child1.siblings(include_self=True)), [self.child1, self.child2])
|
|
629
|
-
parent2 = Prefix.objects.create(prefix="101.102.0
|
|
632
|
+
parent2 = Prefix.objects.create(prefix="101.102.128.0/24", status=self.status, namespace=self.namespace)
|
|
630
633
|
self.assertEqual(list(self.parent.siblings()), [parent2])
|
|
631
634
|
self.assertEqual(list(self.parent.siblings(include_self=True)), [self.parent, parent2])
|
|
632
635
|
|
|
633
|
-
def
|
|
636
|
+
def test_reparenting_on_create_and_delete(self):
|
|
634
637
|
"""Test that reparenting algorithm works in its most basic form."""
|
|
635
638
|
# tree hierarchy
|
|
636
639
|
self.assertIsNone(self.root.parent)
|
|
637
640
|
self.assertEqual(self.parent.parent, self.root)
|
|
638
641
|
self.assertEqual(self.child1.parent, self.parent)
|
|
639
642
|
|
|
640
|
-
# Delete the parent (/
|
|
643
|
+
# Delete the parent (/24); child1/child2 now have root (/16) as their parent.
|
|
641
644
|
num_deleted, _ = self.parent.delete()
|
|
642
645
|
self.assertEqual(num_deleted, 1)
|
|
643
646
|
|
|
@@ -648,9 +651,14 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
|
|
|
648
651
|
self.assertEqual(self.child2.parent, self.root)
|
|
649
652
|
self.assertEqual(list(self.child1.ancestors()), [self.root])
|
|
650
653
|
|
|
651
|
-
# Add /
|
|
652
|
-
# /
|
|
653
|
-
self.parent
|
|
654
|
+
# Add /24 back in as a parent and assert that child1/child2 now have it as their parent, and
|
|
655
|
+
# /16 is its parent.
|
|
656
|
+
self.parent = Prefix.objects.create(
|
|
657
|
+
prefix="101.102.103.0/24",
|
|
658
|
+
status=self.status,
|
|
659
|
+
namespace=self.namespace,
|
|
660
|
+
type=PrefixTypeChoices.TYPE_CONTAINER,
|
|
661
|
+
)
|
|
654
662
|
self.child1.refresh_from_db()
|
|
655
663
|
self.child2.refresh_from_db()
|
|
656
664
|
self.assertEqual(self.child1.parent, self.parent)
|
|
@@ -687,13 +695,266 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
|
|
|
687
695
|
|
|
688
696
|
# Add /25 back in as a parent and assert that child1/child2 now have it as their parent, and
|
|
689
697
|
# /24 is its parent.
|
|
690
|
-
parent
|
|
698
|
+
parent = Prefix.objects.create(
|
|
699
|
+
prefix="101.102.0.0/25", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_CONTAINER
|
|
700
|
+
)
|
|
691
701
|
child1.refresh_from_db()
|
|
692
702
|
child2.refresh_from_db()
|
|
693
703
|
self.assertEqual(child1.parent, parent)
|
|
694
704
|
self.assertEqual(child2.parent, parent)
|
|
695
705
|
self.assertEqual(list(child1.ancestors()), [root, parent])
|
|
696
706
|
|
|
707
|
+
def test_reparenting_on_field_updates(self):
|
|
708
|
+
"""Test that reparenting occurs when network, prefix_length, etc. are updated."""
|
|
709
|
+
self.assertIsNone(self.root.parent)
|
|
710
|
+
self.assertEqual(self.parent.parent, self.root)
|
|
711
|
+
self.assertEqual(self.child1.parent, self.parent)
|
|
712
|
+
self.assertEqual(self.child2.parent, self.parent)
|
|
713
|
+
|
|
714
|
+
ip1 = IPAddress.objects.create(address="101.102.103.127/32", status=self.status, namespace=self.namespace)
|
|
715
|
+
ip2 = IPAddress.objects.create(address="101.102.103.128/32", status=self.status, namespace=self.namespace)
|
|
716
|
+
self.assertEqual(ip1.parent, self.parent)
|
|
717
|
+
self.assertEqual(ip2.parent, self.parent)
|
|
718
|
+
|
|
719
|
+
with self.subTest("Decrease prefix_length, gaining children"):
|
|
720
|
+
self.child1.prefix_length = 25
|
|
721
|
+
self.child1.save()
|
|
722
|
+
self.child1.refresh_from_db()
|
|
723
|
+
self.child2.refresh_from_db()
|
|
724
|
+
self.assertEqual(self.child2.parent, self.child1)
|
|
725
|
+
ip1.refresh_from_db()
|
|
726
|
+
self.assertEqual(ip1.parent, self.child1)
|
|
727
|
+
|
|
728
|
+
with self.subTest("Increase prefix_length, losing children"):
|
|
729
|
+
self.child1.prefix_length = 26
|
|
730
|
+
self.child1.save()
|
|
731
|
+
self.child1.refresh_from_db()
|
|
732
|
+
self.child2.refresh_from_db()
|
|
733
|
+
self.assertEqual(self.child2.parent, self.parent)
|
|
734
|
+
ip1.refresh_from_db()
|
|
735
|
+
self.assertEqual(ip1.parent, self.parent)
|
|
736
|
+
|
|
737
|
+
with self.subTest("Broaden prefix, becoming parent of former parent"):
|
|
738
|
+
self.parent.prefix = "101.0.0.0/8"
|
|
739
|
+
self.parent.save()
|
|
740
|
+
self.assertIsNone(self.parent.parent)
|
|
741
|
+
# Former root is now a child of parent
|
|
742
|
+
self.root.refresh_from_db()
|
|
743
|
+
self.assertEqual(self.root.parent, self.parent)
|
|
744
|
+
# Former children are now children of former root
|
|
745
|
+
self.child1.refresh_from_db()
|
|
746
|
+
self.assertEqual(self.child1.parent, self.root)
|
|
747
|
+
self.child2.refresh_from_db()
|
|
748
|
+
self.assertEqual(self.child2.parent, self.root)
|
|
749
|
+
ip1.refresh_from_db()
|
|
750
|
+
self.assertEqual(ip1.parent, self.root)
|
|
751
|
+
ip2.refresh_from_db()
|
|
752
|
+
self.assertEqual(ip2.parent, self.root)
|
|
753
|
+
|
|
754
|
+
with self.subTest("Narrow prefix, becoming child of former child"):
|
|
755
|
+
self.parent.prefix = "101.102.103.0/24"
|
|
756
|
+
self.parent.save()
|
|
757
|
+
self.assertEqual(self.parent.parent, self.root)
|
|
758
|
+
# Former root is now again root
|
|
759
|
+
self.root.refresh_from_db()
|
|
760
|
+
self.assertIsNone(self.root.parent)
|
|
761
|
+
# Former children are again children of parent
|
|
762
|
+
self.child1.refresh_from_db()
|
|
763
|
+
self.assertEqual(self.child1.parent, self.parent)
|
|
764
|
+
self.child2.refresh_from_db()
|
|
765
|
+
self.assertEqual(self.child2.parent, self.parent)
|
|
766
|
+
ip1.refresh_from_db()
|
|
767
|
+
self.assertEqual(ip1.parent, self.parent)
|
|
768
|
+
ip2.refresh_from_db()
|
|
769
|
+
self.assertEqual(ip2.parent, self.parent)
|
|
770
|
+
|
|
771
|
+
with self.subTest("Change former root on multiple dimensions"):
|
|
772
|
+
self.root.network = "101.102.103.0"
|
|
773
|
+
self.root.prefix_length = 25
|
|
774
|
+
self.root.save()
|
|
775
|
+
self.assertEqual(self.root.parent, self.parent)
|
|
776
|
+
self.parent.refresh_from_db()
|
|
777
|
+
self.assertEqual(self.parent.parent, None)
|
|
778
|
+
self.child1.refresh_from_db()
|
|
779
|
+
self.assertEqual(self.child1.parent, self.root)
|
|
780
|
+
self.child2.refresh_from_db()
|
|
781
|
+
self.assertEqual(self.child2.parent, self.root)
|
|
782
|
+
ip1.refresh_from_db()
|
|
783
|
+
self.assertEqual(ip1.parent, self.root)
|
|
784
|
+
ip2.refresh_from_db()
|
|
785
|
+
self.assertEqual(ip2.parent, self.parent)
|
|
786
|
+
|
|
787
|
+
with self.subTest("Reclaim root position"):
|
|
788
|
+
self.root.network = "101.0.0.0"
|
|
789
|
+
self.root.prefix_length = 8
|
|
790
|
+
self.root.save()
|
|
791
|
+
self.assertIsNone(self.root.parent)
|
|
792
|
+
self.parent.refresh_from_db()
|
|
793
|
+
self.assertEqual(self.parent.parent, self.root)
|
|
794
|
+
self.child1.refresh_from_db()
|
|
795
|
+
self.assertEqual(self.child1.parent, self.parent)
|
|
796
|
+
self.child2.refresh_from_db()
|
|
797
|
+
self.assertEqual(self.child2.parent, self.parent)
|
|
798
|
+
ip1.refresh_from_db()
|
|
799
|
+
self.assertEqual(ip1.parent, self.parent)
|
|
800
|
+
ip2.refresh_from_db()
|
|
801
|
+
self.assertEqual(ip2.parent, self.parent)
|
|
802
|
+
|
|
803
|
+
def test_clean_fails_if_would_orphan_ips(self):
|
|
804
|
+
"""Test that clean() fails if reparenting would orphan IPs."""
|
|
805
|
+
self.ip = IPAddress.objects.create(address="101.102.1.1/32", status=self.status, namespace=self.namespace)
|
|
806
|
+
self.assertEqual(self.ip.parent, self.root)
|
|
807
|
+
with self.assertRaises(ValidationError) as cm:
|
|
808
|
+
self.root.prefix = "102.103.0.0/16"
|
|
809
|
+
self.root.clean()
|
|
810
|
+
self.assertIn(
|
|
811
|
+
f"1 existing IP addresses (including {self.ip.host}) would no longer have a valid parent", str(cm.exception)
|
|
812
|
+
)
|
|
813
|
+
self.root.refresh_from_db()
|
|
814
|
+
self.ip2 = IPAddress.objects.create(address="101.102.1.2/32", status=self.status, namespace=self.namespace)
|
|
815
|
+
self.assertEqual(self.ip2.parent, self.root)
|
|
816
|
+
with self.assertRaises(ValidationError) as cm:
|
|
817
|
+
self.root.prefix = "102.103.0.0/16"
|
|
818
|
+
self.root.clean()
|
|
819
|
+
self.assertIn(
|
|
820
|
+
f"2 existing IP addresses (including {self.ip.host}) would no longer have a valid parent",
|
|
821
|
+
str(cm.exception),
|
|
822
|
+
)
|
|
823
|
+
|
|
824
|
+
def test_clean_fails_if_namespace_changed_and_vrfs_involved(self):
|
|
825
|
+
vrf = VRF.objects.create(name="VRF Red", namespace=self.namespace)
|
|
826
|
+
vrf.add_prefix(self.root)
|
|
827
|
+
|
|
828
|
+
new_namespace = Namespace.objects.exclude(id=self.namespace.id).first()
|
|
829
|
+
|
|
830
|
+
self.root.namespace = new_namespace
|
|
831
|
+
with self.assertRaises(ValidationError) as cm:
|
|
832
|
+
self.root.clean()
|
|
833
|
+
self.assertIn("Cannot move to a different Namespace while associated to VRFs", str(cm.exception))
|
|
834
|
+
|
|
835
|
+
vrf.remove_prefix(self.root)
|
|
836
|
+
self.root.clean()
|
|
837
|
+
|
|
838
|
+
vrf.add_prefix(self.parent)
|
|
839
|
+
with self.assertRaises(ValidationError) as cm:
|
|
840
|
+
self.root.clean()
|
|
841
|
+
self.assertIn(
|
|
842
|
+
"Cannot move to a different Namespace with descendant Prefixes associated to VRFs", str(cm.exception)
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
def test_namespace_change_success_updates_descendants_and_claims_new_children(self):
|
|
846
|
+
new_namespace = Namespace.objects.exclude(id=self.namespace.id).first()
|
|
847
|
+
new_catchall = Prefix.objects.create(prefix="0.0.0.0/0", status=self.status, namespace=new_namespace)
|
|
848
|
+
new_parent = Prefix.objects.create(prefix="101.102.200.0/24", status=self.status, namespace=new_namespace)
|
|
849
|
+
new_child = Prefix.objects.create(prefix="101.102.103.64/26", status=self.status, namespace=new_namespace)
|
|
850
|
+
new_grandchild = Prefix.objects.create(prefix="101.102.103.0/27", status=self.status, namespace=new_namespace)
|
|
851
|
+
new_ip = IPAddress.objects.create(address="101.102.150.200/32", status=self.status, namespace=new_namespace)
|
|
852
|
+
|
|
853
|
+
# Before:
|
|
854
|
+
# self.namespace
|
|
855
|
+
# self.root 101.102.0.0/16
|
|
856
|
+
# self.parent 101.102.103.0/24
|
|
857
|
+
# self.child1 101.102.103.0/26
|
|
858
|
+
# self.child2 101.102.103.104/32
|
|
859
|
+
# new_namespace
|
|
860
|
+
# new_catchall 0.0.0.0/0
|
|
861
|
+
# new_grandchild 101.102.103.0/27
|
|
862
|
+
# new_child 101.102.103.64/26
|
|
863
|
+
# new_ip 101.102.150.200/32
|
|
864
|
+
# new_parent 101.102.200.0/24
|
|
865
|
+
#
|
|
866
|
+
# After:
|
|
867
|
+
# new_namespace
|
|
868
|
+
# new_catchall 0.0.0.0/0
|
|
869
|
+
# self.root 101.102.0.0/16
|
|
870
|
+
# self.parent 101.102.103.0/24
|
|
871
|
+
# self.child1 101.102.103.0/26
|
|
872
|
+
# new_grandchild 101.102.103.0/27
|
|
873
|
+
# new_child 101.102.103.64/26
|
|
874
|
+
# self.child2 101.102.103.104/32
|
|
875
|
+
# new_ip 101.102.150.200/32
|
|
876
|
+
# new_parent 101.102.200.0/24
|
|
877
|
+
|
|
878
|
+
self.root.namespace = new_namespace
|
|
879
|
+
self.root.save()
|
|
880
|
+
self.assertEqual(self.root.namespace, new_namespace)
|
|
881
|
+
self.assertEqual(self.root.parent, new_catchall) # automatically updated
|
|
882
|
+
self.parent.refresh_from_db()
|
|
883
|
+
self.assertEqual(self.parent.namespace, new_namespace) # automatically updated
|
|
884
|
+
self.assertEqual(self.parent.parent, self.root) # unchanged
|
|
885
|
+
self.child1.refresh_from_db()
|
|
886
|
+
self.assertEqual(self.child1.namespace, new_namespace) # automatically updated
|
|
887
|
+
self.assertEqual(self.child1.parent, self.parent) # unchanged
|
|
888
|
+
self.child2.refresh_from_db()
|
|
889
|
+
self.assertEqual(self.child2.namespace, new_namespace) # automatically updated
|
|
890
|
+
self.assertEqual(self.child2.parent, new_child) # automatically updated
|
|
891
|
+
new_parent.refresh_from_db()
|
|
892
|
+
self.assertEqual(new_parent.namespace, new_namespace) # unchanged
|
|
893
|
+
self.assertEqual(new_parent.parent, self.root) # automatically updated
|
|
894
|
+
new_child.refresh_from_db()
|
|
895
|
+
self.assertEqual(new_child.namespace, new_namespace) # unchanged
|
|
896
|
+
self.assertEqual(new_child.parent, self.parent) # automatically updated
|
|
897
|
+
new_grandchild.refresh_from_db()
|
|
898
|
+
self.assertEqual(new_grandchild.namespace, new_namespace) # unchanged
|
|
899
|
+
self.assertEqual(new_grandchild.parent, self.child1) # automatically updated
|
|
900
|
+
new_ip.refresh_from_db()
|
|
901
|
+
self.assertEqual(new_ip.parent, self.root)
|
|
902
|
+
|
|
903
|
+
def test_namespace_change_results_in_merge_collisions(self):
|
|
904
|
+
new_namespace = Namespace.objects.exclude(id=self.namespace.id).first()
|
|
905
|
+
new_root = Prefix.objects.create(prefix="101.102.0.0/16", status=self.status, namespace=new_namespace)
|
|
906
|
+
|
|
907
|
+
self.root.namespace = new_namespace
|
|
908
|
+
with self.assertRaises(IntegrityError):
|
|
909
|
+
self.root.save()
|
|
910
|
+
self.root.refresh_from_db()
|
|
911
|
+
self.assertEqual(self.root.namespace, self.namespace)
|
|
912
|
+
self.parent.refresh_from_db()
|
|
913
|
+
self.assertEqual(self.parent.namespace, self.namespace)
|
|
914
|
+
self.child1.refresh_from_db()
|
|
915
|
+
self.assertEqual(self.child1.namespace, self.namespace)
|
|
916
|
+
self.child2.refresh_from_db()
|
|
917
|
+
self.assertEqual(self.child2.namespace, self.namespace)
|
|
918
|
+
|
|
919
|
+
new_root.delete()
|
|
920
|
+
new_parent = Prefix.objects.create(prefix="101.102.103.0/24", status=self.status, namespace=new_namespace)
|
|
921
|
+
|
|
922
|
+
self.root.namespace = new_namespace
|
|
923
|
+
with self.assertRaises(IntegrityError):
|
|
924
|
+
self.root.save()
|
|
925
|
+
self.root.refresh_from_db()
|
|
926
|
+
self.assertEqual(self.root.namespace, self.namespace)
|
|
927
|
+
self.parent.refresh_from_db()
|
|
928
|
+
self.assertEqual(self.parent.namespace, self.namespace)
|
|
929
|
+
self.child1.refresh_from_db()
|
|
930
|
+
self.assertEqual(self.child1.namespace, self.namespace)
|
|
931
|
+
self.child2.refresh_from_db()
|
|
932
|
+
self.assertEqual(self.child2.namespace, self.namespace)
|
|
933
|
+
|
|
934
|
+
new_parent.delete()
|
|
935
|
+
|
|
936
|
+
existing_ip = IPAddress.objects.create(address="101.102.103.1/32", status=self.status, namespace=self.namespace)
|
|
937
|
+
new_prefix = Prefix.objects.create(prefix="0.0.0.0/0", status=self.status, namespace=new_namespace)
|
|
938
|
+
new_ip = IPAddress.objects.create(address="101.102.103.1/32", status=self.status, namespace=new_namespace)
|
|
939
|
+
self.assertEqual(new_ip.parent, new_prefix)
|
|
940
|
+
|
|
941
|
+
self.root.namespace = new_namespace
|
|
942
|
+
with self.assertRaises(IntegrityError):
|
|
943
|
+
self.root.save()
|
|
944
|
+
self.root.refresh_from_db()
|
|
945
|
+
self.assertIsNone(self.root.parent)
|
|
946
|
+
self.assertEqual(self.root.namespace, self.namespace)
|
|
947
|
+
self.parent.refresh_from_db()
|
|
948
|
+
self.assertEqual(self.parent.namespace, self.namespace)
|
|
949
|
+
self.child1.refresh_from_db()
|
|
950
|
+
self.assertEqual(self.child1.namespace, self.namespace)
|
|
951
|
+
self.child2.refresh_from_db()
|
|
952
|
+
self.assertEqual(self.child2.namespace, self.namespace)
|
|
953
|
+
existing_ip.refresh_from_db()
|
|
954
|
+
self.assertEqual(existing_ip.parent, self.child1)
|
|
955
|
+
new_ip.refresh_from_db()
|
|
956
|
+
self.assertEqual(new_ip.parent, new_prefix)
|
|
957
|
+
|
|
697
958
|
def test_descendants(self):
|
|
698
959
|
prefixes = (
|
|
699
960
|
Prefix.objects.create(
|
|
@@ -901,8 +1162,10 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
|
|
|
901
1162
|
self.assertEqual(slash25.get_utilization(), (4, 128))
|
|
902
1163
|
|
|
903
1164
|
# When the pool does not overlap with broadcast or network address, the denominator decrements by 2
|
|
904
|
-
pool.
|
|
905
|
-
pool.
|
|
1165
|
+
pool.delete()
|
|
1166
|
+
pool = Prefix.objects.create(
|
|
1167
|
+
prefix="10.0.0.132/30", type=PrefixTypeChoices.TYPE_POOL, status=self.status, namespace=self.namespace
|
|
1168
|
+
)
|
|
906
1169
|
self.assertEqual(slash25.get_utilization(), (4, 126))
|
|
907
1170
|
|
|
908
1171
|
# Further distinguishing between get_child_ips() and get_all_ips():
|
|
@@ -1026,86 +1289,10 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
|
|
|
1026
1289
|
Prefix.objects.create(
|
|
1027
1290
|
prefix="11.0.0.0/24", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_NETWORK
|
|
1028
1291
|
)
|
|
1029
|
-
# 3.0 TODO: replace with the commented below once type enforcement is enabled
|
|
1030
|
-
# pool_prefix = Prefix.objects.create(
|
|
1031
1292
|
Prefix.objects.create(
|
|
1032
1293
|
prefix="12.0.0.0/24", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
|
|
1033
1294
|
)
|
|
1034
1295
|
|
|
1035
|
-
# 3.0 TODO: uncomment the below tests once type enforcement is enabled
|
|
1036
|
-
|
|
1037
|
-
# with self.assertRaises(ValidationError, msg="Network prefix parent cannot be a network"):
|
|
1038
|
-
# Prefix.objects.create(
|
|
1039
|
-
# prefix="11.0.0.0/30", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_NETWORK
|
|
1040
|
-
# )
|
|
1041
|
-
|
|
1042
|
-
# with self.assertRaises(ValidationError, msg="Network prefix parent cannot be a pool"):
|
|
1043
|
-
# Prefix.objects.create(
|
|
1044
|
-
# prefix="12.0.0.0/30", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_NETWORK
|
|
1045
|
-
# )
|
|
1046
|
-
|
|
1047
|
-
# with self.assertRaises(ValidationError, msg="Container prefix parent cannot be a network"):
|
|
1048
|
-
# Prefix.objects.create(
|
|
1049
|
-
# prefix="11.0.0.0/30", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_CONTAINER
|
|
1050
|
-
# )
|
|
1051
|
-
|
|
1052
|
-
# with self.assertRaises(ValidationError, msg="Container prefix parent cannot be a pool"):
|
|
1053
|
-
# Prefix.objects.create(
|
|
1054
|
-
# prefix="12.0.0.0/30", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_CONTAINER
|
|
1055
|
-
# )
|
|
1056
|
-
|
|
1057
|
-
# with self.assertRaises(ValidationError, msg="Pool prefix parent cannot be a container"):
|
|
1058
|
-
# Prefix.objects.create(
|
|
1059
|
-
# prefix="10.0.0.0/30", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
|
|
1060
|
-
# )
|
|
1061
|
-
|
|
1062
|
-
# with self.assertRaises(ValidationError, msg="Pool prefix parent cannot be a pool"):
|
|
1063
|
-
# Prefix.objects.create(
|
|
1064
|
-
# prefix="12.0.0.0/30", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
|
|
1065
|
-
# )
|
|
1066
|
-
|
|
1067
|
-
# with self.assertRaises(
|
|
1068
|
-
# ValidationError, msg="Test that an invalid parent cannot be created (network parenting container)"
|
|
1069
|
-
# ):
|
|
1070
|
-
# Prefix.objects.create(
|
|
1071
|
-
# prefix="10.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_NETWORK
|
|
1072
|
-
# )
|
|
1073
|
-
|
|
1074
|
-
# with self.assertRaises(
|
|
1075
|
-
# ValidationError, msg="Test that an invalid parent cannot be created (pool parenting container)"
|
|
1076
|
-
# ):
|
|
1077
|
-
# Prefix.objects.create(
|
|
1078
|
-
# prefix="10.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
|
|
1079
|
-
# )
|
|
1080
|
-
|
|
1081
|
-
# with self.assertRaises(
|
|
1082
|
-
# ValidationError, msg="Test that an invalid parent cannot be created (network parenting network)"
|
|
1083
|
-
# ):
|
|
1084
|
-
# Prefix.objects.create(
|
|
1085
|
-
# prefix="11.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_NETWORK
|
|
1086
|
-
# )
|
|
1087
|
-
|
|
1088
|
-
# with self.assertRaises(
|
|
1089
|
-
# ValidationError, msg="Test that an invalid parent cannot be created (pool parenting network)"
|
|
1090
|
-
# ):
|
|
1091
|
-
# Prefix.objects.create(
|
|
1092
|
-
# prefix="11.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
|
|
1093
|
-
# )
|
|
1094
|
-
|
|
1095
|
-
# with self.assertRaises(
|
|
1096
|
-
# ValidationError, msg="Test that an invalid parent cannot be created (container parenting pool)"
|
|
1097
|
-
# ):
|
|
1098
|
-
# Prefix.objects.create(
|
|
1099
|
-
# prefix="12.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_CONTAINER
|
|
1100
|
-
# )
|
|
1101
|
-
|
|
1102
|
-
# with self.assertRaises(
|
|
1103
|
-
# ValidationError, msg="Test that an invalid parent cannot be created (pool parenting pool)"
|
|
1104
|
-
# ):
|
|
1105
|
-
# Prefix.objects.create(
|
|
1106
|
-
# prefix="12.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
|
|
1107
|
-
# )
|
|
1108
|
-
|
|
1109
1296
|
with self.subTest("Test that valid parents can be created"):
|
|
1110
1297
|
Prefix.objects.create(
|
|
1111
1298
|
prefix="12.0.0.0/16", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_NETWORK
|
|
@@ -1125,14 +1312,6 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
|
|
|
1125
1312
|
prefix="10.0.0.0/26", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_CONTAINER
|
|
1126
1313
|
)
|
|
1127
1314
|
|
|
1128
|
-
# 3.0 TODO: uncomment once type enforcement is enabled
|
|
1129
|
-
# with self.assertRaises(
|
|
1130
|
-
# ValidationError,
|
|
1131
|
-
# msg="Test that modifying a prefix's type fails if it would result in an invalid parent/child relationship",
|
|
1132
|
-
# ):
|
|
1133
|
-
# pool_prefix.type = PrefixTypeChoices.TYPE_NETWORK
|
|
1134
|
-
# pool_prefix.validated_save()
|
|
1135
|
-
|
|
1136
1315
|
with self.subTest(
|
|
1137
1316
|
"Test that modifying a prefix's type is allowed if it does not create an invalid relationship"
|
|
1138
1317
|
):
|
|
@@ -1159,13 +1338,6 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
|
|
|
1159
1338
|
prefix="10.0.0.0/26", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
|
|
1160
1339
|
)
|
|
1161
1340
|
|
|
1162
|
-
# 3.0 TODO: uncomment once type enforcement is enabled
|
|
1163
|
-
# with self.assertRaises(
|
|
1164
|
-
# ProtectedError,
|
|
1165
|
-
# msg="Test that deleting a network prefix that would make a pool prefix's parent a container raises a ProtectedError",
|
|
1166
|
-
# ):
|
|
1167
|
-
# network.delete()
|
|
1168
|
-
|
|
1169
1341
|
with self.subTest("Test that deleting a parent prefix properly reparents the child prefixes"):
|
|
1170
1342
|
container.delete()
|
|
1171
1343
|
root.refresh_from_db()
|
|
@@ -1178,18 +1350,11 @@ class TestPrefix(ModelTestCases.BaseModelTestCase):
|
|
|
1178
1350
|
ip = IPAddress.objects.create(address="10.0.0.1/32", status=self.status, namespace=namespace)
|
|
1179
1351
|
|
|
1180
1352
|
with self.subTest("Test that deleting a pool prefix containing IPs succeeds"):
|
|
1181
|
-
self.assertEqual(ip.parent, pool)
|
|
1353
|
+
self.assertEqual(ip.parent, pool)
|
|
1182
1354
|
pool.delete()
|
|
1183
1355
|
ip.refresh_from_db()
|
|
1184
1356
|
self.assertEqual(ip.parent, network)
|
|
1185
1357
|
|
|
1186
|
-
# 3.0 TODO: uncomment once type enforcement is enabled
|
|
1187
|
-
# with self.assertRaises(
|
|
1188
|
-
# ProtectedError,
|
|
1189
|
-
# msg="Test that deleting a network prefix that would make an IP's parent a container raises a ProtectedError",
|
|
1190
|
-
# ):
|
|
1191
|
-
# network.delete()
|
|
1192
|
-
|
|
1193
1358
|
with self.subTest("Test that deleting the root prefix succeeds"):
|
|
1194
1359
|
root.delete()
|
|
1195
1360
|
network.refresh_from_db()
|
|
@@ -1433,17 +1598,11 @@ class TestIPAddress(ModelTestCases.BaseModelTestCase):
|
|
|
1433
1598
|
prefix="12.0.0.0/24", status=self.status, namespace=namespace, type=PrefixTypeChoices.TYPE_POOL
|
|
1434
1599
|
)
|
|
1435
1600
|
|
|
1436
|
-
# 3.0 TODO: uncomment once type enforcement is enabled
|
|
1437
|
-
# with self.assertRaises(ValidationError, msg="IP Address parent cannot be a container"):
|
|
1438
|
-
# IPAddress.objects.create(address="10.0.0.1/32", status=self.status, namespace=namespace)
|
|
1439
|
-
|
|
1440
|
-
# with self.assertRaises(Prefix.DoesNotExist, msg="IP Address parent cannot be a pool"):
|
|
1441
|
-
# IPAddress.objects.create(address="12.0.0.1/32", status=self.status, namespace=namespace)
|
|
1442
|
-
|
|
1443
1601
|
with self.assertRaises(ValidationError) as err:
|
|
1444
1602
|
IPAddress.objects.create(address="13.0.0.1/32", status=self.status, namespace=namespace)
|
|
1445
1603
|
self.assertEqual(
|
|
1446
|
-
err.exception.message_dict["namespace"][0],
|
|
1604
|
+
err.exception.message_dict["namespace"][0],
|
|
1605
|
+
"No suitable parent Prefix for 13.0.0.1 exists in Namespace test_parenting_constraints",
|
|
1447
1606
|
)
|
|
1448
1607
|
|
|
1449
1608
|
with self.subTest("Test that IP address can be assigned to a valid parent"):
|
|
@@ -1483,7 +1642,7 @@ class TestIPAddress(ModelTestCases.BaseModelTestCase):
|
|
|
1483
1642
|
self.assertIn("namespace", err.exception.message_dict)
|
|
1484
1643
|
self.assertEqual(
|
|
1485
1644
|
err.exception.message_dict["namespace"][0],
|
|
1486
|
-
"No suitable parent Prefix exists in
|
|
1645
|
+
"No suitable parent Prefix for 1976:2023::1 exists in Namespace Global",
|
|
1487
1646
|
)
|
|
1488
1647
|
|
|
1489
1648
|
# Appropriate parent exists in the default namespace --> no error
|
|
@@ -1523,6 +1682,15 @@ class TestIPAddress(ModelTestCases.BaseModelTestCase):
|
|
|
1523
1682
|
ip.validated_save()
|
|
1524
1683
|
self.assertEqual(ip.parent, prefixes[0])
|
|
1525
1684
|
|
|
1685
|
+
def test_change_host(self):
|
|
1686
|
+
ip = IPAddress.objects.create(address="192.0.2.1/32", status=self.status, namespace=self.namespace)
|
|
1687
|
+
self.assertEqual(ip.parent, self.prefix)
|
|
1688
|
+
|
|
1689
|
+
ip.host = "192.168.1.1"
|
|
1690
|
+
with self.assertRaises(ValidationError) as cm:
|
|
1691
|
+
ip.validated_save()
|
|
1692
|
+
self.assertIn("Host address cannot be changed once created", str(cm.exception))
|
|
1693
|
+
|
|
1526
1694
|
def test_varbinary_ip_fields_with_empty_values_do_not_violate_not_null_constrains(self):
|
|
1527
1695
|
# Assert that an error is triggered when the host is not provided.
|
|
1528
1696
|
# Initially, VarbinaryIPField fields with None values are stored as the binary representation of b'',
|