fields.py 9.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.

18 19 20 21 22
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.ipv6 import is_valid_ipv6_address
from south.modelsinspector import add_introspection_rules
23
from django import forms
24 25
from netaddr import (IPAddress, IPNetwork, AddrFormatError, ZEROFILL,
                     EUI, mac_unix)
26 27
import re

28

29 30
alfanum_re = re.compile(r'^[A-Za-z0-9_-]+$')
domain_re = re.compile(r'^([A-Za-z0-9_-]\.?)+$')
31
domain_wildcard_re = re.compile(r'^(\*\.)?([A-Za-z0-9_-]\.?)+$')
32
ipv4_re = re.compile('^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$')
33
reverse_domain_re = re.compile(r'^(%\([abcd]\)d|[a-z0-9.-])+$')
34
ipv6_template_re = re.compile(r'^(%\([abcd]\)[dxX]|[A-Za-z0-9:-])+$')
35

36

37
class MACAddressFormField(forms.Field):
38
    default_error_messages = {
39
        'invalid': _(u'Enter a valid MAC address. %s'),
40 41
    }

42 43
    def validate(self, value):
        try:
44 45
            return MACAddressField.to_python.im_func(None, value)
        except (AddrFormatError, TypeError, ValidationError) as e:
46 47
            raise ValidationError(self.default_error_messages['invalid']
                                  % unicode(e))
48

49

50
class MACAddressField(models.Field):
51 52 53 54 55
    description = _('MAC Address object')
    __metaclass__ = models.SubfieldBase

    class mac_custom(mac_unix):
        word_fmt = '%.2X'
56

57 58 59 60
    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = 17
        super(MACAddressField, self).__init__(*args, **kwargs)

61 62
    def to_python(self, value):
        if not value:
63 64 65 66 67 68 69
            return None

        if isinstance(value, EUI):
            return value

        return EUI(value, dialect=MACAddressField.mac_custom)

70 71 72
    def get_internal_type(self):
        return 'CharField'

73 74
    def get_prep_value(self, value, prepared=False):
        if not value:
75 76 77 78 79
            return None

        if isinstance(value, EUI):
            return str(value)

80
        return value
81

82 83 84 85
    def formfield(self, **kwargs):
        defaults = {'form_class': MACAddressFormField}
        defaults.update(kwargs)
        return super(MACAddressField, self).formfield(**defaults)
86

87

88 89 90 91 92 93 94
class IPAddressFormField(forms.Field):
    default_error_messages = {
        'invalid': _(u'Enter a valid IP address. %s'),
    }

    def validate(self, value):
        try:
95 96
            IPAddressField(version=self.version).to_python(value)
        except (AddrFormatError, TypeError, ValueError) as e:
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
            raise ValidationError(self.default_error_messages['invalid']
                                  % unicode(e))

    def __init__(self, *args, **kwargs):
        self.version = kwargs['version']
        del kwargs['version']
        super(IPAddressFormField, self).__init__(*args, **kwargs)


class IPAddressField(models.Field):
    description = _('IP Network object')
    __metaclass__ = models.SubfieldBase

    def __init__(self, version=4, serialize=True, *args, **kwargs):
        kwargs['max_length'] = 100
        self.version = version
        super(IPAddressField, self).__init__(*args, **kwargs)

115 116 117 118 119
    def get_internal_type(self):
        return "CharField"

    def to_python(self, value):
        if not value:
120 121 122 123 124
            return None

        if isinstance(value, IPAddress):
            return value

125
        return IPAddress(value.split('/')[0], version=self.version,
126
                         flags=ZEROFILL)
127

128 129
    def get_prep_value(self, value, prepared=False):
        if not value:
130 131 132 133
            return None

        if isinstance(value, IPAddress):
            if self.version == 4:
134
                return '.'.join("%03d" % x for x in value.words)
135
            else:
136
                return ':'.join("%04X" % x for x in value.words)
137 138 139 140 141 142 143 144 145
        return value

    def formfield(self, **kwargs):
        defaults = {'form_class': IPAddressFormField}
        defaults['version'] = self.version
        defaults.update(kwargs)
        return super(IPAddressField, self).formfield(**defaults)


146 147 148 149 150 151 152
class IPNetworkFormField(forms.Field):
    default_error_messages = {
        'invalid': _(u'Enter a valid IP network. %s'),
    }

    def validate(self, value):
        try:
153 154
            return IPNetworkField(version=self.version).to_python(value)
        except (AddrFormatError, TypeError) as e:
155 156 157 158 159 160 161 162 163 164 165 166 167
            raise ValidationError(self.default_error_messages['invalid']
                                  % unicode(e))

    def __init__(self, *args, **kwargs):
        self.version = kwargs['version']
        del kwargs['version']
        super(IPNetworkFormField, self).__init__(*args, **kwargs)


class IPNetworkField(models.Field):
    description = _('IP Network object')
    __metaclass__ = models.SubfieldBase

168
    def __init__(self, version=4, serialize=True, *args, **kwargs):
169 170 171 172
        kwargs['max_length'] = 100
        self.version = version
        super(IPNetworkField, self).__init__(*args, **kwargs)

173 174
    def to_python(self, value):
        if not value:
175 176 177 178 179
            return None

        if isinstance(value, IPNetwork):
            return value

180
        return IPNetwork(value, version=self.version)
181 182 183 184

    def get_internal_type(self):
        return "CharField"

185 186
    def get_prep_value(self, value, prepared=False):
        if not value:
187 188 189 190
            return None

        if isinstance(value, IPNetwork):
            if self.version == 4:
191
                return ('.'.join("%03d" % x for x in value.ip.words)
192
                        + '/%02d' % value.prefixlen)
193
            else:
194
                return (':'.join("%04X" % x for x in value.ip.words)
195
                        + '/%03d' % value.prefixlen)
196 197 198 199 200 201 202 203
        return value

    def formfield(self, **kwargs):
        defaults = {'form_class': IPNetworkFormField}
        defaults['version'] = self.version
        defaults.update(kwargs)
        return super(IPNetworkField, self).formfield(**defaults)

204

205
add_introspection_rules([], ["firewall\.fields\."])
206 207


208 209 210 211
def val_alfanum(value):
    """Validate whether the parameter is a valid alphanumeric value."""
    if not alfanum_re.match(value):
        raise ValidationError(_(u'%s - only letters, numbers, underscores '
212 213
                                'and hyphens are allowed!') % value)

214 215 216 217 218

def is_valid_domain(value):
    """Check whether the parameter is a valid domain name."""
    return domain_re.match(value) is not None

219

220 221 222 223 224
def is_valid_domain_wildcard(value):
    """Check whether the parameter is a valid domain name."""
    return domain_wildcard_re.match(value) is not None


225 226 227 228
def val_domain(value):
    """Validate whether the parameter is a valid domin name."""
    if not is_valid_domain(value):
        raise ValidationError(_(u'%s - invalid domain name') % value)
229 230 231 232 233 234


def val_domain_wildcard(value):
    """Validate whether the parameter is a valid domin name."""
    if not is_valid_domain_wildcard(value):
        raise ValidationError(_(u'%s - invalid domain name') % value)
235

236

237 238 239 240
def is_valid_reverse_domain(value):
    """Check whether the parameter is a valid reverse domain name."""
    return reverse_domain_re.match(value) is not None

241

242 243 244 245 246
def val_reverse_domain(value):
    """Validate whether the parameter is a valid reverse domain name."""
    if not is_valid_reverse_domain(value):
        raise ValidationError(u'%s - invalid reverse domain name' % value)

247

248 249 250 251 252 253 254 255 256 257 258
def is_valid_ipv6_template(value):
    """Check whether the parameter is a valid ipv6 template."""
    return ipv6_template_re.match(value) is not None


def val_ipv6_template(value):
    """Validate whether the parameter is a valid ipv6 template."""
    if not is_valid_ipv6_template(value):
        raise ValidationError(u'%s - invalid reverse ipv6 template' % value)


259 260 261 262
def is_valid_ipv4_address(value):
    """Check whether the parameter is a valid IPv4 address."""
    return ipv4_re.match(value) is not None

263

264 265 266 267 268
def val_ipv4(value):
    """Validate whether the parameter is a valid IPv4 address."""
    if not is_valid_ipv4_address(value):
        raise ValidationError(_(u'%s - not an IPv4 address') % value)

269

270 271 272 273 274
def val_ipv6(value):
    """Validate whether the parameter is a valid IPv6 address."""
    if not is_valid_ipv6_address(value):
        raise ValidationError(_(u'%s - not an IPv6 address') % value)

275 276 277 278 279 280 281 282 283 284 285 286 287

def val_mx(value):
    """Validate whether the parameter is a valid MX address definition.

    Expected form is <priority>:<hostname>.
    """
    mx = value.split(':', 1)
    if not (len(mx) == 2 and mx[0].isdigit() and
            domain_re.match(mx[1])):
        raise ValidationError(_("Bad MX address format. "
                                "Should be: <priority>:<hostname>"))


288
def convert_ipv4_to_ipv6(ipv6_template, ipv4):
289
    """Convert IPv4 address string to IPv6 address string."""
290 291 292 293 294
    m = ipv4.words
    return IPAddress(ipv6_template % {'a': int(m[0]),
                                      'b': int(m[1]),
                                      'c': int(m[2]),
                                      'd': int(m[3])})