1
0
mirror of https://github.com/peeringdb/peeringdb.git synced 2024-05-11 05:55:09 +00:00
Files
peeringdb-peeringdb/peeringdb_server/export_views.py
Matt Griswold ba6f9b6432 Qu1003 (#621)
* use new peeringdb client (1.0.0) for pdb_load_data sync (#599)

* drop django-mobi for lack of py3/dj2 support (#492)
remove django-forms-bootstrap for lack of py3/dj2 support (#492)

* black formatted

* django2.2 and py3 upgrade (#492)

* drop ixlans (#21) ui and api changes

* drop local_asn (#168)

* org search (#193)

* phone number validation (#50)

* implement help text tooltips (#228)

* Mark own ASN as transit-free (#394)

* py3 fix for `pdb_migrate_ixlans` command when writing migration report

* pdb_migrate_ixlans: properly handle py3 Runtime error if ixlan dict changes during iteration

* set rest DEFAULT_SCHEMA_CLASS to coreapi to fix swagger apidocs
fix migration 0027 missing from facsimile manifest

* fix swagger doc strings

* fix tests that were broken from api doc fixes

* fix UniqueFieldValidator for netixlan ipaddress validation that broke during django/drf upgrade

* fix org merge tool layout issues

* travis config

* update pipfile and lock

* black formatting

* update travis dist

* beta mode banner (#411)

* add beta banner template (#411)

* automatically scheduled sync may not always be on, add a flag that lets us reflect that state in the beta banner message
clean up beta banner implementation (#411)

* add tests for beta banner (#411)
2020-01-08 13:29:58 -06:00

401 lines
12 KiB
Python

import json
import datetime
import urllib.request, urllib.parse, urllib.error
import csv
import io
import collections
from django.http import JsonResponse, HttpResponse
from django.views import View
from django.utils.translation import ugettext_lazy as _
from rest_framework.test import APIRequestFactory
from peeringdb_server.models import IXLan, NetworkIXLan, InternetExchange
from peeringdb_server.rest import REFTAG_MAP as RestViewSets
from peeringdb_server.renderers import JSONEncoder
def export_ixf_ix_members(ixlans, pretty=False):
member_list = []
ixp_list = []
for ixlan in ixlans:
if ixlan.ix not in ixp_list:
ixp_list.append(ixlan.ix)
rv = {
"version": "0.6",
"timestamp": datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ"),
"member_list": member_list,
"ixp_list": [{"ixp_id": ixp.id, "shortname": ixp.name} for ixp in ixp_list],
}
for ixlan in ixlans:
asns = []
for netixlan in ixlan.netixlan_set_active.all():
if netixlan.asn in asns:
continue
connection_list = []
member = {
"asnum": netixlan.asn,
"member_type": "peering",
"name": netixlan.network.name,
"url": netixlan.network.website,
"contact_email": [
poc.email
for poc in netixlan.network.poc_set_active.filter(visible="Public")
],
"contact_phone": [
poc.phone
for poc in netixlan.network.poc_set_active.filter(visible="Public")
],
"peering_policy": netixlan.network.policy_general.lower(),
"peering_policy_url": netixlan.network.policy_url,
"connection_list": connection_list,
}
member_list.append(member)
asns.append(netixlan.asn)
for _netixlan in ixlan.netixlan_set_active.filter(asn=netixlan.asn):
vlan_list = [{}]
connection = {
"ixp_id": _netixlan.ixlan.ix_id,
"state": "active",
"if_list": [{"if_speed": _netixlan.speed}],
"vlan_list": vlan_list,
}
connection_list.append(connection)
if _netixlan.ipaddr4:
vlan_list[0]["ipv4"] = {
"address": "{}".format(_netixlan.ipaddr4),
"routeserver": _netixlan.is_rs_peer,
"max_prefix": _netixlan.network.info_prefixes4,
"as_macro": _netixlan.network.irr_as_set,
}
if _netixlan.ipaddr6:
vlan_list[0]["ipv6"] = {
"address": "{}".format(_netixlan.ipaddr6),
"routeserver": _netixlan.is_rs_peer,
"max_prefix": _netixlan.network.info_prefixes6,
"as_macro": _netixlan.network.irr_as_set,
}
if pretty:
return json.dumps(rv, indent=2)
else:
return json.dumps(rv)
def view_export_ixf_ix_members(request, ix_id):
return HttpResponse(
export_ixf_ix_members(
IXLan.objects.filter(ix_id=ix_id, status="ok"),
pretty="pretty" in request.GET,
),
content_type="application/json",
)
def view_export_ixf_ixlan_members(request, ixlan_id):
return HttpResponse(
export_ixf_ix_members(
IXLan.objects.filter(id=ixlan_id, status="ok"),
pretty="pretty" in request.GET,
),
content_type="application/json",
)
class ExportView(View):
"""
Base class for more complex data exports
"""
# supported export fortmats
formats = ["json", "json_pretty", "csv"]
# when exporting json data, if this is it not None
# json data will be wrapped in one additional dict
# and referenced at a key with the specified name
json_root_key = "data"
# exporting data should send file attachment headers
download = True
# if download=True this value will be used to specify
# the filename of the downloaded file
download_name = "export.{extension}"
# format to file extension translation table
extensions = {"csv": "csv", "json": "json", "json_pretty": "json"}
def get(self, request, fmt):
fmt = fmt.replace("-", "_")
if fmt not in self.formats:
raise ValueError(_("Invalid export format"))
try:
response_handler = getattr(self, "response_{}".format(fmt))
response = response_handler(self.generate(request))
if self.download == True:
# send attachment header, triggering download on the client side
filename = self.download_name.format(extension=self.extensions.get(fmt))
response["Content-Disposition"] = 'attachment; filename="{}"'.format(
filename
)
return response
except Exception as exc:
return JsonResponse({"non_field_errors": [str(exc)]}, status=400)
def generate(self, request):
"""
Function that generates export data from request
Override this
"""
return {}
def response_json(self, data):
"""
Return Response object for normal json response
Arguments:
- data <list|dict>: serializable data, if list is passed you will need
to specify a value in self.json_root_key
Returns:
- JsonResponse
"""
if self.json_root_key:
data = {self.json_root_key: data}
return JsonResponse(data, encoder=JSONEncoder)
def response_json_pretty(self, data):
"""
Returns Response object for pretty (indented) json response
Arguments:
- data <list|dict>: serializable data, if list is passed tou will need
to specify a value in self.json_root_key
Returns:
- HttpResponse: http response with appropriate json headers, cannot use
JsonResponse here because we need to specify indent level
"""
if self.json_root_key:
data = {self.json_root_key: data}
return HttpResponse(
json.dumps(data, indent=2, cls=JSONEncoder), content_type="application/json"
)
def response_csv(self, data):
"""
Returns Response object for CSV response
Arguments:
- data <list>
Returns:
- HttpResponse
"""
if not data:
return ""
response = HttpResponse(content_type="text/csv")
csv_writer = csv.DictWriter(response, fieldnames=list(data[0].keys()))
csv_writer.writeheader()
for row in data:
for k, v in list(row.items()):
if isinstance(v, str):
row[k] = u"{}".format(v)
csv_writer.writerow(row)
return response
class AdvancedSearchExportView(ExportView):
"""
Allows exporting of advanced search result data
"""
tag = None
json_root_key = "results"
download_name = "advanced_search_export.{extension}"
def fetch(self, request):
"""
Fetch data from api according to GET parameters
Note that `limit` and `depth` will be overwritten, other api
parameters will be passed along as-is
Returns:
- dict: un-rendered dataset returned by api
"""
params = request.GET.dict()
params["limit"] = 250
params["depth"] = 1
# prepare api request
request_factory = APIRequestFactory()
viewset = RestViewSets[self.tag].as_view({"get": "list"})
api_request = request_factory.get(
"/api/{}/?{}".format(self.tag, urllib.parse.urlencode(params))
)
# we want to use the same user as the original request
# so permissions are applied correctly
api_request.user = request.user
response = viewset(api_request)
return response.data
def get(self, request, tag, fmt):
"""
Handle export
"""
self.tag = tag
return super(AdvancedSearchExportView, self).get(request, fmt)
def generate(self, request):
"""
Generate data for the reftag specified in self.tag
This functions will call generate_<tag> and return the result
Arguments:
- request <Request>
Returns:
- list: list containing rendered data rows ready for export
"""
if self.tag not in ["net", "ix", "fac", "org"]:
raise ValueError(_("Invalid tag"))
data_function = getattr(self, "generate_{}".format(self.tag))
return data_function(request)
def generate_net(self, request):
"""
Fetch network data from the api according to request and then render
it ready for export
Arguments:
- request <Request>
Returns:
- list: list containing rendered data ready for export
"""
data = self.fetch(request)
download_data = []
for row in data:
download_data.append(
collections.OrderedDict(
[
("Name", row["name"]),
("Also known as", row["aka"]),
("ASN", row["asn"]),
("General Policy", row["policy_general"]),
("Network Type", row["info_type"]),
("Network Scope", row["info_scope"]),
("Traffic Levels", row["info_traffic"]),
("Traffic Ratio", row["info_ratio"]),
("Exchanges", len(row["netixlan_set"])),
("Facilities", len(row["netfac_set"])),
]
)
)
return download_data
def generate_fac(self, request):
"""
Fetch facility data from the api according to request and then render
it ready for export
Arguments:
- request <Request>
Returns:
- list: list containing rendered data ready for export
"""
data = self.fetch(request)
download_data = []
for row in data:
download_data.append(
collections.OrderedDict(
[
("Name", row["name"]),
("Management", row["org_name"]),
("CLLI", row["clli"]),
("NPA-NXX", row["npanxx"]),
("City", row["city"]),
("Country", row["country"]),
("State", row["state"]),
("Postal Code", row["zipcode"]),
("Networks", row["net_count"]),
]
)
)
return download_data
def generate_ix(self, request):
"""
Fetch exchange data from the api according to request and then render
it ready for export
Arguments:
- request <Request>
Returns:
- list: list containing rendered data ready for export
"""
data = self.fetch(request)
download_data = []
for row in data:
download_data.append(
collections.OrderedDict(
[
("Name", row["name"]),
("Media Type", row["media"]),
("Country", row["country"]),
("City", row["city"]),
("Networks", row["net_count"]),
]
)
)
return download_data
def generate_org(self, request):
"""
Fetch organization data from the api according to request and then render
it ready for export
Arguments:
- request <Request>
Returns:
- list: list containing rendered data ready for export
"""
data = self.fetch(request)
download_data = []
for row in data:
download_data.append(
collections.OrderedDict(
[
("Name", row["name"]),
("Country", row["country"]),
("City", row["city"]),
]
)
)
return download_data