Initial start of the context/server model, including test framework.
All checks were successful
pda-new unit tests / Run-unit-tests (push) Successful in 8s
All checks were successful
pda-new unit tests / Run-unit-tests (push) Successful in 8s
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,7 +3,7 @@
|
|||||||
*.pot
|
*.pot
|
||||||
*.pyc
|
*.pyc
|
||||||
__pycache__
|
__pycache__
|
||||||
db.sqlite3
|
*.sqlite3
|
||||||
media
|
media
|
||||||
staticfiles/
|
staticfiles/
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ INSTALLED_APPS = [
|
|||||||
"django.contrib.staticfiles",
|
"django.contrib.staticfiles",
|
||||||
"allauth",
|
"allauth",
|
||||||
"allauth.account",
|
"allauth.account",
|
||||||
|
'pdns',
|
||||||
]
|
]
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = [
|
AUTHENTICATION_BACKENDS = [
|
||||||
@@ -50,6 +51,7 @@ MIDDLEWARE = [
|
|||||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
"django.contrib.messages.middleware.MessageMiddleware",
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
|
"django.middleware.locale.LocaleMiddleware",
|
||||||
"allauth.account.middleware.AccountMiddleware",
|
"allauth.account.middleware.AccountMiddleware",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -77,7 +79,7 @@ DEFAULT_DB = {
|
|||||||
'ENGINE': f"django.db.backends.{DB_ENGINE}",
|
'ENGINE': f"django.db.backends.{DB_ENGINE}",
|
||||||
}
|
}
|
||||||
if DB_ENGINE=='sqlite3':
|
if DB_ENGINE=='sqlite3':
|
||||||
DEFAULT_DB['NAME'] = os.path.join('BASE_DIR',
|
DEFAULT_DB['NAME'] = os.path.join(BASE_DIR,
|
||||||
env('DB_NAME', default='pdanext.sqlite3'))
|
env('DB_NAME', default='pdanext.sqlite3'))
|
||||||
else:
|
else:
|
||||||
DEFAULT_DB['NAME'] = env('DB_NAME')
|
DEFAULT_DB['NAME'] = env('DB_NAME')
|
||||||
@@ -93,6 +95,8 @@ DATABASES = {
|
|||||||
'default': DEFAULT_DB,
|
'default': DEFAULT_DB,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print('Database: %s' % DATABASES['default']) if DEBUG else None
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
{
|
{
|
||||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||||
|
|||||||
2
src/pdns/__init__.py
Normal file
2
src/pdns/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# __init__.py needs to exist or django won't load the module.
|
||||||
|
|
||||||
65
src/pdns/migrations/0001_initial.py
Normal file
65
src/pdns/migrations/0001_initial.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# Generated by Django 5.2.5 on 2025-10-20 04:54
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="PDNSContext",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(
|
||||||
|
max_length=20,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
unique=True,
|
||||||
|
verbose_name="Name",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
models.TextField(blank=True, null=True, verbose_name="Description"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="PDNSServer",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(
|
||||||
|
max_length=40,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
unique=True,
|
||||||
|
verbose_name="Name",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"server_uri",
|
||||||
|
models.URLField(
|
||||||
|
help_text="Prefer IP addresses; use FQDN at your own risk.",
|
||||||
|
max_length=80,
|
||||||
|
verbose_name="Server URL",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"fk_network",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="servers",
|
||||||
|
to="pdns.pdnscontext",
|
||||||
|
verbose_name="Context",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Generated by Django 5.2.5 on 2025-10-20 07:12
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("pdns", "0001_initial"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="pdnsserver",
|
||||||
|
name="alive",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False, editable=False, verbose_name="Alive status"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="pdnsserver",
|
||||||
|
name="last_checked",
|
||||||
|
field=models.DateTimeField(
|
||||||
|
editable=False, null=True, verbose_name="Last checked"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="pdnsserver",
|
||||||
|
name="last_seen",
|
||||||
|
field=models.DateTimeField(
|
||||||
|
editable=False, null=True, verbose_name="Last seen online"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 5.2.5 on 2025-10-20 08:20
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("pdns", "0002_pdnsserver_alive_pdnsserver_last_checked_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name="pdnsserver",
|
||||||
|
old_name="fk_network",
|
||||||
|
new_name="context",
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="pdnsserver",
|
||||||
|
name="alive",
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="pdnsserver",
|
||||||
|
name="online",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False, editable=False, verbose_name="Online status"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
0
src/pdns/migrations/__init__.py
Normal file
0
src/pdns/migrations/__init__.py
Normal file
99
src/pdns/models.py
Normal file
99
src/pdns/models.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
# Okay, going to illustrate my thinking here:
|
||||||
|
# A ##Context## is a set of servers that operate together as
|
||||||
|
# authoritative for a group of zones.
|
||||||
|
|
||||||
|
# (I was going to call it a Network, but then I found out
|
||||||
|
# about https://doc.powerdns.com/authoritative/views.html )
|
||||||
|
|
||||||
|
# All ##Servers## within a Context are presumed to be
|
||||||
|
# using the same database and be otherwise interchangeable,
|
||||||
|
# Although given the limitations of some backends they can
|
||||||
|
# be set to read-only.
|
||||||
|
|
||||||
|
class PDNSContext(models.Model):
|
||||||
|
name = models.CharField(verbose_name=_('Name'),
|
||||||
|
max_length=20,
|
||||||
|
unique=True,
|
||||||
|
primary_key=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
description = models.TextField(verbose_name=_('Description'),
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def total_servers(self):
|
||||||
|
return self.servers.all().count()
|
||||||
|
|
||||||
|
def online_servers(self):
|
||||||
|
return self.servers.filter(online=True).count()
|
||||||
|
|
||||||
|
def offline_servers(self):
|
||||||
|
return self.servers.filter(online=False).count()
|
||||||
|
|
||||||
|
def status(self):
|
||||||
|
return _('Context %(name)s: %(total)d total, %(online)d online, %(offline)d offline.') % {
|
||||||
|
'name': self.name,
|
||||||
|
'total': self.total_servers(),
|
||||||
|
'online': self.online_servers(),
|
||||||
|
'offline': self.offline_servers()}
|
||||||
|
|
||||||
|
|
||||||
|
class PDNSServer(models.Model):
|
||||||
|
name = models.CharField(verbose_name=_('Name'),
|
||||||
|
max_length=40,
|
||||||
|
unique=True,
|
||||||
|
primary_key=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
server_uri = models.URLField(verbose_name=_('Server URL'),
|
||||||
|
help_text='Prefer IP addresses; use FQDN at your own risk.',
|
||||||
|
max_length=80,
|
||||||
|
blank=False,
|
||||||
|
null=False
|
||||||
|
)
|
||||||
|
|
||||||
|
context=models.ForeignKey(PDNSContext,
|
||||||
|
verbose_name=_('Context'),
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='servers'
|
||||||
|
)
|
||||||
|
|
||||||
|
online = models.BooleanField(verbose_name=_('Online status'),
|
||||||
|
editable=False,
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
last_checked = models.DateTimeField(verbose_name=_('Last checked'),
|
||||||
|
editable=False,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
last_seen = models.DateTimeField(verbose_name=_('Last seen online'),
|
||||||
|
editable=False,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
selfstr= _("%(name)s (%(context)s): %(url)s") % { 'name': self.name,
|
||||||
|
'context': self.context.name,
|
||||||
|
'url': self.server_uri}
|
||||||
|
return selfstr
|
||||||
|
|
||||||
|
def status(self):
|
||||||
|
status = _('Online') if self.online else _('Offline')
|
||||||
|
return _('%(status)s since %(time)s') % {
|
||||||
|
'status': status,
|
||||||
|
'time': self.last_checked
|
||||||
|
}
|
||||||
73
src/pdns/test_models.py
Normal file
73
src/pdns/test_models.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
from unittest import skip
|
||||||
|
|
||||||
|
from .models import (
|
||||||
|
PDNSContext,
|
||||||
|
PDNSServer
|
||||||
|
)
|
||||||
|
|
||||||
|
# Tests are run in transactions, which means that for every test_*() function
|
||||||
|
# the database restores to what's created during setUp()
|
||||||
|
|
||||||
|
class PDNSModelTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.test_time = now()
|
||||||
|
tc = PDNSContext.objects.create(name='test context')
|
||||||
|
PDNSServer.objects.create(name='test server',
|
||||||
|
server_uri='https://localhost:8081',
|
||||||
|
context=tc,
|
||||||
|
online=True,
|
||||||
|
last_seen=self.test_time,
|
||||||
|
last_checked=self.test_time)
|
||||||
|
self.context_target = 'test context'
|
||||||
|
self.server_target = 'test server (test context): https://localhost:8081'
|
||||||
|
self.server_status_target = _('%(status)s since %(date)s') % {
|
||||||
|
'status': _('Online'),
|
||||||
|
'date': self.test_time
|
||||||
|
}
|
||||||
|
self.server_status_offline_target = _('%(status)s since %(date)s') % {
|
||||||
|
'status': _('Offline'),
|
||||||
|
'date': self.test_time
|
||||||
|
}
|
||||||
|
return True
|
||||||
|
|
||||||
|
def test_server(self):
|
||||||
|
test_server = PDNSServer.objects.get(name='test server')
|
||||||
|
self.assertEqual(test_server.__str__(), self.server_target)
|
||||||
|
self.assertEqual(test_server.status(), self.server_status_target)
|
||||||
|
|
||||||
|
def test_server_offline(self):
|
||||||
|
test_server = PDNSServer.objects.get(name='test server')
|
||||||
|
test_server.online = False
|
||||||
|
self.assertEqual(test_server.status(), self.server_status_offline_target)
|
||||||
|
|
||||||
|
def test_context(self):
|
||||||
|
test_context = PDNSContext.objects.get(name='test context')
|
||||||
|
self.assertEqual(test_context.__str__(), self.context_target)
|
||||||
|
|
||||||
|
def test_context_onoff(self):
|
||||||
|
test_context = PDNSContext.objects.get(name='test context')
|
||||||
|
self.assertEqual(test_context.total_servers(),1)
|
||||||
|
self.assertEqual(test_context.online_servers(),1)
|
||||||
|
self.assertEqual(test_context.offline_servers(),0)
|
||||||
|
|
||||||
|
def test_context_offline(self):
|
||||||
|
test_context = PDNSContext.objects.get(name='test context')
|
||||||
|
test_server = PDNSServer.objects.get(name='test server')
|
||||||
|
test_server.online = False
|
||||||
|
test_server.save()
|
||||||
|
self.assertEqual(test_context.total_servers(),1)
|
||||||
|
self.assertEqual(test_context.online_servers(),0)
|
||||||
|
self.assertEqual(test_context.offline_servers(),1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
0
src/tests/basedata.py
Normal file
0
src/tests/basedata.py
Normal file
Reference in New Issue
Block a user