diff --git a/netbox_cmdb/netbox_cmdb/api/cmdb/views.py b/netbox_cmdb/netbox_cmdb/api/cmdb/views.py
index 3d2ea01..1315d80 100644
--- a/netbox_cmdb/netbox_cmdb/api/cmdb/views.py
+++ b/netbox_cmdb/netbox_cmdb/api/cmdb/views.py
@@ -1,57 +1,169 @@
+from dcim.models import Device, Site
from django.db import transaction
-from django.db.models import Q
+from django.http import StreamingHttpResponse
from drf_yasg.utils import swagger_auto_schema
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
from rest_framework import serializers, status
from rest_framework.response import Response
from rest_framework.views import APIView
-from netbox_cmdb.models.bgp import BGPPeerGroup, BGPSession, DeviceBGPSession
-from netbox_cmdb.models.bgp_community_list import BGPCommunityList
-from netbox_cmdb.models.prefix_list import PrefixList
-from netbox_cmdb.models.route_policy import RoutePolicy
-from netbox_cmdb.models.snmp import SNMP
+from netbox_cmdb.helpers import cleaning
-class DeleteAllCMDBObjectsRelatedToDeviceSerializer(serializers.Serializer):
+class DeviceDecommissioningBaseSerializer(serializers.Serializer):
device_name = serializers.CharField()
-class DeleteAllCMDBObjectsRelatedToDevice(APIView):
+class DeviceCMDBDecommissioningAPIView(APIView):
permission_classes = [IsAuthenticatedOrLoginNotRequired]
@swagger_auto_schema(
- request_body=DeleteAllCMDBObjectsRelatedToDeviceSerializer,
+ request_body=DeviceDecommissioningBaseSerializer,
responses={
status.HTTP_200_OK: "Objects related to device have been deleted successfully",
status.HTTP_400_BAD_REQUEST: "Bad Request: Device name is required",
+ status.HTTP_404_NOT_FOUND: "Bad Request: Device not found",
status.HTTP_500_INTERNAL_SERVER_ERROR: "Internal Server Error: Something went wrong on the server",
},
)
- def post(self, request):
+ def delete(self, request):
device_name = request.data.get("device_name", None)
if device_name is None:
return Response(
- {"error": "Device name is required"}, status=status.HTTP_400_BAD_REQUEST
+ {"error": "device_name is required"}, status=status.HTTP_400_BAD_REQUEST
+ )
+
+ devices = Device.objects.filter(name=device_name)
+ device_ids = [dev.id for dev in devices]
+ if not device_ids:
+ return Response(
+ {"error": "no matching devices found"}, status=status.HTTP_404_NOT_FOUND
)
try:
with transaction.atomic():
- # Delete objects in reverse order of dependencies
- BGPSession.objects.filter(
- Q(peer_a__device__name=device_name) | Q(peer_b__device__name=device_name)
- ).delete()
- DeviceBGPSession.objects.filter(device__name=device_name).delete()
- BGPPeerGroup.objects.filter(device__name=device_name).delete()
- RoutePolicy.objects.filter(device__name=device_name).delete()
- PrefixList.objects.filter(device__name=device_name).delete()
- BGPCommunityList.objects.filter(device_name=device_name).delete()
- SNMP.objects.filter(device__name=device_name).delete()
+ deleted = cleaning.clean_cmdb_for_devices(device_ids)
except Exception as e:
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return Response(
- {"message": f"Objects related to device {device_name} have been deleted successfully"},
+ {
+ "message": f"CMDB cleaned for {device_name}",
+ "deleted": deleted,
+ },
status=status.HTTP_200_OK,
)
+
+
+class DeviceDecommissioningAPIView(APIView):
+
+ permission_classes = [IsAuthenticatedOrLoginNotRequired]
+
+ @swagger_auto_schema(
+ request_body=DeviceDecommissioningBaseSerializer,
+ responses={
+ status.HTTP_200_OK: "Objects related to device have been deleted successfully",
+ status.HTTP_400_BAD_REQUEST: "Bad Request: Device name is required",
+ status.HTTP_404_NOT_FOUND: "Bad Request: Device not found",
+ status.HTTP_500_INTERNAL_SERVER_ERROR: "Internal Server Error: Something went wrong on the server",
+ },
+ )
+ def delete(self, request):
+ device_name = request.data.get("device_name", None)
+ if device_name is None:
+ return Response(
+ {"error": "device_name is required"}, status=status.HTTP_400_BAD_REQUEST
+ )
+
+ devices = Device.objects.filter(name=device_name)
+ device_ids = [dev.id for dev in devices]
+ if not device_ids:
+ return Response(
+ {"error": "no matching devices found"}, status=status.HTTP_404_NOT_FOUND
+ )
+
+ try:
+ with transaction.atomic():
+ deleted = cleaning.clean_cmdb_for_devices(device_ids)
+ for device in devices:
+ device.delete()
+ except Exception as e:
+ return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+
+ return Response(
+ {
+ "message": f"{device_name} decommissionned",
+ "deleted": deleted,
+ },
+ status=status.HTTP_200_OK,
+ )
+
+
+class SiteDecommissioningSerializer(serializers.Serializer):
+ site_name = serializers.CharField()
+
+
+class SiteDecommissioningAPIView(APIView):
+
+ permission_classes = [IsAuthenticatedOrLoginNotRequired]
+
+ @swagger_auto_schema(
+ request_body=SiteDecommissioningSerializer,
+ responses={
+ status.HTTP_200_OK: "Site have been deleted successfully",
+ status.HTTP_400_BAD_REQUEST: "Bad Request: Site name is required",
+ status.HTTP_404_NOT_FOUND: "Bad Request: Site not found",
+ status.HTTP_500_INTERNAL_SERVER_ERROR: "Internal Server Error: Something went wrong on the server",
+ },
+ )
+ def delete(self, request):
+ site_name = request.data.get("site_name", None)
+ if site_name is None:
+ return Response({"error": "site_name is required"}, status=status.HTTP_400_BAD_REQUEST)
+
+ try:
+ site = Site.objects.get(name=site_name)
+ except Site.DoesNotExist:
+ return Response({"error": "site not found"}, status=status.HTTP_404_NOT_FOUND)
+ except Exception:
+ return Response(
+ {"error": "internal server error"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
+ )
+
+ devices = Device.objects.filter(site=site.id)
+
+ def _start():
+ CHUNK_SIZE = 20
+ device_ids = [dev.id for dev in devices]
+ for i in range(0, len(device_ids), CHUNK_SIZE):
+ chunk = device_ids[i : i + CHUNK_SIZE]
+ try:
+ with transaction.atomic():
+ cleaning.clean_cmdb_for_devices(chunk)
+ for dev in devices[i : i + CHUNK_SIZE]:
+ dev.delete()
+ yield f'{{"deleted": {[dev.name for dev in devices[i:i+CHUNK_SIZE]]}}}\n\n'
+
+ except Exception as e:
+ StreamingHttpResponse.status_code = 500
+ msg = {"error": str(e)}
+ yield f"{msg}\n\n"
+ return
+
+ try:
+ with transaction.atomic():
+ cleaning.clean_site_topology(site)
+ yield "{{'message': 'topology cleaned'}}\n\n"
+ except Exception as e:
+ StreamingHttpResponse.status_code = 500
+ msg = {"error": str(e)}
+ yield f"{msg}\n\n"
+ return
+
+ msg = {
+ "message": f"site {site_name} has been deleted successfully",
+ }
+ yield f"{msg}\n\n"
+
+ return StreamingHttpResponse(_start(), content_type="text/plain")
diff --git a/netbox_cmdb/netbox_cmdb/api/urls.py b/netbox_cmdb/netbox_cmdb/api/urls.py
index d1930d4..160339c 100644
--- a/netbox_cmdb/netbox_cmdb/api/urls.py
+++ b/netbox_cmdb/netbox_cmdb/api/urls.py
@@ -13,7 +13,11 @@
from netbox_cmdb.api.prefix_list.views import PrefixListViewSet
from netbox_cmdb.api.route_policy.views import RoutePolicyViewSet
from netbox_cmdb.api.snmp.views import SNMPCommunityViewSet, SNMPViewSet
-from netbox_cmdb.api.cmdb.views import DeleteAllCMDBObjectsRelatedToDevice
+from netbox_cmdb.api.cmdb.views import (
+ DeviceCMDBDecommissioningAPIView,
+ DeviceDecommissioningAPIView,
+ SiteDecommissioningAPIView,
+)
router = NetBoxRouter()
@@ -35,9 +39,19 @@
name="asns-available-asn",
),
path(
- "cmdb/delete-all-objects/",
- DeleteAllCMDBObjectsRelatedToDevice.as_view(),
- name="asns-available-asn",
+ "management/device-cmdb-decommissioning/",
+ DeviceCMDBDecommissioningAPIView.as_view(),
+ name="device-cmdb-decommissioning",
+ ),
+ path(
+ "management/device-decommissioning/",
+ DeviceDecommissioningAPIView.as_view(),
+ name="device-decommissioning",
+ ),
+ path(
+ "management/site-decommissioning/",
+ SiteDecommissioningAPIView.as_view(),
+ name="site-decommissioning",
),
]
urlpatterns += router.urls
diff --git a/netbox_cmdb/netbox_cmdb/helpers/cleaning.py b/netbox_cmdb/netbox_cmdb/helpers/cleaning.py
new file mode 100644
index 0000000..6f1e30f
--- /dev/null
+++ b/netbox_cmdb/netbox_cmdb/helpers/cleaning.py
@@ -0,0 +1,58 @@
+from dcim.models import Location, Rack
+from django.db.models import Q
+
+from netbox_cmdb.models.bgp import BGPPeerGroup, BGPSession, DeviceBGPSession
+from netbox_cmdb.models.bgp_community_list import BGPCommunityList
+from netbox_cmdb.models.prefix_list import PrefixList
+from netbox_cmdb.models.route_policy import RoutePolicy
+from netbox_cmdb.models.snmp import SNMP
+
+
+def clean_cmdb_for_devices(device_ids: list[int]):
+ deleted_objects = {
+ "bgp_sessions": [],
+ "device_bgp_sessions": [],
+ "bgp_peer_groups": [],
+ "route_policies": [],
+ "prefix_lists": [],
+ "bgp_community_lists": [],
+ "snmp": [],
+ }
+
+ bgp_sessions = BGPSession.objects.filter(
+ Q(peer_a__device__id__in=device_ids) | Q(peer_b__device__id__in=device_ids)
+ )
+ device_bgp_sessions = DeviceBGPSession.objects.filter(device__id__in=device_ids)
+ bgp_peer_groups = BGPPeerGroup.objects.filter(device__id__in=device_ids)
+ route_policies = RoutePolicy.objects.filter(device__id__in=device_ids)
+ prefix_lists = PrefixList.objects.filter(device__id__in=device_ids)
+ bgp_community_lists = BGPCommunityList.objects.filter(device__id__in=device_ids)
+ snmp = SNMP.objects.filter(device__id__in=device_ids)
+
+ deleted_objects["bgp_sessions"] = [str(val) for val in list(bgp_sessions)]
+ deleted_objects["device_bgp_sessions"] = [str(val) for val in list(device_bgp_sessions)]
+ deleted_objects["bgp_peer_groups"] = [str(val) for val in list(bgp_peer_groups)]
+ deleted_objects["route_policies"] = [str(val) for val in list(route_policies)]
+ deleted_objects["prefix_lists"] = [str(val) for val in list(prefix_lists)]
+ deleted_objects["bgp_community_lists"] = [str(val) for val in list(bgp_community_lists)]
+ deleted_objects["snmp"] = [str(val) for val in list(snmp)]
+
+ bgp_sessions.delete()
+ device_bgp_sessions.delete()
+ bgp_peer_groups.delete()
+ route_policies.delete()
+ prefix_lists.delete()
+ bgp_community_lists.delete()
+ snmp.delete()
+
+ return deleted_objects
+
+
+def clean_site_topology(site):
+ racks = Rack.objects.filter(site=site.id)
+ racks.delete()
+
+ locations = Location.objects.filter(site=site.id)
+ locations.delete()
+
+ site.delete()
diff --git a/netbox_cmdb/netbox_cmdb/template_content.py b/netbox_cmdb/netbox_cmdb/template_content.py
index e573140..b869993 100644
--- a/netbox_cmdb/netbox_cmdb/template_content.py
+++ b/netbox_cmdb/netbox_cmdb/template_content.py
@@ -1,15 +1,22 @@
from extras.plugins import PluginTemplateExtension
-class Decommisioning(PluginTemplateExtension):
- model = "dcim.device"
-
+class DecommissioningBase(PluginTemplateExtension):
def buttons(self):
return (
- f'Decommission'
)
-template_extensions = [Decommisioning]
+class DeviceDecommissioning(DecommissioningBase):
+ model = "dcim.device"
+ obj = "device"
+
+
+class SiteDecommissioning(DecommissioningBase):
+ model = "dcim.site"
+ obj = "site"
+
+
+template_extensions = [DeviceDecommissioning, SiteDecommissioning]
diff --git a/netbox_cmdb/netbox_cmdb/templates/netbox_cmdb/decommissioning.html b/netbox_cmdb/netbox_cmdb/templates/netbox_cmdb/decommissioning.html
deleted file mode 100644
index c456889..0000000
--- a/netbox_cmdb/netbox_cmdb/templates/netbox_cmdb/decommissioning.html
+++ /dev/null
@@ -1,43 +0,0 @@
-{% extends "base/layout.html" %}
-
-{% block content-wrapper %}
-
- {% if error %}
-
{{ error }}
- {% else %}
-
-
Deleted objects
-
-
-
- {% for key, objects in deleted_objects.items %}
- {% if objects %}
-
- {% endif %}
- {% endfor %}
-
-
-
- {% endif %}
-
-{% endblock content-wrapper%}
diff --git a/netbox_cmdb/netbox_cmdb/templates/netbox_cmdb/decommissioning/base.html b/netbox_cmdb/netbox_cmdb/templates/netbox_cmdb/decommissioning/base.html
new file mode 100644
index 0000000..55093e1
--- /dev/null
+++ b/netbox_cmdb/netbox_cmdb/templates/netbox_cmdb/decommissioning/base.html
@@ -0,0 +1,33 @@
+{% extends "base/layout.html" %}
+{% block title %}
+ {{ object.name }} decommissioning
+{% endblock %}
+
+{% block content-wrapper %}
+
+
+
+
+
+
+ {% if error %}
+
{{ error }}
+ {% else %}
+
+
+ Warning: this action will remove both
+ CMDB assets and DCIM of concerned asset(s)
+
+
+
+ {% endif %}
+
+
+
+{% endblock content-wrapper%}
diff --git a/netbox_cmdb/netbox_cmdb/templates/netbox_cmdb/decommissioning/device_summary.html b/netbox_cmdb/netbox_cmdb/templates/netbox_cmdb/decommissioning/device_summary.html
new file mode 100644
index 0000000..415e94a
--- /dev/null
+++ b/netbox_cmdb/netbox_cmdb/templates/netbox_cmdb/decommissioning/device_summary.html
@@ -0,0 +1,35 @@
+
+ {% if error %}
+
{{ error }}
+ {% endif %}
+
+
Deleted objects
+
+
+
+ {% for key, objects in deleted_objects.items %}
+ {% if objects %}
+
+ {% endif %}
+ {% endfor %}
+
diff --git a/netbox_cmdb/netbox_cmdb/templates/netbox_cmdb/decommissioning/site_progressive.html b/netbox_cmdb/netbox_cmdb/templates/netbox_cmdb/decommissioning/site_progressive.html
new file mode 100644
index 0000000..41a03c8
--- /dev/null
+++ b/netbox_cmdb/netbox_cmdb/templates/netbox_cmdb/decommissioning/site_progressive.html
@@ -0,0 +1,29 @@
+{% if not stop %}
+
+{% endif %}
+
+{# --- #}
+{# OOB components #}
+{# -- #}
+
+
+ {{ status | safe }}
+
+
+{% if error %}
+
+ {{ error | safe }}
+
+{% endif %}
+
+
+ {{ message | safe }}
+
diff --git a/netbox_cmdb/netbox_cmdb/urls.py b/netbox_cmdb/netbox_cmdb/urls.py
index eac2fe6..6943f3b 100644
--- a/netbox_cmdb/netbox_cmdb/urls.py
+++ b/netbox_cmdb/netbox_cmdb/urls.py
@@ -20,15 +20,16 @@
BGPSessionEditView,
BGPSessionListView,
BGPSessionView,
- DecommissioningView,
DeviceBGPSessionEditView,
DeviceBGPSessionListView,
DeviceBGPSessionView,
+ DeviceDecommissioningView,
DeviceBGPSessionDeleteView,
RoutePolicyDeleteView,
RoutePolicyEditView,
RoutePolicyListView,
RoutePolicyView,
+ SiteDecommissioningView,
SNMPCommunityDeleteView,
SNMPCommunityEditView,
SNMPCommunityListView,
@@ -39,9 +40,14 @@
urlpatterns = [
path(
- "decommisioning//delete",
- DecommissioningView.as_view(),
- name="decommisioning_delete",
+ "decommissioning/device//delete",
+ DeviceDecommissioningView.as_view(),
+ name="device_decommissioning_delete",
+ ),
+ path(
+ "decommissioning/site//delete",
+ SiteDecommissioningView.as_view(),
+ name="site_decommissioning_delete",
),
# ASN
path("asn/", ASNListView.as_view(), name="asn_list"),
diff --git a/netbox_cmdb/netbox_cmdb/views.py b/netbox_cmdb/netbox_cmdb/views.py
index 47ee2c5..7af3e40 100644
--- a/netbox_cmdb/netbox_cmdb/views.py
+++ b/netbox_cmdb/netbox_cmdb/views.py
@@ -1,8 +1,10 @@
"""Views."""
-from dcim.models import Device
+import math
+from datetime import datetime
+
+from dcim.models import Device, Site
from django.db import transaction
-from django.db.models import Q
from django.shortcuts import render
from netbox.views.generic import (
ObjectDeleteView,
@@ -12,7 +14,6 @@
)
from netbox.views.generic.bulk_views import BulkDeleteView
from utilities.forms import ConfirmationForm
-from utilities.htmx import is_htmx
from utilities.utils import count_related
from netbox_cmdb.filtersets import (
@@ -34,6 +35,7 @@
SNMPCommunityGroupForm,
SNMPGroupForm,
)
+from netbox_cmdb.helpers import cleaning
from netbox_cmdb.models.bgp import (
ASN,
AfiSafi,
@@ -41,8 +43,6 @@
BGPSession,
DeviceBGPSession,
)
-from netbox_cmdb.models.bgp_community_list import BGPCommunityList
-from netbox_cmdb.models.prefix_list import PrefixList
from netbox_cmdb.models.route_policy import RoutePolicy
from netbox_cmdb.models.snmp import SNMP, SNMPCommunity
from netbox_cmdb.tables import (
@@ -56,107 +56,171 @@
)
-## Decommission a device
-class DecommissioningView(ObjectDeleteView):
+class DecommissioningBaseView(ObjectDeleteView):
+ template_name = "netbox_cmdb/decommissioning/base.html"
+ site_template_name = "netbox_cmdb/decommissioning/site_progressive.html"
+ device_template_name = "netbox_cmdb/decommissioning/device_summary.html"
+ base_form_url = ""
+
+
+class DeviceDecommissioningView(DecommissioningBaseView):
+ base_form_url = "/plugins/cmdb/decommissioning/device"
queryset = Device.objects.all()
- template_name = "netbox_cmdb/decommissioning.html"
def get(self, request, *args, **kwargs):
- """
- GET request handler.
-
- Args:
- request: The current request
- """
- obj = self.get_object(**kwargs)
+ device = self.get_object(**kwargs)
form = ConfirmationForm(initial=request.GET)
- # If this is an HTMX request, return only the rendered deletion form as modal content
- if is_htmx(request):
- # form_url = reverse("decommisioning_delete", kwargs={'pk': obj.pk})
- form_url = f"/plugins/cmdb/decommisioning/{kwargs['pk']}/delete"
+ return render(
+ request,
+ self.template_name,
+ {
+ "object": device,
+ "object_type": "device",
+ "form": form,
+ "return_url": self.get_return_url(request, device),
+ **self.get_extra_context(request, device),
+ },
+ )
+
+ def post(self, request, *args, **kwargs):
+ # Fetch the device to delete
+ device = self.get_object(**kwargs)
+
+ try:
+ with transaction.atomic():
+ deleted = cleaning.clean_cmdb_for_devices([device.id])
+ device.delete()
+ except Exception as error:
return render(
request,
- "htmx/delete_form.html",
- {
- "object": obj,
- "object_type": self.queryset.model._meta.verbose_name,
- "form": form,
- "form_url": form_url,
- **self.get_extra_context(request, obj),
- },
+ self.device_template_name,
+ context={"error": f"Failed to clean device: {error}"},
)
+ return render(
+ request,
+ self.device_template_name,
+ context={"deleted_device": device.name, "deleted_objects": deleted},
+ )
+
+
+class SiteDecommissioningView(DecommissioningBaseView):
+ base_form_url = "/plugins/cmdb/decommissioning/site"
+ queryset = Site.objects.all()
+
+ def get(self, request, *args, **kwargs):
+ site = self.get_object(**kwargs)
+ form = ConfirmationForm(initial=request.GET)
+
return render(
request,
self.template_name,
{
- "object": obj,
+ "object": site,
+ "object_type": "site",
"form": form,
- "return_url": self.get_return_url(request, obj),
- **self.get_extra_context(request, obj),
+ "return_url": self.get_return_url(request, site),
+ **self.get_extra_context(request, site),
},
)
def post(self, request, *args, **kwargs):
# Fetch the device to delete
- device = self.get_object(**kwargs)
- deleted_objects = {
- "bgp_sessions": [],
- "device_bgp_sessions": [],
- "bgp_peer_groups": [],
- "route_policies": [],
- "prefix_lists": [],
- "bgp_community_lists": [],
- "snmp": [],
- }
-
- device_name = device.name
+ site = self.get_object(**kwargs)
+ devices = Device.objects.filter(site=site.id)
+
+ # Get list of devices to delete
+ CHUNK_SIZE = 20
+ device_ids = [dev.id for dev in devices]
+ remaining_chunks = math.ceil(len(device_ids) / CHUNK_SIZE)
+
+ # We avoid an infinite loop if we fail to delete devices
+ if request.POST.get("chunks"):
+ previously_remaining_chunks = int(request.POST["chunks"])
+ if remaining_chunks >= previously_remaining_chunks:
+ return render(
+ request,
+ self.site_template_name,
+ context={
+ "object": site,
+ "object_type": "site",
+ "status": 'Failed',
+ "error": "devices are not being deleted, stopping here",
+ "chunks": remaining_chunks,
+ "stop": True,
+ },
+ )
+
+ if not device_ids:
+ try:
+ with transaction.atomic():
+ cleaning.clean_site_topology(site)
+ except Exception as error:
+ return render(
+ request,
+ self.site_template_name,
+ context={
+ "object": site,
+ "object_type": "site",
+ "status": 'Failed',
+ "error": f"Topology cleaning failure: {error}",
+ "chunks": remaining_chunks,
+ "stop": True,
+ },
+ )
+
+ chunk = device_ids[0:CHUNK_SIZE]
try:
with transaction.atomic():
- bgp_sessions = BGPSession.objects.filter(
- Q(peer_a__device__id=device.id) | Q(peer_b__device__id=device.id)
- )
- device_bgp_sessions = DeviceBGPSession.objects.filter(device__id=device.id)
- bgp_peer_groups = BGPPeerGroup.objects.filter(device__id=device.id)
- route_policies = RoutePolicy.objects.filter(device__id=device.id)
- prefix_lists = PrefixList.objects.filter(device__id=device.id)
- bgp_community_lists = BGPCommunityList.objects.filter(device__id=device.id)
- snmp = SNMP.objects.filter(device__id=device.id)
-
- deleted_objects["bgp_sessions"] = [str(val) for val in list(bgp_sessions)]
- deleted_objects["device_bgp_sessions"] = [
- str(val) for val in list(device_bgp_sessions)
- ]
- deleted_objects["bgp_peer_groups"] = [str(val) for val in list(bgp_peer_groups)]
- deleted_objects["route_policies"] = [str(val) for val in list(route_policies)]
- deleted_objects["prefix_lists"] = [str(val) for val in list(prefix_lists)]
- deleted_objects["bgp_community_lists"] = [
- str(val) for val in list(bgp_community_lists)
- ]
- deleted_objects["snmp"] = [str(val) for val in list(snmp)]
-
- bgp_sessions.delete()
- device_bgp_sessions.delete()
- bgp_peer_groups.delete()
- route_policies.delete()
- prefix_lists.delete()
- bgp_community_lists.delete()
- snmp.delete()
-
- except Exception as e:
- # Render the template with an error message
- return render(request, self.template_name, context={"error": str(e)})
-
- # Call the parent class's post method to delete the device
- super().post(request, *args, **kwargs)
-
- # Return the HTML response with the list of deleted objects
+ cleaning.clean_cmdb_for_devices(chunk)
+ device_names = [dev.name for dev in devices[0:CHUNK_SIZE]]
+ for dev in devices[0:CHUNK_SIZE]:
+ dev.delete()
+
+ except Exception as error:
+ return render(
+ request,
+ self.site_template_name,
+ context={
+ "object": site,
+ "object_type": "site",
+ "status": 'Failed',
+ "error": f"Device cleaning failure: {error}",
+ "chunks": remaining_chunks,
+ "stop": True,
+ },
+ )
+
+ # Tell the client to continue requesting deletion
+ if remaining_chunks:
+ status = f"Number of devices to delete: {len(device_ids) - len(chunk)}"
+ message = f'{datetime.now().strftime("%H:%M:%S")} - deleted: {device_names}
'
+
+ return render(
+ request,
+ self.site_template_name,
+ context={
+ "object": site,
+ "object_type": "site",
+ "status": status,
+ "message": message,
+ "chunks": remaining_chunks,
+ },
+ )
+
return render(
request,
- self.template_name,
- context={"deleted_device": device_name, "deleted_objects": deleted_objects},
+ self.site_template_name,
+ context={
+ "object": site,
+ "object_type": "site",
+ "status": 'Success',
+ "message": f'{site.name}: site, racks, locations, devices and CMDB deleted
',
+ "chunks": remaining_chunks,
+ "stop": True,
+ },
)