Merge lp:~rvb/maas/default-zone into lp:~maas-committers/maas/trunk
- default-zone
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Raphaël Badin |
Approved revision: | no longer in the source branch. |
Merged at revision: | 1820 |
Proposed branch: | lp:~rvb/maas/default-zone |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
2062 lines (+980/-346) 23 files modified
src/maasserver/forms.py (+18/-9) src/maasserver/migrations/0060_add_zone_object.py (+2/-1) src/maasserver/migrations/0061_add_ref_from_node_to_zone.py (+0/-229) src/maasserver/migrations/0063_create_default_zone.py (+237/-0) src/maasserver/migrations/0064_set_default_zone.py (+236/-0) src/maasserver/migrations/0065_set_default_zone_as_model_default.py (+234/-0) src/maasserver/models/node.py (+4/-2) src/maasserver/models/tests/test_zone.py (+25/-3) src/maasserver/models/zone.py (+42/-13) src/maasserver/templates/maasserver/nodes_listing.html (+1/-5) src/maasserver/templates/maasserver/zone_detail.html (+10/-2) src/maasserver/templates/maasserver/zone_list.html (+4/-0) src/maasserver/testing/factory.py (+2/-2) src/maasserver/testing/tests/test_factory.py (+0/-6) src/maasserver/tests/test_api_node.py (+13/-0) src/maasserver/tests/test_api_nodes.py (+1/-14) src/maasserver/tests/test_api_zone.py (+37/-3) src/maasserver/tests/test_api_zones.py (+9/-3) src/maasserver/tests/test_forms.py (+53/-27) src/maasserver/tests/test_node_constraint_filter_forms.py (+1/-1) src/maasserver/tests/test_views_nodes.py (+1/-17) src/maasserver/tests/test_views_zones.py (+48/-8) src/maasserver/views/zones.py (+2/-1) |
To merge this branch: | bzr merge lp:~rvb/maas/default-zone |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jeroen T. Vermeulen (community) | Approve | ||
Review via email: mp+200635@code.launchpad.net |
Commit message
Add default zone object; fix node<->zone relation to not allow the zone to be None; remove the zone.delete() hack.
Description of the change
The main goal of this branch is to get nodes to be linked to a default zone instead of having their 'zone' field to be None by default. This branch is a bit big but only because it contains a couple of autogenerated migrations.
This work has been a bit complicated by the fact that we released a version of MAAS with the "None-zone" logic implemented so I had to create new migrations to transition to the new model instead of fixing the existing migrations. Turned out it wasn't that bad but I still meant more work and more testing.
The migration src/maasserver/
Drive-by fix: remove the hack in zone.py:
I tested upgrading from the current Trusty package to a package built from this branch and it behaved as expected: the default zone was created and nodes with a 'None' zone filed were connected to the default zone after the migration took place.
Raphaël Badin (rvb) wrote : | # |
> Having an entire "Is this the default zone" column with a "Yes" in exactly one
> row seems a bit officious. Normally I'd say: just append "(default)" to the
> name. But with the default zone's name fixed to "default," that would look
> ridiculous as well. How about we just leave it out? We could make the
> default zone look different by italicising its displayed name.
Yeah, good point. I tried italicising the name of the default zone but it really didn't look right. I think it's fine: it's named default and it doesn't have the icons to edit and remove it at the end of the line so it's pretty clear what it is and that it's special.
> Similarly, it looks a bit... clerical to say "Default / No" on every zone's
> details page and "Default / Yes" on the default zone's. Shouldn't we just
> have a notice like "This is the default zone. It cannot be renamed or
> deleted." on the default zone's page, and nothing on the rest? There may not
> even be a way to click through to that page without first noticing that there
> is a default zone.
True, done.
> Typo — test_updates_
> "_works" suffix, of the first "s".
Done.
> Finally, several things about test_rejects_
[..]
>
Fixed.
> If you want to verify that we actually got the ValidationError, use
> ExpectedException:
>
> with ExpectedExcepti
> self.client.post(
> ...)
>
> This will make the test fail if the expected exception is not raised.
> Arguably that makes the test more brittle if we ever abolish the
> ValidationError, but in that case the comments will need revisiting anyway so
> it's appropriate for the test to call attention to itself.
Well, I think it's a bit strange to code a test for something that is actually wrong so I left the try/except statement.
> Julian has been saying he would like us to use TODO markers only for reminders
> to ourselves, and never land them. It may be a little late for that though.
Okay, I've removed this TODO.
Thanks for the review!
Preview Diff
1 | === modified file 'src/maasserver/forms.py' |
2 | --- src/maasserver/forms.py 2014-01-16 02:57:53 +0000 |
3 | +++ src/maasserver/forms.py 2014-01-16 08:48:57 +0000 |
4 | @@ -217,6 +217,7 @@ |
5 | |
6 | zone = forms.ModelChoiceField( |
7 | label="Physical zone", required=False, |
8 | + initial=Zone.objects.get_default_zone(), |
9 | queryset=Zone.objects.all(), to_field_name='name') |
10 | |
11 | cpu_count = forms.IntegerField( |
12 | @@ -242,16 +243,13 @@ |
13 | def __init__(self, data=None, instance=None, **kwargs): |
14 | super(AdminNodeForm, self).__init__( |
15 | data=data, instance=instance, **kwargs) |
16 | - self.set_initial_zone(instance) |
17 | self.set_up_power_parameters_field(data, instance) |
18 | - |
19 | - def set_initial_zone(self, instance): |
20 | - if instance is not None: |
21 | - zone = instance.zone |
22 | - if zone is None: |
23 | - self.initial['zone'] = '' |
24 | - else: |
25 | - self.initial['zone'] = zone.name |
26 | + # The zone field is not required because we want to be able |
27 | + # to omit it when using that form in the API. |
28 | + # We don't want the UI to show an entry for the 'empty' zone, |
29 | + # in the zones dropdown. This is why we set 'empty_label' to |
30 | + # None to force Django not to display that empty entry. |
31 | + self.fields['zone'].empty_label = None |
32 | |
33 | def set_up_power_parameters_field(self, data, node): |
34 | """Setup the 'power_parameter' field based on the value for the |
35 | @@ -969,6 +967,7 @@ |
36 | # This adds an input field: the zone. |
37 | self.fields['zone'] = forms.ModelChoiceField( |
38 | label="Physical zone", required=False, |
39 | + initial=Zone.objects.get_default_zone(), |
40 | queryset=Zone.objects.all(), to_field_name='name') |
41 | self.fields['action'] = forms.ChoiceField( |
42 | required=True, choices=action_choices) |
43 | @@ -1108,3 +1107,13 @@ |
44 | 'name', |
45 | 'description', |
46 | ) |
47 | + |
48 | + def clean_name(self): |
49 | + new_name = self.cleaned_data['name'] |
50 | + renaming_instance = ( |
51 | + self.instance is not None and self.instance.is_default() and |
52 | + self.instance.name != new_name) |
53 | + if renaming_instance: |
54 | + raise forms.ValidationError( |
55 | + "This zone is the default zone, it cannot be renamed.") |
56 | + return self.cleaned_data['name'] |
57 | |
58 | === modified file 'src/maasserver/migrations/0060_add_zone_object.py' |
59 | --- src/maasserver/migrations/0060_add_zone_object.py 2013-12-10 11:42:13 +0000 |
60 | +++ src/maasserver/migrations/0060_add_zone_object.py 2014-01-16 08:48:57 +0000 |
61 | @@ -1,8 +1,9 @@ |
62 | # -*- coding: utf-8 -*- |
63 | import datetime |
64 | + |
65 | +from django.db import models |
66 | from south.db import db |
67 | from south.v2 import SchemaMigration |
68 | -from django.db import models |
69 | |
70 | |
71 | class Migration(SchemaMigration): |
72 | |
73 | === added file 'src/maasserver/migrations/0061_add_ref_from_node_to_zone.py' |
74 | --- src/maasserver/migrations/0061_add_ref_from_node_to_zone.py 1970-01-01 00:00:00 +0000 |
75 | +++ src/maasserver/migrations/0061_add_ref_from_node_to_zone.py 2014-01-16 08:48:57 +0000 |
76 | @@ -0,0 +1,229 @@ |
77 | +# -*- coding: utf-8 -*- |
78 | +import datetime |
79 | +from south.db import db |
80 | +from south.v2 import SchemaMigration |
81 | +from django.db import models |
82 | + |
83 | + |
84 | +class Migration(SchemaMigration): |
85 | + |
86 | + def forwards(self, orm): |
87 | + # Adding field 'Node.zone' |
88 | + db.add_column(u'maasserver_node', 'zone', |
89 | + self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['maasserver.Zone'], null=True, blank=True), |
90 | + keep_default=False) |
91 | + |
92 | + |
93 | + def backwards(self, orm): |
94 | + # Deleting field 'Node.zone' |
95 | + db.delete_column(u'maasserver_node', 'zone_id') |
96 | + |
97 | + |
98 | + models = { |
99 | + u'auth.group': { |
100 | + 'Meta': {'object_name': 'Group'}, |
101 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
102 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
103 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
104 | + }, |
105 | + u'auth.permission': { |
106 | + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, |
107 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
108 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), |
109 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
110 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
111 | + }, |
112 | + u'auth.user': { |
113 | + 'Meta': {'object_name': 'User'}, |
114 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
115 | + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), |
116 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
117 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
118 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
119 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
120 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
121 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
122 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
123 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
124 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
125 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
126 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
127 | + }, |
128 | + u'contenttypes.contenttype': { |
129 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
130 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
131 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
132 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
133 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
134 | + }, |
135 | + u'maasserver.bootimage': { |
136 | + 'Meta': {'unique_together': "((u'nodegroup', u'architecture', u'subarchitecture', u'release', u'purpose'),)", 'object_name': 'BootImage'}, |
137 | + 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
138 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
139 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
140 | + 'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
141 | + 'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
142 | + 'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
143 | + }, |
144 | + u'maasserver.componenterror': { |
145 | + 'Meta': {'object_name': 'ComponentError'}, |
146 | + 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}), |
147 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
148 | + 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}), |
149 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
150 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
151 | + }, |
152 | + u'maasserver.config': { |
153 | + 'Meta': {'object_name': 'Config'}, |
154 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
155 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
156 | + 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'}) |
157 | + }, |
158 | + u'maasserver.dhcplease': { |
159 | + 'Meta': {'object_name': 'DHCPLease'}, |
160 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
161 | + 'ip': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15'}), |
162 | + 'mac': ('maasserver.fields.MACAddressField', [], {}), |
163 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}) |
164 | + }, |
165 | + u'maasserver.downloadprogress': { |
166 | + 'Meta': {'object_name': 'DownloadProgress'}, |
167 | + 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
168 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
169 | + 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}), |
170 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
171 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
172 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
173 | + 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
174 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
175 | + }, |
176 | + u'maasserver.filestorage': { |
177 | + 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'}, |
178 | + 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}), |
179 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
180 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
181 | + 'key': ('django.db.models.fields.CharField', [], {'default': "u'87c6b1a2-6338-11e3-9837-9c4e363b1c94'", 'unique': 'True', 'max_length': '36'}), |
182 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
183 | + }, |
184 | + u'maasserver.macaddress': { |
185 | + 'Meta': {'object_name': 'MACAddress'}, |
186 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
187 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
188 | + 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}), |
189 | + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}), |
190 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
191 | + }, |
192 | + u'maasserver.node': { |
193 | + 'Meta': {'object_name': 'Node'}, |
194 | + 'after_commissioning_action': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
195 | + 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), |
196 | + 'architecture': ('django.db.models.fields.CharField', [], {'default': "u'i386/generic'", 'max_length': '31'}), |
197 | + 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
198 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
199 | + 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'null': 'True', 'blank': 'True'}), |
200 | + 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
201 | + 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}), |
202 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
203 | + 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
204 | + 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
205 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}), |
206 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), |
207 | + 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), |
208 | + 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}), |
209 | + 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}), |
210 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}), |
211 | + 'storage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
212 | + 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-87c5d5f2-6338-11e3-9837-9c4e363b1c94'", 'unique': 'True', 'max_length': '41'}), |
213 | + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}), |
214 | + 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}), |
215 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
216 | + 'zone': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['maasserver.Zone']", 'null': 'True', 'blank': 'True'}) |
217 | + }, |
218 | + u'maasserver.nodegroup': { |
219 | + 'Meta': {'object_name': 'NodeGroup'}, |
220 | + 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}), |
221 | + 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}), |
222 | + 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}), |
223 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
224 | + 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
225 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
226 | + 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
227 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}), |
228 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
229 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
230 | + 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}) |
231 | + }, |
232 | + u'maasserver.nodegroupinterface': { |
233 | + 'Meta': {'unique_together': "((u'nodegroup', u'interface'),)", 'object_name': 'NodeGroupInterface'}, |
234 | + 'broadcast_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
235 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
236 | + 'foreign_dhcp_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
237 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
238 | + 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
239 | + 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), |
240 | + 'ip_range_high': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
241 | + 'ip_range_low': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
242 | + 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
243 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
244 | + 'router_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
245 | + 'subnet_mask': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
246 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
247 | + }, |
248 | + u'maasserver.sshkey': { |
249 | + 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'}, |
250 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
251 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
252 | + 'key': ('django.db.models.fields.TextField', [], {}), |
253 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
254 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) |
255 | + }, |
256 | + u'maasserver.tag': { |
257 | + 'Meta': {'object_name': 'Tag'}, |
258 | + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
259 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
260 | + 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
261 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
262 | + 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
263 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
264 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
265 | + }, |
266 | + u'maasserver.userprofile': { |
267 | + 'Meta': {'object_name': 'UserProfile'}, |
268 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
269 | + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) |
270 | + }, |
271 | + u'maasserver.zone': { |
272 | + 'Meta': {'object_name': 'Zone'}, |
273 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
274 | + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
275 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
276 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
277 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
278 | + }, |
279 | + u'piston.consumer': { |
280 | + 'Meta': {'object_name': 'Consumer'}, |
281 | + 'description': ('django.db.models.fields.TextField', [], {}), |
282 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
283 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
284 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
285 | + 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
286 | + 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}), |
287 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"}) |
288 | + }, |
289 | + u'piston.token': { |
290 | + 'Meta': {'object_name': 'Token'}, |
291 | + 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
292 | + 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
293 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}), |
294 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
295 | + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
296 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
297 | + 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
298 | + 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1386858019L'}), |
299 | + 'token_type': ('django.db.models.fields.IntegerField', [], {}), |
300 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}), |
301 | + 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'}) |
302 | + } |
303 | + } |
304 | + |
305 | + complete_apps = ['maasserver'] |
306 | \ No newline at end of file |
307 | |
308 | === removed file 'src/maasserver/migrations/0061_add_ref_from_node_to_zone.py' |
309 | --- src/maasserver/migrations/0061_add_ref_from_node_to_zone.py 2013-12-12 14:22:42 +0000 |
310 | +++ src/maasserver/migrations/0061_add_ref_from_node_to_zone.py 1970-01-01 00:00:00 +0000 |
311 | @@ -1,229 +0,0 @@ |
312 | -# -*- coding: utf-8 -*- |
313 | -import datetime |
314 | -from south.db import db |
315 | -from south.v2 import SchemaMigration |
316 | -from django.db import models |
317 | - |
318 | - |
319 | -class Migration(SchemaMigration): |
320 | - |
321 | - def forwards(self, orm): |
322 | - # Adding field 'Node.zone' |
323 | - db.add_column(u'maasserver_node', 'zone', |
324 | - self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['maasserver.Zone'], null=True, blank=True), |
325 | - keep_default=False) |
326 | - |
327 | - |
328 | - def backwards(self, orm): |
329 | - # Deleting field 'Node.zone' |
330 | - db.delete_column(u'maasserver_node', 'zone_id') |
331 | - |
332 | - |
333 | - models = { |
334 | - u'auth.group': { |
335 | - 'Meta': {'object_name': 'Group'}, |
336 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
337 | - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
338 | - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
339 | - }, |
340 | - u'auth.permission': { |
341 | - 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, |
342 | - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
343 | - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), |
344 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
345 | - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
346 | - }, |
347 | - u'auth.user': { |
348 | - 'Meta': {'object_name': 'User'}, |
349 | - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
350 | - 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), |
351 | - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
352 | - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
353 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
354 | - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
355 | - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
356 | - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
357 | - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
358 | - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
359 | - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
360 | - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
361 | - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
362 | - }, |
363 | - u'contenttypes.contenttype': { |
364 | - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
365 | - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
366 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
367 | - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
368 | - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
369 | - }, |
370 | - u'maasserver.bootimage': { |
371 | - 'Meta': {'unique_together': "((u'nodegroup', u'architecture', u'subarchitecture', u'release', u'purpose'),)", 'object_name': 'BootImage'}, |
372 | - 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
373 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
374 | - 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
375 | - 'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
376 | - 'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
377 | - 'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
378 | - }, |
379 | - u'maasserver.componenterror': { |
380 | - 'Meta': {'object_name': 'ComponentError'}, |
381 | - 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}), |
382 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
383 | - 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}), |
384 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
385 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
386 | - }, |
387 | - u'maasserver.config': { |
388 | - 'Meta': {'object_name': 'Config'}, |
389 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
390 | - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
391 | - 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'}) |
392 | - }, |
393 | - u'maasserver.dhcplease': { |
394 | - 'Meta': {'object_name': 'DHCPLease'}, |
395 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
396 | - 'ip': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15'}), |
397 | - 'mac': ('maasserver.fields.MACAddressField', [], {}), |
398 | - 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}) |
399 | - }, |
400 | - u'maasserver.downloadprogress': { |
401 | - 'Meta': {'object_name': 'DownloadProgress'}, |
402 | - 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
403 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
404 | - 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}), |
405 | - 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
406 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
407 | - 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
408 | - 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
409 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
410 | - }, |
411 | - u'maasserver.filestorage': { |
412 | - 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'}, |
413 | - 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}), |
414 | - 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
415 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
416 | - 'key': ('django.db.models.fields.CharField', [], {'default': "u'87c6b1a2-6338-11e3-9837-9c4e363b1c94'", 'unique': 'True', 'max_length': '36'}), |
417 | - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
418 | - }, |
419 | - u'maasserver.macaddress': { |
420 | - 'Meta': {'object_name': 'MACAddress'}, |
421 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
422 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
423 | - 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}), |
424 | - 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}), |
425 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
426 | - }, |
427 | - u'maasserver.node': { |
428 | - 'Meta': {'object_name': 'Node'}, |
429 | - 'after_commissioning_action': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
430 | - 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), |
431 | - 'architecture': ('django.db.models.fields.CharField', [], {'default': "u'i386/generic'", 'max_length': '31'}), |
432 | - 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
433 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
434 | - 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'null': 'True', 'blank': 'True'}), |
435 | - 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
436 | - 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}), |
437 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
438 | - 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
439 | - 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
440 | - 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}), |
441 | - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), |
442 | - 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), |
443 | - 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}), |
444 | - 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}), |
445 | - 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}), |
446 | - 'storage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
447 | - 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-87c5d5f2-6338-11e3-9837-9c4e363b1c94'", 'unique': 'True', 'max_length': '41'}), |
448 | - 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}), |
449 | - 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}), |
450 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
451 | - 'zone': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['maasserver.Zone']", 'null': 'True', 'blank': 'True'}) |
452 | - }, |
453 | - u'maasserver.nodegroup': { |
454 | - 'Meta': {'object_name': 'NodeGroup'}, |
455 | - 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}), |
456 | - 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}), |
457 | - 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}), |
458 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
459 | - 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
460 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
461 | - 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
462 | - 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}), |
463 | - 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
464 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
465 | - 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}) |
466 | - }, |
467 | - u'maasserver.nodegroupinterface': { |
468 | - 'Meta': {'unique_together': "((u'nodegroup', u'interface'),)", 'object_name': 'NodeGroupInterface'}, |
469 | - 'broadcast_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
470 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
471 | - 'foreign_dhcp_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
472 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
473 | - 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
474 | - 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), |
475 | - 'ip_range_high': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
476 | - 'ip_range_low': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
477 | - 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
478 | - 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
479 | - 'router_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
480 | - 'subnet_mask': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
481 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
482 | - }, |
483 | - u'maasserver.sshkey': { |
484 | - 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'}, |
485 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
486 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
487 | - 'key': ('django.db.models.fields.TextField', [], {}), |
488 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
489 | - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) |
490 | - }, |
491 | - u'maasserver.tag': { |
492 | - 'Meta': {'object_name': 'Tag'}, |
493 | - 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
494 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
495 | - 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
496 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
497 | - 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
498 | - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
499 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
500 | - }, |
501 | - u'maasserver.userprofile': { |
502 | - 'Meta': {'object_name': 'UserProfile'}, |
503 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
504 | - 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) |
505 | - }, |
506 | - u'maasserver.zone': { |
507 | - 'Meta': {'object_name': 'Zone'}, |
508 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
509 | - 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
510 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
511 | - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
512 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
513 | - }, |
514 | - u'piston.consumer': { |
515 | - 'Meta': {'object_name': 'Consumer'}, |
516 | - 'description': ('django.db.models.fields.TextField', [], {}), |
517 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
518 | - 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
519 | - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
520 | - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
521 | - 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}), |
522 | - 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"}) |
523 | - }, |
524 | - u'piston.token': { |
525 | - 'Meta': {'object_name': 'Token'}, |
526 | - 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
527 | - 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
528 | - 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}), |
529 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
530 | - 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
531 | - 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
532 | - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
533 | - 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1386858019L'}), |
534 | - 'token_type': ('django.db.models.fields.IntegerField', [], {}), |
535 | - 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}), |
536 | - 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'}) |
537 | - } |
538 | - } |
539 | - |
540 | - complete_apps = ['maasserver'] |
541 | \ No newline at end of file |
542 | |
543 | === added file 'src/maasserver/migrations/0063_create_default_zone.py' |
544 | --- src/maasserver/migrations/0063_create_default_zone.py 1970-01-01 00:00:00 +0000 |
545 | +++ src/maasserver/migrations/0063_create_default_zone.py 2014-01-16 08:48:57 +0000 |
546 | @@ -0,0 +1,237 @@ |
547 | +# -*- coding: utf-8 -*- |
548 | +import datetime |
549 | + |
550 | +from maasserver.models.zone import DEFAULT_ZONE_NAME |
551 | +from south.v2 import DataMigration |
552 | + |
553 | + |
554 | +class Migration(DataMigration): |
555 | + |
556 | + def forwards(self, orm): |
557 | + now = datetime.datetime.now() |
558 | + ct, created = orm['maasserver.Zone'].objects.get_or_create( |
559 | + name=DEFAULT_ZONE_NAME, |
560 | + defaults={ |
561 | + 'name': DEFAULT_ZONE_NAME, |
562 | + 'created': now, |
563 | + 'updated': now, |
564 | + }) |
565 | + |
566 | + def backwards(self, orm): |
567 | + pass |
568 | + |
569 | + models = { |
570 | + u'auth.group': { |
571 | + 'Meta': {'object_name': 'Group'}, |
572 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
573 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
574 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
575 | + }, |
576 | + u'auth.permission': { |
577 | + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, |
578 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
579 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), |
580 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
581 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
582 | + }, |
583 | + u'auth.user': { |
584 | + 'Meta': {'object_name': 'User'}, |
585 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
586 | + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), |
587 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
588 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
589 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
590 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
591 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
592 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
593 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
594 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
595 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
596 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
597 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
598 | + }, |
599 | + u'contenttypes.contenttype': { |
600 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
601 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
602 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
603 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
604 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
605 | + }, |
606 | + u'maasserver.bootimage': { |
607 | + 'Meta': {'unique_together': "((u'nodegroup', u'architecture', u'subarchitecture', u'release', u'purpose'),)", 'object_name': 'BootImage'}, |
608 | + 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
609 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
610 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
611 | + 'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
612 | + 'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
613 | + 'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
614 | + }, |
615 | + u'maasserver.componenterror': { |
616 | + 'Meta': {'object_name': 'ComponentError'}, |
617 | + 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}), |
618 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
619 | + 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}), |
620 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
621 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
622 | + }, |
623 | + u'maasserver.config': { |
624 | + 'Meta': {'object_name': 'Config'}, |
625 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
626 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
627 | + 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'}) |
628 | + }, |
629 | + u'maasserver.dhcplease': { |
630 | + 'Meta': {'object_name': 'DHCPLease'}, |
631 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
632 | + 'ip': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15'}), |
633 | + 'mac': ('maasserver.fields.MACAddressField', [], {}), |
634 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}) |
635 | + }, |
636 | + u'maasserver.downloadprogress': { |
637 | + 'Meta': {'object_name': 'DownloadProgress'}, |
638 | + 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
639 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
640 | + 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}), |
641 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
642 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
643 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
644 | + 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
645 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
646 | + }, |
647 | + u'maasserver.filestorage': { |
648 | + 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'}, |
649 | + 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}), |
650 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
651 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
652 | + 'key': ('django.db.models.fields.CharField', [], {'default': "u'81461798-7de9-11e3-ad6a-9c4e363b1c94'", 'unique': 'True', 'max_length': '36'}), |
653 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
654 | + }, |
655 | + u'maasserver.macaddress': { |
656 | + 'Meta': {'object_name': 'MACAddress'}, |
657 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
658 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
659 | + 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}), |
660 | + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}), |
661 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
662 | + }, |
663 | + u'maasserver.node': { |
664 | + 'Meta': {'object_name': 'Node'}, |
665 | + 'after_commissioning_action': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
666 | + 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), |
667 | + 'architecture': ('django.db.models.fields.CharField', [], {'default': "u'i386/generic'", 'max_length': '31'}), |
668 | + 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
669 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
670 | + 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'null': 'True', 'blank': 'True'}), |
671 | + 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
672 | + 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}), |
673 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
674 | + 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
675 | + 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
676 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}), |
677 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), |
678 | + 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), |
679 | + 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}), |
680 | + 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}), |
681 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}), |
682 | + 'storage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
683 | + 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-81450ee8-7de9-11e3-ad6a-9c4e363b1c94'", 'unique': 'True', 'max_length': '41'}), |
684 | + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}), |
685 | + 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}), |
686 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
687 | + 'zone': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['maasserver.Zone']", 'null': 'True', 'blank': 'True'}) |
688 | + }, |
689 | + u'maasserver.nodegroup': { |
690 | + 'Meta': {'object_name': 'NodeGroup'}, |
691 | + 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}), |
692 | + 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}), |
693 | + 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}), |
694 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
695 | + 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
696 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
697 | + 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
698 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}), |
699 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
700 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
701 | + 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}) |
702 | + }, |
703 | + u'maasserver.nodegroupinterface': { |
704 | + 'Meta': {'unique_together': "((u'nodegroup', u'interface'),)", 'object_name': 'NodeGroupInterface'}, |
705 | + 'broadcast_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
706 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
707 | + 'foreign_dhcp_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
708 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
709 | + 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
710 | + 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), |
711 | + 'ip_range_high': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
712 | + 'ip_range_low': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
713 | + 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
714 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
715 | + 'router_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
716 | + 'subnet_mask': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
717 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
718 | + }, |
719 | + u'maasserver.sshkey': { |
720 | + 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'}, |
721 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
722 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
723 | + 'key': ('django.db.models.fields.TextField', [], {}), |
724 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
725 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) |
726 | + }, |
727 | + u'maasserver.tag': { |
728 | + 'Meta': {'object_name': 'Tag'}, |
729 | + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
730 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
731 | + 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
732 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
733 | + 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
734 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
735 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
736 | + }, |
737 | + u'maasserver.userprofile': { |
738 | + 'Meta': {'object_name': 'UserProfile'}, |
739 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
740 | + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) |
741 | + }, |
742 | + u'maasserver.vlan': { |
743 | + 'Meta': {'object_name': 'Vlan'}, |
744 | + 'description': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
745 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
746 | + 'tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True'}) |
747 | + }, |
748 | + u'maasserver.zone': { |
749 | + 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'}, |
750 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
751 | + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
752 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
753 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
754 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
755 | + }, |
756 | + u'piston.consumer': { |
757 | + 'Meta': {'object_name': 'Consumer'}, |
758 | + 'description': ('django.db.models.fields.TextField', [], {}), |
759 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
760 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
761 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
762 | + 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
763 | + 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}), |
764 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"}) |
765 | + }, |
766 | + u'piston.token': { |
767 | + 'Meta': {'object_name': 'Token'}, |
768 | + 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
769 | + 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
770 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}), |
771 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
772 | + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
773 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
774 | + 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
775 | + 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1389792759L'}), |
776 | + 'token_type': ('django.db.models.fields.IntegerField', [], {}), |
777 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}), |
778 | + 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'}) |
779 | + } |
780 | + } |
781 | + |
782 | + complete_apps = ['maasserver'] |
783 | + symmetrical = True |
784 | |
785 | === added file 'src/maasserver/migrations/0064_set_default_zone.py' |
786 | --- src/maasserver/migrations/0064_set_default_zone.py 1970-01-01 00:00:00 +0000 |
787 | +++ src/maasserver/migrations/0064_set_default_zone.py 2014-01-16 08:48:57 +0000 |
788 | @@ -0,0 +1,236 @@ |
789 | +# -*- coding: utf-8 -*- |
790 | +import datetime |
791 | + |
792 | +from django.db import models |
793 | +from maasserver.models.zone import DEFAULT_ZONE_NAME |
794 | +from south.db import db |
795 | +from south.v2 import DataMigration |
796 | + |
797 | + |
798 | +class Migration(DataMigration): |
799 | + |
800 | + def forwards(self, orm): |
801 | + default_zone = orm['maasserver.Zone'].objects.get( |
802 | + name=DEFAULT_ZONE_NAME) |
803 | + nodes_with_none_zone = orm['maasserver.Node'].objects.filter( |
804 | + zone=None) |
805 | + nodes_with_none_zone.update(zone=default_zone) |
806 | + |
807 | + def backwards(self, orm): |
808 | + pass |
809 | + |
810 | + models = { |
811 | + u'auth.group': { |
812 | + 'Meta': {'object_name': 'Group'}, |
813 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
814 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
815 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
816 | + }, |
817 | + u'auth.permission': { |
818 | + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, |
819 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
820 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), |
821 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
822 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
823 | + }, |
824 | + u'auth.user': { |
825 | + 'Meta': {'object_name': 'User'}, |
826 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
827 | + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), |
828 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
829 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
830 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
831 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
832 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
833 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
834 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
835 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
836 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
837 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
838 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
839 | + }, |
840 | + u'contenttypes.contenttype': { |
841 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
842 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
843 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
844 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
845 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
846 | + }, |
847 | + u'maasserver.bootimage': { |
848 | + 'Meta': {'unique_together': "((u'nodegroup', u'architecture', u'subarchitecture', u'release', u'purpose'),)", 'object_name': 'BootImage'}, |
849 | + 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
850 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
851 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
852 | + 'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
853 | + 'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
854 | + 'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
855 | + }, |
856 | + u'maasserver.componenterror': { |
857 | + 'Meta': {'object_name': 'ComponentError'}, |
858 | + 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}), |
859 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
860 | + 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}), |
861 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
862 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
863 | + }, |
864 | + u'maasserver.config': { |
865 | + 'Meta': {'object_name': 'Config'}, |
866 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
867 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
868 | + 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'}) |
869 | + }, |
870 | + u'maasserver.dhcplease': { |
871 | + 'Meta': {'object_name': 'DHCPLease'}, |
872 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
873 | + 'ip': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15'}), |
874 | + 'mac': ('maasserver.fields.MACAddressField', [], {}), |
875 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}) |
876 | + }, |
877 | + u'maasserver.downloadprogress': { |
878 | + 'Meta': {'object_name': 'DownloadProgress'}, |
879 | + 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
880 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
881 | + 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}), |
882 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
883 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
884 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
885 | + 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
886 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
887 | + }, |
888 | + u'maasserver.filestorage': { |
889 | + 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'}, |
890 | + 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}), |
891 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
892 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
893 | + 'key': ('django.db.models.fields.CharField', [], {'default': "u'f816df92-7de9-11e3-acc1-9c4e363b1c94'", 'unique': 'True', 'max_length': '36'}), |
894 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
895 | + }, |
896 | + u'maasserver.macaddress': { |
897 | + 'Meta': {'object_name': 'MACAddress'}, |
898 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
899 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
900 | + 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}), |
901 | + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}), |
902 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
903 | + }, |
904 | + u'maasserver.node': { |
905 | + 'Meta': {'object_name': 'Node'}, |
906 | + 'after_commissioning_action': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
907 | + 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), |
908 | + 'architecture': ('django.db.models.fields.CharField', [], {'default': "u'i386/generic'", 'max_length': '31'}), |
909 | + 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
910 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
911 | + 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'null': 'True', 'blank': 'True'}), |
912 | + 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
913 | + 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}), |
914 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
915 | + 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
916 | + 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
917 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}), |
918 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), |
919 | + 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), |
920 | + 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}), |
921 | + 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}), |
922 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}), |
923 | + 'storage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
924 | + 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-f818607e-7de9-11e3-acc1-9c4e363b1c94'", 'unique': 'True', 'max_length': '41'}), |
925 | + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}), |
926 | + 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}), |
927 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
928 | + 'zone': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['maasserver.Zone']", 'null': 'True', 'blank': 'True'}) |
929 | + }, |
930 | + u'maasserver.nodegroup': { |
931 | + 'Meta': {'object_name': 'NodeGroup'}, |
932 | + 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}), |
933 | + 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}), |
934 | + 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}), |
935 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
936 | + 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
937 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
938 | + 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
939 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}), |
940 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
941 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
942 | + 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}) |
943 | + }, |
944 | + u'maasserver.nodegroupinterface': { |
945 | + 'Meta': {'unique_together': "((u'nodegroup', u'interface'),)", 'object_name': 'NodeGroupInterface'}, |
946 | + 'broadcast_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
947 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
948 | + 'foreign_dhcp_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
949 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
950 | + 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
951 | + 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), |
952 | + 'ip_range_high': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
953 | + 'ip_range_low': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
954 | + 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
955 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
956 | + 'router_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
957 | + 'subnet_mask': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
958 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
959 | + }, |
960 | + u'maasserver.sshkey': { |
961 | + 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'}, |
962 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
963 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
964 | + 'key': ('django.db.models.fields.TextField', [], {}), |
965 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
966 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) |
967 | + }, |
968 | + u'maasserver.tag': { |
969 | + 'Meta': {'object_name': 'Tag'}, |
970 | + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
971 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
972 | + 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
973 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
974 | + 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
975 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
976 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
977 | + }, |
978 | + u'maasserver.userprofile': { |
979 | + 'Meta': {'object_name': 'UserProfile'}, |
980 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
981 | + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) |
982 | + }, |
983 | + u'maasserver.vlan': { |
984 | + 'Meta': {'object_name': 'Vlan'}, |
985 | + 'description': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
986 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
987 | + 'tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True'}) |
988 | + }, |
989 | + u'maasserver.zone': { |
990 | + 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'}, |
991 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
992 | + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
993 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
994 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
995 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
996 | + }, |
997 | + u'piston.consumer': { |
998 | + 'Meta': {'object_name': 'Consumer'}, |
999 | + 'description': ('django.db.models.fields.TextField', [], {}), |
1000 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1001 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
1002 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
1003 | + 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
1004 | + 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}), |
1005 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"}) |
1006 | + }, |
1007 | + u'piston.token': { |
1008 | + 'Meta': {'object_name': 'Token'}, |
1009 | + 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
1010 | + 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
1011 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}), |
1012 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1013 | + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
1014 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
1015 | + 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
1016 | + 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1389792959L'}), |
1017 | + 'token_type': ('django.db.models.fields.IntegerField', [], {}), |
1018 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}), |
1019 | + 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'}) |
1020 | + } |
1021 | + } |
1022 | + |
1023 | + complete_apps = ['maasserver'] |
1024 | + symmetrical = True |
1025 | |
1026 | === added file 'src/maasserver/migrations/0065_set_default_zone_as_model_default.py' |
1027 | --- src/maasserver/migrations/0065_set_default_zone_as_model_default.py 1970-01-01 00:00:00 +0000 |
1028 | +++ src/maasserver/migrations/0065_set_default_zone_as_model_default.py 2014-01-16 08:48:57 +0000 |
1029 | @@ -0,0 +1,234 @@ |
1030 | +# -*- coding: utf-8 -*- |
1031 | +import datetime |
1032 | + |
1033 | +from django.db import models |
1034 | +from south.db import db |
1035 | +from south.v2 import SchemaMigration |
1036 | + |
1037 | + |
1038 | +class Migration(SchemaMigration): |
1039 | + |
1040 | + def forwards(self, orm): |
1041 | + |
1042 | + # Changing field 'Node.zone' |
1043 | + db.alter_column(u'maasserver_node', 'zone_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['maasserver.Zone'], on_delete=models.SET_DEFAULT)) |
1044 | + |
1045 | + def backwards(self, orm): |
1046 | + |
1047 | + # Changing field 'Node.zone' |
1048 | + db.alter_column(u'maasserver_node', 'zone_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['maasserver.Zone'], null=True)) |
1049 | + |
1050 | + models = { |
1051 | + u'auth.group': { |
1052 | + 'Meta': {'object_name': 'Group'}, |
1053 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1054 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
1055 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
1056 | + }, |
1057 | + u'auth.permission': { |
1058 | + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, |
1059 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
1060 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), |
1061 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1062 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
1063 | + }, |
1064 | + u'auth.user': { |
1065 | + 'Meta': {'object_name': 'User'}, |
1066 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
1067 | + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), |
1068 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
1069 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
1070 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1071 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
1072 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
1073 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
1074 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
1075 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
1076 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
1077 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
1078 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
1079 | + }, |
1080 | + u'contenttypes.contenttype': { |
1081 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
1082 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
1083 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1084 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
1085 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
1086 | + }, |
1087 | + u'maasserver.bootimage': { |
1088 | + 'Meta': {'unique_together': "((u'nodegroup', u'architecture', u'subarchitecture', u'release', u'purpose'),)", 'object_name': 'BootImage'}, |
1089 | + 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
1090 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1091 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
1092 | + 'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
1093 | + 'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
1094 | + 'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}) |
1095 | + }, |
1096 | + u'maasserver.componenterror': { |
1097 | + 'Meta': {'object_name': 'ComponentError'}, |
1098 | + 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}), |
1099 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
1100 | + 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}), |
1101 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1102 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
1103 | + }, |
1104 | + u'maasserver.config': { |
1105 | + 'Meta': {'object_name': 'Config'}, |
1106 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1107 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
1108 | + 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'}) |
1109 | + }, |
1110 | + u'maasserver.dhcplease': { |
1111 | + 'Meta': {'object_name': 'DHCPLease'}, |
1112 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1113 | + 'ip': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15'}), |
1114 | + 'mac': ('maasserver.fields.MACAddressField', [], {}), |
1115 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}) |
1116 | + }, |
1117 | + u'maasserver.downloadprogress': { |
1118 | + 'Meta': {'object_name': 'DownloadProgress'}, |
1119 | + 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
1120 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
1121 | + 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}), |
1122 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
1123 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1124 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
1125 | + 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
1126 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
1127 | + }, |
1128 | + u'maasserver.filestorage': { |
1129 | + 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'}, |
1130 | + 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}), |
1131 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
1132 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1133 | + 'key': ('django.db.models.fields.CharField', [], {'default': "u'8498ac20-7dea-11e3-b413-9c4e363b1c94'", 'unique': 'True', 'max_length': '36'}), |
1134 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
1135 | + }, |
1136 | + u'maasserver.macaddress': { |
1137 | + 'Meta': {'object_name': 'MACAddress'}, |
1138 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
1139 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1140 | + 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}), |
1141 | + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}), |
1142 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
1143 | + }, |
1144 | + u'maasserver.node': { |
1145 | + 'Meta': {'object_name': 'Node'}, |
1146 | + 'after_commissioning_action': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
1147 | + 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), |
1148 | + 'architecture': ('django.db.models.fields.CharField', [], {'default': "u'i386/generic'", 'max_length': '31'}), |
1149 | + 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
1150 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
1151 | + 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'null': 'True', 'blank': 'True'}), |
1152 | + 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
1153 | + 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}), |
1154 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1155 | + 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
1156 | + 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
1157 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}), |
1158 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), |
1159 | + 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), |
1160 | + 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}), |
1161 | + 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}), |
1162 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}), |
1163 | + 'storage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
1164 | + 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-8497005a-7dea-11e3-b413-9c4e363b1c94'", 'unique': 'True', 'max_length': '41'}), |
1165 | + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}), |
1166 | + 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}), |
1167 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
1168 | + 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'}) |
1169 | + }, |
1170 | + u'maasserver.nodegroup': { |
1171 | + 'Meta': {'object_name': 'NodeGroup'}, |
1172 | + 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}), |
1173 | + 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}), |
1174 | + 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}), |
1175 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
1176 | + 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
1177 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1178 | + 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
1179 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}), |
1180 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
1181 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
1182 | + 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}) |
1183 | + }, |
1184 | + u'maasserver.nodegroupinterface': { |
1185 | + 'Meta': {'unique_together': "((u'nodegroup', u'interface'),)", 'object_name': 'NodeGroupInterface'}, |
1186 | + 'broadcast_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
1187 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
1188 | + 'foreign_dhcp_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
1189 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1190 | + 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
1191 | + 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), |
1192 | + 'ip_range_high': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
1193 | + 'ip_range_low': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
1194 | + 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
1195 | + 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
1196 | + 'router_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
1197 | + 'subnet_mask': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
1198 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
1199 | + }, |
1200 | + u'maasserver.sshkey': { |
1201 | + 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'}, |
1202 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
1203 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1204 | + 'key': ('django.db.models.fields.TextField', [], {}), |
1205 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
1206 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) |
1207 | + }, |
1208 | + u'maasserver.tag': { |
1209 | + 'Meta': {'object_name': 'Tag'}, |
1210 | + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
1211 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
1212 | + 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
1213 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1214 | + 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
1215 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
1216 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
1217 | + }, |
1218 | + u'maasserver.userprofile': { |
1219 | + 'Meta': {'object_name': 'UserProfile'}, |
1220 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1221 | + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) |
1222 | + }, |
1223 | + u'maasserver.vlan': { |
1224 | + 'Meta': {'object_name': 'Vlan'}, |
1225 | + 'description': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
1226 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1227 | + 'tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True'}) |
1228 | + }, |
1229 | + u'maasserver.zone': { |
1230 | + 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'}, |
1231 | + 'created': ('django.db.models.fields.DateTimeField', [], {}), |
1232 | + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
1233 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1234 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
1235 | + 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
1236 | + }, |
1237 | + u'piston.consumer': { |
1238 | + 'Meta': {'object_name': 'Consumer'}, |
1239 | + 'description': ('django.db.models.fields.TextField', [], {}), |
1240 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1241 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
1242 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
1243 | + 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
1244 | + 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}), |
1245 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"}) |
1246 | + }, |
1247 | + u'piston.token': { |
1248 | + 'Meta': {'object_name': 'Token'}, |
1249 | + 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
1250 | + 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
1251 | + 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}), |
1252 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
1253 | + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
1254 | + 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
1255 | + 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
1256 | + 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1389793194L'}), |
1257 | + 'token_type': ('django.db.models.fields.IntegerField', [], {}), |
1258 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}), |
1259 | + 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'}) |
1260 | + } |
1261 | + } |
1262 | + |
1263 | + complete_apps = ['maasserver'] |
1264 | \ No newline at end of file |
1265 | |
1266 | === modified file 'src/maasserver/models/node.py' |
1267 | --- src/maasserver/models/node.py 2014-01-16 02:57:53 +0000 |
1268 | +++ src/maasserver/models/node.py 2014-01-16 08:48:57 +0000 |
1269 | @@ -38,6 +38,7 @@ |
1270 | IntegerField, |
1271 | Manager, |
1272 | ManyToManyField, |
1273 | + SET_DEFAULT, |
1274 | Q, |
1275 | ) |
1276 | from django.shortcuts import get_object_or_404 |
1277 | @@ -438,8 +439,9 @@ |
1278 | agent_name = CharField(max_length=255, default='', blank=True, null=True) |
1279 | |
1280 | zone = ForeignKey( |
1281 | - Zone, verbose_name="Physical zone", default=None, blank=True, |
1282 | - null=True, editable=True, db_index=True) |
1283 | + Zone, verbose_name="Physical zone", |
1284 | + default=Zone.objects.get_default_zone, editable=True, db_index=True, |
1285 | + on_delete=SET_DEFAULT) |
1286 | |
1287 | # Juju expects the following standard constraints, which are stored here |
1288 | # as a basic optimisation over querying the lshw output. |
1289 | |
1290 | === modified file 'src/maasserver/models/tests/test_zone.py' |
1291 | --- src/maasserver/models/tests/test_zone.py 2013-12-18 11:57:24 +0000 |
1292 | +++ src/maasserver/models/tests/test_zone.py 2014-01-16 08:48:57 +0000 |
1293 | @@ -1,4 +1,4 @@ |
1294 | -# Copyright 2013 Canonical Ltd. This software is licensed under the |
1295 | +# Copyright 2013-2014 Canonical Ltd. This software is licensed under the |
1296 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1297 | |
1298 | """Test Zone objects.""" |
1299 | @@ -14,12 +14,28 @@ |
1300 | __metaclass__ = type |
1301 | __all__ = [] |
1302 | |
1303 | -from maasserver.models.zone import Zone |
1304 | +from maasserver.models.zone import ( |
1305 | + DEFAULT_ZONE_NAME, |
1306 | + Zone, |
1307 | + ) |
1308 | from maasserver.testing import reload_object |
1309 | from maasserver.testing.factory import factory |
1310 | from maasserver.testing.testcase import MAASServerTestCase |
1311 | |
1312 | |
1313 | +class TestZoneManager(MAASServerTestCase): |
1314 | + """Tests for `Zone` manager.""" |
1315 | + |
1316 | + def test_get_default_zone_returns_default_zone(self): |
1317 | + self.assertEqual( |
1318 | + DEFAULT_ZONE_NAME, Zone.objects.get_default_zone().name) |
1319 | + |
1320 | + def test_get_default_zone_ignores_other_zones(self): |
1321 | + factory.make_zone() |
1322 | + self.assertEqual( |
1323 | + DEFAULT_ZONE_NAME, Zone.objects.get_default_zone().name) |
1324 | + |
1325 | + |
1326 | class TestZone(MAASServerTestCase): |
1327 | """Tests for :class:`Zone`.""" |
1328 | |
1329 | @@ -56,4 +72,10 @@ |
1330 | self.assertIsNone(reload_object(zone)) |
1331 | node = reload_object(node) |
1332 | self.assertIsNotNone(node) |
1333 | - self.assertIsNone(node.zone) |
1334 | + self.assertEqual(Zone.objects.get_default_zone(), node.zone) |
1335 | + |
1336 | + def test_is_default_returns_True_for_default_zone(self): |
1337 | + self.assertTrue(Zone.objects.get_default_zone().is_default()) |
1338 | + |
1339 | + def test_is_default_returns_False_for_normal_zone(self): |
1340 | + self.assertFalse(factory.make_zone().is_default()) |
1341 | |
1342 | === modified file 'src/maasserver/models/zone.py' |
1343 | --- src/maasserver/models/zone.py 2013-12-18 17:47:51 +0000 |
1344 | +++ src/maasserver/models/zone.py 2014-01-16 08:48:57 +0000 |
1345 | @@ -1,4 +1,4 @@ |
1346 | -# Copyright 2013 Canonical Ltd. This software is licensed under the |
1347 | +# Copyright 2013-2014 Canonical Ltd. This software is licensed under the |
1348 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1349 | |
1350 | """Physical zone objects.""" |
1351 | @@ -13,13 +13,18 @@ |
1352 | |
1353 | __metaclass__ = type |
1354 | __all__ = [ |
1355 | + "DEFAULT_ZONE_NAME", |
1356 | "Zone", |
1357 | "ZONE_NAME_VALIDATOR", |
1358 | ] |
1359 | |
1360 | +import datetime |
1361 | + |
1362 | +from django.core.exceptions import ValidationError |
1363 | from django.core.validators import RegexValidator |
1364 | from django.db.models import ( |
1365 | CharField, |
1366 | + Manager, |
1367 | TextField, |
1368 | ) |
1369 | from maasserver import DefaultMeta |
1370 | @@ -29,12 +34,38 @@ |
1371 | |
1372 | ZONE_NAME_VALIDATOR = RegexValidator('^[\w-]+$') |
1373 | |
1374 | +# Name of the special, default zone. This zone can be neither deleted nor |
1375 | +# renamed. |
1376 | +DEFAULT_ZONE_NAME = 'default' |
1377 | + |
1378 | + |
1379 | +class ZoneManager(Manager): |
1380 | + """Manager for :class:`Zone` model. |
1381 | + |
1382 | + Don't import or instantiate this directly; access as `<Class>.objects` on |
1383 | + the model class it manages. |
1384 | + """ |
1385 | + |
1386 | + def get_default_zone(self): |
1387 | + """Return the default zone.""" |
1388 | + now = datetime.datetime.now() |
1389 | + zone, _ = self.get_or_create( |
1390 | + name=DEFAULT_ZONE_NAME, |
1391 | + defaults={ |
1392 | + 'name': DEFAULT_ZONE_NAME, |
1393 | + 'created': now, |
1394 | + 'updated': now, |
1395 | + } |
1396 | + ) |
1397 | + return zone |
1398 | + |
1399 | |
1400 | class Zone(CleanSave, TimestampedModel): |
1401 | """A `Zone` is an entity used to logically group nodes together. |
1402 | |
1403 | :ivar name: The short-human-identifiable name for this zone. |
1404 | :ivar description: Free-form description for this zone. |
1405 | + :ivar objects: An instance of the class :class:`ZoneManager`. |
1406 | """ |
1407 | |
1408 | class Meta(DefaultMeta): |
1409 | @@ -43,25 +74,23 @@ |
1410 | verbose_name_plural = "Physical zones" |
1411 | ordering = ["name"] |
1412 | |
1413 | + objects = ZoneManager() |
1414 | + |
1415 | name = CharField( |
1416 | max_length=256, unique=True, editable=True, |
1417 | validators=[ZONE_NAME_VALIDATOR]) |
1418 | + |
1419 | description = TextField(blank=True, editable=True) |
1420 | |
1421 | def __unicode__(self): |
1422 | return self.name |
1423 | |
1424 | + def is_default(self): |
1425 | + """Is this the default zone?""" |
1426 | + return self.name == DEFAULT_ZONE_NAME |
1427 | + |
1428 | def delete(self): |
1429 | - """Delete zone, but keep its nodes! |
1430 | - |
1431 | - Deleting a zone, by default, deletes all the nodes that are in that |
1432 | - zone. Use `delete` instead: it will clear those nodes' `zone` fields |
1433 | - so they'll be zoneless, but not gone. |
1434 | - """ |
1435 | - # Avoid circular imports. |
1436 | - from maasserver.models.node import Node |
1437 | - |
1438 | - # Clear nodes' references to this zone. |
1439 | - Node.objects.filter(zone=self).update(zone=None) |
1440 | - # That being done, defer to the reckless default implementation. |
1441 | + if self.is_default(): |
1442 | + raise ValidationError( |
1443 | + "This zone is the default zone, it cannot be deleted.") |
1444 | super(Zone, self).delete() |
1445 | |
1446 | === modified file 'src/maasserver/templates/maasserver/nodes_listing.html' |
1447 | --- src/maasserver/templates/maasserver/nodes_listing.html 2013-12-18 11:16:29 +0000 |
1448 | +++ src/maasserver/templates/maasserver/nodes_listing.html 2014-01-16 08:48:57 +0000 |
1449 | @@ -52,11 +52,7 @@ |
1450 | </td> |
1451 | <td>{{ node.display_status }}</td> |
1452 | <td class="zone-column"> |
1453 | - {% if node.zone %} |
1454 | - <a href="{% url 'zone-view' node.zone.name %}">{{ node.zone }}</a> |
1455 | - {% else %} |
1456 | - (Default) |
1457 | - {% endif %} |
1458 | + <a href="{% url 'zone-view' node.zone.name %}">{{ node.zone }}</a> |
1459 | </td> |
1460 | </tr> |
1461 | {% endfor %} |
1462 | |
1463 | === modified file 'src/maasserver/templates/maasserver/zone_detail.html' |
1464 | --- src/maasserver/templates/maasserver/zone_detail.html 2013-12-20 13:05:55 +0000 |
1465 | +++ src/maasserver/templates/maasserver/zone_detail.html 2014-01-16 08:48:57 +0000 |
1466 | @@ -15,9 +15,11 @@ |
1467 | <a href="{% url 'zone-edit' zone.name %}" class="button secondary"> |
1468 | Edit zone |
1469 | </a> |
1470 | + {% if not zone.is_default %} |
1471 | <a href="{% url 'zone-del' zone.name %}" class="button secondary"> |
1472 | Delete zone |
1473 | </a> |
1474 | + {% endif %} |
1475 | {% endif %} |
1476 | {% endblock %} |
1477 | |
1478 | @@ -29,14 +31,20 @@ |
1479 | <h4>Name</h4> |
1480 | <span>{{ zone.name }}</span> |
1481 | </li> |
1482 | - <li class="block size1"> |
1483 | + <li class="block size3"> |
1484 | <h4>Nodes</h4> |
1485 | <span id="#nodecount"> |
1486 | {{ zone.node_set.count }} |
1487 | (<a href="{{ node_list_link }}">view</a>) |
1488 | </span> |
1489 | </li> |
1490 | - <li class="block size12 first"> |
1491 | + <li class="block size3"> |
1492 | + <h4>Default</h4> |
1493 | + <span id="#default"> |
1494 | + {% if zone.default %}Yes{% else %}No{% endif %} |
1495 | + </span> |
1496 | + </li> |
1497 | + <li class="block size12 first"> |
1498 | {% if zone.description %} |
1499 | <h4>Description</h4> |
1500 | <pre>{{ zone.description }}</pre> |
1501 | |
1502 | === modified file 'src/maasserver/templates/maasserver/zone_list.html' |
1503 | --- src/maasserver/templates/maasserver/zone_list.html 2013-12-20 13:05:55 +0000 |
1504 | +++ src/maasserver/templates/maasserver/zone_list.html 2014-01-16 08:48:57 +0000 |
1505 | @@ -15,6 +15,7 @@ |
1506 | <tr> |
1507 | <th>Name</th> |
1508 | <th>Description</th> |
1509 | + <th>Default</th> |
1510 | <th>Nodes</th> |
1511 | {% if user.is_superuser %} |
1512 | <th></th> |
1513 | @@ -31,12 +32,14 @@ |
1514 | </a> |
1515 | </td> |
1516 | <td>{{ zone_item.description|truncatechars:40 }}</td> |
1517 | + <td>{% if zone_item.is_default %}Yes{% endif %}</td> |
1518 | <td> |
1519 | {{ zone_item.node_set.count }} |
1520 | (<a href="{% url 'node-list' %}?query=zone%3D{{ zone_item.name }}">view</a>) |
1521 | </td> |
1522 | {% if user.is_superuser %} |
1523 | <td> |
1524 | + {% if not zone_item.is_default %} |
1525 | <a href="{% url 'zone-edit' zone_item.name %}" |
1526 | title="Edit zone {{ zone_item.name }}" |
1527 | class="icon"> |
1528 | @@ -55,6 +58,7 @@ |
1529 | <input type="hidden" name="name" |
1530 | value="{{ zone_item.name }}" /> |
1531 | </form> |
1532 | + {% endif %} |
1533 | </td> |
1534 | {% endif %} |
1535 | </tr> |
1536 | |
1537 | === modified file 'src/maasserver/testing/factory.py' |
1538 | --- src/maasserver/testing/factory.py 2014-01-16 02:59:03 +0000 |
1539 | +++ src/maasserver/testing/factory.py 2014-01-16 08:48:57 +0000 |
1540 | @@ -149,7 +149,7 @@ |
1541 | |
1542 | def make_node(self, mac=False, hostname=None, status=None, |
1543 | architecture=ARCHITECTURE.i386, updated=None, |
1544 | - created=None, nodegroup=None, routers=None, zone=NO_VALUE, |
1545 | + created=None, nodegroup=None, routers=None, zone=None, |
1546 | **kwargs): |
1547 | # hostname=None is a valid value, hence the set_hostname trick. |
1548 | if hostname is None: |
1549 | @@ -160,7 +160,7 @@ |
1550 | nodegroup = self.make_node_group() |
1551 | if routers is None: |
1552 | routers = [self.make_MAC()] |
1553 | - if zone == NO_VALUE: |
1554 | + if zone is None: |
1555 | zone = self.make_zone() |
1556 | node = Node( |
1557 | hostname=hostname, status=status, architecture=architecture, |
1558 | |
1559 | === modified file 'src/maasserver/testing/tests/test_factory.py' |
1560 | --- src/maasserver/testing/tests/test_factory.py 2013-12-18 17:35:45 +0000 |
1561 | +++ src/maasserver/testing/tests/test_factory.py 2014-01-16 08:48:57 +0000 |
1562 | @@ -71,12 +71,6 @@ |
1563 | self.assertEqual( |
1564 | nodegroup, factory.make_node(nodegroup=nodegroup).nodegroup) |
1565 | |
1566 | - def test_make_node_sets_zone_by_default(self): |
1567 | - self.assertIsNotNone(factory.make_node().zone) |
1568 | - |
1569 | - def test_make_node_sets_no_zone_if_zone_is_None(self): |
1570 | - self.assertIsNone(factory.make_node(zone=None).zone) |
1571 | - |
1572 | def test_make_zone_returns_physical_zone(self): |
1573 | self.assertIsNotNone(factory.make_zone()) |
1574 | |
1575 | |
1576 | === modified file 'src/maasserver/tests/test_api_node.py' |
1577 | --- src/maasserver/tests/test_api_node.py 2014-01-16 02:57:53 +0000 |
1578 | +++ src/maasserver/tests/test_api_node.py 2014-01-16 08:48:57 +0000 |
1579 | @@ -703,6 +703,19 @@ |
1580 | node = reload_object(node) |
1581 | self.assertEqual(new_zone, node.zone) |
1582 | |
1583 | + def test_PUT_does_not_set_zone_if_not_present(self): |
1584 | + self.become_admin() |
1585 | + new_name = factory.make_name() |
1586 | + node = factory.make_node() |
1587 | + old_zone = node.zone |
1588 | + |
1589 | + response = self.client_put( |
1590 | + self.get_node_uri(node), {'hostname': new_name}) |
1591 | + |
1592 | + self.assertEqual(httplib.OK, response.status_code) |
1593 | + node = reload_object(node) |
1594 | + self.assertEqual((old_zone, new_name), (node.zone, node.hostname)) |
1595 | + |
1596 | #@skip( |
1597 | # "XXX: JeroenVermeulen 2013-12-11 bug=1259872: Clearing the zone " |
1598 | # "field does not work..") |
1599 | |
1600 | === modified file 'src/maasserver/tests/test_api_nodes.py' |
1601 | --- src/maasserver/tests/test_api_nodes.py 2013-12-18 02:36:56 +0000 |
1602 | +++ src/maasserver/tests/test_api_nodes.py 2014-01-16 08:48:57 +0000 |
1603 | @@ -1,4 +1,4 @@ |
1604 | -# Copyright 2013 Canonical Ltd. This software is licensed under the |
1605 | +# Copyright 2013-2014 Canonical Ltd. This software is licensed under the |
1606 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1607 | |
1608 | """Tests for the nodes API.""" |
1609 | @@ -1044,19 +1044,6 @@ |
1610 | node = reload_object(node) |
1611 | self.assertEqual(zone, node.zone) |
1612 | |
1613 | - def test_POST_set_zone_clears_zone_on_nodes(self): |
1614 | - self.become_admin() |
1615 | - node = factory.make_node() |
1616 | - response = self.client.post( |
1617 | - reverse('nodes_handler'), |
1618 | - { |
1619 | - 'op': 'set_zone', |
1620 | - 'nodes': [node.system_id], |
1621 | - }) |
1622 | - self.assertEqual(httplib.OK, response.status_code) |
1623 | - node = reload_object(node) |
1624 | - self.assertIsNone(node.zone) |
1625 | - |
1626 | def test_POST_set_zone_does_not_affect_other_nodes(self): |
1627 | self.become_admin() |
1628 | node = factory.make_node() |
1629 | |
1630 | === modified file 'src/maasserver/tests/test_api_zone.py' |
1631 | --- src/maasserver/tests/test_api_zone.py 2013-12-18 17:35:45 +0000 |
1632 | +++ src/maasserver/tests/test_api_zone.py 2014-01-16 08:48:57 +0000 |
1633 | @@ -1,4 +1,4 @@ |
1634 | -# Copyright 2013 Canonical Ltd. This software is licensed under the |
1635 | +# Copyright 2013-2014 Canonical Ltd. This software is licensed under the |
1636 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1637 | |
1638 | """Tests for physical `Zone` API.""" |
1639 | @@ -18,6 +18,8 @@ |
1640 | import json |
1641 | |
1642 | from django.core.urlresolvers import reverse |
1643 | +from maasserver.models import Zone |
1644 | +from maasserver.models.zone import DEFAULT_ZONE_NAME |
1645 | from maasserver.testing import reload_object |
1646 | from maasserver.testing.api import APITestCase |
1647 | from maasserver.testing.factory import factory |
1648 | @@ -85,6 +87,17 @@ |
1649 | zone = reload_object(zone) |
1650 | self.assertEqual(new_name, zone.name) |
1651 | |
1652 | + def test_PUT_rejects_change_of_default_zone_name(self): |
1653 | + self.become_admin() |
1654 | + zone = Zone.objects.get_default_zone() |
1655 | + |
1656 | + response = self.client_put( |
1657 | + get_zone_uri(zone), |
1658 | + {'name': factory.make_name('zone')}) |
1659 | + self.assertEqual(httplib.BAD_REQUEST, response.status_code) |
1660 | + zone = reload_object(zone) |
1661 | + self.assertEqual(DEFAULT_ZONE_NAME, zone.name) |
1662 | + |
1663 | def test_PUT_changing_name_maintains_foreign_keys(self): |
1664 | self.become_admin() |
1665 | zone = factory.make_zone() |
1666 | @@ -106,12 +119,33 @@ |
1667 | self.assertEqual(httplib.NO_CONTENT, response.status_code) |
1668 | self.assertIsNone(reload_object(zone)) |
1669 | |
1670 | + def test_DELETE_rejects_deletion_of_default_zone(self): |
1671 | + self.become_admin() |
1672 | + response = self.client.delete( |
1673 | + get_zone_uri(Zone.objects.get_default_zone())) |
1674 | + self.assertEqual(httplib.BAD_REQUEST, response.status_code) |
1675 | + self.assertIsNotNone(Zone.objects.get_default_zone()) |
1676 | + |
1677 | def test_DELETE_requires_admin(self): |
1678 | zone = factory.make_zone() |
1679 | response = self.client.delete(get_zone_uri(zone)) |
1680 | self.assertEqual(httplib.FORBIDDEN, response.status_code) |
1681 | |
1682 | - def test_DELETE_clears_foreign_keys(self): |
1683 | + def test_DELETE_cannot_delete_default_zone(self): |
1684 | + self.become_admin() |
1685 | + zone = Zone.objects.get_default_zone() |
1686 | + |
1687 | + response = self.client.delete(get_zone_uri(zone)) |
1688 | + |
1689 | + self.assertEqual( |
1690 | + ( |
1691 | + httplib.BAD_REQUEST, |
1692 | + "This zone is the default zone, it cannot be deleted.", |
1693 | + ), |
1694 | + (response.status_code, response.content)) |
1695 | + |
1696 | + def test_DELETE_sets_foreign_keys_to_default(self): |
1697 | + default_zone = Zone.objects.get_default_zone() |
1698 | self.become_admin() |
1699 | zone = factory.make_zone() |
1700 | node = factory.make_node(zone=zone) |
1701 | @@ -121,7 +155,7 @@ |
1702 | |
1703 | node = reload_object(node) |
1704 | self.assertIsNotNone(node) |
1705 | - self.assertIsNone(node.zone) |
1706 | + self.assertEquals(default_zone, node.zone) |
1707 | |
1708 | def test_DELETE_is_idempotent(self): |
1709 | self.become_admin() |
1710 | |
1711 | === modified file 'src/maasserver/tests/test_api_zones.py' |
1712 | --- src/maasserver/tests/test_api_zones.py 2013-12-18 17:35:45 +0000 |
1713 | +++ src/maasserver/tests/test_api_zones.py 2014-01-16 08:48:57 +0000 |
1714 | @@ -58,7 +58,8 @@ |
1715 | httplib.FORBIDDEN, response.status_code, response.content) |
1716 | |
1717 | def test_list_returns_zone_list(self): |
1718 | - zones = [factory.make_zone() for i in range(3)] |
1719 | + [factory.make_zone() for i in range(3)] |
1720 | + zones = Zone.objects.all() |
1721 | response = self.client.get( |
1722 | reverse('zones_handler'), |
1723 | {}) |
1724 | @@ -77,7 +78,8 @@ |
1725 | for zone in parsed_result]) |
1726 | |
1727 | def test_list_returns_sorted_zone_list(self): |
1728 | - zones = [factory.make_zone() for i in range(10)] |
1729 | + [factory.make_zone() for i in range(10)] |
1730 | + zones = Zone.objects.all() |
1731 | response = self.client.get( |
1732 | reverse('zones_handler'), |
1733 | {}) |
1734 | @@ -85,5 +87,9 @@ |
1735 | parsed_result = json.loads(response.content) |
1736 | # Sorting is case-insensitive. |
1737 | self.assertEqual( |
1738 | - sorted([zone.name for zone in zones], key=lambda s: s.lower()), |
1739 | + sorted( |
1740 | + [ |
1741 | + zone.name |
1742 | + for zone in zones |
1743 | + ], key=lambda s: s.lower()), |
1744 | [zone.get('name') for zone in parsed_result]) |
1745 | |
1746 | === modified file 'src/maasserver/tests/test_forms.py' |
1747 | --- src/maasserver/tests/test_forms.py 2014-01-14 00:41:50 +0000 |
1748 | +++ src/maasserver/tests/test_forms.py 2014-01-16 08:48:57 +0000 |
1749 | @@ -1,4 +1,4 @@ |
1750 | -# Copyright 2012, 2013 Canonical Ltd. This software is licensed under the |
1751 | +# Copyright 2012-2014 Canonical Ltd. This software is licensed under the |
1752 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1753 | |
1754 | """Test forms.""" |
1755 | @@ -55,6 +55,7 @@ |
1756 | remove_None_values, |
1757 | UnconstrainedMultipleChoiceField, |
1758 | ValidatorMultipleChoiceField, |
1759 | + ZoneForm, |
1760 | ) |
1761 | from maasserver.models import ( |
1762 | Config, |
1763 | @@ -62,6 +63,7 @@ |
1764 | Node, |
1765 | NodeGroup, |
1766 | NodeGroupInterface, |
1767 | + Zone, |
1768 | ) |
1769 | from maasserver.models.config import DEFAULT_CONFIG |
1770 | from maasserver.node_action import ( |
1771 | @@ -447,17 +449,6 @@ |
1772 | # The form saved without error, but the nodegroup change was ignored. |
1773 | self.assertEqual(old_nodegroup, node.nodegroup) |
1774 | |
1775 | - def test_AdminForm_sets_zone_initial_value(self): |
1776 | - zone = factory.make_zone() |
1777 | - node = factory.make_node(zone=zone) |
1778 | - form = AdminNodeForm(instance=node) |
1779 | - self.assertEqual(zone.name, form.initial['zone']) |
1780 | - |
1781 | - def test_AdminForm_sets_zone_initial_empty_value(self): |
1782 | - node = factory.make_node(zone=None) |
1783 | - form = AdminNodeForm(instance=node) |
1784 | - self.assertEqual('', form.initial['zone']) |
1785 | - |
1786 | def test_get_node_edit_form_returns_NodeForm_if_non_admin(self): |
1787 | user = factory.make_user() |
1788 | self.assertEqual(NodeForm, get_node_edit_form(user)) |
1789 | @@ -1195,21 +1186,7 @@ |
1790 | "set_zone is not one of the available choices.", |
1791 | form._errors['action']) |
1792 | |
1793 | - def test_set_zone_can_set_no_zone(self): |
1794 | - node = factory.make_node() |
1795 | - form = BulkNodeActionForm( |
1796 | - user=factory.make_admin(), |
1797 | - data={ |
1798 | - 'action': 'set_zone', |
1799 | - 'zone': None, |
1800 | - 'system_id': [node.system_id], |
1801 | - }) |
1802 | - self.assertTrue(form.is_valid(), form._errors) |
1803 | - self.assertEqual((1, 0, 0), form.save()) |
1804 | - node = reload_object(node) |
1805 | - self.assertIsNone(node.zone) |
1806 | - |
1807 | - def test_set_zone_leaves_unselected_zones_alone(self): |
1808 | + def test_set_zone_leaves_unselected_nodes_alone(self): |
1809 | unselected_node = factory.make_node() |
1810 | original_zone = unselected_node.zone |
1811 | form = BulkNodeActionForm( |
1812 | @@ -1276,3 +1253,52 @@ |
1813 | self.assertIsNone( |
1814 | DownloadProgressForm.get_download( |
1815 | factory.make_node_group(), factory.getRandomString(), 1)) |
1816 | + |
1817 | + |
1818 | +class TestZoneForm(MAASServerTestCase): |
1819 | + """Tests for `ZoneForm`.""" |
1820 | + |
1821 | + def test_creates_zone(self): |
1822 | + name = factory.make_name('zone') |
1823 | + description = factory.getRandomString() |
1824 | + form = ZoneForm(data={'name': name, 'description': description}) |
1825 | + form.save() |
1826 | + zone = Zone.objects.get(name=name) |
1827 | + self.assertIsNotNone(zone) |
1828 | + self.assertEqual(description, zone.description) |
1829 | + |
1830 | + def test_updates_zone(self): |
1831 | + zone = factory.make_zone() |
1832 | + new_description = factory.getRandomString() |
1833 | + form = ZoneForm(data={'description': new_description}, instance=zone) |
1834 | + form.save() |
1835 | + zone = reload_object(zone) |
1836 | + self.assertEqual(new_description, zone.description) |
1837 | + |
1838 | + def test_renames_zone(self): |
1839 | + zone = factory.make_zone() |
1840 | + new_name = factory.make_name('zone') |
1841 | + form = ZoneForm(data={'name': new_name}, instance=zone) |
1842 | + form.save() |
1843 | + zone = reload_object(zone) |
1844 | + self.assertEqual(new_name, zone.name) |
1845 | + self.assertEqual(zone, Zone.objects.get(name=new_name)) |
1846 | + |
1847 | + def test_updates_default_zone_description_works(self): |
1848 | + zone = Zone.objects.get_default_zone() |
1849 | + new_description = factory.getRandomString() |
1850 | + form = ZoneForm(data={'description': new_description}, instance=zone) |
1851 | + self.assertTrue(form.is_valid(), form._errors) |
1852 | + form.save() |
1853 | + zone = reload_object(zone) |
1854 | + self.assertEqual(new_description, zone.description) |
1855 | + |
1856 | + def test_disallows_renaming_default_zone(self): |
1857 | + zone = Zone.objects.get_default_zone() |
1858 | + form = ZoneForm( |
1859 | + data={'name': factory.make_name('zone')}, |
1860 | + instance=zone) |
1861 | + self.assertFalse(form.is_valid()) |
1862 | + self.assertEqual( |
1863 | + {'name': ["This zone is the default zone, it cannot be renamed."]}, |
1864 | + form.errors) |
1865 | |
1866 | === modified file 'src/maasserver/tests/test_node_constraint_filter_forms.py' |
1867 | --- src/maasserver/tests/test_node_constraint_filter_forms.py 2013-12-18 17:35:45 +0000 |
1868 | +++ src/maasserver/tests/test_node_constraint_filter_forms.py 2014-01-16 08:48:57 +0000 |
1869 | @@ -285,7 +285,7 @@ |
1870 | |
1871 | def test_not_in_zone_excludes_given_zones(self): |
1872 | ineligible_nodes = [factory.make_node() for _ in range(2)] |
1873 | - eligible_nodes = [factory.make_node(), factory.make_node(zone=None)] |
1874 | + eligible_nodes = [factory.make_node() for _ in range(2)] |
1875 | self.assertConstrainedNodes( |
1876 | eligible_nodes, |
1877 | {'not_in_zone': [node.zone.name for node in ineligible_nodes]}) |
1878 | |
1879 | === modified file 'src/maasserver/tests/test_views_nodes.py' |
1880 | --- src/maasserver/tests/test_views_nodes.py 2014-01-14 00:50:25 +0000 |
1881 | +++ src/maasserver/tests/test_views_nodes.py 2014-01-16 08:48:57 +0000 |
1882 | @@ -1,4 +1,4 @@ |
1883 | -# Copyright 2012, 2013 Canonical Ltd. This software is licensed under the |
1884 | +# Copyright 2012-2014 Canonical Ltd. This software is licensed under the |
1885 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1886 | |
1887 | """Test maasserver nodes views.""" |
1888 | @@ -275,12 +275,6 @@ |
1889 | [zone_link], |
1890 | get_content_links(response, '.zone-column')) |
1891 | |
1892 | - def test_node_list_shows_placeholder_for_zone_if_none_set(self): |
1893 | - factory.make_node(zone=None) |
1894 | - response = self.client.get(reverse('node-list')) |
1895 | - [zone_field] = fromstring(response.content).cssselect('.zone-column') |
1896 | - self.assertEqual("(Default)", zone_field.text_content().strip()) |
1897 | - |
1898 | def test_node_list_displays_sorted_list_of_nodes(self): |
1899 | # Nodes are sorted on the node list page, newest first. |
1900 | nodes = [factory.make_node() for i in range(3)] |
1901 | @@ -456,16 +450,6 @@ |
1902 | [reverse('zone-view', args=[node.zone.name])], |
1903 | get_content_links(response, '#zone')) |
1904 | |
1905 | - def test_view_node_shows_no_physical_zone_if_not_set(self): |
1906 | - node = factory.make_node(zone=None) |
1907 | - node_link = reverse('node-view', args=[node.system_id]) |
1908 | - |
1909 | - response = self.client.get(node_link) |
1910 | - self.assertEqual(httplib.OK, response.status_code) |
1911 | - |
1912 | - doc = fromstring(response.content) |
1913 | - self.assertEqual([], doc.cssselect('#zone')) |
1914 | - |
1915 | def test_view_node_displays_link_to_edit_if_user_owns_node(self): |
1916 | node = factory.make_node(owner=self.logged_in_user) |
1917 | node_link = reverse('node-view', args=[node.system_id]) |
1918 | |
1919 | === modified file 'src/maasserver/tests/test_views_zones.py' |
1920 | --- src/maasserver/tests/test_views_zones.py 2013-12-20 13:05:55 +0000 |
1921 | +++ src/maasserver/tests/test_views_zones.py 2014-01-16 08:48:57 +0000 |
1922 | @@ -1,4 +1,4 @@ |
1923 | -# Copyright 2013 Canonical Ltd. This software is licensed under the |
1924 | +# Copyright 2013-2014 Canonical Ltd. This software is licensed under the |
1925 | # GNU Affero General Public License version 3 (see the file LICENSE). |
1926 | |
1927 | """Test maasserver zones views.""" |
1928 | @@ -18,9 +18,11 @@ |
1929 | import httplib |
1930 | from urllib import urlencode |
1931 | |
1932 | +from django.core.exceptions import ValidationError |
1933 | from django.core.urlresolvers import reverse |
1934 | from lxml.html import fromstring |
1935 | from maasserver.models import Zone |
1936 | +from maasserver.models.zone import DEFAULT_ZONE_NAME |
1937 | from maasserver.testing import ( |
1938 | extract_redirect, |
1939 | get_content_links, |
1940 | @@ -54,7 +56,8 @@ |
1941 | |
1942 | def test_zone_list_displays_zone_details(self): |
1943 | # Zone listing displays the zone name and the zone description. |
1944 | - zones = [factory.make_zone() for i in range(3)] |
1945 | + [factory.make_zone() for i in range(3)] |
1946 | + zones = Zone.objects.all() |
1947 | response = self.client.get(reverse('zone-list')) |
1948 | zone_names = [zone.name for zone in zones] |
1949 | truncated_zone_descriptions = [ |
1950 | @@ -65,7 +68,8 @@ |
1951 | |
1952 | def test_zone_list_displays_sorted_list_of_zones(self): |
1953 | # Zones are alphabetically sorted on the zone list page. |
1954 | - zones = [factory.make_zone() for i in range(3)] |
1955 | + [factory.make_zone() for i in range(3)] |
1956 | + zones = Zone.objects.all() |
1957 | sorted_zones = sorted(zones, key=lambda x: x.name.lower()) |
1958 | response = self.client.get(reverse('zone-list')) |
1959 | zone_links = [ |
1960 | @@ -77,7 +81,8 @@ |
1961 | if link.startswith('/zones/')]) |
1962 | |
1963 | def test_zone_list_displays_links_to_zone_node(self): |
1964 | - zones = [factory.make_zone() for i in range(3)] |
1965 | + [factory.make_zone() for i in range(3)] |
1966 | + zones = Zone.objects.all() |
1967 | sorted_zones = sorted(zones, key=lambda x: x.name.lower()) |
1968 | response = self.client.get(reverse('zone-list')) |
1969 | zone_node_links = [ |
1970 | @@ -117,14 +122,21 @@ |
1971 | |
1972 | def test_zone_list_contains_edit_links(self): |
1973 | zones = [factory.make_zone() for i in range(3)] |
1974 | - response = self.client.get(reverse('zone-list')) |
1975 | + default_zone = Zone.objects.get_default_zone() |
1976 | zone_edit_links = [ |
1977 | reverse('zone-edit', args=[zone.name]) for zone in zones] |
1978 | zone_delete_links = [ |
1979 | reverse('zone-del', args=[zone.name]) for zone in zones] |
1980 | + zone_default_edit = reverse('zone-edit', args=[default_zone]) |
1981 | + zone_default_delete = reverse('zone-del', args=[default_zone]) |
1982 | + |
1983 | + response = self.client.get(reverse('zone-list')) |
1984 | all_links = get_content_links(response) |
1985 | - self.assertThat(all_links, ContainsAll(zone_edit_links)) |
1986 | - self.assertThat(all_links, ContainsAll(zone_delete_links)) |
1987 | + |
1988 | + self.assertThat(all_links, ContainsAll( |
1989 | + zone_edit_links + zone_delete_links)) |
1990 | + self.assertThat(all_links, Not(Contains(zone_default_edit))) |
1991 | + self.assertThat(all_links, Not(Contains(zone_default_delete))) |
1992 | |
1993 | def test_zone_list_contains_add_link(self): |
1994 | response = self.client.get(reverse('zone-list')) |
1995 | @@ -231,6 +243,12 @@ |
1996 | zone_delete_link = reverse('zone-del', args=[zone.name]) |
1997 | self.assertIn(zone_delete_link, get_content_links(response)) |
1998 | |
1999 | + def test_zone_detail_for_default_zone_does_not_contain_delete_link(self): |
2000 | + response = self.client.get( |
2001 | + reverse('zone-view', args=[DEFAULT_ZONE_NAME])) |
2002 | + zone_delete_link = reverse('zone-del', args=[DEFAULT_ZONE_NAME]) |
2003 | + self.assertNotIn(zone_delete_link, get_content_links(response)) |
2004 | + |
2005 | |
2006 | class ZoneEditNonAdminTest(LoggedInTestCase): |
2007 | |
2008 | @@ -281,6 +299,28 @@ |
2009 | self.assertEqual(httplib.FOUND, response.status_code) |
2010 | self.assertIsNone(reload_object(zone)) |
2011 | |
2012 | + def test_rejects_deletion_of_default_zone(self): |
2013 | + try: |
2014 | + self.client.post( |
2015 | + reverse('zone-del', args=[DEFAULT_ZONE_NAME]), |
2016 | + {'post': 'yes'}) |
2017 | + except ValidationError: |
2018 | + # XXX: Right now, this generates an error because the deletion |
2019 | + # is prevented in the model code and not at the form level. |
2020 | + # This is not so bad because we make sure that the deletion link |
2021 | + # for the default zone isn't showed anywhere. |
2022 | + # Still, ideally, once the validation happens at the form level, |
2023 | + # this try/except statement should be removed and this test |
2024 | + # should be extended further to check that the reponse to |
2025 | + # the above self.client.post indicates a failure to validate |
2026 | + # the data. |
2027 | + pass |
2028 | + |
2029 | + # TODO: check that the page failed to validate the data. |
2030 | + |
2031 | + # The default zone is still there. |
2032 | + self.assertIsNotNone(Zone.objects.get_default_zone()) |
2033 | + |
2034 | def test_redirects_to_listing(self): |
2035 | zone = factory.make_zone() |
2036 | response = self.client.post( |
2037 | @@ -298,4 +338,4 @@ |
2038 | self.assertIsNone(reload_object(zone)) |
2039 | node = reload_object(node) |
2040 | self.assertIsNotNone(node) |
2041 | - self.assertIsNone(node.zone) |
2042 | + self.assertEqual(Zone.objects.get_default_zone(), node.zone) |
2043 | |
2044 | === modified file 'src/maasserver/views/zones.py' |
2045 | --- src/maasserver/views/zones.py 2013-12-18 17:35:45 +0000 |
2046 | +++ src/maasserver/views/zones.py 2014-01-16 08:48:57 +0000 |
2047 | @@ -82,6 +82,7 @@ |
2048 | """View for editing a physical zone.""" |
2049 | |
2050 | model = Zone |
2051 | + form_class = ZoneForm |
2052 | template_name = 'maasserver/zone_edit.html' |
2053 | |
2054 | def get_object(self): |
2055 | @@ -95,7 +96,7 @@ |
2056 | class ZoneDelete(HelpfulDeleteView): |
2057 | """View for deleting a physical zone.""" |
2058 | |
2059 | - template_name = 'maasserver/zone_configm_delete.html' |
2060 | + template_name = 'maasserver/zone_confirm_delete.html' |
2061 | context_object_name = 'zone_to_delete' |
2062 | model = Zone |
2063 |
Good stuff, though I tried to squeeze out a few notes:
.
Having an entire "Is this the default zone" column with a "Yes" in exactly one row seems a bit officious. Normally I'd say: just append "(default)" to the name. But with the default zone's name fixed to "default," that would look ridiculous as well. How about we just leave it out? We could make the default zone look different by italicising its displayed name.
.
Similarly, it looks a bit... clerical to say "Default / No" on every zone's details page and "Default / Yes" on the default zone's. Shouldn't we just have a notice like "This is the default zone. It cannot be renamed or deleted." on the default zone's page, and nothing on the rest? There may not even be a way to click through to that page without first noticing that there is a default zone.
.
Typo — test_updates_ default_ zone_descriptio n_works should lose either the "_works" suffix, of the first "s".
.
Finally, several things about test_rejects_ deletion_ of_default_ zone:
def test_rejects_ deletion_ of_default_ zone(self) :
self. client. post(
reverse( 'zone-del' , args=[DEFAULT_ ZONE_NAME] ),
{'post' : 'yes'})
try:
except ValidationError:
# XXX: Right now, this generates an error because the deletion
If you're going to have an XXX here, add your name and the date. Frankly though I'm not sure it's worth one — we have so many bigger fish to fry!
# is prevented in the model code and not at the form level.
# This is not so bad because we make sure that the deletion link
# for the default zone isn't showed anywhere.
It's "shown," not "showed."
# Still, ideally, once the validation happens at the form level,
# this try/except statement should be removed and this test
# should be extended further to check that the reponse to
# the above self.client.post indicates a failure to validate
# the data.
pass
English has the extra "s" in "response." Keep technical English direct, and avoid the passive voice where possible! For example, I think this version takes less reading to understand: "If we move validation to the form level, this exception goes away and we'll have to check the HTTP response for a validation failure."
# TODO: check that the page failed to validate the data.
If you want to verify that we actually got the ValidationError, use ExpectedException:
with ExpectedExcepti on(ValidationEr ror):
self. client. post(
...)
This will make the test fail if the expected exception is not raised. Arguably that makes the test more brittle if we ever abolish the ValidationError, but in that case the comments will need revisiting anyway so it's appropriate for the test to call attention to itself.
Julian has been saying he would like us to use TODO markers only for reminders to ourselves, and never land them. It may be a little late for that though.
Anyway, thanks for the branch. I'll be happy to see this item closed!