diff options
Diffstat (limited to 'rpki')
-rw-r--r-- | rpki/async.py | 8 | ||||
-rw-r--r-- | rpki/gui/app/forms.py | 5 | ||||
-rw-r--r-- | rpki/gui/app/glue.py | 2 | ||||
-rw-r--r-- | rpki/gui/app/templates/app/roarequest_multi_form.html | 2 | ||||
-rw-r--r-- | rpki/gui/app/views.py | 132 | ||||
-rw-r--r-- | rpki/irdb/zookeeper.py | 4 |
6 files changed, 102 insertions, 51 deletions
diff --git a/rpki/async.py b/rpki/async.py index f2abd05d..5b3d35b1 100644 --- a/rpki/async.py +++ b/rpki/async.py @@ -352,8 +352,9 @@ class sync_wrapper(object): err = None fin = False - def __init__(self, func): + def __init__(self, func, disable_signal_handlers = False): self.func = func + self.disable_signal_handlers = disable_signal_handlers def cb(self, res = None): """ @@ -389,7 +390,10 @@ class sync_wrapper(object): self.eb(e) event_defer(thunk) - event_loop() + if self.disable_signal_handlers: + event_loop(catch_signals = ()) + else: + event_loop() if not self.fin: logger.warning("%r event_loop terminated without callback or errback", self) if self.err is None: diff --git a/rpki/gui/app/forms.py b/rpki/gui/app/forms.py index fffac1be..f173c15d 100644 --- a/rpki/gui/app/forms.py +++ b/rpki/gui/app/forms.py @@ -106,6 +106,11 @@ class ImportClientForm(forms.Form): class ImportCSVForm(forms.Form): csv = forms.FileField(label='CSV file') + ignore_missing_children = forms.BooleanField( + label='Ignore missing children', + help_text='Discard data for children that are not currently in the IRDB', + required=False + ) class UserCreateForm(forms.Form): diff --git a/rpki/gui/app/glue.py b/rpki/gui/app/glue.py index 8a008447..bfade6d8 100644 --- a/rpki/gui/app/glue.py +++ b/rpki/gui/app/glue.py @@ -77,7 +77,7 @@ def list_received_resources(log, conf): foreign keys. """ - z = Zookeeper(handle=conf.handle) + z = Zookeeper(handle=conf.handle, disable_signal_handlers=True) req = Element(tag_msg, nsmap=nsmap, type="query", version=version) SubElement(req, tag_list_received_resources, self_handle=conf.handle) pdus = z.call_rpkid(req) diff --git a/rpki/gui/app/templates/app/roarequest_multi_form.html b/rpki/gui/app/templates/app/roarequest_multi_form.html index 06d07943..0fbc49ae 100644 --- a/rpki/gui/app/templates/app/roarequest_multi_form.html +++ b/rpki/gui/app/templates/app/roarequest_multi_form.html @@ -14,7 +14,7 @@ {{ form.prefix }} {{ form.max_prefixlen }} {{ form.asn }} - <label class="checkbox inline span1">{{ form.DELETE }} Delete</label> + {# <label class="checkbox inline span1">{{ form.DELETE }} Delete</label> #} {% if form.errors %}<span class="help-inline">{{ form.errors }}</span>{% endif %} {% if form.non_field_errors %}<span class="help-inline">{{ form.non_field_errors }}</span>{% endif %} </div> diff --git a/rpki/gui/app/views.py b/rpki/gui/app/views.py index a9c038f5..dfd36dbb 100644 --- a/rpki/gui/app/views.py +++ b/rpki/gui/app/views.py @@ -47,6 +47,7 @@ from rpki.resource_set import (resource_range_as, resource_range_ip, roa_prefix_ipv4) from rpki import sundial import rpki.exceptions +import rpki.csv_utils from rpki.gui.cacheview.models import ROA from rpki.gui.routeview.models import RouteOrigin @@ -324,12 +325,26 @@ def import_asns(request): f = NamedTemporaryFile(prefix='asns', suffix='.csv', delete=False) f.write(request.FILES['csv'].read()) f.close() - z = Zookeeper(handle=conf.handle) - z.load_asns(f.name) - z.run_rpkid_now() - os.unlink(f.name) - messages.success(request, 'Successfully imported AS delgations from CSV file.') - return redirect(dashboard) + z = Zookeeper(handle=conf.handle, disable_signal_handlers=True) + try: + z.load_asns( + f.name, + ignore_missing_children=form.cleaned_data['ignore_missing_children'] + ) + except rpki.irdb.models.Child.DoesNotExist: + messages.error( + request, + 'CSV file contains children not found in the IRDB' + ) + except rpki.csv_utils.BadCSVSyntax as e: + messages.error(request, + 'CSV file has an invalid syntax: %s' % (e,)) + else: + z.run_rpkid_now() + messages.success(request, 'Successfully imported AS delgations from CSV file.') + return redirect(dashboard) + finally: + os.unlink(f.name) else: form = forms.ImportCSVForm() return render(request, 'app/import_resource_form.html', { @@ -359,12 +374,23 @@ def import_prefixes(request): f = NamedTemporaryFile(prefix='prefixes', suffix='.csv', delete=False) f.write(request.FILES['csv'].read()) f.close() - z = Zookeeper(handle=conf.handle) - z.load_prefixes(f.name) - z.run_rpkid_now() - os.unlink(f.name) - messages.success(request, 'Successfully imported prefix delegations from CSV file.') - return redirect(dashboard) + z = Zookeeper(handle=conf.handle, disable_signal_handlers=True) + try: + z.load_prefixes( + f.name, + ignore_missing_children=form.cleaned_data['ignore_missing_children'] + ) + except rpki.irdb.models.Child.DoesNotExist: + messages.error(request, 'CSV file contains children not found in the IRDB') + except rpki.csv_utils.BadCSVSyntax as e: + messages.error(request, + 'CSV file has an invalid syntax: %s' % (e,)) + else: + z.run_rpkid_now() + messages.success(request, 'Successfully imported AS delgations from CSV file.') + return redirect(dashboard) + finally: + os.unlink(f.name) else: form = forms.ImportCSVForm() return render(request, 'app/import_resource_form.html', { @@ -437,7 +463,11 @@ def child_add_prefix(request, pk): version = 'IPv%d' % r.version child.address_ranges.create(start_ip=str(r.min), end_ip=str(r.max), version=version) - Zookeeper(handle=conf.handle, logstream=logstream).run_rpkid_now() + Zookeeper( + handle=conf.handle, + logstream=logstream, + disable_signal_handlers=True + ).run_rpkid_now() return http.HttpResponseRedirect(child.get_absolute_url()) else: form = forms.AddNetForm(child=child) @@ -456,7 +486,11 @@ def child_add_asn(request, pk): asns = form.cleaned_data.get('asns') r = resource_range_as.parse_str(asns) child.asns.create(start_as=r.min, end_as=r.max) - Zookeeper(handle=conf.handle, logstream=logstream).run_rpkid_now() + Zookeeper( + handle=conf.handle, + logstream=logstream, + disable_signal_handlers=True + ).run_rpkid_now() return http.HttpResponseRedirect(child.get_absolute_url()) else: form = forms.AddASNForm(child=child) @@ -486,7 +520,11 @@ def child_edit(request, pk): # remove AS & prefixes that are not selected in the form models.ChildASN.objects.filter(child=child).exclude(pk__in=form.cleaned_data.get('as_ranges')).delete() models.ChildNet.objects.filter(child=child).exclude(pk__in=form.cleaned_data.get('address_ranges')).delete() - Zookeeper(handle=conf.handle, logstream=log).run_rpkid_now() + Zookeeper( + handle=conf.handle, + logstream=logstream, + disable_signal_handlers=True + ).run_rpkid_now() return http.HttpResponseRedirect(child.get_absolute_url()) else: form = form_class(initial={ @@ -666,27 +704,24 @@ def roa_create_multi(request): v = [] rng.chop_into_prefixes(v) init.extend([{'asn': asn, 'prefix': str(p)} for p in v]) - formset = formset_factory(forms.ROARequestFormFactory(conf), can_delete=True)(initial=init) + extra = 0 if init else 1 + formset = formset_factory(forms.ROARequestFormFactory(conf), extra=extra)(initial=init) elif request.method == 'POST': - formset = formset_factory(forms.ROARequestFormFactory(conf), extra=0, can_delete=True)(request.POST, request.FILES) + formset = formset_factory(forms.ROARequestFormFactory(conf), extra=0)(request.POST, request.FILES) if formset.is_valid(): routes = [] v = [] - # as of Django 1.4.5 we still can't use formset.cleaned_data - # because deleted forms are not excluded, which causes an - # AttributeError to be raised. for form in formset: - if hasattr(form, 'cleaned_data') and form.cleaned_data: # exclude empty forms - asn = form.cleaned_data.get('asn') - rng = resource_range_ip.parse_str(form.cleaned_data.get('prefix')) - max_prefixlen = int(form.cleaned_data.get('max_prefixlen')) - # FIXME: This won't do the right thing in the event that a - # route is covered by multiple ROAs created in the form. - # You will see duplicate entries, each with a potentially - # different validation status. - routes.extend(get_covered_routes(rng, max_prefixlen, asn)) - v.append({'prefix': str(rng), 'max_prefixlen': max_prefixlen, - 'asn': asn}) + asn = form.cleaned_data['asn'] + rng = resource_range_ip.parse_str(form.cleaned_data['prefix']) + max_prefixlen = int(form.cleaned_data['max_prefixlen']) + # FIXME: This won't do the right thing in the event that a + # route is covered by multiple ROAs created in the form. + # You will see duplicate entries, each with a potentially + # different validation status. + routes.extend(get_covered_routes(rng, max_prefixlen, asn)) + v.append({'prefix': str(rng), 'max_prefixlen': max_prefixlen, + 'asn': asn}) # if there were no rows, skip the confirmation step if v: formset = formset_factory(forms.ROARequestConfirm, extra=0)(initial=v) @@ -718,7 +753,7 @@ def roa_create_confirm(request): roa.prefixes.create(version=v, prefix=str(rng.min), prefixlen=rng.prefixlen(), max_prefixlen=max_prefixlen) - Zookeeper(handle=conf.handle, logstream=log).run_rpkid_now() + Zookeeper(handle=conf.handle, logstream=log, disable_signal_handlers=True).run_rpkid_now() return http.HttpResponseRedirect(reverse(dashboard)) # What should happen when the submission form isn't valid? For now # just fall through and redirect back to the ROA creation form @@ -748,7 +783,7 @@ def roa_create_multi_confirm(request): roa.prefixes.create(version=v, prefix=str(rng.min), prefixlen=rng.prefixlen(), max_prefixlen=max_prefixlen) - Zookeeper(handle=conf.handle, logstream=log).run_rpkid_now() + Zookeeper(handle=conf.handle, logstream=log, disable_signal_handlers=True).run_rpkid_now() return redirect(dashboard) # What should happen when the submission form isn't valid? For now # just fall through and redirect back to the ROA creation form @@ -768,7 +803,7 @@ def roa_delete(request, pk): roa = get_object_or_404(conf.roas, pk=pk) if request.method == 'POST': roa.delete() - Zookeeper(handle=conf.handle).run_rpkid_now() + Zookeeper(handle=conf.handle, disable_signal_handlers=True).run_rpkid_now() return redirect(reverse(dashboard)) ### Process GET ### @@ -825,12 +860,19 @@ def roa_import(request): tmp = tempfile.NamedTemporaryFile(suffix='.csv', prefix='roas', delete=False) tmp.write(request.FILES['csv'].read()) tmp.close() - z = Zookeeper(handle=request.session['handle']) - z.load_roa_requests(tmp.name) - z.run_rpkid_now() - os.unlink(tmp.name) - messages.success(request, 'Successfully imported ROAs.') - return redirect(dashboard) + z = Zookeeper(handle=request.session['handle'], + disable_signal_handlers=True) + try: + z.load_roa_requests(tmp.name) + except rpki.csv_utils.BadCSVSyntax as e: + messages.error(request, + 'CSV has bad syntax: %s' % (e,)) + else: + z.run_rpkid_now() + messages.success(request, 'Successfully imported ROAs.') + return redirect(dashboard) + finally: + os.unlink(tmp.name) else: form = forms.ImportCSVForm() return render(request, 'app/import_resource_form.html', { @@ -870,7 +912,7 @@ def ghostbuster_delete(request, pk): form = forms.Empty(request.POST, request.FILES) if form.is_valid(): obj.delete() - Zookeeper(handle=conf.handle, logstream=logstream).run_rpkid_now() + Zookeeper(handle=conf.handle, logstream=logstream, disable_signal_handlers=True).run_rpkid_now() return http.HttpResponseRedirect(reverse(dashboard)) else: form = forms.Empty(request.POST, request.FILES) @@ -892,7 +934,7 @@ def ghostbuster_create(request): obj = form.save(commit=False) obj.vcard = glue.ghostbuster_to_vcard(obj) obj.save() - Zookeeper(handle=conf.handle, logstream=logstream).run_rpkid_now() + Zookeeper(handle=conf.handle, logstream=logstream, disable_signal_handlers=True).run_rpkid_now() return http.HttpResponseRedirect(reverse(dashboard)) else: form = forms.GhostbusterRequestForm(conf=conf) @@ -912,7 +954,7 @@ def ghostbuster_edit(request, pk): obj = form.save(commit=False) obj.vcard = glue.ghostbuster_to_vcard(obj) obj.save() - Zookeeper(handle=conf.handle, logstream=logstream).run_rpkid_now() + Zookeeper(handle=conf.handle, logstream=logstream, disable_signal_handlers=True).run_rpkid_now() return http.HttpResponseRedirect(reverse(dashboard)) else: form = forms.GhostbusterRequestForm(conf=conf, instance=obj) @@ -1356,7 +1398,7 @@ class RouterDeleteView(DeleteView): def delete(self, request, *args, **kwargs): # override the delete hook so that we can nudge rpkid after the object is gone r = super(RouterDeleteView, self).delete(request, *args, **kwargs) - Zookeeper(handle=request.session['handle']).run_rpkid_now() + Zookeeper(handle=request.session['handle'], disable_signal_handlers=True).run_rpkid_now() return r @@ -1375,7 +1417,7 @@ class RouterImportView(FormView): delete=False) tmpf.write(form.cleaned_data['xml'].read()) tmpf.close() - z = Zookeeper(handle=conf.handle) + z = Zookeeper(handle=conf.handle, disable_signal_handlers=True) z.add_router_certificate_request(tmpf.name) z.run_rpkid_now() os.remove(tmpf.name) diff --git a/rpki/irdb/zookeeper.py b/rpki/irdb/zookeeper.py index bcdfaf7e..9a843a4e 100644 --- a/rpki/irdb/zookeeper.py +++ b/rpki/irdb/zookeeper.py @@ -192,7 +192,7 @@ class Zookeeper(object): show_xml = None - def __init__(self, cfg = None, handle = None, logstream = None): + def __init__(self, cfg = None, handle = None, logstream = None, disable_signal_handlers = False): if cfg is None: cfg = rpki.config.parser() @@ -203,6 +203,7 @@ class Zookeeper(object): self.cfg = cfg self.logstream = logstream + self.disable_signal_handlers = disable_signal_handlers self.run_rpkid = cfg.getboolean("run_rpkid", section = myrpki_section) self.run_pubd = cfg.getboolean("run_pubd", section = myrpki_section) @@ -1066,7 +1067,6 @@ class Zookeeper(object): if not suppress_error_check: self.check_error_report(r_msg) - return r_msg |