nautobot 3.0.0a3__py3-none-any.whl → 3.0.0rc1__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.
- nautobot/apps/choices.py +4 -0
- nautobot/apps/ui.py +4 -0
- nautobot/apps/utils.py +8 -0
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +0 -3
- nautobot/circuits/views.py +6 -2
- nautobot/core/api/serializers.py +1 -1
- nautobot/core/api/urls.py +1 -0
- nautobot/core/api/views.py +4 -0
- nautobot/core/choices.py +1 -1
- nautobot/core/cli/bootstrap_v3_to_v5.py +36 -13
- nautobot/core/cli/migrate_deprecated_templates.py +36 -9
- nautobot/core/filters.py +4 -0
- nautobot/core/forms/__init__.py +2 -0
- nautobot/core/forms/widgets.py +21 -2
- nautobot/core/jobs/__init__.py +56 -0
- nautobot/core/management/commands/generate_test_data.py +3 -3
- nautobot/core/models/__init__.py +11 -0
- nautobot/core/models/utils.py +1 -1
- nautobot/core/settings.py +17 -7
- nautobot/core/settings.yaml +4 -26
- nautobot/core/templates/admin/base.html +1 -2
- nautobot/core/templates/admin/change_list.html +9 -12
- nautobot/core/templates/base_django.html +1 -2
- nautobot/core/templates/components/panel/header_extra_content_table.html +1 -1
- nautobot/core/templates/components/tab/content_wrapper.html +4 -4
- nautobot/core/templates/echarts/echarts.html +21 -8
- nautobot/core/templates/generic/object_bulk_create.html +2 -2
- nautobot/core/templates/generic/object_bulk_delete.html +1 -1
- nautobot/core/templates/generic/object_bulk_edit.html +1 -1
- nautobot/core/templates/generic/object_bulk_import.html +1 -1
- nautobot/core/templates/generic/object_delete.html +1 -1
- nautobot/core/templates/generic/object_detail.html +1 -1
- nautobot/core/templates/generic/object_edit.html +1 -1
- nautobot/core/templates/generic/object_retrieve.html +2 -2
- nautobot/core/templates/graphene/graphiql.html +0 -1
- nautobot/core/templates/inc/footer.html +3 -1
- nautobot/core/templates/inc/header.html +10 -0
- nautobot/core/templates/inc/media.html +14 -0
- nautobot/core/templates/inc/nav_menu.html +1 -8
- nautobot/core/templates/inc/object_details_advanced_panel.html +2 -2
- nautobot/core/templates/nautobot_config.py.j2 +0 -6
- nautobot/core/templates/rest_framework/api.html +103 -2
- nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +33 -0
- nautobot/core/templates/utilities/theme_preview.html +3 -0
- nautobot/core/templates/widgets/number_input_with_choices.html +44 -0
- nautobot/core/templatetags/helpers.py +24 -12
- nautobot/core/testing/integration.py +24 -13
- nautobot/core/testing/utils.py +18 -4
- nautobot/core/testing/views.py +104 -17
- nautobot/core/tests/integration/test_filters.py +48 -11
- nautobot/core/tests/integration/test_theme.py +22 -21
- nautobot/core/tests/nautobot_config.py +3 -0
- nautobot/core/tests/runner.py +1 -2
- nautobot/core/tests/test_breadcrumbs.py +21 -21
- nautobot/core/tests/test_jobs.py +73 -6
- nautobot/core/tests/test_renderers.py +59 -0
- nautobot/core/tests/test_settings_schema.py +1 -0
- nautobot/core/tests/test_templatetags_helpers.py +9 -0
- nautobot/core/tests/test_titles.py +0 -16
- nautobot/core/tests/test_ui.py +122 -3
- nautobot/core/tests/test_utils.py +41 -1
- nautobot/core/ui/breadcrumbs.py +68 -17
- nautobot/core/ui/bulk_buttons.py +1 -1
- nautobot/core/ui/choices.py +49 -65
- nautobot/core/ui/echarts.py +15 -20
- nautobot/core/ui/object_detail.py +54 -46
- nautobot/core/ui/titles.py +3 -6
- nautobot/core/urls.py +8 -8
- nautobot/core/utils/filtering.py +11 -1
- nautobot/core/utils/lookup.py +46 -0
- nautobot/core/views/mixins.py +31 -20
- nautobot/core/views/renderers.py +2 -3
- nautobot/data_validation/migrations/0002_data_migration_from_app.py +3 -2
- nautobot/dcim/api/serializers.py +3 -0
- nautobot/dcim/choices.py +49 -0
- nautobot/dcim/constants.py +7 -0
- nautobot/dcim/factory.py +1 -1
- nautobot/dcim/filters.py +13 -1
- nautobot/dcim/forms.py +89 -3
- nautobot/dcim/migrations/0075_interface_duplex_interface_speed_and_more.py +32 -0
- nautobot/dcim/migrations/{0075_add_deviceclusterassignment.py → 0076_add_deviceclusterassignment.py} +1 -1
- nautobot/dcim/migrations/{0076_device_cluster_to_clusters_data_migration.py → 0077_device_cluster_to_clusters_data_migration.py} +1 -1
- nautobot/dcim/migrations/{0077_remove_device_cluster.py → 0078_remove_device_cluster.py} +1 -1
- nautobot/dcim/migrations/{0078_remove_device_location_tenant_name_uniqueness.py → 0079_remove_device_location_tenant_name_uniqueness.py} +1 -1
- nautobot/dcim/migrations/{0079_device_name_data_migration.py → 0080_device_name_data_migration.py} +1 -1
- nautobot/dcim/migrations/0081_alter_device_device_redundancy_group_priority_and_more.py +25 -0
- nautobot/dcim/models/device_component_templates.py +33 -1
- nautobot/dcim/models/device_components.py +22 -1
- nautobot/dcim/models/devices.py +17 -4
- nautobot/dcim/tables/devices.py +15 -0
- nautobot/dcim/tables/devicetypes.py +8 -1
- nautobot/dcim/tables/racks.py +0 -2
- nautobot/dcim/tables/template_code.py +1 -1
- nautobot/dcim/templates/dcim/cable_trace.html +0 -2
- nautobot/dcim/templates/dcim/consoleport.html +1 -1
- nautobot/dcim/templates/dcim/consoleserverport.html +1 -1
- nautobot/dcim/templates/dcim/devicebay.html +1 -1
- nautobot/dcim/templates/dcim/frontport.html +1 -1
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +1 -1
- nautobot/dcim/templates/dcim/inc/rack_elevation.html +1 -1
- nautobot/dcim/templates/dcim/interface.html +9 -1
- nautobot/dcim/templates/dcim/interface_edit.html +2 -0
- nautobot/dcim/templates/dcim/inventoryitem.html +1 -1
- nautobot/dcim/templates/dcim/module_consoleports.html +1 -1
- nautobot/dcim/templates/dcim/module_consoleserverports.html +1 -1
- nautobot/dcim/templates/dcim/module_frontports.html +1 -1
- nautobot/dcim/templates/dcim/module_interfaces.html +1 -1
- nautobot/dcim/templates/dcim/module_modulebays.html +1 -1
- nautobot/dcim/templates/dcim/module_poweroutlets.html +1 -1
- nautobot/dcim/templates/dcim/module_powerports.html +1 -1
- nautobot/dcim/templates/dcim/module_rearports.html +1 -1
- nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
- nautobot/dcim/templates/dcim/poweroutlet.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +1 -1
- nautobot/dcim/templates/dcim/rack_elevation_list.html +1 -1
- nautobot/dcim/templates/dcim/rack_retrieve.html +0 -11
- nautobot/dcim/templates/dcim/rearport.html +1 -1
- nautobot/dcim/templates/dcim/trace/cable.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_update.html +1 -1
- nautobot/dcim/tests/integration/test_controller.py +3 -6
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +1 -5
- nautobot/dcim/tests/integration/test_create_device.py +0 -2
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +1 -3
- nautobot/dcim/tests/integration/test_fileinputpicker.py +6 -10
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +0 -2
- nautobot/dcim/tests/integration/test_module_bay_position.py +3 -4
- nautobot/dcim/tests/test_api.py +186 -6
- nautobot/dcim/tests/test_filters.py +43 -1
- nautobot/dcim/tests/test_forms.py +110 -8
- nautobot/dcim/tests/test_graphql.py +44 -1
- nautobot/dcim/tests/test_models.py +265 -0
- nautobot/dcim/tests/test_tables.py +160 -0
- nautobot/dcim/tests/test_views.py +69 -7
- nautobot/dcim/views.py +232 -126
- nautobot/extras/api/views.py +51 -44
- nautobot/extras/datasources/git.py +3 -1
- nautobot/extras/filters.py +19 -2
- nautobot/extras/forms/forms.py +9 -2
- nautobot/extras/jobs.py +2 -0
- nautobot/extras/jobs_ui.py +4 -3
- nautobot/extras/management/__init__.py +2 -0
- nautobot/extras/management/commands/refresh_dynamic_group_member_caches.py +4 -1
- nautobot/extras/migrations/0131_configcontext_device_families.py +18 -0
- nautobot/extras/models/approvals.py +11 -1
- nautobot/extras/models/change_logging.py +4 -0
- nautobot/extras/models/jobs.py +1 -3
- nautobot/extras/models/models.py +10 -2
- nautobot/extras/plugins/marketplace_manifest.yml +49 -1
- nautobot/extras/plugins/views.py +0 -5
- nautobot/extras/querysets.py +8 -0
- nautobot/extras/tables.py +12 -0
- nautobot/extras/templates/django_ajax_tables/ajax_wrapper.html +2 -0
- nautobot/extras/templates/extras/configcontext_update.html +1 -0
- nautobot/extras/templates/extras/dynamicgroup_update.html +1 -1
- nautobot/extras/templates/extras/objectchange_retrieve.html +0 -2
- nautobot/extras/templates/extras/plugin_detail.html +3 -3
- nautobot/extras/templates/extras/secret_create.html +1 -1
- nautobot/extras/tests/integration/test_computedfields.py +8 -9
- nautobot/extras/tests/integration/test_customfields.py +1 -3
- nautobot/extras/tests/integration/test_dynamicgroups.py +7 -8
- nautobot/extras/tests/integration/test_relationships.py +0 -2
- nautobot/extras/tests/test_api.py +63 -0
- nautobot/extras/tests/test_changelog.py +24 -2
- nautobot/extras/tests/test_filters.py +36 -3
- nautobot/extras/tests/test_models.py +38 -2
- nautobot/extras/tests/test_utils.py +3 -4
- nautobot/extras/tests/test_views.py +22 -83
- nautobot/extras/urls.py +0 -14
- nautobot/extras/views.py +83 -52
- nautobot/ipam/filters.py +26 -0
- nautobot/ipam/tables.py +6 -0
- nautobot/ipam/templates/ipam/namespace_ip_addresses.html +1 -1
- nautobot/ipam/templates/ipam/namespace_prefixes.html +1 -1
- nautobot/ipam/templates/ipam/namespace_vrfs.html +1 -1
- nautobot/ipam/tests/test_filters.py +26 -1
- nautobot/ipam/tests/test_models.py +1 -1
- nautobot/ipam/views.py +9 -7
- nautobot/load_balancers/__init__.py +0 -0
- nautobot/load_balancers/api/__init__.py +1 -0
- nautobot/load_balancers/api/serializers.py +75 -0
- nautobot/load_balancers/api/urls.py +23 -0
- nautobot/load_balancers/api/views.py +61 -0
- nautobot/load_balancers/apps.py +17 -0
- nautobot/load_balancers/choices.py +167 -0
- nautobot/load_balancers/filters.py +225 -0
- nautobot/load_balancers/forms.py +532 -0
- nautobot/load_balancers/management/commands/__init__.py +0 -0
- nautobot/load_balancers/management/commands/generate_load_balancer_models_test_data.py +38 -0
- nautobot/load_balancers/migrations/0001_initial.py +465 -0
- nautobot/load_balancers/migrations/0002_create_default_statuses_pool_members.py +31 -0
- nautobot/load_balancers/migrations/__init__.py +0 -0
- nautobot/load_balancers/models.py +423 -0
- nautobot/load_balancers/navigation.py +80 -0
- nautobot/load_balancers/tables.py +255 -0
- nautobot/load_balancers/tests/__init__.py +474 -0
- nautobot/load_balancers/tests/test_api.py +353 -0
- nautobot/load_balancers/tests/test_filters.py +134 -0
- nautobot/load_balancers/tests/test_forms.py +266 -0
- nautobot/load_balancers/tests/test_models.py +195 -0
- nautobot/load_balancers/tests/test_views.py +229 -0
- nautobot/load_balancers/urls.py +17 -0
- nautobot/load_balancers/views.py +248 -0
- nautobot/project-static/dist/css/github-dark.min.css +10 -0
- nautobot/project-static/dist/css/github.min.css +10 -0
- nautobot/project-static/dist/css/nautobot.css +1 -11
- nautobot/project-static/dist/css/nautobot.css.map +1 -1
- nautobot/project-static/dist/js/libraries.js +1 -1
- nautobot/project-static/dist/js/libraries.js.map +1 -1
- nautobot/project-static/dist/js/nautobot.js +1 -1
- nautobot/project-static/dist/js/nautobot.js.map +1 -1
- nautobot/project-static/js/forms.js +13 -0
- nautobot/project-static/nautobot-icons/bus-globe.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-shield-check.svg +3 -0
- nautobot/project-static/nautobot-icons/bus-shield.svg +3 -0
- nautobot/ui/package-lock.json +87 -4
- nautobot/ui/package.json +2 -1
- nautobot/ui/src/js/nautobot.js +0 -1
- nautobot/ui/src/js/select2.js +53 -2
- nautobot/ui/src/scss/nautobot.scss +51 -2
- nautobot/ui/webpack.config.js +13 -0
- nautobot/users/templates/users/preferences.html +11 -2
- nautobot/virtualization/filters.py +6 -1
- nautobot/virtualization/tests/test_filters.py +10 -1
- nautobot/virtualization/tests/test_models.py +1 -0
- nautobot/virtualization/views.py +4 -1
- nautobot/vpn/factory.py +25 -15
- nautobot/vpn/filters.py +1 -0
- nautobot/vpn/forms.py +1 -0
- nautobot/vpn/migrations/0001_initial.py +1 -1
- nautobot/vpn/models.py +16 -8
- nautobot/vpn/tables.py +5 -2
- nautobot/vpn/tests/test_api.py +0 -5
- nautobot/vpn/tests/test_forms.py +1 -2
- nautobot/vpn/tests/test_models.py +57 -7
- nautobot/vpn/tests/test_views.py +22 -3
- nautobot/vpn/views.py +78 -20
- {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/METADATA +4 -4
- {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/RECORD +243 -352
- nautobot/circuits/templates/circuits/circuit.html +0 -2
- nautobot/circuits/templates/circuits/circuit_edit.html +0 -2
- nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/circuit_update.html +0 -1
- nautobot/circuits/templates/circuits/circuittermination.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_edit.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/circuittermination_update.html +0 -1
- nautobot/circuits/templates/circuits/circuittype.html +0 -2
- nautobot/circuits/templates/circuits/circuittype_retrieve.html +0 -2
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +0 -85
- nautobot/circuits/templates/circuits/provider.html +0 -2
- nautobot/circuits/templates/circuits/provider_edit.html +0 -2
- nautobot/circuits/templates/circuits/provider_retrieve.html +0 -1
- nautobot/circuits/templates/circuits/provider_update.html +0 -1
- nautobot/circuits/templates/circuits/providernetwork.html +0 -2
- nautobot/circuits/templates/circuits/providernetwork_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudaccount_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudnetwork_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudresourcetype_retrieve.html +0 -2
- nautobot/cloud/templates/cloud/cloudservice_retrieve.html +0 -2
- nautobot/core/templates/buttons/import.html +0 -9
- nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +0 -1
- nautobot/dcim/templates/dcim/cable.html +0 -2
- nautobot/dcim/templates/dcim/cable_edit.html +0 -2
- nautobot/dcim/templates/dcim/controller/base.html +0 -2
- nautobot/dcim/templates/dcim/controller_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +0 -2
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/device/base.html +0 -2
- nautobot/dcim/templates/dcim/device/consoleports.html +0 -2
- nautobot/dcim/templates/dcim/device/consoleserverports.html +0 -2
- nautobot/dcim/templates/dcim/device/devicebays.html +0 -2
- nautobot/dcim/templates/dcim/device/frontports.html +0 -2
- nautobot/dcim/templates/dcim/device/interfaces.html +0 -2
- nautobot/dcim/templates/dcim/device/inventory.html +0 -2
- nautobot/dcim/templates/dcim/device/modulebays.html +0 -2
- nautobot/dcim/templates/dcim/device/poweroutlets.html +0 -2
- nautobot/dcim/templates/dcim/device/powerports.html +0 -2
- nautobot/dcim/templates/dcim/device/rearports.html +0 -2
- nautobot/dcim/templates/dcim/device/wireless.html +0 -2
- nautobot/dcim/templates/dcim/device_component.html +0 -2
- nautobot/dcim/templates/dcim/device_edit.html +0 -2
- nautobot/dcim/templates/dcim/devicefamily_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/deviceredundancygroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/devicetype.html +0 -2
- nautobot/dcim/templates/dcim/devicetype_edit.html +0 -2
- nautobot/dcim/templates/dcim/devicetype_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/inc/device_napalm_tabs.html +0 -1
- nautobot/dcim/templates/dcim/interfaceredundancygroup_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/location.html +0 -2
- nautobot/dcim/templates/dcim/location_edit.html +0 -2
- nautobot/dcim/templates/dcim/location_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/locationtype.html +0 -2
- nautobot/dcim/templates/dcim/locationtype_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/manufacturer.html +0 -2
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -1
- nautobot/dcim/templates/dcim/platform.html +0 -2
- nautobot/dcim/templates/dcim/powerfeed.html +0 -2
- nautobot/dcim/templates/dcim/powerfeed_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel_edit.html +0 -2
- nautobot/dcim/templates/dcim/powerpanel_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/rack.html +0 -2
- nautobot/dcim/templates/dcim/rack_edit.html +0 -2
- nautobot/dcim/templates/dcim/rackgroup.html +0 -2
- nautobot/dcim/templates/dcim/rackreservation.html +0 -2
- nautobot/dcim/templates/dcim/softwareimagefile_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/softwareversion_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_add.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_edit.html +0 -2
- nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +0 -2
- nautobot/dcim/templates/dcim/virtualdevicecontext_retrieve.html +0 -2
- nautobot/dcim/ui.py +0 -29
- nautobot/extras/templates/extras/computedfield.html +0 -2
- nautobot/extras/templates/extras/computedfield_retrieve.html +0 -2
- nautobot/extras/templates/extras/configcontext.html +0 -2
- nautobot/extras/templates/extras/configcontext_edit.html +0 -2
- nautobot/extras/templates/extras/configcontext_retrieve.html +0 -2
- nautobot/extras/templates/extras/configcontextschema.html +0 -2
- nautobot/extras/templates/extras/configcontextschema_edit.html +0 -2
- nautobot/extras/templates/extras/contact_retrieve.html +0 -2
- nautobot/extras/templates/extras/customfield.html +0 -2
- nautobot/extras/templates/extras/customfield_edit.html +0 -2
- nautobot/extras/templates/extras/customfield_retrieve.html +0 -2
- nautobot/extras/templates/extras/customlink.html +0 -2
- nautobot/extras/templates/extras/dynamicgroup.html +0 -2
- nautobot/extras/templates/extras/dynamicgroup_edit.html +0 -2
- nautobot/extras/templates/extras/exporttemplate.html +0 -2
- nautobot/extras/templates/extras/gitrepository.html +0 -2
- nautobot/extras/templates/extras/gitrepository_object_edit.html +0 -2
- nautobot/extras/templates/extras/graphqlquery.html +0 -2
- nautobot/extras/templates/extras/graphqlquery_list.html +0 -1
- nautobot/extras/templates/extras/graphqlquery_retrieve.html +0 -2
- nautobot/extras/templates/extras/job_detail.html +0 -2
- nautobot/extras/templates/extras/jobbutton_retrieve.html +0 -2
- nautobot/extras/templates/extras/jobhook.html +0 -2
- nautobot/extras/templates/extras/jobqueue_retrieve.html +0 -2
- nautobot/extras/templates/extras/jobresult.html +0 -2
- nautobot/extras/templates/extras/metadatatype_retrieve.html +0 -2
- nautobot/extras/templates/extras/note.html +0 -2
- nautobot/extras/templates/extras/note_retrieve.html +0 -1
- nautobot/extras/templates/extras/object_changelog.html +0 -2
- nautobot/extras/templates/extras/object_notes.html +0 -2
- nautobot/extras/templates/extras/objectchange.html +0 -2
- nautobot/extras/templates/extras/objectchange_list.html +0 -3
- nautobot/extras/templates/extras/relationship.html +0 -1
- nautobot/extras/templates/extras/secret.html +0 -1
- nautobot/extras/templates/extras/secret_edit.html +0 -1
- nautobot/extras/templates/extras/secretsgroup.html +0 -2
- nautobot/extras/templates/extras/secretsgroup_edit.html +0 -2
- nautobot/extras/templates/extras/secretsgroup_retrieve.html +0 -2
- nautobot/extras/templates/extras/status.html +0 -2
- nautobot/extras/templates/extras/tag.html +0 -2
- nautobot/extras/templates/extras/tag_edit.html +0 -2
- nautobot/extras/templates/extras/tag_retrieve.html +0 -2
- nautobot/extras/templates/extras/team_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/namespace_retrieve.html +0 -1
- nautobot/ipam/templates/ipam/prefix.html +0 -2
- nautobot/ipam/templates/ipam/prefix_edit.html +0 -1
- nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/rir.html +0 -2
- nautobot/ipam/templates/ipam/routetarget.html +0 -1
- nautobot/ipam/templates/ipam/service.html +0 -2
- nautobot/ipam/templates/ipam/service_edit.html +0 -2
- nautobot/ipam/templates/ipam/service_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/vlan.html +0 -2
- nautobot/ipam/templates/ipam/vlan_edit.html +0 -2
- nautobot/ipam/templates/ipam/vlan_retrieve.html +0 -2
- nautobot/ipam/templates/ipam/vlangroup.html +0 -2
- nautobot/ipam/templates/ipam/vrf.html +0 -1
- nautobot/tenancy/templates/tenancy/tenant.html +0 -2
- nautobot/tenancy/templates/tenancy/tenant_edit.html +0 -2
- nautobot/tenancy/templates/tenancy/tenantgroup.html +0 -2
- nautobot/tenancy/templates/tenancy/tenantgroup_retrieve.html +0 -1
- nautobot/virtualization/templates/virtualization/clustergroup.html +0 -2
- nautobot/virtualization/templates/virtualization/clustertype.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +0 -2
- nautobot/virtualization/templates/virtualization/virtualmachine_retrieve.html +0 -2
- nautobot/vpn/templates/vpn/vpnprofile.html +0 -2
- nautobot/wireless/templates/wireless/radioprofile_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/supporteddatarate_retrieve.html +0 -2
- nautobot/wireless/templates/wireless/wirelessnetwork_retrieve.html +0 -2
- {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/LICENSE.txt +0 -0
- {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/NOTICE +0 -0
- {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/WHEEL +0 -0
- {nautobot-3.0.0a3.dist-info → nautobot-3.0.0rc1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Test virtualserver forms."""
|
|
2
|
+
|
|
3
|
+
from nautobot.core.testing.forms import FormTestCases
|
|
4
|
+
from nautobot.load_balancers import choices, forms
|
|
5
|
+
from nautobot.load_balancers.tests import LoadBalancerModelsTestCaseMixin
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# pylint: disable=no-member
|
|
9
|
+
class VirtualServerFormTest(LoadBalancerModelsTestCaseMixin, FormTestCases.BaseFormTestCase):
|
|
10
|
+
"""Test VirtualServer forms."""
|
|
11
|
+
|
|
12
|
+
form_class = forms.VirtualServerForm
|
|
13
|
+
|
|
14
|
+
def test_specifying_all_fields_success(self):
|
|
15
|
+
form = forms.VirtualServerForm(
|
|
16
|
+
data={
|
|
17
|
+
"name": "Virtual Server 1",
|
|
18
|
+
"vip": self.ip_address.pk,
|
|
19
|
+
"enabled": True,
|
|
20
|
+
"port": 80,
|
|
21
|
+
"protocol": choices.ProtocolChoices.PROTOCOL_TCP,
|
|
22
|
+
"load_balancer_pool": self.load_balancer_pools[0].pk,
|
|
23
|
+
"load_balancer_type": choices.LoadBalancerTypeChoices.TYPE_LAYER4,
|
|
24
|
+
"tenant": self.tenant1.pk,
|
|
25
|
+
"source_nat_pool": self.source_nat_pool1.pk,
|
|
26
|
+
"source_nat_type": choices.SourceNATTypeChoices.TYPE_STATIC,
|
|
27
|
+
"device": self.device1.pk,
|
|
28
|
+
"health_check_monitor": self.health_check_monitors[1].pk,
|
|
29
|
+
"ssl_offload": True,
|
|
30
|
+
"certificate_profiles": [self.certificate_profiles[0]],
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
self.assertTrue(form.is_valid())
|
|
34
|
+
self.assertTrue(form.save())
|
|
35
|
+
|
|
36
|
+
def test_specifying_only_required_success(self):
|
|
37
|
+
form = forms.VirtualServerForm(
|
|
38
|
+
data={
|
|
39
|
+
"name": "Virtual Server 1",
|
|
40
|
+
"vip": self.ip_address.pk,
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
self.assertTrue(form.is_valid())
|
|
44
|
+
self.assertTrue(form.save())
|
|
45
|
+
|
|
46
|
+
def test_validate_name_is_required(self):
|
|
47
|
+
form = forms.VirtualServerForm(data={"vip": self.ip_address.pk})
|
|
48
|
+
self.assertFalse(form.is_valid())
|
|
49
|
+
self.assertIn("This field is required.", form.errors["name"])
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# pylint: disable=no-member
|
|
53
|
+
class LoadBalancerPoolFormTest(LoadBalancerModelsTestCaseMixin, FormTestCases.BaseFormTestCase):
|
|
54
|
+
"""Test the LoadBalancerPool form."""
|
|
55
|
+
|
|
56
|
+
form_class = forms.LoadBalancerPoolForm
|
|
57
|
+
|
|
58
|
+
def test_specifying_all_fields_success(self):
|
|
59
|
+
"""Test specifying all fields."""
|
|
60
|
+
|
|
61
|
+
data = {
|
|
62
|
+
"name": "Load Balance Pool 1",
|
|
63
|
+
"load_balancing_algorithm": choices.LoadBalancingAlgorithmChoices.ROUND_ROBIN,
|
|
64
|
+
"health_check_monitor": self.health_check_monitors[0].pk,
|
|
65
|
+
"tenant": self.tenant1.pk,
|
|
66
|
+
}
|
|
67
|
+
form = self.form_class(data=data)
|
|
68
|
+
self.assertTrue(form.is_valid())
|
|
69
|
+
self.assertTrue(form.save())
|
|
70
|
+
|
|
71
|
+
def test_specifying_required_fields_success(self):
|
|
72
|
+
"""Test specifying only required fields."""
|
|
73
|
+
|
|
74
|
+
data = {
|
|
75
|
+
"name": "Load Balance Pool 2",
|
|
76
|
+
"load_balancing_algorithm": choices.LoadBalancingAlgorithmChoices.ROUND_ROBIN,
|
|
77
|
+
}
|
|
78
|
+
form = self.form_class(data=data)
|
|
79
|
+
self.assertTrue(form.is_valid())
|
|
80
|
+
self.assertTrue(form.save())
|
|
81
|
+
|
|
82
|
+
def test_validate_name_is_required(self):
|
|
83
|
+
"""Test that the name field is required."""
|
|
84
|
+
|
|
85
|
+
data = {
|
|
86
|
+
"load_balancing_algorithm": choices.LoadBalancingAlgorithmChoices.ROUND_ROBIN,
|
|
87
|
+
"virtual_server": self.virtual_servers[0].pk,
|
|
88
|
+
}
|
|
89
|
+
form = self.form_class(data=data)
|
|
90
|
+
self.assertFalse(form.is_valid())
|
|
91
|
+
self.assertIn("This field is required.", form.errors["name"])
|
|
92
|
+
|
|
93
|
+
def test_validate_load_balancing_algorithm_is_required(self):
|
|
94
|
+
"""Test that the load_balancing_algorithm field is required."""
|
|
95
|
+
|
|
96
|
+
data = {
|
|
97
|
+
"name": "Load Balance Pool 4",
|
|
98
|
+
"virtual_server": self.virtual_servers[0].pk,
|
|
99
|
+
}
|
|
100
|
+
form = self.form_class(data=data)
|
|
101
|
+
self.assertFalse(form.is_valid())
|
|
102
|
+
self.assertIn("This field is required.", form.errors["load_balancing_algorithm"])
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# pylint: disable=no-member
|
|
106
|
+
class LoadBalancerPoolMemberFormTest(LoadBalancerModelsTestCaseMixin, FormTestCases.BaseFormTestCase):
|
|
107
|
+
"""Test the LoadBalancerPoolMember form."""
|
|
108
|
+
|
|
109
|
+
form_class = forms.LoadBalancerPoolMemberForm
|
|
110
|
+
|
|
111
|
+
def test_specifying_all_fields_success(self):
|
|
112
|
+
"""Test specifying all fields."""
|
|
113
|
+
|
|
114
|
+
data = {
|
|
115
|
+
"ip_address": self.ip1.pk,
|
|
116
|
+
"load_balancer_pool": self.load_balancer_pools[0].pk,
|
|
117
|
+
"certificate_profiles": [self.certificate_profiles[0]],
|
|
118
|
+
"port": 123,
|
|
119
|
+
"ssl_offload": True,
|
|
120
|
+
"health_check_monitor": self.health_check_monitors[1].pk,
|
|
121
|
+
"tenant": self.tenant1.pk,
|
|
122
|
+
"status": self.status.pk,
|
|
123
|
+
}
|
|
124
|
+
form = self.form_class(data=data)
|
|
125
|
+
self.assertTrue(form.is_valid())
|
|
126
|
+
self.assertTrue(form.save())
|
|
127
|
+
|
|
128
|
+
def test_specifying_required_fields_success(self):
|
|
129
|
+
"""Test specifying only required fields."""
|
|
130
|
+
|
|
131
|
+
data = {
|
|
132
|
+
"ip_address": self.ip2.pk,
|
|
133
|
+
"load_balancer_pool": self.load_balancer_pools[0].pk,
|
|
134
|
+
"port": 123,
|
|
135
|
+
"ssl_offload": False,
|
|
136
|
+
"status": self.status.pk,
|
|
137
|
+
}
|
|
138
|
+
form = self.form_class(data=data)
|
|
139
|
+
self.assertTrue(form.is_valid())
|
|
140
|
+
self.assertTrue(form.save())
|
|
141
|
+
|
|
142
|
+
def test_validate_ip_address_is_required(self):
|
|
143
|
+
"""Test that the ip_address field is required."""
|
|
144
|
+
|
|
145
|
+
data = {
|
|
146
|
+
"load_balancer_pool": self.load_balancer_pools[0].pk,
|
|
147
|
+
"certificate_profiles": [self.certificate_profiles[0]],
|
|
148
|
+
"port": 123,
|
|
149
|
+
"ssl_offload": True,
|
|
150
|
+
"health_check_monitor": self.health_check_monitors[0].pk,
|
|
151
|
+
"tenant": self.tenant1.pk,
|
|
152
|
+
"status": self.status.pk,
|
|
153
|
+
}
|
|
154
|
+
form = self.form_class(data=data)
|
|
155
|
+
self.assertFalse(form.is_valid())
|
|
156
|
+
self.assertIn("This field is required.", form.errors["ip_address"])
|
|
157
|
+
|
|
158
|
+
def test_validate_load_balancer_pool_is_required(self):
|
|
159
|
+
"""Test that the load_balancer_pool field is required."""
|
|
160
|
+
|
|
161
|
+
data = {
|
|
162
|
+
"ip_address": self.ip1.pk,
|
|
163
|
+
"certificate_profiles": [self.certificate_profiles[0]],
|
|
164
|
+
"port": 123,
|
|
165
|
+
"ssl_offload": True,
|
|
166
|
+
"health_check_monitor": self.health_check_monitors[0].pk,
|
|
167
|
+
"tenant": self.tenant1.pk,
|
|
168
|
+
"status": self.status.pk,
|
|
169
|
+
}
|
|
170
|
+
form = self.form_class(data=data)
|
|
171
|
+
self.assertFalse(form.is_valid())
|
|
172
|
+
self.assertIn("This field is required.", form.errors["load_balancer_pool"])
|
|
173
|
+
|
|
174
|
+
def test_validate_port_is_required(self):
|
|
175
|
+
"""Test that the port field is required."""
|
|
176
|
+
|
|
177
|
+
data = {
|
|
178
|
+
"ip_address": self.ip1.pk,
|
|
179
|
+
"load_balancer_pool": self.load_balancer_pools[0].pk,
|
|
180
|
+
"port": None,
|
|
181
|
+
"certificate_profiles": [self.certificate_profiles[0]],
|
|
182
|
+
"ssl_offload": True,
|
|
183
|
+
"health_check_monitor": self.health_check_monitors[1].pk,
|
|
184
|
+
"tenant": self.tenant1.pk,
|
|
185
|
+
"status": self.status.pk,
|
|
186
|
+
}
|
|
187
|
+
form = self.form_class(data=data)
|
|
188
|
+
self.assertFalse(form.is_valid())
|
|
189
|
+
self.assertIn("This field is required.", form.errors["port"])
|
|
190
|
+
|
|
191
|
+
def test_validate_status_is_required(self):
|
|
192
|
+
"""Test that the status field is required."""
|
|
193
|
+
|
|
194
|
+
data = {
|
|
195
|
+
"ip_address": self.ip1.pk,
|
|
196
|
+
"load_balancer_pool": self.load_balancer_pools[0].pk,
|
|
197
|
+
"certificate_profiles": [self.certificate_profiles[0]],
|
|
198
|
+
"port": 123,
|
|
199
|
+
"ssl_offload": True,
|
|
200
|
+
"health_check_monitor": self.health_check_monitors[0].pk,
|
|
201
|
+
"tenant": self.tenant1.pk,
|
|
202
|
+
}
|
|
203
|
+
form = self.form_class(data=data)
|
|
204
|
+
self.assertFalse(form.is_valid())
|
|
205
|
+
self.assertIn("This field is required.", form.errors["status"])
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class HealthCheckMonitorFormTest(LoadBalancerModelsTestCaseMixin, FormTestCases.BaseFormTestCase):
|
|
209
|
+
"""Test the HealthCheckMonitor form."""
|
|
210
|
+
|
|
211
|
+
form_class = forms.HealthCheckMonitorForm
|
|
212
|
+
|
|
213
|
+
def test_specifying_all_fields_success(self):
|
|
214
|
+
"""Test specifying all fields."""
|
|
215
|
+
|
|
216
|
+
data = {
|
|
217
|
+
"name": "HTTPS Monitor 4",
|
|
218
|
+
"interval": 4,
|
|
219
|
+
"retry": 5,
|
|
220
|
+
"timeout": 6,
|
|
221
|
+
"port": 443,
|
|
222
|
+
"health_check_type": choices.HealthCheckTypeChoices.HTTPS,
|
|
223
|
+
"tenant": self.tenant2.pk,
|
|
224
|
+
}
|
|
225
|
+
form = self.form_class(data=data)
|
|
226
|
+
self.assertTrue(form.is_valid())
|
|
227
|
+
self.assertTrue(form.save())
|
|
228
|
+
|
|
229
|
+
def test_specifying_required_fields_success(self):
|
|
230
|
+
"""Test specifying only required fields."""
|
|
231
|
+
|
|
232
|
+
data = {"name": "DNS Monitor 4"}
|
|
233
|
+
form = self.form_class(data=data)
|
|
234
|
+
self.assertTrue(form.is_valid())
|
|
235
|
+
self.assertTrue(form.save())
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class CertificateProfileFormTest(LoadBalancerModelsTestCaseMixin, FormTestCases):
|
|
239
|
+
"""Test the CertificateProfile form."""
|
|
240
|
+
|
|
241
|
+
form_class = forms.CertificateProfileForm
|
|
242
|
+
|
|
243
|
+
def test_specifying_all_fields_success(self):
|
|
244
|
+
"""Test specifying all fields."""
|
|
245
|
+
|
|
246
|
+
data = {
|
|
247
|
+
"name": "Certificate Profile 4",
|
|
248
|
+
"certificate_type": choices.CertificateTypeChoices.TYPE_CLIENT,
|
|
249
|
+
"certificate_file_path": "test_certificate.crt",
|
|
250
|
+
"chain_file_path": "chain.pem",
|
|
251
|
+
"key_file_path": "id_rsa.key",
|
|
252
|
+
"expiration_date": "2022-01-01",
|
|
253
|
+
"cipher": "RSA-AES256-GCM-SHA384",
|
|
254
|
+
"tenant": self.tenant2.pk,
|
|
255
|
+
}
|
|
256
|
+
form = self.form_class(data=data)
|
|
257
|
+
self.assertTrue(form.is_valid())
|
|
258
|
+
self.assertTrue(form.save())
|
|
259
|
+
|
|
260
|
+
def test_specifying_required_fields_success(self):
|
|
261
|
+
"""Test specifying only required fields."""
|
|
262
|
+
|
|
263
|
+
data = {"name": "Certificate Profile 5"}
|
|
264
|
+
form = self.form_class(data=data)
|
|
265
|
+
self.assertTrue(form.is_valid())
|
|
266
|
+
self.assertTrue(form.save())
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""Test VirtualServer."""
|
|
2
|
+
|
|
3
|
+
from django.db.utils import IntegrityError
|
|
4
|
+
|
|
5
|
+
from nautobot.core.testing.models import ModelTestCases
|
|
6
|
+
from nautobot.ipam.models import IPAddress
|
|
7
|
+
from nautobot.load_balancers import choices, models
|
|
8
|
+
from nautobot.load_balancers.tests import LoadBalancerModelsTestCaseMixin
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# pylint: disable=no-member
|
|
12
|
+
class TestVirtualServer(LoadBalancerModelsTestCaseMixin, ModelTestCases.BaseModelTestCase):
|
|
13
|
+
"""Test VirtualServer."""
|
|
14
|
+
|
|
15
|
+
model = models.VirtualServer
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def setUpTestData(cls):
|
|
19
|
+
"""Setup test data."""
|
|
20
|
+
super().setUpTestData()
|
|
21
|
+
|
|
22
|
+
# Create a Virtual Server for the generic tests to use
|
|
23
|
+
vip = IPAddress.objects.create(address="10.0.0.50/32", namespace=cls.namespace, status=cls.status)
|
|
24
|
+
cls.virtual_server = models.VirtualServer.objects.create(name="VS0", vip=vip)
|
|
25
|
+
cls.lb_pool = models.LoadBalancerPool.objects.first()
|
|
26
|
+
|
|
27
|
+
def test_create_virtualserver_only_required(self):
|
|
28
|
+
"""Create VirtualServer with only required fields, and validate __str__."""
|
|
29
|
+
virtual_server = models.VirtualServer.objects.create(name="VS1", vip=self.ip_address)
|
|
30
|
+
self.assertEqual(virtual_server.name, "VS1")
|
|
31
|
+
self.assertEqual(str(virtual_server), "VS1")
|
|
32
|
+
|
|
33
|
+
def test_create_virtualserver_vip_required(self):
|
|
34
|
+
"""Create VirtualServer without vip."""
|
|
35
|
+
with self.assertRaises(IntegrityError):
|
|
36
|
+
models.VirtualServer.objects.create(name="VS1")
|
|
37
|
+
|
|
38
|
+
def test_create_virtualserver_all_fields_success(self):
|
|
39
|
+
"""Create VirtualServer with all fields."""
|
|
40
|
+
# Assign to a device
|
|
41
|
+
virtual_server = models.VirtualServer.objects.create(
|
|
42
|
+
name="VS1",
|
|
43
|
+
vip=self.ip_address,
|
|
44
|
+
port=80,
|
|
45
|
+
protocol=choices.ProtocolChoices.PROTOCOL_TCP,
|
|
46
|
+
source_nat_pool=self.source_nat_pool1,
|
|
47
|
+
source_nat_type=choices.SourceNATTypeChoices.TYPE_POOL,
|
|
48
|
+
load_balancer_pool=self.lb_pool,
|
|
49
|
+
load_balancer_type=choices.LoadBalancerTypeChoices.TYPE_LAYER4,
|
|
50
|
+
enabled=False,
|
|
51
|
+
ssl_offload=True,
|
|
52
|
+
tenant=self.tenant1,
|
|
53
|
+
device=self.device1,
|
|
54
|
+
)
|
|
55
|
+
self.assertEqual(virtual_server.name, "VS1")
|
|
56
|
+
|
|
57
|
+
# Assign to a device redundancy group
|
|
58
|
+
virtual_server.device = None
|
|
59
|
+
virtual_server.device_redundancy_group = self.device_redundancy_group1
|
|
60
|
+
virtual_server.save()
|
|
61
|
+
virtual_server.refresh_from_db()
|
|
62
|
+
self.assertIsNone(virtual_server.device)
|
|
63
|
+
self.assertEqual(virtual_server.device_redundancy_group, self.device_redundancy_group1)
|
|
64
|
+
|
|
65
|
+
# Assign to a cloud service
|
|
66
|
+
virtual_server.device_redundancy_group = None
|
|
67
|
+
virtual_server.cloud_service = self.cloud_service1
|
|
68
|
+
virtual_server.save()
|
|
69
|
+
virtual_server.refresh_from_db()
|
|
70
|
+
self.assertIsNone(virtual_server.device_redundancy_group)
|
|
71
|
+
self.assertEqual(virtual_server.cloud_service, self.cloud_service1)
|
|
72
|
+
|
|
73
|
+
# Assign to a virtual chassis
|
|
74
|
+
virtual_server.cloud_service = None
|
|
75
|
+
virtual_server.virtual_chassis = self.virtual_chassis1
|
|
76
|
+
virtual_server.save()
|
|
77
|
+
virtual_server.refresh_from_db()
|
|
78
|
+
self.assertIsNone(virtual_server.cloud_service)
|
|
79
|
+
self.assertEqual(virtual_server.virtual_chassis, self.virtual_chassis1)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class TestLoadBalancerPool(LoadBalancerModelsTestCaseMixin, ModelTestCases.BaseModelTestCase):
|
|
83
|
+
"""Test LoadBalancerPool."""
|
|
84
|
+
|
|
85
|
+
model = models.LoadBalancerPool
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def setUpTestData(cls):
|
|
89
|
+
"""Setup test data."""
|
|
90
|
+
super().setUpTestData()
|
|
91
|
+
|
|
92
|
+
# Create a LoadBalancerPool for the generic tests to use
|
|
93
|
+
cls.lb_pool = models.LoadBalancerPool.objects.create(name="Pool0", load_balancing_algorithm="round_robin")
|
|
94
|
+
|
|
95
|
+
def test_create_load_balancer_pool_only_required(self):
|
|
96
|
+
"""Create LoadBalancerPool with only required fields, and validate __str__."""
|
|
97
|
+
lb_pool = models.LoadBalancerPool.objects.create(
|
|
98
|
+
name="Pool1",
|
|
99
|
+
load_balancing_algorithm="url_hash",
|
|
100
|
+
)
|
|
101
|
+
self.assertEqual(lb_pool.name, "Pool1")
|
|
102
|
+
self.assertEqual(str(lb_pool), "Pool1")
|
|
103
|
+
|
|
104
|
+
def test_create_load_balancer_pool_load_balancing_algorithm_required(self):
|
|
105
|
+
"""Create LoadBalancerPool without load_balancing_algorithm."""
|
|
106
|
+
with self.assertRaises(IntegrityError):
|
|
107
|
+
models.LoadBalancerPool.objects.create(name="Pool2", load_balancing_algorithm=None)
|
|
108
|
+
|
|
109
|
+
def test_create_load_balancer_pool_name_required(self):
|
|
110
|
+
"""Create LoadBalancerPool without name."""
|
|
111
|
+
with self.assertRaises(IntegrityError):
|
|
112
|
+
models.LoadBalancerPool.objects.create(load_balancing_algorithm="url_hash", name=None)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# pylint: disable=no-member
|
|
116
|
+
class TestLoadBalancerPoolMemberModel(LoadBalancerModelsTestCaseMixin, ModelTestCases.BaseModelTestCase):
|
|
117
|
+
"""Test LoadBalancerPoolMember model."""
|
|
118
|
+
|
|
119
|
+
model = models.LoadBalancerPoolMember
|
|
120
|
+
|
|
121
|
+
@classmethod
|
|
122
|
+
def setUpTestData(cls):
|
|
123
|
+
"""Set up test data."""
|
|
124
|
+
super().setUpTestData()
|
|
125
|
+
|
|
126
|
+
# Create a LoadBalancerPool for the generic tests to use
|
|
127
|
+
cls.lb_pool = models.LoadBalancerPool.objects.create(name="Pool0", load_balancing_algorithm="round_robin")
|
|
128
|
+
|
|
129
|
+
# Create a LoadBalancerPoolMember for the generic tests to use
|
|
130
|
+
cls.load_balancer_pool_member = models.LoadBalancerPoolMember.objects.create(
|
|
131
|
+
ip_address=cls.ip_address,
|
|
132
|
+
port=80,
|
|
133
|
+
tenant=cls.tenant1,
|
|
134
|
+
ssl_offload=True,
|
|
135
|
+
load_balancer_pool=cls.lb_pool,
|
|
136
|
+
status=cls.status,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def test_create_load_balancer_pool_member_only_required(self):
|
|
140
|
+
"""Create LoadBalancerPoolMember with only required fields, and validate __str__."""
|
|
141
|
+
ip_address = IPAddress.objects.create(address="10.0.0.50/32", namespace=self.namespace, status=self.status)
|
|
142
|
+
load_balancer_pool_member = models.LoadBalancerPoolMember.objects.create(
|
|
143
|
+
ip_address=ip_address, port=80, load_balancer_pool=self.lb_pool, status=self.status
|
|
144
|
+
)
|
|
145
|
+
self.assertEqual(load_balancer_pool_member.port, 80)
|
|
146
|
+
self.assertEqual(str(load_balancer_pool_member), f"{ip_address.host}:80")
|
|
147
|
+
|
|
148
|
+
def test_create_load_balancer_pool_member_ipaddress_required(self):
|
|
149
|
+
"""Create LoadBalancerPoolMember without ipaddress."""
|
|
150
|
+
with self.assertRaises(IntegrityError):
|
|
151
|
+
models.LoadBalancerPoolMember.objects.create(port=80, load_balancer_pool=self.lb_pool, status=self.status)
|
|
152
|
+
|
|
153
|
+
def test_create_load_balancer_pool_member_port_required(self):
|
|
154
|
+
"""Create LoadBalancerPoolMember without port."""
|
|
155
|
+
ip_address = IPAddress.objects.create(address="10.0.0.50/32", namespace=self.namespace, status=self.status)
|
|
156
|
+
with self.assertRaises(IntegrityError):
|
|
157
|
+
models.LoadBalancerPoolMember.objects.create(
|
|
158
|
+
ip_address=ip_address, port=None, load_balancer_pool=self.lb_pool, status=self.status
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def test_create_load_balancer_pool_member_load_balancer_pool_required(self):
|
|
162
|
+
"""Create LoadBalancerPoolMember without load_balancer_pool."""
|
|
163
|
+
ip_address = IPAddress.objects.create(address="10.0.0.50/32", namespace=self.namespace, status=self.status)
|
|
164
|
+
with self.assertRaises(IntegrityError):
|
|
165
|
+
models.LoadBalancerPoolMember.objects.create(ip_address=ip_address, port=80, status=self.status)
|
|
166
|
+
|
|
167
|
+
def test_create_load_balancer_pool_member_status_required(self):
|
|
168
|
+
"""Create LoadBalancerPoolMember without status."""
|
|
169
|
+
ip_address = IPAddress.objects.create(address="10.0.0.50/32", namespace=self.namespace, status=self.status)
|
|
170
|
+
with self.assertRaises(IntegrityError):
|
|
171
|
+
models.LoadBalancerPoolMember.objects.create(
|
|
172
|
+
ip_address=ip_address, port=80, load_balancer_pool=self.lb_pool
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class TestHealthCheckMonitorModel(LoadBalancerModelsTestCaseMixin, ModelTestCases.BaseModelTestCase):
|
|
177
|
+
"""Test HealthCheckMonitor model."""
|
|
178
|
+
|
|
179
|
+
model = models.HealthCheckMonitor
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def setUpTestData(cls):
|
|
183
|
+
"""Set up test data."""
|
|
184
|
+
super().setUpTestData()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class TestCertificateProfileModel(LoadBalancerModelsTestCaseMixin, ModelTestCases.BaseModelTestCase):
|
|
188
|
+
"""Test CertificateProfile model."""
|
|
189
|
+
|
|
190
|
+
model = models.CertificateProfile
|
|
191
|
+
|
|
192
|
+
@classmethod
|
|
193
|
+
def setUpTestData(cls):
|
|
194
|
+
"""Set up test data."""
|
|
195
|
+
super().setUpTestData()
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""Unit tests for views."""
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
|
|
5
|
+
from django.utils.timezone import make_aware
|
|
6
|
+
|
|
7
|
+
from nautobot.core.testing.views import ViewTestCases
|
|
8
|
+
from nautobot.load_balancers import choices, models
|
|
9
|
+
from nautobot.load_balancers.tests import LoadBalancerModelsTestCaseMixin
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# pylint: disable=too-many-ancestors, no-member
|
|
13
|
+
class VirtualServerViewTest(LoadBalancerModelsTestCaseMixin, ViewTestCases.PrimaryObjectViewTestCase):
|
|
14
|
+
"""Test the VirtualServer views."""
|
|
15
|
+
|
|
16
|
+
model = models.VirtualServer
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def setUpTestData(cls):
|
|
20
|
+
"""Setup test data."""
|
|
21
|
+
super().setUpTestData()
|
|
22
|
+
|
|
23
|
+
cls.form_data = {
|
|
24
|
+
"name": "VS4",
|
|
25
|
+
"vip": cls.vips[-4].pk,
|
|
26
|
+
"port": 4444,
|
|
27
|
+
"load_balancer_pool": cls.load_balancer_pools[0].pk,
|
|
28
|
+
"protocol": choices.ProtocolChoices.PROTOCOL_TCP,
|
|
29
|
+
"source_nat_pool": None,
|
|
30
|
+
"source_nat_type": "",
|
|
31
|
+
"load_balancer_type": choices.LoadBalancerTypeChoices.TYPE_LAYER4,
|
|
32
|
+
"enabled": True,
|
|
33
|
+
"ssl_offload": False,
|
|
34
|
+
"device": cls.device1.pk,
|
|
35
|
+
"device_redundancy_group": None,
|
|
36
|
+
"cloud_service": None,
|
|
37
|
+
"virtual_chassis": None,
|
|
38
|
+
"tenant": None,
|
|
39
|
+
"health_check_monitor": None,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
cls.update_data = {
|
|
43
|
+
"name": "VS5",
|
|
44
|
+
"vip": cls.vips[-5].pk,
|
|
45
|
+
"port": 5555,
|
|
46
|
+
"protocol": choices.ProtocolChoices.PROTOCOL_DNS,
|
|
47
|
+
"load_balancer_pool": cls.load_balancer_pools[1].pk,
|
|
48
|
+
"source_nat_pool": None,
|
|
49
|
+
"source_nat_type": "",
|
|
50
|
+
"load_balancer_type": choices.LoadBalancerTypeChoices.TYPE_DNS,
|
|
51
|
+
"enabled": False,
|
|
52
|
+
"ssl_offload": True,
|
|
53
|
+
"device": None,
|
|
54
|
+
"device_redundancy_group": cls.device_redundancy_group1.pk,
|
|
55
|
+
"cloud_service": None,
|
|
56
|
+
"virtual_chassis": None,
|
|
57
|
+
"tenant": None,
|
|
58
|
+
"health_check_monitor": cls.health_check_monitors[0].pk,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
cls.bulk_edit_data = {
|
|
62
|
+
"port": 7777,
|
|
63
|
+
"protocol": choices.ProtocolChoices.PROTOCOL_ANY,
|
|
64
|
+
"source_nat_pool": None,
|
|
65
|
+
"source_nat_type": "",
|
|
66
|
+
"load_balancer_pool": cls.load_balancer_pools[0].pk,
|
|
67
|
+
"load_balancer_type": choices.LoadBalancerTypeChoices.TYPE_LAYER2,
|
|
68
|
+
"enabled": False,
|
|
69
|
+
"ssl_offload": True,
|
|
70
|
+
"device": None,
|
|
71
|
+
"device_redundancy_group": None,
|
|
72
|
+
"cloud_service": None,
|
|
73
|
+
"virtual_chassis": cls.virtual_chassis1.pk,
|
|
74
|
+
"tenant": None,
|
|
75
|
+
"health_check_monitor": cls.health_check_monitors[1].pk,
|
|
76
|
+
"add_certificate_profiles": [cls.certificate_profiles[1].pk],
|
|
77
|
+
"remove_certificate_profiles": [cls.certificate_profiles[0].pk],
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# pylint: disable=too-many-ancestors, no-member
|
|
82
|
+
class LoadBalancerPoolViewTestCase(LoadBalancerModelsTestCaseMixin, ViewTestCases.PrimaryObjectViewTestCase):
|
|
83
|
+
"""Test the LoadBalancerPool views."""
|
|
84
|
+
|
|
85
|
+
model = models.LoadBalancerPool
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def setUpTestData(cls):
|
|
89
|
+
"""Set up test data."""
|
|
90
|
+
super().setUpTestData()
|
|
91
|
+
|
|
92
|
+
cls.form_data = {
|
|
93
|
+
"name": "Load Balancer Pool 1",
|
|
94
|
+
"load_balancing_algorithm": choices.LoadBalancingAlgorithmChoices.ROUND_ROBIN,
|
|
95
|
+
"health_check_monitor": None,
|
|
96
|
+
"tenant": cls.tenant1.pk,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
cls.update_data = {
|
|
100
|
+
"name": "Load Balancer Pool 2",
|
|
101
|
+
"load_balancing_algorithm": choices.LoadBalancingAlgorithmChoices.LEAST_CONNECTIONS,
|
|
102
|
+
"health_check_monitor": cls.health_check_monitors[0].pk,
|
|
103
|
+
"tenant": None,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
cls.bulk_edit_data = {
|
|
107
|
+
"name": "Load Balancer Pool 2",
|
|
108
|
+
"load_balancing_algorithm": choices.LoadBalancingAlgorithmChoices.URL_HASH,
|
|
109
|
+
"health_check_monitor": cls.health_check_monitors[1].pk,
|
|
110
|
+
"tenant": None,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# pylint: disable=too-many-ancestors, no-member
|
|
115
|
+
class LoadBalancerPoolMemberViewTestCase(LoadBalancerModelsTestCaseMixin, ViewTestCases.PrimaryObjectViewTestCase):
|
|
116
|
+
"""Test the LoadBalancerPoolMember views."""
|
|
117
|
+
|
|
118
|
+
model = models.LoadBalancerPoolMember
|
|
119
|
+
|
|
120
|
+
@classmethod
|
|
121
|
+
def setUpTestData(cls):
|
|
122
|
+
"""Set up test data."""
|
|
123
|
+
super().setUpTestData()
|
|
124
|
+
|
|
125
|
+
cls.form_data = {
|
|
126
|
+
"ip_address": cls.ip2.pk,
|
|
127
|
+
"load_balancer_pool": cls.load_balancer_pools[1].pk,
|
|
128
|
+
"port": 161,
|
|
129
|
+
"ssl_offload": True,
|
|
130
|
+
"tenant": cls.tenant1.pk,
|
|
131
|
+
"status": cls.status.pk,
|
|
132
|
+
"health_check_monitor": cls.health_check_monitors[0].pk,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
cls.bulk_edit_data = {
|
|
136
|
+
"load_balancer_pool": cls.load_balancer_pools[0].pk,
|
|
137
|
+
"ssl_offload": False,
|
|
138
|
+
"tenant": None,
|
|
139
|
+
"status": cls.status.pk,
|
|
140
|
+
"health_check_monitor": cls.health_check_monitors[1].pk,
|
|
141
|
+
"add_certificate_profiles": [cls.certificate_profiles[1].pk],
|
|
142
|
+
"remove_certificate_profiles": [cls.certificate_profiles[0].pk],
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# pylint: disable=too-many-ancestors, no-member
|
|
147
|
+
class HealthCheckMonitorViewTestCase(LoadBalancerModelsTestCaseMixin, ViewTestCases.PrimaryObjectViewTestCase):
|
|
148
|
+
"""Test the HealthCheckMonitor views."""
|
|
149
|
+
|
|
150
|
+
model = models.HealthCheckMonitor
|
|
151
|
+
|
|
152
|
+
@classmethod
|
|
153
|
+
def setUpTestData(cls):
|
|
154
|
+
"""Set up test data."""
|
|
155
|
+
super().setUpTestData()
|
|
156
|
+
|
|
157
|
+
cls.form_data = {
|
|
158
|
+
"name": "HTTP Monitor 4",
|
|
159
|
+
"interval": 35,
|
|
160
|
+
"retry": 3,
|
|
161
|
+
"timeout": 30,
|
|
162
|
+
"port": 8008,
|
|
163
|
+
"health_check_type": choices.HealthCheckTypeChoices.HTTP,
|
|
164
|
+
"tenant": cls.tenant1.pk,
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
cls.update_data = {
|
|
168
|
+
"name": "DNS Monitor 3",
|
|
169
|
+
"interval": 4,
|
|
170
|
+
"retry": 5,
|
|
171
|
+
"timeout": 6,
|
|
172
|
+
"port": 0,
|
|
173
|
+
"health_check_type": choices.HealthCheckTypeChoices.DNS,
|
|
174
|
+
"tenant": cls.tenant2.pk,
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
cls.bulk_edit_data = {
|
|
178
|
+
"interval": 8,
|
|
179
|
+
"retry": 9,
|
|
180
|
+
"timeout": 10,
|
|
181
|
+
"port": 11,
|
|
182
|
+
"health_check_type": choices.HealthCheckTypeChoices.TCP,
|
|
183
|
+
"tenant": None,
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# pylint: disable=too-many-ancestors, no-member
|
|
188
|
+
class CertificateProfileViewTestCase(LoadBalancerModelsTestCaseMixin, ViewTestCases.PrimaryObjectViewTestCase):
|
|
189
|
+
"""Test the CertificateProfile views."""
|
|
190
|
+
|
|
191
|
+
model = models.CertificateProfile
|
|
192
|
+
|
|
193
|
+
@classmethod
|
|
194
|
+
def setUpTestData(cls):
|
|
195
|
+
"""Set up test data."""
|
|
196
|
+
super().setUpTestData()
|
|
197
|
+
|
|
198
|
+
cls.form_data = {
|
|
199
|
+
"name": "Test Certificate Profile 1",
|
|
200
|
+
"certificate_type": choices.CertificateTypeChoices.TYPE_CLIENT,
|
|
201
|
+
"certificate_file_path": "/this/is/a/path.crt",
|
|
202
|
+
"chain_file_path": "/chain/certificate/path.pem",
|
|
203
|
+
"key_file_path": "test_key.key",
|
|
204
|
+
"expiration_date": make_aware(datetime.datetime(2023, 11, 1, 0, 0)),
|
|
205
|
+
"cipher": "TLS_AES_256_GCM_SHA384",
|
|
206
|
+
"tenant": cls.tenant1.pk,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
cls.update_data = {
|
|
210
|
+
"name": "Test Certificate Profile 2",
|
|
211
|
+
"certificate_type": choices.CertificateTypeChoices.TYPE_SERVER,
|
|
212
|
+
"certificate_file_path": "/test_cert.pem",
|
|
213
|
+
"chain_file_path": "/test_chain.pem",
|
|
214
|
+
"key_file_path": "/test_key",
|
|
215
|
+
"expiration_date": make_aware(datetime.datetime(2030, 12, 1, 0, 0)),
|
|
216
|
+
"cipher": "ECDHE_RSA",
|
|
217
|
+
"tenant": cls.tenant2.pk,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
cls.bulk_edit_data = {
|
|
221
|
+
"name": "Test Certificate Profile 3",
|
|
222
|
+
"tenant": None,
|
|
223
|
+
"certificate_type": choices.CertificateTypeChoices.TYPE_MTLS,
|
|
224
|
+
"certificate_file_path": "/etc/certificate.pem",
|
|
225
|
+
"chain_file_path": "/bin/chain.pem",
|
|
226
|
+
"key_file_path": "/home/key",
|
|
227
|
+
"expiration_date": make_aware(datetime.datetime(2020, 1, 1, 0, 0, 0, 0)),
|
|
228
|
+
"cipher": "CHACHA20_POLY1305",
|
|
229
|
+
}
|