Merge lp:~rvb/maas/ipfield into lp:~maas-committers/maas/trunk

Proposed by Raphaël Badin
Status: Merged
Approved by: Raphaël Badin
Approved revision: no longer in the source branch.
Merged at revision: 2525
Proposed branch: lp:~rvb/maas/ipfield
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 694 lines (+445/-29)
8 files modified
src/maasserver/fields.py (+27/-0)
src/maasserver/migrations/0088_ip_to_custom_field.py (+377/-0)
src/maasserver/models/dhcplease.py (+5/-3)
src/maasserver/models/network.py (+3/-3)
src/maasserver/models/nodegroupinterface.py (+11/-11)
src/maasserver/models/staticipaddress.py (+3/-12)
src/maasserver/tests/models.py (+6/-0)
src/maasserver/tests/test_fields.py (+13/-0)
To merge this branch: bzr merge lp:~rvb/maas/ipfield
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+225804@code.launchpad.net

Commit message

Re-do the fix for bug 1338452 in a more consistent way: work around the Django bug (see reference in the code) by defining a custom IP field. Update all the IP fields in the codebase to use the new field.

Description of the change

Although this is a bit of a hack, it's much safer than having a workaround in just one place of the code.

I've tested that an upgrade to the package built from this branch works fine (objects with IP address fields look okay) and I ran a test in the lab with this branch.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) :
review: Approve
Revision history for this message
Julian Edwards (julian-edwards) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Please let's not have a repeat of the TestCase debacle, and call this
*MAAS*IPAddressField instead.

Also, surprised it needs a migration just for a field name change.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iEYEARECAAYFAlO7RT4ACgkQWhGlTF8G/HdakQCgjBvFZWZz+LoW6eoa8NFqmDO/
bSEAnipHz21PGLPmeBy0zTqgg0eKsiFp
=E/ET
-----END PGP SIGNATURE-----

Revision history for this message
Raphaël Badin (rvb) wrote :

On 07/08/2014 03:12 AM, Julian Edwards wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Please let's not have a repeat of the TestCase debacle, and call this
> *MAAS*IPAddressField instead.

Fair point. Done.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/fields.py'
--- src/maasserver/fields.py 2014-05-19 14:44:58 +0000
+++ src/maasserver/fields.py 2014-07-08 07:55:43 +0000
@@ -14,6 +14,7 @@
14__metaclass__ = type14__metaclass__ = type
15__all__ = [15__all__ = [
16 "EditableBinaryField",16 "EditableBinaryField",
17 "MAASIPAddressField",
17 "MAC",18 "MAC",
18 "MACAddressField",19 "MACAddressField",
19 "MACAddressFormField",20 "MACAddressFormField",
@@ -33,6 +34,7 @@
33from django.db.models import (34from django.db.models import (
34 BinaryField,35 BinaryField,
35 Field,36 Field,
37 GenericIPAddressField,
36 SubfieldBase,38 SubfieldBase,
37 )39 )
38from django.forms import (40from django.forms import (
@@ -90,6 +92,7 @@
90 "^maasserver\.fields\.JSONObjectField",92 "^maasserver\.fields\.JSONObjectField",
91 "^maasserver\.fields\.XMLField",93 "^maasserver\.fields\.XMLField",
92 "^maasserver\.fields\.EditableBinaryField",94 "^maasserver\.fields\.EditableBinaryField",
95 "^maasserver\.fields\.MAASIPAddressField",
93 ])96 ])
9497
9598
@@ -385,3 +388,27 @@
385 def __init__(self, *args, **kwargs):388 def __init__(self, *args, **kwargs):
386 super(EditableBinaryField, self).__init__(*args, **kwargs)389 super(EditableBinaryField, self).__init__(*args, **kwargs)
387 self.editable = True390 self.editable = True
391
392
393class MAASIPAddressField(GenericIPAddressField):
394 """A version of GenericIPAddressField with a custom get_internal_type().
395
396 This class exists to work around a bug in Django that inserts a HOST() cast
397 on the IP, causing the wrong comparison on the IP field. See
398 https://code.djangoproject.com/ticket/11442 for details.
399 """
400
401 def get_internal_type(self):
402 """Returns a value different from 'GenericIPAddressField' and
403 'IPAddressField' to force Django not to use a HOST() case when
404 performing operation on this field.
405 """
406 return "IPField"
407
408 def db_type(self, connection):
409 """Returns the database column data type for IPAddressField.
410
411 Override the default implementation which uses get_internal_type()
412 and force a 'inet' type field.
413 """
414 return 'inet'
388415
=== added file 'src/maasserver/migrations/0088_ip_to_custom_field.py'
--- src/maasserver/migrations/0088_ip_to_custom_field.py 1970-01-01 00:00:00 +0000
+++ src/maasserver/migrations/0088_ip_to_custom_field.py 2014-07-08 07:55:43 +0000
@@ -0,0 +1,377 @@
1from django.db import models
2from south.db import db
3# -*- coding: utf-8 -*-
4from south.utils import datetime_utils as datetime
5from south.v2 import SchemaMigration
6
7
8class Migration(SchemaMigration):
9
10 def forwards(self, orm):
11
12 # Changing field 'NodeGroupInterface.ip_range_high'
13 db.alter_column(u'maasserver_nodegroupinterface', 'ip_range_high', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True))
14
15 # Changing field 'NodeGroupInterface.static_ip_range_low'
16 db.alter_column(u'maasserver_nodegroupinterface', 'static_ip_range_low', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True))
17
18 # Changing field 'NodeGroupInterface.ip'
19 db.alter_column(u'maasserver_nodegroupinterface', 'ip', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39))
20
21 # Changing field 'NodeGroupInterface.ip_range_low'
22 db.alter_column(u'maasserver_nodegroupinterface', 'ip_range_low', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True))
23
24 # Changing field 'NodeGroupInterface.subnet_mask'
25 db.alter_column(u'maasserver_nodegroupinterface', 'subnet_mask', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True))
26
27 # Changing field 'NodeGroupInterface.broadcast_ip'
28 db.alter_column(u'maasserver_nodegroupinterface', 'broadcast_ip', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True))
29
30 # Changing field 'NodeGroupInterface.router_ip'
31 db.alter_column(u'maasserver_nodegroupinterface', 'router_ip', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True))
32
33 # Changing field 'NodeGroupInterface.foreign_dhcp_ip'
34 db.alter_column(u'maasserver_nodegroupinterface', 'foreign_dhcp_ip', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True))
35
36 # Changing field 'NodeGroupInterface.static_ip_range_high'
37 db.alter_column(u'maasserver_nodegroupinterface', 'static_ip_range_high', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True))
38
39 # Changing field 'StaticIPAddress.ip'
40 db.alter_column(u'maasserver_staticipaddress', 'ip', self.gf('maasserver.fields.MAASIPAddressField')(unique=True, max_length=39))
41
42 # Changing field 'DHCPLease.ip'
43 db.alter_column(u'maasserver_dhcplease', 'ip', self.gf('maasserver.fields.MAASIPAddressField')(unique=True, max_length=39))
44
45 # Changing field 'Network.ip'
46 db.alter_column(u'maasserver_network', 'ip', self.gf('maasserver.fields.MAASIPAddressField')(unique=True, max_length=39))
47
48 # Changing field 'Network.netmask'
49 db.alter_column(u'maasserver_network', 'netmask', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39))
50
51 def backwards(self, orm):
52
53 # Changing field 'NodeGroupInterface.ip_range_high'
54 db.alter_column(u'maasserver_nodegroupinterface', 'ip_range_high', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True))
55
56 # Changing field 'NodeGroupInterface.static_ip_range_low'
57 db.alter_column(u'maasserver_nodegroupinterface', 'static_ip_range_low', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True))
58
59 # Changing field 'NodeGroupInterface.ip'
60 db.alter_column(u'maasserver_nodegroupinterface', 'ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39))
61
62 # Changing field 'NodeGroupInterface.ip_range_low'
63 db.alter_column(u'maasserver_nodegroupinterface', 'ip_range_low', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True))
64
65 # Changing field 'NodeGroupInterface.subnet_mask'
66 db.alter_column(u'maasserver_nodegroupinterface', 'subnet_mask', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True))
67
68 # Changing field 'NodeGroupInterface.broadcast_ip'
69 db.alter_column(u'maasserver_nodegroupinterface', 'broadcast_ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True))
70
71 # Changing field 'NodeGroupInterface.router_ip'
72 db.alter_column(u'maasserver_nodegroupinterface', 'router_ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True))
73
74 # Changing field 'NodeGroupInterface.foreign_dhcp_ip'
75 db.alter_column(u'maasserver_nodegroupinterface', 'foreign_dhcp_ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True))
76
77 # Changing field 'NodeGroupInterface.static_ip_range_high'
78 db.alter_column(u'maasserver_nodegroupinterface', 'static_ip_range_high', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True))
79
80 # Changing field 'StaticIPAddress.ip'
81 db.alter_column(u'maasserver_staticipaddress', 'ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, unique=True))
82
83 # Changing field 'DHCPLease.ip'
84 db.alter_column(u'maasserver_dhcplease', 'ip', self.gf('django.db.models.fields.IPAddressField')(max_length=15, unique=True))
85
86 # Changing field 'Network.ip'
87 db.alter_column(u'maasserver_network', 'ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, unique=True))
88
89 # Changing field 'Network.netmask'
90 db.alter_column(u'maasserver_network', 'netmask', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39))
91
92 models = {
93 u'auth.group': {
94 'Meta': {'object_name': 'Group'},
95 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
96 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
97 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
98 },
99 u'auth.permission': {
100 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
101 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
102 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
103 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
104 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
105 },
106 u'auth.user': {
107 'Meta': {'object_name': 'User'},
108 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
109 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
110 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
111 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
112 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
113 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
114 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
115 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
116 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
117 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
118 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
119 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
120 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
121 },
122 u'contenttypes.contenttype': {
123 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
124 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
125 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
126 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
127 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
128 },
129 u'maasserver.bootimage': {
130 'Meta': {'unique_together': "((u'nodegroup', u'osystem', u'architecture', u'subarchitecture', u'release', u'purpose', u'label'),)", 'object_name': 'BootImage'},
131 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
132 'created': ('django.db.models.fields.DateTimeField', [], {}),
133 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
134 'label': ('django.db.models.fields.CharField', [], {'default': "u'release'", 'max_length': '255'}),
135 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
136 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
137 'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
138 'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
139 'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
140 'supported_subarches': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
141 'updated': ('django.db.models.fields.DateTimeField', [], {}),
142 'xinstall_path': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
143 'xinstall_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '30', 'null': 'True', 'blank': 'True'})
144 },
145 u'maasserver.bootsource': {
146 'Meta': {'object_name': 'BootSource'},
147 'cluster': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
148 'created': ('django.db.models.fields.DateTimeField', [], {}),
149 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
150 'keyring_data': ('maasserver.fields.EditableBinaryField', [], {'blank': 'True'}),
151 'keyring_filename': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}),
152 'updated': ('django.db.models.fields.DateTimeField', [], {}),
153 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
154 },
155 u'maasserver.bootsourceselection': {
156 'Meta': {'object_name': 'BootSourceSelection'},
157 'arches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
158 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}),
159 'created': ('django.db.models.fields.DateTimeField', [], {}),
160 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
161 'labels': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
162 'release': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
163 'subarches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}),
164 'updated': ('django.db.models.fields.DateTimeField', [], {})
165 },
166 u'maasserver.componenterror': {
167 'Meta': {'object_name': 'ComponentError'},
168 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
169 'created': ('django.db.models.fields.DateTimeField', [], {}),
170 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
171 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
172 'updated': ('django.db.models.fields.DateTimeField', [], {})
173 },
174 u'maasserver.config': {
175 'Meta': {'object_name': 'Config'},
176 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
177 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
178 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
179 },
180 u'maasserver.dhcplease': {
181 'Meta': {'object_name': 'DHCPLease'},
182 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
183 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
184 'mac': ('maasserver.fields.MACAddressField', [], {}),
185 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"})
186 },
187 u'maasserver.downloadprogress': {
188 'Meta': {'object_name': 'DownloadProgress'},
189 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
190 'created': ('django.db.models.fields.DateTimeField', [], {}),
191 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}),
192 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
193 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
194 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
195 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
196 'updated': ('django.db.models.fields.DateTimeField', [], {})
197 },
198 u'maasserver.filestorage': {
199 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'},
200 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}),
201 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
202 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
203 'key': ('django.db.models.fields.CharField', [], {'default': "u'1ed17c44-0672-11e4-aedf-9c4e363b1c94'", 'unique': 'True', 'max_length': '36'}),
204 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
205 },
206 u'maasserver.licensekey': {
207 'Meta': {'unique_together': "((u'osystem', u'distro_series'),)", 'object_name': 'LicenseKey'},
208 'created': ('django.db.models.fields.DateTimeField', [], {}),
209 'distro_series': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
210 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
211 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
212 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
213 'updated': ('django.db.models.fields.DateTimeField', [], {})
214 },
215 u'maasserver.macaddress': {
216 'Meta': {'object_name': 'MACAddress'},
217 'cluster_interface': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['maasserver.NodeGroupInterface']", 'null': 'True', 'blank': 'True'}),
218 'created': ('django.db.models.fields.DateTimeField', [], {}),
219 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
220 'ip_addresses': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.StaticIPAddress']", 'symmetrical': 'False', 'through': u"orm['maasserver.MACStaticIPAddressLink']", 'blank': 'True'}),
221 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}),
222 'networks': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Network']", 'symmetrical': 'False', 'blank': 'True'}),
223 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
224 'updated': ('django.db.models.fields.DateTimeField', [], {})
225 },
226 u'maasserver.macstaticipaddresslink': {
227 'Meta': {'unique_together': "((u'ip_address', u'mac_address'),)", 'object_name': 'MACStaticIPAddressLink'},
228 'created': ('django.db.models.fields.DateTimeField', [], {}),
229 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
230 'ip_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.StaticIPAddress']", 'unique': 'True'}),
231 'mac_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.MACAddress']"}),
232 'nic_alias': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
233 'updated': ('django.db.models.fields.DateTimeField', [], {})
234 },
235 u'maasserver.network': {
236 'Meta': {'object_name': 'Network'},
237 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
238 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
239 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
240 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
241 'netmask': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}),
242 'vlan_tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'null': 'True', 'blank': 'True'})
243 },
244 u'maasserver.node': {
245 'Meta': {'object_name': 'Node'},
246 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
247 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '31'}),
248 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
249 'created': ('django.db.models.fields.DateTimeField', [], {}),
250 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
251 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
252 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}),
253 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
254 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
255 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
256 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
257 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
258 'osystem': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}),
259 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
260 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
261 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
262 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}),
263 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
264 'storage': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
265 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-1ececdb4-0672-11e4-aedf-9c4e363b1c94'", 'unique': 'True', 'max_length': '41'}),
266 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}),
267 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}),
268 'updated': ('django.db.models.fields.DateTimeField', [], {}),
269 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'})
270 },
271 u'maasserver.nodegroup': {
272 'Meta': {'object_name': 'NodeGroup'},
273 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}),
274 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}),
275 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}),
276 'created': ('django.db.models.fields.DateTimeField', [], {}),
277 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
278 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
279 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
280 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
281 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
282 'updated': ('django.db.models.fields.DateTimeField', [], {}),
283 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
284 },
285 u'maasserver.nodegroupinterface': {
286 'Meta': {'unique_together': "((u'nodegroup', u'interface'),)", 'object_name': 'NodeGroupInterface'},
287 'broadcast_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
288 'created': ('django.db.models.fields.DateTimeField', [], {}),
289 'foreign_dhcp_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
290 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
291 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
292 'ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}),
293 'ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
294 'ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
295 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
296 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
297 'router_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
298 'static_ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
299 'static_ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
300 'subnet_mask': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
301 'updated': ('django.db.models.fields.DateTimeField', [], {})
302 },
303 u'maasserver.sshkey': {
304 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'},
305 'created': ('django.db.models.fields.DateTimeField', [], {}),
306 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
307 'key': ('django.db.models.fields.TextField', [], {}),
308 'updated': ('django.db.models.fields.DateTimeField', [], {}),
309 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
310 },
311 u'maasserver.sslkey': {
312 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSLKey'},
313 'created': ('django.db.models.fields.DateTimeField', [], {}),
314 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
315 'key': ('django.db.models.fields.TextField', [], {}),
316 'updated': ('django.db.models.fields.DateTimeField', [], {}),
317 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
318 },
319 u'maasserver.staticipaddress': {
320 'Meta': {'object_name': 'StaticIPAddress'},
321 'alloc_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
322 'created': ('django.db.models.fields.DateTimeField', [], {}),
323 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
324 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}),
325 'updated': ('django.db.models.fields.DateTimeField', [], {}),
326 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
327 },
328 u'maasserver.tag': {
329 'Meta': {'object_name': 'Tag'},
330 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
331 'created': ('django.db.models.fields.DateTimeField', [], {}),
332 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
333 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
334 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
335 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
336 'updated': ('django.db.models.fields.DateTimeField', [], {})
337 },
338 u'maasserver.userprofile': {
339 'Meta': {'object_name': 'UserProfile'},
340 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
341 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
342 },
343 u'maasserver.zone': {
344 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'},
345 'created': ('django.db.models.fields.DateTimeField', [], {}),
346 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
347 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
348 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
349 'updated': ('django.db.models.fields.DateTimeField', [], {})
350 },
351 u'piston.consumer': {
352 'Meta': {'object_name': 'Consumer'},
353 'description': ('django.db.models.fields.TextField', [], {}),
354 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
355 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
356 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
357 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
358 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
359 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"})
360 },
361 u'piston.token': {
362 'Meta': {'object_name': 'Token'},
363 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
364 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
365 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}),
366 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
367 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
368 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
369 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
370 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1404804793L'}),
371 'token_type': ('django.db.models.fields.IntegerField', [], {}),
372 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}),
373 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
374 }
375 }
376
377 complete_apps = ['maasserver']
0\ No newline at end of file378\ No newline at end of file
1379
=== modified file 'src/maasserver/models/dhcplease.py'
--- src/maasserver/models/dhcplease.py 2014-07-03 12:29:46 +0000
+++ src/maasserver/models/dhcplease.py 2014-07-08 07:55:43 +0000
@@ -20,7 +20,6 @@
20from django.db import connection20from django.db import connection
21from django.db.models import (21from django.db.models import (
22 ForeignKey,22 ForeignKey,
23 IPAddressField,
24 Manager,23 Manager,
25 Model,24 Model,
26 )25 )
@@ -28,7 +27,10 @@
28from django.dispatch import receiver27from django.dispatch import receiver
29from maasserver import DefaultMeta28from maasserver import DefaultMeta
30from maasserver.enum import NODE_STATUS29from maasserver.enum import NODE_STATUS
31from maasserver.fields import MACAddressField30from maasserver.fields import (
31 MAASIPAddressField,
32 MACAddressField,
33 )
32from maasserver.models.cleansave import CleanSave34from maasserver.models.cleansave import CleanSave
33from maasserver.models.macaddress import MACAddress35from maasserver.models.macaddress import MACAddress
34from maasserver.utils import strip_domain36from maasserver.utils import strip_domain
@@ -159,7 +161,7 @@
159 objects = DHCPLeaseManager()161 objects = DHCPLeaseManager()
160162
161 nodegroup = ForeignKey('maasserver.NodeGroup', null=False, editable=False)163 nodegroup = ForeignKey('maasserver.NodeGroup', null=False, editable=False)
162 ip = IPAddressField(null=False, editable=False, unique=True)164 ip = MAASIPAddressField(null=False, editable=False, unique=True)
163 mac = MACAddressField(null=False, editable=False, unique=False)165 mac = MACAddressField(null=False, editable=False, unique=False)
164166
165 def __unicode__(self):167 def __unicode__(self):
166168
=== modified file 'src/maasserver/models/network.py'
--- src/maasserver/models/network.py 2014-06-30 14:46:10 +0000
+++ src/maasserver/models/network.py 2014-07-08 07:55:43 +0000
@@ -27,13 +27,13 @@
27from django.core.validators import RegexValidator27from django.core.validators import RegexValidator
28from django.db.models import (28from django.db.models import (
29 CharField,29 CharField,
30 GenericIPAddressField,
31 Manager,30 Manager,
32 Model,31 Model,
33 PositiveSmallIntegerField,32 PositiveSmallIntegerField,
34 TextField,33 TextField,
35 )34 )
36from maasserver import DefaultMeta35from maasserver import DefaultMeta
36from maasserver.fields import MAASIPAddressField
37from maasserver.models.cleansave import CleanSave37from maasserver.models.cleansave import CleanSave
38from netaddr import IPAddress38from netaddr import IPAddress
39from netaddr.core import AddrFormatError39from netaddr.core import AddrFormatError
@@ -220,11 +220,11 @@
220 validators=[NETWORK_NAME_VALIDATOR],220 validators=[NETWORK_NAME_VALIDATOR],
221 help_text="Identifying name for this network.")221 help_text="Identifying name for this network.")
222222
223 ip = GenericIPAddressField(223 ip = MAASIPAddressField(
224 blank=False, editable=True, unique=True, null=False,224 blank=False, editable=True, unique=True, null=False,
225 help_text="Network address (e.g. 192.168.1.0).")225 help_text="Network address (e.g. 192.168.1.0).")
226226
227 netmask = GenericIPAddressField(227 netmask = MAASIPAddressField(
228 blank=False, editable=True, null=False,228 blank=False, editable=True, null=False,
229 help_text="Network mask (e.g. 255.255.255.0).")229 help_text="Network mask (e.g. 255.255.255.0).")
230230
231231
=== modified file 'src/maasserver/models/nodegroupinterface.py'
--- src/maasserver/models/nodegroupinterface.py 2014-07-07 09:31:31 +0000
+++ src/maasserver/models/nodegroupinterface.py 2014-07-08 07:55:43 +0000
@@ -23,7 +23,6 @@
23from django.db.models import (23from django.db.models import (
24 CharField,24 CharField,
25 ForeignKey,25 ForeignKey,
26 GenericIPAddressField,
27 IntegerField,26 IntegerField,
28 )27 )
29from maasserver import DefaultMeta28from maasserver import DefaultMeta
@@ -32,6 +31,7 @@
32 NODEGROUPINTERFACE_MANAGEMENT_CHOICES,31 NODEGROUPINTERFACE_MANAGEMENT_CHOICES,
33 NODEGROUPINTERFACE_MANAGEMENT_CHOICES_DICT,32 NODEGROUPINTERFACE_MANAGEMENT_CHOICES_DICT,
34 )33 )
34from maasserver.fields import MAASIPAddressField
35from maasserver.models.cleansave import CleanSave35from maasserver.models.cleansave import CleanSave
36from maasserver.models.timestampedmodel import TimestampedModel36from maasserver.models.timestampedmodel import TimestampedModel
37from netaddr import (37from netaddr import (
@@ -55,7 +55,7 @@
55 unique_together = ('nodegroup', 'interface')55 unique_together = ('nodegroup', 'interface')
5656
57 # Static IP of the interface.57 # Static IP of the interface.
58 ip = GenericIPAddressField(58 ip = MAASIPAddressField(
59 null=False, editable=True,59 null=False, editable=True,
60 help_text="Static IP Address of the interface",60 help_text="Static IP Address of the interface",
61 verbose_name="IP")61 verbose_name="IP")
@@ -72,33 +72,33 @@
72 interface = CharField(72 interface = CharField(
73 blank=True, editable=True, max_length=255, default='',73 blank=True, editable=True, max_length=255, default='',
74 help_text="Name of this interface (e.g. 'em1').")74 help_text="Name of this interface (e.g. 'em1').")
75 subnet_mask = GenericIPAddressField(75 subnet_mask = MAASIPAddressField(
76 editable=True, unique=False, blank=True, null=True, default=None,76 editable=True, unique=False, blank=True, null=True, default=None,
77 help_text="e.g. 255.255.255.0")77 help_text="e.g. 255.255.255.0")
78 broadcast_ip = GenericIPAddressField(78 broadcast_ip = MAASIPAddressField(
79 editable=True, unique=False, blank=True, null=True, default=None,79 editable=True, unique=False, blank=True, null=True, default=None,
80 verbose_name="Broadcast IP",80 verbose_name="Broadcast IP",
81 help_text="e.g. 192.168.1.255")81 help_text="e.g. 192.168.1.255")
82 router_ip = GenericIPAddressField(82 router_ip = MAASIPAddressField(
83 editable=True, unique=False, blank=True, null=True, default=None,83 editable=True, unique=False, blank=True, null=True, default=None,
84 verbose_name="Router IP",84 verbose_name="Router IP",
85 help_text="IP of this network's router given to DHCP clients")85 help_text="IP of this network's router given to DHCP clients")
86 ip_range_low = GenericIPAddressField(86 ip_range_low = MAASIPAddressField(
87 editable=True, unique=False, blank=True, null=True, default=None,87 editable=True, unique=False, blank=True, null=True, default=None,
88 verbose_name="DHCP dynamic IP range low value",88 verbose_name="DHCP dynamic IP range low value",
89 help_text="Lowest IP number of the range for dynamic IPs, used for "89 help_text="Lowest IP number of the range for dynamic IPs, used for "
90 "enlistment, commissioning and unknown devices.")90 "enlistment, commissioning and unknown devices.")
91 ip_range_high = GenericIPAddressField(91 ip_range_high = MAASIPAddressField(
92 editable=True, unique=False, blank=True, null=True, default=None,92 editable=True, unique=False, blank=True, null=True, default=None,
93 verbose_name="DHCP dynamic IP range high value",93 verbose_name="DHCP dynamic IP range high value",
94 help_text="Highest IP number of the range for dynamic IPs, used for "94 help_text="Highest IP number of the range for dynamic IPs, used for "
95 "enlistment, commissioning and unknown devices.")95 "enlistment, commissioning and unknown devices.")
96 static_ip_range_low = GenericIPAddressField(96 static_ip_range_low = MAASIPAddressField(
97 editable=True, unique=False, blank=True, null=True, default=None,97 editable=True, unique=False, blank=True, null=True, default=None,
98 verbose_name="Static IP range low value",98 verbose_name="Static IP range low value",
99 help_text="Lowest IP number of the range for IPs given to allocated "99 help_text="Lowest IP number of the range for IPs given to allocated "
100 "nodes, must be in same network as dynamic range.")100 "nodes, must be in same network as dynamic range.")
101 static_ip_range_high = GenericIPAddressField(101 static_ip_range_high = MAASIPAddressField(
102 editable=True, unique=False, blank=True, null=True, default=None,102 editable=True, unique=False, blank=True, null=True, default=None,
103 verbose_name="Static IP range high value",103 verbose_name="Static IP range high value",
104 help_text="Highest IP number of the range for IPs given to allocated "104 help_text="Highest IP number of the range for IPs given to allocated "
@@ -106,7 +106,7 @@
106106
107 # Foreign DHCP server address, if any, that was detected on this107 # Foreign DHCP server address, if any, that was detected on this
108 # interface.108 # interface.
109 foreign_dhcp_ip = GenericIPAddressField(109 foreign_dhcp_ip = MAASIPAddressField(
110 null=True, default=None, editable=True, blank=True, unique=False)110 null=True, default=None, editable=True, blank=True, unique=False)
111111
112 @property112 @property
@@ -120,7 +120,7 @@
120 ip = self.ip120 ip = self.ip
121 netmask = self.subnet_mask121 netmask = self.subnet_mask
122 # Nullness check for GenericIPAddress fields is deliberately kept122 # Nullness check for GenericIPAddress fields is deliberately kept
123 # vague: GenericIPAddressField seems to represent nulls as empty123 # vague: MAASIPAddressField seems to represent nulls as empty
124 # strings.124 # strings.
125 if netmask:125 if netmask:
126 return make_network(ip, netmask).cidr126 return make_network(ip, netmask).cidr
127127
=== modified file 'src/maasserver/models/staticipaddress.py'
--- src/maasserver/models/staticipaddress.py 2014-07-07 10:14:12 +0000
+++ src/maasserver/models/staticipaddress.py 2014-07-08 07:55:43 +0000
@@ -28,13 +28,13 @@
28from django.db import connection28from django.db import connection
29from django.db.models import (29from django.db.models import (
30 ForeignKey,30 ForeignKey,
31 GenericIPAddressField,
32 IntegerField,31 IntegerField,
33 Manager,32 Manager,
34 )33 )
35from maasserver import DefaultMeta34from maasserver import DefaultMeta
36from maasserver.enum import IPADDRESS_TYPE35from maasserver.enum import IPADDRESS_TYPE
37from maasserver.exceptions import StaticIPAddressExhaustion36from maasserver.exceptions import StaticIPAddressExhaustion
37from maasserver.fields import MAASIPAddressField
38from maasserver.models.cleansave import CleanSave38from maasserver.models.cleansave import CleanSave
39from maasserver.models.timestampedmodel import TimestampedModel39from maasserver.models.timestampedmodel import TimestampedModel
40from maasserver.utils import strip_domain40from maasserver.utils import strip_domain
@@ -82,16 +82,7 @@
82 # When we do ipv6, this needs changing.82 # When we do ipv6, this needs changing.
83 range_list = [ip.ipv4().format() for ip in static_range]83 range_list = [ip.ipv4().format() for ip in static_range]
8484
85 # We're using a raw query to bypass a bug in Django that inserts85 existing = self.filter(ip__gte=range_low, ip__lte=range_high)
86 # a HOST() cast on the IP, causing the wrong comparison on the
87 # IP field.
88 # https://code.djangoproject.com/ticket/11442
89 existing = StaticIPAddress.objects.raw(
90 """
91 SELECT * FROM maasserver_staticipaddress
92 WHERE IP >= %s AND IP <= %s
93 """,
94 [range_low, range_high])
9586
96 # Calculate the set of available IPs. This will be inefficient87 # Calculate the set of available IPs. This will be inefficient
97 # with large sets, but it will do for now.88 # with large sets, but it will do for now.
@@ -169,7 +160,7 @@
169 verbose_name = "Static IP Address"160 verbose_name = "Static IP Address"
170 verbose_name_plural = "Static IP Addresses"161 verbose_name_plural = "Static IP Addresses"
171162
172 ip = GenericIPAddressField(163 ip = MAASIPAddressField(
173 unique=True, null=False, editable=False, blank=False,164 unique=True, null=False, editable=False, blank=False,
174 verbose_name='IP')165 verbose_name='IP')
175166
176167
=== modified file 'src/maasserver/tests/models.py'
--- src/maasserver/tests/models.py 2013-10-07 09:12:40 +0000
+++ src/maasserver/tests/models.py 2014-07-08 07:55:43 +0000
@@ -13,6 +13,7 @@
1313
14__metaclass__ = type14__metaclass__ = type
15__all__ = [15__all__ = [
16 'MAASIPAddressFieldModel',
16 'JSONFieldModel',17 'JSONFieldModel',
17 'FieldChangeTestModel',18 'FieldChangeTestModel',
18 ]19 ]
@@ -24,6 +25,7 @@
24 )25 )
25from maasserver.fields import (26from maasserver.fields import (
26 JSONObjectField,27 JSONObjectField,
28 MAASIPAddressField,
27 XMLField,29 XMLField,
28 )30 )
29from maasserver.models.managers import BulkManager31from maasserver.models.managers import BulkManager
@@ -67,3 +69,7 @@
67 parent = ForeignKey('BulkManagerParentTestModel', editable=False)69 parent = ForeignKey('BulkManagerParentTestModel', editable=False)
6870
69 objects = BulkManager()71 objects = BulkManager()
72
73
74class MAASIPAddressFieldModel(Model):
75 ip_address = MAASIPAddressField()
7076
=== modified file 'src/maasserver/tests/test_fields.py'
--- src/maasserver/tests/test_fields.py 2014-07-02 07:05:24 +0000
+++ src/maasserver/tests/test_fields.py 2014-07-08 07:55:43 +0000
@@ -42,6 +42,7 @@
42from maasserver.testing.testcase import MAASServerTestCase42from maasserver.testing.testcase import MAASServerTestCase
43from maasserver.tests.models import (43from maasserver.tests.models import (
44 JSONFieldModel,44 JSONFieldModel,
45 MAASIPAddressFieldModel,
45 XMLFieldModel,46 XMLFieldModel,
46 )47 )
47from maastesting.djangotestcase import TestModelMixin48from maastesting.djangotestcase import TestModelMixin
@@ -388,3 +389,15 @@
388389
389 def test_is_editable(self):390 def test_is_editable(self):
390 self.assertTrue(EditableBinaryField().editable)391 self.assertTrue(EditableBinaryField().editable)
392
393
394class TestMAASIPAddressField(TestModelMixin, MAASServerTestCase):
395
396 app = 'maasserver.tests'
397
398 def test_uses_ip_comparison(self):
399 ip_object = MAASIPAddressFieldModel.objects.create(
400 ip_address='192.0.2.99')
401 results = MAASIPAddressFieldModel.objects.filter(
402 ip_address__lte='192.0.2.100')
403 self.assertItemsEqual([ip_object], results)