Fix interface comparison. Modified global post

This commit is contained in:
rizlas 2021-12-28 20:29:37 +01:00
parent 80112869e0
commit d1b2b82d13
3 changed files with 234 additions and 176 deletions

View File

@ -59,6 +59,10 @@ class InterfaceComparison(ParentTypedComparison):
and (self.mgmt_only == other.mgmt_only)
)
def __hash__(self):
# Ignore some fields when hashing; ignore interface name case and whitespaces
return hash((self.name.lower().replace(" ", ""), self.type))
@dataclass(frozen=True)
class FrontPortComparison(ParentTypedComparison):
@ -143,24 +147,3 @@ class DeviceBayComparison(ParentComparison):
"""A unified way to represent the interface and interface template"""
is_template: bool = False
@dataclass(frozen=True)
class UnifiedInterface:
"""A unified way to represent the interface and interface template"""
id: int
name: str
type: str = ""
type_display: str = ""
is_template: bool = False
def __eq__(self, other):
# Ignore some fields when comparing; ignore interface name case and whitespaces
return (
self.name.lower().replace(" ", "") == other.name.lower().replace(" ", "")
) and (self.type == other.type)
def __hash__(self):
# Ignore some fields when hashing; ignore interface name case and whitespaces
return hash((self.name.lower().replace(" ", ""), self.type))

View File

@ -2,7 +2,7 @@ import re
from typing import Iterable
from django.shortcuts import render, redirect
from django.contrib import messages
from .comparison import UnifiedInterface
from django.core.exceptions import ObjectDoesNotExist
def split(s):
@ -20,18 +20,6 @@ def human_sorted(iterable: Iterable):
def get_components(request, device, components, unified_components, unified_component_templates):
# try:
# unified_components = [UnifiedInterface(i.id, i.name, i.type, i.get_type_display()) for i in components]
# except AttributeError:
# unified_components = [UnifiedInterface(i.id, i.name) for i in components]
# try:
# unified_component_templates = [
# UnifiedInterface(i.id, i.name, i.type, i.get_type_display(), is_template=True) for i in component_templates]
# except AttributeError:
# unified_component_templates = [
# UnifiedInterface(i.id, i.name, is_template=True) for i in component_templates]
# List of interfaces and interface templates presented in the unified format
overall_powers = list(set(unified_component_templates + unified_components))
overall_powers.sort(key=lambda o: natural_keys(o.name))
@ -67,7 +55,7 @@ def get_components(request, device, components, unified_components, unified_comp
def post_components(
request, device, components, component_templates, ObjectType, ObjectTemplateType
request, device, components, component_templates, ObjectType, ObjectTemplateType, unified_component, unified_component_templates
):
# Manually validating interfaces and interface templates lists
add_to_device = filter(
@ -89,50 +77,38 @@ def post_components(
add_to_device_component = ObjectTemplateType.objects.filter(id__in=add_to_device)
bulk_create = []
updated = 0
keys_to_avoid = ["id"]
for i in add_to_device_component.values():
tmp = ObjectType()
tmp.device = device
to_create = False
try:
tmp = components.get(name=i["name"])
except ObjectDoesNotExist:
tmp = ObjectType()
tmp.device = device
to_create = True
for k in i.keys():
if k not in keys_to_avoid:
setattr(tmp, k, i[k])
bulk_create.append(tmp)
if to_create:
bulk_create.append(tmp)
else:
tmp.save()
updated += 1
created = len(ObjectType.objects.bulk_create(bulk_create))
# Getting and validating a list of interfaces to rename
fix_name_components = filter(
lambda i: str(i.id) in request.POST.getlist("fix_name"), components
)
# Casting interface templates into UnifiedInterface objects for proper comparison with interfaces for renaming
try:
unified_component_templates = [
UnifiedInterface(i.id, i.name, i.type, i.get_type_display())
for i in component_templates
]
except AttributeError:
unified_component_templates = [
UnifiedInterface(i.id, i.name) for i in component_templates
]
# Rename selected interfaces
fixed = 0
for component in fix_name_components:
try:
unified_component = UnifiedInterface(
component.id,
component.name,
component.type,
component.get_type_display(),
)
except AttributeError:
unified_component = UnifiedInterface(component.id, component.name)
for component, component_comparison in unified_component:
try:
# Try to extract an interface template with the corresponding name
corresponding_template = unified_component_templates[
unified_component_templates.index(unified_component)
unified_component_templates.index(component_comparison)
]
component.name = corresponding_template.name
component.save()
@ -144,6 +120,8 @@ def post_components(
message = []
if created > 0:
message.append(f"created {created} interfaces")
if updated > 0:
message.append(f"updated {updated} interfaces")
if deleted > 0:
message.append(f"deleted {deleted} interfaces")
if fixed > 0:

View File

@ -40,7 +40,27 @@ class InterfaceComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View)
interfaces = interfaces.exclude(type__in=["virtual", "lag"])
interface_templates = InterfaceTemplate.objects.filter(device_type=device.device_type)
return post_components(request, device, interfaces, interface_templates, Interface, InterfaceTemplate)
# Getting and validating a list of interfaces to rename
fix_name_components = filter(
lambda i: str(i.id) in request.POST.getlist("fix_name"), interfaces
)
unified_interface_templates = [
InterfaceComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.mgmt_only, is_template=True) for i in interface_templates]
unified_interfaces = []
for component in fix_name_components:
unified_interfaces.append((component, InterfaceComparison(
component.id,
component.name,
component.label,
component.description,
component.type,
component.get_type_display(),
component.mgmt_only)))
return post_components(request, device, interfaces, interface_templates, Interface, InterfaceTemplate, unified_interfaces, unified_interface_templates)
class PowerPortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
"""Comparison of interfaces between a device and a device type and beautiful visualization"""
@ -66,7 +86,28 @@ class PowerPortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View)
powerports = device.powerports.all()
powerports_templates = PowerPortTemplate.objects.filter(device_type=device.device_type)
return post_components(request, device, powerports, powerports_templates, PowerPort, PowerPortTemplate)
# Getting and validating a list of interfaces to rename
fix_name_components = filter(
lambda i: str(i.id) in request.POST.getlist("fix_name"), powerports
)
unified_powerport_templates = [
PowerPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), i.maximum_draw, i.allocated_draw, is_template=True) for i in powerports_templates]
unified_powerports = []
for component in fix_name_components:
unified_powerports.append((component, PowerPortComparison(
component.id,
component.name,
component.label,
component.description,
component.type,
component.get_type_display(),
component.maximum_draw,
component.allocated_draw)))
return post_components(request, device, powerports, powerports_templates, PowerPort, PowerPortTemplate, unified_powerports, unified_powerport_templates)
class ConsolePortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
"""Comparison of interfaces between a device and a device type and beautiful visualization"""
@ -92,7 +133,26 @@ class ConsolePortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, Vie
consoleports = device.consoleports.all()
consoleports_templates = ConsolePortTemplate.objects.filter(device_type=device.device_type)
return post_components(request, device, consoleports, consoleports_templates, ConsolePort, ConsolePortTemplate)
# Getting and validating a list of interfaces to rename
fix_name_components = filter(
lambda i: str(i.id) in request.POST.getlist("fix_name"), consoleports
)
unified_consoleport_templates = [
ConsolePortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), is_template=True) for i in consoleports_templates]
unified_consoleports = []
for component in fix_name_components:
unified_consoleports.append((component, ConsolePortComparison(
component.id,
component.name,
component.label,
component.description,
component.type,
component.get_type_display())))
return post_components(request, device, consoleports, consoleports_templates, ConsolePort, ConsolePortTemplate, unified_consoleports, unified_consoleport_templates)
class ConsoleServerPortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
"""Comparison of interfaces between a device and a device type and beautiful visualization"""
@ -118,7 +178,26 @@ class ConsoleServerPortComparisonView(LoginRequiredMixin, PermissionRequiredMixi
consoleserverports = device.consoleserverports.all()
consoleserverports_templates = ConsoleServerPortTemplate.objects.filter(device_type=device.device_type)
return post_components(request, device, consoleserverports, consoleserverports_templates, ConsoleServerPort, ConsoleServerPortTemplate)
# Getting and validating a list of interfaces to rename
fix_name_components = filter(
lambda i: str(i.id) in request.POST.getlist("fix_name"), consoleserverports
)
unified_consoleserverport_templates = [
ConsoleServerPortComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), is_template=True) for i in consoleserverports_templates]
unified_consoleserverports = []
for component in fix_name_components:
unified_consoleserverports.append((component, ConsoleServerPortComparison(
component.id,
component.name,
component.label,
component.description,
component.type,
component.get_type_display())))
return post_components(request, device, consoleserverports, consoleserverports_templates, ConsoleServerPort, ConsoleServerPortTemplate, unified_consoleserverports, unified_consoleserverport_templates)
class PowerOutletComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
"""Comparison of interfaces between a device and a device type and beautiful visualization"""
@ -141,118 +220,118 @@ class PowerOutletComparisonView(LoginRequiredMixin, PermissionRequiredMixin, Vie
if form.is_valid():
device = get_object_or_404(Device.objects.filter(id=device_id))
poweroutlets = device.poweroutlets.all()
poweroutlets_templates = PowerOutletTemplate.objects.filter(device_type=device.device_type)
poweroutlets = device.poweroutlets.all()
poweroutlets_templates = PowerOutletTemplate.objects.filter(device_type=device.device_type)
# Generating result message
message = []
created = 0
updated = 0
fixed = 0
# Generating result message
message = []
created = 0
updated = 0
fixed = 0
remove_from_device = filter(
lambda i: i in poweroutlets.values_list("id", flat=True),
map(int, filter(lambda x: x.isdigit(), request.POST.getlist("remove_from_device")))
)
# Remove selected interfaces from the device and count them
deleted = PowerOutlet.objects.filter(id__in=remove_from_device).delete()[0]
# Get device power ports to check dependency between power outlets
device_pp = PowerPort.objects.filter(device_id=device.id)
matching = {}
mismatch = False
for i in poweroutlets_templates:
found = False
if i.power_port_id is not None:
ppt = PowerPortTemplate.objects.get(id=i.power_port_id)
for pp in device_pp:
if pp.name == ppt.name:
# Save matching to add the correct power port later
matching[i.id] = pp.id
found = True
# If at least on power port is found there is a dependency
# Better not to sync at all
if not found:
mismatch = True
break
if not mismatch:
# Manually validating interfaces and interface templates lists
add_to_device = filter(
lambda i: i in poweroutlets_templates.values_list("id", flat=True),
map(int, filter(lambda x: x.isdigit(), request.POST.getlist("add_to_device")))
remove_from_device = filter(
lambda i: i in poweroutlets.values_list("id", flat=True),
map(int, filter(lambda x: x.isdigit(), request.POST.getlist("remove_from_device")))
)
# Add selected interfaces to the device and count them
add_to_device_component = PowerOutletTemplate.objects.filter(id__in=add_to_device)
# Remove selected interfaces from the device and count them
deleted = PowerOutlet.objects.filter(id__in=remove_from_device).delete()[0]
bulk_create = []
updated = 0
keys_to_avoid = ["id"]
# Get device power ports to check dependency between power outlets
device_pp = PowerPort.objects.filter(device_id=device.id)
for i in add_to_device_component.values():
to_create = False
matching = {}
mismatch = False
for i in poweroutlets_templates:
found = False
if i.power_port_id is not None:
ppt = PowerPortTemplate.objects.get(id=i.power_port_id)
for pp in device_pp:
if pp.name == ppt.name:
# Save matching to add the correct power port later
matching[i.id] = pp.id
found = True
try:
# If power outlets already exists, update and do not recreate
po = device.poweroutlets.get(name=i["name"])
except PowerOutlet.DoesNotExist:
po = PowerOutlet()
po.device = device
to_create = True
# If at least on power port is found there is a dependency
# Better not to sync at all
if not found:
mismatch = True
break
# Copy all fields from template
for k in i.keys():
if k not in keys_to_avoid:
setattr(po, k, i[k])
po.power_port_id = matching.get(i["id"], None)
if not mismatch:
# Manually validating interfaces and interface templates lists
add_to_device = filter(
lambda i: i in poweroutlets_templates.values_list("id", flat=True),
map(int, filter(lambda x: x.isdigit(), request.POST.getlist("add_to_device")))
)
if to_create:
bulk_create.append(po)
else:
po.save()
updated += 1
# Add selected interfaces to the device and count them
add_to_device_component = PowerOutletTemplate.objects.filter(id__in=add_to_device)
created = len(PowerOutlet.objects.bulk_create(bulk_create))
bulk_create = []
updated = 0
keys_to_avoid = ["id"]
# Getting and validating a list of interfaces to rename
fix_name_components = filter(lambda i: str(i.id) in request.POST.getlist("fix_name"), poweroutlets)
for i in add_to_device_component.values():
to_create = False
# Casting interface templates into UnifiedInterface objects for proper comparison with interfaces for renaming
unified_component_templates = [
PowerOutletComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), power_port_name=PowerPortTemplate.objects.get(id=i.power_port_id).name if i.power_port_id is not None else "", feed_leg=i.feed_leg, is_template=True) for i in poweroutlets_templates]
try:
# If power outlets already exists, update and do not recreate
po = device.poweroutlets.get(name=i["name"])
except PowerOutlet.DoesNotExist:
po = PowerOutlet()
po.device = device
to_create = True
# Rename selected interfaces
fixed = 0
for component in fix_name_components:
unified_component = [PowerOutletComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), power_port_name=PowerPort.objects.get(id=i.power_port_id).name if i.power_port_id is not None else "", feed_leg=i.feed_leg) for i in poweroutlets]
# Copy all fields from template
for k in i.keys():
if k not in keys_to_avoid:
setattr(po, k, i[k])
po.power_port_id = matching.get(i["id"], None)
try:
# Try to extract an interface template with the corresponding name
corresponding_template = unified_component_templates[unified_component_templates.index(unified_component)]
component.name = corresponding_template.name
component.save()
fixed += 1
except ValueError:
pass
else:
message.append("Dependecy detected, sync power ports first!")
if to_create:
bulk_create.append(po)
else:
po.save()
updated += 1
if created > 0:
message.append(f"created {created} interfaces")
if updated > 0:
message.append(f"updated {updated} interfaces")
if deleted > 0:
message.append(f"deleted {deleted} interfaces")
if fixed > 0:
message.append(f"fixed {fixed} interfaces")
created = len(PowerOutlet.objects.bulk_create(bulk_create))
messages.info(request, "; ".join(message).capitalize())
# Getting and validating a list of interfaces to rename
fix_name_components = filter(lambda i: str(i.id) in request.POST.getlist("fix_name"), poweroutlets)
return redirect(request.path)
# Casting interface templates into UnifiedInterface objects for proper comparison with interfaces for renaming
unified_component_templates = [
PowerOutletComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), power_port_name=PowerPortTemplate.objects.get(id=i.power_port_id).name if i.power_port_id is not None else "", feed_leg=i.feed_leg, is_template=True) for i in poweroutlets_templates]
# Rename selected interfaces
fixed = 0
for component in fix_name_components:
unified_component = [PowerOutletComparison(i.id, i.name, i.label, i.description, i.type, i.get_type_display(), power_port_name=PowerPort.objects.get(id=i.power_port_id).name if i.power_port_id is not None else "", feed_leg=i.feed_leg) for i in poweroutlets]
try:
# Try to extract an interface template with the corresponding name
corresponding_template = unified_component_templates[unified_component_templates.index(unified_component)]
component.name = corresponding_template.name
component.save()
fixed += 1
except ValueError:
pass
else:
message.append("Dependecy detected, sync power ports first!")
if created > 0:
message.append(f"created {created} interfaces")
if updated > 0:
message.append(f"updated {updated} interfaces")
if deleted > 0:
message.append(f"deleted {deleted} interfaces")
if fixed > 0:
message.append(f"fixed {fixed} interfaces")
messages.info(request, "; ".join(message).capitalize())
return redirect(request.path)
class FrontPortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
"""Comparison of interfaces between a device and a device type and beautiful visualization"""
@ -271,10 +350,10 @@ class FrontPortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View)
if form.is_valid():
device = get_object_or_404(Device.objects.filter(id=device_id))
frontports = device.frontports.all()
frontports_templates = FrontPortTemplate.objects.filter(device_type=device.device_type)
frontports = device.frontports.all()
frontports_templates = FrontPortTemplate.objects.filter(device_type=device.device_type)
return post_components(request, device, frontports, frontports_templates, FrontPort, FrontPortTemplate)
return post_components(request, device, frontports, frontports_templates, FrontPort, FrontPortTemplate)
class RearPortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
"""Comparison of interfaces between a device and a device type and beautiful visualization"""
@ -293,10 +372,10 @@ class RearPortComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
if form.is_valid():
device = get_object_or_404(Device.objects.filter(id=device_id))
rearports = device.rearports.all()
rearports_templates = RearPortTemplate.objects.filter(device_type=device.device_type)
rearports = device.rearports.all()
rearports_templates = RearPortTemplate.objects.filter(device_type=device.device_type)
return post_components(request, device, rearports, rearports_templates, RearPort, RearPortTemplate)
return post_components(request, device, rearports, rearports_templates, RearPort, RearPortTemplate)
class DeviceBayComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View):
"""Comparison of interfaces between a device and a device type and beautiful visualization"""
@ -319,7 +398,25 @@ class DeviceBayComparisonView(LoginRequiredMixin, PermissionRequiredMixin, View)
if form.is_valid():
device = get_object_or_404(Device.objects.filter(id=device_id))
devicebays = device.devicebays.all()
devicebays_templates = DeviceBayTemplate.objects.filter(device_type=device.device_type)
devicebays = device.devicebays.all()
devicebays_templates = DeviceBayTemplate.objects.filter(device_type=device.device_type)
return post_components(request, device, devicebays, devicebays_templates, DeviceBay, DeviceBayTemplate)
# Getting and validating a list of devicebays to rename
fix_name_components = filter(
lambda i: str(i.id) in request.POST.getlist("fix_name"), devicebays
)
unified_devicebay_templates = [
DeviceBayComparison(i.id, i.name, i.label, i.description, is_template=True) for i in devicebays_templates]
unified_devicebays = []
for component in fix_name_components:
unified_devicebays.append((component, DeviceBayComparison(
component.id,
component.name,
component.label,
component.description
)))
return post_components(request, device, devicebays, devicebays_templates, DeviceBay, DeviceBayTemplate, unified_devicebays, unified_devicebay_templates)