Merge lp:~rvb/maas/ui-update-fqdn-1.2 into lp:maas/1.2

Proposed by Raphaël Badin on 2012-11-16
Status: Merged
Approved by: Raphaël Badin on 2012-11-16
Approved revision: 1304
Merged at revision: 1303
Proposed branch: lp:~rvb/maas/ui-update-fqdn-1.2
Merge into: lp:maas/1.2
Diff against target: 881 lines (+505/-58)
18 files modified
src/maasserver/forms.py (+9/-0)
src/maasserver/migrations/0043_unique_hostname_preparation.py (+218/-0)
src/maasserver/migrations/0044_node_hostname_unique.py (+201/-0)
src/maasserver/models/node.py (+2/-1)
src/maasserver/templates/maasserver/node_edit.html (+2/-0)
src/maasserver/templates/maasserver/node_view.html (+4/-4)
src/maasserver/templates/maasserver/nodes_listing.html (+9/-6)
src/maasserver/testing/factory.py (+3/-3)
src/maasserver/tests/test_api.py (+15/-7)
src/maasserver/tests/test_dhcplease.py (+3/-3)
src/maasserver/tests/test_dns.py (+1/-1)
src/maasserver/tests/test_forms.py (+0/-12)
src/maasserver/tests/test_node.py (+2/-3)
src/maasserver/tests/test_node_constraint_filter.py (+2/-2)
src/maasserver/tests/test_views_nodes.py (+18/-1)
src/maasserver/tests/test_views_tags.py (+10/-14)
src/maasserver/views/nodes.py (+2/-0)
src/maasserver/views/tags.py (+4/-1)
To merge this branch: bzr merge lp:~rvb/maas/ui-update-fqdn-1.2
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve on 2012-11-16
Review via email: mp+134663@code.launchpad.net

Commit message

Backport of 1335 (and required db migration merged in revision 1330).

Description of the change

Backport of 1335 (and required db migration merged in revision 1330).

To post a comment you must log in.
Raphaël Badin (rvb) wrote :

This is a backport of 1335 (and the required db migration merged in revision 1330). Self-approving.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/forms.py'
2--- src/maasserver/forms.py 2012-11-16 07:43:42 +0000
3+++ src/maasserver/forms.py 2012-11-16 13:58:29 +0000
4@@ -152,6 +152,15 @@
5 initial=ARCHITECTURE.i386,
6 error_messages={'invalid_choice': INVALID_ARCHITECTURE_MESSAGE})
7
8+ hostname = forms.CharField(
9+ label="Host name", required=False, help_text=(
10+ "The FQDN (Fully Qualified Domain Name) is derived from the "
11+ "host name: If the cluster controller for this node is managing "
12+ "DNS then the domain part in the host name (if any) is replaced "
13+ "by the domain defined on the cluster; if the cluster controller "
14+ "does not manage DNS, then the host name as entered will be the "
15+ "FQDN."))
16+
17 class Meta:
18 model = Node
19
20
21=== added file 'src/maasserver/migrations/0043_unique_hostname_preparation.py'
22--- src/maasserver/migrations/0043_unique_hostname_preparation.py 1970-01-01 00:00:00 +0000
23+++ src/maasserver/migrations/0043_unique_hostname_preparation.py 2012-11-16 13:58:29 +0000
24@@ -0,0 +1,218 @@
25+# -*- coding: utf-8 -*-
26+import datetime
27+
28+from django.db import models
29+from django.db.models import Count
30+from south.db import db
31+from south.v2 import DataMigration
32+
33+
34+class Migration(DataMigration):
35+
36+ def forwards(self, orm):
37+ # Find the nodes with duplicated hostnames.
38+ duplicated_hostnames = orm['maasserver.node'].objects.values_list(
39+ 'hostname', flat=True).annotate(
40+ hostname_count=Count('hostname')).exclude(hostname_count=1)
41+ # Rename the nodes with duplicated hostnames.
42+ for duplicated_hostname in duplicated_hostnames:
43+ nodes_with_duplicated_hostnames = (
44+ orm['maasserver.node'].objects.filter(
45+ hostname=duplicated_hostname))
46+ # Rename all the nodes but one.
47+ for node in nodes_with_duplicated_hostnames[1:]:
48+ number = 1
49+ while True:
50+ new_hostname = '%s-%d' % (node.hostname, number)
51+ if not orm['maasserver.node'].objects.filter(hostname=new_hostname).exists():
52+ node.hostname = new_hostname
53+ node.save()
54+ break
55+ number += 1
56+
57+
58+ def backwards(self, orm):
59+ pass
60+
61+ models = {
62+ 'auth.group': {
63+ 'Meta': {'object_name': 'Group'},
64+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
65+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
66+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
67+ },
68+ 'auth.permission': {
69+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
70+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
71+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
72+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
73+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
74+ },
75+ 'auth.user': {
76+ 'Meta': {'object_name': 'User'},
77+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
78+ 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
79+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
80+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
81+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
82+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
83+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
84+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
85+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
86+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
87+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
88+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
89+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
90+ },
91+ 'contenttypes.contenttype': {
92+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
93+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
94+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
95+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
96+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
97+ },
98+ u'maasserver.bootimage': {
99+ 'Meta': {'unique_together': "((u'nodegroup', u'architecture', u'subarchitecture', u'release', u'purpose'),)", 'object_name': 'BootImage'},
100+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
101+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
102+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
103+ 'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
104+ 'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
105+ 'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'})
106+ },
107+ u'maasserver.componenterror': {
108+ 'Meta': {'object_name': 'ComponentError'},
109+ 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
110+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
111+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
112+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
113+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
114+ },
115+ u'maasserver.config': {
116+ 'Meta': {'object_name': 'Config'},
117+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
118+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
119+ 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
120+ },
121+ u'maasserver.dhcplease': {
122+ 'Meta': {'object_name': 'DHCPLease'},
123+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
124+ 'ip': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15'}),
125+ 'mac': ('maasserver.fields.MACAddressField', [], {}),
126+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"})
127+ },
128+ u'maasserver.filestorage': {
129+ 'Meta': {'object_name': 'FileStorage'},
130+ 'content': ('metadataserver.fields.BinaryField', [], {}),
131+ 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
132+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
133+ },
134+ u'maasserver.macaddress': {
135+ 'Meta': {'object_name': 'MACAddress'},
136+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
137+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
138+ 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}),
139+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
140+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
141+ },
142+ u'maasserver.node': {
143+ 'Meta': {'object_name': 'Node'},
144+ 'after_commissioning_action': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
145+ 'architecture': ('django.db.models.fields.CharField', [], {'default': "u'i386/generic'", 'max_length': '31'}),
146+ 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
147+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
148+ 'distro_series': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '10', 'null': 'True', 'blank': 'True'}),
149+ 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
150+ 'hardware_details': ('maasserver.fields.XMLField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
151+ 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
152+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
153+ 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
154+ 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
155+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
156+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
157+ 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
158+ 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
159+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
160+ 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-6b5652fe-2412-11e2-9eff-9c4e363b1c94'", 'unique': 'True', 'max_length': '41'}),
161+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}),
162+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['piston.Token']", 'null': 'True'}),
163+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
164+ },
165+ u'maasserver.nodegroup': {
166+ 'Meta': {'object_name': 'NodeGroup'},
167+ 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}),
168+ 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['piston.Token']", 'unique': 'True'}),
169+ 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}),
170+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
171+ 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
172+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
173+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
174+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
175+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
176+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
177+ },
178+ u'maasserver.nodegroupinterface': {
179+ 'Meta': {'unique_together': "((u'nodegroup', u'interface'),)", 'object_name': 'NodeGroupInterface'},
180+ 'broadcast_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
181+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
182+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
183+ 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
184+ 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
185+ 'ip_range_high': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
186+ 'ip_range_low': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
187+ 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
188+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
189+ 'router_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
190+ 'subnet_mask': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
191+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
192+ },
193+ u'maasserver.sshkey': {
194+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'},
195+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
196+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
197+ 'key': ('django.db.models.fields.TextField', [], {}),
198+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
199+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
200+ },
201+ u'maasserver.tag': {
202+ 'Meta': {'object_name': 'Tag'},
203+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
204+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
205+ 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
206+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
207+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
208+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
209+ },
210+ u'maasserver.userprofile': {
211+ 'Meta': {'object_name': 'UserProfile'},
212+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
213+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
214+ },
215+ 'piston.consumer': {
216+ 'Meta': {'object_name': 'Consumer'},
217+ 'description': ('django.db.models.fields.TextField', [], {}),
218+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
219+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
220+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
221+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
222+ 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
223+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': "orm['auth.User']"})
224+ },
225+ 'piston.token': {
226+ 'Meta': {'object_name': 'Token'},
227+ 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
228+ 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
229+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['piston.Consumer']"}),
230+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
231+ 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
232+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
233+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
234+ 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1351767230L'}),
235+ 'token_type': ('django.db.models.fields.IntegerField', [], {}),
236+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': "orm['auth.User']"}),
237+ 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
238+ }
239+ }
240+
241+ complete_apps = ['maasserver']
242+ symmetrical = True
243
244=== added file 'src/maasserver/migrations/0044_node_hostname_unique.py'
245--- src/maasserver/migrations/0044_node_hostname_unique.py 1970-01-01 00:00:00 +0000
246+++ src/maasserver/migrations/0044_node_hostname_unique.py 2012-11-16 13:58:29 +0000
247@@ -0,0 +1,201 @@
248+# -*- coding: utf-8 -*-
249+import datetime
250+
251+from django.db import models
252+from south.db import db
253+from south.v2 import SchemaMigration
254+
255+
256+class Migration(SchemaMigration):
257+
258+ def forwards(self, orm):
259+ # Adding unique constraint on 'Node', fields ['hostname']
260+ db.create_unique(u'maasserver_node', ['hostname'])
261+
262+
263+ def backwards(self, orm):
264+ # Removing unique constraint on 'Node', fields ['hostname']
265+ db.delete_unique(u'maasserver_node', ['hostname'])
266+
267+
268+ models = {
269+ 'auth.group': {
270+ 'Meta': {'object_name': 'Group'},
271+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
272+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
273+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
274+ },
275+ 'auth.permission': {
276+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
277+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
278+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
279+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
280+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
281+ },
282+ 'auth.user': {
283+ 'Meta': {'object_name': 'User'},
284+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
285+ 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
286+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
287+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
288+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
289+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
290+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
291+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
292+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
293+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
294+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
295+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
296+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
297+ },
298+ 'contenttypes.contenttype': {
299+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
300+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
301+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
302+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
303+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
304+ },
305+ u'maasserver.bootimage': {
306+ 'Meta': {'unique_together': "((u'nodegroup', u'architecture', u'subarchitecture', u'release', u'purpose'),)", 'object_name': 'BootImage'},
307+ 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
308+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
309+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
310+ 'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
311+ 'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
312+ 'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'})
313+ },
314+ u'maasserver.componenterror': {
315+ 'Meta': {'object_name': 'ComponentError'},
316+ 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
317+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
318+ 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
319+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
320+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
321+ },
322+ u'maasserver.config': {
323+ 'Meta': {'object_name': 'Config'},
324+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
325+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
326+ 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
327+ },
328+ u'maasserver.dhcplease': {
329+ 'Meta': {'object_name': 'DHCPLease'},
330+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
331+ 'ip': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15'}),
332+ 'mac': ('maasserver.fields.MACAddressField', [], {}),
333+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"})
334+ },
335+ u'maasserver.filestorage': {
336+ 'Meta': {'object_name': 'FileStorage'},
337+ 'content': ('metadataserver.fields.BinaryField', [], {}),
338+ 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
339+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
340+ },
341+ u'maasserver.macaddress': {
342+ 'Meta': {'object_name': 'MACAddress'},
343+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
344+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
345+ 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}),
346+ 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
347+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
348+ },
349+ u'maasserver.node': {
350+ 'Meta': {'object_name': 'Node'},
351+ 'after_commissioning_action': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
352+ 'architecture': ('django.db.models.fields.CharField', [], {'default': "u'i386/generic'", 'max_length': '31'}),
353+ 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
354+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
355+ 'distro_series': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '10', 'null': 'True', 'blank': 'True'}),
356+ 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
357+ 'hardware_details': ('maasserver.fields.XMLField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
358+ 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}),
359+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
360+ 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
361+ 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
362+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
363+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
364+ 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
365+ 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
366+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
367+ 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-763aa1fc-2412-11e2-be98-9c4e363b1c94'", 'unique': 'True', 'max_length': '41'}),
368+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}),
369+ 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['piston.Token']", 'null': 'True'}),
370+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
371+ },
372+ u'maasserver.nodegroup': {
373+ 'Meta': {'object_name': 'NodeGroup'},
374+ 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}),
375+ 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['piston.Token']", 'unique': 'True'}),
376+ 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}),
377+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
378+ 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
379+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
380+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
381+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
382+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
383+ 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
384+ },
385+ u'maasserver.nodegroupinterface': {
386+ 'Meta': {'unique_together': "((u'nodegroup', u'interface'),)", 'object_name': 'NodeGroupInterface'},
387+ 'broadcast_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
388+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
389+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
390+ 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
391+ 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
392+ 'ip_range_high': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
393+ 'ip_range_low': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
394+ 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
395+ 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
396+ 'router_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
397+ 'subnet_mask': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}),
398+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
399+ },
400+ u'maasserver.sshkey': {
401+ 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'},
402+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
403+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
404+ 'key': ('django.db.models.fields.TextField', [], {}),
405+ 'updated': ('django.db.models.fields.DateTimeField', [], {}),
406+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
407+ },
408+ u'maasserver.tag': {
409+ 'Meta': {'object_name': 'Tag'},
410+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
411+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
412+ 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
413+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
414+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
415+ 'updated': ('django.db.models.fields.DateTimeField', [], {})
416+ },
417+ u'maasserver.userprofile': {
418+ 'Meta': {'object_name': 'UserProfile'},
419+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
420+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
421+ },
422+ 'piston.consumer': {
423+ 'Meta': {'object_name': 'Consumer'},
424+ 'description': ('django.db.models.fields.TextField', [], {}),
425+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
426+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
427+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
428+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
429+ 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
430+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': "orm['auth.User']"})
431+ },
432+ 'piston.token': {
433+ 'Meta': {'object_name': 'Token'},
434+ 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
435+ 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
436+ 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['piston.Consumer']"}),
437+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
438+ 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
439+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
440+ 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
441+ 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1351767248L'}),
442+ 'token_type': ('django.db.models.fields.IntegerField', [], {}),
443+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': "orm['auth.User']"}),
444+ 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
445+ }
446+ }
447+
448+ complete_apps = ['maasserver']
449\ No newline at end of file
450
451=== modified file 'src/maasserver/models/node.py'
452--- src/maasserver/models/node.py 2012-11-16 07:43:42 +0000
453+++ src/maasserver/models/node.py 2012-11-16 13:58:29 +0000
454@@ -428,6 +428,7 @@
455 :ivar power_type: The :class:`POWER_TYPE` that determines how this
456 node will be powered on. If not given, the default will be used as
457 configured in the `node_power_type` setting.
458+ :ivar nodegroup: The `NodeGroup` this `Node` belongs to.
459 :ivar tags: The list of :class:`Tag`s associated with this `Node`.
460 :ivar objects: The :class:`NodeManager`.
461
462@@ -440,7 +441,7 @@
463 max_length=41, unique=True, default=generate_node_system_id,
464 editable=False)
465
466- hostname = CharField(max_length=255, default='', blank=True)
467+ hostname = CharField(max_length=255, default='', blank=True, unique=True)
468
469 status = IntegerField(
470 max_length=10, choices=NODE_STATUS_CHOICES, editable=False,
471
472=== modified file 'src/maasserver/templates/maasserver/node_edit.html'
473--- src/maasserver/templates/maasserver/node_edit.html 2012-07-10 16:03:27 +0000
474+++ src/maasserver/templates/maasserver/node_edit.html 2012-11-16 13:58:29 +0000
475@@ -30,6 +30,7 @@
476 {% endblock %}
477
478 {% block content %}
479+ <div id="node-edit" class="block size7">
480 <form action="." method="post" class="block auto-width">
481 {% csrf_token %}
482 <ul>
483@@ -62,4 +63,5 @@
484 <input type="submit" value="Save node" class="right" />
485 <a class="link-button" href="{% url 'node-view' node.system_id %}">Cancel</a>
486 </form>
487+ </div>
488 {% endblock %}
489
490=== modified file 'src/maasserver/templates/maasserver/node_view.html'
491--- src/maasserver/templates/maasserver/node_view.html 2012-10-05 17:42:12 +0000
492+++ src/maasserver/templates/maasserver/node_view.html 2012-11-16 13:58:29 +0000
493@@ -1,8 +1,8 @@
494 {% extends "maasserver/base.html" %}
495
496 {% block nav-active-settings %}active{% endblock %}
497-{% block title %}Node: {{ node.hostname }}{% endblock %}
498-{% block page-title %}Node: {{ node.hostname }}{% endblock %}
499+{% block title %}Node: {{ node.fqdn }}{% endblock %}
500+{% block page-title %}Node: {{ node.fqdn }}{% endblock %}
501 {% block layout-modifiers %}sidebar{% endblock %}
502
503 {% block sidebar %}
504@@ -39,8 +39,8 @@
505 {% block content %}
506 <ul class="data-list">
507 <li class="block size3 first">
508- <h4>Hostname</h4>
509- <span>{{ node.hostname }}</span>
510+ <h4><acronym title="Fully Qualified Domain Name">FQDN</acronym></h4>
511+ <span>{{ node.fqdn }}</span>
512 </li>
513 <li class="block size3">
514 <h4>MAC addresses</h4>
515
516=== modified file 'src/maasserver/templates/maasserver/nodes_listing.html'
517--- src/maasserver/templates/maasserver/nodes_listing.html 2012-10-10 08:13:01 +0000
518+++ src/maasserver/templates/maasserver/nodes_listing.html 2012-11-16 13:58:29 +0000
519@@ -2,19 +2,22 @@
520 <table class="list">
521 <thead>
522 <tr>
523- <th>MAC</th>
524- <th>Status</th>
525+ <th><acronym title="Fully Qualified Domain Name">FQDN</acronym></th>
526+ <th><acronym
527+ title="Media Access Control addresses">MAC</acronym></th>
528 </tr>
529 </thead>
530 {% for node in node_list %}
531 <tr class="node {% cycle 'even' 'odd' %}">
532 <td>
533 <a href="{% url 'node-view' node.system_id %}">
534- {% for macaddress in node.macaddress_set.all reversed %}
535- {{ macaddress }}{% if not forloop.last %},{% endif %}
536- {% endfor %}
537+ {{ node.fqdn }}
538 </a>
539- ({{ node.hostname }})
540+ </td>
541+ <td>
542+ {% for macaddress in node.macaddress_set.all reversed %}
543+ {{ macaddress }}{% if not forloop.last %},{% endif %}
544+ {% endfor %}
545 </td>
546 <td>{{ node.display_status }}</td>
547 </tr>
548
549=== modified file 'src/maasserver/testing/factory.py'
550--- src/maasserver/testing/factory.py 2012-11-08 09:14:58 +0000
551+++ src/maasserver/testing/factory.py 2012-11-16 13:58:29 +0000
552@@ -96,11 +96,11 @@
553 finally:
554 NODE_TRANSITIONS[None] = valid_initial_states
555
556- def make_node(self, mac=False, hostname='', set_hostname=False,
557- status=None, architecture=ARCHITECTURE.i386, updated=None,
558+ def make_node(self, mac=False, hostname=None, status=None,
559+ architecture=ARCHITECTURE.i386, updated=None,
560 created=None, nodegroup=None, **kwargs):
561 # hostname=None is a valid value, hence the set_hostname trick.
562- if hostname is '' and set_hostname:
563+ if hostname is None:
564 hostname = self.getRandomString(20)
565 if status is None:
566 status = NODE_STATUS.DEFAULT_STATUS
567
568=== modified file 'src/maasserver/tests/test_api.py'
569--- src/maasserver/tests/test_api.py 2012-11-16 07:43:42 +0000
570+++ src/maasserver/tests/test_api.py 2012-11-16 13:58:29 +0000
571@@ -1170,7 +1170,7 @@
572
573 def test_GET_returns_node(self):
574 # The api allows for fetching a single Node (using system_id).
575- node = factory.make_node(set_hostname=True)
576+ node = factory.make_node()
577 response = self.client.get(self.get_node_uri(node))
578
579 self.assertEqual(httplib.OK, response.status_code)
580@@ -1179,7 +1179,7 @@
581 self.assertEqual(node.system_id, parsed_result['system_id'])
582
583 def test_GET_returns_associated_tag(self):
584- node = factory.make_node(set_hostname=True)
585+ node = factory.make_node()
586 tag = factory.make_tag()
587 node.tags.add(tag)
588 response = self.client.get(self.get_node_uri(node))
589@@ -1424,6 +1424,15 @@
590 self.assertEqual(0, Node.objects.filter(hostname='diane').count())
591 self.assertEqual(1, Node.objects.filter(hostname='francis').count())
592
593+ def test_PUT_omitted_hostname(self):
594+ hostname = factory.make_name('hostname')
595+ node = factory.make_node(hostname=hostname, owner=self.logged_in_user)
596+ response = self.client.put(
597+ self.get_node_uri(node),
598+ {'architecture': factory.getRandomChoice(ARCHITECTURE_CHOICES)})
599+ self.assertEqual(httplib.OK, response.status_code, response.content)
600+ self.assertTrue(Node.objects.filter(hostname=hostname).exists())
601+
602 def test_PUT_ignores_unknown_fields(self):
603 node = factory.make_node(
604 owner=self.logged_in_user,
605@@ -1677,7 +1686,7 @@
606 def test_DELETE_deletes_node(self):
607 # The api allows to delete a Node.
608 self.become_admin()
609- node = factory.make_node(set_hostname=True, owner=self.logged_in_user)
610+ node = factory.make_node(owner=self.logged_in_user)
611 system_id = node.system_id
612 response = self.client.delete(self.get_node_uri(node))
613
614@@ -1699,14 +1708,14 @@
615
616 def test_DELETE_deletes_node_fails_if_not_admin(self):
617 # Only superusers can delete nodes.
618- node = factory.make_node(set_hostname=True, owner=self.logged_in_user)
619+ node = factory.make_node(owner=self.logged_in_user)
620 response = self.client.delete(self.get_node_uri(node))
621
622 self.assertEqual(httplib.FORBIDDEN, response.status_code)
623
624 def test_DELETE_forbidden_without_edit_permission(self):
625 # A user without the edit permission cannot delete a Node.
626- node = factory.make_node(set_hostname=True)
627+ node = factory.make_node()
628 response = self.client.delete(self.get_node_uri(node))
629
630 self.assertEqual(httplib.FORBIDDEN, response.status_code)
631@@ -1771,8 +1780,7 @@
632 # The api allows for fetching the list of Nodes.
633 node1 = factory.make_node()
634 node2 = factory.make_node(
635- set_hostname=True, status=NODE_STATUS.ALLOCATED,
636- owner=self.logged_in_user)
637+ status=NODE_STATUS.ALLOCATED, owner=self.logged_in_user)
638 response = self.client.get(self.get_uri('nodes/'), {'op': 'list'})
639 parsed_result = json.loads(response.content)
640
641
642=== modified file 'src/maasserver/tests/test_dhcplease.py'
643--- src/maasserver/tests/test_dhcplease.py 2012-10-30 15:15:30 +0000
644+++ src/maasserver/tests/test_dhcplease.py 2012-11-16 13:58:29 +0000
645@@ -168,7 +168,7 @@
646 expected_mapping = {}
647 for i in range(3):
648 node = factory.make_node(
649- nodegroup=nodegroup, set_hostname=True)
650+ nodegroup=nodegroup)
651 mac = factory.make_mac_address(node=node)
652 factory.make_mac_address(node=node)
653 lease = factory.make_dhcp_lease(
654@@ -193,7 +193,7 @@
655 def test_get_hostname_ip_mapping_considers_only_first_mac(self):
656 nodegroup = factory.make_node_group()
657 node = factory.make_node(
658- nodegroup=nodegroup, set_hostname=True)
659+ nodegroup=nodegroup)
660 factory.make_mac_address(node=node)
661 second_mac = factory.make_mac_address(node=node)
662 # Create a lease for the second MAC Address.
663@@ -205,7 +205,7 @@
664 def test_get_hostname_ip_mapping_considers_given_nodegroup(self):
665 nodegroup = factory.make_node_group()
666 node = factory.make_node(
667- nodegroup=nodegroup, set_hostname=True)
668+ nodegroup=nodegroup)
669 mac = factory.make_mac_address(node=node)
670 factory.make_dhcp_lease(
671 nodegroup=nodegroup, mac=mac.mac_address)
672
673=== modified file 'src/maasserver/tests/test_dns.py'
674--- src/maasserver/tests/test_dns.py 2012-10-11 01:17:24 +0000
675+++ src/maasserver/tests/test_dns.py 2012-11-16 13:58:29 +0000
676@@ -157,7 +157,7 @@
677 management=NODEGROUPINTERFACE_MANAGEMENT.DHCP_AND_DNS)
678 interface = nodegroup.get_managed_interface()
679 node = factory.make_node(
680- nodegroup=nodegroup, set_hostname=True)
681+ nodegroup=nodegroup)
682 mac = factory.make_mac_address(node=node)
683 ips = IPRange(interface.ip_range_low, interface.ip_range_high)
684 lease_ip = str(islice(ips, lease_number, lease_number + 1).next())
685
686=== modified file 'src/maasserver/tests/test_forms.py'
687--- src/maasserver/tests/test_forms.py 2012-11-09 18:12:36 +0000
688+++ src/maasserver/tests/test_forms.py 2012-11-16 13:58:29 +0000
689@@ -381,18 +381,6 @@
690 form.save()
691 self.assertEqual(old_name, reload_object(node).hostname)
692
693- def test_AdminNodeForm_accepts_omitted_hostname_on_allocated_node(self):
694- node = factory.make_node(status=NODE_STATUS.ALLOCATED)
695- old_name = node.hostname
696- form = AdminNodeForm(
697- data={
698- 'architecture': node.architecture,
699- },
700- instance=node)
701- self.assertTrue(form.is_valid())
702- form.save()
703- self.assertEqual(old_name, reload_object(node).hostname)
704-
705 def test_remove_None_values_removes_None_values_in_dict(self):
706 random_input = factory.getRandomString()
707 self.assertEqual(
708
709=== modified file 'src/maasserver/tests/test_node.py'
710--- src/maasserver/tests/test_node.py 2012-11-16 07:43:42 +0000
711+++ src/maasserver/tests/test_node.py 2012-11-16 13:58:29 +0000
712@@ -313,7 +313,7 @@
713 node: node.get_effective_power_type()
714 for node in nodes}
715 started_nodes = Node.objects.start_nodes(
716- list(node_power_types.keys()), user)
717+ [node.system_id for node in list(node_power_types.keys())], user)
718 successful_types = [node_power_types[node] for node in started_nodes]
719 self.assertItemsEqual(configless_power_types, successful_types)
720
721@@ -636,8 +636,7 @@
722 status = NODE_STATUS.READY
723 else:
724 status = NODE_STATUS.ALLOCATED
725- return factory.make_node(
726- set_hostname=True, status=status, owner=user, **kwargs)
727+ return factory.make_node(status=status, owner=user, **kwargs)
728
729 def make_node_with_mac(self, user=None, **kwargs):
730 node = self.make_node(user, **kwargs)
731
732=== modified file 'src/maasserver/tests/test_node_constraint_filter.py'
733--- src/maasserver/tests/test_node_constraint_filter.py 2012-10-11 06:59:33 +0000
734+++ src/maasserver/tests/test_node_constraint_filter.py 2012-11-16 13:58:29 +0000
735@@ -60,8 +60,8 @@
736 self.assertConstrainedNodes([node1, node2], {})
737
738 def test_hostname(self):
739- node1 = factory.make_node(set_hostname=True)
740- node2 = factory.make_node(set_hostname=True)
741+ node1 = factory.make_node()
742+ node2 = factory.make_node()
743 self.assertConstrainedNodes([node1], {'hostname': node1.hostname})
744 self.assertConstrainedNodes([node2], {'hostname': node2.hostname})
745 self.assertConstrainedNodes([], {'hostname': 'unknown-name'})
746
747=== modified file 'src/maasserver/tests/test_views_nodes.py'
748--- src/maasserver/tests/test_views_nodes.py 2012-10-17 05:20:13 +0000
749+++ src/maasserver/tests/test_views_nodes.py 2012-11-16 13:58:29 +0000
750@@ -25,6 +25,8 @@
751 ARCHITECTURE_CHOICES,
752 NODE_AFTER_COMMISSIONING_ACTION,
753 NODE_STATUS,
754+ NODEGROUP_STATUS,
755+ NODEGROUPINTERFACE_MANAGEMENT,
756 )
757 from maasserver.exceptions import (
758 InvalidConstraint,
759@@ -79,6 +81,21 @@
760 enlist_preseed_link = reverse('enlist-preseed-view')
761 self.assertIn(enlist_preseed_link, get_content_links(response))
762
763+ def test_node_list_displays_fqdn_dns_not_managed(self):
764+ nodes = [factory.make_node() for i in range(3)]
765+ response = self.client.get(reverse('node-list'))
766+ node_fqdns = [node.fqdn for node in nodes]
767+ self.assertThat(response.content, ContainsAll(node_fqdns))
768+
769+ def test_node_list_displays_fqdn_dns_managed(self):
770+ nodegroup = factory.make_node_group(
771+ status=NODEGROUP_STATUS.ACCEPTED,
772+ management=NODEGROUPINTERFACE_MANAGEMENT.DHCP_AND_DNS)
773+ nodes = [factory.make_node(nodegroup=nodegroup) for i in range(3)]
774+ response = self.client.get(reverse('node-list'))
775+ node_fqdns = [node.fqdn for node in nodes]
776+ self.assertThat(response.content, ContainsAll(node_fqdns))
777+
778 def test_node_list_displays_sorted_list_of_nodes(self):
779 # Nodes are sorted on the node list page, newest first.
780 nodes = [factory.make_node() for i in range(3)]
781@@ -493,7 +510,7 @@
782 def test_preseedview_node_displays_message_if_commissioning(self):
783 node = factory.make_node(
784 owner=self.logged_in_user, status=NODE_STATUS.COMMISSIONING,
785- set_hostname=True)
786+ )
787 node_preseed_link = reverse('node-preseed-view', args=[node.system_id])
788 response = self.client.get(node_preseed_link)
789 self.assertThat(
790
791=== modified file 'src/maasserver/tests/test_views_tags.py'
792--- src/maasserver/tests/test_views_tags.py 2012-11-07 11:32:16 +0000
793+++ src/maasserver/tests/test_views_tags.py 2012-11-16 13:58:29 +0000
794@@ -15,15 +15,11 @@
795 from django.core.urlresolvers import reverse
796 from lxml.etree import XPath
797 from lxml.html import fromstring
798-from maastesting.matchers import ContainsAll
799-from maasserver.testing import (
800- get_content_links,
801- )
802+from maasserver.testing import get_content_links
803 from maasserver.testing.factory import factory
804-from maasserver.testing.testcase import (
805- LoggedInTestCase,
806- )
807+from maasserver.testing.testcase import LoggedInTestCase
808 from maasserver.views import tags as tags_views
809+from maastesting.matchers import ContainsAll
810
811
812 class TagViewsTest(LoggedInTestCase):
813@@ -37,12 +33,12 @@
814 response = self.client.get(tag_link)
815 doc = fromstring(response.content)
816 content_text = doc.cssselect('#content')[0].text_content()
817- self.assertThat(content_text,
818- ContainsAll([tag.comment, tag.definition]))
819+ self.assertThat(
820+ content_text, ContainsAll([tag.comment, tag.definition]))
821
822 def test_view_tag_includes_node_links(self):
823 tag = factory.make_tag()
824- node = factory.make_node(set_hostname=True)
825+ node = factory.make_node()
826 node.tags.add(tag)
827 mac = factory.make_mac_address(node=node).mac_address
828 tag_link = reverse('tag-view', args=[tag.name])
829@@ -50,8 +46,8 @@
830 response = self.client.get(tag_link)
831 doc = fromstring(response.content)
832 content_text = doc.cssselect('#content')[0].text_content()
833- self.assertThat(content_text,
834- ContainsAll([mac, '(%s)' % node.hostname]))
835+ self.assertThat(
836+ content_text, ContainsAll([mac, '%s' % node.hostname]))
837 self.assertNotIn(node.system_id, content_text)
838 self.assertIn(node_link, get_content_links(response))
839
840@@ -82,8 +78,8 @@
841
842 def test_view_tag_hides_private_nodes(self):
843 tag = factory.make_tag()
844- node = factory.make_node(set_hostname=True)
845- node2 = factory.make_node(owner=factory.make_user(), set_hostname=True)
846+ node = factory.make_node()
847+ node2 = factory.make_node(owner=factory.make_user())
848 node.tags.add(tag)
849 node2.tags.add(tag)
850 tag_link = reverse('tag-view', args=[tag.name])
851
852=== modified file 'src/maasserver/views/nodes.py'
853--- src/maasserver/views/nodes.py 2012-10-17 05:20:13 +0000
854+++ src/maasserver/views/nodes.py 2012-11-16 13:58:29 +0000
855@@ -123,6 +123,8 @@
856 except InvalidConstraint as e:
857 self.query_error = e
858 return Node.objects.none()
859+ nodes = nodes.prefetch_related('nodegroup')
860+ nodes = nodes.prefetch_related('nodegroup__nodegroupinterface_set')
861 return nodes
862
863 def get_context_data(self, **kwargs):
864
865=== modified file 'src/maasserver/views/tags.py'
866--- src/maasserver/views/tags.py 2012-11-07 11:32:16 +0000
867+++ src/maasserver/views/tags.py 2012-11-16 13:58:29 +0000
868@@ -33,9 +33,12 @@
869 return super(TagView, self).get(request, *args, **kwargs)
870
871 def get_queryset(self):
872- return Tag.objects.get_nodes(
873+ nodes = Tag.objects.get_nodes(
874 self.tag, user=self.request.user, prefetch_mac=True,
875 ).order_by('-created')
876+ nodes = nodes.prefetch_related('nodegroup')
877+ nodes = nodes.prefetch_related('nodegroup__nodegroupinterface_set')
878+ return nodes
879
880 def get_context_data(self, **kwargs):
881 context = super(TagView, self).get_context_data(**kwargs)

Subscribers

People subscribed via source and target branches

to status/vote changes: