netbox-docker-plugin 1.6.2__tar.gz → 1.8.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-1.6.2/netbox_docker_plugin.egg-info → netbox_docker_plugin-1.8.0}/PKG-INFO +2 -2
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/__init__.py +5 -3
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/api/serializers.py +3 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/bind.py +2 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/container.py +2 -24
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/mount.py +2 -0
- netbox_docker_plugin-1.8.0/netbox_docker_plugin/migrations/0028_mount_and_bind_read_only.py +25 -0
- netbox_docker_plugin-1.8.0/netbox_docker_plugin/migrations/0029_alter_container_state.py +20 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/models/container.py +41 -2
- netbox_docker_plugin-1.8.0/netbox_docker_plugin/signals.py +33 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tables.py +24 -4
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/templates/netbox_docker_plugin/container.html +28 -4
- netbox_docker_plugin-1.8.0/netbox_docker_plugin/tests/container/test_container_actions.py +171 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/container/test_container_api.py +22 -3
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/container/test_container_operation_view.py +35 -10
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/container/test_container_validation.py +10 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/container/test_container_views.py +30 -9
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0/netbox_docker_plugin.egg-info}/PKG-INFO +2 -2
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin.egg-info/SOURCES.txt +4 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/pyproject.toml +2 -1
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/LICENSE +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/MANIFEST.in +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/README.md +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/api/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/api/urls.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/api/views.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/filtersets.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/env.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/host.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/image.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/label.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/network.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/network_setting.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/port.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/registry.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/volume.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0001_initial.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0002_image.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0003_image_size.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0004_volume.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0005_network.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0006_container_port_mount_label_env_and_more.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0007_remove_container_netbox_docker_plugin_container_unique_name_host__and_more.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0008_alter_network_unique_together_network_networkid_and_more.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0009_remove_container_network_networksettings_and_more.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0010_container_containerid_and_more.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0011_host_token.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0012_host_state.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0013_host_netbox_url.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0014_container_operation.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0015_remove_mount_netbox_docker_plugin_mount_unique_volume_and_more.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0016_alter_env_value.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0017_network_state.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0018_alter_mount_volume_alter_networksetting_network.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0019_host_agent_version_host_docker_api_version.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0020_container_hostname.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0021_registry_and_more.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0022_bind_bind_netbox_docker_plugin_bind_unique_bind.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0023_delete_hosts.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0024_registry_host_alter_registry_name_and_more.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0025_alter_image_version.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0026_image_digest.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/0027_container_restart_policy.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/migrations/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/models/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/models/host.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/models/image.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/models/network.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/models/registry.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/models/volume.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/navigation.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/templates/netbox_docker_plugin/host.html +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/templates/netbox_docker_plugin/image.html +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/templates/netbox_docker_plugin/network.html +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/templates/netbox_docker_plugin/registry.html +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/templates/netbox_docker_plugin/volume.html +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/templatetags/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/templatetags/host.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/base.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/container/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/host/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/host/test_host_api.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/host/test_host_views.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/host/test_replace_password.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/image/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/image/test_image_api.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/image/test_image_views.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/network/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/network/test_network_api.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/network/test_network_views.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/registry/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/registry/test_registry_api.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/registry/test_registry_views.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/test_init.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/volume/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/volume/test_volume_api.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/tests/volume/test_volume_views.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/urls.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/utilities/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/views/__init__.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/views/bind.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/views/container.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/views/env.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/views/host.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/views/image.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/views/label.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/views/mount.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/views/network.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/views/network_setting.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/views/port.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/views/registry.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/views/volume.py +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin.egg-info/dependency_links.txt +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin.egg-info/top_level.txt +0 -0
- {netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/setup.cfg +0 -0
{netbox_docker_plugin-1.6.2/netbox_docker_plugin.egg-info → netbox_docker_plugin-1.8.0}/PKG-INFO
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: netbox-docker-plugin
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
4
4
|
Summary: Manage Docker with Netbox & style.
|
|
5
|
-
Author-email: Vincent Simonin <vincent@saashup.com>
|
|
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
|
|
7
7
|
Project-URL: Bug Tracker, https://github.com/SaaShup/netbox-docker-plugin/issues
|
|
8
8
|
Keywords: netbox,netbox-plugin,docker
|
|
@@ -10,12 +10,14 @@ class NetBoxDockerConfig(PluginConfig):
|
|
|
10
10
|
name = "netbox_docker_plugin"
|
|
11
11
|
verbose_name = " NetBox Docker Plugin"
|
|
12
12
|
description = "Manage Docker"
|
|
13
|
-
version = "1.
|
|
13
|
+
version = "1.8.0"
|
|
14
14
|
base_url = "docker"
|
|
15
|
-
author= "Vincent Simonin"
|
|
16
|
-
author_email= "vincent@saashup.com"
|
|
15
|
+
author= "Vincent Simonin <vincent@saashup.com>, David Delassus <david.jose.delassus@gmail.com>"
|
|
16
|
+
author_email= "vincent@saashup.com, david.jose.delassus@gmail.com"
|
|
17
17
|
|
|
18
18
|
def ready(self):
|
|
19
|
+
from . import signals # pylint: disable=unused-import, import-outside-toplevel
|
|
20
|
+
|
|
19
21
|
post_migrate.connect(create_webhook)
|
|
20
22
|
|
|
21
23
|
super().ready()
|
{netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/api/serializers.py
RENAMED
|
@@ -159,6 +159,7 @@ class NestedMountSerializer(WritableNestedSerializer):
|
|
|
159
159
|
fields = (
|
|
160
160
|
"id",
|
|
161
161
|
"source",
|
|
162
|
+
"read_only",
|
|
162
163
|
)
|
|
163
164
|
|
|
164
165
|
|
|
@@ -340,6 +341,7 @@ class MountSerializer(serializers.ModelSerializer):
|
|
|
340
341
|
fields = (
|
|
341
342
|
"source",
|
|
342
343
|
"volume",
|
|
344
|
+
"read_only",
|
|
343
345
|
)
|
|
344
346
|
|
|
345
347
|
|
|
@@ -353,6 +355,7 @@ class BindSerializer(serializers.ModelSerializer):
|
|
|
353
355
|
fields = (
|
|
354
356
|
"host_path",
|
|
355
357
|
"container_path",
|
|
358
|
+
"read_only",
|
|
356
359
|
)
|
|
357
360
|
|
|
358
361
|
|
{netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/bind.py
RENAMED
|
@@ -27,11 +27,13 @@ class BindForm(BootstrapMixin, forms.ModelForm):
|
|
|
27
27
|
"container",
|
|
28
28
|
"host_path",
|
|
29
29
|
"container_path",
|
|
30
|
+
"read_only",
|
|
30
31
|
)
|
|
31
32
|
labels = {
|
|
32
33
|
"container": "Container",
|
|
33
34
|
"host_path": "Path to mount on host",
|
|
34
35
|
"container_path": "Mountpoint in container",
|
|
36
|
+
"read_only": "Mount as read-only within the container",
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
|
{netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/container.py
RENAMED
|
@@ -14,11 +14,7 @@ from netbox.forms import (
|
|
|
14
14
|
)
|
|
15
15
|
from ..models.volume import Volume
|
|
16
16
|
from ..models.host import Host
|
|
17
|
-
from ..models.container import
|
|
18
|
-
Container,
|
|
19
|
-
ContainerStateChoices,
|
|
20
|
-
ContainerRestartPolicyChoices,
|
|
21
|
-
)
|
|
17
|
+
from ..models.container import Container, ContainerRestartPolicyChoices
|
|
22
18
|
from ..models.image import Image
|
|
23
19
|
|
|
24
20
|
|
|
@@ -44,7 +40,6 @@ class ContainerForm(NetBoxModelForm):
|
|
|
44
40
|
"image",
|
|
45
41
|
"name",
|
|
46
42
|
"hostname",
|
|
47
|
-
"state",
|
|
48
43
|
"restart_policy",
|
|
49
44
|
"tags",
|
|
50
45
|
)
|
|
@@ -53,7 +48,6 @@ class ContainerForm(NetBoxModelForm):
|
|
|
53
48
|
"host": "Host",
|
|
54
49
|
"image": "Image",
|
|
55
50
|
"hostname": "Hostname",
|
|
56
|
-
"state": "State",
|
|
57
51
|
"restart_policy": "Restart Policy",
|
|
58
52
|
}
|
|
59
53
|
|
|
@@ -79,9 +73,6 @@ class ContainerFilterForm(NetBoxModelFilterSetForm):
|
|
|
79
73
|
required=False,
|
|
80
74
|
label="Image",
|
|
81
75
|
)
|
|
82
|
-
state = forms.ChoiceField(
|
|
83
|
-
label="State", choices=ContainerStateChoices, required=False
|
|
84
|
-
)
|
|
85
76
|
restart_policy = forms.ChoiceField(
|
|
86
77
|
label="Restart Policy", choices=ContainerRestartPolicyChoices, required=False
|
|
87
78
|
)
|
|
@@ -91,13 +82,6 @@ class ContainerFilterForm(NetBoxModelFilterSetForm):
|
|
|
91
82
|
class ContainerImportForm(NetBoxModelImportForm):
|
|
92
83
|
"""Container importation form definition class"""
|
|
93
84
|
|
|
94
|
-
state = forms.ChoiceField(
|
|
95
|
-
label="State",
|
|
96
|
-
choices=ContainerStateChoices,
|
|
97
|
-
required=False,
|
|
98
|
-
help_text="Container State. Can be `created`, `restarting`, "
|
|
99
|
-
+ "`running`, `paused`, `exited` or `dead`.",
|
|
100
|
-
)
|
|
101
85
|
restart_policy = forms.ChoiceField(
|
|
102
86
|
label="Restart Policy",
|
|
103
87
|
choices=ContainerRestartPolicyChoices,
|
|
@@ -114,7 +98,6 @@ class ContainerImportForm(NetBoxModelImportForm):
|
|
|
114
98
|
"name",
|
|
115
99
|
"host",
|
|
116
100
|
"image",
|
|
117
|
-
"state",
|
|
118
101
|
"hostname",
|
|
119
102
|
)
|
|
120
103
|
labels = {
|
|
@@ -127,11 +110,6 @@ class ContainerImportForm(NetBoxModelImportForm):
|
|
|
127
110
|
class ContainerBulkEditForm(NetBoxModelBulkEditForm):
|
|
128
111
|
"""Container bulk edit form definition class"""
|
|
129
112
|
|
|
130
|
-
state = forms.ChoiceField(
|
|
131
|
-
label="State",
|
|
132
|
-
choices=ContainerStateChoices,
|
|
133
|
-
required=True,
|
|
134
|
-
)
|
|
135
113
|
restart_policy = forms.ChoiceField(
|
|
136
114
|
label="Restart Policy",
|
|
137
115
|
choices=ContainerRestartPolicyChoices,
|
|
@@ -142,7 +120,7 @@ class ContainerBulkEditForm(NetBoxModelBulkEditForm):
|
|
|
142
120
|
)
|
|
143
121
|
|
|
144
122
|
model = Container
|
|
145
|
-
fieldsets = (("General", ("
|
|
123
|
+
fieldsets = (("General", ("restart_policy", "hostname")),)
|
|
146
124
|
|
|
147
125
|
|
|
148
126
|
class ContainerOperationForm(NetBoxModelForm):
|
{netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/forms/mount.py
RENAMED
|
@@ -31,11 +31,13 @@ class MountForm(BootstrapMixin, forms.ModelForm):
|
|
|
31
31
|
"container",
|
|
32
32
|
"source",
|
|
33
33
|
"volume",
|
|
34
|
+
"read_only",
|
|
34
35
|
)
|
|
35
36
|
labels = {
|
|
36
37
|
"container": "Container",
|
|
37
38
|
"source": "Source directory",
|
|
38
39
|
"volume": "Volume",
|
|
40
|
+
"read_only": "Mount as read-only within the container",
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# pylint: disable=C0103
|
|
2
|
+
"""Migration file"""
|
|
3
|
+
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
"""Migration file"""
|
|
9
|
+
|
|
10
|
+
dependencies = [
|
|
11
|
+
("netbox_docker_plugin", "0027_container_restart_policy"),
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
operations = [
|
|
15
|
+
migrations.AddField(
|
|
16
|
+
model_name="mount",
|
|
17
|
+
name="read_only",
|
|
18
|
+
field=models.BooleanField(default=False),
|
|
19
|
+
),
|
|
20
|
+
migrations.AddField(
|
|
21
|
+
model_name="bind",
|
|
22
|
+
name="read_only",
|
|
23
|
+
field=models.BooleanField(default=False),
|
|
24
|
+
),
|
|
25
|
+
]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# pylint: disable=C0103
|
|
2
|
+
"""Migration file"""
|
|
3
|
+
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
"""Migration file"""
|
|
9
|
+
|
|
10
|
+
dependencies = [
|
|
11
|
+
("netbox_docker_plugin", "0028_mount_and_bind_read_only"),
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
operations = [
|
|
15
|
+
migrations.AlterField(
|
|
16
|
+
model_name="container",
|
|
17
|
+
name="state",
|
|
18
|
+
field=models.CharField(default="none", max_length=32),
|
|
19
|
+
),
|
|
20
|
+
]
|
{netbox_docker_plugin-1.6.2 → netbox_docker_plugin-1.8.0}/netbox_docker_plugin/models/container.py
RENAMED
|
@@ -16,7 +16,7 @@ from utilities.choices import ChoiceSet
|
|
|
16
16
|
from utilities.querysets import RestrictedQuerySet
|
|
17
17
|
from netbox.models import NetBoxModel
|
|
18
18
|
from .image import Image
|
|
19
|
-
from .host import Host
|
|
19
|
+
from .host import Host, HostStateChoices
|
|
20
20
|
from .network import Network
|
|
21
21
|
from .volume import Volume
|
|
22
22
|
|
|
@@ -26,7 +26,7 @@ class ContainerStateChoices(ChoiceSet):
|
|
|
26
26
|
|
|
27
27
|
key = "Container.state"
|
|
28
28
|
|
|
29
|
-
DEFAULT_VALUE = "
|
|
29
|
+
DEFAULT_VALUE = "none"
|
|
30
30
|
|
|
31
31
|
CHOICES = [
|
|
32
32
|
("created", "Created", "dark"),
|
|
@@ -133,6 +133,39 @@ class Container(NetBoxModel):
|
|
|
133
133
|
default=ContainerRestartPolicyChoices.DEFAULT_VALUE,
|
|
134
134
|
)
|
|
135
135
|
|
|
136
|
+
@property
|
|
137
|
+
def can_create(self) -> bool:
|
|
138
|
+
""" Check if the container can be created """
|
|
139
|
+
return self.state == "none"
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def can_start(self) -> bool:
|
|
143
|
+
""" Check if the container can be started """
|
|
144
|
+
return self.state in ["created", "exited", "dead"]
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def can_stop(self) -> bool:
|
|
148
|
+
""" Check if the container can be stopped """
|
|
149
|
+
return self.state in ["running"]
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def can_restart(self) -> bool:
|
|
153
|
+
""" Check if the container can be restarted """
|
|
154
|
+
return self.state in ["running"]
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def can_recreate(self) -> bool:
|
|
158
|
+
""" Check if the container can be recreated """
|
|
159
|
+
return self.state in ["created", "running", "exited", "dead"]
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def can_delete(self) -> bool:
|
|
163
|
+
""" Check if the container can be deleted """
|
|
164
|
+
return (
|
|
165
|
+
self.host.state == HostStateChoices.STATE_DELETED
|
|
166
|
+
or self.state in ["created", "paused", "exited", "dead"]
|
|
167
|
+
)
|
|
168
|
+
|
|
136
169
|
class Meta:
|
|
137
170
|
"""Image Model Meta Class"""
|
|
138
171
|
|
|
@@ -301,6 +334,9 @@ class Mount(models.Model):
|
|
|
301
334
|
MaxLengthValidator(limit_value=1024),
|
|
302
335
|
],
|
|
303
336
|
)
|
|
337
|
+
read_only = models.BooleanField(
|
|
338
|
+
default=False,
|
|
339
|
+
)
|
|
304
340
|
|
|
305
341
|
class Meta:
|
|
306
342
|
"""Mount Model Meta Class"""
|
|
@@ -349,6 +385,9 @@ class Bind(models.Model):
|
|
|
349
385
|
MaxLengthValidator(limit_value=1024),
|
|
350
386
|
],
|
|
351
387
|
)
|
|
388
|
+
read_only = models.BooleanField(
|
|
389
|
+
default=False,
|
|
390
|
+
)
|
|
352
391
|
|
|
353
392
|
class Meta:
|
|
354
393
|
"""Mount Model Meta Class"""
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
""" Hooks on Django model signals. """
|
|
2
|
+
|
|
3
|
+
from django.db.models.signals import pre_delete, pre_save
|
|
4
|
+
from django.dispatch import receiver
|
|
5
|
+
from utilities.exceptions import AbortRequest
|
|
6
|
+
|
|
7
|
+
from .models import Container
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@receiver(pre_delete, sender=Container)
|
|
11
|
+
def prevent_deleting_running_containers(instance, **_kwargs):
|
|
12
|
+
""" Abort deletion if container is running. """
|
|
13
|
+
|
|
14
|
+
if not instance.can_delete:
|
|
15
|
+
raise AbortRequest(
|
|
16
|
+
f"The container {instance.name} is running, please stop it before deletion"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@receiver(pre_save, sender=Container)
|
|
21
|
+
def restrict_container_actions(instance, **_kwargs):
|
|
22
|
+
""" Check if the container's state is correct for the given action """
|
|
23
|
+
|
|
24
|
+
if any([
|
|
25
|
+
instance.operation == "create" and not instance.can_create,
|
|
26
|
+
instance.operation == "start" and not instance.can_start,
|
|
27
|
+
instance.operation == "restart" and not instance.can_restart,
|
|
28
|
+
instance.operation == "stop" and not instance.can_stop,
|
|
29
|
+
instance.operation == "recreate" and not instance.can_recreate,
|
|
30
|
+
]):
|
|
31
|
+
raise AbortRequest(
|
|
32
|
+
f"Cannot {instance.operation} container {instance.name} in state {instance.state}"
|
|
33
|
+
)
|
|
@@ -335,8 +335,18 @@ class MountTable(NetBoxTable):
|
|
|
335
335
|
"""Mount Table definition Meta class"""
|
|
336
336
|
|
|
337
337
|
model = Mount
|
|
338
|
-
fields = (
|
|
339
|
-
|
|
338
|
+
fields = (
|
|
339
|
+
"container",
|
|
340
|
+
"source",
|
|
341
|
+
"volume",
|
|
342
|
+
"read_only",
|
|
343
|
+
)
|
|
344
|
+
default_columns = (
|
|
345
|
+
"container",
|
|
346
|
+
"source",
|
|
347
|
+
"volume",
|
|
348
|
+
"read_only",
|
|
349
|
+
)
|
|
340
350
|
|
|
341
351
|
|
|
342
352
|
class BindTable(NetBoxTable):
|
|
@@ -350,8 +360,18 @@ class BindTable(NetBoxTable):
|
|
|
350
360
|
"""Bind Table definition Meta class"""
|
|
351
361
|
|
|
352
362
|
model = Bind
|
|
353
|
-
fields = (
|
|
354
|
-
|
|
363
|
+
fields = (
|
|
364
|
+
"container",
|
|
365
|
+
"host_path",
|
|
366
|
+
"container_path",
|
|
367
|
+
"read_only",
|
|
368
|
+
)
|
|
369
|
+
default_columns = (
|
|
370
|
+
"container",
|
|
371
|
+
"host_path",
|
|
372
|
+
"container_path",
|
|
373
|
+
"read_only",
|
|
374
|
+
)
|
|
355
375
|
|
|
356
376
|
|
|
357
377
|
class NetworkSettingTable(NetBoxTable):
|
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
<form action="{% url 'plugins:netbox_docker_plugin:container_operation' pk=object.id operation='start' %}" method="post">
|
|
7
7
|
{% csrf_token %}
|
|
8
8
|
<input type="hidden" name="operation" value="start">
|
|
9
|
-
<button
|
|
9
|
+
<button
|
|
10
|
+
type="submit"
|
|
11
|
+
class="btn btn-sm btn-success"
|
|
12
|
+
{% if not object.can_start %}
|
|
13
|
+
disabled
|
|
14
|
+
{% endif %}
|
|
15
|
+
>
|
|
10
16
|
<i class="mdi mdi-play"></i> Start
|
|
11
17
|
</button>
|
|
12
18
|
</form>
|
|
@@ -14,7 +20,13 @@
|
|
|
14
20
|
<form action="{% url 'plugins:netbox_docker_plugin:container_operation' pk=object.id operation='stop' %}" method="post">
|
|
15
21
|
{% csrf_token %}
|
|
16
22
|
<input type="hidden" name="operation" value="stop">
|
|
17
|
-
<button
|
|
23
|
+
<button
|
|
24
|
+
type="submit"
|
|
25
|
+
class="btn btn-sm btn-danger"
|
|
26
|
+
{% if not object.can_stop %}
|
|
27
|
+
disabled
|
|
28
|
+
{% endif %}
|
|
29
|
+
>
|
|
18
30
|
<i class="mdi mdi-stop"></i> Stop
|
|
19
31
|
</button>
|
|
20
32
|
</form>
|
|
@@ -22,7 +34,13 @@
|
|
|
22
34
|
<form action="{% url 'plugins:netbox_docker_plugin:container_operation' pk=object.id operation='restart' %}" method="post">
|
|
23
35
|
{% csrf_token %}
|
|
24
36
|
<input type="hidden" name="operation" value="restart">
|
|
25
|
-
<button
|
|
37
|
+
<button
|
|
38
|
+
type="submit"
|
|
39
|
+
class="btn btn-sm btn-light"
|
|
40
|
+
{% if not object.can_restart %}
|
|
41
|
+
disabled
|
|
42
|
+
{% endif %}
|
|
43
|
+
>
|
|
26
44
|
<i class="mdi mdi-restart"></i> Restart
|
|
27
45
|
</button>
|
|
28
46
|
</form>
|
|
@@ -30,7 +48,13 @@
|
|
|
30
48
|
<form action="{% url 'plugins:netbox_docker_plugin:container_operation' pk=object.id operation='recreate' %}" method="post">
|
|
31
49
|
{% csrf_token %}
|
|
32
50
|
<input type="hidden" name="operation" value="recreate">
|
|
33
|
-
<button
|
|
51
|
+
<button
|
|
52
|
+
type="submit"
|
|
53
|
+
class="btn btn-sm btn-dark"
|
|
54
|
+
{% if not object.can_recreate %}
|
|
55
|
+
disabled
|
|
56
|
+
{% endif %}
|
|
57
|
+
>
|
|
34
58
|
<i class="mdi mdi-autorenew"></i> Recreate
|
|
35
59
|
</button>
|
|
36
60
|
</form>
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
""" Container action restrictions test suite """
|
|
2
|
+
|
|
3
|
+
from django.db import transaction
|
|
4
|
+
from django.test import TestCase
|
|
5
|
+
from utilities.exceptions import AbortRequest
|
|
6
|
+
from netbox_docker_plugin.models.container import Container
|
|
7
|
+
from netbox_docker_plugin.models.host import Host, HostStateChoices
|
|
8
|
+
from netbox_docker_plugin.models.image import Image
|
|
9
|
+
from netbox_docker_plugin.models.registry import Registry
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ContainerActionTestCase(TestCase):
|
|
13
|
+
""" Test the various scenarii of container actions """
|
|
14
|
+
|
|
15
|
+
objects = {}
|
|
16
|
+
|
|
17
|
+
def test_allowed_operations(self):
|
|
18
|
+
""" Test that only specific operations are allowed according to a state """
|
|
19
|
+
|
|
20
|
+
cases = [
|
|
21
|
+
("create", ["none"]),
|
|
22
|
+
("start", ["created", "exited", "dead"]),
|
|
23
|
+
("stop", ["running"]),
|
|
24
|
+
("restart", ["running"]),
|
|
25
|
+
("recreate", ["created", "exited", "dead", "running"]),
|
|
26
|
+
("noop", ["created", "restarting", "running", "paused", "exited", "dead", "none"]),
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
for operation, states in cases:
|
|
30
|
+
for state in states:
|
|
31
|
+
with self.subTest(operation=operation, state=state):
|
|
32
|
+
with transaction.atomic():
|
|
33
|
+
name = f"container-{operation}-{state}"
|
|
34
|
+
obj = Container.objects.create(
|
|
35
|
+
host=self.objects["host1"],
|
|
36
|
+
image=self.objects["image2"],
|
|
37
|
+
name=name,
|
|
38
|
+
operation="none",
|
|
39
|
+
state=state,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
obj.operation = operation
|
|
43
|
+
obj.save()
|
|
44
|
+
|
|
45
|
+
self.assertEqual(
|
|
46
|
+
Container.objects.get(name=name).operation,
|
|
47
|
+
operation,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def test_forbidden_operations(self):
|
|
51
|
+
""" Test that forbidden operations raise an AbortRequest """
|
|
52
|
+
|
|
53
|
+
cases = [
|
|
54
|
+
("create", ["created", "restarting", "running", "paused", "exited", "dead"]),
|
|
55
|
+
("start", ["running", "restarting", "paused", "none"]),
|
|
56
|
+
("stop", ["created", "restarting", "paused", "exited", "dead", "none"]),
|
|
57
|
+
("restart", ["created", "restarting", "paused", "exited", "dead", "none"]),
|
|
58
|
+
("recreate", ["restarting", "paused", "none"]),
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
for operation, states in cases:
|
|
62
|
+
for state in states:
|
|
63
|
+
with self.subTest(operation=operation, state=state):
|
|
64
|
+
with transaction.atomic():
|
|
65
|
+
with self.assertRaises(
|
|
66
|
+
AbortRequest,
|
|
67
|
+
msg=f"AbortRequest not raised for {state}",
|
|
68
|
+
):
|
|
69
|
+
name = f"container-{operation}-{state}"
|
|
70
|
+
obj = Container.objects.create(
|
|
71
|
+
host=self.objects["host1"],
|
|
72
|
+
image=self.objects["image2"],
|
|
73
|
+
name=name,
|
|
74
|
+
operation="none",
|
|
75
|
+
state=state,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
obj.operation = operation
|
|
79
|
+
obj.save()
|
|
80
|
+
|
|
81
|
+
def test_delete_non_running_container(self):
|
|
82
|
+
""" Test that a non running container can be deleted """
|
|
83
|
+
|
|
84
|
+
for state in ["created", "paused", "exited", "dead"]:
|
|
85
|
+
with self.subTest(operation="delete", state=state):
|
|
86
|
+
with transaction.atomic():
|
|
87
|
+
name = f"container-delete-{state}"
|
|
88
|
+
obj = Container.objects.create(
|
|
89
|
+
host=self.objects["host1"],
|
|
90
|
+
image=self.objects["image2"],
|
|
91
|
+
name=name,
|
|
92
|
+
operation="none",
|
|
93
|
+
state=state,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
obj.delete()
|
|
97
|
+
|
|
98
|
+
self.assertFalse(Container.objects.filter(name=name).exists())
|
|
99
|
+
|
|
100
|
+
def test_delete_running_container(self):
|
|
101
|
+
""" Test that a running container cannot be deleted """
|
|
102
|
+
|
|
103
|
+
for state in ["running", "restarting", "none"]:
|
|
104
|
+
with self.subTest(operation="delete", state=state):
|
|
105
|
+
with transaction.atomic():
|
|
106
|
+
with self.assertRaises(
|
|
107
|
+
AbortRequest,
|
|
108
|
+
msg=f"AbortRequest not raised for {state}",
|
|
109
|
+
):
|
|
110
|
+
name = f"container-delete-{state}"
|
|
111
|
+
obj = Container.objects.create(
|
|
112
|
+
host=self.objects["host1"],
|
|
113
|
+
image=self.objects["image2"],
|
|
114
|
+
name=name,
|
|
115
|
+
operation="none",
|
|
116
|
+
state=state,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
obj.delete()
|
|
120
|
+
|
|
121
|
+
def test_delete_container_on_deleted_host(self):
|
|
122
|
+
""" Test that a container on a deleted host can be deleted """
|
|
123
|
+
|
|
124
|
+
for state in ["created", "running", "restarted", "paused", "exited", "dead", "none"]:
|
|
125
|
+
with self.subTest(operation="delete", state=state):
|
|
126
|
+
with transaction.atomic():
|
|
127
|
+
name = f"container-delete-{state}"
|
|
128
|
+
obj = Container.objects.create(
|
|
129
|
+
host=self.objects["host2"],
|
|
130
|
+
image=self.objects["image2"],
|
|
131
|
+
name=name,
|
|
132
|
+
operation="none",
|
|
133
|
+
state=state,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
obj.delete()
|
|
137
|
+
|
|
138
|
+
self.assertFalse(Container.objects.filter(name=name).exists())
|
|
139
|
+
|
|
140
|
+
@classmethod
|
|
141
|
+
def setUpTestData(cls):
|
|
142
|
+
cls.objects["host1"] = Host.objects.create(
|
|
143
|
+
endpoint="http://localhost:8080",
|
|
144
|
+
name="host1",
|
|
145
|
+
)
|
|
146
|
+
cls.objects["registry1"] = Registry.objects.create(
|
|
147
|
+
host=cls.objects["host1"],
|
|
148
|
+
name="registry1",
|
|
149
|
+
serveraddress="http://localhost:8080",
|
|
150
|
+
)
|
|
151
|
+
cls.objects["image2"] = Image.objects.create(
|
|
152
|
+
host=cls.objects["host1"],
|
|
153
|
+
registry=cls.objects["registry1"],
|
|
154
|
+
name="image2",
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
cls.objects["host2"] = Host.objects.create(
|
|
158
|
+
endpoint="http://localhost:8080",
|
|
159
|
+
name="host2",
|
|
160
|
+
state=HostStateChoices.STATE_DELETED,
|
|
161
|
+
)
|
|
162
|
+
cls.objects["registry2"] = Registry.objects.create(
|
|
163
|
+
host=cls.objects["host2"],
|
|
164
|
+
name="registry2",
|
|
165
|
+
serveraddress="http://localhost:8080",
|
|
166
|
+
)
|
|
167
|
+
cls.objects["image2"] = Image.objects.create(
|
|
168
|
+
host=cls.objects["host2"],
|
|
169
|
+
registry=cls.objects["registry2"],
|
|
170
|
+
name="image2",
|
|
171
|
+
)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# pylint: disable=R0801
|
|
1
2
|
"""Container Test Case"""
|
|
2
3
|
|
|
3
4
|
from utilities.testing import APIViewTestCases
|
|
@@ -64,9 +65,27 @@ class ContainerApiTestCase(
|
|
|
64
65
|
volume2 = Volume.objects.create(host=host1, name="volume2")
|
|
65
66
|
volume3 = Volume.objects.create(host=host2, name="volume3")
|
|
66
67
|
|
|
67
|
-
Container.objects.create(
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
Container.objects.create(
|
|
69
|
+
host=host1,
|
|
70
|
+
image=image1,
|
|
71
|
+
name="container1",
|
|
72
|
+
operation="none",
|
|
73
|
+
state="created",
|
|
74
|
+
)
|
|
75
|
+
Container.objects.create(
|
|
76
|
+
host=host1,
|
|
77
|
+
image=image1,
|
|
78
|
+
name="container2",
|
|
79
|
+
operation="none",
|
|
80
|
+
state="created",
|
|
81
|
+
)
|
|
82
|
+
Container.objects.create(
|
|
83
|
+
host=host2,
|
|
84
|
+
image=image2,
|
|
85
|
+
name="container3",
|
|
86
|
+
operation="none",
|
|
87
|
+
state="created",
|
|
88
|
+
)
|
|
70
89
|
|
|
71
90
|
cls.create_data = [
|
|
72
91
|
{
|