diff options
-rw-r--r-- | portal-gui/rpkigui/myrpki/glue.py | 2 | ||||
-rw-r--r-- | portal-gui/rpkigui/myrpki/misc.py | 10 | ||||
-rw-r--r-- | portal-gui/rpkigui/myrpki/views.py | 229 | ||||
-rwxr-xr-x | portal-gui/scripts/load_csv.py | 117 |
4 files changed, 92 insertions, 266 deletions
diff --git a/portal-gui/rpkigui/myrpki/glue.py b/portal-gui/rpkigui/myrpki/glue.py index eececa7a..868687a5 100644 --- a/portal-gui/rpkigui/myrpki/glue.py +++ b/portal-gui/rpkigui/myrpki/glue.py @@ -7,8 +7,6 @@ import os.path import math import rpki from rpki.myrpki import csv_writer -from rpki.resource_set import resource_range_ipv4 -from rpki.ipaddrs import v4addr from django.conf import settings #def form_to_conf(data): diff --git a/portal-gui/rpkigui/myrpki/misc.py b/portal-gui/rpkigui/myrpki/misc.py index 3b54107f..4e0970a6 100644 --- a/portal-gui/rpkigui/myrpki/misc.py +++ b/portal-gui/rpkigui/myrpki/misc.py @@ -23,14 +23,4 @@ def str_to_range(lo, hi): else: return rpki.resource_set.resource_range_ipv6(x, y) -#def str_to_roa(lo, hi): -# """Convert IP address strings to a subclass of roa_prefix.""" -# x = str_to_addr(lo) -# y = str_to_addr(hi) -# assert type(x) == type(y) -# if isinstance(x, rpki.ipaddrs.v4addr): -# return rpki.resource_set.roa_prefix_ipv4(x, y) -# else: -# return rpki.resource_set.roa_prefix_ipv6(x, y) - # vim:sw=4 ts=8 expandtab diff --git a/portal-gui/rpkigui/myrpki/views.py b/portal-gui/rpkigui/myrpki/views.py index 33502631..24e095f4 100644 --- a/portal-gui/rpkigui/myrpki/views.py +++ b/portal-gui/rpkigui/myrpki/views.py @@ -15,7 +15,6 @@ from functools import update_wrapper import models import forms import glue -from asnset import asnset from rpkigui.myrpki.misc import str_to_range from rpkigui.myrpki.asnset import asnset @@ -45,38 +44,6 @@ def handle_required(f): return f(request, *args, **kwargs) return wrapped_fn -#class handle_required(object): -# '''A decorator to require picking a configuration. __call__ is -# decorated with login_required so that we can be sure that the -# request has a user. -# -# We don't support picking the configuration yet -- if multiple -# configurations match, we redirect to handle_picker, which should -# allow a staff member to pick any handle. -# ''' -# -# def __init__(self, f): -# self.f = f -# update_wrapper( self, f ) -# -# @login_required -# def __call__(self, request, *args, **kwargs): -# if 'handle' not in request.session: -# conf = models.Conf.objects.all().filter( -# owner__in=request.user.groups.all()) -# if conf.count() == 1: -# handle = conf[ 0 ] -# elif conf.count() == 0: -# return http.HttpResponseRedirect('/myrpki/conf/add') -# else: -# # Should reverse the view for this instead of hardcoding -# # the URL. -# return http.HttpResponseRedirect( -# '/myrpki/conf/select?next=%s' % -# urlquote(request.get_full_path())) -# request.session[ 'handle' ] = handle -# return self.f(request, *args, **kwargs) - def render(template, context, request): return render_to_response(template, context, context_instance=RequestContext(request)) @@ -112,14 +79,16 @@ def dashboard(request): # get list of ASNs used in my ROAs roa_asns = [r.asn for r in handle.roas.all()] # get list of address ranges included in ROAs - roa_addrs = [p.prefix for r in handle.roas.all() for p in r.from_roa_request.all()] + roa_addrs = [p.prefix for r in handle.roas.all() + for p in r.from_roa_request.all()] asns=[] prefixes=[] for p in handle.parents.all(): for c in p.resources.all(): asns.extend(c.asn.filter(allocated__isnull=True).exclude(lo__in=roa_asns)) - prefixes.extend(c.address_range.filter(allocated__isnull=True, roa_requests__isnull=True)) + prefixes.extend(c.address_range.filter(allocated__isnull=True, + roa_requests__isnull=True)) asns, prefixes = unallocated_resources(handle, roa_asns, roa_addrs, asns, prefixes) @@ -128,40 +97,6 @@ def dashboard(request): return render('myrpki/dashboard.html', { 'conf': handle, 'asns': asns, 'ars': prefixes }, request) -#@handle_required -#def cert_add( request ): -# return create_object( request, form_class=forms.ConfCertForm( request ), -# post_save_redirect='/myrpki/cert/' ) - -#@handle_required -#def cert_view( request, id ): -# handle = request.session[ 'handle' ] -# queryset = models.Cert.objects.filter( conf=handle ) -# return object_detail( request, queryset=queryset, object_id=id, -# template_object_name='cert' ) -# -#@handle_required -#def cert_list( request ): -# handle = request.session[ 'handle' ] -# queryset = models.Cert.objects.filter( conf=handle ) -# return object_list( request, queryset=queryset, -# template_object_name='cert' ) -# -#@handle_required -#def cert_edit( request, id ): -# handle = request.session[ 'handle' ] -# cert = get_object_or_404( models.Cert, pk=id, conf=handle ) -# return update_object( request, form_class=forms.ConfCertForm( request ), -# object_id=id, -# post_save_redirect='/myrpki/cert/' ) -# -#@handle_required -#def cert_delete( request, id ): -# handle = request.session[ 'handle' ] -# cert = get_object_or_404( models.Cert, pk=id, conf=handle ) -# return delete_object( request, model=models.Cert, object_id=id, -# post_delete_redirect='/dashboard/' ) - #@login_required #def conf_add(request): # '''Allow the user to create a new configuration.''' @@ -281,35 +216,6 @@ def parent_view(request, parent_handle): handle__exact=parent_handle) return render('myrpki/parent_view.html', { 'parent': parent }, request) -#def parent_resource(request, parent_handle, obj_type, form_type): -# """Add an resource range to a parent.""" -# handle = request.session['handle'] -# parent = get_object_or_404(handle.parents.all(), -# handle__exact=parent_handle) -# if request.method == 'POST': -# form = form_type(request.POST) -# if form.is_valid(): -# obj = obj_type(parent).create( -# lo=form.cleaned_data['lo'], hi=form.cleaned_data['hi']) -# -# glue.configure_resources(handle) -# -# return http.HttpResponseRedirect('/myrpki/parent/' + parent_handle) -# else: -# form = form_type() -# return render('myrpki/parent_resource.html', -# { 'parent': parent, 'form': form }, request) - -#@handle_required -#def parent_address(request, parent_handle): -# return parent_resource(request, parent_handle, -# lambda p: p.address_range, forms.AddressRangeForm) - -#@handle_required -#def parent_asn(request, parent_handle): -# return parent_resource(request, parent_handle, -# lambda p: p.asn, forms.AsnRangeForm) - @handle_required def child_import(request): handle = request.session['handle'].handle @@ -355,38 +261,6 @@ def get_parents_or_404(handle, obj): return handle.parents.filter(pk__in=[c.parent.pk for c in cert_set]) -#def resource_view(request, object_type, form_type, pk, resource_type): -# '''view/subdivide an address range.''' -# handle = request.session['handle'] -# obj = get_object_or_404(object_type, pk=pk) -# # ensure this resource range belongs to a parent of the current conf -# parent_set = get_parents_or_404(handle, obj) -# -# if request.method == 'POST': -# form = form_type(handle, obj, request.POST) -# if form.is_valid(): -# if form.cleaned_data['child'] is None: -# hi = form.cleaned_data['hi'] -# lo = form.cleaned_data['lo'] -# print hi, lo -# # if a range is given, create a new object -# if hi and lo: -# subobj = object_type.objects.create( -# lo=lo, hi=hi, parent=obj, allocated=None) -# subobj.save() -# if obj.allocated: -# obj.allocated = None -# obj.save() -# else: -# obj.allocated = form.cleaned_data['child'] -# obj.save() -# -# glue.configure_resources(handle) -# else: -# form = form_type(handle, obj) -# return render('myrpki/resource_view.html', { 'addr': obj, 'form': form, -# 'parent': parent_set, 'resource_type': resource_type }, request) - @handle_required def address_view(request, pk): handle = request.session['handle'] @@ -408,42 +282,6 @@ def asn_view(request, pk): return render('myrpki/asn_view.html', { 'asn': obj, 'parent': parent_set }, request) -#@handle_required -#def roa_edit(request, pk=None): -# '''Create or edit a ROA.''' -# -# handle = request.session['handle'] -# -# if not pk is None: -# obj = get_object_or_404(models.Roa, pk=pk) -# if obj.conf != handle: -# raise http.Http404 -# else: -# obj = None -# -# if request.method == 'POST': -# form = forms.RoaForm(handle, None, None, None, request.POST) -# if form.is_valid(): -# if not obj: -# obj = models.Roa(conf=handle, asn=form.cleaned_data['asn'], -# comments=form.cleaned_data['comments'], max_len=0) -# else: -# obj.asn = form.cleaned_data['asn'] -# obj.comments = form.cleaned_data['comments'] -# obj.save() -# obj.prefix.clear() -# obj.prefix.add(*form.cleaned_data['prefix']) -# -# glue.configure_resources(handle) -# -# return http.HttpResponseRedirect('/myrpki/') -# else: -# asn = obj.asn if obj else None -# comments = obj.comments if obj else None -# prefix = [o.pk for o in obj.prefix.all()] if obj else [] -# form = forms.RoaForm(handle, asn, comments, prefix) -# return render('myrpki/roaform.html', { 'form': form }, request) - @handle_required def child_view(request, child_handle): '''Detail view of child for the currently selected handle.''' @@ -462,7 +300,8 @@ def prefix_split_view(request, pk): if request.method == 'POST': form = forms.PrefixSplitForm(prefix, request.POST) if form.is_valid(): - obj = models.AddressRange(lo=form.cleaned_data['lo'], hi=form.cleaned_data['hi'], parent=prefix) + obj = models.AddressRange(lo=form.cleaned_data['lo'], + hi=form.cleaned_data['hi'], parent=prefix) obj.save() return http.HttpResponseRedirect(obj.get_absolute_url()) else: @@ -486,65 +325,13 @@ def prefix_allocate_view(request, pk): glue.configure_resources(handle) return http.HttpResponseRedirect(prefix.get_absolute_url()) else: - form = forms.PrefixAllocateForm(prefix.allocated.pk if prefix.allocated else None, + form = forms.PrefixAllocateForm( + prefix.allocated.pk if prefix.allocated else None, handle.children.all()) return render('myrpki/prefix_view.html', { 'form': form, 'addr': prefix, 'form': form, 'parent': parent_set }, request) -def parent_prefix(prefix): - '''Returns the top-most parent prefix for the given prefix.''' - while prefix.parent: - prefix = prefix.parent - return prefix - -def common_cert(prefix, prefix_set): - '''Return true if prefix is derived from the same resource cert - as all the addresses in prefix_set.''' - while prefix.parent: - prefix = prefix.parent - - # list of certs for the target prefix - certs = prefix.from_cert.all() - # all prefixes will have the same cert, so just check the first one - prefix_certs = prefix_set[0].from_cert.all() - # all we need is one match - for c in certs: - if c in prefix_certs: - return True - return False - -def update_roas(handle, prefix): - '''Recompute the required ROAs for the prefix that was changed.''' - # generate the list of ASNs - asns = list(asnset(prefix.asns)) - - print 'updating roas for %s: asns=%r' % (prefix, asns) - - # remove this prefix from any roa not on the updated list - for roa in prefix.from_roa.exclude(asn__in=asns): - print 'removing %s from roa for asn %d' % (prefix, roa.asn) - roa.prefix.remove(prefix) - # if the roa is empty, delete it now - if roa.prefix.all().count() == 0: - print 'deleting roa for asn %d because it is empty' % (roa.asn,) - roa.delete() - - for asid in asns: - for roa in handle.roas.filter(asn=asid): - # determine if this roa includes prefixes from the same - # resource cert as the prefix we just changed - if common_cert(prefix, roa.prefix.all()): - roa.prefix.add(prefix) - break - else: - # no roa is present for this ASN, create a new one - print 'creating new roa for asn %d with %s' % (asid, prefix) - roa = models.Roa.objects.create(asn=asid, conf=handle, - active=False) - roa.save() - roa.prefix.add(prefix) - def add_roa_requests(handle, prefix, asns, max_length): for asid in asns: req_set = prefix.roa_requests.filter(roa__asn=asid, diff --git a/portal-gui/scripts/load_csv.py b/portal-gui/scripts/load_csv.py index ed046c5a..de48447e 100755 --- a/portal-gui/scripts/load_csv.py +++ b/portal-gui/scripts/load_csv.py @@ -19,8 +19,7 @@ import rpki.ipaddrs from rpki.myrpki import csv_reader from rpkigui.myrpki import models -from rpkigui.myrpki.views import update_roas -from rpkigui.myrpki.asnset import asnset +from rpkigui.myrpki.views import add_roa_requests cfg_file = os.getenv("MYRPKI_CONF", "myrpki.conf") cfg = rpki.config.parser(cfg_file, "myrpki") @@ -33,39 +32,91 @@ print 'processing csv files for resource handle', handle conf = models.Conf.objects.get(handle=handle) -for child_handle, asn in csv_reader(asn_csv, columns=2): - child = conf.children.get(handle=child_handle) - asn = models.Asn.objects.get(lo=asn, hi=asn, - from_cert__parent__in=conf.parents.all()) - child.asn.add(asn) - -for child_handle, prefix in csv_reader(prefix_csv, columns=2): - child = conf.children.get(handle=child_handle) - try: - rs = rpki.resource_set.resource_range_ipv4.from_str(prefix) - except socket.error: - rs = rpki.resource_set.resource_range_ipv6.from_str(prefix) - obj = models.AddressRange.objects.get(lo=str(rs.min), hi=str(rs.max), - from_cert__parent__in=conf.parents.all()) - child.address_range.add(obj) +# every parent has a favorite +def best_child(parent, parent_range): + '''Return the child address range that is the closest match, or + returns the arguments if no children.''' + best = None + best_range = None + for q in parent.children.all(): + if best is None: + best = q + best_range = q.as_resource_range() + else: + t = q.as_resource_range() + if t.min >= best_range.min and t.max <= best_range.max: + best = q + best_range = t + if best: + if best.children.all(): + best, best_range = best_child(best, best_range) + return (best, best_range) -for prefix, asn, group in csv_reader(roa_csv, columns=3): - try: - rs = rpki.resource_set.roa_prefix_set_ipv4().parse_str(prefix) - except socket.error: - rs = rpki.resource_set.roa_prefix_set_ipv6().parse_str(prefix) + return parent, parent_range - if rs.prefixlen != rs.max_prefixlen: - raise ValueError, \ - "%s: max prefixlen larger than prefixlen is not currently supported." % (prefix,) +def get_or_create_prefix(address_range): + '''Returns a AddressRange object for the resource_range_ip specified + as an argument. If no match is found, a new AddressRange object is + created as a child of the best matching received resource.''' - print str(rs.min()), str(rs.max()) - obj = models.AddressRange.objects.get(lo=str(rs.min()), hi=str(rs.max()), + # get all resources from our parents + prefix_set = models.AddressRange.objects.filter( from_cert__parent__in=conf.parents.all()) - roa_asns = asnset(obj.asns) - asid = int(asn) - if asid not in roa_asns: - roa_asns.add(asid) - obj.asns = str(roa_asns) + + # gross, since we store the address ranges as strings in the django + # db, we can't use the normal __lte and __gte filters, so we get to + # do it in python instead. + for prefix in prefix_set: + prefix_range = prefix.as_resource_range() + if (prefix_range.min <= address_range.min and + prefix_range.max >= address_range.max): + # there should only ever be a single matching prefix + break + else: + raise RuntimeError, '%s does not match any received address range.' % ( + address_range,) + + # find the best match among the children + grandchildren + prefix, prefix_range = best_child(prefix, prefix_range) + + print 'best match for %s is %s' % (address_range, prefix) + if prefix_range.min != address_range.min or prefix_range.max != address_range.max: + # create suballocation + print 'creating new range' + prefix = models.AddressRange.objects.create(lo=str(address_range.min), + hi=str(address_range.max), parent=prefix) + + return prefix + +def do_asns(): + for child_handle, asn in csv_reader(asn_csv, columns=2): + child = conf.children.get(handle=child_handle) + asn = models.Asn.objects.get(lo=asn, hi=asn, + from_cert__parent__in=conf.parents.all()) + child.asn.add(asn) + +def do_prefixes(): + for child_handle, prefix in csv_reader(prefix_csv, columns=2): + child = conf.children.get(handle=child_handle) + try: + rs = rpki.resource_set.resource_set_ipv4().parse_str(prefix) + except socket.error: + rs = rpki.resource_set.resource_set_ipv6().parse_str(prefix) + obj = get_or_create_prefix(rs) + obj.allocated = child obj.save() - update_roas(conf, obj) + +def do_roas(): + for prefix, asn, group in csv_reader(roa_csv, columns=3): + try: + rs = rpki.resource_set.roa_prefix_set_ipv4().parse_str(prefix) + except socket.error: + rs = rpki.resource_set.roa_prefix_set_ipv6().parse_str(prefix) + + print str(rs.min()), str(rs.max()), rs.max_prefixlen + obj = get_or_create_prefix(rs.to_resource_range()) + add_roa_requests(conf, obj, [int(asn)], rs.max_prefixlen) + +do_asns() +do_prefixes() +do_roas() |