diff options
Diffstat (limited to 'rpki/gui/models.py')
-rw-r--r-- | rpki/gui/models.py | 110 |
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 |