mirror of
https://github.com/peeringdb/peeringdb.git
synced 2024-05-11 05:55:09 +00:00
* Change label from primary ASN to ASN * Raise validation error when trying to update ASN * first steps for dotf importer procotol (#697) * migrations (#697) * Add translation to error meessage * Make ASN readonly in table * Add test now that ASN should not be able to update * Set fac.rencode to '' for all entries and make it readonly in serializer * Add unique constraints to network ixlan ip addresses * Add migration to null out duplicate ipaddresses for deleted netixlans * Add unique constraints to network ixlan ip addresses * Add migration to null out duplicate ipaddresses for deleted netixlans * remove old migrations (#697) * fix netixlan ipaddr dedupe migration (#268) add netixlan ipaddr unique constraint migration (#268) * ixf_member_data migrations (#697) * fix table name (#697) * importer protocol (#697) * fix netixlan ipaddr dedupe migration (#268) add netixlan ipaddr unique constraint migration (#268) * ixf proposed changes notifications (#697) * Delete repeated query * Add a test to show rencode is readonly * Blank out rencode when mocking data * Remove validator now that constraint exists * Add back unique field validator w Check Deleted true * conflict resolving (#697) * UniqueFieldValidator raise error with code "unique" (#268) * conflict resolution (#697) * Add fixme comment to tests * conflict resolution (#697) * Remove now invalid undelete tests * UniqueFieldValidator raise error with code "unique" (#268) * delete admin tools for duplicate ip addresses * Make migration to delete duplicateipnetworkixlan * Add ixlan-ixpfx status matching validation, add corresponding test * delete redundant checking in test * resolve conflict ui (#697) * fix migrations hierarchy * squash migrations for ixf member data * clean up preview and post-mortem tools * remove non-sensical permission check when undeleting soft-deleted objects through unique integrity error handling * only include the ix-f data url in notifications to admincom (#697) * resolve on --skip-import (#697) * ac conflict resolution (#697) * Define more accurately the incompatible statuses for ixlan and ixpfx * Add another status test * Preventing disrupting changes (#697) * fix tests (#697) * Stop allow_ixp_update from being write only and add a global stat for automated networks * Add tests for global stats that appear in footer * Change how timezone is called with datetime, to get test_stats.py/test_generate_for_current_date to pass * test for protected entities (#697) * admincom conflict resolution refine readonly fields (#697) network notifications only if the problem is actually actionable by the network (#697) * ixp / ac notifcation when ix-f source cannot be parsed (#697) fix issue with ixlan prefix protection (#697) * migrations (#697) * code documentation (#697) * ux tweaks (#697) * UX tweaks (#697) * Fix typo * fix netixlan returned in IXFMemberData.apply when adding a new one (#697) * fix import log incosistencies (#697) * Add IXFMemberData to test * Update test data * Add protocol tests * Add tests for views * always persist changes to remote data on set_conflict (#697) * More tests * always persist changes to remote data on set_conflict (#697) * suggest-add test * net_present_at_ix should check status (#697) * Add more protocol tests * Edit language of some tests * django-peeringdb to 2.1.1 relock pipfile, pin django-ratelimit to <3 as it breaks stuff * Add net_count_ixf field to ix object (#683) * Add the IX-F Member Export URL to the ixlan API endpoint (#249) * Lock some objects from being deleted by the owner (#696) * regenerate api docs (#249) * always persist changes to remote data on set_add and set_update (#697) * IXFMemberData: always persist remote data changes during set_add and set_update, also allow for saving without touching the updated field * always persist changes to remote data on set_add and set_update (#697) * Fix suggest-add tests * IXFMemberData: always persist remote data changes during set_add and set_update, also allow for saving without touching the updated field * IXFMemberData: always persist remote data changes during set_add and set_update, also allow for saving without touching the updated field * fix issue with deletion when ixfmemberdata for entry existed previously (#697) * fix test_suggest_delete_local_ixf_no_flag (#697 tests) * fix issue with deletion when ixfmemberdata for entry existed previously (#697) * invalid ips get logged and notified to the ix via notify_error (#697) * Fix more tests * issue with previous_data when running without save (#697) properly track speed errors (#697) * reset errors on ixfmemberdata that go into pending_save (#697) * add remote_data to admin view (#697) * fix error reset inconsistency (#697) * Refine invalid data tests * remove debug output * for notifications to ac include contact points for net and ix in the message (#697) * settings to toggle ix-f tickets / emails (#697) * allow turning off ix-f notifications for net and ix separately (#697) * add jsonschema test * Add idempotent tests to updater * remove old ixf member tests * Invalid data tests when ixp_updates are enabled * fix speed error validation (#697) * fix issue with rollback (#697) * fix migration hierarchy * fix ixfmemberdata _email * django-peeringdb to 2.2 and relock * add ixf rollback tests * ixf email notifications off by default * black formatted * pyupgrade Co-authored-by: egfrank <egfrank@20c.com> Co-authored-by: Stefan Pratter <stefan@20c.com>
248 lines
7.3 KiB
Python
248 lines
7.3 KiB
Python
import json
|
|
|
|
from django.db.models import Q
|
|
from django import http
|
|
from django.utils import html
|
|
from django.core.exceptions import ObjectDoesNotExist
|
|
from reversion.models import Version
|
|
from grappelli.views.related import AutocompleteLookup as GrappelliAutocomplete
|
|
from dal import autocomplete
|
|
from peeringdb_server.models import (
|
|
InternetExchange,
|
|
Facility,
|
|
NetworkFacility,
|
|
InternetExchangeFacility,
|
|
Organization,
|
|
IXLan,
|
|
CommandLineTool,
|
|
REFTAG_MAP,
|
|
)
|
|
|
|
from peeringdb_server.admin_commandline_tools import TOOL_MAP
|
|
|
|
|
|
class GrappelliHandlerefAutocomplete(GrappelliAutocomplete):
|
|
"""
|
|
makes sure that the auto-complete fields managed
|
|
by grappelli in django admin exclude soft-deleted
|
|
objects
|
|
"""
|
|
|
|
def get_queryset(self):
|
|
qs = super().get_queryset()
|
|
|
|
if hasattr(self.model, "HandleRef"):
|
|
qs = qs.exclude(status="deleted")
|
|
|
|
return qs
|
|
|
|
|
|
class AutocompleteHTMLResponse(autocomplete.Select2QuerySetView):
|
|
def has_add_permissions(self, request):
|
|
return False
|
|
|
|
def render_to_response(self, context):
|
|
q = self.request.GET.get("q", None)
|
|
return http.HttpResponse(
|
|
"".join([i.get("text") for i in self.get_results(context)]),
|
|
content_type="text/html",
|
|
)
|
|
|
|
|
|
class ExchangeAutocompleteJSON(autocomplete.Select2QuerySetView):
|
|
def get_queryset(self):
|
|
qs = InternetExchange.objects.filter(status="ok")
|
|
if self.q:
|
|
qs = qs.filter(name__icontains=self.q)
|
|
qs = qs.order_by("name")
|
|
return qs
|
|
|
|
|
|
class ExchangeAutocomplete(AutocompleteHTMLResponse):
|
|
def get_queryset(self):
|
|
qs = InternetExchange.objects.filter(status="ok")
|
|
if self.q:
|
|
qs = qs.filter(name__icontains=self.q)
|
|
qs = qs.order_by("name")
|
|
return qs
|
|
|
|
def get_result_label(self, item):
|
|
return '<span data-value="%d"><div class="main">%s</div></span>' % (
|
|
item.pk,
|
|
html.escape(item.name),
|
|
)
|
|
|
|
|
|
class FacilityAutocompleteJSON(autocomplete.Select2QuerySetView):
|
|
def get_queryset(self):
|
|
qs = Facility.objects.filter(status="ok")
|
|
if self.q:
|
|
qs = qs.filter(name__icontains=self.q)
|
|
qs = qs.order_by("name")
|
|
return qs
|
|
|
|
|
|
class FacilityAutocomplete(AutocompleteHTMLResponse):
|
|
def get_queryset(self):
|
|
qs = Facility.objects.filter(status="ok")
|
|
if self.q:
|
|
qs = qs.filter(Q(name__icontains=self.q) | Q(address1__icontains=self.q))
|
|
qs = qs.order_by("name")
|
|
return qs
|
|
|
|
def get_result_label(self, item):
|
|
return (
|
|
'<span data-value="%d"><div class="main">%s</div> <div class="sub">%s</div></span>'
|
|
% (item.pk, html.escape(item.name), html.escape(item.address1))
|
|
)
|
|
|
|
|
|
class FacilityAutocompleteForNetwork(FacilityAutocomplete):
|
|
def get_queryset(self):
|
|
qs = super().get_queryset()
|
|
net_id = self.request.resolver_match.kwargs.get("net_id")
|
|
fac_ids = [
|
|
nf.facility_id
|
|
for nf in NetworkFacility.objects.filter(status="ok", network_id=net_id)
|
|
]
|
|
qs = qs.exclude(id__in=fac_ids)
|
|
return qs
|
|
|
|
|
|
class FacilityAutocompleteForExchange(FacilityAutocomplete):
|
|
def get_queryset(self):
|
|
qs = super().get_queryset()
|
|
ix_id = self.request.resolver_match.kwargs.get("ix_id")
|
|
fac_ids = [
|
|
nf.facility_id
|
|
for nf in InternetExchangeFacility.objects.filter(status="ok", ix_id=ix_id)
|
|
]
|
|
qs = qs.exclude(id__in=fac_ids)
|
|
return qs
|
|
|
|
|
|
class OrganizationAutocomplete(AutocompleteHTMLResponse):
|
|
def get_queryset(self):
|
|
qs = Organization.objects.filter(status="ok")
|
|
if self.q:
|
|
qs = qs.filter(name__icontains=self.q)
|
|
qs = qs.order_by("name")
|
|
return qs
|
|
|
|
def get_result_label(self, item):
|
|
return '<span data-value="%d"><div class="main">%s</div></span>' % (
|
|
item.pk,
|
|
html.escape(item.name),
|
|
)
|
|
|
|
|
|
class IXLanAutocomplete(AutocompleteHTMLResponse):
|
|
def get_queryset(self):
|
|
qs = IXLan.objects.filter(status="ok").select_related("ix")
|
|
if self.q:
|
|
qs = qs.filter(
|
|
Q(ix__name__icontains=self.q) | Q(ix__name_long__icontains=self.q)
|
|
)
|
|
qs = qs.order_by("ix__name")
|
|
return qs
|
|
|
|
def get_result_label(self, item):
|
|
return (
|
|
'<span data-value="%d"><div class="main">%s <div class="tiny suffix">%s</div></div> <div class="sub">%s</div></span>'
|
|
% (
|
|
item.pk,
|
|
html.escape(item.ix.name),
|
|
html.escape(item.ix.country.code),
|
|
html.escape(item.ix.name_long),
|
|
)
|
|
)
|
|
|
|
|
|
class DeletedVersionAutocomplete(autocomplete.Select2QuerySetView):
|
|
"""
|
|
Autocomplete that will show reversion versions where an object
|
|
was set to deleted
|
|
"""
|
|
|
|
def get_queryset(self):
|
|
# Only staff needs to be able to see these
|
|
if not self.request.user.is_staff:
|
|
return []
|
|
|
|
# no query supplied, return empty result
|
|
if not self.q:
|
|
return []
|
|
|
|
try:
|
|
# query is expected to be of format "<reftag> <id>"
|
|
# return empty result on parsing failure
|
|
reftag, _id = tuple(self.q.split(" "))
|
|
except ValueError:
|
|
return []
|
|
|
|
try:
|
|
# make sure target object exists, return
|
|
# empty result if not
|
|
obj = REFTAG_MAP[reftag].objects.get(id=_id)
|
|
except (KeyError, ObjectDoesNotExist):
|
|
return []
|
|
|
|
versions = (
|
|
Version.objects.get_for_object(obj)
|
|
.order_by("revision_id")
|
|
.select_related("revision")
|
|
)
|
|
rv = []
|
|
previous = {}
|
|
|
|
# cycle through all versions of the object and collect the ones where
|
|
# status was changed from 'ok' to 'deleted'
|
|
#
|
|
# order them by most recent first
|
|
for version in versions:
|
|
data = json.loads(version.serialized_data)[0].get("fields")
|
|
|
|
if previous.get("status", "ok") == "ok" and data.get("status") == "deleted":
|
|
rv.insert(0, version)
|
|
|
|
previous = data
|
|
|
|
return rv
|
|
|
|
def get_result_label(self, item):
|
|
# label should be obj representation as well as date of deletion
|
|
# we split the date string to remove the ms and tz parts
|
|
return "{} - {}".format(item, str(item.revision.date_created).split(".")[0])
|
|
|
|
|
|
class CommandLineToolHistoryAutocomplete(autocomplete.Select2QuerySetView):
|
|
"""
|
|
Autocomplete for command line tools that were ran via the admin ui
|
|
"""
|
|
|
|
tool = ""
|
|
|
|
def get_queryset(self):
|
|
# Only staff needs to be able to see these
|
|
if not self.request.user.is_staff:
|
|
return []
|
|
qs = CommandLineTool.objects.filter(tool=self.tool).order_by("-created")
|
|
if self.q:
|
|
qs = qs.filter(description__icontains=self.q)
|
|
return qs
|
|
|
|
def get_result_label(self, item):
|
|
return item.description or self.tool
|
|
|
|
|
|
clt_history = {}
|
|
# class for each command line tool wrapper that we will map to an auto-complete
|
|
# url in urls.py
|
|
for tool_id, tool in list(TOOL_MAP.items()):
|
|
|
|
class ToolHistory(CommandLineToolHistoryAutocomplete):
|
|
tool = tool_id
|
|
|
|
ToolHistory.__name__ = f"CLT_{tool_id}_Autocomplete"
|
|
clt_history[tool_id] = ToolHistory
|