add related hosts functionality, to update host records based on main host network address + interface id
this is esp. useful for v6 hosts, where your provider might give you a changing prefix (we assume /64), but your interface ids stay the same. but same mechanism also principally works for v4 (we assume /29), except that you usually do not get a v4 network where the network address is changing. and when it comes to dynamic addresses, many people only get 1 ipv4 address anyway. the related host could then be used for aliaseses with ifid == 0 for all aliases.
This commit is contained in:
parent
66cedf5425
commit
fa640706f5
@ -17,6 +17,8 @@ TEST_HOST = FQDN('test%da' % randint(1, 1000000), TESTDOMAIN) # unit tests can
|
|||||||
TEST_SECRET = "secret"
|
TEST_SECRET = "secret"
|
||||||
TEST_HOST2 = FQDN('test%db' % randint(1, 1000000), TESTDOMAIN)
|
TEST_HOST2 = FQDN('test%db' % randint(1, 1000000), TESTDOMAIN)
|
||||||
TEST_SECRET2 = "somethingelse"
|
TEST_SECRET2 = "somethingelse"
|
||||||
|
RELATED_HOST_NAME = 'rh'
|
||||||
|
TEST_HOST_RELATED = FQDN(RELATED_HOST_NAME + '.' + TEST_HOST.host, TEST_HOST.domain)
|
||||||
NAMESERVER_IP = "85.10.192.104"
|
NAMESERVER_IP = "85.10.192.104"
|
||||||
NAMESERVER_UPDATE_ALGORITHM = "HMAC_SHA512"
|
NAMESERVER_UPDATE_ALGORITHM = "HMAC_SHA512"
|
||||||
# no problem, you can ONLY update the TESTDOMAIN with this secret, nothing else:
|
# no problem, you can ONLY update the TESTDOMAIN with this secret, nothing else:
|
||||||
@ -62,7 +64,7 @@ def db_init(db): # note: db is a predefined fixture and required here to have t
|
|||||||
Init the database contents for testing, so we have a service domain, ...
|
Init the database contents for testing, so we have a service domain, ...
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from nsupdate.main.models import Host, Domain, ServiceUpdater, ServiceUpdaterHostConfig
|
from nsupdate.main.models import Host, RelatedHost, Domain, ServiceUpdater, ServiceUpdaterHostConfig
|
||||||
user_model = get_user_model()
|
user_model = get_user_model()
|
||||||
# create a fresh test user
|
# create a fresh test user
|
||||||
u = user_model.objects.create_user(USERNAME, settings.DEFAULT_FROM_EMAIL, PASSWORD)
|
u = user_model.objects.create_user(USERNAME, settings.DEFAULT_FROM_EMAIL, PASSWORD)
|
||||||
@ -115,6 +117,8 @@ def db_init(db): # note: db is a predefined fixture and required here to have t
|
|||||||
created_by=u,
|
created_by=u,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
RelatedHost.objects.create(name=RELATED_HOST_NAME, interface_id_ipv4="0.0.0.1", interface_id_ipv6="::1", main_host=h)
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
activate('en')
|
activate('en')
|
||||||
|
@ -110,7 +110,7 @@ def test_nic_update_authorized_ns_unavailable(client):
|
|||||||
assert response.content == b'dnserr'
|
assert response.content == b'dnserr'
|
||||||
|
|
||||||
|
|
||||||
def test_nic_update_authorized_myip(client):
|
def test_nic_update_authorized_myip_v4(client):
|
||||||
response = client.get(reverse('nic_update') + '?myip=4.3.2.1',
|
response = client.get(reverse('nic_update') + '?myip=4.3.2.1',
|
||||||
HTTP_AUTHORIZATION=make_basic_auth_header(TEST_HOST, TEST_SECRET))
|
HTTP_AUTHORIZATION=make_basic_auth_header(TEST_HOST, TEST_SECRET))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
@ -126,6 +126,28 @@ def test_nic_update_authorized_myip(client):
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
# must be nochg (was same IP)
|
# must be nochg (was same IP)
|
||||||
assert response.content == b'nochg 1.2.3.4'
|
assert response.content == b'nochg 1.2.3.4'
|
||||||
|
# now check if it updated the ipv4 related hosts also:
|
||||||
|
assert query_ns(TEST_HOST_RELATED, 'A') == '1.2.3.1' # 1.2.3.4/29 + 0.0.0.1
|
||||||
|
|
||||||
|
|
||||||
|
def test_nic_update_authorized_myip_v6(client):
|
||||||
|
response = client.get(reverse('nic_update') + '?myip=2000::2',
|
||||||
|
HTTP_AUTHORIZATION=make_basic_auth_header(TEST_HOST, TEST_SECRET))
|
||||||
|
assert response.status_code == 200
|
||||||
|
# we don't care whether it is nochg or good, but should be the ip from myip=...:
|
||||||
|
assert response.content in [b'good 2000::2', b'nochg 2000::2']
|
||||||
|
response = client.get(reverse('nic_update') + '?myip=2000::3',
|
||||||
|
HTTP_AUTHORIZATION=make_basic_auth_header(TEST_HOST, TEST_SECRET))
|
||||||
|
assert response.status_code == 200
|
||||||
|
# must be good (was different IP)
|
||||||
|
assert response.content == b'good 2000::3'
|
||||||
|
response = client.get(reverse('nic_update') + '?myip=2000::3',
|
||||||
|
HTTP_AUTHORIZATION=make_basic_auth_header(TEST_HOST, TEST_SECRET))
|
||||||
|
assert response.status_code == 200
|
||||||
|
# must be nochg (was same IP)
|
||||||
|
assert response.content == b'nochg 2000::3'
|
||||||
|
# now check if it updated the ipv4 related hosts also:
|
||||||
|
assert query_ns(TEST_HOST_RELATED, 'AAAA') == '2000::1' # 2000::3/64 + ::1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires_sequential
|
@pytest.mark.requires_sequential
|
||||||
|
@ -7,6 +7,8 @@ import logging
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from netaddr import IPAddress, IPNetwork
|
||||||
|
from netaddr.core import AddrFormatError
|
||||||
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -18,7 +20,7 @@ from django.utils.decorators import method_decorator
|
|||||||
|
|
||||||
from ..utils import log, ddns_client
|
from ..utils import log, ddns_client
|
||||||
from ..main.models import Host
|
from ..main.models import Host
|
||||||
from ..main.dnstools import (update, delete, check_ip, put_ip_into_session,
|
from ..main.dnstools import (FQDN, update, delete, check_ip, put_ip_into_session,
|
||||||
SameIpError, DnsUpdateError, NameServerNotAvailable)
|
SameIpError, DnsUpdateError, NameServerNotAvailable)
|
||||||
|
|
||||||
|
|
||||||
@ -343,9 +345,55 @@ def _update(host, ipaddr, secure=False, logger=None):
|
|||||||
host.poke(kind, secure)
|
host.poke(kind, secure)
|
||||||
try:
|
try:
|
||||||
update(fqdn, ipaddr)
|
update(fqdn, ipaddr)
|
||||||
|
except SameIpError:
|
||||||
|
msg = '%s - received no-change update, ip: %s tls: %r' % (fqdn, ipaddr, secure)
|
||||||
|
logger.warning(msg)
|
||||||
|
host.register_client_result(msg, fault=True)
|
||||||
|
return Response('nochg %s' % ipaddr)
|
||||||
|
except (DnsUpdateError, NameServerNotAvailable) as e:
|
||||||
|
msg = str(e)
|
||||||
|
msg = '%s - received update that resulted in a dns error [%s], ip: %s tls: %r' % (
|
||||||
|
fqdn, msg, ipaddr, secure)
|
||||||
|
logger.error(msg)
|
||||||
|
host.register_server_result(msg, fault=True)
|
||||||
|
return Response('dnserr')
|
||||||
|
else:
|
||||||
msg = '%s - received good update -> ip: %s tls: %r' % (fqdn, ipaddr, secure)
|
msg = '%s - received good update -> ip: %s tls: %r' % (fqdn, ipaddr, secure)
|
||||||
logger.info(msg)
|
logger.info(msg)
|
||||||
host.register_client_result(msg, fault=False)
|
host.register_client_result(msg, fault=False)
|
||||||
|
# update related hosts
|
||||||
|
for rh in host.relatedhosts.all():
|
||||||
|
if rh.available:
|
||||||
|
# TODO: make netmask configurable in Host record
|
||||||
|
if kind == 'ipv4':
|
||||||
|
ifid, netmask = rh.interface_id_ipv4.strip(), '/29'
|
||||||
|
else: # kind == 'ipv6':
|
||||||
|
ifid, netmask = rh.interface_id_ipv6.strip(), '/64'
|
||||||
|
if not ifid:
|
||||||
|
# ifid can be just left blank if no address record of this type is wanted
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
ifid = IPAddress(ifid)
|
||||||
|
network = IPNetwork(ipaddr + netmask)
|
||||||
|
rh_ipaddr = str(IPAddress(network.network) + int(ifid))
|
||||||
|
rh_fqdn = FQDN(rh.name + '.' + fqdn.host, fqdn.domain)
|
||||||
|
except AddrFormatError as e:
|
||||||
|
logger.warning("trouble computing address of related host %s [%s]" % (rh, e))
|
||||||
|
else:
|
||||||
|
logger.info("updating related host %s -> %s" % (rh_fqdn, rh_ipaddr))
|
||||||
|
try:
|
||||||
|
update(rh_fqdn, rh_ipaddr)
|
||||||
|
except SameIpError:
|
||||||
|
msg = '%s - received no-change update, ip: %s tls: %r' % (rh_fqdn, rh_ipaddr, secure)
|
||||||
|
logger.warning(msg)
|
||||||
|
host.register_client_result(msg, fault=True)
|
||||||
|
except (DnsUpdateError, NameServerNotAvailable) as e:
|
||||||
|
msg = str(e)
|
||||||
|
msg = '%s - received update that resulted in a dns error [%s], ip: %s tls: %r' % (
|
||||||
|
rh_fqdn, msg, rh_ipaddr, secure)
|
||||||
|
logger.error(msg)
|
||||||
|
host.register_server_result(msg, fault=True)
|
||||||
|
|
||||||
# now check if there are other services we shall relay updates to:
|
# now check if there are other services we shall relay updates to:
|
||||||
for hc in host.serviceupdaterhostconfigs.all():
|
for hc in host.serviceupdaterhostconfigs.all():
|
||||||
if (kind == 'ipv4' and hc.give_ipv4 and hc.service.accept_ipv4
|
if (kind == 'ipv4' and hc.give_ipv4 and hc.service.accept_ipv4
|
||||||
@ -363,18 +411,6 @@ def _update(host, ipaddr, secure=False, logger=None):
|
|||||||
kwargs.pop('password')
|
kwargs.pop('password')
|
||||||
logger.exception("the dyndns2 updater raised an exception [%r]" % kwargs)
|
logger.exception("the dyndns2 updater raised an exception [%r]" % kwargs)
|
||||||
return Response('good %s' % ipaddr)
|
return Response('good %s' % ipaddr)
|
||||||
except SameIpError:
|
|
||||||
msg = '%s - received no-change update, ip: %s tls: %r' % (fqdn, ipaddr, secure)
|
|
||||||
logger.warning(msg)
|
|
||||||
host.register_client_result(msg, fault=True)
|
|
||||||
return Response('nochg %s' % ipaddr)
|
|
||||||
except (DnsUpdateError, NameServerNotAvailable) as e:
|
|
||||||
msg = str(e)
|
|
||||||
msg = '%s - received update that resulted in a dns error [%s], ip: %s tls: %r' % (
|
|
||||||
fqdn, msg, ipaddr, secure)
|
|
||||||
logger.error(msg)
|
|
||||||
host.register_server_result(msg, fault=True)
|
|
||||||
return Response('dnserr')
|
|
||||||
|
|
||||||
|
|
||||||
def _delete(host, ipaddr, secure=False, logger=None):
|
def _delete(host, ipaddr, secure=False, logger=None):
|
||||||
|
@ -0,0 +1,155 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from south.utils import datetime_utils as datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
# Adding model 'RelatedHost'
|
||||||
|
db.create_table(u'main_relatedhost', (
|
||||||
|
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||||
|
('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
||||||
|
('interface_id_ipv4', self.gf('django.db.models.fields.CharField')(default='', max_length=16)),
|
||||||
|
('interface_id_ipv6', self.gf('django.db.models.fields.CharField')(default='', max_length=22)),
|
||||||
|
('available', self.gf('django.db.models.fields.BooleanField')(default=True)),
|
||||||
|
('main_host', self.gf('django.db.models.fields.related.ForeignKey')(related_name='relatedhosts', to=orm['main.Host'])),
|
||||||
|
))
|
||||||
|
db.send_create_signal(u'main', ['RelatedHost'])
|
||||||
|
|
||||||
|
# Adding unique constraint on 'RelatedHost', fields ['name', 'main_host']
|
||||||
|
db.create_unique(u'main_relatedhost', ['name', 'main_host_id'])
|
||||||
|
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
# Removing unique constraint on 'RelatedHost', fields ['name', 'main_host']
|
||||||
|
db.delete_unique(u'main_relatedhost', ['name', 'main_host_id'])
|
||||||
|
|
||||||
|
# Deleting model 'RelatedHost'
|
||||||
|
db.delete_table(u'main_relatedhost')
|
||||||
|
|
||||||
|
|
||||||
|
models = {
|
||||||
|
u'auth.group': {
|
||||||
|
'Meta': {'object_name': 'Group'},
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||||
|
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
u'auth.permission': {
|
||||||
|
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
|
||||||
|
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||||
|
},
|
||||||
|
u'auth.user': {
|
||||||
|
'Meta': {'object_name': 'User'},
|
||||||
|
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||||
|
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||||
|
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||||
|
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
|
||||||
|
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||||
|
},
|
||||||
|
u'contenttypes.contenttype': {
|
||||||
|
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||||
|
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||||
|
},
|
||||||
|
u'main.blacklistedhost': {
|
||||||
|
'Meta': {'object_name': 'BlacklistedHost'},
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blacklisted_domains'", 'to': u"orm['auth.User']"}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'last_update': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'name_re': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
|
||||||
|
},
|
||||||
|
u'main.domain': {
|
||||||
|
'Meta': {'object_name': 'Domain'},
|
||||||
|
'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'comment': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'domains'", 'to': u"orm['auth.User']"}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'last_update': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
|
||||||
|
'nameserver_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
|
||||||
|
'nameserver_update_algorithm': ('django.db.models.fields.CharField', [], {'default': "'HMAC_SHA512'", 'max_length': '16'}),
|
||||||
|
'nameserver_update_secret': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '88'}),
|
||||||
|
'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
|
||||||
|
},
|
||||||
|
u'main.host': {
|
||||||
|
'Meta': {'unique_together': "(('name', 'domain'),)", 'object_name': 'Host', 'index_together': "(('name', 'domain'),)"},
|
||||||
|
'abuse': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'abuse_blocked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'client_faults': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||||
|
'client_result_msg': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'comment': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts'", 'to': u"orm['auth.User']"}),
|
||||||
|
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['main.Domain']"}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'last_update': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'last_update_ipv4': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'last_update_ipv6': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||||
|
'server_faults': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
|
||||||
|
'server_result_msg': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'tls_update_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'tls_update_ipv6': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'update_secret': ('django.db.models.fields.CharField', [], {'max_length': '64'})
|
||||||
|
},
|
||||||
|
u'main.relatedhost': {
|
||||||
|
'Meta': {'unique_together': "(('name', 'main_host'),)", 'object_name': 'RelatedHost'},
|
||||||
|
'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'interface_id_ipv4': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '16'}),
|
||||||
|
'interface_id_ipv6': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '22'}),
|
||||||
|
'main_host': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'relatedhosts'", 'to': u"orm['main.Host']"}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
|
||||||
|
},
|
||||||
|
u'main.serviceupdater': {
|
||||||
|
'Meta': {'object_name': 'ServiceUpdater'},
|
||||||
|
'accept_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'accept_ipv6': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'comment': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'serviceupdater'", 'to': u"orm['auth.User']"}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'last_update': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
|
||||||
|
'path': ('django.db.models.fields.CharField', [], {'default': "'/nic/update'", 'max_length': '255'}),
|
||||||
|
'secure': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'server': ('django.db.models.fields.CharField', [], {'max_length': '255'})
|
||||||
|
},
|
||||||
|
u'main.serviceupdaterhostconfig': {
|
||||||
|
'Meta': {'object_name': 'ServiceUpdaterHostConfig'},
|
||||||
|
'comment': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||||
|
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'serviceupdaterhostconfigs'", 'to': u"orm['auth.User']"}),
|
||||||
|
'give_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'give_ipv6': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||||
|
'host': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'serviceupdaterhostconfigs'", 'to': u"orm['main.Host']"}),
|
||||||
|
'hostname': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'last_update': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||||
|
'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||||
|
'service': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['main.ServiceUpdater']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['main']
|
@ -285,6 +285,40 @@ def pre_delete_host(sender, **kwargs):
|
|||||||
pre_delete.connect(pre_delete_host, sender=Host)
|
pre_delete.connect(pre_delete_host, sender=Host)
|
||||||
|
|
||||||
|
|
||||||
|
class RelatedHost(models.Model):
|
||||||
|
# host addr = network_of_main_host + interface_id
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=255, # RFC 2181 (and considering having multiple joined labels here later)
|
||||||
|
validators=[
|
||||||
|
RegexValidator(
|
||||||
|
regex=r'^(([a-z0-9][a-z0-9\-]*[a-z0-9])|[a-z0-9])$',
|
||||||
|
message='Invalid host name: only "a-z", "0-9" and "-" is allowed'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
help_text=_("The name of a host in same network as your main host."))
|
||||||
|
interface_id_ipv4 = models.CharField(
|
||||||
|
default='',
|
||||||
|
max_length=16, # 123.123.123.123
|
||||||
|
help_text=_("The IPv4 interface ID of this host."))
|
||||||
|
interface_id_ipv6 = models.CharField(
|
||||||
|
default='',
|
||||||
|
max_length=22, # ::1234:5678:9abc:def0
|
||||||
|
help_text=_("The IPv6 interface ID of this host."))
|
||||||
|
available = models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
help_text=_("Check if host is available/in use - "
|
||||||
|
"if not checked, we won't accept updates for this host"))
|
||||||
|
|
||||||
|
main_host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='relatedhosts')
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u"%s.%s" % (
|
||||||
|
self.name, unicode(self.main_host))
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
unique_together = (('name', 'main_host'), )
|
||||||
|
|
||||||
|
|
||||||
class ServiceUpdater(models.Model):
|
class ServiceUpdater(models.Model):
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=32,
|
max_length=32,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# packages always needed
|
# packages always needed
|
||||||
dnspython
|
dnspython
|
||||||
|
netaddr
|
||||||
django==1.6.6
|
django==1.6.6
|
||||||
django-bootstrap-form
|
django-bootstrap-form
|
||||||
django-registration
|
django-registration
|
||||||
|
1
setup.py
1
setup.py
@ -54,6 +54,7 @@ setup(
|
|||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
platforms='any',
|
platforms='any',
|
||||||
install_requires=install_requires + [
|
install_requires=install_requires + [
|
||||||
|
'netaddr',
|
||||||
'django >=1.6, <1.7', # 1.7 is not tested yet
|
'django >=1.6, <1.7', # 1.7 is not tested yet
|
||||||
# django >= 1.5.3 also works, but needs a code change, see
|
# django >= 1.5.3 also works, but needs a code change, see
|
||||||
# https://github.com/nsupdate-info/nsupdate.info/issues/141
|
# https://github.com/nsupdate-info/nsupdate.info/issues/141
|
||||||
|
Loading…
x
Reference in New Issue
Block a user