Browse Source

Evvverything!

Sebastian Lohff 2 years ago
parent
commit
81bbcda8eb

+ 0
- 0
api/__init__.py View File


+ 3
- 0
api/admin.py View File

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

+ 5
- 0
api/apps.py View File

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

+ 0
- 0
api/migrations/__init__.py View File


+ 3
- 0
api/models.py View File

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

+ 3
- 0
api/tests.py View File

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

+ 9
- 0
api/urls.py View File

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

+ 107
- 0
api/views.py View File

@@ -0,0 +1,107 @@
1
+#from django.shortcuts import render
2
+from django.http import JsonResponse
3
+from django.core.exceptions import ValidationError
4
+from django.contrib.auth.views import login_required
5
+
6
+from whoisdb.models import ASBlock, ASNumber, InetNum
7
+
8
+@login_required
9
+def asblockFreeAS(request):
10
+
11
+	ret = {
12
+		"success": False,
13
+		"errorMsg": None,
14
+		"number": -1,
15
+	}
16
+
17
+	try:
18
+		blockName = request.GET.get('block', None)
19
+		if not blockName:
20
+			raise ValidationError("No block given")
21
+
22
+		try:
23
+			mnts = request.user.maintainer_set.all()
24
+			block = ASBlock.objects.filter(mnt_by__in=mnts).distinct().get(handle=blockName)
25
+			if block.asblock_set.count() > 0:
26
+				raise ValidationError("AS Block already has sub AS Blocks")
27
+			if block.asnumber_set.count() > 0:
28
+				num = block.asnumber_set.order_by("-number")[0].number + 1
29
+				if num > block.asEnd:
30
+					num = None
31
+					for n in range(block.asBegin, block.asEnd+1):
32
+						try:
33
+							ASNumber.objects.get(number=n)
34
+						except ASNumber.DoesNotExist:
35
+							num = n
36
+							break
37
+					if not num:
38
+						raise ValidationError("No free AS Number in block")
39
+				ret["number"] = num
40
+			else:
41
+				ret["number"] = block.asBegin
42
+		except ASBlock.DoesNotExist:
43
+			raise ValidationError("Could not get AS Block")
44
+
45
+		ret["success"] = True
46
+	except ValidationError as e:
47
+		ret["errorMsg"] = e.message
48
+	return JsonResponse(ret)
49
+@login_required
50
+def freeSubnet(request):
51
+
52
+	ret = {
53
+		"success": False,
54
+		"errorMsg": None,
55
+		"network": None,
56
+	}
57
+		
58
+
59
+	try:
60
+		parentRangeName = request.GET.get('parentRange', None)
61
+		if not parentRangeName:
62
+			raise ValidationError("No subnet given")
63
+
64
+		parentRange = None
65
+		try:
66
+			mnts = request.user.maintainer_set.all()
67
+			parentRange = InetNum.objects.filter(mnt_by__in=mnts).distinct().get(handle=parentRangeName)
68
+		except InetNum.DoesNotExist:
69
+			raise ValidationError("Parent range does not exist / is not maintained by you")
70
+
71
+		prefixLen = 0
72
+		try:
73
+			prefixLen = int(request.GET.get("prefixLen", 27))
74
+			if prefixLen < 8 or \
75
+				(parentRange.protocol == InetNum.IPv4 and prefixLen > 32) or \
76
+				(parentRange.protocol == InetNum.IPv6 and prefixLen > 128):
77
+				raise ValidationError("Given prefix length is out of range")
78
+		except ValueError:
79
+			raise ValidationError("PrefixLen is not a number")
80
+		nets = list(parentRange.getNetwork().subnets())
81
+		for subRange in parentRange.inetnum_set.all():
82
+			newNet = None
83
+			for net in nets:
84
+				if subRange.getNetwork().network_address in net:
85
+					newNet = net
86
+			if not newNet:
87
+				# critical error, we want a 500 here
88
+				raise ValueError("Subnet not in range")
89
+
90
+			nets.remove(newNet)
91
+			nets.extend(newNet.address_exclude(subRange.getNetwork()))
92
+
93
+		nets = sorted(nets)
94
+		usableNet = None
95
+		for net in nets:
96
+			if net.prefixlen <= prefixLen:
97
+				usableNet = net
98
+				break
99
+
100
+		if not usableNet:
101
+			raise ValidationError("No space left in given range")
102
+
103
+		ret["network"] = "%s/%s" % (usableNet.network_address, prefixLen)
104
+		ret["success"] = True
105
+	except ValidationError as e:
106
+		ret["errorMsg"] = e.message
107
+	return JsonResponse(ret)

+ 2
- 0
dnmgmt/settings.py View File

@@ -39,10 +39,12 @@ INSTALLED_APPS = [
39 39
 	'django.contrib.messages',
40 40
 	'django.contrib.staticfiles',
41 41
 	'crispy_forms',
42
+	'formtools',
42 43
 	'dncore',
43 44
 	'whoisdb',
44 45
 	'rrequests',
45 46
 	'domains',
47
+	'api',
46 48
 ]
47 49
 
48 50
 MIDDLEWARE = [

+ 2
- 0
dnmgmt/urls.py View File

@@ -21,6 +21,7 @@ import dncore.views
21 21
 import whoisdb.urls
22 22
 import rrequests.urls
23 23
 import domains.urls
24
+import api.urls
24 25
 
25 26
 
26 27
 urlpatterns = [
@@ -32,4 +33,5 @@ urlpatterns = [
32 33
 	url(r'^whoisdb/', include(whoisdb.urls, namespace='whoisdb')),
33 34
 	url(r'^rrequests/', include(rrequests.urls, namespace='rrequests')),
34 35
 	url(r'^domains/', include(domains.urls, namespace='domains')),
36
+	url(r'^api/', include(api.urls, namespace='api')),
35 37
 ]

+ 20
- 0
domains/migrations/0003_auto_20170322_1801.py View File

@@ -0,0 +1,20 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.10.5 on 2017-03-22 18:01
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('domains', '0002_auto_20170321_1854'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AlterField(
16
+            model_name='nameserver',
17
+            name='name',
18
+            field=models.CharField(max_length=256),
19
+        ),
20
+    ]

+ 76
- 0
templates/whoisdb/create_js.html View File

@@ -0,0 +1,76 @@
1
+<script>
2
+function findASNumber(step) {
3
+	// extra field foo
4
+	var ef = ""
5
+	if(step >= 0)
6
+		ef = "" + step + "-";
7
+
8
+	var asNumInp = $('#div_id_'+ef+'number input');
9
+	asNumInp.val("");
10
+	asNumInp.attr("placeholder", "Getting AS number...");
11
+
12
+	var blockOpt = $('#id_'+ef+'asblock option:selected');
13
+	if(blockOpt.val()) {
14
+		$.get("{% url "api:asblock-free-as" %}", {block: blockOpt.text()}, function(data) {
15
+			if(data.success) {
16
+				asNumInp.attr("placeholder", "")
17
+				asNumInp.val(data.number);
18
+			} else {
19
+				errorMsg = "Nothing returned via API";
20
+				if(data.errorMsg)
21
+					errorMsg = data.errorMsg;
22
+				asNumInp.attr("placeholder", data.errorMsg);
23
+			}
24
+		});
25
+	} else {
26
+		asNumInp.attr("placeholder", "No AS Block selected!");
27
+	}
28
+}
29
+
30
+function findPrefix(size, step) {
31
+	// extra field foo
32
+	var ef = ""
33
+	if(step >= 0)
34
+		ef = "" + step + "-";
35
+
36
+	var netInp = $('#div_id_'+ef+'prefix input');
37
+	var netInpText = netInp.val();
38
+	netInp.val("");
39
+	netInp.attr("placeholder", "Searching free prefix...");
40
+
41
+	var parentRangeOpt = $('#id_'+ef+'parent_range option:selected');
42
+	if(parentRangeOpt.val()) {
43
+		var prefixLen = 27;
44
+		var m = netInpText.match("([0-9.:]+/)?(\\d+)");
45
+		console.log(prefixLen)
46
+		console.log(netInpText);
47
+		if(m) {
48
+			prefixLen = m[2];
49
+		}
50
+
51
+
52
+		$.get("{% url "api:inetnum-free-subnet" %}", {parentRange: parentRangeOpt.text(), prefixLen: prefixLen}, function(data) {
53
+			if(data.success) {
54
+				netInp.attr("placeholder", "")
55
+				netInp.val(data.network);
56
+			} else {
57
+				errorMsg = "Nothing returned via API";
58
+				if(data.errorMsg)
59
+					errorMsg = data.errorMsg;
60
+				netInp.attr("placeholder", data.errorMsg);
61
+			}
62
+		});
63
+	} else {
64
+		netInp.attr("placeholder", "No Parent Range selected!");
65
+	}
66
+}
67
+
68
+$(document).ready(function() {
69
+	console.log("NOOT NOOT");
70
+	$('#div_id_number label').append(' <small>(<a onClick="findASNumber();">next free number</a>)</small>');
71
+	$('#div_id_0-number label').append(' <small>(<a onClick="findASNumber(0);">next free number</a>)</small>');
72
+	$('#div_id_prefix label').append(' <small>(<a onClick="findPrefix(27);">next free /27</a>)</small>');
73
+	$('#div_id_1-prefix label').append(' <small>(<a onClick="findPrefix(27, 1);">next free /27</a>)</small>');
74
+});
75
+</script>
76
+

+ 3
- 0
templates/whoisdb/obj_create.html View File

@@ -17,5 +17,8 @@
17 17
 		</div>
18 18
 	</div>
19 19
 </div>
20
+
21
+{% include "whoisdb/create_js.html" %}
22
+
20 23
 {% endblock %}
21 24
 

+ 1
- 1
templates/whoisdb/overview.html View File

@@ -15,7 +15,7 @@
15 15
 				</p>
16 16
 
17 17
 				</p>
18
-				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 %}
18
+				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 %}
19 19
 				</p>
20 20
 				<!--
21 21
 				<h3>Create Database Objects</h3>

+ 46
- 0
templates/whoisdb/wizard.html View File

@@ -0,0 +1,46 @@
1
+{% extends "base.html" %}
2
+{% load crispy_forms_tags %}
3
+
4
+
5
+{% block content %}
6
+<div class="row">
7
+	<div class="col-sm-12">
8
+		<div class="panel panel-default">
9
+			<div class="panel-heading">Create AS &amp; Net - Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</div>
10
+			<div class="panel-body">
11
+<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
12
+<p>{{ message }}</p>
13
+<form action="" method="post">{% csrf_token %}
14
+<table>
15
+{{ wizard.management_form }}
16
+{% if wizard.form.forms %}
17
+    {{ wizard.form.management_form }}
18
+    {% for form in wizard.form.forms %}
19
+        {{ form }}
20
+    {% endfor %}
21
+
22
+{% else %}
23
+    {{ wizard.form|crispy }}
24
+{% endif %}
25
+</table>
26
+{% if wizard.steps.prev %}
27
+<button class="btn" name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">Back</button>
28
+{% endif %}
29
+{% if wizard.steps.next %}
30
+{# <button class="btn" name="wizard_goto_step" type="submit" value="{{ wizard.steps.next }}">Next</button> #}
31
+{# <button class="btn" name="wizard_goto_step" type="submit" value="{{ wizard.steps.next }}">{% if wizard.steps.next %}Next{% else %}Create{% endif %}</button> #}
32
+{% else %}
33
+{# <button class="btn btn-primary" type="submit" value="submit">Create</button> #}
34
+{% endif %}
35
+<button class="btn" type="submit" value="{{ wizard.steps.next }}">{% if wizard.steps.next %}Next{% else %}Create{% endif %}</button>
36
+</form>
37
+
38
+			</div>
39
+		</div>
40
+	</div>
41
+</div>
42
+
43
+{% include "whoisdb/create_js.html" %}
44
+
45
+{% endblock %}
46
+

+ 3
- 0
whoisdb/urls.py View File

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

+ 36
- 0
whoisdb/views.py View File

@@ -5,6 +5,8 @@ from django.http import HttpResponseRedirect, Http404
5 5
 from django.urls import reverse, reverse_lazy
6 6
 from django.views.generic import DetailView, CreateView, UpdateView
7 7
 from django.contrib.auth.mixins import LoginRequiredMixin
8
+from django.contrib import messages
9
+from formtools.wizard.views import SessionWizardView
8 10
 
9 11
 from .models import Maintainer, Contact, InetNum, ASBlock, ASNumber
10 12
 from .forms import MntForm, MntInitialForm, ContactForm, ContactInitialForm, InetNumForm, ASBlockForm, ASNumberForm
@@ -307,3 +309,37 @@ class ASNumberDelete(MntGenericMixin, LoginRequiredMixin, DeleteCheckView):
307 309
 	slug_field = "handle"
308 310
 	slug_url_kwarg = "handle"
309 311
 	success_url = reverse_lazy("whoisdb:dashboard")
312
+
313
+
314
+class ASAndSubnetWizard(LoginRequiredMixin, SessionWizardView):
315
+	form_list = [ASNumberForm, InetNumForm]
316
+	template_name = "whoisdb/wizard.html"
317
+
318
+	def get_form_initial(self, step):
319
+		return {"handle": "AUTO"}
320
+
321
+	def done(self, form_list, **kwargs):
322
+		fl = list(form_list)
323
+		asNum = fl[0].save()
324
+		net = fl[1].save()
325
+
326
+		messages.info(self.request, "The following objects have been created: AS %s %s, Subnet %s %s" % (asNum.handle, asNum.number, net.handle, net.getNetwork()))
327
+
328
+		return HttpResponseRedirect(reverse("whoisdb:dashboard"))
329
+
330
+	def get_form_kwargs(self, *args, **kwargs):
331
+		kwargs = super(ASAndSubnetWizard, self).get_form_kwargs(*args, **kwargs)
332
+		kwargs["user"] = self.request.user
333
+
334
+		return kwargs
335
+
336
+	def get_context_data(self, *args, **kwargs):
337
+		d = super(ASAndSubnetWizard, self).get_context_data(*args, **kwargs)
338
+
339
+		step = d["wizard"]["steps"].step1
340
+		if step == 1:
341
+			d["message"] = "Create an AS object"
342
+		elif step == 2:
343
+			d["message"] = "Create a Subnet"
344
+
345
+		return d

Loading…
Cancel
Save