firewall: add sensible default and help_text to Vlan.ipv6_template
closes #360
Showing
closes #360
| ... | @@ -19,6 +19,7 @@ | ... | @@ -19,6 +19,7 @@ |
| from string import ascii_letters | from string import ascii_letters | ||
| from itertools import islice, ifilter, chain | from itertools import islice, ifilter, chain | ||
| from math import ceil | |||
| import logging | import logging | ||
| import random | import random | ||
| ... | @@ -35,7 +36,7 @@ from django.core.validators import MinValueValidator, MaxValueValidator | ... | @@ -35,7 +36,7 @@ from django.core.validators import MinValueValidator, MaxValueValidator |
| import django.conf | import django.conf | ||
| from django.db.models.signals import post_save, post_delete | from django.db.models.signals import post_save, post_delete | ||
| from celery.exceptions import TimeoutError | from celery.exceptions import TimeoutError | ||
| from netaddr import IPSet, EUI, IPNetwork, IPAddress | from netaddr import IPSet, EUI, IPNetwork, IPAddress, ipv6_full | ||
| from common.models import method_cache, WorkerNotFound, HumanSortField | from common.models import method_cache, WorkerNotFound, HumanSortField | ||
| from firewall.tasks.local_tasks import reloadtask | from firewall.tasks.local_tasks import reloadtask | ||
| ... | @@ -363,9 +364,20 @@ class Vlan(AclBase, models.Model): | ... | @@ -363,9 +364,20 @@ class Vlan(AclBase, models.Model): |
| 'address is: "%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa".'), | 'address is: "%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa".'), | ||
| default="%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa") | default="%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa") | ||
| ipv6_template = models.TextField( | ipv6_template = models.TextField( | ||
| validators=[val_ipv6_template], | blank=True, | ||
| verbose_name=_('ipv6 template'), | help_text=_('Template for translating IPv4 addresses to IPv6. ' | ||
| default="2001:738:2001:4031:%(b)d:%(c)d:%(d)d:0") | 'Automatically generated hosts in dual-stack networks ' | ||
| 'will get this address. The template ' | |||
| 'can contain four tokens: "%(a)d", "%(b)d", ' | |||
| '"%(c)d", and "%(d)d", representing the four bytes ' | |||
| 'of the IPv4 address, respectively, in decimal notation. ' | |||
| 'Moreover you can use any standard printf format ' | |||
| 'specification like %(a)02x to get the first byte as two ' | |||
| 'hexadecimal digits. Usual choices for mapping ' | |||
| '198.51.100.0/24 to 2001:0DB8:1:1::/64 would be ' | |||
| '"2001:0DB8:51:1:1:%(d)d::" and ' | |||
Please
register
or
sign in
to reply
|
|||
| '"2001:0DB8:51:1:1:%(d)x00::".'), | |||
| validators=[val_ipv6_template], verbose_name=_('ipv6 template')) | |||
| dhcp_pool = models.TextField(blank=True, verbose_name=_('DHCP pool'), | dhcp_pool = models.TextField(blank=True, verbose_name=_('DHCP pool'), | ||
| help_text=_( | help_text=_( | ||
| 'The address range of the DHCP pool: ' | 'The address range of the DHCP pool: ' | ||
| ... | @@ -380,6 +392,52 @@ class Vlan(AclBase, models.Model): | ... | @@ -380,6 +392,52 @@ class Vlan(AclBase, models.Model): |
| modified_at = models.DateTimeField(auto_now=True, | modified_at = models.DateTimeField(auto_now=True, | ||
| verbose_name=_('modified at')) | verbose_name=_('modified at')) | ||
| def clean(self): | |||
| super(Vlan, self).clean() | |||
| if self.ipv6_template: | |||
| if not self.network6: | |||
| raise ValidationError( | |||
| _("You cannot specify an IPv6 template if there is no " | |||
| "IPv6 network set.")) | |||
| for i in (self.network4[1], self.network4[-1]): | |||
| i6 = self.convert_ipv4_to_ipv6(i) | |||
| if i6 not in self.network6: | |||
| raise ValidationError( | |||
| _("%(ip6)s (translated from %(ip4)s) is outside of " | |||
| "the IPv6 network.") % {"ip4": i, "ip6": i6}) | |||
| if not self.ipv6_template and self.network6: | |||
| # come up with some sensible default | |||
| self.ipv6_template = self._magic_ipv6_template() | |||
| host4_bytes = int(ceil((32 - self.network4.prefixlen) / 8)) | |||
|
|||
| host6_bytes = int(ceil((128 - self.network6.prefixlen) / 8)) | |||
| if host4_bytes > host6_bytes: | |||
| raise ValidationError( | |||
| _("IPv6 network is too small to map IPv4 addresses to it.")) | |||
| def _magic_ipv6_template(self): | |||
| host4_bytes = int(ceil((32 - self.network4.prefixlen) / 8)) | |||
| host6_bytes = int(ceil((128 - self.network6.prefixlen) / 8)) | |||
| letters = ascii_letters[4-host4_bytes:4] | |||
| remove = host6_bytes // 2 | |||
| ipstr = self.network6.network.format(ipv6_full) | |||
| s = ipstr.split(":")[0:-remove] | |||
| if 2 * (host4_bytes + 1) < host6_bytes: # use verbose format | |||
| for i in letters: | |||
| s.append("%({})d".format(i)) | |||
| else: # use short format | |||
| remain = host6_bytes | |||
| for i in letters: | |||
| if remain % 2 == 1: # can use last half word | |||
| if s[-1].endswith("00"): | |||
| s[-1] = s[-1][:-2] | |||
| s[-1] += "%({})02x".format(i) | |||
| else: | |||
| s.append("%({})02x00".format(i)) | |||
| remain -= 1 | |||
| if host6_bytes > host4_bytes: | |||
| s.append(":") | |||
| return ":".join(s) | |||
| def __unicode__(self): | def __unicode__(self): | ||
| return "%s - %s" % ("managed" if self.managed else "unmanaged", | return "%s - %s" % ("managed" if self.managed else "unmanaged", | ||
| self.name) | self.name) | ||
| ... | ... |