Merge lp:~julian-edwards/maas/dup-boot-source-selections-bug-1360280 into lp:~maas-committers/maas/trunk
- dup-boot-source-selections-bug-1360280
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Julian Edwards |
Approved revision: | no longer in the source branch. |
Merged at revision: | 3361 |
Proposed branch: | lp:~julian-edwards/maas/dup-boot-source-selections-bug-1360280 |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
867 lines (+832/-0) 4 files modified
src/maasserver/migrations/0115_unique_boot_source_selections.py (+433/-0) src/maasserver/migrations/0116_unique_boot_source_selections.py (+373/-0) src/maasserver/models/bootsourceselection.py (+1/-0) src/maasserver/tests/test_forms_bootsourceselection.py (+25/-0) |
To merge this branch: | bzr merge lp:~julian-edwards/maas/dup-boot-source-selections-bug-1360280 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Graham Binns (community) | Approve | ||
Review via email:
|
Commit message
Make os, release and boot_source unique for BootSourceSelection to stop duplicate entries.
Description of the change
Add a unique index across boot_source, os and release on BootSourceSelec
This will obviously potentially fail a migration if someone has dupes already, but this really needs to go in. Since nobody was around to do a pre-imp I'm throwing this up as-is to discuss ideas:
1. Land it as-is and if it fails add a release note telling someone to remove the dupes manually (so they get to choose which to keep)
2. Add code to the migration to force-delete all but the newest entry for each boot source.
Anything else?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Graham Binns (gmb) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Julian Edwards (julian-edwards) wrote : | # |
On Monday 10 Nov 2014 07:18:40 you wrote:
> ISTR Blake saying that we had to coalesce the entries for one OS/release
> rather than just dropping all-but-one, because the entries may all have
> different arches, subarches and labels — all of which are perfectly valid
> choices, but which need to be recorded in a single row, not several.
>
> So, if we don't want to have settings go away surprisingly, (1) is a good
> option.
>
> That said, could we add a persistent, user-clearable error* after doing (2)
> that says something like "MAAS's records of your boot source settings may
> have changed; check the Images tab to ensure that your boot image settings
> are correct."
>
> *Not sure we actually have the facility to do this; I was thinking of the
> persistent error stuff, but that all seems to be auto-cleared or not at
> all.
Actually I hadn't considered coalescing them - good point! I'll write some
runes to do that later.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Julian Edwards (julian-edwards) wrote : | # |
Right, coalescing migration added, care to take another look?
I have tested this reasonably thoroughly in the dev environment, I used this script to poke values in http://
Note that there's two migrations as you can't do data and schema changes in the same migration.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Graham Binns (gmb) wrote : | # |
Tip top. Really nice work, Jools!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~julian-edwards/maas/dup-boot-source-selections-bug-1360280 into lp:maas failed. Below is the output from the failed tests.
Ign http://
Ign http://
Get:1 http://
Ign http://
Get:2 http://
Hit http://
Get:3 http://
Hit http://
Get:4 http://
Hit http://
Hit http://
Hit http://
Get:5 http://
Hit http://
Hit http://
Hit http://
Get:6 http://
Get:7 http://
Get:8 http://
Get:9 http://
Get:10 http://
Get:11 http://
Hit http://
Hit http://
Get:12 http://
Hit http://
Hit http://
Ign http://
Ign http://
Fetched 1,200 kB in 2s (411 kB/s)
Reading package lists...
sudo DEBIAN_
--
Preview Diff
1 | === added file 'src/maasserver/migrations/0115_unique_boot_source_selections.py' | |||
2 | --- src/maasserver/migrations/0115_unique_boot_source_selections.py 1970-01-01 00:00:00 +0000 | |||
3 | +++ src/maasserver/migrations/0115_unique_boot_source_selections.py 2014-11-12 00:21:52 +0000 | |||
4 | @@ -0,0 +1,433 @@ | |||
5 | 1 | from collections import namedtuple | ||
6 | 2 | |||
7 | 3 | from django.db import models | ||
8 | 4 | from south.db import db | ||
9 | 5 | # -*- coding: utf-8 -*- | ||
10 | 6 | from south.utils import datetime_utils as datetime | ||
11 | 7 | from south.v2 import SchemaMigration | ||
12 | 8 | |||
13 | 9 | |||
14 | 10 | Selection = namedtuple('Selection', ['boot_source', 'os', 'release']) | ||
15 | 11 | Values = namedtuple('Values', ['arches', 'subarches', 'labels']) | ||
16 | 12 | |||
17 | 13 | |||
18 | 14 | class Migration(SchemaMigration): | ||
19 | 15 | |||
20 | 16 | def forwards(self, orm): | ||
21 | 17 | # Coalesce all the arch/subarch/label fields for duplicated | ||
22 | 18 | # records across release/os/boot_source. Do this by using the | ||
23 | 19 | # Selection as the key in a dict containing Values. | ||
24 | 20 | boot_sources = dict() | ||
25 | 21 | for bss in orm['maasserver.bootsourceselection'].objects.all(): | ||
26 | 22 | selection = Selection( | ||
27 | 23 | boot_source=bss.boot_source, | ||
28 | 24 | os=bss.os, | ||
29 | 25 | release=bss.release, | ||
30 | 26 | ) | ||
31 | 27 | values = Values( | ||
32 | 28 | arches=set(bss.arches), | ||
33 | 29 | subarches=set(bss.subarches), | ||
34 | 30 | labels=set(bss.labels), | ||
35 | 31 | ) | ||
36 | 32 | if selection not in boot_sources: | ||
37 | 33 | # New boot_source/os/release combo. | ||
38 | 34 | boot_sources[selection] = values | ||
39 | 35 | else: | ||
40 | 36 | # Merge with existing. | ||
41 | 37 | arches_set = boot_sources[selection].arches | ||
42 | 38 | subarches_set = boot_sources[selection].subarches | ||
43 | 39 | labels_set = boot_sources[selection].labels | ||
44 | 40 | |||
45 | 41 | boot_sources[selection] = Values( | ||
46 | 42 | arches=arches_set.union(values.arches), | ||
47 | 43 | subarches=subarches_set.union(values.subarches), | ||
48 | 44 | labels=labels_set.union(values.labels), | ||
49 | 45 | ) | ||
50 | 46 | |||
51 | 47 | # Delete all the existing rows in the table. Nothing currently | ||
52 | 48 | # has BootSourceSelection as a foreign key, so Django's default | ||
53 | 49 | # behaviour of ON DELETE CASCADE should not delete anything | ||
54 | 50 | # else here. | ||
55 | 51 | orm['maasserver.bootsourceselection'].objects.all().delete() | ||
56 | 52 | |||
57 | 53 | now = datetime.datetime.utcnow() | ||
58 | 54 | # Add the coalesced records. | ||
59 | 55 | for selection, values in boot_sources.viewitems(): | ||
60 | 56 | bss = orm['maasserver.BootSourceSelection']( | ||
61 | 57 | created=now, | ||
62 | 58 | updated=now, | ||
63 | 59 | boot_source=selection.boot_source, | ||
64 | 60 | os=selection.os, | ||
65 | 61 | release=selection.release, | ||
66 | 62 | arches=list(values.arches), | ||
67 | 63 | subarches=list(values.subarches), | ||
68 | 64 | labels=list(values.labels), | ||
69 | 65 | ) | ||
70 | 66 | bss.save() | ||
71 | 67 | |||
72 | 68 | # This happens in the next migration as you cannot do data and | ||
73 | 69 | # schema in the same one. | ||
74 | 70 | # Adding unique constraint on 'BootSourceSelection', fields ['release', 'boot_source', 'os'] | ||
75 | 71 | #db.create_unique(u'maasserver_bootsourceselection', ['release', 'boot_source_id', 'os']) | ||
76 | 72 | |||
77 | 73 | |||
78 | 74 | def backwards(self, orm): | ||
79 | 75 | # Removing unique constraint on 'BootSourceSelection', fields ['release', 'boot_source', 'os'] | ||
80 | 76 | #db.delete_unique(u'maasserver_bootsourceselection', ['release', 'boot_source_id', 'os']) | ||
81 | 77 | pass | ||
82 | 78 | |||
83 | 79 | |||
84 | 80 | models = { | ||
85 | 81 | u'auth.group': { | ||
86 | 82 | 'Meta': {'object_name': 'Group'}, | ||
87 | 83 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
88 | 84 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), | ||
89 | 85 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) | ||
90 | 86 | }, | ||
91 | 87 | u'auth.permission': { | ||
92 | 88 | 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, | ||
93 | 89 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
94 | 90 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), | ||
95 | 91 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
96 | 92 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | ||
97 | 93 | }, | ||
98 | 94 | u'auth.user': { | ||
99 | 95 | 'Meta': {'object_name': 'User'}, | ||
100 | 96 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
101 | 97 | 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), | ||
102 | 98 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
103 | 99 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), | ||
104 | 100 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
105 | 101 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | ||
106 | 102 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
107 | 103 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
108 | 104 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
109 | 105 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
110 | 106 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), | ||
111 | 107 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), | ||
112 | 108 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) | ||
113 | 109 | }, | ||
114 | 110 | u'contenttypes.contenttype': { | ||
115 | 111 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, | ||
116 | 112 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
117 | 113 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
118 | 114 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
119 | 115 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) | ||
120 | 116 | }, | ||
121 | 117 | u'maasserver.bootresource': { | ||
122 | 118 | 'Meta': {'unique_together': "((u'name', u'architecture'),)", 'object_name': 'BootResource'}, | ||
123 | 119 | 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
124 | 120 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
125 | 121 | 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), | ||
126 | 122 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
127 | 123 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
128 | 124 | 'rtype': ('django.db.models.fields.IntegerField', [], {'max_length': '10'}), | ||
129 | 125 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
130 | 126 | }, | ||
131 | 127 | u'maasserver.bootresourcefile': { | ||
132 | 128 | 'Meta': {'unique_together': "((u'resource_set', u'filetype'),)", 'object_name': 'BootResourceFile'}, | ||
133 | 129 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
134 | 130 | 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), | ||
135 | 131 | 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
136 | 132 | 'filetype': ('django.db.models.fields.CharField', [], {'default': "u'root-tgz'", 'max_length': '20'}), | ||
137 | 133 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
138 | 134 | 'largefile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.LargeFile']"}), | ||
139 | 135 | 'resource_set': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'files'", 'to': u"orm['maasserver.BootResourceSet']"}), | ||
140 | 136 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
141 | 137 | }, | ||
142 | 138 | u'maasserver.bootresourceset': { | ||
143 | 139 | 'Meta': {'unique_together': "((u'resource', u'version'),)", 'object_name': 'BootResourceSet'}, | ||
144 | 140 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
145 | 141 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
146 | 142 | 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
147 | 143 | 'resource': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'sets'", 'to': u"orm['maasserver.BootResource']"}), | ||
148 | 144 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
149 | 145 | 'version': ('django.db.models.fields.CharField', [], {'max_length': '255'}) | ||
150 | 146 | }, | ||
151 | 147 | u'maasserver.bootsource': { | ||
152 | 148 | 'Meta': {'object_name': 'BootSource'}, | ||
153 | 149 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
154 | 150 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
155 | 151 | 'keyring_data': ('maasserver.fields.EditableBinaryField', [], {'blank': 'True'}), | ||
156 | 152 | 'keyring_filename': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}), | ||
157 | 153 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
158 | 154 | 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'}) | ||
159 | 155 | }, | ||
160 | 156 | u'maasserver.bootsourcecache': { | ||
161 | 157 | 'Meta': {'object_name': 'BootSourceCache'}, | ||
162 | 158 | 'arch': ('django.db.models.fields.CharField', [], {'max_length': '20'}), | ||
163 | 159 | 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}), | ||
164 | 160 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
165 | 161 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
166 | 162 | 'label': ('django.db.models.fields.CharField', [], {'max_length': '20'}), | ||
167 | 163 | 'os': ('django.db.models.fields.CharField', [], {'max_length': '20'}), | ||
168 | 164 | 'release': ('django.db.models.fields.CharField', [], {'max_length': '20'}), | ||
169 | 165 | 'subarch': ('django.db.models.fields.CharField', [], {'max_length': '20'}), | ||
170 | 166 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
171 | 167 | }, | ||
172 | 168 | u'maasserver.bootsourceselection': { | ||
173 | 169 | 'Meta': {'unique_together': "((u'boot_source', u'os', u'release'),)", 'object_name': 'BootSourceSelection'}, | ||
174 | 170 | 'arches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), | ||
175 | 171 | 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}), | ||
176 | 172 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
177 | 173 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
178 | 174 | 'labels': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), | ||
179 | 175 | 'os': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), | ||
180 | 176 | 'release': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), | ||
181 | 177 | 'subarches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), | ||
182 | 178 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
183 | 179 | }, | ||
184 | 180 | u'maasserver.candidatename': { | ||
185 | 181 | 'Meta': {'unique_together': "((u'name', u'position'),)", 'object_name': 'CandidateName'}, | ||
186 | 182 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
187 | 183 | 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), | ||
188 | 184 | 'position': ('django.db.models.fields.IntegerField', [], {}) | ||
189 | 185 | }, | ||
190 | 186 | u'maasserver.componenterror': { | ||
191 | 187 | 'Meta': {'object_name': 'ComponentError'}, | ||
192 | 188 | 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}), | ||
193 | 189 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
194 | 190 | 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}), | ||
195 | 191 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
196 | 192 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
197 | 193 | }, | ||
198 | 194 | u'maasserver.config': { | ||
199 | 195 | 'Meta': {'object_name': 'Config'}, | ||
200 | 196 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
201 | 197 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
202 | 198 | 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'}) | ||
203 | 199 | }, | ||
204 | 200 | u'maasserver.dhcplease': { | ||
205 | 201 | 'Meta': {'object_name': 'DHCPLease'}, | ||
206 | 202 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
207 | 203 | 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}), | ||
208 | 204 | 'mac': ('maasserver.fields.MACAddressField', [], {}), | ||
209 | 205 | 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}) | ||
210 | 206 | }, | ||
211 | 207 | u'maasserver.downloadprogress': { | ||
212 | 208 | 'Meta': {'object_name': 'DownloadProgress'}, | ||
213 | 209 | 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), | ||
214 | 210 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
215 | 211 | 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}), | ||
216 | 212 | 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
217 | 213 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
218 | 214 | 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), | ||
219 | 215 | 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), | ||
220 | 216 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
221 | 217 | }, | ||
222 | 218 | u'maasserver.event': { | ||
223 | 219 | 'Meta': {'object_name': 'Event'}, | ||
224 | 220 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
225 | 221 | 'description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}), | ||
226 | 222 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
227 | 223 | 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}), | ||
228 | 224 | 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.EventType']"}), | ||
229 | 225 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
230 | 226 | }, | ||
231 | 227 | u'maasserver.eventtype': { | ||
232 | 228 | 'Meta': {'object_name': 'EventType'}, | ||
233 | 229 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
234 | 230 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
235 | 231 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
236 | 232 | 'level': ('django.db.models.fields.IntegerField', [], {}), | ||
237 | 233 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), | ||
238 | 234 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
239 | 235 | }, | ||
240 | 236 | u'maasserver.filestorage': { | ||
241 | 237 | 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'}, | ||
242 | 238 | 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}), | ||
243 | 239 | 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
244 | 240 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
245 | 241 | 'key': ('django.db.models.fields.CharField', [], {'default': "u'0121a28a-6899-11e4-b353-0026c71eea0e'", 'unique': 'True', 'max_length': '36'}), | ||
246 | 242 | 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) | ||
247 | 243 | }, | ||
248 | 244 | u'maasserver.largefile': { | ||
249 | 245 | 'Meta': {'object_name': 'LargeFile'}, | ||
250 | 246 | 'content': ('maasserver.fields.LargeObjectField', [], {}), | ||
251 | 247 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
252 | 248 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
253 | 249 | 'sha256': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), | ||
254 | 250 | 'total_size': ('django.db.models.fields.BigIntegerField', [], {}), | ||
255 | 251 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
256 | 252 | }, | ||
257 | 253 | u'maasserver.licensekey': { | ||
258 | 254 | 'Meta': {'unique_together': "((u'osystem', u'distro_series'),)", 'object_name': 'LicenseKey'}, | ||
259 | 255 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
260 | 256 | 'distro_series': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
261 | 257 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
262 | 258 | 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
263 | 259 | 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
264 | 260 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
265 | 261 | }, | ||
266 | 262 | u'maasserver.macaddress': { | ||
267 | 263 | 'Meta': {'ordering': "(u'created',)", 'object_name': 'MACAddress'}, | ||
268 | 264 | 'cluster_interface': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['maasserver.NodeGroupInterface']", 'null': 'True', 'blank': 'True'}), | ||
269 | 265 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
270 | 266 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
271 | 267 | 'ip_addresses': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.StaticIPAddress']", 'symmetrical': 'False', 'through': u"orm['maasserver.MACStaticIPAddressLink']", 'blank': 'True'}), | ||
272 | 268 | 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}), | ||
273 | 269 | 'networks': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Network']", 'symmetrical': 'False', 'blank': 'True'}), | ||
274 | 270 | 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}), | ||
275 | 271 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
276 | 272 | }, | ||
277 | 273 | u'maasserver.macstaticipaddresslink': { | ||
278 | 274 | 'Meta': {'unique_together': "((u'ip_address', u'mac_address'),)", 'object_name': 'MACStaticIPAddressLink'}, | ||
279 | 275 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
280 | 276 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
281 | 277 | 'ip_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.StaticIPAddress']", 'unique': 'True'}), | ||
282 | 278 | 'mac_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.MACAddress']"}), | ||
283 | 279 | 'nic_alias': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), | ||
284 | 280 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
285 | 281 | }, | ||
286 | 282 | u'maasserver.network': { | ||
287 | 283 | 'Meta': {'object_name': 'Network'}, | ||
288 | 284 | 'default_gateway': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
289 | 285 | 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), | ||
290 | 286 | 'dns_servers': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), | ||
291 | 287 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
292 | 288 | 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}), | ||
293 | 289 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), | ||
294 | 290 | 'netmask': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}), | ||
295 | 291 | 'vlan_tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'null': 'True', 'blank': 'True'}) | ||
296 | 292 | }, | ||
297 | 293 | u'maasserver.node': { | ||
298 | 294 | 'Meta': {'object_name': 'Node'}, | ||
299 | 295 | 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), | ||
300 | 296 | 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '31'}), | ||
301 | 297 | 'boot_type': ('django.db.models.fields.CharField', [], {'default': "u'fastpath'", 'max_length': '20'}), | ||
302 | 298 | 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | ||
303 | 299 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
304 | 300 | 'disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
305 | 301 | 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), | ||
306 | 302 | 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), | ||
307 | 303 | 'error_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}), | ||
308 | 304 | 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}), | ||
309 | 305 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
310 | 306 | 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), | ||
311 | 307 | 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | ||
312 | 308 | 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | ||
313 | 309 | 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}), | ||
314 | 310 | 'osystem': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), | ||
315 | 311 | 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), | ||
316 | 312 | 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), | ||
317 | 313 | 'power_state': ('django.db.models.fields.CharField', [], {'default': "u'unknown'", 'max_length': '10'}), | ||
318 | 314 | 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}), | ||
319 | 315 | 'pxe_mac': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "u'+'", 'null': 'True', 'blank': 'True', 'to': u"orm['maasserver.MACAddress']"}), | ||
320 | 316 | 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}), | ||
321 | 317 | 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}), | ||
322 | 318 | 'storage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | ||
323 | 319 | 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-012616a8-6899-11e4-b353-0026c71eea0e'", 'unique': 'True', 'max_length': '41'}), | ||
324 | 320 | 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}), | ||
325 | 321 | 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}), | ||
326 | 322 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
327 | 323 | 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'}) | ||
328 | 324 | }, | ||
329 | 325 | u'maasserver.nodegroup': { | ||
330 | 326 | 'Meta': {'object_name': 'NodeGroup'}, | ||
331 | 327 | 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}), | ||
332 | 328 | 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}), | ||
333 | 329 | 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}), | ||
334 | 330 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
335 | 331 | 'default_disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
336 | 332 | 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), | ||
337 | 333 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
338 | 334 | 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), | ||
339 | 335 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}), | ||
340 | 336 | 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | ||
341 | 337 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
342 | 338 | 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}) | ||
343 | 339 | }, | ||
344 | 340 | u'maasserver.nodegroupinterface': { | ||
345 | 341 | 'Meta': {'unique_together': "((u'nodegroup', u'name'),)", 'object_name': 'NodeGroupInterface'}, | ||
346 | 342 | 'broadcast_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
347 | 343 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
348 | 344 | 'foreign_dhcp_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
349 | 345 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
350 | 346 | 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), | ||
351 | 347 | 'ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}), | ||
352 | 348 | 'ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
353 | 349 | 'ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
354 | 350 | 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | ||
355 | 351 | 'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), | ||
356 | 352 | 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), | ||
357 | 353 | 'router_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
358 | 354 | 'static_ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
359 | 355 | 'static_ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
360 | 356 | 'subnet_mask': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
361 | 357 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
362 | 358 | }, | ||
363 | 359 | u'maasserver.sshkey': { | ||
364 | 360 | 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'}, | ||
365 | 361 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
366 | 362 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
367 | 363 | 'key': ('django.db.models.fields.TextField', [], {}), | ||
368 | 364 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
369 | 365 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) | ||
370 | 366 | }, | ||
371 | 367 | u'maasserver.sslkey': { | ||
372 | 368 | 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSLKey'}, | ||
373 | 369 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
374 | 370 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
375 | 371 | 'key': ('django.db.models.fields.TextField', [], {}), | ||
376 | 372 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
377 | 373 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) | ||
378 | 374 | }, | ||
379 | 375 | u'maasserver.staticipaddress': { | ||
380 | 376 | 'Meta': {'object_name': 'StaticIPAddress'}, | ||
381 | 377 | 'alloc_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | ||
382 | 378 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
383 | 379 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
384 | 380 | 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}), | ||
385 | 381 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
386 | 382 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) | ||
387 | 383 | }, | ||
388 | 384 | u'maasserver.tag': { | ||
389 | 385 | 'Meta': {'object_name': 'Tag'}, | ||
390 | 386 | 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), | ||
391 | 387 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
392 | 388 | 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}), | ||
393 | 389 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
394 | 390 | 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), | ||
395 | 391 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), | ||
396 | 392 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
397 | 393 | }, | ||
398 | 394 | u'maasserver.userprofile': { | ||
399 | 395 | 'Meta': {'object_name': 'UserProfile'}, | ||
400 | 396 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
401 | 397 | 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) | ||
402 | 398 | }, | ||
403 | 399 | u'maasserver.zone': { | ||
404 | 400 | 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'}, | ||
405 | 401 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
406 | 402 | 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), | ||
407 | 403 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
408 | 404 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), | ||
409 | 405 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
410 | 406 | }, | ||
411 | 407 | u'piston.consumer': { | ||
412 | 408 | 'Meta': {'object_name': 'Consumer'}, | ||
413 | 409 | 'description': ('django.db.models.fields.TextField', [], {}), | ||
414 | 410 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
415 | 411 | 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), | ||
416 | 412 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
417 | 413 | 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), | ||
418 | 414 | 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}), | ||
419 | 415 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"}) | ||
420 | 416 | }, | ||
421 | 417 | u'piston.token': { | ||
422 | 418 | 'Meta': {'object_name': 'Token'}, | ||
423 | 419 | 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), | ||
424 | 420 | 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
425 | 421 | 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}), | ||
426 | 422 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
427 | 423 | 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
428 | 424 | 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), | ||
429 | 425 | 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), | ||
430 | 426 | 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1415596707L'}), | ||
431 | 427 | 'token_type': ('django.db.models.fields.IntegerField', [], {}), | ||
432 | 428 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}), | ||
433 | 429 | 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'}) | ||
434 | 430 | } | ||
435 | 431 | } | ||
436 | 432 | |||
437 | 433 | complete_apps = ['maasserver'] | ||
438 | 0 | 434 | ||
439 | === added file 'src/maasserver/migrations/0116_unique_boot_source_selections.py' | |||
440 | --- src/maasserver/migrations/0116_unique_boot_source_selections.py 1970-01-01 00:00:00 +0000 | |||
441 | +++ src/maasserver/migrations/0116_unique_boot_source_selections.py 2014-11-12 00:21:52 +0000 | |||
442 | @@ -0,0 +1,373 @@ | |||
443 | 1 | from django.db import models | ||
444 | 2 | from south.db import db | ||
445 | 3 | # -*- coding: utf-8 -*- | ||
446 | 4 | from south.utils import datetime_utils as datetime | ||
447 | 5 | from south.v2 import SchemaMigration | ||
448 | 6 | |||
449 | 7 | |||
450 | 8 | class Migration(SchemaMigration): | ||
451 | 9 | |||
452 | 10 | def forwards(self, orm): | ||
453 | 11 | # Adding unique constraint on 'BootSourceSelection', fields ['release', 'boot_source', 'os'] | ||
454 | 12 | db.create_unique(u'maasserver_bootsourceselection', ['release', 'boot_source_id', 'os']) | ||
455 | 13 | |||
456 | 14 | |||
457 | 15 | def backwards(self, orm): | ||
458 | 16 | # Removing unique constraint on 'BootSourceSelection', fields ['release', 'boot_source', 'os'] | ||
459 | 17 | db.delete_unique(u'maasserver_bootsourceselection', ['release', 'boot_source_id', 'os']) | ||
460 | 18 | |||
461 | 19 | |||
462 | 20 | models = { | ||
463 | 21 | u'auth.group': { | ||
464 | 22 | 'Meta': {'object_name': 'Group'}, | ||
465 | 23 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
466 | 24 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), | ||
467 | 25 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) | ||
468 | 26 | }, | ||
469 | 27 | u'auth.permission': { | ||
470 | 28 | 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, | ||
471 | 29 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
472 | 30 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), | ||
473 | 31 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
474 | 32 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | ||
475 | 33 | }, | ||
476 | 34 | u'auth.user': { | ||
477 | 35 | 'Meta': {'object_name': 'User'}, | ||
478 | 36 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
479 | 37 | 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), | ||
480 | 38 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
481 | 39 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), | ||
482 | 40 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
483 | 41 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | ||
484 | 42 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
485 | 43 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
486 | 44 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
487 | 45 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
488 | 46 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), | ||
489 | 47 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), | ||
490 | 48 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) | ||
491 | 49 | }, | ||
492 | 50 | u'contenttypes.contenttype': { | ||
493 | 51 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, | ||
494 | 52 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
495 | 53 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
496 | 54 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
497 | 55 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) | ||
498 | 56 | }, | ||
499 | 57 | u'maasserver.bootresource': { | ||
500 | 58 | 'Meta': {'unique_together': "((u'name', u'architecture'),)", 'object_name': 'BootResource'}, | ||
501 | 59 | 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
502 | 60 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
503 | 61 | 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), | ||
504 | 62 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
505 | 63 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
506 | 64 | 'rtype': ('django.db.models.fields.IntegerField', [], {'max_length': '10'}), | ||
507 | 65 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
508 | 66 | }, | ||
509 | 67 | u'maasserver.bootresourcefile': { | ||
510 | 68 | 'Meta': {'unique_together': "((u'resource_set', u'filetype'),)", 'object_name': 'BootResourceFile'}, | ||
511 | 69 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
512 | 70 | 'extra': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), | ||
513 | 71 | 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
514 | 72 | 'filetype': ('django.db.models.fields.CharField', [], {'default': "u'root-tgz'", 'max_length': '20'}), | ||
515 | 73 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
516 | 74 | 'largefile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.LargeFile']"}), | ||
517 | 75 | 'resource_set': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'files'", 'to': u"orm['maasserver.BootResourceSet']"}), | ||
518 | 76 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
519 | 77 | }, | ||
520 | 78 | u'maasserver.bootresourceset': { | ||
521 | 79 | 'Meta': {'unique_together': "((u'resource', u'version'),)", 'object_name': 'BootResourceSet'}, | ||
522 | 80 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
523 | 81 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
524 | 82 | 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
525 | 83 | 'resource': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'sets'", 'to': u"orm['maasserver.BootResource']"}), | ||
526 | 84 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
527 | 85 | 'version': ('django.db.models.fields.CharField', [], {'max_length': '255'}) | ||
528 | 86 | }, | ||
529 | 87 | u'maasserver.bootsource': { | ||
530 | 88 | 'Meta': {'object_name': 'BootSource'}, | ||
531 | 89 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
532 | 90 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
533 | 91 | 'keyring_data': ('maasserver.fields.EditableBinaryField', [], {'blank': 'True'}), | ||
534 | 92 | 'keyring_filename': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}), | ||
535 | 93 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
536 | 94 | 'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '200'}) | ||
537 | 95 | }, | ||
538 | 96 | u'maasserver.bootsourcecache': { | ||
539 | 97 | 'Meta': {'object_name': 'BootSourceCache'}, | ||
540 | 98 | 'arch': ('django.db.models.fields.CharField', [], {'max_length': '20'}), | ||
541 | 99 | 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}), | ||
542 | 100 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
543 | 101 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
544 | 102 | 'label': ('django.db.models.fields.CharField', [], {'max_length': '20'}), | ||
545 | 103 | 'os': ('django.db.models.fields.CharField', [], {'max_length': '20'}), | ||
546 | 104 | 'release': ('django.db.models.fields.CharField', [], {'max_length': '20'}), | ||
547 | 105 | 'subarch': ('django.db.models.fields.CharField', [], {'max_length': '20'}), | ||
548 | 106 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
549 | 107 | }, | ||
550 | 108 | u'maasserver.bootsourceselection': { | ||
551 | 109 | 'Meta': {'unique_together': "((u'boot_source', u'os', u'release'),)", 'object_name': 'BootSourceSelection'}, | ||
552 | 110 | 'arches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), | ||
553 | 111 | 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}), | ||
554 | 112 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
555 | 113 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
556 | 114 | 'labels': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), | ||
557 | 115 | 'os': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), | ||
558 | 116 | 'release': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), | ||
559 | 117 | 'subarches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), | ||
560 | 118 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
561 | 119 | }, | ||
562 | 120 | u'maasserver.candidatename': { | ||
563 | 121 | 'Meta': {'unique_together': "((u'name', u'position'),)", 'object_name': 'CandidateName'}, | ||
564 | 122 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
565 | 123 | 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), | ||
566 | 124 | 'position': ('django.db.models.fields.IntegerField', [], {}) | ||
567 | 125 | }, | ||
568 | 126 | u'maasserver.componenterror': { | ||
569 | 127 | 'Meta': {'object_name': 'ComponentError'}, | ||
570 | 128 | 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}), | ||
571 | 129 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
572 | 130 | 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}), | ||
573 | 131 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
574 | 132 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
575 | 133 | }, | ||
576 | 134 | u'maasserver.config': { | ||
577 | 135 | 'Meta': {'object_name': 'Config'}, | ||
578 | 136 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
579 | 137 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
580 | 138 | 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'}) | ||
581 | 139 | }, | ||
582 | 140 | u'maasserver.dhcplease': { | ||
583 | 141 | 'Meta': {'object_name': 'DHCPLease'}, | ||
584 | 142 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
585 | 143 | 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}), | ||
586 | 144 | 'mac': ('maasserver.fields.MACAddressField', [], {}), | ||
587 | 145 | 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}) | ||
588 | 146 | }, | ||
589 | 147 | u'maasserver.downloadprogress': { | ||
590 | 148 | 'Meta': {'object_name': 'DownloadProgress'}, | ||
591 | 149 | 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), | ||
592 | 150 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
593 | 151 | 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}), | ||
594 | 152 | 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
595 | 153 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
596 | 154 | 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), | ||
597 | 155 | 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), | ||
598 | 156 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
599 | 157 | }, | ||
600 | 158 | u'maasserver.event': { | ||
601 | 159 | 'Meta': {'object_name': 'Event'}, | ||
602 | 160 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
603 | 161 | 'description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}), | ||
604 | 162 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
605 | 163 | 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}), | ||
606 | 164 | 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.EventType']"}), | ||
607 | 165 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
608 | 166 | }, | ||
609 | 167 | u'maasserver.eventtype': { | ||
610 | 168 | 'Meta': {'object_name': 'EventType'}, | ||
611 | 169 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
612 | 170 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
613 | 171 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
614 | 172 | 'level': ('django.db.models.fields.IntegerField', [], {}), | ||
615 | 173 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), | ||
616 | 174 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
617 | 175 | }, | ||
618 | 176 | u'maasserver.filestorage': { | ||
619 | 177 | 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'}, | ||
620 | 178 | 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}), | ||
621 | 179 | 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
622 | 180 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
623 | 181 | 'key': ('django.db.models.fields.CharField', [], {'default': "u'0121a28a-6899-11e4-b353-0026c71eea0e'", 'unique': 'True', 'max_length': '36'}), | ||
624 | 182 | 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) | ||
625 | 183 | }, | ||
626 | 184 | u'maasserver.largefile': { | ||
627 | 185 | 'Meta': {'object_name': 'LargeFile'}, | ||
628 | 186 | 'content': ('maasserver.fields.LargeObjectField', [], {}), | ||
629 | 187 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
630 | 188 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
631 | 189 | 'sha256': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}), | ||
632 | 190 | 'total_size': ('django.db.models.fields.BigIntegerField', [], {}), | ||
633 | 191 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
634 | 192 | }, | ||
635 | 193 | u'maasserver.licensekey': { | ||
636 | 194 | 'Meta': {'unique_together': "((u'osystem', u'distro_series'),)", 'object_name': 'LicenseKey'}, | ||
637 | 195 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
638 | 196 | 'distro_series': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
639 | 197 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
640 | 198 | 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
641 | 199 | 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
642 | 200 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
643 | 201 | }, | ||
644 | 202 | u'maasserver.macaddress': { | ||
645 | 203 | 'Meta': {'ordering': "(u'created',)", 'object_name': 'MACAddress'}, | ||
646 | 204 | 'cluster_interface': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['maasserver.NodeGroupInterface']", 'null': 'True', 'blank': 'True'}), | ||
647 | 205 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
648 | 206 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
649 | 207 | 'ip_addresses': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.StaticIPAddress']", 'symmetrical': 'False', 'through': u"orm['maasserver.MACStaticIPAddressLink']", 'blank': 'True'}), | ||
650 | 208 | 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}), | ||
651 | 209 | 'networks': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Network']", 'symmetrical': 'False', 'blank': 'True'}), | ||
652 | 210 | 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}), | ||
653 | 211 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
654 | 212 | }, | ||
655 | 213 | u'maasserver.macstaticipaddresslink': { | ||
656 | 214 | 'Meta': {'unique_together': "((u'ip_address', u'mac_address'),)", 'object_name': 'MACStaticIPAddressLink'}, | ||
657 | 215 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
658 | 216 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
659 | 217 | 'ip_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.StaticIPAddress']", 'unique': 'True'}), | ||
660 | 218 | 'mac_address': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.MACAddress']"}), | ||
661 | 219 | 'nic_alias': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), | ||
662 | 220 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
663 | 221 | }, | ||
664 | 222 | u'maasserver.network': { | ||
665 | 223 | 'Meta': {'object_name': 'Network'}, | ||
666 | 224 | 'default_gateway': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
667 | 225 | 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), | ||
668 | 226 | 'dns_servers': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), | ||
669 | 227 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
670 | 228 | 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}), | ||
671 | 229 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), | ||
672 | 230 | 'netmask': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}), | ||
673 | 231 | 'vlan_tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'null': 'True', 'blank': 'True'}) | ||
674 | 232 | }, | ||
675 | 233 | u'maasserver.node': { | ||
676 | 234 | 'Meta': {'object_name': 'Node'}, | ||
677 | 235 | 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), | ||
678 | 236 | 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '31'}), | ||
679 | 237 | 'boot_type': ('django.db.models.fields.CharField', [], {'default': "u'fastpath'", 'max_length': '20'}), | ||
680 | 238 | 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | ||
681 | 239 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
682 | 240 | 'disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
683 | 241 | 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), | ||
684 | 242 | 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), | ||
685 | 243 | 'error_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}), | ||
686 | 244 | 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}), | ||
687 | 245 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
688 | 246 | 'license_key': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), | ||
689 | 247 | 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | ||
690 | 248 | 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | ||
691 | 249 | 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}), | ||
692 | 250 | 'osystem': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'blank': 'True'}), | ||
693 | 251 | 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), | ||
694 | 252 | 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), | ||
695 | 253 | 'power_state': ('django.db.models.fields.CharField', [], {'default': "u'unknown'", 'max_length': '10'}), | ||
696 | 254 | 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}), | ||
697 | 255 | 'pxe_mac': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "u'+'", 'null': 'True', 'blank': 'True', 'to': u"orm['maasserver.MACAddress']"}), | ||
698 | 256 | 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}), | ||
699 | 257 | 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}), | ||
700 | 258 | 'storage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | ||
701 | 259 | 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-012616a8-6899-11e4-b353-0026c71eea0e'", 'unique': 'True', 'max_length': '41'}), | ||
702 | 260 | 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}), | ||
703 | 261 | 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}), | ||
704 | 262 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
705 | 263 | 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'}) | ||
706 | 264 | }, | ||
707 | 265 | u'maasserver.nodegroup': { | ||
708 | 266 | 'Meta': {'object_name': 'NodeGroup'}, | ||
709 | 267 | 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}), | ||
710 | 268 | 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}), | ||
711 | 269 | 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}), | ||
712 | 270 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
713 | 271 | 'default_disable_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
714 | 272 | 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), | ||
715 | 273 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
716 | 274 | 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), | ||
717 | 275 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}), | ||
718 | 276 | 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | ||
719 | 277 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
720 | 278 | 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}) | ||
721 | 279 | }, | ||
722 | 280 | u'maasserver.nodegroupinterface': { | ||
723 | 281 | 'Meta': {'unique_together': "((u'nodegroup', u'name'),)", 'object_name': 'NodeGroupInterface'}, | ||
724 | 282 | 'broadcast_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
725 | 283 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
726 | 284 | 'foreign_dhcp_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
727 | 285 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
728 | 286 | 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), | ||
729 | 287 | 'ip': ('maasserver.fields.MAASIPAddressField', [], {'max_length': '39'}), | ||
730 | 288 | 'ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
731 | 289 | 'ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
732 | 290 | 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | ||
733 | 291 | 'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), | ||
734 | 292 | 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), | ||
735 | 293 | 'router_ip': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
736 | 294 | 'static_ip_range_high': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
737 | 295 | 'static_ip_range_low': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
738 | 296 | 'subnet_mask': ('maasserver.fields.MAASIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), | ||
739 | 297 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
740 | 298 | }, | ||
741 | 299 | u'maasserver.sshkey': { | ||
742 | 300 | 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'}, | ||
743 | 301 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
744 | 302 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
745 | 303 | 'key': ('django.db.models.fields.TextField', [], {}), | ||
746 | 304 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
747 | 305 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) | ||
748 | 306 | }, | ||
749 | 307 | u'maasserver.sslkey': { | ||
750 | 308 | 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSLKey'}, | ||
751 | 309 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
752 | 310 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
753 | 311 | 'key': ('django.db.models.fields.TextField', [], {}), | ||
754 | 312 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
755 | 313 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) | ||
756 | 314 | }, | ||
757 | 315 | u'maasserver.staticipaddress': { | ||
758 | 316 | 'Meta': {'object_name': 'StaticIPAddress'}, | ||
759 | 317 | 'alloc_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}), | ||
760 | 318 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
761 | 319 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
762 | 320 | 'ip': ('maasserver.fields.MAASIPAddressField', [], {'unique': 'True', 'max_length': '39'}), | ||
763 | 321 | 'updated': ('django.db.models.fields.DateTimeField', [], {}), | ||
764 | 322 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) | ||
765 | 323 | }, | ||
766 | 324 | u'maasserver.tag': { | ||
767 | 325 | 'Meta': {'object_name': 'Tag'}, | ||
768 | 326 | 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), | ||
769 | 327 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
770 | 328 | 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}), | ||
771 | 329 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
772 | 330 | 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), | ||
773 | 331 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), | ||
774 | 332 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
775 | 333 | }, | ||
776 | 334 | u'maasserver.userprofile': { | ||
777 | 335 | 'Meta': {'object_name': 'UserProfile'}, | ||
778 | 336 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
779 | 337 | 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) | ||
780 | 338 | }, | ||
781 | 339 | u'maasserver.zone': { | ||
782 | 340 | 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'}, | ||
783 | 341 | 'created': ('django.db.models.fields.DateTimeField', [], {}), | ||
784 | 342 | 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), | ||
785 | 343 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
786 | 344 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), | ||
787 | 345 | 'updated': ('django.db.models.fields.DateTimeField', [], {}) | ||
788 | 346 | }, | ||
789 | 347 | u'piston.consumer': { | ||
790 | 348 | 'Meta': {'object_name': 'Consumer'}, | ||
791 | 349 | 'description': ('django.db.models.fields.TextField', [], {}), | ||
792 | 350 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
793 | 351 | 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), | ||
794 | 352 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
795 | 353 | 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), | ||
796 | 354 | 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}), | ||
797 | 355 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"}) | ||
798 | 356 | }, | ||
799 | 357 | u'piston.token': { | ||
800 | 358 | 'Meta': {'object_name': 'Token'}, | ||
801 | 359 | 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), | ||
802 | 360 | 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
803 | 361 | 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}), | ||
804 | 362 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
805 | 363 | 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
806 | 364 | 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), | ||
807 | 365 | 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), | ||
808 | 366 | 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1415596707L'}), | ||
809 | 367 | 'token_type': ('django.db.models.fields.IntegerField', [], {}), | ||
810 | 368 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}), | ||
811 | 369 | 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'}) | ||
812 | 370 | } | ||
813 | 371 | } | ||
814 | 372 | |||
815 | 373 | complete_apps = ['maasserver'] | ||
816 | 0 | 374 | ||
817 | === modified file 'src/maasserver/models/bootsourceselection.py' | |||
818 | --- src/maasserver/models/bootsourceselection.py 2014-08-25 14:08:13 +0000 | |||
819 | +++ src/maasserver/models/bootsourceselection.py 2014-11-12 00:21:52 +0000 | |||
820 | @@ -37,6 +37,7 @@ | |||
821 | 37 | 37 | ||
822 | 38 | class Meta(DefaultMeta): | 38 | class Meta(DefaultMeta): |
823 | 39 | """Needed for South to recognize this model.""" | 39 | """Needed for South to recognize this model.""" |
824 | 40 | unique_together = ("boot_source", "os", "release") | ||
825 | 40 | 41 | ||
826 | 41 | objects = BootSourceSelectionManager() | 42 | objects = BootSourceSelectionManager() |
827 | 42 | 43 | ||
828 | 43 | 44 | ||
829 | === modified file 'src/maasserver/tests/test_forms_bootsourceselection.py' | |||
830 | --- src/maasserver/tests/test_forms_bootsourceselection.py 2014-09-10 16:20:31 +0000 | |||
831 | +++ src/maasserver/tests/test_forms_bootsourceselection.py 2014-11-12 00:21:52 +0000 | |||
832 | @@ -14,6 +14,7 @@ | |||
833 | 14 | __metaclass__ = type | 14 | __metaclass__ = type |
834 | 15 | __all__ = [] | 15 | __all__ = [] |
835 | 16 | 16 | ||
836 | 17 | from django.core.exceptions import ValidationError | ||
837 | 17 | from maasserver.forms import BootSourceSelectionForm | 18 | from maasserver.forms import BootSourceSelectionForm |
838 | 18 | from maasserver.testing.factory import factory | 19 | from maasserver.testing.factory import factory |
839 | 19 | from maasserver.testing.orm import reload_object | 20 | from maasserver.testing.orm import reload_object |
840 | @@ -54,3 +55,27 @@ | |||
841 | 54 | self.assertTrue(form.is_valid(), form._errors) | 55 | self.assertTrue(form.is_valid(), form._errors) |
842 | 55 | boot_source_selection = form.save() | 56 | boot_source_selection = form.save() |
843 | 56 | self.assertAttributes(boot_source_selection, params) | 57 | self.assertAttributes(boot_source_selection, params) |
844 | 58 | |||
845 | 59 | def test_cannot_create_duplicate_entry(self): | ||
846 | 60 | boot_source = factory.make_BootSource() | ||
847 | 61 | params = { | ||
848 | 62 | 'os': factory.make_name('os'), | ||
849 | 63 | 'release': factory.make_name('release'), | ||
850 | 64 | 'arches': [factory.make_name('arch'), factory.make_name('arch')], | ||
851 | 65 | 'subarches': [ | ||
852 | 66 | factory.make_name('subarch'), factory.make_name('subarch')], | ||
853 | 67 | 'labels': [factory.make_name('label'), factory.make_name('label')], | ||
854 | 68 | } | ||
855 | 69 | form = BootSourceSelectionForm(boot_source=boot_source, data=params) | ||
856 | 70 | self.assertTrue(form.is_valid(), form._errors) | ||
857 | 71 | form.save() | ||
858 | 72 | |||
859 | 73 | # Duplicates should be detected for the same boot_source, os and | ||
860 | 74 | # release, the other fields are irrelevant. | ||
861 | 75 | dup_params = { | ||
862 | 76 | 'os': params['os'], | ||
863 | 77 | 'release': params['release'], | ||
864 | 78 | } | ||
865 | 79 | form = BootSourceSelectionForm( | ||
866 | 80 | boot_source=boot_source, data=dup_params) | ||
867 | 81 | self.assertRaises(ValidationError, form.save) |
ISTR Blake saying that we had to coalesce the entries for one OS/release rather than just dropping all-but-one, because the entries may all have different arches, subarches and labels — all of which are perfectly valid choices, but which need to be recorded in a single row, not several.
So, if we don't want to have settings go away surprisingly, (1) is a good option.
That said, could we add a persistent, user-clearable error* after doing (2) that says something like "MAAS's records of your boot source settings may have changed; check the Images tab to ensure that your boot image settings are correct."
*Not sure we actually have the facility to do this; I was thinking of the persistent error stuff, but that all seems to be auto-cleared or not at all.