mirror of
https://github.com/peeringdb/peeringdb.git
synced 2024-05-11 05:55:09 +00:00
* install django-grainy * nsp to grainy first iteration * Fix validation error message overflow * Add migration, update views.py and template to add help_text to UI * nsp to grainy second iteration * grainy and django-grainy pinned to latest releases * deskpro ticket cc (#875) * black formatting * move ac link to bottom for ticket body * Fix typo * Update djangorestframework, peeringdb, django-ratelimit * Rewrite login view ratelimit decorator * Relock pipfile * add list() to make copy of dictionaries before iterating * respect ix-f url visibilty in ix-f conflict emails * Add type coercion to settings taken from environment variables * Add bool handling * relock pipfile with python3.9 change docker to use python3.9 * Check bool via isinstance * add ordering to admin search queryset for deskproticket and email * update settings with envvar_type option * Add tooltips to add ix and add exchange views (in org) * Add tooltip to suggest fac view * get phone information in view * add missing migration * add migration and make org a geo model * Wire normalization to put/create requests for Facility * Update admin with new address fields * Refactor serializer using mixin * Add floor and suite to address API * Write command to geonormalize existing entries * Remove unnecessary method from model * Add floor and suite to views * Add ignore geo status * Force refresh for fac and org updates * adjust frontend typo * add checking if update needs geosync * redo error handling for geosync * remove save keyword from geonormalize command script * change raw_id_fields * alternate autocomplete lookup field depending on where inline is called * remove unnecessary error handling * Add csv option * Fix bug with None vs empty string * add regex parsing for suite and floor conversion * Add migration that removes geo error as a field * add geostatus update to command * Ignore suite floor and address2 changes for api normalization * update geomodel by removing geo_error * Black models.py * Black serializers.py * remove geocode error from admin * Add function for reversing pretty speed * add conversion to export method * fix typo * fix speed value feedback after submit * remove conditional * Add error handling to create endpoint * Refine floor and suite parsing regex * Add geocoding tests * Add json for tests * IX-F Importer: Bogus output of "Preview" tool #896 * remove cruft * black formatting * IX-F Importer: history of changes per ixlan & netixlan #893 * 6 add geocode to org view * 4 update geocode without refresh * Update error display * Fix bug with formatting translated string * Add DateTimeFields to model * Add update signals * add last updated fields to views and serializers * Add last updated model migration * Add the data migration for last updated fields * add test that tests a normal org user with create org permissions * grainy to 1.7 django grainy to 1.9.1 * Fix formatting issues * Adjust var names * Refactor signals * Temporary: save override from network model * Empty vlan lists no longer cause error * typo in ixf.py * typo in admin * Typos in model verbose names * Add serializer IXLAN validation for ixf_ixp_import_enabled * Add model validation to IXLan * relock pipfile * relock pipfile * begin signal test file * Remove full clean from save in ixlan * use post_reversion_commit signal instead * remove redundant save override * remove cruft / debug code * Add signal tests * exclude organizations with city missing from commandline geosync * Skip geosync if the only address information we have is a country * initial commit for vlan matcher in importer * Add more tests and remove unused imports * update tests * Actually add vlan matching to importer * Add type checking for speed list and state * Change how we register connection.state * add bootstrap options * add rdap cache command * remove outdated perm docs * rdap from master and relock * propagate rdap settings to peeringdb.settings * add loaddata for initial fixtures * user friendly error message on RdapNotFound errors (#497) * update rdap errors * django-peeringdb to 2.5.0 and relock * rdap to 1.2.0 and relock * fix migration hierarchy * add ignore_recurse_errors option * add missing fields to mock remove cruft missed during merge * rdap to 1.2.1 * dont geo validate during api tests * fix tests * Add test file * fix merge * RDAP_SELF_BOOTSTRAP to False while running tests * black formatted * run black * add github actions * add runs on Co-authored-by: Stefan Pratter <stefan@20c.com> Co-authored-by: Elliot Frank <elliot@20c.com>
174 lines
5.7 KiB
Python
174 lines
5.7 KiB
Python
import re
|
|
|
|
from collections import defaultdict
|
|
|
|
from django.db.models import OneToOneRel, DateTimeField
|
|
from django.core.exceptions import ValidationError
|
|
from django.conf import settings
|
|
from django.db import IntegrityError
|
|
|
|
from peeringdb import resource
|
|
|
|
import peeringdb_server.models as models
|
|
|
|
from django_peeringdb.client_adaptor.backend import (
|
|
Backend as BaseBackend,
|
|
reftag_to_cls,
|
|
)
|
|
|
|
__version__ = "1.0"
|
|
|
|
|
|
class Backend(BaseBackend):
|
|
|
|
"""
|
|
Custom tailored peeringdb_server backend for the
|
|
peeringdb client we can use to sync data from
|
|
another peeringdb server instance.
|
|
|
|
We can inherit most of the official django-peeringdb
|
|
Backend, however we need bind resources to the peeringdb
|
|
models and fix some issues with validation and relationships.
|
|
"""
|
|
|
|
# map peeringdb_server models to peeringdb client resources
|
|
|
|
RESOURCE_MAP = {
|
|
resource.Facility: models.Facility,
|
|
resource.InternetExchange: models.InternetExchange,
|
|
resource.InternetExchangeFacility: models.InternetExchangeFacility,
|
|
resource.InternetExchangeLan: models.IXLan,
|
|
resource.InternetExchangeLanPrefix: models.IXLanPrefix,
|
|
resource.Network: models.Network,
|
|
resource.NetworkContact: models.NetworkContact,
|
|
resource.NetworkFacility: models.NetworkFacility,
|
|
resource.NetworkIXLan: models.NetworkIXLan,
|
|
resource.Organization: models.Organization,
|
|
}
|
|
|
|
def get_resource(self, cls):
|
|
"""
|
|
Override this so it doesn't hard fail on a non
|
|
existing resource. As sync will try to obtain resources
|
|
for relationships in peeringdb_server that aren't
|
|
really resources (sponsorships, partnerships etc.)
|
|
"""
|
|
|
|
return self.CONCRETE_MAP.get(cls)
|
|
|
|
@reftag_to_cls
|
|
def get_fields(self, concrete):
|
|
"""
|
|
Sync currently doesnt support OneToOne relationships
|
|
and none of the ones that exist in peeringdb_server
|
|
are relevant to the data we want to sync.
|
|
|
|
However they still get processed, causing errors.
|
|
|
|
Here we make sure to not process OneToOneRel relationships
|
|
"""
|
|
|
|
_fields = super().get_fields(concrete)
|
|
fields = []
|
|
for field in _fields:
|
|
if isinstance(field, OneToOneRel):
|
|
continue
|
|
fields.append(field)
|
|
return fields
|
|
|
|
def set_relation_many_to_many(self, obj, field_name, objs):
|
|
"""
|
|
Sync will try to process sponsorship_set off of `org`
|
|
and run into an error, so we make sure to ignore it
|
|
when handling many 2 many relationships during sync
|
|
"""
|
|
|
|
if field_name in ["sponsorship_set"]:
|
|
return
|
|
return super().set_relation_many_to_many(obj, field_name, objs)
|
|
|
|
def clean(self, obj):
|
|
"""
|
|
We override the object validation here to handle
|
|
common validation issues that exist in the official production
|
|
db, where valdiators are set, but data has not yet been
|
|
fixed retroactively.
|
|
|
|
These instances are:
|
|
|
|
- info_prefixes4 on networks (adjust data)
|
|
- info_prefixes6 on networks (adjust data)
|
|
- overlapping prefixes on ixlan prefixes (skip validation)
|
|
- invalid prefix length on ixlan prefixes (skip validation)
|
|
- ipaddr4 out of prefix address space on netixlans (skip validation)
|
|
- ipaddr6 out of prefix address space on netixlans (skip validation)
|
|
"""
|
|
|
|
obj.updated = (
|
|
obj._meta.get_field("updated")
|
|
.to_python(obj.updated)
|
|
.replace(tzinfo=models.UTC())
|
|
)
|
|
obj.created = (
|
|
obj._meta.get_field("created")
|
|
.to_python(obj.created)
|
|
.replace(tzinfo=models.UTC())
|
|
)
|
|
|
|
def save(self, obj):
|
|
|
|
# make sure all datetime values have their timezone set
|
|
|
|
for field in obj._meta.get_fields():
|
|
if field.get_internal_type() == "DateTimeField":
|
|
value = getattr(obj, field.name)
|
|
if not value:
|
|
continue
|
|
if isinstance(value, str):
|
|
value = field.to_python(value)
|
|
value = value.replace(tzinfo=models.UTC())
|
|
setattr(obj, field.name, value)
|
|
|
|
if obj.HandleRef.tag == "ix":
|
|
obj.save(create_ixlan=False)
|
|
else:
|
|
obj.save()
|
|
|
|
def detect_uniqueness_error(self, exc):
|
|
"""
|
|
Parse error, and if it describes any violations of a uniqueness constraint,
|
|
return the corresponding fields, else None
|
|
"""
|
|
pattern = r"(\w+) with this (\w+) already exists"
|
|
|
|
fields = []
|
|
if isinstance(exc, IntegrityError):
|
|
return self._detect_integrity_error(exc)
|
|
assert isinstance(exc, ValidationError), TypeError
|
|
|
|
error_dict = getattr(exc, "error_dict", getattr(exc, "message_dict", {}))
|
|
|
|
for name, err in error_dict.items():
|
|
if re.search(pattern, str(err)):
|
|
fields.append(name)
|
|
return fields or None
|
|
|
|
def detect_missing_relations(self, obj, exc):
|
|
"""
|
|
Parse error messages and collect the missing-relationship errors
|
|
as a dict of Resource -> {id set}
|
|
"""
|
|
missing = defaultdict(set)
|
|
|
|
error_dict = getattr(exc, "error_dict", getattr(exc, "message_dict", {}))
|
|
|
|
for name, err in error_dict.items():
|
|
# check if it was a relationship that doesnt exist locally
|
|
pattern = r".+ with id (\d+) does not exist.+"
|
|
m = re.match(pattern, str(err))
|
|
if m:
|
|
field = obj._meta.get_field(name)
|
|
res = self.get_resource(field.related_model)
|
|
missing[res].add(int(m.group(1)))
|
|
return missing
|