netbox-maintenance-device 1.2.1__py3-none-any.whl
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_maintenance_device/__init__.py +40 -0
- netbox_maintenance_device/admin.py +16 -0
- netbox_maintenance_device/api/__init__.py +1 -0
- netbox_maintenance_device/api/__pycache__/views.cpython-312.pyc +0 -0
- netbox_maintenance_device/api/permissions.py +65 -0
- netbox_maintenance_device/api/serializers.py +217 -0
- netbox_maintenance_device/api/urls.py +60 -0
- netbox_maintenance_device/api/views.py +461 -0
- netbox_maintenance_device/database_healer.py +126 -0
- netbox_maintenance_device/forms.py +68 -0
- netbox_maintenance_device/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- netbox_maintenance_device/locale/pt_BR/LC_MESSAGES/django.po +251 -0
- netbox_maintenance_device/migrations/0001_initial.py +56 -0
- netbox_maintenance_device/migrations/0002_cleanup_notifications.py +137 -0
- netbox_maintenance_device/migrations/__init__.py +1 -0
- netbox_maintenance_device/models.py +126 -0
- netbox_maintenance_device/navigation.py +43 -0
- netbox_maintenance_device/static/netbox_maintenance_device/css/maintenance.css +318 -0
- netbox_maintenance_device/tables.py +132 -0
- netbox_maintenance_device/template_content.py +50 -0
- netbox_maintenance_device/templates/netbox_maintenance_device/device_maintenance_buttons.html +24 -0
- netbox_maintenance_device/templates/netbox_maintenance_device/device_maintenance_section.html +266 -0
- netbox_maintenance_device/templates/netbox_maintenance_device/device_maintenance_tab.html +290 -0
- netbox_maintenance_device/templates/netbox_maintenance_device/maintenanceexecution.html +57 -0
- netbox_maintenance_device/templates/netbox_maintenance_device/maintenanceplan.html +94 -0
- netbox_maintenance_device/templates/netbox_maintenance_device/upcoming_maintenance.html +218 -0
- netbox_maintenance_device/templatetags/__init__.py +1 -0
- netbox_maintenance_device/templatetags/maintenance_extras.py +27 -0
- netbox_maintenance_device/urls.py +32 -0
- netbox_maintenance_device/views.py +219 -0
- netbox_maintenance_device-1.2.1.dist-info/METADATA +309 -0
- netbox_maintenance_device-1.2.1.dist-info/RECORD +35 -0
- netbox_maintenance_device-1.2.1.dist-info/WHEEL +5 -0
- netbox_maintenance_device-1.2.1.dist-info/licenses/LICENSE +198 -0
- netbox_maintenance_device-1.2.1.dist-info/top_level.txt +3 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from netbox.plugins import PluginConfig
|
|
2
|
+
from django.utils.translation import gettext_lazy as _
|
|
3
|
+
|
|
4
|
+
class MaintenanceDeviceConfig(PluginConfig):
|
|
5
|
+
name = 'netbox_maintenance_device'
|
|
6
|
+
verbose_name = _('NetBox Device Maintenance')
|
|
7
|
+
description = 'Manage device preventive and corrective maintenance with multilingual support'
|
|
8
|
+
version = '1.2.1'
|
|
9
|
+
author = 'Diego Godoy'
|
|
10
|
+
author_email = 'diegoalex-gdy@outlook.com'
|
|
11
|
+
base_url = 'maintenance-device'
|
|
12
|
+
icon = 'mdi-wrench-cog'
|
|
13
|
+
|
|
14
|
+
# Required NetBox version - Conservative range for this release
|
|
15
|
+
min_version = '4.4.0'
|
|
16
|
+
max_version = '4.4.99'
|
|
17
|
+
|
|
18
|
+
# Default configurations
|
|
19
|
+
default_settings = {
|
|
20
|
+
'default_frequency_days': 30,
|
|
21
|
+
'auto_heal_database': True, # Enable automatic database healing
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
# Translation configuration
|
|
25
|
+
default_language = 'en'
|
|
26
|
+
locale_paths = ['locale']
|
|
27
|
+
|
|
28
|
+
def ready(self):
|
|
29
|
+
"""
|
|
30
|
+
Called when the plugin is ready. Perform any necessary initialization.
|
|
31
|
+
"""
|
|
32
|
+
super().ready()
|
|
33
|
+
|
|
34
|
+
# Note: Database auto-healing is handled by migrations and model operations
|
|
35
|
+
# to avoid issues during initial Django setup and collectstatic operations
|
|
36
|
+
import logging
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
logger.info("NetBox Maintenance Device v1.2.1 initialized successfully")
|
|
39
|
+
|
|
40
|
+
config = MaintenanceDeviceConfig
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
from . import models
|
|
3
|
+
|
|
4
|
+
@admin.register(models.MaintenancePlan)
|
|
5
|
+
class MaintenancePlanAdmin(admin.ModelAdmin):
|
|
6
|
+
list_display = ['device', 'name', 'maintenance_type', 'frequency_days', 'is_active', 'created']
|
|
7
|
+
list_filter = ['maintenance_type', 'is_active', 'created']
|
|
8
|
+
search_fields = ['device__name', 'name', 'description']
|
|
9
|
+
ordering = ['device', 'name']
|
|
10
|
+
|
|
11
|
+
@admin.register(models.MaintenanceExecution)
|
|
12
|
+
class MaintenanceExecutionAdmin(admin.ModelAdmin):
|
|
13
|
+
list_display = ['maintenance_plan', 'scheduled_date', 'completed_date', 'status', 'technician']
|
|
14
|
+
list_filter = ['status', 'completed', 'scheduled_date']
|
|
15
|
+
search_fields = ['maintenance_plan__device__name', 'maintenance_plan__name', 'technician']
|
|
16
|
+
ordering = ['-scheduled_date']
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# API module for netbox_maintenance_device plugin
|
|
Binary file
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Custom permissions for NetBox Maintenance Device API.
|
|
3
|
+
|
|
4
|
+
This module defines custom permission classes for the API endpoints,
|
|
5
|
+
following NetBox patterns and ensuring proper access control.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from rest_framework import permissions
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MaintenanceDevicePermissions(permissions.BasePermission):
|
|
12
|
+
"""
|
|
13
|
+
Custom permissions for maintenance device operations.
|
|
14
|
+
|
|
15
|
+
Provides granular control for maintenance device API operations
|
|
16
|
+
with proper object-level permissions.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def has_permission(self, request, view):
|
|
20
|
+
"""Check if user has permission for the requested action"""
|
|
21
|
+
if not request.user or not request.user.is_authenticated:
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
# Map HTTP methods to permission requirements
|
|
25
|
+
permission_map = {
|
|
26
|
+
'GET': 'view',
|
|
27
|
+
'POST': 'add',
|
|
28
|
+
'PUT': 'change',
|
|
29
|
+
'PATCH': 'change',
|
|
30
|
+
'DELETE': 'delete',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
required_action = permission_map.get(request.method, 'view')
|
|
34
|
+
|
|
35
|
+
# Get the model name from the viewset
|
|
36
|
+
if hasattr(view, 'queryset') and view.queryset is not None:
|
|
37
|
+
model_name = view.queryset.model._meta.model_name
|
|
38
|
+
perm_name = f'netbox_maintenance_device.{required_action}_{model_name}'
|
|
39
|
+
return request.user.has_perm(perm_name)
|
|
40
|
+
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
def has_object_permission(self, request, view, obj):
|
|
44
|
+
"""Check object-level permissions"""
|
|
45
|
+
return self.has_permission(request, view)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class CanScheduleMaintenance(permissions.BasePermission):
|
|
49
|
+
"""Permission to schedule maintenance executions"""
|
|
50
|
+
|
|
51
|
+
def has_permission(self, request, view):
|
|
52
|
+
"""Check if user has permission to schedule maintenance"""
|
|
53
|
+
return (request.user and
|
|
54
|
+
request.user.is_authenticated and
|
|
55
|
+
request.user.has_perm('netbox_maintenance_device.add_maintenanceexecution'))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class CanCompleteMaintenance(permissions.BasePermission):
|
|
59
|
+
"""Permission to complete maintenance executions"""
|
|
60
|
+
|
|
61
|
+
def has_permission(self, request, view):
|
|
62
|
+
"""Check if user has permission to complete maintenance"""
|
|
63
|
+
return (request.user and
|
|
64
|
+
request.user.is_authenticated and
|
|
65
|
+
request.user.has_perm('netbox_maintenance_device.change_maintenanceexecution'))
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
from rest_framework import serializers
|
|
2
|
+
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
|
|
3
|
+
from netbox_maintenance_device import models
|
|
4
|
+
from django.utils import timezone
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DeviceNestedSerializer(WritableNestedSerializer):
|
|
8
|
+
"""Nested serializer for Device references - NetBox 4.4.x compatible"""
|
|
9
|
+
|
|
10
|
+
class Meta:
|
|
11
|
+
# Import Device model at class level
|
|
12
|
+
from dcim import models as dcim_models
|
|
13
|
+
model = dcim_models.Device
|
|
14
|
+
fields = ['id', 'url', 'display', 'name']
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class NestedMaintenancePlanSerializer(WritableNestedSerializer):
|
|
18
|
+
"""Nested serializer for MaintenancePlan references"""
|
|
19
|
+
url = serializers.HyperlinkedIdentityField(
|
|
20
|
+
view_name='plugins-api:netbox_maintenance_device-api:maintenanceplan-detail'
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
class Meta:
|
|
24
|
+
model = models.MaintenancePlan
|
|
25
|
+
fields = ['id', 'url', 'display', 'name', 'maintenance_type']
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class NestedMaintenanceExecutionSerializer(WritableNestedSerializer):
|
|
29
|
+
"""Nested serializer for MaintenanceExecution references"""
|
|
30
|
+
url = serializers.HyperlinkedIdentityField(
|
|
31
|
+
view_name='plugins-api:netbox_maintenance_device-api:maintenanceexecution-detail'
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
class Meta:
|
|
35
|
+
model = models.MaintenanceExecution
|
|
36
|
+
fields = ['id', 'url', 'display', 'scheduled_date', 'status']
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MaintenancePlanSerializer(NetBoxModelSerializer):
|
|
40
|
+
"""Complete serializer for MaintenancePlan with all relationships and computed fields"""
|
|
41
|
+
|
|
42
|
+
url = serializers.HyperlinkedIdentityField(
|
|
43
|
+
view_name='plugins-api:netbox_maintenance_device-api:maintenanceplan-detail'
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Nested relationships
|
|
47
|
+
device = DeviceNestedSerializer()
|
|
48
|
+
executions = NestedMaintenanceExecutionSerializer(many=True, read_only=True)
|
|
49
|
+
|
|
50
|
+
# Computed fields
|
|
51
|
+
execution_count = serializers.IntegerField(read_only=True)
|
|
52
|
+
next_maintenance_date = serializers.DateTimeField(read_only=True)
|
|
53
|
+
is_overdue = serializers.BooleanField(read_only=True)
|
|
54
|
+
days_until_due = serializers.IntegerField(read_only=True)
|
|
55
|
+
last_execution_date = serializers.DateTimeField(read_only=True)
|
|
56
|
+
|
|
57
|
+
class Meta:
|
|
58
|
+
model = models.MaintenancePlan
|
|
59
|
+
fields = [
|
|
60
|
+
'id', 'url', 'display', 'device', 'name', 'description',
|
|
61
|
+
'maintenance_type', 'frequency_days', 'is_active',
|
|
62
|
+
'created', 'last_updated', 'custom_field_data', 'tags',
|
|
63
|
+
# Relationships
|
|
64
|
+
'executions', 'execution_count',
|
|
65
|
+
# Computed fields
|
|
66
|
+
'next_maintenance_date', 'is_overdue', 'days_until_due',
|
|
67
|
+
'last_execution_date'
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
def validate_frequency_days(self, value):
|
|
71
|
+
"""Validate frequency days is positive and reasonable"""
|
|
72
|
+
if value <= 0:
|
|
73
|
+
raise serializers.ValidationError("Frequency must be greater than 0 days")
|
|
74
|
+
if value > 3650: # 10 years
|
|
75
|
+
raise serializers.ValidationError("Frequency cannot exceed 3650 days (10 years)")
|
|
76
|
+
return value
|
|
77
|
+
|
|
78
|
+
def validate(self, data):
|
|
79
|
+
"""Additional model-level validations"""
|
|
80
|
+
device = data.get('device')
|
|
81
|
+
name = data.get('name')
|
|
82
|
+
|
|
83
|
+
# Check for duplicate plan names per device (excluding current instance)
|
|
84
|
+
if device and name:
|
|
85
|
+
existing_plans = models.MaintenancePlan.objects.filter(
|
|
86
|
+
device=device, name=name
|
|
87
|
+
)
|
|
88
|
+
if self.instance:
|
|
89
|
+
existing_plans = existing_plans.exclude(pk=self.instance.pk)
|
|
90
|
+
|
|
91
|
+
if existing_plans.exists():
|
|
92
|
+
raise serializers.ValidationError({
|
|
93
|
+
'name': f"A maintenance plan with name '{name}' already exists for this device."
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
return data
|
|
97
|
+
|
|
98
|
+
def to_representation(self, instance):
|
|
99
|
+
"""Add computed fields to the representation"""
|
|
100
|
+
data = super().to_representation(instance)
|
|
101
|
+
|
|
102
|
+
# Add computed fields
|
|
103
|
+
data['execution_count'] = instance.executions.count()
|
|
104
|
+
data['next_maintenance_date'] = instance.get_next_maintenance_date()
|
|
105
|
+
data['is_overdue'] = instance.is_overdue()
|
|
106
|
+
data['days_until_due'] = instance.days_until_due()
|
|
107
|
+
|
|
108
|
+
# Add last execution date
|
|
109
|
+
last_execution = instance.executions.filter(completed=True).order_by('-completed_date').first()
|
|
110
|
+
data['last_execution_date'] = last_execution.completed_date if last_execution else None
|
|
111
|
+
|
|
112
|
+
return data
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class MaintenanceExecutionSerializer(NetBoxModelSerializer):
|
|
116
|
+
"""Complete serializer for MaintenanceExecution with all relationships and validations"""
|
|
117
|
+
|
|
118
|
+
url = serializers.HyperlinkedIdentityField(
|
|
119
|
+
view_name='plugins-api:netbox_maintenance_device-api:maintenanceexecution-detail'
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Nested relationships
|
|
123
|
+
maintenance_plan = NestedMaintenancePlanSerializer()
|
|
124
|
+
|
|
125
|
+
# Computed fields
|
|
126
|
+
device = serializers.CharField(source='maintenance_plan.device.name', read_only=True)
|
|
127
|
+
device_id = serializers.IntegerField(source='maintenance_plan.device.id', read_only=True)
|
|
128
|
+
plan_name = serializers.CharField(source='maintenance_plan.name', read_only=True)
|
|
129
|
+
duration_days = serializers.SerializerMethodField()
|
|
130
|
+
|
|
131
|
+
class Meta:
|
|
132
|
+
model = models.MaintenanceExecution
|
|
133
|
+
fields = [
|
|
134
|
+
'id', 'url', 'display', 'maintenance_plan', 'scheduled_date',
|
|
135
|
+
'completed_date', 'status', 'notes', 'technician', 'completed',
|
|
136
|
+
'created', 'last_updated', 'custom_field_data', 'tags',
|
|
137
|
+
# Computed fields
|
|
138
|
+
'device', 'device_id', 'plan_name', 'duration_days'
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
def get_duration_days(self, obj):
|
|
142
|
+
"""Calculate duration between scheduled and completed dates"""
|
|
143
|
+
if obj.scheduled_date and obj.completed_date:
|
|
144
|
+
delta = obj.completed_date - obj.scheduled_date
|
|
145
|
+
return delta.days
|
|
146
|
+
return None
|
|
147
|
+
|
|
148
|
+
def validate_scheduled_date(self, value):
|
|
149
|
+
"""Validate scheduled date is not too far in the past"""
|
|
150
|
+
if value and value.date() < (timezone.now().date() - timezone.timedelta(days=365)):
|
|
151
|
+
raise serializers.ValidationError("Scheduled date cannot be more than 1 year in the past")
|
|
152
|
+
return value
|
|
153
|
+
|
|
154
|
+
def validate_completed_date(self, value):
|
|
155
|
+
"""Validate completed date is not in the future"""
|
|
156
|
+
if value and value.date() > timezone.now().date():
|
|
157
|
+
raise serializers.ValidationError("Completed date cannot be in the future")
|
|
158
|
+
return value
|
|
159
|
+
|
|
160
|
+
def validate(self, data):
|
|
161
|
+
"""Additional model-level validations"""
|
|
162
|
+
scheduled_date = data.get('scheduled_date')
|
|
163
|
+
completed_date = data.get('completed_date')
|
|
164
|
+
status = data.get('status')
|
|
165
|
+
|
|
166
|
+
# If status is completed, completed_date should be set
|
|
167
|
+
if status == 'completed' and not completed_date:
|
|
168
|
+
raise serializers.ValidationError({
|
|
169
|
+
'completed_date': "Completed date is required when status is 'completed'."
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
# If completed_date is set, status should be completed
|
|
173
|
+
if completed_date and status != 'completed':
|
|
174
|
+
raise serializers.ValidationError({
|
|
175
|
+
'status': "Status must be 'completed' when completed date is set."
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
# Completed date should not be before scheduled date
|
|
179
|
+
if scheduled_date and completed_date and completed_date < scheduled_date:
|
|
180
|
+
raise serializers.ValidationError({
|
|
181
|
+
'completed_date': "Completed date cannot be before scheduled date."
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
return data
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# Bulk operation serializers for efficient operations
|
|
188
|
+
class BulkMaintenancePlanSerializer(serializers.ListSerializer):
|
|
189
|
+
"""Bulk operations for MaintenancePlan"""
|
|
190
|
+
|
|
191
|
+
def create(self, validated_data):
|
|
192
|
+
"""Bulk create maintenance plans"""
|
|
193
|
+
return [models.MaintenancePlan.objects.create(**attrs) for attrs in validated_data]
|
|
194
|
+
|
|
195
|
+
def update(self, instance_list, validated_data):
|
|
196
|
+
"""Bulk update maintenance plans"""
|
|
197
|
+
plan_mapping = {plan.id: plan for plan in instance_list}
|
|
198
|
+
|
|
199
|
+
updated_plans = []
|
|
200
|
+
for attrs in validated_data:
|
|
201
|
+
plan_id = attrs.pop('id', None)
|
|
202
|
+
if plan_id and plan_id in plan_mapping:
|
|
203
|
+
plan = plan_mapping[plan_id]
|
|
204
|
+
for key, value in attrs.items():
|
|
205
|
+
setattr(plan, key, value)
|
|
206
|
+
plan.save()
|
|
207
|
+
updated_plans.append(plan)
|
|
208
|
+
|
|
209
|
+
return updated_plans
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class BulkMaintenanceExecutionSerializer(serializers.ListSerializer):
|
|
213
|
+
"""Bulk operations for MaintenanceExecution"""
|
|
214
|
+
|
|
215
|
+
def create(self, validated_data):
|
|
216
|
+
"""Bulk create maintenance executions"""
|
|
217
|
+
return [models.MaintenanceExecution.objects.create(**attrs) for attrs in validated_data]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from django.urls import path, include
|
|
2
|
+
from netbox.api.routers import NetBoxRouter
|
|
3
|
+
from . import views
|
|
4
|
+
|
|
5
|
+
app_name = 'netbox_maintenance_device-api'
|
|
6
|
+
|
|
7
|
+
# Main API router for CRUD operations
|
|
8
|
+
router = NetBoxRouter()
|
|
9
|
+
|
|
10
|
+
# Register main viewsets
|
|
11
|
+
router.register('maintenance-plans', views.MaintenancePlanViewSet, basename='maintenanceplan')
|
|
12
|
+
router.register('maintenance-executions', views.MaintenanceExecutionViewSet, basename='maintenanceexecution')
|
|
13
|
+
|
|
14
|
+
# Include all router URLs
|
|
15
|
+
urlpatterns = [
|
|
16
|
+
# Main API routes (includes all CRUD operations and custom actions)
|
|
17
|
+
path('', include(router.urls)),
|
|
18
|
+
|
|
19
|
+
# Additional custom endpoints can be added here if needed
|
|
20
|
+
# Example: path('reports/', views.MaintenanceReportView.as_view(), name='maintenance-reports'),
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
# URL patterns summary for documentation:
|
|
24
|
+
#
|
|
25
|
+
# MaintenancePlan endpoints:
|
|
26
|
+
# GET /api/plugins/netbox-maintenance-device/maintenance-plans/ - List all plans
|
|
27
|
+
# POST /api/plugins/netbox-maintenance-device/maintenance-plans/ - Create new plan
|
|
28
|
+
# GET /api/plugins/netbox-maintenance-device/maintenance-plans/{id}/ - Get specific plan
|
|
29
|
+
# PUT /api/plugins/netbox-maintenance-device/maintenance-plans/{id}/ - Update plan (full)
|
|
30
|
+
# PATCH /api/plugins/netbox-maintenance-device/maintenance-plans/{id}/ - Update plan (partial)
|
|
31
|
+
# DELETE /api/plugins/netbox-maintenance-device/maintenance-plans/{id}/ - Delete plan
|
|
32
|
+
#
|
|
33
|
+
# Custom MaintenancePlan actions:
|
|
34
|
+
# POST /api/plugins/netbox-maintenance-device/maintenance-plans/{id}/schedule-maintenance/ - Schedule execution
|
|
35
|
+
# GET /api/plugins/netbox-maintenance-device/maintenance-plans/overdue/ - Get overdue plans
|
|
36
|
+
# GET /api/plugins/netbox-maintenance-device/maintenance-plans/upcoming/ - Get upcoming plans
|
|
37
|
+
# GET /api/plugins/netbox-maintenance-device/maintenance-plans/statistics/ - Get plan statistics
|
|
38
|
+
#
|
|
39
|
+
# MaintenanceExecution endpoints:
|
|
40
|
+
# GET /api/plugins/netbox-maintenance-device/maintenance-executions/ - List all executions
|
|
41
|
+
# POST /api/plugins/netbox-maintenance-device/maintenance-executions/ - Create new execution
|
|
42
|
+
# GET /api/plugins/netbox-maintenance-device/maintenance-executions/{id}/ - Get specific execution
|
|
43
|
+
# PUT /api/plugins/netbox-maintenance-device/maintenance-executions/{id}/ - Update execution (full)
|
|
44
|
+
# PATCH /api/plugins/netbox-maintenance-device/maintenance-executions/{id}/ - Update execution (partial)
|
|
45
|
+
# DELETE /api/plugins/netbox-maintenance-device/maintenance-executions/{id}/ - Delete execution
|
|
46
|
+
#
|
|
47
|
+
# Custom MaintenanceExecution actions:
|
|
48
|
+
# POST /api/plugins/netbox-maintenance-device/maintenance-executions/{id}/complete/ - Mark as completed
|
|
49
|
+
# POST /api/plugins/netbox-maintenance-device/maintenance-executions/{id}/cancel/ - Cancel execution
|
|
50
|
+
# GET /api/plugins/netbox-maintenance-device/maintenance-executions/pending/ - Get pending executions
|
|
51
|
+
# GET /api/plugins/netbox-maintenance-device/maintenance-executions/overdue-executions/ - Get overdue executions
|
|
52
|
+
# GET /api/plugins/netbox-maintenance-device/maintenance-executions/statistics/ - Get execution statistics
|
|
53
|
+
#
|
|
54
|
+
# All endpoints support:
|
|
55
|
+
# - Filtering via query parameters (see FilterSets in views.py)
|
|
56
|
+
# - Ordering via ?ordering=field_name
|
|
57
|
+
# - Search via ?q=search_term
|
|
58
|
+
# - Pagination via ?limit=N&offset=N
|
|
59
|
+
# - Field selection via ?fields=field1,field2
|
|
60
|
+
# - Format selection via ?format=json|api (HTML browsable API)
|