Skip to content

Commit 5faa685

Browse files
committed
Add NetBox's changelog model to OnboardingTask
1 parent 3c6b064 commit 5faa685

File tree

7 files changed

+103
-33
lines changed

7 files changed

+103
-33
lines changed

netbox_onboarding/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@ class OnboardingTaskAdmin(admin.ModelAdmin):
3232
"failed_reason",
3333
"port",
3434
"timeout",
35-
"created_on",
35+
"created",
3636
)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from django.db import migrations, models
2+
3+
4+
class Migration(migrations.Migration):
5+
6+
dependencies = [
7+
("netbox_onboarding", "0002_onboardingdevice"),
8+
]
9+
10+
operations = [
11+
migrations.AddField(
12+
model_name="onboardingtask", name="created", field=models.DateField(auto_now_add=True, null=True),
13+
),
14+
migrations.AddField(
15+
model_name="onboardingtask", name="last_updated", field=models.DateTimeField(auto_now=True, null=True),
16+
),
17+
migrations.AlterModelOptions(name="onboardingtask", options={},),
18+
migrations.RemoveField(model_name="onboardingtask", name="created_on",),
19+
]

netbox_onboarding/models.py

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,15 @@
1919
from .choices import OnboardingStatusChoices, OnboardingFailChoices
2020
from .release import NETBOX_RELEASE_CURRENT, NETBOX_RELEASE_29
2121

22+
# Support NetBox 2.8
23+
if NETBOX_RELEASE_CURRENT < NETBOX_RELEASE_29:
24+
from utilities.models import ChangeLoggedModel # pylint: disable=no-name-in-module, import-error
25+
# Support NetBox 2.9
26+
else:
27+
from extras.models import ChangeLoggedModel # pylint: disable=no-name-in-module, import-error
2228

23-
class OnboardingTask(models.Model):
29+
30+
class OnboardingTask(ChangeLoggedModel):
2431
"""The status of each onboarding Task is tracked in the OnboardingTask table."""
2532

2633
created_device = models.ForeignKey(to="dcim.Device", on_delete=models.SET_NULL, blank=True, null=True)
@@ -50,11 +57,6 @@ class OnboardingTask(models.Model):
5057
help_text="Timeout period in sec to wait while connecting to the device", default=30
5158
)
5259

53-
created_on = models.DateTimeField(auto_now_add=True)
54-
55-
class Meta: # noqa: D106 "missing docstring in public nested class"
56-
ordering = ["created_on"]
57-
5860
def __str__(self):
5961
"""String representation of an OnboardingTask."""
6062
return f"{self.site} : {self.ip_address}"
@@ -78,40 +80,67 @@ class OnboardingDevice(models.Model):
7880
@property
7981
def last_check_attempt_date(self):
8082
"""Date of last onboarding attempt for a device."""
81-
try:
82-
return OnboardingTask.objects.filter(created_device=self.device).latest("created_on").created_on
83-
except ValueError:
83+
if self.device.primary_ip4:
84+
try:
85+
return (
86+
OnboardingTask.objects.filter(
87+
ip_address=self.device.primary_ip4.address.ip.format() # pylint: disable=no-member
88+
)
89+
.latest("last_updated")
90+
.created
91+
)
92+
except OnboardingTask.DoesNotExist:
93+
return "unknown"
94+
else:
8495
return "unknown"
8596

8697
@property
8798
def last_check_successful_date(self):
8899
"""Date of last successful onboarding for a device."""
89-
try:
90-
return (
91-
OnboardingTask.objects.filter(
92-
created_device=self.device, status=OnboardingStatusChoices.STATUS_SUCCEEDED
100+
if self.device.primary_ip4:
101+
try:
102+
return (
103+
OnboardingTask.objects.filter(
104+
ip_address=self.device.primary_ip4.address.ip.format(), # pylint: disable=no-member
105+
status=OnboardingStatusChoices.STATUS_SUCCEEDED,
106+
)
107+
.latest("last_updated")
108+
.created
93109
)
94-
.latest("created_on")
95-
.created_on
96-
)
97-
except ValueError:
110+
except OnboardingTask.DoesNotExist:
111+
return "unknown"
112+
else:
98113
return "unknown"
99114

100115
@property
101116
def status(self):
102117
"""Last onboarding status."""
103-
try:
104-
return OnboardingTask.objects.filter(created_device=self.device).latest("created_on").status
105-
except ValueError:
118+
if self.device.primary_ip4:
119+
try:
120+
return (
121+
OnboardingTask.objects.filter(
122+
ip_address=self.device.primary_ip4.address.ip.format() # pylint: disable=no-member
123+
)
124+
.latest("last_updated")
125+
.status
126+
)
127+
except OnboardingTask.DoesNotExist:
128+
return "unknown"
129+
else:
106130
return "unknown"
107131

108132
@property
109133
def last_ot(self):
110134
"""Last onboarding task."""
111-
try:
112-
return OnboardingTask.objects.filter(created_device=self.device).latest("created_on")
113-
except ValueError:
114-
return None
135+
if self.device.primary_ip4:
136+
try:
137+
return OnboardingTask.objects.filter(
138+
ip_address=self.device.primary_ip4.address.ip.format() # pylint: disable=no-member
139+
).latest("last_updated")
140+
except OnboardingTask.DoesNotExist:
141+
return "unknown"
142+
else:
143+
return "unknown"
115144

116145

117146
@receiver(post_save, sender=Device)

netbox_onboarding/tables.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class Meta(BaseTable.Meta): # noqa: D106 "Missing docstring in public nested cl
3030
fields = (
3131
"pk",
3232
"id",
33-
"created_on",
33+
"created",
3434
"ip_address",
3535
"site",
3636
"platform",
@@ -50,7 +50,7 @@ class Meta(BaseTable.Meta): # noqa: D106 "Missing docstring in public nested cl
5050
model = OnboardingTask
5151
fields = (
5252
"id",
53-
"created_on",
53+
"created",
5454
"site",
5555
"platform",
5656
"ip_address",

netbox_onboarding/templates/netbox_onboarding/onboardingtask.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ <h1>{% block title %}Device: {{ onboardingtask.ip_address }}{% endblock %}</h1>
1818
<li role="presentation"{% if not active_tab %} class="active"{% endif %}>
1919
<a href="{{ onboardingtask.get_absolute_url }}">Onboarding Task</a>
2020
</li>
21+
{% if perms.extras.view_objectchange %}
22+
<li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
23+
<a href="{% url 'plugins:netbox_onboarding:onboardingtask_changelog' pk=onboardingtask.pk %}">Change Log</a>
24+
</li>
25+
{% endif %}
2126
</ul>
2227
{% endblock %}
2328

@@ -74,8 +79,8 @@ <h1>{% block title %}Device: {{ onboardingtask.ip_address }}{% endblock %}</h1>
7479
<td>{{ onboardingtask.message|placeholder }}</td>
7580
</tr>
7681
<tr>
77-
<td>Created On</td>
78-
<td>{{ onboardingtask.created_on|placeholder }}</td>
82+
<td>Created</td>
83+
<td>{{ onboardingtask.created|placeholder }}</td>
7984
</tr>
8085
</table>
8186
</div>

netbox_onboarding/tests/test_models.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"""
1414
from django.test import TestCase
1515

16-
from dcim.models import Site, DeviceRole, DeviceType, Manufacturer, Device
16+
from dcim.models import Site, DeviceRole, DeviceType, Manufacturer, Device, Interface
17+
from ipam.models import IPAddress
1718

1819
from netbox_onboarding.models import OnboardingTask
1920
from netbox_onboarding.models import OnboardingDevice
@@ -31,9 +32,17 @@ def setUp(self):
3132
device_type = DeviceType.objects.create(slug="srx3600", model="SRX3600", manufacturer=manufacturer)
3233

3334
self.device = Device.objects.create(
34-
device_type=device_type, name="device1", device_role=device_role, site=self.site
35+
device_type=device_type, name="device1", device_role=device_role, site=self.site,
3536
)
3637

38+
intf = Interface.objects.create(name="test_intf", device=self.device)
39+
40+
primary_ip = IPAddress.objects.create(address="10.10.10.10/32")
41+
intf.ip_addresses.add(primary_ip)
42+
43+
self.device.primary_ip4 = primary_ip
44+
self.device.save()
45+
3746
self.succeeded_task1 = OnboardingTask.objects.create(
3847
ip_address="10.10.10.10",
3948
site=self.site,
@@ -70,12 +79,12 @@ def test_onboardingdevice_autocreated(self):
7079
def test_last_check_attempt_date(self):
7180
"""Verify OnboardingDevice last attempt."""
7281
onboarding_device = OnboardingDevice.objects.get(device=self.device)
73-
self.assertEqual(onboarding_device.last_check_attempt_date, self.failed_task2.created_on)
82+
self.assertEqual(onboarding_device.last_check_attempt_date, self.failed_task2.created)
7483

7584
def test_last_check_successful_date(self):
7685
"""Verify OnboardingDevice last success."""
7786
onboarding_device = OnboardingDevice.objects.get(device=self.device)
78-
self.assertEqual(onboarding_device.last_check_successful_date, self.succeeded_task2.created_on)
87+
self.assertEqual(onboarding_device.last_check_successful_date, self.succeeded_task2.created)
7988

8089
def test_status(self):
8190
"""Verify OnboardingDevice status."""

netbox_onboarding/urls.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
limitations under the License.
1313
"""
1414
from django.urls import path
15+
from extras.views import ObjectChangeLogView
1516

17+
from .models import OnboardingTask
1618
from .views import (
1719
OnboardingTaskView,
1820
OnboardingTaskListView,
@@ -27,4 +29,10 @@
2729
path("add/", OnboardingTaskCreateView.as_view(), name="onboardingtask_add"),
2830
path("delete/", OnboardingTaskBulkDeleteView.as_view(), name="onboardingtask_bulk_delete"),
2931
path("import/", OnboardingTaskFeedBulkImportView.as_view(), name="onboardingtask_import"),
32+
path(
33+
"<int:pk>/changelog/",
34+
ObjectChangeLogView.as_view(),
35+
name="onboardingtask_changelog",
36+
kwargs={"model": OnboardingTask},
37+
),
3038
]

0 commit comments

Comments
 (0)