aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Elkins <melkins@tislabs.com>2010-07-02 22:59:55 +0000
committerMichael Elkins <melkins@tislabs.com>2010-07-02 22:59:55 +0000
commit5f0e898ff4cc99e1b2843118e6d6ef2e608ecd54 (patch)
tree926dcc26dfa6a089ef4bd4452d76dadde1ad3925
parent27df7ae98cfdc06b34e3140963ddc957d08d9442 (diff)
portal-gui updates
svn path=/portal-gui/README; revision=3310
-rw-r--r--portal-gui/README149
-rw-r--r--portal-gui/rpkigui/myrpki/asnset.py23
-rw-r--r--portal-gui/rpkigui/myrpki/forms.py74
-rw-r--r--portal-gui/rpkigui/myrpki/glue.py72
-rw-r--r--portal-gui/rpkigui/myrpki/models.py34
-rw-r--r--portal-gui/rpkigui/myrpki/urls.py13
-rw-r--r--portal-gui/rpkigui/myrpki/views.py284
-rw-r--r--portal-gui/rpkigui/settings.py8
-rw-r--r--portal-gui/rpkigui/templates/myrpki/asn_view.html53
-rw-r--r--portal-gui/rpkigui/templates/myrpki/dashboard.html72
-rw-r--r--portal-gui/rpkigui/templates/myrpki/prefix_view.html58
-rw-r--r--portal-gui/rpkigui/templates/myrpki/resource_view.html18
-rwxr-xr-xportal-gui/scripts/helper (renamed from portal-gui/scripts/list_resources)3
-rwxr-xr-xportal-gui/scripts/list_resources.py162
l---------portal-gui/scripts/load_csv1
-rwxr-xr-xportal-gui/scripts/load_csv.py61
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)