aboutsummaryrefslogtreecommitdiff
path: root/rpki/gui/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'rpki/gui/models.py')
-rw-r--r--rpki/gui/models.py110
1 files changed, 66 insertions, 44 deletions
diff --git a/rpki/gui/models.py b/rpki/gui/models.py
index 184383c0..4d56c18e 100644
--- a/rpki/gui/models.py
+++ b/rpki/gui/models.py
@@ -19,57 +19,72 @@ Common classes for reuse in apps.
__version__ = '$Id$'
from django.db import models
+from django.core.exceptions import ValidationError
import rpki.resource_set
import rpki.POW
-from south.modelsinspector import add_introspection_rules
-class IPv6AddressField(models.Field):
- "Field large enough to hold a 128-bit unsigned integer."
-
- __metaclass__ = models.SubfieldBase
-
- def db_type(self, connection):
- return 'binary(16)'
-
- def to_python(self, value):
- if isinstance(value, rpki.POW.IPAddress):
+class IPAddressField(models.CharField):
+ """
+ Field class for rpki.POW.IPAddress, stored as zero-padded
+ hexadecimal so lexicographic order is identical to numeric order.
+ """
+
+ # Django's CharField type doesn't distinguish between the length
+ # of the human readable form and the length of the storage form,
+ # so we have to leave room for IPv6 punctuation even though we
+ # only store hexadecimal digits and thus will never use the full
+ # width of the database field. Price we pay for portability.
+ #
+ # Documentation on the distinction between the various conversion
+ # methods is fairly opaque, to put it politely, and we have to
+ # handle database engines which sometimes return buffers or other
+ # classes instead of strings, so the conversions are a bit
+ # finicky. If this goes haywire, your best bet is probably to
+ # litter the code with logging.debug() calls and debug by printf.
+
+ def __init__(self, *args, **kwargs):
+ kwargs["max_length"] = 40
+ super(IPAddressField, self).__init__(*args, **kwargs)
+
+ def deconstruct(self):
+ name, path, args, kwargs = super(IPAddressField, self).deconstruct()
+ del kwargs["max_length"]
+ return name, path, args, kwargs
+
+ @staticmethod
+ def _value_to_ipaddress(value):
+ if value is None or isinstance(value, rpki.POW.IPAddress):
return value
- return rpki.POW.IPAddress.fromBytes(value)
-
- def get_db_prep_value(self, value, connection, prepared):
- """
- Note that we add a custom conversion to encode long values as hex
- strings in SQL statements. See settings.get_conv() for details.
-
- """
- return value.toBytes()
+ value = str(value)
+ if ":" in value or "." in value:
+ return rpki.POW.IPAddress(value)
+ else:
+ return rpki.POW.IPAddress.fromBytes(value.decode("hex"))
-
-class IPv4AddressField(models.Field):
- "Wrapper around rpki.POW.IPAddress."
-
- __metaclass__ = models.SubfieldBase
-
- def db_type(self, connection):
- return 'int UNSIGNED'
+ def from_db_value(self, value, expression, connection, context):
+ # Can't use super() here, see Django documentation.
+ return self._value_to_ipaddress(value)
def to_python(self, value):
+ return self._value_to_ipaddress(
+ super(IPAddressField, self).to_python(value))
+
+ @staticmethod
+ def _hex_from_ipaddress(value):
if isinstance(value, rpki.POW.IPAddress):
+ return value.toBytes().encode("hex")
+ else:
return value
- return rpki.POW.IPAddress(value, version=4)
- def get_db_prep_value(self, value, connection, prepared):
- return long(value)
+ def get_prep_value(self, value):
+ return super(IPAddressField, self).get_prep_value(
+ self._hex_from_ipaddress(value))
-add_introspection_rules(
- [
- ([IPv4AddressField, IPv6AddressField], [], {})
- ],
- [r'^rpki\.gui\.models\.IPv4AddressField',
- r'^rpki\.gui\.models\.IPv6AddressField']
-)
+ def get_db_prep_value(self, value, connection, prepared = False):
+ return self._hex_from_ipaddress(
+ super(IPAddressField, self).get_db_prep_value(value, connection, prepared))
class Prefix(models.Model):
@@ -82,6 +97,7 @@ class Prefix(models.Model):
"""
Returns the prefix as a rpki.resource_set.resource_range_ip object.
"""
+
return self.range_cls(self.prefix_min, self.prefix_max)
@property
@@ -96,6 +112,7 @@ class Prefix(models.Model):
def __unicode__(self):
"""This method may be overridden by subclasses. The default
implementation calls get_prefix_display(). """
+
return self.get_prefix_display()
class Meta:
@@ -110,8 +127,8 @@ class PrefixV4(Prefix):
range_cls = rpki.resource_set.resource_range_ipv4
- prefix_min = IPv4AddressField(db_index=True, null=False)
- prefix_max = IPv4AddressField(db_index=True, null=False)
+ prefix_min = IPAddressField(db_index=True, null=False)
+ prefix_max = IPAddressField(db_index=True, null=False)
class Meta(Prefix.Meta):
abstract = True
@@ -122,20 +139,25 @@ class PrefixV6(Prefix):
range_cls = rpki.resource_set.resource_range_ipv6
- prefix_min = IPv6AddressField(db_index=True, null=False)
- prefix_max = IPv6AddressField(db_index=True, null=False)
+ prefix_min = IPAddressField(db_index=True, null=False)
+ prefix_max = IPAddressField(db_index=True, null=False)
class Meta(Prefix.Meta):
abstract = True
+def validate_asn(value):
+ if value < 0 or value > 0xFFFFFFFFL:
+ raise ValidationError('%s is not valid autonomous sequence number' % value)
+
+
class ASN(models.Model):
"""Represents a range of ASNs.
This model is abstract, and is intended to be reused by applications."""
- min = models.PositiveIntegerField(null=False)
- max = models.PositiveIntegerField(null=False)
+ min = models.BigIntegerField(null=False, validators=[validate_asn])
+ max = models.BigIntegerField(null=False, validators=[validate_asn])
class Meta:
abstract = True