netbox-docker-plugin 2.1.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.
- {netbox_docker_plugin-2.1.0/netbox_docker_plugin.egg-info → netbox_docker_plugin-2.5.0}/PKG-INFO +1 -1
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/__init__.py +1 -1
- netbox_docker_plugin-2.5.0/netbox_docker_plugin/api/renderers.py +19 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/api/views.py +57 -15
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/container.html +42 -20
- netbox_docker_plugin-2.5.0/netbox_docker_plugin/templates/netbox_docker_plugin/host-graph.html +211 -0
- netbox_docker_plugin-2.5.0/netbox_docker_plugin/templates/netbox_docker_plugin/image.html +122 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/test_container_api.py +2 -2
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/image/test_image_api.py +37 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/urls.py +5 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/host.py +11 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0/netbox_docker_plugin.egg-info}/PKG-INFO +1 -1
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin.egg-info/SOURCES.txt +1 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/pyproject.toml +1 -1
- netbox_docker_plugin-2.1.0/netbox_docker_plugin/api/renderers.py +0 -14
- netbox_docker_plugin-2.1.0/netbox_docker_plugin/templates/netbox_docker_plugin/image.html +0 -59
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/LICENSE +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/MANIFEST.in +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/README.md +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/api/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/api/serializers.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/api/urls.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/filtersets.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/bind.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/container.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/env.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/host.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/image.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/label.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/mount.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/network.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/network_setting.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/port.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/registry.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/forms/volume.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0001_initial.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0002_image.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0003_image_size.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0004_volume.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0005_network.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0006_container_port_mount_label_env_and_more.py +0 -0
- {netbox_docker_plugin-2.1.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
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0008_alter_network_unique_together_network_networkid_and_more.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0009_remove_container_network_networksettings_and_more.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0010_container_containerid_and_more.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0011_host_token.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0012_host_state.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0013_host_netbox_url.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0014_container_operation.py +0 -0
- {netbox_docker_plugin-2.1.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
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0016_alter_env_value.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0017_network_state.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0018_alter_mount_volume_alter_networksetting_network.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0019_host_agent_version_host_docker_api_version.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0020_container_hostname.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0021_registry_and_more.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0022_bind_bind_netbox_docker_plugin_bind_unique_bind.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0023_delete_hosts.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0024_registry_host_alter_registry_name_and_more.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0025_alter_image_version.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0026_image_digest.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0027_container_restart_policy.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0028_mount_and_bind_read_only.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0029_alter_container_state.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/0030_alter_container_containerid_alter_container_hostname_and_more.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/migrations/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/container.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/host.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/image.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/network.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/registry.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/models/volume.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/navigation.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/signals.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tables.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/container-exec.html +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/container-layout.html +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/container-logs.html +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/host.html +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/network.html +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/registry.html +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templates/netbox_docker_plugin/volume.html +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templatetags/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/templatetags/host.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/base.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/test_container_actions.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/test_container_api_exec.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/test_container_operation_view.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/test_container_validation.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/container/test_container_views.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/host/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/host/test_host_api.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/host/test_host_views.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/host/test_replace_password.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/image/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/image/test_image_views.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/network/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/network/test_network_api.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/network/test_network_views.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/registry/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/registry/test_registry_api.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/registry/test_registry_views.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/test_init.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/volume/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/volume/test_volume_api.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/tests/volume/test_volume_views.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/utilities/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/__init__.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/bind.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/container.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/env.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/image.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/label.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/mount.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/network.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/network_setting.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/port.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/registry.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin/views/volume.py +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin.egg-info/dependency_links.txt +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/netbox_docker_plugin.egg-info/top_level.txt +0 -0
- {netbox_docker_plugin-2.1.0 → netbox_docker_plugin-2.5.0}/setup.cfg +0 -0
{netbox_docker_plugin-2.1.0/netbox_docker_plugin.egg-info → netbox_docker_plugin-2.5.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: netbox-docker-plugin
|
|
3
|
-
Version: 2.
|
|
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.
|
|
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
|
|
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/
|
|
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/
|
|
162
|
+
media_type="text/html",
|
|
121
163
|
),
|
|
122
164
|
],
|
|
123
165
|
),
|
|
124
|
-
(502, "text/
|
|
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/
|
|
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=[
|
|
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/
|
|
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/
|
|
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, "
|
|
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/
|
|
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/
|
|
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/
|
|
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">
|
|
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:
|
|
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:
|
|
65
|
-
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add a
|
|
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">
|
|
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:
|
|
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:
|
|
79
|
-
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
|
|
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">
|
|
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:
|
|
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:
|
|
93
|
-
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
|
|
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">
|
|
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:
|
|
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:
|
|
121
|
-
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
|
|
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">
|
|
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:
|
|
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:
|
|
135
|
-
<i class="mdi mdi-plus-thick" aria-hidden="true"></i>
|
|
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 %}
|
netbox_docker_plugin-2.5.0/netbox_docker_plugin/templates/netbox_docker_plugin/host-graph.html
ADDED
|
@@ -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 }} 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 > World")
|
|
275
275
|
|
|
276
276
|
def test_that_container_host_cannot_be_changed(self):
|
|
277
277
|
"""Test that container's host cannot be changed"""
|