Evvverything!

This commit is contained in:
Sebastian Lohff 2017-03-25 04:11:37 +01:00
parent 8dbd77dc79
commit 81bbcda8eb
17 changed files with 319 additions and 1 deletions

0
api/__init__.py Normal file
View File

3
api/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
api/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class ApiConfig(AppConfig):
name = 'api'

View File

3
api/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
api/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

9
api/urls.py Normal file
View File

@ -0,0 +1,9 @@
from django.conf.urls import url
from . import views as api_views
urlpatterns = [
url(r'asblock/free-as/$', api_views.asblockFreeAS, name='asblock-free-as'),
url(r'asblock/free-subnet/$', api_views.freeSubnet, name='inetnum-free-subnet'),
]

107
api/views.py Normal file
View File

@ -0,0 +1,107 @@
#from django.shortcuts import render
from django.http import JsonResponse
from django.core.exceptions import ValidationError
from django.contrib.auth.views import login_required
from whoisdb.models import ASBlock, ASNumber, InetNum
@login_required
def asblockFreeAS(request):
ret = {
"success": False,
"errorMsg": None,
"number": -1,
}
try:
blockName = request.GET.get('block', None)
if not blockName:
raise ValidationError("No block given")
try:
mnts = request.user.maintainer_set.all()
block = ASBlock.objects.filter(mnt_by__in=mnts).distinct().get(handle=blockName)
if block.asblock_set.count() > 0:
raise ValidationError("AS Block already has sub AS Blocks")
if block.asnumber_set.count() > 0:
num = block.asnumber_set.order_by("-number")[0].number + 1
if num > block.asEnd:
num = None
for n in range(block.asBegin, block.asEnd+1):
try:
ASNumber.objects.get(number=n)
except ASNumber.DoesNotExist:
num = n
break
if not num:
raise ValidationError("No free AS Number in block")
ret["number"] = num
else:
ret["number"] = block.asBegin
except ASBlock.DoesNotExist:
raise ValidationError("Could not get AS Block")
ret["success"] = True
except ValidationError as e:
ret["errorMsg"] = e.message
return JsonResponse(ret)
@login_required
def freeSubnet(request):
ret = {
"success": False,
"errorMsg": None,
"network": None,
}
try:
parentRangeName = request.GET.get('parentRange', None)
if not parentRangeName:
raise ValidationError("No subnet given")
parentRange = None
try:
mnts = request.user.maintainer_set.all()
parentRange = InetNum.objects.filter(mnt_by__in=mnts).distinct().get(handle=parentRangeName)
except InetNum.DoesNotExist:
raise ValidationError("Parent range does not exist / is not maintained by you")
prefixLen = 0
try:
prefixLen = int(request.GET.get("prefixLen", 27))
if prefixLen < 8 or \
(parentRange.protocol == InetNum.IPv4 and prefixLen > 32) or \
(parentRange.protocol == InetNum.IPv6 and prefixLen > 128):
raise ValidationError("Given prefix length is out of range")
except ValueError:
raise ValidationError("PrefixLen is not a number")
nets = list(parentRange.getNetwork().subnets())
for subRange in parentRange.inetnum_set.all():
newNet = None
for net in nets:
if subRange.getNetwork().network_address in net:
newNet = net
if not newNet:
# critical error, we want a 500 here
raise ValueError("Subnet not in range")
nets.remove(newNet)
nets.extend(newNet.address_exclude(subRange.getNetwork()))
nets = sorted(nets)
usableNet = None
for net in nets:
if net.prefixlen <= prefixLen:
usableNet = net
break
if not usableNet:
raise ValidationError("No space left in given range")
ret["network"] = "%s/%s" % (usableNet.network_address, prefixLen)
ret["success"] = True
except ValidationError as e:
ret["errorMsg"] = e.message
return JsonResponse(ret)

View File

@ -39,10 +39,12 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'crispy_forms',
'formtools',
'dncore',
'whoisdb',
'rrequests',
'domains',
'api',
]
MIDDLEWARE = [

View File

@ -21,6 +21,7 @@ import dncore.views
import whoisdb.urls
import rrequests.urls
import domains.urls
import api.urls
urlpatterns = [
@ -32,4 +33,5 @@ urlpatterns = [
url(r'^whoisdb/', include(whoisdb.urls, namespace='whoisdb')),
url(r'^rrequests/', include(rrequests.urls, namespace='rrequests')),
url(r'^domains/', include(domains.urls, namespace='domains')),
url(r'^api/', include(api.urls, namespace='api')),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-22 18:01
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('domains', '0002_auto_20170321_1854'),
]
operations = [
migrations.AlterField(
model_name='nameserver',
name='name',
field=models.CharField(max_length=256),
),
]

View File

@ -0,0 +1,76 @@
<script>
function findASNumber(step) {
// extra field foo
var ef = ""
if(step >= 0)
ef = "" + step + "-";
var asNumInp = $('#div_id_'+ef+'number input');
asNumInp.val("");
asNumInp.attr("placeholder", "Getting AS number...");
var blockOpt = $('#id_'+ef+'asblock option:selected');
if(blockOpt.val()) {
$.get("{% url "api:asblock-free-as" %}", {block: blockOpt.text()}, function(data) {
if(data.success) {
asNumInp.attr("placeholder", "")
asNumInp.val(data.number);
} else {
errorMsg = "Nothing returned via API";
if(data.errorMsg)
errorMsg = data.errorMsg;
asNumInp.attr("placeholder", data.errorMsg);
}
});
} else {
asNumInp.attr("placeholder", "No AS Block selected!");
}
}
function findPrefix(size, step) {
// extra field foo
var ef = ""
if(step >= 0)
ef = "" + step + "-";
var netInp = $('#div_id_'+ef+'prefix input');
var netInpText = netInp.val();
netInp.val("");
netInp.attr("placeholder", "Searching free prefix...");
var parentRangeOpt = $('#id_'+ef+'parent_range option:selected');
if(parentRangeOpt.val()) {
var prefixLen = 27;
var m = netInpText.match("([0-9.:]+/)?(\\d+)");
console.log(prefixLen)
console.log(netInpText);
if(m) {
prefixLen = m[2];
}
$.get("{% url "api:inetnum-free-subnet" %}", {parentRange: parentRangeOpt.text(), prefixLen: prefixLen}, function(data) {
if(data.success) {
netInp.attr("placeholder", "")
netInp.val(data.network);
} else {
errorMsg = "Nothing returned via API";
if(data.errorMsg)
errorMsg = data.errorMsg;
netInp.attr("placeholder", data.errorMsg);
}
});
} else {
netInp.attr("placeholder", "No Parent Range selected!");
}
}
$(document).ready(function() {
console.log("NOOT NOOT");
$('#div_id_number label').append(' <small>(<a onClick="findASNumber();">next free number</a>)</small>');
$('#div_id_0-number label').append(' <small>(<a onClick="findASNumber(0);">next free number</a>)</small>');
$('#div_id_prefix label').append(' <small>(<a onClick="findPrefix(27);">next free /27</a>)</small>');
$('#div_id_1-prefix label').append(' <small>(<a onClick="findPrefix(27, 1);">next free /27</a>)</small>');
});
</script>

View File

@ -17,5 +17,8 @@
</div>
</div>
</div>
{% include "whoisdb/create_js.html" %}
{% endblock %}

View File

@ -15,7 +15,7 @@
</p>
</p>
Create <a href="{% url "whoisdb:mnt-create" %}">new Maintainer</a>, create <a href="{% url "whoisdb:contact-create" %}">new Contact</a>, <a href="{% url "rrequests:dashboard" %}">request resources</a>{% if netblocks %}, create <a href="{% url "whoisdb:inetnum-create" %}">Subnet</a>{% endif %}{% if asblocks %}, create <a href="{% url "whoisdb:asnumber-create" %}">AS</a>, create <a href="{% url "whoisdb:asblock-create" %}">ASblock</a>{% endif %}
Create <a href="{% url "whoisdb:mnt-create" %}">new Maintainer</a>, create <a href="{% url "whoisdb:contact-create" %}">new Contact</a>, <a href="{% url "rrequests:dashboard" %}">request resources</a>{% if netblocks %}, create <a href="{% url "whoisdb:inetnum-create" %}">Subnet</a>{% endif %}{% if asblocks %}, create <a href="{% url "whoisdb:asnumber-create" %}">AS</a>, create <a href="{% url "whoisdb:asblock-create" %}">ASblock</a>{% endif %}{% if netblocks and asblocks %}, create <a href="{% url "whoisdb:asandsubnet-wizard" %}">AS / Subnet pair</a>{% endif %}
</p>
<!--
<h3>Create Database Objects</h3>

View File

@ -0,0 +1,46 @@
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="row">
<div class="col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Create AS &amp; Net - Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</div>
<div class="panel-body">
<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
<p>{{ message }}</p>
<form action="" method="post">{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form|crispy }}
{% endif %}
</table>
{% if wizard.steps.prev %}
<button class="btn" name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">Back</button>
{% endif %}
{% if wizard.steps.next %}
{# <button class="btn" name="wizard_goto_step" type="submit" value="{{ wizard.steps.next }}">Next</button> #}
{# <button class="btn" name="wizard_goto_step" type="submit" value="{{ wizard.steps.next }}">{% if wizard.steps.next %}Next{% else %}Create{% endif %}</button> #}
{% else %}
{# <button class="btn btn-primary" type="submit" value="submit">Create</button> #}
{% endif %}
<button class="btn" type="submit" value="{{ wizard.steps.next }}">{% if wizard.steps.next %}Next{% else %}Create{% endif %}</button>
</form>
</div>
</div>
</div>
</div>
{% include "whoisdb/create_js.html" %}
{% endblock %}

View File

@ -37,4 +37,7 @@ urlpatterns = [
url(r'^asnumber/show/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.ASNumberDetail.as_view(), name='asnumber-detail'),
url(r'^asnumber/edit/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.ASNumberEdit.as_view(), name='asnumber-edit'),
url(r'^asnumber/delete/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.ASNumberDelete.as_view(), name='asnumber-delete'),
url(r'^assubnetset/create/$', whoisdb_views.ASAndSubnetWizard.as_view(), name='asandsubnet-wizard'),
]

View File

@ -5,6 +5,8 @@ from django.http import HttpResponseRedirect, Http404
from django.urls import reverse, reverse_lazy
from django.views.generic import DetailView, CreateView, UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib import messages
from formtools.wizard.views import SessionWizardView
from .models import Maintainer, Contact, InetNum, ASBlock, ASNumber
from .forms import MntForm, MntInitialForm, ContactForm, ContactInitialForm, InetNumForm, ASBlockForm, ASNumberForm
@ -307,3 +309,37 @@ class ASNumberDelete(MntGenericMixin, LoginRequiredMixin, DeleteCheckView):
slug_field = "handle"
slug_url_kwarg = "handle"
success_url = reverse_lazy("whoisdb:dashboard")
class ASAndSubnetWizard(LoginRequiredMixin, SessionWizardView):
form_list = [ASNumberForm, InetNumForm]
template_name = "whoisdb/wizard.html"
def get_form_initial(self, step):
return {"handle": "AUTO"}
def done(self, form_list, **kwargs):
fl = list(form_list)
asNum = fl[0].save()
net = fl[1].save()
messages.info(self.request, "The following objects have been created: AS %s %s, Subnet %s %s" % (asNum.handle, asNum.number, net.handle, net.getNetwork()))
return HttpResponseRedirect(reverse("whoisdb:dashboard"))
def get_form_kwargs(self, *args, **kwargs):
kwargs = super(ASAndSubnetWizard, self).get_form_kwargs(*args, **kwargs)
kwargs["user"] = self.request.user
return kwargs
def get_context_data(self, *args, **kwargs):
d = super(ASAndSubnetWizard, self).get_context_data(*args, **kwargs)
step = d["wizard"]["steps"].step1
if step == 1:
d["message"] = "Create an AS object"
elif step == 2:
d["message"] = "Create a Subnet"
return d