netbox-docker-plugin 2.0.0__tar.gz → 2.5.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 (126) hide show
  1. {netbox_docker_plugin-2.0.0/netbox_docker_plugin.egg-info → netbox_docker_plugin-2.5.0}/PKG-INFO +1 -1
  2. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/__init__.py +1 -1
  3. netbox_docker_plugin-2.5.0/netbox_docker_plugin/api/renderers.py +19 -0
  4. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/api/views.py +57 -15
  5. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/container.html +42 -20
  6. netbox_docker_plugin-2.5.0/netbox_docker_plugin/templates/netbox_docker_plugin/host-graph.html +211 -0
  7. netbox_docker_plugin-2.5.0/netbox_docker_plugin/templates/netbox_docker_plugin/image.html +122 -0
  8. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/test_container_api.py +2 -2
  9. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/image/test_image_api.py +37 -0
  10. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/urls.py +5 -0
  11. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/utilities/__init__.py +13 -0
  12. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/host.py +11 -0
  13. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0/netbox_docker_plugin.egg-info}/PKG-INFO +1 -1
  14. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin.egg-info/SOURCES.txt +1 -0
  15. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/pyproject.toml +1 -1
  16. netbox_docker_plugin-2.0.0/netbox_docker_plugin/api/renderers.py +0 -14
  17. netbox_docker_plugin-2.0.0/netbox_docker_plugin/templates/netbox_docker_plugin/image.html +0 -59
  18. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/LICENSE +0 -0
  19. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/MANIFEST.in +0 -0
  20. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/README.md +0 -0
  21. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/api/__init__.py +0 -0
  22. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/api/serializers.py +0 -0
  23. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/api/urls.py +0 -0
  24. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/filtersets.py +0 -0
  25. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/__init__.py +0 -0
  26. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/bind.py +0 -0
  27. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/container.py +0 -0
  28. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/env.py +0 -0
  29. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/host.py +0 -0
  30. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/image.py +0 -0
  31. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/label.py +0 -0
  32. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/mount.py +0 -0
  33. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/network.py +0 -0
  34. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/network_setting.py +0 -0
  35. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/port.py +0 -0
  36. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/registry.py +0 -0
  37. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/volume.py +0 -0
  38. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0001_initial.py +0 -0
  39. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0002_image.py +0 -0
  40. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0003_image_size.py +0 -0
  41. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0004_volume.py +0 -0
  42. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0005_network.py +0 -0
  43. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0006_container_port_mount_label_env_and_more.py +0 -0
  44. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0007_remove_container_netbox_docker_plugin_container_unique_name_host__and_more.py +0 -0
  45. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0008_alter_network_unique_together_network_networkid_and_more.py +0 -0
  46. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0009_remove_container_network_networksettings_and_more.py +0 -0
  47. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0010_container_containerid_and_more.py +0 -0
  48. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0011_host_token.py +0 -0
  49. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0012_host_state.py +0 -0
  50. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0013_host_netbox_url.py +0 -0
  51. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0014_container_operation.py +0 -0
  52. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0015_remove_mount_netbox_docker_plugin_mount_unique_volume_and_more.py +0 -0
  53. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0016_alter_env_value.py +0 -0
  54. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0017_network_state.py +0 -0
  55. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0018_alter_mount_volume_alter_networksetting_network.py +0 -0
  56. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0019_host_agent_version_host_docker_api_version.py +0 -0
  57. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0020_container_hostname.py +0 -0
  58. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0021_registry_and_more.py +0 -0
  59. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0022_bind_bind_netbox_docker_plugin_bind_unique_bind.py +0 -0
  60. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0023_delete_hosts.py +0 -0
  61. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0024_registry_host_alter_registry_name_and_more.py +0 -0
  62. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0025_alter_image_version.py +0 -0
  63. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0026_image_digest.py +0 -0
  64. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0027_container_restart_policy.py +0 -0
  65. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0028_mount_and_bind_read_only.py +0 -0
  66. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0029_alter_container_state.py +0 -0
  67. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0030_alter_container_containerid_alter_container_hostname_and_more.py +0 -0
  68. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/__init__.py +0 -0
  69. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/__init__.py +0 -0
  70. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/container.py +0 -0
  71. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/host.py +0 -0
  72. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/image.py +0 -0
  73. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/network.py +0 -0
  74. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/registry.py +0 -0
  75. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/volume.py +0 -0
  76. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/navigation.py +0 -0
  77. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/signals.py +0 -0
  78. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tables.py +0 -0
  79. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/container-exec.html +0 -0
  80. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/container-layout.html +0 -0
  81. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/container-logs.html +0 -0
  82. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/host.html +0 -0
  83. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/network.html +0 -0
  84. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/registry.html +0 -0
  85. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/volume.html +0 -0
  86. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templatetags/__init__.py +0 -0
  87. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templatetags/host.py +0 -0
  88. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/__init__.py +0 -0
  89. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/base.py +0 -0
  90. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/__init__.py +0 -0
  91. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/test_container_actions.py +0 -0
  92. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/test_container_api_exec.py +0 -0
  93. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/test_container_operation_view.py +0 -0
  94. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/test_container_validation.py +0 -0
  95. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/test_container_views.py +0 -0
  96. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/host/__init__.py +0 -0
  97. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/host/test_host_api.py +0 -0
  98. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/host/test_host_views.py +0 -0
  99. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/host/test_replace_password.py +0 -0
  100. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/image/__init__.py +0 -0
  101. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/image/test_image_views.py +0 -0
  102. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/network/__init__.py +0 -0
  103. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/network/test_network_api.py +0 -0
  104. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/network/test_network_views.py +0 -0
  105. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/registry/__init__.py +0 -0
  106. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/registry/test_registry_api.py +0 -0
  107. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/registry/test_registry_views.py +0 -0
  108. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/test_init.py +0 -0
  109. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/volume/__init__.py +0 -0
  110. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/volume/test_volume_api.py +0 -0
  111. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/volume/test_volume_views.py +0 -0
  112. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/__init__.py +0 -0
  113. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/bind.py +0 -0
  114. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/container.py +0 -0
  115. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/env.py +0 -0
  116. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/image.py +0 -0
  117. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/label.py +0 -0
  118. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/mount.py +0 -0
  119. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/network.py +0 -0
  120. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/network_setting.py +0 -0
  121. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/port.py +0 -0
  122. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/registry.py +0 -0
  123. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/volume.py +0 -0
  124. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin.egg-info/dependency_links.txt +0 -0
  125. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin.egg-info/top_level.txt +0 -0
  126. {netbox_docker_plugin-2.0.0 → netbox_docker_plugin-2.5.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: netbox-docker-plugin
3
- Version: 2.0.0
3
+ Version: 2.5.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
@@ -10,7 +10,7 @@ class NetBoxDockerConfig(PluginConfig):
10
10
  name = "netbox_docker_plugin"
11
11
  verbose_name = " NetBox Docker Plugin"
12
12
  description = "Manage Docker"
13
- version = "2.0.0"
13
+ version = "2.5.0"
14
14
  base_url = "docker"
15
15
  min_version = "4.0.0"
16
16
  author= "Vincent Simonin <vincent@saashup.com>, David Delassus <david.jose.delassus@gmail.com>"
@@ -0,0 +1,19 @@
1
+ """ Response renderers for Django Rest Framework """
2
+
3
+ from django.utils.encoding import smart_str
4
+ from rest_framework import renderers
5
+
6
+
7
+ def make_content_type_renderer(content_type: str, api_format: str):
8
+ """ Create a response renderer for the specific content type """
9
+
10
+ class ContentTypeRenderer(renderers.BaseRenderer):
11
+ """ content type renderer """
12
+
13
+ media_type = content_type
14
+ format = api_format
15
+
16
+ def render(self, data, accepted_media_type=None, renderer_context=None):
17
+ return smart_str(data, encoding=self.charset)
18
+
19
+ return ContentTypeRenderer
@@ -3,6 +3,7 @@
3
3
  from collections.abc import Sequence
4
4
  import json
5
5
  import requests
6
+ from django.utils.html import escape
6
7
  from rest_framework import status
7
8
  from rest_framework.renderers import JSONRenderer
8
9
  from rest_framework.decorators import action
@@ -15,7 +16,7 @@ from drf_spectacular.utils import (
15
16
  from users.models import Token
16
17
  from netbox.api.viewsets import NetBoxModelViewSet
17
18
  from .. import filtersets
18
- from .renderers import PlainTextRenderer
19
+ from .renderers import make_content_type_renderer
19
20
  from .serializers import (
20
21
  HostSerializer,
21
22
  ImageSerializer,
@@ -71,6 +72,47 @@ class ImageViewSet(NetBoxModelViewSet):
71
72
  serializer_class = ImageSerializer
72
73
  http_method_names = ["get", "post", "patch", "delete", "options"]
73
74
 
75
+ @extend_schema(
76
+ operation_id="plugins_docker_image_force_pull",
77
+ responses={
78
+ (200, "application/json"): OpenApiResponse(response=str),
79
+ (502, "application/json"): OpenApiResponse(response=str),
80
+ },
81
+ )
82
+ @action(
83
+ detail=True,
84
+ methods=["post"],
85
+ renderer_classes=[JSONRenderer],
86
+ )
87
+ def force_pull(self, _request, **_kwargs):
88
+ """ Force pull an existing image """
89
+
90
+ image: Image = self.get_object()
91
+ agent_url = image.host.endpoint
92
+
93
+ url = f"{agent_url}/api/engine/images"
94
+
95
+ try:
96
+ serializer = self.get_serializer(image)
97
+ data = serializer.data
98
+ data["force"] = True
99
+
100
+ resp = requests.post(url, timeout=10, json={"data": data})
101
+ resp.raise_for_status()
102
+
103
+ except requests.HTTPError:
104
+ return Response(
105
+ {"success": False, "payload": resp.text},
106
+ status=status.HTTP_502_BAD_GATEWAY,
107
+ content_type="application/json",
108
+ )
109
+
110
+ return Response(
111
+ {"success": True, "payload": resp.json()},
112
+ status=status.HTTP_200_OK,
113
+ content_type="application/json",
114
+ )
115
+
74
116
 
75
117
  class VolumeViewSet(NetBoxModelViewSet):
76
118
  """Volume view set class"""
@@ -111,23 +153,23 @@ class ContainerViewSet(NetBoxModelViewSet):
111
153
  @extend_schema(
112
154
  operation_id="plugins_docker_container_logs",
113
155
  responses={
114
- (200, "text/plain"): OpenApiResponse(
156
+ (200, "text/html"): OpenApiResponse(
115
157
  response=str,
116
158
  examples=[
117
159
  OpenApiExample(
118
160
  "Container's logs",
119
161
  value="Hello World",
120
- media_type="text/plain",
162
+ media_type="text/html",
121
163
  ),
122
164
  ],
123
165
  ),
124
- (502, "text/plain"): OpenApiResponse(
166
+ (502, "text/html"): OpenApiResponse(
125
167
  response=str,
126
168
  examples=[
127
169
  OpenApiExample(
128
170
  "Engine error",
129
171
  value="Error as returned by Agent",
130
- media_type="text/plain",
172
+ media_type="text/html",
131
173
  ),
132
174
  ],
133
175
  ),
@@ -136,7 +178,7 @@ class ContainerViewSet(NetBoxModelViewSet):
136
178
  @action(
137
179
  detail=True,
138
180
  methods=["get"],
139
- renderer_classes=[PlainTextRenderer],
181
+ renderer_classes=[make_content_type_renderer("text/html", "txt")],
140
182
  )
141
183
  def logs(self, _request, **_kwargs):
142
184
  """Fetch container's logs"""
@@ -153,22 +195,22 @@ class ContainerViewSet(NetBoxModelViewSet):
153
195
 
154
196
  except requests.HTTPError:
155
197
  return Response(
156
- resp.text,
198
+ escape(resp.text),
157
199
  status=status.HTTP_502_BAD_GATEWAY,
158
- content_type="text/plain",
200
+ content_type="text/html",
159
201
  )
160
202
 
161
203
  return Response(
162
- resp.text,
204
+ escape(resp.text),
163
205
  status=status.HTTP_200_OK,
164
- content_type="text/plain",
206
+ content_type="text/html",
165
207
  )
166
208
 
167
209
  @extend_schema(
168
210
  operation_id="plugins_docker_container_exec",
169
211
  request=ContainerCommandSerializer,
170
212
  responses={
171
- (200, "text/plain"): OpenApiResponse(
213
+ (200, "application/json"): OpenApiResponse(
172
214
  response=str,
173
215
  examples=[
174
216
  OpenApiExample(
@@ -178,13 +220,13 @@ class ContainerViewSet(NetBoxModelViewSet):
178
220
  ),
179
221
  ],
180
222
  ),
181
- (502, "text/plain"): OpenApiResponse(
223
+ (502, "text/html"): OpenApiResponse(
182
224
  response=str,
183
225
  examples=[
184
226
  OpenApiExample(
185
227
  "Engine error",
186
228
  value="Error as returned by Agent",
187
- media_type="text/plain",
229
+ media_type="text/html",
188
230
  ),
189
231
  ],
190
232
  ),
@@ -215,9 +257,9 @@ class ContainerViewSet(NetBoxModelViewSet):
215
257
 
216
258
  except requests.HTTPError:
217
259
  return Response(
218
- resp.text,
260
+ escape(resp.text),
219
261
  status=status.HTTP_502_BAD_GATEWAY,
220
- content_type="text/plain",
262
+ content_type="text/html",
221
263
  )
222
264
 
223
265
  return Response(data=json.loads(resp.text))
@@ -54,47 +54,61 @@
54
54
  </div>
55
55
  <div class="col col-md-6">
56
56
  <div class="card">
57
- <h5 class="card-header">PORT MAPPINGS</h5>
57
+ <h5 class="card-header">LABELS</h5>
58
58
  <div class="card-body htmx-container table-responsive"
59
- hx-get="{% url 'plugins:netbox_docker_plugin:port_list' %}?container_id={{ object.pk }}"
59
+ hx-get="{% url 'plugins:netbox_docker_plugin:label_list' %}?container_id={{ object.pk }}"
60
60
  hx-trigger="load"
61
61
  ></div>
62
62
  {% if perms.netbox_docker_plugin.add_env %}
63
63
  <div class="card-footer text-end noprint">
64
- <a href="{% url 'plugins:netbox_docker_plugin:port_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
65
- <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add a Port Mapping
64
+ <a href="{% url 'plugins:netbox_docker_plugin:label_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
65
+ <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add a Label
66
66
  </a>
67
67
  </div>
68
68
  {% endif %}
69
69
  </div>
70
+ </div>
71
+ </div>
72
+
73
+ <h2>Runtime</h2>
74
+ <div class="row mb-3">
75
+ <div class="col col-md-6">
70
76
  <div class="card">
71
- <h5 class="card-header">MOUNTS</h5>
77
+ <h5 class="card-header">ENVIRONMENT VARIABLES</h5>
72
78
  <div class="card-body htmx-container table-responsive"
73
- hx-get="{% url 'plugins:netbox_docker_plugin:mount_list' %}?container_id={{ object.pk }}"
79
+ hx-get="{% url 'plugins:netbox_docker_plugin:env_list' %}?container_id={{ object.pk }}"
74
80
  hx-trigger="load"
75
81
  ></div>
76
82
  {% if perms.netbox_docker_plugin.add_env %}
77
83
  <div class="card-footer text-end noprint">
78
- <a href="{% url 'plugins:netbox_docker_plugin:mount_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
79
- <i class="mdi mdi-plus-thick" aria-hidden="true"></i> New Mount
84
+ <a href="{% url 'plugins:netbox_docker_plugin:env_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
85
+ <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add an Env Variable
80
86
  </a>
81
87
  </div>
82
88
  {% endif %}
83
89
  </div>
90
+ </div>
91
+ </div>
92
+
93
+ <h2>Networking</h2>
94
+ <div class="row mb-3">
95
+ <div class="col col-md-6">
84
96
  <div class="card">
85
- <h5 class="card-header">BINDS</h5>
97
+ <h5 class="card-header">PORT MAPPINGS</h5>
86
98
  <div class="card-body htmx-container table-responsive"
87
- hx-get="{% url 'plugins:netbox_docker_plugin:bind_list' %}?container_id={{ object.pk }}"
99
+ hx-get="{% url 'plugins:netbox_docker_plugin:port_list' %}?container_id={{ object.pk }}"
88
100
  hx-trigger="load"
89
101
  ></div>
90
102
  {% if perms.netbox_docker_plugin.add_env %}
91
103
  <div class="card-footer text-end noprint">
92
- <a href="{% url 'plugins:netbox_docker_plugin:bind_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
93
- <i class="mdi mdi-plus-thick" aria-hidden="true"></i> New Bind
104
+ <a href="{% url 'plugins:netbox_docker_plugin:port_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
105
+ <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add a Port Mapping
94
106
  </a>
95
107
  </div>
96
108
  {% endif %}
97
109
  </div>
110
+ </div>
111
+ <div class="col col-md-6">
98
112
  <div class="card">
99
113
  <h5 class="card-header">NETWORK SETTINGS</h5>
100
114
  <div class="card-body htmx-container table-responsive"
@@ -109,30 +123,38 @@
109
123
  </div>
110
124
  {% endif %}
111
125
  </div>
126
+ </div>
127
+ </div>
128
+
129
+ <h2>Storage</h2>
130
+ <div class="row mb-3">
131
+ <div class="col col-md-6">
112
132
  <div class="card">
113
- <h5 class="card-header">ENV VARIABLE</h5>
133
+ <h5 class="card-header">MOUNTS</h5>
114
134
  <div class="card-body htmx-container table-responsive"
115
- hx-get="{% url 'plugins:netbox_docker_plugin:env_list' %}?container_id={{ object.pk }}"
135
+ hx-get="{% url 'plugins:netbox_docker_plugin:mount_list' %}?container_id={{ object.pk }}"
116
136
  hx-trigger="load"
117
137
  ></div>
118
138
  {% if perms.netbox_docker_plugin.add_env %}
119
139
  <div class="card-footer text-end noprint">
120
- <a href="{% url 'plugins:netbox_docker_plugin:env_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
121
- <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add an Env Variable
140
+ <a href="{% url 'plugins:netbox_docker_plugin:mount_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
141
+ <i class="mdi mdi-plus-thick" aria-hidden="true"></i> New Mount
122
142
  </a>
123
143
  </div>
124
144
  {% endif %}
125
145
  </div>
146
+ </div>
147
+ <div class="col col-md-6">
126
148
  <div class="card">
127
- <h5 class="card-header">LABELS</h5>
149
+ <h5 class="card-header">BINDS</h5>
128
150
  <div class="card-body htmx-container table-responsive"
129
- hx-get="{% url 'plugins:netbox_docker_plugin:label_list' %}?container_id={{ object.pk }}"
151
+ hx-get="{% url 'plugins:netbox_docker_plugin:bind_list' %}?container_id={{ object.pk }}"
130
152
  hx-trigger="load"
131
153
  ></div>
132
154
  {% if perms.netbox_docker_plugin.add_env %}
133
155
  <div class="card-footer text-end noprint">
134
- <a href="{% url 'plugins:netbox_docker_plugin:label_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
135
- <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add a Label
156
+ <a href="{% url 'plugins:netbox_docker_plugin:bind_add' %}?container={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
157
+ <i class="mdi mdi-plus-thick" aria-hidden="true"></i> New Bind
136
158
  </a>
137
159
  </div>
138
160
  {% endif %}
@@ -0,0 +1,211 @@
1
+ {% extends 'generic/object.html' %}
2
+
3
+ {% load plugins %}
4
+
5
+ {% block content %}
6
+ <div class="row mb-3">
7
+ <div class="col col-md-12">
8
+ <div class="card">
9
+ <div class="card-body">
10
+ <div
11
+ id="graph"
12
+ style="
13
+ display: block;
14
+ width: 100%;
15
+ min-height: 350px;
16
+ "
17
+ ></div>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ {% endblock %}
23
+
24
+ {% block head %}
25
+ <script
26
+ type="application/javascript"
27
+ src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.30.1/cytoscape.min.js"
28
+ integrity="sha512-kv2oCpI8sYlWvr442qCcFNgXW9swhLRlDJ/39GlfZLGAWOqgU/Kz30YCYBm0yLumVzRvYEMU6uSxFniGzitKiA=="
29
+ crossorigin="anonymous"
30
+ referrerpolicy="no-referrer"
31
+ ></script>
32
+
33
+ <script type="application/javascript">
34
+ document.addEventListener('DOMContentLoaded', () => {
35
+ const elt = document.getElementById('graph');
36
+
37
+ const cy = cytoscape({
38
+ container: elt,
39
+ elements: [
40
+ // {% for registry in object.registries.all %}
41
+ {
42
+ data: {
43
+ id: 'registry:{{ registry.pk }}',
44
+ name: '{{ registry.name }}',
45
+ },
46
+ classes: ['registry'],
47
+ },
48
+ // {% endfor %}
49
+ // {% for image in object.images.all %}
50
+ {
51
+ data: {
52
+ id: 'image:{{ image.pk }}',
53
+ name: '{{ image.name }}',
54
+ },
55
+ classes: [
56
+ 'image',
57
+ // {% if image.Digest %}
58
+ 'pulled',
59
+ // {% endif %}
60
+ ],
61
+ },
62
+ {
63
+ data: {
64
+ id: 'registry-image:{{ image.registry.pk }}:{{ image.pk }}',
65
+ source: 'registry:{{ image.registry.pk }}',
66
+ target: 'image:{{ image.pk }}',
67
+ },
68
+ },
69
+ // {% endfor %}
70
+ // {% for network in object.networks.all %}
71
+ {
72
+ data: {
73
+ id: 'network:{{ network.pk }}',
74
+ name: '{{ network.name }}',
75
+ },
76
+ classes: ['network'],
77
+ },
78
+ // {% endfor %}
79
+ // {% for volume in object.volumes.all %}
80
+ {
81
+ data: {
82
+ id: 'volume:{{ volume.pk }}',
83
+ name: '{{ volume.name }}',
84
+ },
85
+ classes: ['volume'],
86
+ },
87
+ // {% endfor %}
88
+ // {% for container in object.containers.all %}
89
+ {
90
+ data: {
91
+ id: 'container:{{ container.pk }}',
92
+ name: '{{ container.name }}',
93
+ },
94
+ classes: [
95
+ 'container',
96
+ // {% if container.state == "running" %}
97
+ 'running',
98
+ // {% endif %}
99
+ ],
100
+ },
101
+ {
102
+ data: {
103
+ id: 'image-container:{{ container.image.pk }}:{{ container.pk }}',
104
+ source: 'image:{{ container.image.pk }}',
105
+ target: 'container:{{ container.pk }}',
106
+ },
107
+ },
108
+ // {% for network in container.networks.all %}
109
+ {
110
+ data: {
111
+ id: 'network-container:{{ network.pk }}:{{ container.pk }}',
112
+ source: 'network:{{ network.pk }}',
113
+ target: 'container:{{ container.pk }}',
114
+ },
115
+ },
116
+ // {% endfor %}
117
+ // {% for volume in container.volumes.all %}
118
+ {
119
+ data: {
120
+ id: 'volume-container:{{ volume.pk }}:{{ container.pk }}',
121
+ source: 'volume:{{ volume.pk }}',
122
+ target: 'container:{{ container.pk }}',
123
+ },
124
+ },
125
+ // {% endfor %}
126
+ // {% endfor %}
127
+ ],
128
+
129
+ style: [ // the stylesheet for the graph
130
+ {
131
+ selector: 'node',
132
+ style: {
133
+ 'outline-width': 2,
134
+ 'outline-color': '#CCCCCC',
135
+ 'background-color': '#59594A',
136
+ 'label': ''
137
+ },
138
+ },
139
+ {
140
+ selector: 'node.hover',
141
+ style: {
142
+ 'label': 'data(name)',
143
+ },
144
+ },
145
+ {
146
+ selector: 'node.image',
147
+ style: {
148
+ 'background-color': '#BE6E46',
149
+ },
150
+ },
151
+ {
152
+ selector: 'node.image.pulled',
153
+ style: {
154
+ 'background-color': '#A3BFA8',
155
+ }
156
+ },
157
+ {
158
+ selector: 'node.network',
159
+ style: {
160
+ 'shape': 'hexagon',
161
+ },
162
+ },
163
+ {
164
+ selector: 'node.volume',
165
+ style: {
166
+ 'shape': 'triangle',
167
+ },
168
+ },
169
+ {
170
+ selector: 'node.container',
171
+ style: {
172
+ 'background-color': '#BE6E46',
173
+ 'shape': 'rectangle',
174
+ },
175
+ },
176
+ {
177
+ selector: 'node.container.running',
178
+ style: {
179
+ 'background-color': '#A3BFA8',
180
+ },
181
+ },
182
+ {
183
+ selector: 'edge',
184
+ style: {
185
+ 'width': 3,
186
+ 'line-color': '#ccc',
187
+ 'target-arrow-color': '#ccc',
188
+ 'target-arrow-shape': 'triangle',
189
+ 'curve-style': 'bezier'
190
+ }
191
+ }
192
+ ],
193
+
194
+ layout: {
195
+ name: 'cose',
196
+ nodeDimensionsIncludeLabels: true,
197
+ },
198
+ })
199
+
200
+ cy.on('mouseover', 'node', function (evt) {
201
+ const node = evt.target
202
+ node.addClass('hover')
203
+ })
204
+
205
+ cy.on('mouseout', 'node', function (evt) {
206
+ const node = evt.target
207
+ node.removeClass('hover')
208
+ })
209
+ })
210
+ </script>
211
+ {% endblock %}
@@ -0,0 +1,122 @@
1
+ {% extends 'generic/object.html' %}
2
+
3
+ {% load plugins %}
4
+
5
+ {% block extra_controls %}
6
+ <button
7
+ class="btn btn-sm btn-success"
8
+ onclick="
9
+ (async () => {
10
+ this.setAttribute('disabled', 'disabled')
11
+ const notificationData = {}
12
+ try {
13
+ const resp = await fetch(
14
+ '/api/plugins/docker/images/{{ object.pk }}/force_pull/',
15
+ {
16
+ method: 'POST',
17
+ headers: {
18
+ 'X-CSRFToken': window.CSRF_TOKEN,
19
+ },
20
+ },
21
+ )
22
+ const data = await resp.json()
23
+ console.log(data)
24
+ notificationData.kind = 'success'
25
+ notificationData.icon = ['mdi', 'mdi-check']
26
+ notificationData.message = 'Pulling image again'
27
+ }
28
+ catch (err) {
29
+ console.error(err)
30
+ notificationData.kind = 'danger'
31
+ notificationData.icon = ['mdi', 'mdi-alert']
32
+ notificationData.message = 'Failed to force pull image'
33
+ }
34
+ finally {
35
+ this.removeAttribute('disabled')
36
+ }
37
+ const notification = document.createElement('div')
38
+ notification.classList.add('toast', 'toast-dark', 'border-0', 'shadow-sm', 'rounded-0')
39
+ notification.setAttribute('role', 'alert')
40
+ notification.setAttribute('aria-live', 'assertive')
41
+ notification.setAttribute('aria-atomic', 'true')
42
+ notification.dataset['bsDelay'] = 10_000
43
+ const header = document.createElement('div')
44
+ header.classList.add('toast-header', `bg-${notificationData.kind}`, 'rounded-0')
45
+ const headerIcon = document.createElement('i')
46
+ headerIcon.classList.add(...notificationData.icon, 'me-2')
47
+ const headerTitle = document.createElement('span')
48
+ headerTitle.innerText = notificationData.message
49
+ const closeButton = document.createElement('button')
50
+ closeButton.classList.add('btn-close', 'me-0', 'm-auto')
51
+ closeButton.setAttribute('type', 'button')
52
+ closeButton.setAttribute('aria-label', 'Close')
53
+ closeButton.dataset['bsDismiss'] = 'toast'
54
+ header.appendChild(headerIcon)
55
+ header.appendChild(headerTitle)
56
+ header.appendChild(closeButton)
57
+ notification.appendChild(header)
58
+ document.querySelector('.toast-container').appendChild(notification)
59
+ const toast = new Toast(notification)
60
+ toast.show()
61
+ })()
62
+ "
63
+ >
64
+ <i class="mdi mdi-reload"></i> Force Pull
65
+ </button>
66
+ {% endblock %}
67
+
68
+ {% block content %}
69
+ <div class="row mb-3">
70
+ <div class="col col-md-6">
71
+ <div class="card">
72
+ <h5 class="card-header">IMAGE</h5>
73
+ <div class="card-body">
74
+ <table class="table table-hover attr-table">
75
+ <tr>
76
+ <th scope="row">Host</th>
77
+ <td><a href="{{ object.host.get_absolute_url }}">{{ object.host }}</a></td>
78
+ </tr>
79
+ <tr>
80
+ <th scope="row">Name & Version</th>
81
+ <td>{{ object.name }}:{{ object.version }}</td>
82
+ </tr>
83
+ <tr>
84
+ <th scope="row">Registry</th>
85
+ <td><a href="{{ object.registry.get_absolute_url }}">{{ object.registry }}</a></td>
86
+ </tr>
87
+ <tr>
88
+ <th scope="row">Size</th>
89
+ <td>{{ object.size }}&nbsp;MByte</td>
90
+ </tr>
91
+ <tr>
92
+ <th scope="row">ImageID</th>
93
+ <td>{{ object.ImageID|placeholder }}</td>
94
+ </tr>
95
+ <tr>
96
+ <th scope="row">Digest</th>
97
+ <td>
98
+ {% if object.Digest is None %}
99
+ {{ object.Digest|placeholder }}
100
+ {% else %}
101
+ {{ object.Digest|truncatechars:32 }}
102
+ {% endif %}
103
+ </td>
104
+ </tr>
105
+ </table>
106
+ </div>
107
+ </div>
108
+ {% include 'inc/panels/custom_fields.html' %}
109
+ {% include 'inc/panels/tags.html' %}
110
+ {% plugin_left_page object %}
111
+ </div>
112
+ <div class="col col-md-6">
113
+ <div class="card">
114
+ <h5 class="card-header">USED BY</h5>
115
+ <div class="card-body htmx-container table-responsive"
116
+ hx-get="{% url 'plugins:netbox_docker_plugin:container_list' %}?image_id={{ object.pk }}"
117
+ hx-trigger="load"
118
+ ></div>
119
+ </div>
120
+ </div>
121
+ </div>
122
+ {% endblock content %}
@@ -266,12 +266,12 @@ class ContainerApiTestCase(
266
266
  with requests_mock.Mocker() as m:
267
267
  m.get(
268
268
  f"http://localhost:8080/api/engine/containers/{container_id}/logs",
269
- text="Hello World",
269
+ text="Hello > World",
270
270
  )
271
271
 
272
272
  response = self.client.get(endpoint, **self.header)
273
273
  self.assertHttpStatus(response, status.HTTP_200_OK)
274
- self.assertEqual(response.data, "Hello World")
274
+ self.assertEqual(response.data, "Hello &gt; World")
275
275
 
276
276
  def test_that_container_host_cannot_be_changed(self):
277
277
  """Test that container's host cannot be changed"""