2018-11-08 19:45:21 +00:00
import importlib
2019-05-02 15:20:20 +00:00
2020-01-08 13:29:58 -06:00
import re
import traceback
import time
import datetime
2019-05-02 15:20:20 +00:00
import unidecode
2019-12-05 16:57:52 +00:00
from rest_framework import routers , serializers , status , viewsets
2018-11-08 19:45:21 +00:00
from rest_framework . response import Response
from rest_framework . views import exception_handler
from rest_framework . exceptions import ValidationError as RestValidationError
from django . apps import apps
from django . conf import settings
from django . core . exceptions import FieldError , ValidationError , ObjectDoesNotExist
from django . db import connection
from django . utils import timezone
from django . db . models import DateTimeField
import django_namespace_perms . rest as nsp_rest
2020-01-08 13:29:58 -06:00
import reversion
from peeringdb_server . models import Network , UTC
2018-11-08 19:45:21 +00:00
from peeringdb_server . serializers import ParentStatusException
from peeringdb_server . api_cache import CacheRedirect , APICacheLoader
import django_namespace_perms . util as nsp
from django_namespace_perms . exceptions import *
###############################################################################
class RestRouter ( routers . DefaultRouter ) :
schema_title = " PeeringDB API "
schema_url = " "
schema_renderers = None
routes = [
# List route.
2019-12-05 16:57:52 +00:00
routers . Route (
url = r " ^ {prefix} {trailing_slash} $ " ,
mapping = { " get " : " list " , " post " : " create " } ,
name = " {basename} -list " ,
2020-01-08 13:29:58 -06:00
detail = False ,
2019-12-05 16:57:52 +00:00
initkwargs = { " suffix " : " List " } ,
) ,
2018-11-08 19:45:21 +00:00
# Detail route.
routers . Route (
2019-12-05 16:57:52 +00:00
url = r " ^ {prefix} / {lookup} {trailing_slash} $ " ,
mapping = {
" get " : " retrieve " ,
" put " : " update " ,
" patch " : " partial_update " ,
" delete " : " destroy " ,
} ,
name = " {basename} -detail " ,
2020-01-08 13:29:58 -06:00
detail = True ,
2019-12-05 16:57:52 +00:00
initkwargs = { " suffix " : " Instance " } ,
) ,
2020-01-08 13:29:58 -06:00
routers . DynamicRoute (
2019-12-05 16:57:52 +00:00
url = r " ^ {prefix} / {lookup} / {methodnamehyphen} $ " ,
name = " {basename} - {methodnamehyphen} " ,
2020-01-08 13:29:58 -06:00
detail = True ,
2019-12-05 16:57:52 +00:00
initkwargs = { } ,
) ,
2018-11-08 19:45:21 +00:00
# Dynamically generated routes.
# Generated using @action or @link decorators on methods of the
# viewset.
2019-12-05 16:57:52 +00:00
routers . Route (
url = r " ^ {prefix} / {lookup} / {methodname} {trailing_slash} $ " ,
mapping = { " {httpmethod} " : " {methodname} " , } ,
name = " {basename} - {methodnamehyphen} " ,
2020-01-08 13:29:58 -06:00
detail = False ,
2019-12-05 16:57:52 +00:00
initkwargs = { } ,
) ,
2018-11-08 19:45:21 +00:00
]
def __init__ ( self , trailing_slash = False ) :
2019-12-05 16:57:52 +00:00
self . trailing_slash = trailing_slash and " / " or " "
2018-11-08 19:45:21 +00:00
super ( routers . DefaultRouter , self ) . __init__ ( trailing_slash = False )
###############################################################################
def pdb_exception_handler ( exc ) :
2020-01-08 13:29:58 -06:00
print ( traceback . format_exc ( ) )
2018-11-08 19:45:21 +00:00
return exception_handler ( exc )
2019-01-11 11:24:00 +00:00
class client_check ( object ) :
"""
decorator that can be attached to rest viewset responses and will
generate an error response if the requesting peeringdb client
is running a client or backend version that is incompatible with
the server
compatibilty is controlled via facsimile during deploy and can
be configured in env . misc . api . compat
"""
def __init__ ( self ) :
self . min_version = settings . CLIENT_COMPAT . get ( " client " ) . get ( " min " )
self . max_version = settings . CLIENT_COMPAT . get ( " client " ) . get ( " max " )
self . backends = settings . CLIENT_COMPAT . get ( " backends " , { } )
def __call__ ( self , fn ) :
compat_check = self . compat_check
def wrapped ( self , request , * args , * * kwargs ) :
try :
compat_check ( request )
except ValueError as exc :
2019-12-05 16:57:52 +00:00
return Response (
status = status . HTTP_400_BAD_REQUEST , data = { " detail " : str ( exc ) }
)
2019-01-11 11:24:00 +00:00
return fn ( self , request , * args , * * kwargs )
return wrapped
def version_tuple ( self , str_version ) :
""" take a semantic version string and turn into a tuple """
return tuple ( [ int ( i ) for i in str_version . split ( " . " ) ] )
def version_pad ( self , version ) :
""" take a semantic version tuple and zero pad to dev version """
while len ( version ) < 4 :
2019-12-05 16:57:52 +00:00
version = version + ( 0 , )
2019-01-11 11:24:00 +00:00
return version
def version_string ( self , version ) :
""" take a semantic version tuple and turn into a " . " delimited string """
return " . " . join ( [ str ( i ) for i in version ] )
def backend_min_version ( self , backend ) :
""" return the min supported version for the specified backend """
return self . backends . get ( backend , { } ) . get ( " min " )
def backend_max_version ( self , backend ) :
""" return the max supported version for the specified backend """
return self . backends . get ( backend , { } ) . get ( " max " )
def client_info ( self , request ) :
"""
parse the useragent in the request and return client version
info if possible .
any connecting client that is NOT the peeringdb client will currently
return an empty dict and not compatibility checking will be done
"""
# if no user agent was specified in headers we bail
try :
agent = request . META [ " HTTP_USER_AGENT " ]
except KeyError :
return { }
# check if connecting client is peeringdb-py client and
# if it parse
# - the client version
# - backend name
# - backend version
2020-01-08 13:29:58 -06:00
m = re . match ( r " PeeringDB/([ \ d \ .]+) ( \ S+)/([ \ d \ .]+) " , agent )
2019-01-11 11:24:00 +00:00
if m :
return {
" client " : self . version_tuple ( m . group ( 1 ) ) ,
" backend " : {
" name " : m . group ( 2 ) ,
2019-12-05 16:57:52 +00:00
" version " : self . version_tuple ( m . group ( 3 ) ) ,
} ,
2019-01-11 11:24:00 +00:00
}
return { }
def compat_check ( self , request ) :
"""
Check if the connecting client is compatible with the api
This is currently only sensible when the request is made through
the official peeringdb - py client , any other client will be
passed through without checks
On incompatibility a ValueError is raised
"""
info = self . client_info ( request )
compat = True
if info :
backend = info [ " backend " ] [ " name " ]
if backend not in self . backends :
return
backend_min = self . backend_min_version ( backend )
backend_max = self . backend_max_version ( backend )
client_version = info . get ( " client " )
backend_version = info . get ( " backend " ) . get ( " version " )
2019-12-05 16:57:52 +00:00
if self . version_pad ( self . min_version ) > self . version_pad ( client_version ) :
2019-01-11 11:24:00 +00:00
# client version is too low
compat = False
2019-12-05 16:57:52 +00:00
elif self . version_pad ( self . max_version ) < self . version_pad ( client_version ) :
2019-01-11 11:24:00 +00:00
# client version is too high
compat = False
2019-12-05 16:57:52 +00:00
if self . version_pad ( backend_min ) > self . version_pad ( backend_version ) :
2019-01-11 11:24:00 +00:00
# client backend version is too low
compat = False
2019-12-05 16:57:52 +00:00
elif self . version_pad ( backend_max ) < self . version_pad ( backend_version ) :
2019-01-11 11:24:00 +00:00
# client backend version is too high
compat = False
if not compat :
raise ValueError (
2019-12-05 16:57:52 +00:00
" Your client version is incompatible with server version of the api, please install peeringdb>= {} ,<= {} {} >= {} ,<= {} " . format (
2019-01-11 11:24:00 +00:00
self . version_string ( self . min_version ) ,
2019-12-05 16:57:52 +00:00
self . version_string ( self . max_version ) ,
backend ,
2019-01-11 11:24:00 +00:00
self . version_string ( backend_min ) ,
2019-12-05 16:57:52 +00:00
self . version_string ( backend_max ) ,
)
)
2019-01-11 11:24:00 +00:00
2018-11-08 19:45:21 +00:00
###############################################################################
# VIEW SETS
class ModelViewSet ( viewsets . ModelViewSet ) :
"""
Generic ModelViewSet Base Class
This should probably be moved to a common lib ?
Ueaj
"""
2019-12-05 16:57:52 +00:00
paginate_by_param = ( " limit " , )
2018-11-08 19:45:21 +00:00
# use django namespace permissions backend, this is also specified in the
# settings but for some reason it only works when explicitly set here,
# need to investigate
2019-12-05 16:57:52 +00:00
permission_classes = ( nsp_rest . BasePermission , )
2018-11-08 19:45:21 +00:00
def get_queryset ( self ) :
"""
Prepare the queryset
"""
qset = self . model . handleref . all ( )
self . request . meta_response = { }
if hasattr ( self . serializer_class , " prepare_query " ) :
try :
qset , p_filters = self . serializer_class . prepare_query (
2019-12-05 16:57:52 +00:00
qset , * * self . request . query_params
)
2020-01-08 13:29:58 -06:00
except ValidationError as inst :
2018-11-08 19:45:21 +00:00
raise RestValidationError ( { " detail " : str ( inst [ 0 ] ) } )
2020-01-08 13:29:58 -06:00
except ValueError as inst :
2018-11-08 19:45:21 +00:00
raise RestValidationError ( { " detail " : str ( inst [ 0 ] ) } )
2020-01-08 13:29:58 -06:00
except TypeError as inst :
2018-11-08 19:45:21 +00:00
raise RestValidationError ( { " detail " : str ( inst [ 0 ] ) } )
2020-01-08 13:29:58 -06:00
except FieldError as inst :
2018-11-08 19:45:21 +00:00
raise RestValidationError ( { " detail " : " Invalid query " } )
else :
p_filters = { }
try :
2019-12-05 16:57:52 +00:00
since = int ( float ( self . request . query_params . get ( " since " , 0 ) ) )
2018-11-08 19:45:21 +00:00
except ValueError :
2019-12-05 16:57:52 +00:00
raise RestValidationError (
{ " detail " : " ' since ' needs to be a unix timestamp (epoch seconds) " }
)
2018-11-08 19:45:21 +00:00
try :
2019-12-05 16:57:52 +00:00
skip = int ( self . request . query_params . get ( " skip " , 0 ) )
2018-11-08 19:45:21 +00:00
except ValueError :
2019-12-05 16:57:52 +00:00
raise RestValidationError ( { " detail " : " ' skip ' needs to be a number " } )
2018-11-08 19:45:21 +00:00
try :
2019-12-05 16:57:52 +00:00
limit = int ( self . request . query_params . get ( " limit " , 0 ) )
2018-11-08 19:45:21 +00:00
except ValueError :
2019-12-05 16:57:52 +00:00
raise RestValidationError ( { " detail " : " ' limit ' needs to be a number " } )
2018-11-08 19:45:21 +00:00
try :
depth = int ( self . request . query_params . get ( " depth " , 0 ) )
except ValueError :
2019-12-05 16:57:52 +00:00
raise RestValidationError ( { " detail " : " ' depth ' needs to be a number " } )
2018-11-08 19:45:21 +00:00
2019-12-05 16:57:52 +00:00
field_names = [
fld . name for fld in self . model . _meta . get_fields ( )
] + self . serializer_class . queryable_relations ( )
2018-11-08 19:45:21 +00:00
date_fields = [ " DateTimeField " , " DateField " ]
# filters
filters = { }
2020-01-08 13:29:58 -06:00
for k , v in list ( self . request . query_params . items ( ) ) :
2018-11-08 19:45:21 +00:00
2019-05-02 15:20:20 +00:00
v = unidecode . unidecode ( v )
2018-11-08 19:45:21 +00:00
if k [ - 3 : ] == " _id " and k not in field_names :
k = k [ : - 3 ]
xl = self . serializer_class . queryable_field_xl
# only apply filter if the field actually exists and uses a
# valid suffix
m = re . match ( " ^(.+)__(lt|lte|gt|gte|contains|startswith|in)$ " , k )
# run queryable field translation
# on the targeted field so that the filter is actually run on
# a field that django orm is aware of - which in most cases is
# identical to the serializer field anyways, but in some cases it
# may need to be substituted
if m :
flt = xl ( m . group ( 1 ) )
k = k . replace ( m . group ( 1 ) , flt , 1 )
if flt [ - 3 : ] == " _id " and flt not in field_names :
flt = flt [ : - 3 ]
else :
k = xl ( k )
flt = None
# prepare db filters
if m and flt in field_names :
# filter by function provided in suffix
try :
intyp = self . model . _meta . get_field ( flt ) . get_internal_type ( )
except :
intyp = " CharField "
# for greater than date checks we want to force the time to 1
# msecond before midnight
if intyp in date_fields :
if m . group ( 2 ) in [ " gt " , " lte " ] :
if len ( v ) == 10 :
v = " %s 23:59:59.999 " % v
# convert to datetime and make tz aware
try :
v = DateTimeField ( ) . to_python ( v )
2020-01-08 13:29:58 -06:00
except ValidationError as inst :
2018-11-08 19:45:21 +00:00
raise RestValidationError ( { " detail " : str ( inst [ 0 ] ) } )
if timezone . is_naive ( v ) :
v = timezone . make_aware ( v )
if " _ctf " in self . request . query_params :
2019-12-05 16:57:52 +00:00
self . request . _ctf = { " %s __ %s " % ( m . group ( 1 ) , m . group ( 2 ) ) : v }
2018-11-08 19:45:21 +00:00
# contains should become icontains because we always
# want it to do case-insensitive checks
if m . group ( 2 ) == " contains " :
filters [ " %s __icontains " % flt ] = v
elif m . group ( 2 ) == " startswith " :
filters [ " %s __istartswith " % flt ] = v
# when the 'in' filters is found attempt to split the
# provided search value into a list
elif m . group ( 2 ) == " in " :
filters [ k ] = v . split ( " , " )
else :
filters [ k ] = v
elif k in field_names :
# filter exact matches
try :
intyp = self . model . _meta . get_field ( k ) . get_internal_type ( )
except :
intyp = " CharField "
if intyp == " ForeignKey " :
filters [ " %s _id " % k ] = v
elif intyp == " DateTimeField " or intyp == " DateField " :
filters [ " %s __startswith " % k ] = v
else :
filters [ " %s __iexact " % k ] = v
if filters :
try :
qset = qset . filter ( * * filters )
2020-01-08 13:29:58 -06:00
except ValidationError as inst :
2018-11-08 19:45:21 +00:00
raise RestValidationError ( { " detail " : str ( inst [ 0 ] ) } )
2020-01-08 13:29:58 -06:00
except ValueError as inst :
2018-11-08 19:45:21 +00:00
raise RestValidationError ( { " detail " : str ( inst [ 0 ] ) } )
2020-01-08 13:29:58 -06:00
except TypeError as inst :
2018-11-08 19:45:21 +00:00
raise RestValidationError ( { " detail " : str ( inst [ 0 ] ) } )
2020-01-08 13:29:58 -06:00
except FieldError as inst :
2018-11-08 19:45:21 +00:00
raise RestValidationError ( { " detail " : " Invalid query " } )
# check if request qualifies for a cache load
filters . update ( p_filters )
api_cache = APICacheLoader ( self , qset , filters )
if api_cache . qualifies ( ) :
raise CacheRedirect ( api_cache )
if not self . kwargs :
if since > 0 :
# .filter(status__in=["ok","deleted"])
2019-12-05 16:57:52 +00:00
qset = (
2020-01-08 13:29:58 -06:00
qset . since (
timestamp = datetime . datetime . fromtimestamp ( since ) . replace (
tzinfo = UTC ( )
) ,
deleted = True ,
)
2019-12-05 16:57:52 +00:00
. order_by ( " updated " )
. filter ( status__in = [ " ok " , " deleted " ] )
)
2018-11-08 19:45:21 +00:00
else :
qset = qset . filter ( status = " ok " )
else :
qset = qset . filter ( status__in = [ " ok " , " pending " ] )
if not self . kwargs :
if limit > 0 :
2019-12-05 16:57:52 +00:00
qset = qset [ skip : skip + limit ]
2018-11-08 19:45:21 +00:00
else :
qset = qset [ skip : ]
adrl = getattr ( settings , " API_DEPTH_ROW_LIMIT " , 250 )
row_count = qset . count ( )
if adrl and depth > 0 and row_count > adrl :
qset = qset [ : adrl ]
2019-12-05 16:57:52 +00:00
self . request . meta_response [ " truncated " ] = (
" Your search query (with depth %d ) returned more than %d rows and has been truncated. Please be more specific in your filters, use the limit and skip parameters to page through the resultset or drop the depth parameter "
% ( depth , adrl )
)
2018-11-08 19:45:21 +00:00
if depth > 0 or self . kwargs :
return self . serializer_class . prefetch_related (
2019-12-05 16:57:52 +00:00
qset , self . request , is_list = ( len ( self . kwargs ) == 0 )
)
2018-11-08 19:45:21 +00:00
else :
return qset
2019-01-11 11:24:00 +00:00
@client_check ( )
2018-11-08 19:45:21 +00:00
def list ( self , request , * args , * * kwargs ) :
t = time . time ( )
try :
r = super ( ModelViewSet , self ) . list ( request , * args , * * kwargs )
2020-01-08 13:29:58 -06:00
except ValueError as inst :
2019-12-05 16:57:52 +00:00
return Response (
status = status . HTTP_400_BAD_REQUEST , data = { " detail " : str ( inst ) }
)
2020-01-08 13:29:58 -06:00
except TypeError as inst :
2019-12-05 16:57:52 +00:00
return Response (
status = status . HTTP_400_BAD_REQUEST , data = { " detail " : str ( inst ) }
)
2020-01-08 13:29:58 -06:00
except CacheRedirect as inst :
2018-11-08 19:45:21 +00:00
r = Response ( status = 200 , data = inst . loader . load ( ) )
d = time . time ( ) - t
2019-12-05 16:57:52 +00:00
# FIXME: this waits for peeringdb-py fix to deal with 404 raise properly
2018-11-08 19:45:21 +00:00
if not r or not len ( r . data ) :
if self . serializer_class . is_unique_query ( request ) :
2019-01-11 11:24:00 +00:00
return Response (
2019-12-05 16:57:52 +00:00
status = 404 , data = { " data " : [ ] , " detail " : " Entity not found " }
)
2018-11-08 19:45:21 +00:00
return r
2019-01-11 11:24:00 +00:00
@client_check ( )
2018-11-08 19:45:21 +00:00
def retrieve ( self , request , * args , * * kwargs ) :
# could add fk relationships here, one at a time, but we need to define
# them somewhere by the time we get the serializer, the data is already
# populated
t = time . time ( )
r = super ( ModelViewSet , self ) . retrieve ( request , * args , * * kwargs )
d = time . time ( ) - t
2020-01-08 13:29:58 -06:00
print ( " done in %.5f seconds, %d queries " % ( d , len ( connection . queries ) ) )
2018-11-08 19:45:21 +00:00
return r
2019-01-11 11:24:00 +00:00
@client_check ( )
2018-11-08 19:45:21 +00:00
def create ( self , request , * args , * * kwargs ) :
"""
Create object
"""
try :
with reversion . create_revision ( ) :
if request . user :
reversion . set_user ( request . user )
2019-12-05 16:57:52 +00:00
return super ( ModelViewSet , self ) . create ( request , * args , * * kwargs )
2020-01-08 13:29:58 -06:00
except PermissionDenied as inst :
2018-11-08 19:45:21 +00:00
return Response ( status = status . HTTP_403_FORBIDDEN )
2020-01-08 13:29:58 -06:00
except ParentStatusException as inst :
2019-12-05 16:57:52 +00:00
return Response (
status = status . HTTP_400_BAD_REQUEST , data = { " detail " : str ( inst ) }
)
2018-11-08 19:45:21 +00:00
finally :
self . get_serializer ( ) . finalize_create ( request )
2019-01-11 11:24:00 +00:00
@client_check ( )
2018-11-08 19:45:21 +00:00
def update ( self , request , * args , * * kwargs ) :
"""
Update object
"""
try :
with reversion . create_revision ( ) :
if request . user :
reversion . set_user ( request . user )
2019-12-05 16:57:52 +00:00
return super ( ModelViewSet , self ) . update ( request , * args , * * kwargs )
2020-01-08 13:29:58 -06:00
except TypeError as inst :
2019-12-05 16:57:52 +00:00
return Response (
status = status . HTTP_400_BAD_REQUEST , data = { " detail " : str ( inst ) }
)
2020-01-08 13:29:58 -06:00
except ValueError as inst :
2019-12-05 16:57:52 +00:00
return Response (
status = status . HTTP_400_BAD_REQUEST , data = { " detail " : str ( inst ) }
)
2018-11-08 19:45:21 +00:00
finally :
self . get_serializer ( ) . finalize_update ( request )
def partial_update ( self , request , * args , * * kwargs ) :
"""
PATCH ( partial update ) is currently disabled
"""
return Response ( status = status . HTTP_403_FORBIDDEN )
2019-01-11 11:24:00 +00:00
@client_check ( )
2018-11-08 19:45:21 +00:00
def destroy ( self , request , pk , format = None ) :
"""
Delete object
"""
try :
try :
obj = self . model . objects . get ( pk = pk )
except ValueError :
2019-12-05 16:57:52 +00:00
return Response (
status = status . HTTP_400_BAD_REQUEST , data = { " extra " : " Invalid id " }
)
2018-11-08 19:45:21 +00:00
except self . model . DoesNotExist :
return Response ( status = status . HTTP_204_NO_CONTENT )
if nsp . has_perms ( request . user , obj , " delete " ) :
with reversion . create_revision ( ) :
if request . user :
reversion . set_user ( request . user )
obj . delete ( )
return Response ( status = status . HTTP_204_NO_CONTENT )
else :
return Response ( status = status . HTTP_403_FORBIDDEN )
finally :
self . get_serializer ( ) . finalize_delete ( request )
2019-12-05 16:57:52 +00:00
pdb_serializers = importlib . import_module ( " peeringdb_server.serializers " )
2018-11-08 19:45:21 +00:00
router = RestRouter ( trailing_slash = False )
# router helpers
def ref_dict ( ) :
return { tag : view . model for tag , view , na in router . registry }
2020-02-05 21:25:25 -06:00
def model_view_set ( model , methods = None ) :
2018-11-08 19:45:21 +00:00
"""
shortcut for peeringdb models to generate viewset and register in the API urls
"""
# lookup Serializer class
2019-12-05 16:57:52 +00:00
scls = getattr ( pdb_serializers , model + " Serializer " )
2018-11-08 19:45:21 +00:00
2019-12-05 16:57:52 +00:00
model_t = apps . get_model ( " peeringdb_server " , model )
2018-11-08 19:45:21 +00:00
# setup class attributes
clsdict = {
2019-12-05 16:57:52 +00:00
" model " : model_t ,
" serializer_class " : scls ,
2020-01-08 13:29:58 -06:00
# TODO: use coreapi schemas to build parameter docs
# for retrieve and list
" __doc__ " : (
" Rest API endpoint for "
+ model
+ " \n \n list: \n "
+ settings . API_DOC_STR [ " list " ]
+ " \n \n retrieve: \n "
+ settings . API_DOC_STR [ " retrieve " ]
+ " \n \n create: \n "
+ settings . API_DOC_STR [ " create " ]
+ " \n \n update: \n "
+ settings . API_DOC_STR [ " update " ]
+ " \n \n delete: \n "
+ settings . API_DOC_STR [ " delete " ]
) ,
2018-11-08 19:45:21 +00:00
}
# create the type
2019-12-05 16:57:52 +00:00
viewset_t = type ( model + " ViewSet " , ( ModelViewSet , ) , clsdict )
2018-11-08 19:45:21 +00:00
2020-02-05 21:25:25 -06:00
if methods :
viewset_t . http_method_names = methods
2018-11-08 19:45:21 +00:00
# register with the rest router for incoming requests
ref_tag = model_t . handleref . tag
2020-01-08 13:29:58 -06:00
router . register ( ref_tag , viewset_t , basename = ref_tag )
2018-11-08 19:45:21 +00:00
return viewset_t
2019-12-05 16:57:52 +00:00
FacilityViewSet = model_view_set ( " Facility " )
InternetExchangeViewSet = model_view_set ( " InternetExchange " )
InternetExchangeFacilityViewSet = model_view_set ( " InternetExchangeFacility " )
2020-02-05 21:25:25 -06:00
IXLanViewSet = model_view_set ( " IXLan " , methods = [ " get " , " put " ] )
2019-12-05 16:57:52 +00:00
IXLanPrefixViewSet = model_view_set ( " IXLanPrefix " )
NetworkViewSet = model_view_set ( " Network " )
NetworkContactViewSet = model_view_set ( " NetworkContact " )
NetworkFacilityViewSet = model_view_set ( " NetworkFacility " )
NetworkIXLanViewSet = model_view_set ( " NetworkIXLan " )
OrganizationViewSet = model_view_set ( " Organization " )
2018-11-08 19:45:21 +00:00
2019-05-02 18:13:03 +00:00
class ReadOnlyMixin ( object ) :
2018-11-08 19:45:21 +00:00
def destroy ( self , request , pk , format = None ) :
"""
2019-05-02 18:13:03 +00:00
This endpoint is readonly
2018-11-08 19:45:21 +00:00
"""
return Response ( status = status . HTTP_405_METHOD_NOT_ALLOWED )
def create ( self , request , * args , * * kwargs ) :
"""
2019-05-02 18:13:03 +00:00
This endpoint is readonly
2018-11-08 19:45:21 +00:00
"""
return Response ( status = status . HTTP_405_METHOD_NOT_ALLOWED )
def update ( self , request , * args , * * kwargs ) :
"""
2019-05-02 18:13:03 +00:00
This endpoint is readonly
2018-11-08 19:45:21 +00:00
"""
return Response ( status = status . HTTP_405_METHOD_NOT_ALLOWED )
2019-05-02 18:13:03 +00:00
def patch ( self , request , * args , * * kwargs ) :
"""
This endpoint is readonly
"""
return Response ( status = status . HTTP_405_METHOD_NOT_ALLOWED )
class ASSetViewSet ( ReadOnlyMixin , viewsets . ModelViewSet ) :
"""
AS - SET endpoint
lists all as sets mapped by asn
"""
lookup_field = " asn "
http_method_names = [ " get " ]
model = Network
def get_queryset ( self ) :
return Network . objects . filter ( status = " ok " ) . exclude ( irr_as_set = " " )
def list ( self , request ) :
return Response ( Network . as_set_map ( self . get_queryset ( ) ) )
def retrieve ( self , request , asn ) :
try :
network = Network . objects . get ( asn = int ( asn ) )
except ValueError :
2019-12-05 16:57:52 +00:00
return Response (
status = status . HTTP_400_BAD_REQUEST , data = { " detail " : " Invalid ASN " }
)
2019-05-02 18:13:03 +00:00
except ObjectDoesNotExist :
return Response ( status = status . HTTP_404_NOT_FOUND )
2019-12-05 16:57:52 +00:00
return Response ( { network . asn : network . irr_as_set } )
2019-05-02 18:13:03 +00:00
2020-01-08 13:29:58 -06:00
router . register ( " as_set " , ASSetViewSet , basename = " as_set " )
2018-11-08 19:45:21 +00:00
# set here in case we want to add more urls later
urls = router . urls
2019-12-05 16:57:52 +00:00
REFTAG_MAP = dict (
[
( cls . model . handleref . tag , cls )
for cls in [
OrganizationViewSet ,
NetworkViewSet ,
FacilityViewSet ,
InternetExchangeViewSet ,
InternetExchangeFacilityViewSet ,
NetworkFacilityViewSet ,
NetworkIXLanViewSet ,
NetworkContactViewSet ,
IXLanViewSet ,
IXLanPrefixViewSet ,
]
]
)