netbox-docker-plugin 4.7.0__tar.gz → 4.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. {netbox_docker_plugin-4.7.0/netbox_docker_plugin.egg-info → netbox_docker_plugin-4.9.0}/PKG-INFO +1 -1
  2. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/__init__.py +1 -1
  3. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/api/serializers.py +28 -0
  4. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/api/views.py +10 -3
  5. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/filtersets.py +20 -0
  6. netbox_docker_plugin-4.9.0/netbox_docker_plugin/forms/sysctl.py +28 -0
  7. netbox_docker_plugin-4.9.0/netbox_docker_plugin/migrations/1041_sysctl.py +67 -0
  8. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/models/container.py +54 -3
  9. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tables.py +16 -0
  10. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/templates/netbox_docker_plugin/container.html +64 -40
  11. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/container/test_container_api.py +10 -2
  12. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/host/test_host_api.py +29 -0
  13. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/host/test_host_views.py +21 -0
  14. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/urls.py +31 -3
  15. netbox_docker_plugin-4.9.0/netbox_docker_plugin/views/sysctl.py +27 -0
  16. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0/netbox_docker_plugin.egg-info}/PKG-INFO +1 -1
  17. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin.egg-info/SOURCES.txt +3 -0
  18. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/pyproject.toml +1 -1
  19. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/LICENSE +0 -0
  20. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/MANIFEST.in +0 -0
  21. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/README.md +0 -0
  22. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/api/__init__.py +0 -0
  23. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/api/renderers.py +0 -0
  24. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/api/urls.py +0 -0
  25. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/__init__.py +0 -0
  26. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/bind.py +0 -0
  27. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/container.py +0 -0
  28. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/device.py +0 -0
  29. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/env.py +0 -0
  30. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/host.py +0 -0
  31. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/image.py +0 -0
  32. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/label.py +0 -0
  33. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/log_driver_option.py +0 -0
  34. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/mount.py +0 -0
  35. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/network.py +0 -0
  36. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/network_setting.py +0 -0
  37. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/port.py +0 -0
  38. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/registry.py +0 -0
  39. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/forms/volume.py +0 -0
  40. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0001_initial.py +0 -0
  41. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0002_image.py +0 -0
  42. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0003_image_size.py +0 -0
  43. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0004_volume.py +0 -0
  44. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0005_network.py +0 -0
  45. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0006_container_port_mount_label_env_and_more.py +0 -0
  46. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0007_remove_container_netbox_docker_plugin_container_unique_name_host__and_more.py +0 -0
  47. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0008_alter_network_unique_together_network_networkid_and_more.py +0 -0
  48. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0009_remove_container_network_networksettings_and_more.py +0 -0
  49. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0010_container_containerid_and_more.py +0 -0
  50. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0011_host_token.py +0 -0
  51. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0012_host_state.py +0 -0
  52. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0013_host_netbox_url.py +0 -0
  53. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0014_container_operation.py +0 -0
  54. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0015_remove_mount_netbox_docker_plugin_mount_unique_volume_and_more.py +0 -0
  55. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0016_alter_env_value.py +0 -0
  56. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0017_network_state.py +0 -0
  57. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0018_alter_mount_volume_alter_networksetting_network.py +0 -0
  58. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0019_host_agent_version_host_docker_api_version.py +0 -0
  59. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0020_container_hostname.py +0 -0
  60. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0021_registry_and_more.py +0 -0
  61. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0022_bind_bind_netbox_docker_plugin_bind_unique_bind.py +0 -0
  62. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0023_delete_hosts.py +0 -0
  63. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0024_registry_host_alter_registry_name_and_more.py +0 -0
  64. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0025_alter_image_version.py +0 -0
  65. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0026_image_digest.py +0 -0
  66. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0027_container_restart_policy.py +0 -0
  67. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0028_mount_and_bind_read_only.py +0 -0
  68. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0029_alter_container_state.py +0 -0
  69. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0030_alter_container_containerid_alter_container_hostname_and_more.py +0 -0
  70. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0031_container_cap_add.py +0 -0
  71. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0032_device_and_more.py +0 -0
  72. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0033_host_operation.py +0 -0
  73. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0034_volume_max_size.py +0 -0
  74. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0035_container_log_driver_logdriveroption.py +0 -0
  75. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0036_alter_container_log_driver.py +0 -0
  76. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0037_alter_env_value.py +0 -0
  77. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/0038_container_cmd.py +0 -0
  78. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/1039_alter_port_public_port.py +0 -0
  79. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/1040_alter_container_log_driver.py +0 -0
  80. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/migrations/__init__.py +0 -0
  81. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/models/__init__.py +0 -0
  82. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/models/host.py +0 -0
  83. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/models/image.py +0 -0
  84. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/models/network.py +0 -0
  85. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/models/registry.py +0 -0
  86. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/models/volume.py +0 -0
  87. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/navigation.py +0 -0
  88. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/signals.py +0 -0
  89. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/templates/netbox_docker_plugin/container-exec.html +0 -0
  90. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/templates/netbox_docker_plugin/container-layout.html +0 -0
  91. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/templates/netbox_docker_plugin/container-logs.html +0 -0
  92. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/templates/netbox_docker_plugin/host-graph.html +0 -0
  93. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/templates/netbox_docker_plugin/host.html +0 -0
  94. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/templates/netbox_docker_plugin/image.html +0 -0
  95. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/templates/netbox_docker_plugin/network.html +0 -0
  96. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/templates/netbox_docker_plugin/registry.html +0 -0
  97. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/templates/netbox_docker_plugin/volume.html +0 -0
  98. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/templatetags/__init__.py +0 -0
  99. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/templatetags/host.py +0 -0
  100. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/__init__.py +0 -0
  101. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/base.py +0 -0
  102. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/container/__init__.py +0 -0
  103. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/container/test_container_actions.py +0 -0
  104. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/container/test_container_api_exec.py +0 -0
  105. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/container/test_container_operation_view.py +0 -0
  106. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/container/test_container_validation.py +0 -0
  107. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/container/test_container_views.py +0 -0
  108. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/host/__init__.py +0 -0
  109. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/host/test_replace_password.py +0 -0
  110. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/image/__init__.py +0 -0
  111. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/image/test_image_api.py +0 -0
  112. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/image/test_image_views.py +0 -0
  113. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/network/__init__.py +0 -0
  114. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/network/test_network_api.py +0 -0
  115. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/network/test_network_views.py +2 -2
  116. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/registry/__init__.py +0 -0
  117. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/registry/test_registry_api.py +0 -0
  118. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/registry/test_registry_views.py +0 -0
  119. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/test_init.py +0 -0
  120. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/volume/__init__.py +0 -0
  121. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/volume/test_volume_api.py +0 -0
  122. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/tests/volume/test_volume_views.py +0 -0
  123. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/utilities/__init__.py +0 -0
  124. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/__init__.py +0 -0
  125. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/bind.py +0 -0
  126. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/container.py +0 -0
  127. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/device.py +0 -0
  128. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/env.py +0 -0
  129. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/host.py +0 -0
  130. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/image.py +0 -0
  131. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/label.py +0 -0
  132. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/log_driver_option.py +0 -0
  133. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/mount.py +0 -0
  134. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/network.py +0 -0
  135. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/network_setting.py +0 -0
  136. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/port.py +0 -0
  137. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/registry.py +0 -0
  138. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin/views/volume.py +0 -0
  139. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin.egg-info/dependency_links.txt +0 -0
  140. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/netbox_docker_plugin.egg-info/top_level.txt +0 -0
  141. {netbox_docker_plugin-4.7.0 → netbox_docker_plugin-4.9.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netbox-docker-plugin
3
- Version: 4.7.0
3
+ Version: 4.9.0
4
4
  Summary: Manage Docker with Netbox & style.
5
5
  Author-email: Vincent Simonin <vincent@saashup.com>, David Delassus <david.jose.delassus@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/SaaShup/netbox-docker-plugin
@@ -11,7 +11,7 @@ class NetBoxDockerConfig(PluginConfig):
11
11
  name = "netbox_docker_plugin"
12
12
  verbose_name = " NetBox Docker Plugin"
13
13
  description = "Manage Docker"
14
- version = "4.7.0"
14
+ version = "4.9.0"
15
15
  base_url = "docker"
16
16
  min_version = "4.3.7"
17
17
  author = "Vincent Simonin <vincent@saashup.com>, David Delassus <david.jose.delassus@gmail.com>"
@@ -20,6 +20,7 @@ from ..models.container import (
20
20
  NetworkSetting,
21
21
  Device,
22
22
  LogDriverOption,
23
+ Sysctl,
23
24
  )
24
25
  from ..models.registry import Registry
25
26
 
@@ -422,6 +423,19 @@ class DeviceSerializer(serializers.ModelSerializer):
422
423
  )
423
424
 
424
425
 
426
+ class SysctlSerializer(serializers.ModelSerializer):
427
+ """Container sysctl Serializer class"""
428
+
429
+ class Meta:
430
+ """Container sysctl Serializer Meta class"""
431
+
432
+ model = Sysctl
433
+ fields = (
434
+ "key",
435
+ "value",
436
+ )
437
+
438
+
425
439
  class ContainerSerializer(NetBoxModelSerializer):
426
440
  """Container Serializer class"""
427
441
 
@@ -438,6 +452,7 @@ class ContainerSerializer(NetBoxModelSerializer):
438
452
  network_settings = NetworkSettingSerializer(many=True, required=False)
439
453
  devices = DeviceSerializer(many=True, required=False)
440
454
  log_driver_options = LogDriverOptionSerializer(many=True, required=False)
455
+ sysctls = SysctlSerializer(many=True, required=False)
441
456
 
442
457
  class Meta:
443
458
  """Container Serializer Meta class"""
@@ -467,6 +482,7 @@ class ContainerSerializer(NetBoxModelSerializer):
467
482
  "devices",
468
483
  "log_driver",
469
484
  "log_driver_options",
485
+ "sysctls",
470
486
  "custom_fields",
471
487
  "created",
472
488
  "last_updated",
@@ -484,6 +500,7 @@ class ContainerSerializer(NetBoxModelSerializer):
484
500
  attrs.pop("binds", None)
485
501
  attrs.pop("network_settings", None)
486
502
  attrs.pop("devices", None)
503
+ attrs.pop("sysctls", None)
487
504
 
488
505
  super().validate(attrs)
489
506
 
@@ -499,6 +516,7 @@ class ContainerSerializer(NetBoxModelSerializer):
499
516
  binds_data = validated_data.pop("binds", None)
500
517
  network_settings_data = validated_data.pop("network_settings", None)
501
518
  devices_data = validated_data.pop("devices", None)
519
+ sysctls_data = validated_data.pop("sysctls", None)
502
520
 
503
521
  container = super().create(validated_data)
504
522
 
@@ -542,6 +560,10 @@ class ContainerSerializer(NetBoxModelSerializer):
542
560
  obj.full_clean()
543
561
  obj.save()
544
562
 
563
+ if sysctls_data is not None:
564
+ for sysctl in sysctls_data:
565
+ Sysctl.objects.create(container=container, **sysctl)
566
+
545
567
  return container
546
568
 
547
569
  # pylint: disable=R0912
@@ -554,6 +576,7 @@ class ContainerSerializer(NetBoxModelSerializer):
554
576
  binds_data = validated_data.pop("binds", None)
555
577
  network_settings_data = validated_data.pop("network_settings", None)
556
578
  devices_data = validated_data.pop("devices", None)
579
+ sysctls_data = validated_data.pop("sysctls", None)
557
580
 
558
581
  container = super().update(instance, validated_data)
559
582
 
@@ -605,6 +628,11 @@ class ContainerSerializer(NetBoxModelSerializer):
605
628
  obj.full_clean()
606
629
  obj.save()
607
630
 
631
+ if sysctls_data is not None:
632
+ Sysctl.objects.filter(container=container).delete()
633
+ for sysctl in sysctls_data:
634
+ Sysctl.objects.create(container=container, **sysctl)
635
+
608
636
  return container
609
637
 
610
638
 
@@ -47,13 +47,19 @@ class HostViewSet(NetBoxModelViewSet):
47
47
  def perform_create(self, serializer):
48
48
  if isinstance(serializer.validated_data, Sequence):
49
49
  for obj in serializer.validated_data:
50
- token = Token(user=self.request.user, write_enabled=True, description="DockerAgent")
50
+ token = Token(
51
+ user=self.request.user,
52
+ write_enabled=True,
53
+ description="DockerAgent",
54
+ )
51
55
  token.save()
52
56
 
53
57
  obj["token"] = token
54
58
  obj["netbox_base_url"] = self.request.stream.META["HTTP_ORIGIN"]
55
59
  else:
56
- token = Token(user=self.request.user, write_enabled=True, description="DockerAgent")
60
+ token = Token(
61
+ user=self.request.user, write_enabled=True, description="DockerAgent"
62
+ )
57
63
  token.save()
58
64
 
59
65
  serializer.validated_data["token"] = token
@@ -85,7 +91,7 @@ class ImageViewSet(NetBoxModelViewSet):
85
91
  renderer_classes=[JSONRenderer],
86
92
  )
87
93
  def force_pull(self, _request, **_kwargs):
88
- """ Force pull an existing image """
94
+ """Force pull an existing image"""
89
95
 
90
96
  image: Image = self.get_object()
91
97
  agent_url = image.host.endpoint
@@ -146,6 +152,7 @@ class ContainerViewSet(NetBoxModelViewSet):
146
152
  "ports",
147
153
  "labels",
148
154
  "log_driver_options",
155
+ "sysctls",
149
156
  "tags",
150
157
  )
151
158
  filterset_class = filtersets.ContainerFilterSet
@@ -17,6 +17,7 @@ from .models.container import (
17
17
  NetworkSetting,
18
18
  Device,
19
19
  LogDriverOption,
20
+ Sysctl,
20
21
  )
21
22
  from .models.registry import Registry
22
23
 
@@ -340,3 +341,22 @@ class DeviceFilterSet(BaseFilterSet):
340
341
 
341
342
  model = Device
342
343
  fields = ("id",)
344
+
345
+
346
+ class SysctlFilterSet(BaseFilterSet):
347
+ """Sysctl filterset definition class"""
348
+
349
+ container_id = ModelMultipleChoiceFilter(
350
+ field_name="container_id",
351
+ queryset=Container.objects.all(),
352
+ label="Container (ID)",
353
+ )
354
+
355
+ class Meta:
356
+ """Sysctl filterset definition meta class"""
357
+
358
+ model = Sysctl
359
+ fields = (
360
+ "id",
361
+ "key",
362
+ )
@@ -0,0 +1,28 @@
1
+ """Sysctl Form definition"""
2
+
3
+ from django import forms
4
+ from utilities.forms.fields import DynamicModelChoiceField
5
+ from ..models.container import Sysctl, Container
6
+
7
+
8
+ class SysctlForm(forms.ModelForm):
9
+ """Sysctl form definition class"""
10
+
11
+ container = DynamicModelChoiceField(
12
+ label="Container", queryset=Container.objects.all(), required=True
13
+ )
14
+
15
+ class Meta:
16
+ """Sysctl form definition Meta class"""
17
+
18
+ model = Sysctl
19
+ fields = (
20
+ "container",
21
+ "key",
22
+ "value",
23
+ )
24
+ labels = {
25
+ "container": "Container",
26
+ "key": "Key",
27
+ "value": "Value",
28
+ }
@@ -0,0 +1,67 @@
1
+ # pylint: disable=C0103
2
+ # pylint: disable=R0801
3
+ """Migration file"""
4
+
5
+ import django.core.validators
6
+ import django.db.models.deletion
7
+ from django.db import migrations, models
8
+
9
+
10
+ class Migration(migrations.Migration):
11
+ """Migration file"""
12
+
13
+ dependencies = [
14
+ ("netbox_docker_plugin", "1040_alter_container_log_driver"),
15
+ ]
16
+
17
+ operations = [
18
+ migrations.CreateModel(
19
+ name="Sysctl",
20
+ fields=[
21
+ (
22
+ "id",
23
+ models.BigAutoField(
24
+ auto_created=True, primary_key=True, serialize=False
25
+ ),
26
+ ),
27
+ (
28
+ "key",
29
+ models.CharField(
30
+ max_length=255,
31
+ validators=[
32
+ django.core.validators.MinLengthValidator(limit_value=1),
33
+ django.core.validators.MaxLengthValidator(limit_value=255),
34
+ ],
35
+ ),
36
+ ),
37
+ (
38
+ "value",
39
+ models.CharField(
40
+ blank=True,
41
+ max_length=4095,
42
+ validators=[
43
+ django.core.validators.MaxLengthValidator(limit_value=4095)
44
+ ],
45
+ ),
46
+ ),
47
+ (
48
+ "container",
49
+ models.ForeignKey(
50
+ on_delete=django.db.models.deletion.CASCADE,
51
+ related_name="sysctls",
52
+ to="netbox_docker_plugin.container",
53
+ ),
54
+ ),
55
+ ],
56
+ options={
57
+ "ordering": ("container", "key"),
58
+ "constraints": [
59
+ models.UniqueConstraint(
60
+ models.F("key"),
61
+ models.F("container"),
62
+ name="netbox_docker_plugin_sysctl_unique_key_container'",
63
+ )
64
+ ],
65
+ },
66
+ ),
67
+ ]
@@ -95,6 +95,20 @@ class ContainerCapAddChoices(ChoiceSet):
95
95
 
96
96
  CHOICES = [
97
97
  ("NET_ADMIN", "NET_ADMIN"),
98
+ ("CHOWN", "CHOWN"),
99
+ ("DAC_OVERRIDE", "DAC_OVERRIDE"),
100
+ ("DAC_READ_SEARCH", "DAC_READ_SEARCH"),
101
+ ("FOWNER", "FOWNER"),
102
+ ("FSETID", "FSETID"),
103
+ ("KILL", "KILL"),
104
+ ("NET_RAW", "NET_RAW"),
105
+ ("SETFCAP", "SETFCAP"),
106
+ ("SETGID", "SETGID"),
107
+ ("SETUID", "SETUID"),
108
+ ("SYS_ADMIN", "SYS_ADMIN"),
109
+ ("SYS_CHROOT", "SYS_CHROOT"),
110
+ ("SYS_PTRACE", "SYS_PTRACE"),
111
+ ("SYS_RESOURCE", "SYS_RESOURCE"),
98
112
  ]
99
113
 
100
114
 
@@ -161,9 +175,7 @@ class Container(NetBoxModel):
161
175
  blank=True,
162
176
  )
163
177
  cmd = ArrayField(
164
- models.CharField(
165
- max_length=1024, blank=True, null=True
166
- ),
178
+ models.CharField(max_length=1024, blank=True, null=True),
167
179
  null=True,
168
180
  blank=True,
169
181
  )
@@ -571,3 +583,42 @@ class Device(models.Model):
571
583
 
572
584
  def __str__(self):
573
585
  return f"{self.host_path}:{self.container_path}"
586
+
587
+
588
+ class Sysctl(models.Model):
589
+ """SysCtl definition class"""
590
+
591
+ objects = RestrictedQuerySet.as_manager()
592
+
593
+ container = models.ForeignKey(
594
+ Container, on_delete=models.CASCADE, related_name="sysctls"
595
+ )
596
+ key = models.CharField(
597
+ max_length=255,
598
+ validators=[
599
+ MinLengthValidator(limit_value=1),
600
+ MaxLengthValidator(limit_value=255),
601
+ ],
602
+ )
603
+ value = models.CharField(
604
+ max_length=4095,
605
+ validators=[
606
+ MaxLengthValidator(limit_value=4095),
607
+ ],
608
+ blank=True,
609
+ )
610
+
611
+ class Meta:
612
+ """SysCtl Model Meta Class"""
613
+
614
+ ordering = ("container", "key")
615
+ constraints = (
616
+ models.UniqueConstraint(
617
+ "key",
618
+ "container",
619
+ name="%(app_label)s_%(class)s_unique_key_container'",
620
+ ),
621
+ )
622
+
623
+ def __str__(self):
624
+ return f"{self.key}"
@@ -16,6 +16,7 @@ from .models.container import (
16
16
  NetworkSetting,
17
17
  Device,
18
18
  LogDriverOption,
19
+ Sysctl,
19
20
  )
20
21
  from .models.registry import Registry
21
22
  from .templatetags.host import remove_password
@@ -461,3 +462,18 @@ class DeviceTable(NetBoxTable):
461
462
  "host_path",
462
463
  "container_path",
463
464
  )
465
+
466
+
467
+ class SysctlTable(NetBoxTable):
468
+ """Sysctl Table definition class"""
469
+
470
+ container = tables.Column(linkify=True)
471
+
472
+ actions = columns.ActionsColumn(actions=("edit", "delete"))
473
+
474
+ class Meta(NetBoxTable.Meta):
475
+ """Sysctl Table definition Meta class"""
476
+
477
+ model = Sysctl
478
+ fields = ("container", "key", "value")
479
+ default_columns = ("container", "key", "value")
@@ -67,11 +67,12 @@
67
67
  <h2 class="card-header">
68
68
  Label
69
69
  {% if perms.netbox_docker_plugin.add_env %}
70
- <div class="card-actions">
71
- <a href="{% url 'plugins:netbox_docker_plugin:label_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
72
- <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add a Label
73
- </a>
74
- </div>
70
+ <div class="card-actions">
71
+ <a href="{% url 'plugins:netbox_docker_plugin:label_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}"
72
+ class="btn btn-ghost-primary btn-sm">
73
+ <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add a Label
74
+ </a>
75
+ </div>
75
76
  {% endif %}
76
77
  </h2>
77
78
  {% htmx_table 'plugins:netbox_docker_plugin:label_list' container_id=object.pk %}
@@ -85,11 +86,12 @@
85
86
  <h2 class="card-header">
86
87
  Environment Variables
87
88
  {% if perms.netbox_docker_plugin.add_env %}
88
- <div class="card-actions">
89
- <a href="{% url 'plugins:netbox_docker_plugin:env_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
90
- <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add an Env Variable
91
- </a>
92
- </div>
89
+ <div class="card-actions">
90
+ <a href="{% url 'plugins:netbox_docker_plugin:env_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}"
91
+ class="btn btn-ghost-primary btn-sm">
92
+ <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add an Env Variable
93
+ </a>
94
+ </div>
93
95
  {% endif %}
94
96
  </h2>
95
97
  {% htmx_table 'plugins:netbox_docker_plugin:env_list' container_id=object.pk %}
@@ -103,11 +105,12 @@
103
105
  <h2 class="card-header">
104
106
  Port Mappings
105
107
  {% if perms.netbox_docker_plugin.add_env %}
106
- <div class="card-actions">
107
- <a href="{% url 'plugins:netbox_docker_plugin:port_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
108
- <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add a Port Mapping
109
- </a>
110
- </div>
108
+ <div class="card-actions">
109
+ <a href="{% url 'plugins:netbox_docker_plugin:port_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}"
110
+ class="btn btn-ghost-primary btn-sm">
111
+ <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add a Port Mapping
112
+ </a>
113
+ </div>
111
114
  {% endif %}
112
115
  </h2>
113
116
  {% htmx_table 'plugins:netbox_docker_plugin:port_list' container_id=object.pk %}
@@ -119,11 +122,12 @@
119
122
  <h2 class="card-header">
120
123
  Network Settings
121
124
  {% if perms.netbox_docker_plugin.add_network_setting %}
122
- <div class="card-actions">
123
- <a href="{% url 'plugins:netbox_docker_plugin:networksetting_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
124
- <i class="mdi mdi-plus-thick" aria-hidden="true"></i> New Network Setting
125
- </a>
126
- </div>
125
+ <div class="card-actions">
126
+ <a href="{% url 'plugins:netbox_docker_plugin:networksetting_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}"
127
+ class="btn btn-ghost-primary btn-sm">
128
+ <i class="mdi mdi-plus-thick" aria-hidden="true"></i> New Network Setting
129
+ </a>
130
+ </div>
127
131
  {% endif %}
128
132
  </h2>
129
133
  {% htmx_table 'plugins:netbox_docker_plugin:networksetting_list' container_id=object.pk %}
@@ -137,11 +141,12 @@
137
141
  <h2 class="card-header">
138
142
  Mount
139
143
  {% if perms.netbox_docker_plugin.add_env %}
140
- <div class="card-actions">
141
- <a href="{% url 'plugins:netbox_docker_plugin:mount_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
142
- <i class="mdi mdi-plus-thick" aria-hidden="true"></i> New Mount
143
- </a>
144
- </div>
144
+ <div class="card-actions">
145
+ <a href="{% url 'plugins:netbox_docker_plugin:mount_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}"
146
+ class="btn btn-ghost-primary btn-sm">
147
+ <i class="mdi mdi-plus-thick" aria-hidden="true"></i> New Mount
148
+ </a>
149
+ </div>
145
150
  {% endif %}
146
151
  </h2>
147
152
  {% htmx_table 'plugins:netbox_docker_plugin:mount_list' container_id=object.pk %}
@@ -152,11 +157,12 @@
152
157
  <h2 class="card-header">
153
158
  Binds
154
159
  {% if perms.netbox_docker_plugin.add_env %}
155
- <div class="card-actions">
156
- <a href="{% url 'plugins:netbox_docker_plugin:bind_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
157
- <i class="mdi mdi-plus-thick" aria-hidden="true"></i> New Bind
158
- </a>
159
- </div>
160
+ <div class="card-actions">
161
+ <a href="{% url 'plugins:netbox_docker_plugin:bind_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}"
162
+ class="btn btn-ghost-primary btn-sm">
163
+ <i class="mdi mdi-plus-thick" aria-hidden="true"></i> New Bind
164
+ </a>
165
+ </div>
160
166
  {% endif %}
161
167
  </h2>
162
168
  {% htmx_table 'plugins:netbox_docker_plugin:bind_list' container_id=object.pk %}
@@ -170,11 +176,12 @@
170
176
  <h2 class="card-header">
171
177
  Devices
172
178
  {% if perms.netbox_docker_plugin.add_device %}
173
- <div class="card-actions">
174
- <a href="{% url 'plugins:netbox_docker_plugin:device_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
175
- <i class="mdi mdi-plus-thick" aria-hidden="true"></i> New Device
176
- </a>
177
- </div>
179
+ <div class="card-actions">
180
+ <a href="{% url 'plugins:netbox_docker_plugin:device_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}"
181
+ class="btn btn-ghost-primary btn-sm">
182
+ <i class="mdi mdi-plus-thick" aria-hidden="true"></i> New Device
183
+ </a>
184
+ </div>
178
185
  {% endif %}
179
186
  </h2>
180
187
  {% htmx_table 'plugins:netbox_docker_plugin:device_list' container_id=object.pk %}
@@ -185,15 +192,32 @@
185
192
  <h2 class="card-header">
186
193
  Log Driver Options
187
194
  {% if perms.netbox_docker_plugin.add_env %}
188
- <div class="card-actions">
189
- <a href="{% url 'plugins:netbox_docker_plugin:logdriveroption_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-ghost-primary btn-sm">
190
- <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add a Logging driver option
191
- </a>
192
- </div>
195
+ <div class="card-actions">
196
+ <a href="{% url 'plugins:netbox_docker_plugin:logdriveroption_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}"
197
+ class="btn btn-ghost-primary btn-sm">
198
+ <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add a Logging driver option
199
+ </a>
200
+ </div>
193
201
  {% endif %}
194
202
  </h2>
195
203
  {% htmx_table 'plugins:netbox_docker_plugin:logdriveroption_list' container_id=object.pk %}
196
204
  </div>
197
205
  </div>
206
+ <div class="col col-md-6">
207
+ <div class="card">
208
+ <h2 class="card-header">
209
+ Sysctl
210
+ {% if perms.netbox_docker_plugin.add_device %}
211
+ <div class="card-actions">
212
+ <a href="{% url 'plugins:netbox_docker_plugin:sysctl_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}"
213
+ class="btn btn-ghost-primary btn-sm">
214
+ <i class="mdi mdi-plus-thick" aria-hidden="true"></i> New Sysctl value
215
+ </a>
216
+ </div>
217
+ {% endif %}
218
+ </h2>
219
+ {% htmx_table 'plugins:netbox_docker_plugin:sysctl_list' container_id=object.pk %}
220
+ </div>
221
+ </div>
198
222
  </div>
199
223
  {% endblock content %}
@@ -51,6 +51,7 @@ class ContainerApiTestCase(
51
51
  "network_settings",
52
52
  "devices",
53
53
  "log_driver_options",
54
+ "sysctls",
54
55
  ]
55
56
 
56
57
  @classmethod
@@ -96,7 +97,7 @@ class ContainerApiTestCase(
96
97
  name="container3",
97
98
  operation="none",
98
99
  state="created",
99
- cmd=["ls", "-al"]
100
+ cmd=["ls", "-al"],
100
101
  )
101
102
 
102
103
  cls.create_data = [
@@ -135,6 +136,10 @@ class ContainerApiTestCase(
135
136
  "log_driver_options": [
136
137
  {"option_name": "syslog-address", "value": "udp://127.0.0.1:514"},
137
138
  ],
139
+ "sysctls": [
140
+ {"key": "net.ipv4.ip_forward", "value": "1"},
141
+ {"key": "kernel.panic", "value": "5"},
142
+ ],
138
143
  },
139
144
  {
140
145
  "host": host2.pk,
@@ -159,6 +164,7 @@ class ContainerApiTestCase(
159
164
  "ports": [],
160
165
  "env": [],
161
166
  "labels": [],
167
+ "sysctls": [],
162
168
  "mounts": [
163
169
  {"source": "/data", "volume": volume3.pk},
164
170
  {"source": "/etc", "volume": volume3.pk},
@@ -172,6 +178,7 @@ class ContainerApiTestCase(
172
178
  "ports": [],
173
179
  "env": [],
174
180
  "labels": [],
181
+ "sysctls": [],
175
182
  "mounts": [
176
183
  {"source": "/data", "volume": volume3.pk},
177
184
  {"source": "/etc", "volume": volume3.pk},
@@ -184,8 +191,9 @@ class ContainerApiTestCase(
184
191
  "ports": [],
185
192
  "env": [{"var_name": "ENV", "value": ""}],
186
193
  "labels": [],
194
+ "sysctls": [],
187
195
  "cap_add": ["NET_ADMIN"],
188
- "cmd": ["cat", "/etc/hosts"]
196
+ "cmd": ["cat", "/etc/hosts"],
189
197
  },
190
198
  ]
191
199
 
@@ -123,6 +123,35 @@ class HostApiTestCase(
123
123
  objectchanges[1].action, ObjectChangeActionChoices.ACTION_UPDATE
124
124
  )
125
125
 
126
+ def test_bulk_delete_objects(self):
127
+ """
128
+ DELETE a set of objects in a single request.
129
+ """
130
+ # Add object-level permission
131
+ obj_perm = ObjectPermission(name="Test permission", actions=["delete"])
132
+ obj_perm.save()
133
+ # pylint: disable=E1101
134
+ obj_perm.users.add(self.user)
135
+ # pylint: disable=E1101
136
+ obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model))
137
+
138
+ # Target the three most recently created objects to avoid triggering recursive deletions
139
+ # (e.g. with MPTT objects)
140
+ id_list = list(
141
+ self._get_queryset().order_by("-id").values_list("id", flat=True)[:3]
142
+ )
143
+ self.assertEqual(
144
+ len(id_list), 3, "Insufficient number of objects to test bulk deletion"
145
+ )
146
+ data = [{"id": id} for id in id_list]
147
+
148
+ initial_count = self._get_queryset().count()
149
+ response = self.client.delete(
150
+ self._get_list_url(), data, format="json", **self.header
151
+ )
152
+ self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
153
+ self.assertEqual(self._get_queryset().count(), initial_count - 3)
154
+
126
155
  @classmethod
127
156
  def setUpTestData(cls) -> None:
128
157
  Host.objects.create(endpoint="http://localhost:8080", name="host1")
@@ -65,6 +65,27 @@ class HostViewsTestCase(BaseModelViewTestCase, ViewTestCases.PrimaryObjectViewTe
65
65
  objectchanges[1].action, ObjectChangeActionChoices.ACTION_UPDATE
66
66
  )
67
67
 
68
+ def test_bulk_delete_objects_with_permission(self):
69
+ pk_list = list(self._get_queryset().values_list("pk", flat=True))[:3]
70
+ data = {
71
+ "pk": pk_list,
72
+ "confirm": True,
73
+ "_confirm": True, # Form button
74
+ }
75
+
76
+ # Assign unconstrained permission
77
+ obj_perm = ObjectPermission(name="Test permission", actions=["delete"])
78
+ obj_perm.save()
79
+ # pylint: disable=E1101
80
+ obj_perm.users.add(self.user)
81
+ # pylint: disable=E1101
82
+ obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model))
83
+
84
+ # Try POST with model-level permission
85
+ response = self.client.post(self._get_url("bulk_delete"), data)
86
+ self.assertHttpStatus(response, 302)
87
+ self.assertFalse(self._get_queryset().filter(pk__in=pk_list).exists())
88
+
68
89
  @classmethod
69
90
  def setUpTestData(cls):
70
91
  host1 = Host.objects.create(endpoint="http://localhost:8080", name="host1")