diff options
-rw-r--r-- | portal-gui/README | 149 | ||||
-rw-r--r-- | portal-gui/rpkigui/myrpki/asnset.py | 23 | ||||
-rw-r--r-- | portal-gui/rpkigui/myrpki/forms.py | 74 | ||||
-rw-r--r-- | portal-gui/rpkigui/myrpki/glue.py | 72 | ||||
-rw-r--r-- | portal-gui/rpkigui/myrpki/models.py | 34 | ||||
-rw-r--r-- | portal-gui/rpkigui/myrpki/urls.py | 13 | ||||
-rw-r--r-- | portal-gui/rpkigui/myrpki/views.py | 284 | ||||
-rw-r--r-- | portal-gui/rpkigui/settings.py | 8 | ||||
-rw-r--r-- | portal-gui/rpkigui/templates/myrpki/asn_view.html | 53 | ||||
-rw-r--r-- | portal-gui/rpkigui/templates/myrpki/dashboard.html | 72 | ||||
-rw-r--r-- | portal-gui/rpkigui/templates/myrpki/prefix_view.html | 58 | ||||
-rw-r--r-- | portal-gui/rpkigui/templates/myrpki/resource_view.html | 18 | ||||
-rwxr-xr-x | portal-gui/scripts/helper (renamed from portal-gui/scripts/list_resources) | 3 | ||||
-rwxr-xr-x | portal-gui/scripts/list_resources.py | 162 | ||||
l--------- | portal-gui/scripts/load_csv | 1 | ||||
-rwxr-xr-x | portal-gui/scripts/load_csv.py | 61 |
16 files changed, 816 insertions, 269 deletions
diff --git a/portal-gui/README b/portal-gui/README index 2491c3f5..daa72daa 100644 --- a/portal-gui/README +++ b/portal-gui/README @@ -10,3 +10,152 @@ about Django at http://www.djangoproject.com/ This package is an interface to rpkid and friends, so it assumes that you'll be running rpkid. If you haven't already done so, you should set up rpkid first; see ../rpkid/doc/Installation. + +=== Assumptions === + +This is a list of the assumptions the current rpkigui code makes: + +1) There will be at least one resource holder which runs rpkid. This handle + *must* be the parent of all other resource holders served by the same rpkid + instance. In rpki parlance, there is one "self-hosted" resource holder, and + all the others are "hosted." + +2) The myrpki.py command line tool will handle all the heavy lifting, so it must + be present on the installed system. + +3) All the directories containing the files assosiated with each + resource handle must reside in the same directory. That is, the + rpkigui expects the following structure: + + /datadir + /dad + /myrpki.conf + /entitydb/ + ... + /mom + /myrpki.conf + /entitydb/ + ... + /baby + /myrpki.conf + /entitydb/ + ... + +=== Installation === + +The web interface can be run out of the source directory for testing, or may be +deployed to work with an existing web server. Instructions for using Django +with Apache and mod_wsgi can be found at +http://docs.djangoproject.com/en/1.2/howto/deployment/modwsgi/#howto-deployment-modwsgi + +==== Configuation === + +The primary configuration file for the portal gui is +$top/portal-gui/rpkigui/settings.py. At a minimum, you will need to change the +default settings for the following variables: + +* DATABASE_NAME + This string variable should contain the full path for the sqlite +database that the portal GUI uses to store information. NOTE: this is a +different database from the one that rpkid uses. + +* SECRET_KEY + This string value should be set to a unique value for your installation. +There is no required format, but the longer the better. Something suitable +might be generated by running: + $ ps auxwww | sha1sum + +* TEMPLATE_DIRS + This is directory where the html templates for the various views in the +portal gui. Change the last entry to reflect your installation. + +* MYRPKI_DATA_DIR + The top level directory under which all the directories for your RPKI +handles are stored. Each directory must have a myrpki.conf file. + +* MYRPKI_SRC_DIR + The directory where myrpki.py command line tool can be found. + +There is one additional file that needs to be edited: +$top/portal-gui/rpkigui/urls.py. Near the end of the file is a dict entry for +"document_root." You must change the value to the full path for the directory +containing the image files used by the GUI. This can be a path into the +portal-gui source tree. + +=== Initialize === + +The next step is to initialize the sqlite database that the portal-gui uses to +store information. This is done using the "manage.py" wrapper script for +django: + $ cd $top/portal-gui/rpkigui/ + $ python manage.py syncdb + +The manage.py script will prompt you to create an administrative user if you +desire. The administrative user is useful because you can use django's admin +views to inspect the database directly, which may be useful for debugging. + +=== list_resources helper script === + +The portal-gui does not directly talk to the rpkid server. Instead, there is a +command line script named "list_resources" which talks to rpkid and updates the +portal-gui database with information that has changed. For testing purposes, +this script can be run by hand. However, for deployment you will need to set up +a cron job to run this script periodically for *each* resource handle the +portal-gui is serving. + +Note that "list_resources" only should be run in the directory where the +myrpki.conf for the resource handle that is self-hosting the rpkid. You may way +to create a script which is invoked by cron: + + #!/bin/sh + cd /var/lib/myrpki + $top/portal-gui/scripts/list_resources mom + $top/portal-gui/scripts/list_resources dad + $top/portal-gui/scripts/list_resources baby + +This script probably only needs to be run infrequently. It's sole purpose is to +query rpkid to ask what resources and children are configured for each resource +handle. This information does not change often. + +=== Load existing data === + +If you already have delegated resources to children, or created ROAs in the .csv +files for the myrpki.py command line tool, you will want to load the portal-gui +with this information. There is a helper script for doing this step. Simply +chdir to the directory containing your myrpki.conf and .csv files and run: + + $top/portal-gui/scripts/load_csv + +NOTE that you must run the "list_resources" script *prior* to using "load_csv" +or you will get errors because portal-gui won't yet know about which handles it +is serving. + +You should run "load_csv" in *each* of your directories for each handle. + +=== Starting the Portal GUI === + +If you have configured django to use apached and mod_wsgi, you just need to +start your web server. + +Alternatively, django comes with a test web server which is useful for small +deployments, or for just testing the portal-gui. You can start the test server +by running: + $ cd $top/portal-gui/rpkigui/ + $ python manage.py runserver <ip:port> +where <ip:port> is the address where you want to run the server. If you don't +specify the <ip:port>, the default is 127.0.0.1:8000. + +Now you can navigate to http://<ip:port>/myrpki/ to use the GUI. + +=== Creating Users === + +By default, the administrative user created during the "Initialization" step +above can manage all resource handles. However, the portal-gui's security model +allows the use of separate logins to manage resource handles. Each resource +handle needs to be configured to allow one or more users to manage it. This is +accomplished by using the django admin interface. http://<ip:port>/admin/ + +You can configure which users are allowed to manage a particular resource handle +by navigating to http://<ip:port>/admin/myrpki/conf/. Simply click on the +handle you want to change, and select one or more users in the "Owner" list box +and click "Save." diff --git a/portal-gui/rpkigui/myrpki/asnset.py b/portal-gui/rpkigui/myrpki/asnset.py new file mode 100644 index 00000000..2fa52450 --- /dev/null +++ b/portal-gui/rpkigui/myrpki/asnset.py @@ -0,0 +1,23 @@ +class asnset(object): + """a set-like objet for containing sets of ASN values.""" + v = set() + + def __init__(self, init=None): + """ + May be initialized from a comma separated list of positive integers. + """ + if init: + self.v = set(int(x) for x in init.split(',') if x.strip() != '') + if any([x for x in self.v if x < 0]): + raise ValueError, "can't contain negative values." + + def __str__(self): + return ','.join(str(x) for x in sorted(self.v)) + + def __iter__(self): + return iter(self.v) + + def add(self, n): + assert isinstance(n, int) + assert n > 0 + self.v.add(n) diff --git a/portal-gui/rpkigui/myrpki/forms.py b/portal-gui/rpkigui/myrpki/forms.py index ae7bca23..98c28934 100644 --- a/portal-gui/rpkigui/myrpki/forms.py +++ b/portal-gui/rpkigui/myrpki/forms.py @@ -1,5 +1,5 @@ from django import forms -from myrpki import models +import models def ConfCertForm(request): class CertForm(forms.ModelForm): @@ -96,17 +96,13 @@ def SubOrAssignForm(handle, addr, field_type, *args, **kwargs): child = forms.ModelChoiceField(required=False, label='Assign to child', initial=get_pk(addr.allocated), queryset=handle.children.all()) - #def __init__(self, *inargs, **inkwargs): - # super(Wrapper, self).__init__(*inargs, **inkwargs) - # if isinstance(addr, models.AddressRange): - # self.asn = forms.ModelChoiceField(required=False, - # label='Issue ROA', queryset=models.Asn.objects.all()) - def clean_lo(self): '''validate the self.lo field to ensure it is within the parent's range.''' lo = self.cleaned_data['lo'] - if lo is not None: + if lo == '': + lo = None + if lo != None: if lo < addr.lo or lo > addr.hi: raise forms.ValidationError, 'Value is out of range of parent.' # ensure there is no overlap with other children @@ -120,9 +116,12 @@ def SubOrAssignForm(handle, addr, field_type, *args, **kwargs): '''validate the self.hi field to ensure it is within the parent's range.''' hi = self.cleaned_data['hi'] - if hi is not None: + if hi == '': + hi = None + if hi != None: if hi < addr.lo or hi > addr.hi: - raise forms.ValidationError, 'Value is out of range of parent.' + raise forms.ValidationError, \ + 'Value is out of range of parent.' # ensure there is no overlap with other children for c in addr.children.all(): if hi >= c.lo and hi <= c.hi: @@ -132,7 +131,8 @@ def SubOrAssignForm(handle, addr, field_type, *args, **kwargs): def clean_child(self): if self.cleaned_data['child'] and addr.children.count(): - raise forms.ValidationError, "Can't allocate a subdivided address." + raise forms.ValidationError, \ + "Can't allocate a subdivided address." return self.cleaned_data['child'] def clean(self): @@ -140,7 +140,9 @@ def SubOrAssignForm(handle, addr, field_type, *args, **kwargs): child = clean_data.get('child') lo = clean_data.get('lo') hi = clean_data.get('hi') - if child and (lo or hi): + loset = lo != '' and lo != None + hiset = hi != '' and hi != None + if (child and (loset or hiset)): raise forms.ValidationError, \ 'Either a range or a child must be set, but not both.' elif (lo and not hi) or (hi and not lo): @@ -156,13 +158,57 @@ def SubOrAssignAddressForm(handle, addr, *args, **kwargs): def SubOrAssignAsnForm(handle, asn, *args, **kwargs): return SubOrAssignForm(handle, asn, forms.IntegerField, *args, **kwargs) -def RoaForm(handle, pk=None, initcomment=None, initval=[], *args, **kwargs): +def RoaForm(handle, pk=None, initval=[], *args, **kwargs): vals = models.AddressRange.objects.filter(from_parent__in=handle.parents.all()) class Wrapped(forms.Form): asn = AsnField(initial=pk) prefix = forms.ModelMultipleChoiceField(label='Prefixes', queryset=vals, initial=initval) - comments = forms.CharField(required=False, initial=initcomment) return Wrapped(*args, **kwargs) + +def PrefixSplitForm(prefix, *args, **kwargs): + class _wrapper(forms.Form): + hi = forms.IPAddressField() + lo = forms.IPAddressField() + + def clean_lo(self): + lo = self.cleaned_data.get('lo') + if lo > prefix.hi: + raise forms.ValidationError, 'Value out of range of parent prefix' + + def clean_hi(self): + hi = self.cleaned_data.get('hi') + if hi < prefix.lo: + raise forms.ValidationError, 'Value out of range of parent prefix' + + def clean(self): + hi = self.cleaned_data['hi'] + lo = self.cleaned_data['lo'] + if hi < lo: + raise forms.ValidationError, 'Invalid upper range' + if prefix.allocated: + raise forms.ValidationError, 'Prefix is assigned to child' + + return _wrapper(*args, **kwargs) + +def PrefixAllocateForm(iv, child_set, *args, **kwargs): + class _wrapper(forms.Form): + child = forms.ModelChoiceField(initial=iv, queryset=child_set, required=False) + return _wrapper(*args, **kwargs) + +class PrefixRoaForm(forms.Form): + asns = forms.CharField(max_length=200, required=False) + + def clean_asns(self): + try: + v = [int(d) for d in self.cleaned_data['asns'].split(',') if d.strip() != ''] + if any([x for x in v if x < 0]): + raise forms.ValidationError, 'must be a positive integer' + return ','.join(str(x) for x in sorted(v)) + except ValueError: + raise forms.ValidationError, 'must be a list of integers separated by commas' + return self.cleaned_data['asns'] + +# vim:sw=4 ts=8 expandtab diff --git a/portal-gui/rpkigui/myrpki/glue.py b/portal-gui/rpkigui/myrpki/glue.py index c25b3055..a8abac89 100644 --- a/portal-gui/rpkigui/myrpki/glue.py +++ b/portal-gui/rpkigui/myrpki/glue.py @@ -2,26 +2,26 @@ from __future__ import with_statement import os import os.path -import csv import math -import rpki.myrpki +import rpki +from rpki.myrpki import csv_writer from rpki.resource_set import resource_range_ipv4 from rpki.ipaddrs import v4addr -import settings +from django.conf import settings -def form_to_conf(data): - """Write out a myrpki.conf based on the given form data.""" - handle = data['handle'] - confdir = settings.MYRPKI_DATA_DIR + '/' + handle - if os.path.exists(confdir): - raise RuntimeError, '%s: directory already exists!' % (confdir, ) - os.makedirs(confdir) - template = open(settings.MYRPKI_DATA_DIR + '/examples/myrpki.conf', 'r').read() - # stuff the appropriate output directory into the dict - data['MYRPKI_DATA_DIR'] = confdir - with open(confdir + '/myrpki.conf', 'w') as conf: - print >>conf, template % data - invoke_rpki(handle, ['initialize']) +#def form_to_conf(data): +# """Write out a myrpki.conf based on the given form data.""" +# handle = data['handle'] +# confdir = settings.MYRPKI_DATA_DIR + '/' + handle +# if os.path.exists(confdir): +# raise RuntimeError, '%s: directory already exists!' % (confdir, ) +# os.makedirs(confdir) +# template = open(settings.MYRPKI_DATA_DIR + '/examples/myrpki.conf', 'r').read() +# # stuff the appropriate output directory into the dict +# data['MYRPKI_DATA_DIR'] = confdir +# with open(confdir + '/myrpki.conf', 'w') as conf: +# print >>conf, template % data +# invoke_rpki(handle, ['initialize']) def invoke_rpki(handle, args): """Invoke the myrpki cli for the specified configuration.""" @@ -40,37 +40,43 @@ def read_identity(handle): def read_child_response(handle, child): fname = '%s/%s/entitydb/children/%s.xml' % (settings.MYRPKI_DATA_DIR, handle, child) with open(fname, 'r') as fp: - data = fp.read() + data = fp.read() return data -def output_asns(handle): +def output_asns(path, handle): '''Write out csv file containing resources delegated to my children.''' - confdir = settings.MYRPKI_DATA_DIR + '/' + handle.handle - f = csv.writer(open(confdir + '/asns.csv', 'w'), delimiter='\t') + f = csv_writer(path) for p in handle.children.all(): for asn in p.asn.all(): if asn.lo == asn.hi: f.writerow([p.handle, asn.lo]) -def output_prefixes(handle): +def output_prefixes(path, handle): '''Write out csv file containing resources delegated to my children.''' confdir = settings.MYRPKI_DATA_DIR + '/' + handle.handle - f = csv.writer(open(confdir + '/prefixes.csv', 'w'), delimiter='\t') + f = csv_writer(path) for p in handle.children.all(): - for prefix in p.address_range.all(): - f.writerow([p.handle, '%s-%s' % (prefix.lo, prefix.hi)]) + for prefix in p.address_range.all(): + f.writerow([p.handle, '%s-%s' % (prefix.lo, prefix.hi)]) -def output_roas(handle): - confdir = settings.MYRPKI_DATA_DIR + '/' + handle.handle - f = csv.writer(open(confdir + '/roas.csv', 'w'), delimiter='\t') +def output_roas(path, handle): + f = csv_writer(path) for r in handle.roas.all(): for addr in r.prefix.all(): - f.writerow([resource_range_ipv4(v4addr(str(addr.lo)), v4addr(str(addr.hi))), - r.asn, handle.handle]) + f.writerow([resource_range_ipv4(v4addr(str(addr.lo)), + v4addr(str(addr.hi))), + r.asn, + '%s-group-%d' % (handle.handle, r.pk)]) def configure_resources(handle): - # write out the .csv files and invoke the myrpki command line tool - output_asns(handle) - output_prefixes(handle) - output_roas(handle) + '''write out the .csv files and invoke the myrpki command line tool.''' + # chdir to the repo dir since the default myrpki.conf uses relative + # pathnames.. + os.chdir(settings.MYRPKI_DATA_DIR + '/' + handle.handle) + cfg = rpki.config.parser('myrpki.conf', 'myrpki') + output_asns(cfg.get('asn_csv'), handle) + output_prefixes(cfg.get('prefix_csv'), handle) + output_roas(cfg.get('roa_csv'), handle) #invoke_rpki(handle.handle, ['configure_daemons']) + +# vim:sw=4 ts=8 expandtab diff --git a/portal-gui/rpkigui/myrpki/models.py b/portal-gui/rpkigui/myrpki/models.py index 9abc80ed..04c2bb2c 100644 --- a/portal-gui/rpkigui/myrpki/models.py +++ b/portal-gui/rpkigui/myrpki/models.py @@ -1,20 +1,24 @@ from django.db import models -from django.contrib.auth.models import Group +from django.contrib.auth.models import User class HandleField(models.CharField): def __init__(self, **kwargs): models.CharField.__init__(self, max_length=255, **kwargs) -class IPAddressField( models.CharField ): +class IPAddressField(models.CharField): def __init__( self, **kwargs ): models.CharField.__init__(self, max_length=40, **kwargs) +class ASNListField(models.CharField): + def __init__( self, **kwargs ): + models.CharField.__init__(self, max_length=255, **kwargs) + class Conf(models.Model): '''This is the center of the universe, also known as a place to have a handle on a resource-holding entity. It's the <self> in the rpkid schema.''' handle = HandleField(unique=True, db_index=True) - owner = models.OneToOneField(Group) + owner = models.ManyToManyField(User) def __unicode__(self): return self.handle @@ -26,8 +30,11 @@ class AddressRange(models.Model): # parent address range parent = models.ForeignKey('AddressRange', related_name='children', blank=True, null=True) + # child to which this resource is delegated allocated = models.ForeignKey('Child', related_name='address_range', blank=True, null=True) + # who can originate routes for this prefix + asns = ASNListField(null=True, blank=True) def __unicode__(self): if self.lo == self.hi: @@ -45,6 +52,7 @@ class Asn(models.Model): # parent asn range parent = models.ForeignKey('Asn', related_name='children', blank=True, null=True) + # child to which this resource is delegated allocated = models.ForeignKey('Child', related_name='asn', blank=True, null=True) @@ -60,7 +68,6 @@ class Asn(models.Model): class Child(models.Model): conf = models.ForeignKey(Conf, related_name='children') handle = HandleField() # parent's name for child - validity = models.DateTimeField() def __unicode__(self): return u"%s's child %s" % (self.conf, self.handle) @@ -75,13 +82,7 @@ class Child(models.Model): class Parent(models.Model): conf = models.ForeignKey(Conf, related_name='parents') - handle = HandleField() -# service_uri = models.URLField( verify_exists=False ) -# sia_base = models.URLField( verify_exists=False ) - - #address_range = models.ManyToManyField(AddressRange, blank=True, - # related_name='from_parent') - #asn = models.ManyToManyField(Asn, related_name='from_parent', blank=True) + handle = HandleField() # my name for this parent def __unicode__(self): return u"%s's parent %s" % (self.conf, self.handle) @@ -119,17 +120,16 @@ class ResourceCert(models.Model): self.parent.handle) class Roa(models.Model): + '''Maps an ASN to the set of prefixes it can originate routes for. This + differs from a real ROA in that prefixes from multiple parents/resource + certs can be selected. The glue module contains code to split the ROAs + into groups by common resource certs.''' conf = models.ForeignKey(Conf, related_name='roas') - prefix = models.ManyToManyField(AddressRange) - max_len = models.IntegerField() asn = models.IntegerField() - comments = models.TextField() + prefix = models.ManyToManyField(AddressRange, related_name='from_roa') active = models.BooleanField() def __unicode__(self): return u"%s's ROA for %d" % (self.conf, self.asn) - def get_absolute_url(self): - return u'/myrpki/roa/%d' % (self.pk, ) - # vim:sw=4 ts=8 expandtab diff --git a/portal-gui/rpkigui/myrpki/urls.py b/portal-gui/rpkigui/myrpki/urls.py index b8230bff..2247c738 100644 --- a/portal-gui/rpkigui/myrpki/urls.py +++ b/portal-gui/rpkigui/myrpki/urls.py @@ -3,11 +3,6 @@ from django.views.generic.list_detail import object_list import views urlpatterns = patterns('', -# (r'^cert/$', views.cert_list ), -# (r'^cert/add/$', views.cert_add ), -# (r'^cert/(?P<id>\d+)/$', views.cert_view ), -# (r'^cert/(?P<id>\d+)/edit/$', views.cert_edit ), -# (r'^cert/(?P<id>\d+)/delete/$', views.cert_delete ), (r'^$', views.dashboard), # (r'^conf/add$', views.conf_add), (r'^conf/export$', views.conf_export), @@ -20,9 +15,13 @@ urlpatterns = patterns('', # (r'^parent/(?P<parent_handle>[^/]+)/address$', views.parent_address), # (r'^parent/(?P<parent_handle>[^/]+)/asn$', views.parent_asn), (r'^address/(?P<pk>\d+)$', views.address_view), + (r'^address/(?P<pk>\d+)/split$', views.prefix_split_view), + (r'^address/(?P<pk>\d+)/allocate$', views.prefix_allocate_view), + (r'^address/(?P<pk>\d+)/roa$', views.prefix_roa_view), (r'^asn/(?P<pk>\d+)$', views.asn_view), - (r'^roa/$', views.roa_edit ), - (r'^roa/(?P<pk>\d+)$', views.roa_edit ), + (r'^asn/(?P<pk>\d+)/allocate$', views.asn_allocate_view), +# (r'^roa/$', views.roa_edit ), +# (r'^roa/(?P<pk>\d+)$', views.roa_edit ), ) # vim:sw=4 ts=8 expandtab diff --git a/portal-gui/rpkigui/myrpki/views.py b/portal-gui/rpkigui/myrpki/views.py index c7d93779..70c453c1 100644 --- a/portal-gui/rpkigui/myrpki/views.py +++ b/portal-gui/rpkigui/myrpki/views.py @@ -8,13 +8,12 @@ from django.shortcuts import get_object_or_404, render_to_response from django.utils.http import urlquote from django.template import RequestContext from django.db import IntegrityError -from django.db.models import Q from django import http from functools import update_wrapper import models import forms -import settings import glue +from asnset import asnset # For each type of object, we have a detail view, a create view and # an update view. We heavily leverage the generic views, only @@ -24,9 +23,12 @@ def handle_required(f): @login_required def wrapped_fn(request, *args, **kwargs): if 'handle' not in request.session: - conf = models.Conf.objects.all().filter(owner__in=request.user.groups.all()) + if request.user.is_superuser: + conf = models.Conf.objects.all() + else: + conf = models.Conf.objects.filter(owner=request.user) if conf.count() == 1: - handle = conf[ 0 ] + handle = conf[0] elif conf.count() == 0: return http.HttpResponseRedirect('/myrpki/conf/add') else: @@ -174,7 +176,10 @@ def dashboard(request): @login_required def conf_list(request): """Allow the user to select a handle.""" - queryset = models.Conf.objects.filter(owner__in=request.user.groups.all()) + if request.user.is_superuser: + queryset = models.Conf.objects.all() + else: + queryset = models.Conf.objects.filter(owner__in=request.user.groups.all()) return object_list(request, queryset, template_name='myrpki/conf_list.html', template_object_name='conf') @@ -188,11 +193,14 @@ def conf_select(request): if next_url == '': next_url = '/myrpki/' - # since the handle is passed in as a parameter, need to verify that - # the user is actually in the group - conf = models.Conf.objects.filter( - Q(handle__exact=handle) & Q(owner__in=request.user.groups.all())) - if conf.count() > 0: + if request.user.is_superuser: + conf = models.Conf.objects.filter(handle=handle) + else: + # since the handle is passed in as a parameter, need to verify that + # the user is actually in the group + conf = models.Conf.objects.filter(handle=handle, + owner__in=request.user.groups.all()) + if conf: request.session['handle'] = conf[0] return http.HttpResponseRedirect(next_url) @@ -327,90 +335,224 @@ 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): - '''view/subdivide an address range.''' +#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'] - obj = get_object_or_404(object_type, pk=pk) + obj = get_object_or_404(models.AddressRange.objects.all(), 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'] - # 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 }, request) - -@handle_required -def address_view(request, pk): - '''view/subdivide an address range.''' - return resource_view(request, models.AddressRange, - forms.SubOrAssignAddressForm, pk) + return render('myrpki/prefix_view.html', + { 'addr': obj, 'parent': parent_set }, request) @handle_required def asn_view(request, pk): '''view/subdivide an asn range.''' - return resource_view(request, models.Asn, forms.SubOrAssignAsnForm, pk) + handle = request.session['handle'] + obj = get_object_or_404(models.Asn.objects, pk=pk) + # ensure this resource range belongs to a parent of the current conf + parent_set = get_parents_or_404(handle, obj) + + return render('myrpki/_view.html', + { 'addr': 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 roa_edit(request, pk=None): - '''Create or edit a ROA.''' +def child_view(request, child_handle): + '''Detail view of child for the currently selected handle.''' + handle = request.session['handle'] + child = get_object_or_404(handle.children.all(), handle__exact=child_handle) + + return render('myrpki/child_view.html', { 'child': child }, request) +@handle_required +def prefix_split_view(request, pk): handle = request.session['handle'] + prefix = get_object_or_404(models.AddressRange.objects, pk=pk) + # ensure this resource range belongs to a parent of the current conf + parent_set = get_parents_or_404(handle, prefix) - if not pk is None: - obj = get_object_or_404(models.Roa, pk=pk) - if obj.conf != handle: - raise http.Http404 + if request.method == 'POST': + form = forms.PrefixSplitForm(prefix, request.POST) + if form.is_valid(): + obj = models.AddressRange.create(form.cleaned_data['lo'], form.cleaned_data['hi'], parent=parent) + obj.save() + return http.HttpResponseRedirect(obj.get_absolute_url()) else: - obj = None + form = forms.PrefixSplitForm(prefix) + + return render('myrpki/prefix_view.html', { 'form': form, + 'addr': prefix, 'form': form, 'parent': parent_set }, request) + +@handle_required +def prefix_allocate_view(request, pk): + handle = request.session['handle'] + prefix = get_object_or_404(models.AddressRange.objects, pk=pk) + # ensure this resource range belongs to a parent of the current conf + parent_set = get_parents_or_404(handle, prefix) if request.method == 'POST': - form = forms.RoaForm(handle, None, None, None, request.POST) + form = forms.PrefixAllocateForm(None, handle.children.all(), 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']) + prefix.allocated = form.cleaned_data['child'] + prefix.save() + return http.HttpResponseRedirect(prefix.get_absolute_url()) + else: + 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 common_cert(prefix, prefix_set): + '''Return true if prefix is derived from the same resource cert + as all the addresses in prefix_set.''' + # 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) - glue.configure_resources(handle) +@handle_required +def prefix_roa_view(request, pk): + handle = request.session['handle'] + obj = get_object_or_404(models.AddressRange.objects, pk=pk) + # ensure this resource range belongs to a parent of the current conf + parent_set = get_parents_or_404(handle, obj) - return http.HttpResponseRedirect('/myrpki/') + if request.method == 'POST': + form = forms.PrefixRoaForm(request.POST) + if form.is_valid(): + obj.asns = form.cleaned_data['asns'] + obj.save() + update_roas(handle, obj) + glue.configure_resources(handle) + return http.HttpResponseRedirect(obj.get_absolute_url()) 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) + form = forms.PrefixRoaForm(initial={ 'asns': obj.asns }) + + return render('myrpki/prefix_view.html', { 'form': form, + 'addr': obj, 'form': form, 'parent': parent_set }, request) @handle_required -def child_view(request, child_handle): - '''Detail view of child for the currently selected handle.''' +def asn_allocate_view(request, pk): handle = request.session['handle'] - child = get_object_or_404(handle.children.all(), handle__exact=child_handle) + obj = get_object_or_404(models.Asn.objects, pk=pk) + # ensure this resource range belongs to a parent of the current conf + parent_set = get_parents_or_404(handle, obj) - return render('myrpki/child_view.html', { 'child': child }, request) + if request.method == 'POST': + form = forms.PrefixAllocateForm(None, handle.children.all(), request.POST) + if form.is_valid(): + obj.allocated = form.cleaned_data['child'] + obj.save() + return http.HttpResponseRedirect(obj.get_absolute_url()) + else: + form = forms.PrefixAllocateForm(obj.allocated.pk if obj.allocated else None, + handle.children.all()) + + return render('myrpki/asn_view.html', { 'form': form, + 'asn': obj, 'form': form, 'parent': parent_set }, request) # vim:sw=4 ts=8 expandtab diff --git a/portal-gui/rpkigui/settings.py b/portal-gui/rpkigui/settings.py index bc7a6a99..afabc19d 100644 --- a/portal-gui/rpkigui/settings.py +++ b/portal-gui/rpkigui/settings.py @@ -10,7 +10,7 @@ ADMINS = ( MANAGERS = ADMINS DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. -DATABASE_NAME = '/home/me/myrpki/rpkiop' # Or path to database file if using sqlite3. +DATABASE_NAME = '/home/melkins/myrpki/rpkiop' # Or path to database file if using sqlite3. DATABASE_USER = '' # Not used with sqlite3. DATABASE_PASSWORD = '' # Not used with sqlite3. DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. @@ -72,7 +72,7 @@ TEMPLATE_DIRS = ( # Always use forward slashes, even on Windows. # Don't forget to use absolute paths, not relative paths. #XXX - '/home/me/src/rpki/portal-gui/rpkigui/templates', + '/home/melkins/subvert-rpki.hactrn.net/portal-gui/rpkigui/templates', ) INSTALLED_APPS = ( @@ -99,7 +99,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( # # Top of directory tree where myrpki.conf, etc are stored for each resource holder -MYRPKI_DATA_DIR = '/home/me/myrpki' +MYRPKI_DATA_DIR = '/home/melkins/myrpki' # where to find the myrpki.py command line tool -MYRPKI_SRC_DIR = '/home/me/src/rpki/rpkid' +MYRPKI_SRC_DIR = '/home/melkins/subvert-rpki.hactrn.net/rpkid' diff --git a/portal-gui/rpkigui/templates/myrpki/asn_view.html b/portal-gui/rpkigui/templates/myrpki/asn_view.html new file mode 100644 index 00000000..587b73db --- /dev/null +++ b/portal-gui/rpkigui/templates/myrpki/asn_view.html @@ -0,0 +1,53 @@ +{% extends "base.html" %} + +{% block content %} +<p>Handle: <a href="/myrpki/">{{ request.session.handle }}</a> + +<h1>ASN View</h1> + +<table> + <tr> <td>ASN:</td><td>{{ asn }}</td> </tr> + {% if asn.parent %} + <tr> + <td>Suballocated from:</td> + <td><a href="{{ asn.parent.get_absolute_url }}">{{ asn.parent }}</a></td> + </tr> + {% endif %} + <tr> + <td>Received from:</td> + <td> + {% for p in parent %} + <a href="{{ p.get_absolute_url }}">{{ p.handle }}</a> + {% endfor %} + </td> + </tr> + <tr><td>Validity:</td><td>{{ asn.from_cert.all.0.not_before }} - {{ asn.from_cert.all.0.not_after }} </td></tr> + + {% if asn.allocated %} + <tr><td>Allocated:</td><td><a href="{{asn.allocated.get_absolute_url}}">{{asn.allocated.handle}}</a></td></tr> + {% endif %} +</table> + +{% if asn.children.count %} +<h2>Suballocations</h2> + +<ul> +{% for subaddr in asn.children.all %} +<li><a href="{{ subaddr.get_absolute_url }}">{{ subaddr }}</a> +{% endfor %} +</ul> + +{% endif %} + +{% if form %} +<h2>Edit</h2> +<form method="POST" action="{{ request.get_full_path }}"> + {{ form.as_p }} + <input type="submit"> +</form> +{% endif %} + +<p>Action: +<a href="{{asn.get_absolute_url}}/allocate">give to child</a> + +{% endblock %} diff --git a/portal-gui/rpkigui/templates/myrpki/dashboard.html b/portal-gui/rpkigui/templates/myrpki/dashboard.html index 692e204d..7485c700 100644 --- a/portal-gui/rpkigui/templates/myrpki/dashboard.html +++ b/portal-gui/rpkigui/templates/myrpki/dashboard.html @@ -2,8 +2,8 @@ {% block content %} <p>Handle: {{ request.session.handle }} -| <a href="/myrpki/conf/export">export</a> -| <a href="/myrpki/conf/list">change</a> +| <a href="/myrpki/conf/export">export identity</a> +| <a href="/myrpki/conf/list">select</a> <div style="border: inset"> <h1 style="text-align: center">Parents</h1> @@ -11,23 +11,28 @@ <ul> {% for parent in request.session.handle.parents.all %} <li><a href="{{ parent.get_absolute_url }}">{{ parent.handle }}</a> - -{% if parent.resources.count %} -<p>Accepted resources: -<ul> +<p> +<table style="width:100%; border: solid 1px"> +<tr><th>Accepted Resource</th><th>Not Before</th><th>Not After</th></tr> {% for cert in parent.resources.all %} {% for asn in cert.asn.all %} -<li><a href="{{ asn.get_absolute_url }}">{{ asn }}</a> +<tr><td><a href="{{ asn.get_absolute_url }}">{{ asn }}</a></td> +<td style='text-align: center'>{{cert.not_before}}</td> +<td style='text-align: center'>{{cert.not_after}}</td> +</tr> {% endfor %} {% for address in cert.address_range.all %} -<li><a href="{{ address.get_absolute_url }}">{{ address }}</a> +<tr><td> +<a href="{{ address.get_absolute_url }}">{{ address }}</a> +</td><td style='text-align: center'>{{cert.not_before}}</td> +<td style='text-align: center'>{{cert.not_after}}</td> +</tr> {% endfor %} {% endfor %} <!--certs--> -</ul> -{% endif %} +</table> {% endfor %} </ul> @@ -60,57 +65,48 @@ <div style="border: outset"> <h1 style="text-align: center">My ROA [request]s</h1> -{% if request.session.handle.roas.count %} + <table style="border: groove; width: 100%" border="1"> <tr> <th>Prefix</th> <th>ASN</th> -<th>Max Len</th> -<th>Comments</th> -<th></th> </tr> + {% for roa in request.session.handle.roas.all %} +<tr><td> {% for address_range in roa.prefix.all %} -<tr> -<td>{{ address_range }}</td> +<li><a href="{{ address_range.get_absolute_url }}">{{ address_range }}</a> +{% endfor %} +</td> <td>{{ roa.asn }}</td> -<td>{{ roa.max_len }}</td> -<td>{{ roa.comments }}</td> -<td><a href="{{ roa.get_absolute_url }}">edit</a></td> </tr> {% endfor %} -{% endfor %} </table> -{% else %} -<p> --- none -- -{% endif %} -<p><a href="/myrpki/roa/">[add]</a> -</div> +</div><!-- roas --> <div style="border: outset"> <h1 style="text-align: center">Unallocated Resources</h1> {% if asns or ars %} -<ul> +<table> {% for asn in asns %} - <li><a href="{{ asn.get_absolute_url }}">{{ asn }}</a> +<tr> +<td><a href="{{ asn.get_absolute_url }}">{{ asn }}</a></td> +<td style='padding-left: 2em'> <a href="{{ asn.get_absolute_url }}/allocate">give</a></td> +</tr> {% endfor %} {% for addr in ars %} - <li><a href="{{ addr.get_absolute_url }}">{{ addr }}</a> +<tr> +<td><a href="{{ addr.get_absolute_url }}">{{ addr }}</a></td> +<td style='padding-left: 2em'><a href="{{ addr.get_absolute_url }}/allocate">give</a> +| <a href="{{ addr.get_absolute_url }}/split">split</a> +| <a href="{{ addr.get_absolute_url }}/roa">roa</a></td> +</tr> {% endfor %} +</table> {% else %} <p>-- none -- {% endif %} - <!-- -<li>Address range 172.17.2.0-172.17.255.255 - | <a href="#">subdivide</a> | - <a href="#">give to child</a> | - <a href="#">issue roa</a> -<li>ASN 1 | <a href="#">give to child</a> -<li>ASNs 3-100 | <a href="#">subdivide</a> | - <a href="#">give to child</a> - --> </ul> </div> </span> diff --git a/portal-gui/rpkigui/templates/myrpki/prefix_view.html b/portal-gui/rpkigui/templates/myrpki/prefix_view.html new file mode 100644 index 00000000..f700ef72 --- /dev/null +++ b/portal-gui/rpkigui/templates/myrpki/prefix_view.html @@ -0,0 +1,58 @@ +{% extends "base.html" %} + +{% block content %} +<p>Handle: <a href="/myrpki/">{{ request.session.handle }}</a> + +<h1>Prefix View</h1> + +<table> + <tr> <td>Range:</td><td>{{ addr }}</td> </tr> + {% if addr.parent %} + <tr> + <td>Suballocated from:</td> + <td><a href="{{ addr.parent.get_absolute_url }}">{{ addr.parent }}</a></td> + </tr> + {% endif %} + <tr> + <td>Received from:</td> + <td> + {% for p in parent %} + <a href="{{ p.get_absolute_url }}">{{ p.handle }}</a> + {% endfor %} + </td> + </tr> + <tr><td>Validity:</td><td>{{ addr.from_cert.all.0.not_before }} - {{ addr.from_cert.all.0.not_after }} </td></tr> + + {% if addr.allocated %} + <tr><td>Allocated:</td><td><a href="{{addr.allocated.get_absolute_url}}">{{addr.allocated.handle}}</a></td></tr> + {% endif %} + + <tr><td>Allowed to orignate:</td><td>{{addr.asns}}</td></tr> + +</table> + +{% if addr.children.count %} +<h2>Suballocations</h2> + +<ul> +{% for subaddr in addr.children.all %} +<li><a href="{{ subaddr.get_absolute_url }}">{{ subaddr }}</a> +{% endfor %} +</ul> + +{% endif %} + +{% if form %} +<h2>Edit</h2> +<form method="POST" action="{{ request.get_full_path }}"> + {{ form.as_p }} + <input type="submit"> +</form> +{% endif %} + +<p>Action: +<a href="{{addr.get_absolute_url}}/split">split</a> | +<a href="{{addr.get_absolute_url}}/allocate">give to child</a> | +<a href="{{addr.get_absolute_url}}/roa">edit allowed originators</a> + +{% endblock %} diff --git a/portal-gui/rpkigui/templates/myrpki/resource_view.html b/portal-gui/rpkigui/templates/myrpki/resource_view.html index d230e66f..f8219136 100644 --- a/portal-gui/rpkigui/templates/myrpki/resource_view.html +++ b/portal-gui/rpkigui/templates/myrpki/resource_view.html @@ -2,7 +2,7 @@ {% block content %} <p>Handle: <a href="/myrpki/">{{ request.session.handle }}</a> -<h1>Resource Range View</h1> +<h1>{{resource_type.capitalize}} View</h1> <table> <tr> <td>Range:</td><td>{{ addr }}</td> @@ -15,6 +15,7 @@ {% endfor %} </td> </tr> + <tr><td>Validity:</td><td>{{ addr.from_cert.all.0.not_before }} - {{ addr.from_cert.all.0.not_after }} </td></tr> {% if addr.parent %} <tr> <td>Suballocated from:</td> @@ -39,9 +40,24 @@ {% endif %} +<div> +<h2>Delegate</h2> + +<p>Child: +<!-- <form action="{{ request.url }}" method="post"> {{ form.as_p }} <p><input type="submit" value="Submit"> </form> +--> +</div> + +{% if resource_type == 'prefix' %} +<div> +<h2>Originate</h2> +<p>ASNs allowed to originate routes for this prefix: +</div> + +{% endif %} {% endblock %} diff --git a/portal-gui/scripts/list_resources b/portal-gui/scripts/helper index 07df3541..7fd9e8c0 100755 --- a/portal-gui/scripts/list_resources +++ b/portal-gui/scripts/helper @@ -1,5 +1,6 @@ #!/bin/sh +NAME=`basename $0` BASE_PATH=`dirname $0`/../.. export PYTHONPATH=$BASE_PATH/rpkid:$BASE_PATH/portal-gui export DJANGO_SETTINGS_MODULE=rpkigui.settings -python `dirname $0`/list_resources.py +python `dirname $0`/${NAME}.py $* diff --git a/portal-gui/scripts/list_resources.py b/portal-gui/scripts/list_resources.py index ae95228b..acd97847 100755 --- a/portal-gui/scripts/list_resources.py +++ b/portal-gui/scripts/list_resources.py @@ -1,6 +1,9 @@ #!/usr/bin/env python +import sys import os +from datetime import datetime + from rpki.myrpki import EntityDB, CA import rpki.config import rpki.x509 @@ -12,20 +15,6 @@ import rpki.ipaddrs from rpkigui.myrpki import models -class ReceivedResources(object): - def __init__(self, self_handle, parent_handle, asn, ipv4, ipv6, uri, not_before, not_after): - self.self_handle = self_handle - self.parent_handle = parent_handle - self.asn = asn - self.ipv4 = ipv4 - self.ipv6 = ipv6 - self.uri = uri - self.not_before = not_before - self.not_after = not_after - - def __str__(self): - return "%s's received resources from parent %s" % (self.self_handle, self.parent_handle, ) - def query_rpkid(handle=None): """Fetch our received resources from the local rpkid using the myrpki.conf in the current directory.""" cfg_file = os.getenv("MYRPKI_CONF", "myrpki.conf") @@ -46,87 +35,94 @@ def query_rpkid(handle=None): url = rpkid_base + "left-right", debug = True)) - print 'calling rpkid...' + print 'calling rpkid... for self_handle=', handle rpkid_reply = call_rpkid( #rpki.left_right.parent_elt.make_pdu(action="list", tag="parents", self_handle=handle), #rpki.left_right.list_roa_requests_elt.make_pdu(tag='roas', self_handle=handle), + rpki.left_right.child_elt.make_pdu(action="list", tag="children", + self_handle = handle), rpki.left_right.list_received_resources_elt.make_pdu(tag = "resources", self_handle = handle)) print 'done' - resources = [] - for x in rpkid_reply: - if isinstance(x, rpki.left_right.parent_elt): - print x.parent_handle, x.sia_base, x.sender_name, x.recipient_name, \ - x.peer_contact_uri - #elif isinstance(x, rpki.left_right.list_roa_requests_elt): - # print x.asn, x.ipv4, x.ipv6 - if isinstance(x, rpki.left_right.list_received_resources_elt): - resources.append(ReceivedResources(self_handle=handle, - parent_handle=x.parent_handle, - asn=rpki.resource_set.resource_set_as(x.asn), - ipv4=rpki.resource_set.resource_set_ipv4(x.ipv4), - ipv6=rpki.resource_set.resource_set_ipv6(x.ipv6), - uri=x.uri, - not_after=x.notAfter, - not_before=x.notBefore)) - return resources - -x = query_rpkid() -for y in x: - conf = models.Conf.objects.filter(handle=y.self_handle)[0] + return rpkid_reply - parent_set = conf.parents.filter(handle=y.parent_handle) - if not parent_set: - print 'have not yet seen parent %s, creating...' % (y.parent_handle, ) - # have not seen this parent before - parent = models.Parent(conf=conf, handle=y.parent_handle) - parent.save() +for pdu in query_rpkid(None if len(sys.argv) == 1 else sys.argv[1]): + conf_set = models.Conf.objects.filter(handle=pdu.self_handle) + if conf_set.count(): + conf = conf_set[0] else: - parent = parent_set[0] + print 'creating new conf for %s' % (pdu.self_handle,) + conf = models.Conf.objects.create(handle=pdu.self_handle) - # have we seen this resource cert before? - cert_set = conf.resources.filter(uri=y.uri) - if cert_set.count() == 0: - # no - cert = models.ResourceCert(uri=uri, parent=parent, not_before=x.not_before, - not_after=x.not_after) - else: - # yes - cert = cert_set[0] + #if isinstance(pdu, rpki.left_right.parent_elt): +# print x.parent_handle, x.sia_base, x.sender_name, x.recipient_name, \ +# x.peer_contact_uri + if isinstance(pdu, rpki.left_right.child_elt): + # have we seen this parent before? + child_set = conf.children.filter(handle=pdu.child_handle) + if not child_set: + print 'creating new child %s' % (pdu.child_handle,) + child = models.Child(conf=conf, handle=pdu.child_handle) + child.save() + #elif isinstance(x, rpki.left_right.list_roa_requests_elt): + # print x.asn, x.ipv4, x.ipv6 + elif isinstance(pdu, rpki.left_right.list_received_resources_elt): + # have we seen this parent before? + parent_set = conf.parents.filter(handle=pdu.parent_handle) + if not parent_set: + parent = models.Parent(conf=conf, handle=pdu.parent_handle) + parent.save() + else: + parent = parent_set[0] + + not_before = datetime.strptime(pdu.notBefore, "%Y-%m-%dT%H:%M:%SZ") + not_after = datetime.strptime(pdu.notAfter, "%Y-%m-%dT%H:%M:%SZ") + + # have we seen this resource cert before? + cert_set = parent.resources.filter(uri=pdu.uri) + if cert_set.count() == 0: + cert = models.ResourceCert(uri=pdu.uri, parent=parent, + not_before=not_before, not_after=not_after) + else: + cert = cert_set[0] + # update timestamps since it could have been modified + cert.not_before = not_before + cert.not_after = not_after + cert.save() - for asn in y.asn: - # see if this resource is already part of the cert - if cert.asn.get(lo=asn.min, hi=asn.max) is None: - # ensure that this range wasn't previously seen from another of our parents - for v in models.Asn.objects.filter(lo=asn.min, hi=asn.max): - # determine if this resource is delegated from another parent as well - if v.from_cert.filter(parent__in=conf.parents.all()).count(): - cert.asn.add(v) - break - else: - print 'could not find ASN %s in known set' % ( asn, ) - cert.asn.create(lo=asn.min, hi=asn.max) - cert.save() + for asn in rpki.resource_set.resource_set_as(pdu.asn): + # see if this resource is already part of the cert + if cert.asn.filter(lo=asn.min, hi=asn.max).count() == 0: + # ensure this range wasn't seen from another of our parents + for v in models.Asn.objects.filter(lo=asn.min, hi=asn.max): + # determine if resource is delegated from another parent + if v.from_cert.filter(parent__in=conf.parents.all()).count(): + cert.asn.add(v) + break + else: + print 'could not find ASN %s in known set' % ( asn, ) + cert.asn.create(lo=asn.min, hi=asn.max) + cert.save() - # IPv4/6 - not separated in the django db - def add_missing_address(addr_set): - for ip in addr_set: - lo=str(ip.min) - hi=str(ip.max) - if cert.address_range.get(lo=lo, hi=hi) is None: - # ensure that this range wasn't previously seen from another of our parents - for v in models.AddressRange.objects.filter(lo=lo, hi=hi): - # determine if this resource is delegated from another parent as well - if v.from_cert.filter(parent__in=conf.parents.all()).count(): - cert.address_range.add(v) - break - else: - print 'could not find address range %s in known set' % ( ip, ) - cert.address_range.create(lo=lo, hi=hi) - cert.save() + # IPv4/6 - not separated in the django db + def add_missing_address(addr_set): + for ip in addr_set: + lo=str(ip.min) + hi=str(ip.max) + if cert.address_range.filter(lo=lo, hi=hi).count() == 0: + # ensure that this range wasn't previously seen from another of our parents + for v in models.AddressRange.objects.filter(lo=lo, hi=hi): + # determine if this resource is delegated from another parent as well + if v.from_cert.filter(parent__in=conf.parents.all()).count(): + cert.address_range.add(v) + break + else: + print 'could not find address range %s in known set' % (ip,) + cert.address_range.create(lo=lo, hi=hi) + cert.save() - add_missing_address(y.ipv4) - add_missing_address(y.ipv6) + add_missing_address(rpki.resource_set.resource_set_ipv4(pdu.ipv4)) + add_missing_address(rpki.resource_set.resource_set_ipv6(pdu.ipv6)) # vim:sw=4 expandtab ts=4 diff --git a/portal-gui/scripts/load_csv b/portal-gui/scripts/load_csv new file mode 120000 index 00000000..f0521c79 --- /dev/null +++ b/portal-gui/scripts/load_csv @@ -0,0 +1 @@ +helper
\ No newline at end of file diff --git a/portal-gui/scripts/load_csv.py b/portal-gui/scripts/load_csv.py new file mode 100755 index 00000000..e34039db --- /dev/null +++ b/portal-gui/scripts/load_csv.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# +# Helper script to load existing data from csv into the Django DB. +# Primarly useful for the initial load, as the GUI does not sync changes +# made directly to the csv files back into the database. +# +# This script should be run from the directory containing the myrpki.conf +# for the handle you are loading data +# + +import os +import csv + +import rpki +import rpki.resource_set +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 + +cfg_file = os.getenv("MYRPKI_CONF", "myrpki.conf") +cfg = rpki.config.parser(cfg_file, "myrpki") +handle = cfg.get('handle') +asn_csv = cfg.get('asn_csv') +prefix_csv = cfg.get('prefix_csv') +roa_csv = cfg.get('roa_csv') + +conf = models.Conf.objects.get(handle=handle) + +for asn, child_handle in csv_reader(asn_csv, columns=2): + child = conf.children.get(conf=conf, handle=child_handle) + asn = models.Asn.objects.get(lo=asn, hi=asn, + from_cert__parent__in=conf.parents.all()) + child.asn.add(asn) + +def prefix_to_range(s): + """returns a tuple of (lo,hi) of the address range specified by a prefix""" + net, bits = prefix.split('/') + addr = rpki.resource_set.resource_range_ipv4.make_prefix(rpki.ipaddrs.v4addr(net), int(bits)) + return str(addr.min), str(addr.max) + +for prefix, child_handle in csv_reader(prefix_csv, columns=2): + child = conf.children.get(conf=conf, handle=child_handle) + addr = prefix_to_range(prefix) + obj = models.AddressRange.objects.get(lo=addr[0], hi=addr[1], + from_cert__parent__in=conf.parents.all()) + child.address_range.add(obj) + +for prefix, asn, group in csv_reader(roa_csv, columns=3): + addr = prefix_to_range(prefix) + obj = models.AddressRange.objects.get(lo=addr[0], hi=addr[1], + 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) + obj.save() + update_roas(conf, obj) |