Merge lp:~rvb/maas/ipfield into lp:~maas-committers/maas/trunk
- ipfield
- Merge into 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 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella (community) | Approve | ||
Review via email:
|
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
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Gavin Panella (allenap) : | # |
review:
Approve
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Julian Edwards (julian-edwards) wrote : | # |
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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*IPAddress
Fair point. Done.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/maasserver/fields.py' |
2 | --- src/maasserver/fields.py 2014-05-19 14:44:58 +0000 |
3 | +++ src/maasserver/fields.py 2014-07-08 07:55:43 +0000 |
4 | @@ -14,6 +14,7 @@ |
5 | __metaclass__ = type |
6 | __all__ = [ |
7 | "EditableBinaryField", |
8 | + "MAASIPAddressField", |
9 | "MAC", |
10 | "MACAddressField", |
11 | "MACAddressFormField", |
12 | @@ -33,6 +34,7 @@ |
13 | from django.db.models import ( |
14 | BinaryField, |
15 | Field, |
16 | + GenericIPAddressField, |
17 | SubfieldBase, |
18 | ) |
19 | from django.forms import ( |
20 | @@ -90,6 +92,7 @@ |
21 | "^maasserver\.fields\.JSONObjectField", |
22 | "^maasserver\.fields\.XMLField", |
23 | "^maasserver\.fields\.EditableBinaryField", |
24 | + "^maasserver\.fields\.MAASIPAddressField", |
25 | ]) |
26 | |
27 | |
28 | @@ -385,3 +388,27 @@ |
29 | def __init__(self, *args, **kwargs): |
30 | super(EditableBinaryField, self).__init__(*args, **kwargs) |
31 | self.editable = True |
32 | + |
33 | + |
34 | +class MAASIPAddressField(GenericIPAddressField): |
35 | + """A version of GenericIPAddressField with a custom get_internal_type(). |
36 | + |
37 | + This class exists to work around a bug in Django that inserts a HOST() cast |
38 | + on the IP, causing the wrong comparison on the IP field. See |
39 | + https://code.djangoproject.com/ticket/11442 for details. |
40 | + """ |
41 | + |
42 | + def get_internal_type(self): |
43 | + """Returns a value different from 'GenericIPAddressField' and |
44 | + 'IPAddressField' to force Django not to use a HOST() case when |
45 | + performing operation on this field. |
46 | + """ |
47 | + return "IPField" |
48 | + |
49 | + def db_type(self, connection): |
50 | + """Returns the database column data type for IPAddressField. |
51 | + |
52 | + Override the default implementation which uses get_internal_type() |
53 | + and force a 'inet' type field. |
54 | + """ |
55 | + return 'inet' |
56 | |
57 | === added file 'src/maasserver/migrations/0088_ip_to_custom_field.py' |
58 | --- src/maasserver/migrations/0088_ip_to_custom_field.py 1970-01-01 00:00:00 +0000 |
59 | +++ src/maasserver/migrations/0088_ip_to_custom_field.py 2014-07-08 07:55:43 +0000 |
60 | @@ -0,0 +1,377 @@ |
61 | +from django.db import models |
62 | +from south.db import db |
63 | +# -*- coding: utf-8 -*- |
64 | +from south.utils import datetime_utils as datetime |
65 | +from south.v2 import SchemaMigration |
66 | + |
67 | + |
68 | +class Migration(SchemaMigration): |
69 | + |
70 | + def forwards(self, orm): |
71 | + |
72 | + # Changing field 'NodeGroupInterface.ip_range_high' |
73 | + db.alter_column(u'maasserver_nodegroupinterface', 'ip_range_high', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True)) |
74 | + |
75 | + # Changing field 'NodeGroupInterface.static_ip_range_low' |
76 | + db.alter_column(u'maasserver_nodegroupinterface', 'static_ip_range_low', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True)) |
77 | + |
78 | + # Changing field 'NodeGroupInterface.ip' |
79 | + db.alter_column(u'maasserver_nodegroupinterface', 'ip', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39)) |
80 | + |
81 | + # Changing field 'NodeGroupInterface.ip_range_low' |
82 | + db.alter_column(u'maasserver_nodegroupinterface', 'ip_range_low', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True)) |
83 | + |
84 | + # Changing field 'NodeGroupInterface.subnet_mask' |
85 | + db.alter_column(u'maasserver_nodegroupinterface', 'subnet_mask', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True)) |
86 | + |
87 | + # Changing field 'NodeGroupInterface.broadcast_ip' |
88 | + db.alter_column(u'maasserver_nodegroupinterface', 'broadcast_ip', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True)) |
89 | + |
90 | + # Changing field 'NodeGroupInterface.router_ip' |
91 | + db.alter_column(u'maasserver_nodegroupinterface', 'router_ip', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True)) |
92 | + |
93 | + # Changing field 'NodeGroupInterface.foreign_dhcp_ip' |
94 | + db.alter_column(u'maasserver_nodegroupinterface', 'foreign_dhcp_ip', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True)) |
95 | + |
96 | + # Changing field 'NodeGroupInterface.static_ip_range_high' |
97 | + db.alter_column(u'maasserver_nodegroupinterface', 'static_ip_range_high', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39, null=True)) |
98 | + |
99 | + # Changing field 'StaticIPAddress.ip' |
100 | + db.alter_column(u'maasserver_staticipaddress', 'ip', self.gf('maasserver.fields.MAASIPAddressField')(unique=True, max_length=39)) |
101 | + |
102 | + # Changing field 'DHCPLease.ip' |
103 | + db.alter_column(u'maasserver_dhcplease', 'ip', self.gf('maasserver.fields.MAASIPAddressField')(unique=True, max_length=39)) |
104 | + |
105 | + # Changing field 'Network.ip' |
106 | + db.alter_column(u'maasserver_network', 'ip', self.gf('maasserver.fields.MAASIPAddressField')(unique=True, max_length=39)) |
107 | + |
108 | + # Changing field 'Network.netmask' |
109 | + db.alter_column(u'maasserver_network', 'netmask', self.gf('maasserver.fields.MAASIPAddressField')(max_length=39)) |
110 | + |
111 | + def backwards(self, orm): |
112 | + |
113 | + # Changing field 'NodeGroupInterface.ip_range_high' |
114 | + db.alter_column(u'maasserver_nodegroupinterface', 'ip_range_high', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True)) |
115 | + |
116 | + # Changing field 'NodeGroupInterface.static_ip_range_low' |
117 | + db.alter_column(u'maasserver_nodegroupinterface', 'static_ip_range_low', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True)) |
118 | + |
119 | + # Changing field 'NodeGroupInterface.ip' |
120 | + db.alter_column(u'maasserver_nodegroupinterface', 'ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)) |
121 | + |
122 | + # Changing field 'NodeGroupInterface.ip_range_low' |
123 | + db.alter_column(u'maasserver_nodegroupinterface', 'ip_range_low', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True)) |
124 | + |
125 | + # Changing field 'NodeGroupInterface.subnet_mask' |
126 | + db.alter_column(u'maasserver_nodegroupinterface', 'subnet_mask', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True)) |
127 | + |
128 | + # Changing field 'NodeGroupInterface.broadcast_ip' |
129 | + db.alter_column(u'maasserver_nodegroupinterface', 'broadcast_ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True)) |
130 | + |
131 | + # Changing field 'NodeGroupInterface.router_ip' |
132 | + db.alter_column(u'maasserver_nodegroupinterface', 'router_ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True)) |
133 | + |
134 | + # Changing field 'NodeGroupInterface.foreign_dhcp_ip' |
135 | + db.alter_column(u'maasserver_nodegroupinterface', 'foreign_dhcp_ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True)) |
136 | + |
137 | + # Changing field 'NodeGroupInterface.static_ip_range_high' |
138 | + db.alter_column(u'maasserver_nodegroupinterface', 'static_ip_range_high', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True)) |
139 | + |
140 | + # Changing field 'StaticIPAddress.ip' |
141 | + db.alter_column(u'maasserver_staticipaddress', 'ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, unique=True)) |
142 | + |
143 | + # Changing field 'DHCPLease.ip' |
144 | + db.alter_column(u'maasserver_dhcplease', 'ip', self.gf('django.db.models.fields.IPAddressField')(max_length=15, unique=True)) |
145 | + |
146 | + # Changing field 'Network.ip' |
147 | + db.alter_column(u'maasserver_network', 'ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, unique=True)) |
148 | + |
149 | + # Changing field 'Network.netmask' |
150 | + db.alter_column(u'maasserver_network', 'netmask', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39)) |
151 | + |
152 | + models = { |
153 | + u'auth.group': { |
154 | + 'Meta': {'object_name': 'Group'}, |
155 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
156 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
157 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
158 | + }, |
159 | + u'auth.permission': { |
160 | + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, |
161 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
162 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), |
163 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
164 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
165 | + }, |
166 | + u'auth.user': { |
167 | + 'Meta': {'object_name': 'User'}, |
168 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
169 | + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), |
170 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
171 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), |
172 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
173 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
174 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
175 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
176 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
177 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
178 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
179 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), |
180 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
181 | + }, |
182 | + u'contenttypes.contenttype': { |
183 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
184 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
185 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
186 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
187 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
188 | + }, |
189 | + u'maasserver.bootimage': { |
190 | + 'Meta': {'unique_together': "((u'nodegroup', u'osystem', u'architecture', u'subarchitecture', u'release', u'purpose', u'label'),)", 'object_name': 'BootImage'}, |
191 | + 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
192 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
193 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
194 | + 'label': ('django.db.models.fields.CharField', [], {'default': "u'release'", 'max_length': '255'}), |
195 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
196 | + 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
197 | + 'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
198 | + 'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
199 | + 'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
200 | + 'supported_subarches': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), |
201 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
202 | + 'xinstall_path': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), |
203 | + 'xinstall_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '30', 'null': 'True', 'blank': 'True'}) |
204 | + }, |
205 | + u'maasserver.bootsource': { |
206 | + 'Meta': {'object_name': 'BootSource'}, |
207 | + 'cluster': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}), |
208 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
209 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
210 | + 'keyring_data': ('maasserver.fields.EditableBinaryField', [], {'blank': 'True'}), |
211 | + 'keyring_filename': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}), |
212 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
213 | + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'}) |
214 | + }, |
215 | + u'maasserver.bootsourceselection': { |
216 | + 'Meta': {'object_name': 'BootSourceSelection'}, |
217 | + 'arches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), |
218 | + 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}), |
219 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
220 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
221 | + 'labels': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), |
222 | + 'release': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), |
223 | + 'subarches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), |
224 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
225 | + }, |
226 | + u'maasserver.componenterror': { |
227 | + 'Meta': {'object_name': 'ComponentError'}, |
228 | + 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}), |
229 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
230 | + 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}), |
231 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
232 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
233 | + }, |
234 | + u'maasserver.config': { |
235 | + 'Meta': {'object_name': 'Config'}, |
236 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
237 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
238 | + 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'}) |
239 | + }, |
240 | + u'maasserver.dhcplease': { |
241 | + 'Meta': {'object_name': 'DHCPLease'}, |
242 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
243 | + 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}), |
244 | + 'mac': ('maasserver.fields.MACAddressField', [], {}), |
245 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}) |
246 | + }, |
247 | + u'maasserver.downloadprogress': { |
248 | + 'Meta': {'object_name': 'DownloadProgress'}, |
249 | + 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
250 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
251 | + 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}), |
252 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
253 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
254 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
255 | + 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
256 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
257 | + }, |
258 | + u'maasserver.filestorage': { |
259 | + 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'}, |
260 | + 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}), |
261 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
262 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
263 | + 'key': ('django.db.models.fields.CharField', [], {'default': "u'1ed17c44-0672-11e4-aedf-9c4e363b1c94'", 'unique': 'True', 'max_length': '36'}), |
264 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
265 | + }, |
266 | + u'maasserver.licensekey': { |
267 | + 'Meta': {'unique_together': "((u'osystem', u'distro_series'),)", 'object_name': 'LicenseKey'}, |
268 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
269 | + 'distro_series': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
270 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
271 | + 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
272 | + 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
273 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
274 | + }, |
275 | + u'maasserver.macaddress': { |
276 | + 'Meta': {'object_name': 'MACAddress'}, |
277 | + 'cluster_interface': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['maasserver.NodeGroupInterface']", 'null': 'True', 'blank': 'True'}), |
278 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
279 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
280 | + 'ip_addresses': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.StaticIPAddress']", 'symmetrical': 'False', 'through': u"orm['maasserver.MACStaticIPAddressLink']", 'blank': 'True'}), |
281 | + 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}), |
282 | + 'networks': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Network']", 'symmetrical': 'False', 'blank': 'True'}), |
283 | + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}), |
284 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
285 | + }, |
286 | + u'maasserver.macstaticipaddresslink': { |
287 | + 'Meta': {'unique_together': "((u'ip_address', u'mac_address'),)", 'object_name': 'MACStaticIPAddressLink'}, |
288 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
289 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
290 | + 'ip_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.StaticIPAddress']", 'unique': 'True'}), |
291 | + 'mac_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.MACAddress']"}), |
292 | + 'nic_alias': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), |
293 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
294 | + }, |
295 | + u'maasserver.network': { |
296 | + 'Meta': {'object_name': 'Network'}, |
297 | + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
298 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
299 | + 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}), |
300 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), |
301 | + 'netmask': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}), |
302 | + 'vlan_tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'null': 'True', 'blank': 'True'}) |
303 | + }, |
304 | + u'maasserver.node': { |
305 | + 'Meta': {'object_name': 'Node'}, |
306 | + 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), |
307 | + 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '31'}), |
308 | + 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
309 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
310 | + 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), |
311 | + 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
312 | + 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}), |
313 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
314 | + 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), |
315 | + 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
316 | + 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
317 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}), |
318 | + 'osystem': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), |
319 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), |
320 | + 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), |
321 | + 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}), |
322 | + 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}), |
323 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}), |
324 | + 'storage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
325 | + 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-1ececdb4-0672-11e4-aedf-9c4e363b1c94'", 'unique': 'True', 'max_length': '41'}), |
326 | + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}), |
327 | + 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}), |
328 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
329 | + 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'}) |
330 | + }, |
331 | + u'maasserver.nodegroup': { |
332 | + 'Meta': {'object_name': 'NodeGroup'}, |
333 | + 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}), |
334 | + 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}), |
335 | + 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}), |
336 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
337 | + 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
338 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
339 | + 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
340 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}), |
341 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
342 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
343 | + 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}) |
344 | + }, |
345 | + u'maasserver.nodegroupinterface': { |
346 | + 'Meta': {'unique_together': "((u'nodegroup', u'interface'),)", 'object_name': 'NodeGroupInterface'}, |
347 | + 'broadcast_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
348 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
349 | + 'foreign_dhcp_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
350 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
351 | + 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
352 | + 'ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}), |
353 | + 'ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
354 | + 'ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
355 | + 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
356 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
357 | + 'router_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
358 | + 'static_ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
359 | + 'static_ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
360 | + 'subnet_mask': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
361 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
362 | + }, |
363 | + u'maasserver.sshkey': { |
364 | + 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'}, |
365 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
366 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
367 | + 'key': ('django.db.models.fields.TextField', [], {}), |
368 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
369 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) |
370 | + }, |
371 | + u'maasserver.sslkey': { |
372 | + 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSLKey'}, |
373 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
374 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
375 | + 'key': ('django.db.models.fields.TextField', [], {}), |
376 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
377 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) |
378 | + }, |
379 | + u'maasserver.staticipaddress': { |
380 | + 'Meta': {'object_name': 'StaticIPAddress'}, |
381 | + 'alloc_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
382 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
383 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
384 | + 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}), |
385 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
386 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
387 | + }, |
388 | + u'maasserver.tag': { |
389 | + 'Meta': {'object_name': 'Tag'}, |
390 | + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
391 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
392 | + 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
393 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
394 | + 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
395 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
396 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
397 | + }, |
398 | + u'maasserver.userprofile': { |
399 | + 'Meta': {'object_name': 'UserProfile'}, |
400 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
401 | + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) |
402 | + }, |
403 | + u'maasserver.zone': { |
404 | + 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'}, |
405 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
406 | + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
407 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
408 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
409 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
410 | + }, |
411 | + u'piston.consumer': { |
412 | + 'Meta': {'object_name': 'Consumer'}, |
413 | + 'description': ('django.db.models.fields.TextField', [], {}), |
414 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
415 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
416 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
417 | + 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
418 | + 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}), |
419 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"}) |
420 | + }, |
421 | + u'piston.token': { |
422 | + 'Meta': {'object_name': 'Token'}, |
423 | + 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
424 | + 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
425 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}), |
426 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
427 | + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
428 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
429 | + 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
430 | + 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1404804793L'}), |
431 | + 'token_type': ('django.db.models.fields.IntegerField', [], {}), |
432 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}), |
433 | + 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'}) |
434 | + } |
435 | + } |
436 | + |
437 | + complete_apps = ['maasserver'] |
438 | \ No newline at end of file |
439 | |
440 | === modified file 'src/maasserver/models/dhcplease.py' |
441 | --- src/maasserver/models/dhcplease.py 2014-07-03 12:29:46 +0000 |
442 | +++ src/maasserver/models/dhcplease.py 2014-07-08 07:55:43 +0000 |
443 | @@ -20,7 +20,6 @@ |
444 | from django.db import connection |
445 | from django.db.models import ( |
446 | ForeignKey, |
447 | - IPAddressField, |
448 | Manager, |
449 | Model, |
450 | ) |
451 | @@ -28,7 +27,10 @@ |
452 | from django.dispatch import receiver |
453 | from maasserver import DefaultMeta |
454 | from maasserver.enum import NODE_STATUS |
455 | -from maasserver.fields import MACAddressField |
456 | +from maasserver.fields import ( |
457 | + MAASIPAddressField, |
458 | + MACAddressField, |
459 | + ) |
460 | from maasserver.models.cleansave import CleanSave |
461 | from maasserver.models.macaddress import MACAddress |
462 | from maasserver.utils import strip_domain |
463 | @@ -159,7 +161,7 @@ |
464 | objects = DHCPLeaseManager() |
465 | |
466 | nodegroup = ForeignKey('maasserver.NodeGroup', null=False, editable=False) |
467 | - ip = IPAddressField(null=False, editable=False, unique=True) |
468 | + ip = MAASIPAddressField(null=False, editable=False, unique=True) |
469 | mac = MACAddressField(null=False, editable=False, unique=False) |
470 | |
471 | def __unicode__(self): |
472 | |
473 | === modified file 'src/maasserver/models/network.py' |
474 | --- src/maasserver/models/network.py 2014-06-30 14:46:10 +0000 |
475 | +++ src/maasserver/models/network.py 2014-07-08 07:55:43 +0000 |
476 | @@ -27,13 +27,13 @@ |
477 | from django.core.validators import RegexValidator |
478 | from django.db.models import ( |
479 | CharField, |
480 | - GenericIPAddressField, |
481 | Manager, |
482 | Model, |
483 | PositiveSmallIntegerField, |
484 | TextField, |
485 | ) |
486 | from maasserver import DefaultMeta |
487 | +from maasserver.fields import MAASIPAddressField |
488 | from maasserver.models.cleansave import CleanSave |
489 | from netaddr import IPAddress |
490 | from netaddr.core import AddrFormatError |
491 | @@ -220,11 +220,11 @@ |
492 | validators=[NETWORK_NAME_VALIDATOR], |
493 | help_text="Identifying name for this network.") |
494 | |
495 | - ip = GenericIPAddressField( |
496 | + ip = MAASIPAddressField( |
497 | blank=False, editable=True, unique=True, null=False, |
498 | help_text="Network address (e.g. 192.168.1.0).") |
499 | |
500 | - netmask = GenericIPAddressField( |
501 | + netmask = MAASIPAddressField( |
502 | blank=False, editable=True, null=False, |
503 | help_text="Network mask (e.g. 255.255.255.0).") |
504 | |
505 | |
506 | === modified file 'src/maasserver/models/nodegroupinterface.py' |
507 | --- src/maasserver/models/nodegroupinterface.py 2014-07-07 09:31:31 +0000 |
508 | +++ src/maasserver/models/nodegroupinterface.py 2014-07-08 07:55:43 +0000 |
509 | @@ -23,7 +23,6 @@ |
510 | from django.db.models import ( |
511 | CharField, |
512 | ForeignKey, |
513 | - GenericIPAddressField, |
514 | IntegerField, |
515 | ) |
516 | from maasserver import DefaultMeta |
517 | @@ -32,6 +31,7 @@ |
518 | NODEGROUPINTERFACE_MANAGEMENT_CHOICES, |
519 | NODEGROUPINTERFACE_MANAGEMENT_CHOICES_DICT, |
520 | ) |
521 | +from maasserver.fields import MAASIPAddressField |
522 | from maasserver.models.cleansave import CleanSave |
523 | from maasserver.models.timestampedmodel import TimestampedModel |
524 | from netaddr import ( |
525 | @@ -55,7 +55,7 @@ |
526 | unique_together = ('nodegroup', 'interface') |
527 | |
528 | # Static IP of the interface. |
529 | - ip = GenericIPAddressField( |
530 | + ip = MAASIPAddressField( |
531 | null=False, editable=True, |
532 | help_text="Static IP Address of the interface", |
533 | verbose_name="IP") |
534 | @@ -72,33 +72,33 @@ |
535 | interface = CharField( |
536 | blank=True, editable=True, max_length=255, default='', |
537 | help_text="Name of this interface (e.g. 'em1').") |
538 | - subnet_mask = GenericIPAddressField( |
539 | + subnet_mask = MAASIPAddressField( |
540 | editable=True, unique=False, blank=True, null=True, default=None, |
541 | help_text="e.g. 255.255.255.0") |
542 | - broadcast_ip = GenericIPAddressField( |
543 | + broadcast_ip = MAASIPAddressField( |
544 | editable=True, unique=False, blank=True, null=True, default=None, |
545 | verbose_name="Broadcast IP", |
546 | help_text="e.g. 192.168.1.255") |
547 | - router_ip = GenericIPAddressField( |
548 | + router_ip = MAASIPAddressField( |
549 | editable=True, unique=False, blank=True, null=True, default=None, |
550 | verbose_name="Router IP", |
551 | help_text="IP of this network's router given to DHCP clients") |
552 | - ip_range_low = GenericIPAddressField( |
553 | + ip_range_low = MAASIPAddressField( |
554 | editable=True, unique=False, blank=True, null=True, default=None, |
555 | verbose_name="DHCP dynamic IP range low value", |
556 | help_text="Lowest IP number of the range for dynamic IPs, used for " |
557 | "enlistment, commissioning and unknown devices.") |
558 | - ip_range_high = GenericIPAddressField( |
559 | + ip_range_high = MAASIPAddressField( |
560 | editable=True, unique=False, blank=True, null=True, default=None, |
561 | verbose_name="DHCP dynamic IP range high value", |
562 | help_text="Highest IP number of the range for dynamic IPs, used for " |
563 | "enlistment, commissioning and unknown devices.") |
564 | - static_ip_range_low = GenericIPAddressField( |
565 | + static_ip_range_low = MAASIPAddressField( |
566 | editable=True, unique=False, blank=True, null=True, default=None, |
567 | verbose_name="Static IP range low value", |
568 | help_text="Lowest IP number of the range for IPs given to allocated " |
569 | "nodes, must be in same network as dynamic range.") |
570 | - static_ip_range_high = GenericIPAddressField( |
571 | + static_ip_range_high = MAASIPAddressField( |
572 | editable=True, unique=False, blank=True, null=True, default=None, |
573 | verbose_name="Static IP range high value", |
574 | help_text="Highest IP number of the range for IPs given to allocated " |
575 | @@ -106,7 +106,7 @@ |
576 | |
577 | # Foreign DHCP server address, if any, that was detected on this |
578 | # interface. |
579 | - foreign_dhcp_ip = GenericIPAddressField( |
580 | + foreign_dhcp_ip = MAASIPAddressField( |
581 | null=True, default=None, editable=True, blank=True, unique=False) |
582 | |
583 | @property |
584 | @@ -120,7 +120,7 @@ |
585 | ip = self.ip |
586 | netmask = self.subnet_mask |
587 | # Nullness check for GenericIPAddress fields is deliberately kept |
588 | - # vague: GenericIPAddressField seems to represent nulls as empty |
589 | + # vague: MAASIPAddressField seems to represent nulls as empty |
590 | # strings. |
591 | if netmask: |
592 | return make_network(ip, netmask).cidr |
593 | |
594 | === modified file 'src/maasserver/models/staticipaddress.py' |
595 | --- src/maasserver/models/staticipaddress.py 2014-07-07 10:14:12 +0000 |
596 | +++ src/maasserver/models/staticipaddress.py 2014-07-08 07:55:43 +0000 |
597 | @@ -28,13 +28,13 @@ |
598 | from django.db import connection |
599 | from django.db.models import ( |
600 | ForeignKey, |
601 | - GenericIPAddressField, |
602 | IntegerField, |
603 | Manager, |
604 | ) |
605 | from maasserver import DefaultMeta |
606 | from maasserver.enum import IPADDRESS_TYPE |
607 | from maasserver.exceptions import StaticIPAddressExhaustion |
608 | +from maasserver.fields import MAASIPAddressField |
609 | from maasserver.models.cleansave import CleanSave |
610 | from maasserver.models.timestampedmodel import TimestampedModel |
611 | from maasserver.utils import strip_domain |
612 | @@ -82,16 +82,7 @@ |
613 | # When we do ipv6, this needs changing. |
614 | range_list = [ip.ipv4().format() for ip in static_range] |
615 | |
616 | - # We're using a raw query to bypass a bug in Django that inserts |
617 | - # a HOST() cast on the IP, causing the wrong comparison on the |
618 | - # IP field. |
619 | - # https://code.djangoproject.com/ticket/11442 |
620 | - existing = StaticIPAddress.objects.raw( |
621 | - """ |
622 | - SELECT * FROM maasserver_staticipaddress |
623 | - WHERE IP >= %s AND IP <= %s |
624 | - """, |
625 | - [range_low, range_high]) |
626 | + existing = self.filter(ip__gte=range_low, ip__lte=range_high) |
627 | |
628 | # Calculate the set of available IPs. This will be inefficient |
629 | # with large sets, but it will do for now. |
630 | @@ -169,7 +160,7 @@ |
631 | verbose_name = "Static IP Address" |
632 | verbose_name_plural = "Static IP Addresses" |
633 | |
634 | - ip = GenericIPAddressField( |
635 | + ip = MAASIPAddressField( |
636 | unique=True, null=False, editable=False, blank=False, |
637 | verbose_name='IP') |
638 | |
639 | |
640 | === modified file 'src/maasserver/tests/models.py' |
641 | --- src/maasserver/tests/models.py 2013-10-07 09:12:40 +0000 |
642 | +++ src/maasserver/tests/models.py 2014-07-08 07:55:43 +0000 |
643 | @@ -13,6 +13,7 @@ |
644 | |
645 | __metaclass__ = type |
646 | __all__ = [ |
647 | + 'MAASIPAddressFieldModel', |
648 | 'JSONFieldModel', |
649 | 'FieldChangeTestModel', |
650 | ] |
651 | @@ -24,6 +25,7 @@ |
652 | ) |
653 | from maasserver.fields import ( |
654 | JSONObjectField, |
655 | + MAASIPAddressField, |
656 | XMLField, |
657 | ) |
658 | from maasserver.models.managers import BulkManager |
659 | @@ -67,3 +69,7 @@ |
660 | parent = ForeignKey('BulkManagerParentTestModel', editable=False) |
661 | |
662 | objects = BulkManager() |
663 | + |
664 | + |
665 | +class MAASIPAddressFieldModel(Model): |
666 | + ip_address = MAASIPAddressField() |
667 | |
668 | === modified file 'src/maasserver/tests/test_fields.py' |
669 | --- src/maasserver/tests/test_fields.py 2014-07-02 07:05:24 +0000 |
670 | +++ src/maasserver/tests/test_fields.py 2014-07-08 07:55:43 +0000 |
671 | @@ -42,6 +42,7 @@ |
672 | from maasserver.testing.testcase import MAASServerTestCase |
673 | from maasserver.tests.models import ( |
674 | JSONFieldModel, |
675 | + MAASIPAddressFieldModel, |
676 | XMLFieldModel, |
677 | ) |
678 | from maastesting.djangotestcase import TestModelMixin |
679 | @@ -388,3 +389,15 @@ |
680 | |
681 | def test_is_editable(self): |
682 | self.assertTrue(EditableBinaryField().editable) |
683 | + |
684 | + |
685 | +class TestMAASIPAddressField(TestModelMixin, MAASServerTestCase): |
686 | + |
687 | + app = 'maasserver.tests' |
688 | + |
689 | + def test_uses_ip_comparison(self): |
690 | + ip_object = MAASIPAddressFieldModel.objects.create( |
691 | + ip_address='192.0.2.99') |
692 | + results = MAASIPAddressFieldModel.objects.filter( |
693 | + ip_address__lte='192.0.2.100') |
694 | + self.assertItemsEqual([ip_object], results) |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Please let's not have a repeat of the TestCase debacle, and call this Field instead.
*MAAS*IPAddress
Also, surprised it needs a migration just for a field name change. www.enigmail. net/
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
Comment: Using GnuPG with Thunderbird - http://
iEYEARECAAYFAlO 7RT4ACgkQWhGlTF 8G/HdakQCgjBvFZ WZz+LoW6eoa8NFq mDO/ meBy0zTqgg0eKsi Fp
bSEAnipHz21PGLP
=E/ET
-----END PGP SIGNATURE-----