Merge lp:~blake-rouse/maas/osystem-preseed-cleanup into lp:~maas-committers/maas/trunk
- osystem-preseed-cleanup
- Merge into trunk
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~blake-rouse/maas/osystem-preseed-cleanup | ||||
Merge into: | lp:~maas-committers/maas/trunk | ||||
Diff against target: |
1796 lines (+141/-1132) 22 files modified
src/maasserver/api.py (+0/-43) src/maasserver/compose_preseed.py (+8/-0) src/maasserver/enum.py (+0/-33) src/maasserver/forms.py (+0/-122) src/maasserver/forms_settings.py (+0/-28) src/maasserver/migrations/0077_add_osystem_to_node.py (+0/-264) src/maasserver/models/bootsource.py (+8/-0) src/maasserver/models/bootsourceselection.py (+10/-6) src/maasserver/models/node.py (+0/-56) src/maasserver/models/tests/test_node.py (+0/-36) src/maasserver/static/js/node_add.js (+0/-19) src/maasserver/static/js/os_distro_select.js (+0/-131) src/maasserver/static/js/tests/test_os_distro_select.html (+0/-38) src/maasserver/static/js/tests/test_os_distro_select.js (+0/-106) src/maasserver/testing/factory.py (+3/-2) src/maasserver/tests/test_api_boot_source_selections.py (+7/-7) src/maasserver/tests/test_api_node.py (+12/-87) src/maasserver/tests/test_api_pxeconfig.py (+0/-10) src/maasserver/tests/test_compose_preseed.py (+15/-0) src/maasserver/tests/test_forms.py (+66/-140) src/maasserver/views/tests/test_settings.py (+0/-4) src/provisioningserver/driver/__init__.py (+12/-0) |
||||
To merge this branch: | bzr merge lp:~blake-rouse/maas/osystem-preseed-cleanup | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jeroen T. Vermeulen (community) | Approve | ||
Review via email: mp+217099@code.launchpad.net |
This proposal supersedes a proposal from 2014-04-24.
This proposal has been superseded by a proposal from 2014-05-29.
Commit message
Description of the change
This is the final change in the series of changes to allow MAAS to deploy other operating systems. As we have Windows and CentOS support coming soon this is needed to easily add new operating systems.
Removed the DISTRO_SERIES enums as they are no longer needed, all information comes from the OperatingSystem
Added the ability for an operating system to compose its own preseed. This will be used for Windows, CentOS and other operating systems.
Blake Rouse (blake-rouse) wrote : | # |
Removed the hasattr check to use a NotImplemented exception. Can you give this another review?
Jeroen T. Vermeulen (jtv) wrote : | # |
Thanks, this branch looks a lot simpler than I remember it! Could you add a note in the docstring for compose_preseed about the NotImplementedError and what it means? Similar to “:param …:” and “:return:” the format also supports “:raise …:”.
Preview Diff
1 | === modified file 'src/maasserver/api.py' |
2 | --- src/maasserver/api.py 2014-05-29 18:21:27 +0000 |
3 | +++ src/maasserver/api.py 2014-05-29 18:21:27 +0000 |
4 | @@ -258,10 +258,6 @@ |
5 | OperatingSystemRegistry, |
6 | ) |
7 | from provisioningserver.kernel_opts import KernelParameters |
8 | -from provisioningserver.driver import ( |
9 | - BOOT_IMAGE_PURPOSE, |
10 | - OperatingSystemRegistry, |
11 | - ) |
12 | from provisioningserver.power_schema import UNKNOWN_POWER_TYPE |
13 | import simplejson as json |
14 | |
15 | @@ -422,11 +418,7 @@ |
16 | operating system the node will use. |
17 | :type osystem: unicode |
18 | :param distro_series: If present, this parameter specifies the |
19 | -<<<<<<< TREE |
20 | OS release the node will use. |
21 | -======= |
22 | - os relase the node will use. |
23 | ->>>>>>> MERGE-SOURCE |
24 | :type distro_series: unicode |
25 | |
26 | Ideally we'd have MIME multipart and content-transfer-encoding etc. |
27 | @@ -453,7 +445,6 @@ |
28 | node = Node.objects.get_node_or_404( |
29 | system_id=system_id, user=request.user, |
30 | perm=NODE_PERMISSION.EDIT) |
31 | -<<<<<<< TREE |
32 | Form = get_node_edit_form(request.user) |
33 | form = Form(instance=node) |
34 | form.set_distro_series(series=series) |
35 | @@ -461,9 +452,6 @@ |
36 | form.save() |
37 | else: |
38 | raise ValidationError(form.errors) |
39 | -======= |
40 | - node.set_osystem_and_distro_series(osystem, series) |
41 | ->>>>>>> MERGE-SOURCE |
42 | nodes = Node.objects.start_nodes( |
43 | [system_id], request.user, user_data=user_data) |
44 | if len(nodes) == 0: |
45 | @@ -476,10 +464,6 @@ |
46 | """Release a node. Opposite of `NodesHandler.acquire`.""" |
47 | node = Node.objects.get_node_or_404( |
48 | system_id=system_id, user=request.user, perm=NODE_PERMISSION.EDIT) |
49 | -<<<<<<< TREE |
50 | -======= |
51 | - node.set_osystem_and_distro_series(osystem='', series='') |
52 | ->>>>>>> MERGE-SOURCE |
53 | if node.status == NODE_STATUS.READY: |
54 | # Nothing to do. This may be a redundant retry, and the |
55 | # postcondition is achieved, so call this success. |
56 | @@ -2429,7 +2413,6 @@ |
57 | if node.should_use_traditional_installer(): |
58 | return "install" |
59 | else: |
60 | -<<<<<<< TREE |
61 | # Return normal install if the booting operating system |
62 | # doesn't support xinstall. |
63 | osystem_obj = OperatingSystemRegistry[osystem] |
64 | @@ -2438,17 +2421,6 @@ |
65 | if BOOT_IMAGE_PURPOSE.XINSTALL in purposes: |
66 | return "xinstall" |
67 | return "install" |
68 | -======= |
69 | - # Check that the booting operating system, actually supports |
70 | - # fast-path installation. If it does not then, we need to |
71 | - # return normal install. |
72 | - osystem_obj = OperatingSystemRegistry[osystem] |
73 | - purposes = osystem_obj.get_boot_image_purposes( |
74 | - arch, subarch, series, label) |
75 | - if BOOT_IMAGE_PURPOSE.XINSTALL in purposes: |
76 | - return "xinstall" |
77 | - return "install" |
78 | ->>>>>>> MERGE-SOURCE |
79 | else: |
80 | return "local" # TODO: Investigate. |
81 | else: |
82 | @@ -2555,18 +2527,10 @@ |
83 | |
84 | subarch = get_optional_param(request.GET, 'subarch', 'generic') |
85 | |
86 | -<<<<<<< TREE |
87 | # Get the purpose, without a selected label |
88 | purpose = get_boot_purpose( |
89 | node, osystem, arch, subarch, series, label=None) |
90 | |
91 | -======= |
92 | - # Get the purpose, checking that if node is using xinstall, the operating |
93 | - # system actaully supports that mode. We pass None for the label, here |
94 | - # because it is unknown which label will be used yet. |
95 | - purpose = get_boot_purpose(node, osystem, arch, subarch, series, None) |
96 | - |
97 | ->>>>>>> MERGE-SOURCE |
98 | # We use as our default label the label of the most recent image for |
99 | # the criteria we've assembled above. If there is no latest image |
100 | # (which should never happen in reality but may happen in tests), we |
101 | @@ -2590,16 +2554,9 @@ |
102 | subarch = latest_image.subarchitecture |
103 | label = get_optional_param(request.GET, 'label', latest_label) |
104 | |
105 | -<<<<<<< TREE |
106 | # Get the supported purpose with the boot label |
107 | purpose = get_boot_purpose(node, osystem, arch, subarch, series, label) |
108 | |
109 | -======= |
110 | - # Now that we have the correct label, lets check the boot purpose again |
111 | - # to make sure that the boot image with that label, is the correct purpose. |
112 | - purpose = get_boot_purpose(node, osystem, arch, subarch, series, label) |
113 | - |
114 | ->>>>>>> MERGE-SOURCE |
115 | if node is not None: |
116 | # We don't care if the kernel opts is from the global setting or a tag, |
117 | # just get the options |
118 | |
119 | === modified file 'src/maasserver/compose_preseed.py' |
120 | --- src/maasserver/compose_preseed.py 2013-10-18 09:54:17 +0000 |
121 | +++ src/maasserver/compose_preseed.py 2014-05-29 18:21:27 +0000 |
122 | @@ -20,6 +20,7 @@ |
123 | |
124 | from maasserver.enum import NODE_STATUS |
125 | from maasserver.utils import absolute_reverse |
126 | +from provisioningserver.driver import OperatingSystemRegistry |
127 | import yaml |
128 | |
129 | |
130 | @@ -104,6 +105,13 @@ |
131 | if node.status == NODE_STATUS.COMMISSIONING: |
132 | return compose_commissioning_preseed(token, base_url) |
133 | else: |
134 | + osystem = OperatingSystemRegistry[node.get_osystem()] |
135 | + metadata_url = absolute_reverse('metadata', base_url=base_url) |
136 | + try: |
137 | + return osystem.compose_preseed(node, token, metadata_url) |
138 | + except NotImplementedError: |
139 | + pass |
140 | + |
141 | if node.should_use_traditional_installer(): |
142 | return compose_cloud_init_preseed(token, base_url) |
143 | else: |
144 | |
145 | === modified file 'src/maasserver/enum.py' |
146 | --- src/maasserver/enum.py 2014-03-28 16:36:32 +0000 |
147 | +++ src/maasserver/enum.py 2014-05-29 18:21:27 +0000 |
148 | @@ -13,7 +13,6 @@ |
149 | |
150 | __metaclass__ = type |
151 | __all__ = [ |
152 | - 'COMMISSIONING_DISTRO_SERIES_CHOICES', |
153 | 'COMPONENT', |
154 | 'NODEGROUP_STATUS', |
155 | 'NODEGROUP_STATUS_CHOICES', |
156 | @@ -25,8 +24,6 @@ |
157 | 'NODE_STATUS_CHOICES', |
158 | 'NODE_STATUS_CHOICES_DICT', |
159 | 'PRESEED_TYPE', |
160 | - 'DISTRO_SERIES', |
161 | - 'DISTRO_SERIES_CHOICES', |
162 | 'USERDATA_TYPE', |
163 | ] |
164 | |
165 | @@ -86,36 +83,6 @@ |
166 | NODE_STATUS_CHOICES_DICT = OrderedDict(NODE_STATUS_CHOICES) |
167 | |
168 | |
169 | -class DISTRO_SERIES: |
170 | - """List of supported ubuntu releases.""" |
171 | - #: |
172 | - default = '' |
173 | - #: |
174 | - precise = 'precise' |
175 | - #: |
176 | - quantal = 'quantal' |
177 | - #: |
178 | - raring = 'raring' |
179 | - #: |
180 | - saucy = 'saucy' |
181 | - #: |
182 | - trusty = 'trusty' |
183 | - |
184 | -DISTRO_SERIES_CHOICES = ( |
185 | - (DISTRO_SERIES.default, 'Default Ubuntu Release'), |
186 | - (DISTRO_SERIES.precise, 'Ubuntu 12.04 LTS "Precise Pangolin"'), |
187 | - (DISTRO_SERIES.quantal, 'Ubuntu 12.10 "Quantal Quetzal"'), |
188 | - (DISTRO_SERIES.raring, 'Ubuntu 13.04 "Raring Ringtail"'), |
189 | - (DISTRO_SERIES.saucy, 'Ubuntu 13.10 "Saucy Salamander"'), |
190 | - (DISTRO_SERIES.trusty, 'Ubuntu 14.04 LTS "Trusty Tahr"'), |
191 | -) |
192 | - |
193 | - |
194 | -COMMISSIONING_DISTRO_SERIES_CHOICES = ( |
195 | - (DISTRO_SERIES.trusty, dict(DISTRO_SERIES_CHOICES)[DISTRO_SERIES.trusty]), |
196 | -) |
197 | - |
198 | - |
199 | class NODE_PERMISSION: |
200 | """Permissions relating to nodes.""" |
201 | VIEW = 'view_node' |
202 | |
203 | === modified file 'src/maasserver/forms.py' |
204 | --- src/maasserver/forms.py 2014-05-29 18:21:27 +0000 |
205 | +++ src/maasserver/forms.py 2014-05-29 17:03:20 +0000 |
206 | @@ -85,10 +85,6 @@ |
207 | from maasserver.forms_settings import ( |
208 | CONFIG_ITEMS_KEYS, |
209 | get_config_field, |
210 | -<<<<<<< TREE |
211 | -======= |
212 | - list_commisioning_choices, |
213 | ->>>>>>> MERGE-SOURCE |
214 | INVALID_SETTING_MSG_TEMPLATE, |
215 | list_commisioning_choices, |
216 | ) |
217 | @@ -219,7 +215,6 @@ |
218 | return all_architectures[0] |
219 | |
220 | |
221 | -<<<<<<< TREE |
222 | def list_all_usable_osystems(): |
223 | """Return all operating systems that can be used for nodes. |
224 | |
225 | @@ -320,82 +315,6 @@ |
226 | return None |
227 | |
228 | |
229 | -======= |
230 | -def list_all_usable_osystems(): |
231 | - """Return all operating systems that can be used for nodes. |
232 | - |
233 | - These are the operating systems for which any nodegroup has the boot images |
234 | - required to boot the node. |
235 | - """ |
236 | - # The Node edit form offers all usable operating systems as options for the |
237 | - # osystem field. Not all of these may be available in the node's |
238 | - # nodegroup, but to represent that accurately in the UI would depend on |
239 | - # the currently selected nodegroup. Narrowing the options down further |
240 | - # would have to happen browser-side. |
241 | - osystems = set() |
242 | - for nodegroup in NodeGroup.objects.all(): |
243 | - osystems = osystems.union( |
244 | - BootImage.objects.get_usable_osystems(nodegroup)) |
245 | - osystems = [OperatingSystemRegistry[osystem] for osystem in osystems] |
246 | - return sorted(osystems, key=lambda osystem: osystem.title) |
247 | - |
248 | - |
249 | -def list_osystem_choices(osystems): |
250 | - """Return Django "choices" list for `osystem`.""" |
251 | - choices = [('', 'Default OS')] |
252 | - choices += [ |
253 | - (osystem.name, osystem.title) |
254 | - for osystem in osystems |
255 | - ] |
256 | - return choices |
257 | - |
258 | - |
259 | -def list_all_usable_releases(osystems): |
260 | - """Return dictionary of usable `releases` for each opertaing system.""" |
261 | - distro_series = {} |
262 | - nodegroups = list(NodeGroup.objects.all()) |
263 | - for osystem in osystems: |
264 | - releases = set() |
265 | - for nodegroup in nodegroups: |
266 | - releases = releases.union( |
267 | - BootImage.objects.get_usable_releases(nodegroup, osystem.name)) |
268 | - distro_series[osystem.name] = sorted(releases) |
269 | - return distro_series |
270 | - |
271 | - |
272 | -def list_release_choices(releases): |
273 | - """Return Django "choices" list for `releases`.""" |
274 | - choices = [('', 'Default OS Release')] |
275 | - for key, value in releases.items(): |
276 | - osystem = OperatingSystemRegistry[key] |
277 | - options = osystem.format_release_choices(value) |
278 | - choices += [( |
279 | - '%s/' % osystem.name, |
280 | - 'Latest %s Release' % osystem.title |
281 | - )] |
282 | - choices += [ |
283 | - ('%s/%s' % (osystem.name, name), title) |
284 | - for name, title in options |
285 | - ] |
286 | - return choices |
287 | - |
288 | - |
289 | -def clean_distro_series_field(form, field, os_field): |
290 | - # distro_series field can be supplied the value os/release, that is the |
291 | - # way the web UI provides the value. |
292 | - new_distro_series = form.cleaned_data.get(field) |
293 | - if new_distro_series is not None and '/' in new_distro_series: |
294 | - os, release = new_distro_series.split('/', 1) |
295 | - if 'os' in form.cleaned_data: |
296 | - new_os = form.cleaned_data[os_field] |
297 | - if os != new_os: |
298 | - raise ValidationError( |
299 | - "%s option does not match osystem option." % field) |
300 | - return release |
301 | - return new_distro_series |
302 | - |
303 | - |
304 | ->>>>>>> MERGE-SOURCE |
305 | class NodeForm(ModelForm): |
306 | |
307 | def __init__(self, request=None, *args, **kwargs): |
308 | @@ -428,7 +347,6 @@ |
309 | choices=choices, required=True, initial=default_arch, |
310 | error_messages={'invalid_choice': invalid_arch_message}) |
311 | |
312 | -<<<<<<< TREE |
313 | def set_up_osystem_and_distro_series_fields(self, instance): |
314 | """Create the `osystem` and `distro_series` fields. |
315 | |
316 | @@ -455,43 +373,6 @@ |
317 | if instance is not None: |
318 | self.initial['distro_series'] = initial_value |
319 | |
320 | -======= |
321 | - def set_up_osystem_and_distro_series_fields(self, instance): |
322 | - """Create the `osystem` and `distro_series` fields. |
323 | - |
324 | - This needs to be done on the fly so that we can pass a dynamic list of |
325 | - usable operating systems and distro_series. |
326 | - """ |
327 | - osystems = list_all_usable_osystems() |
328 | - releases = list_all_usable_releases(osystems) |
329 | - choices = list_osystem_choices(osystems) |
330 | - distro_choices = list_release_choices(releases) |
331 | - invalid_osystem_message = compose_invalid_choice_text( |
332 | - 'osystem', choices) |
333 | - invalid_distro_series_message = compose_invalid_choice_text( |
334 | - 'distro_series', distro_choices) |
335 | - self.fields['osystem'] = forms.ChoiceField( |
336 | - label="OS", choices=choices, required=False, initial='', |
337 | - error_messages={'invalid_choice': invalid_osystem_message}) |
338 | - self.fields['distro_series'] = forms.ChoiceField( |
339 | - label="Release", choices=distro_choices, |
340 | - required=False, initial='', |
341 | - error_messages={'invalid_choice': invalid_distro_series_message}) |
342 | - |
343 | - # Set the initial for the distro_series so it is selected |
344 | - # correctly in the gui. |
345 | - if instance is not None: |
346 | - osystem = instance.osystem |
347 | - series = instance.distro_series |
348 | - if osystem is not None and osystem != '': |
349 | - if series is None: |
350 | - series = '' |
351 | - self.initial['distro_series'] = '%s/%s' % ( |
352 | - osystem, |
353 | - series, |
354 | - ) |
355 | - |
356 | ->>>>>>> MERGE-SOURCE |
357 | def clean_hostname(self): |
358 | # Don't allow the hostname to be changed if the node is |
359 | # currently allocated. Juju knows the node by its old name, so |
360 | @@ -516,7 +397,6 @@ |
361 | is_valid = False |
362 | return is_valid |
363 | |
364 | -<<<<<<< TREE |
365 | def set_distro_series(self, series=''): |
366 | """Sets the osystem and distro_series, from the provided |
367 | distro_series. |
368 | @@ -536,8 +416,6 @@ |
369 | else: |
370 | self.data['distro_series'] = series |
371 | |
372 | -======= |
373 | ->>>>>>> MERGE-SOURCE |
374 | hostname = forms.CharField( |
375 | label="Host name", required=False, help_text=( |
376 | "The FQDN (Fully Qualified Domain Name) is derived from the " |
377 | |
378 | === modified file 'src/maasserver/forms_settings.py' |
379 | --- src/maasserver/forms_settings.py 2014-05-29 18:21:27 +0000 |
380 | +++ src/maasserver/forms_settings.py 2014-05-29 16:59:04 +0000 |
381 | @@ -31,7 +31,6 @@ |
382 | INVALID_URL_MESSAGE = "Enter a valid url (e.g. http://host.example.com)." |
383 | |
384 | |
385 | -<<<<<<< TREE |
386 | def list_osystem_choices(): |
387 | osystems = [osystem for _, osystem in OperatingSystemRegistry] |
388 | osystems = sorted(osystems, key=lambda osystem: osystem.title) |
389 | @@ -59,33 +58,6 @@ |
390 | releases = DEFAULT_OS.get_supported_commissioning_releases() |
391 | options = DEFAULT_OS.format_release_choices(releases) |
392 | return list(options) |
393 | -======= |
394 | -def list_osystem_choices(): |
395 | - return [ |
396 | - (osystem.name, osystem.title) |
397 | - for _, osystem in OperatingSystemRegistry |
398 | - ] |
399 | - |
400 | - |
401 | -def list_release_choices(): |
402 | - osystems = [osystem for _, osystem in OperatingSystemRegistry] |
403 | - choices = [] |
404 | - for osystem in osystems: |
405 | - supported = sorted(osystem.get_supported_releases()) |
406 | - options = osystem.format_release_choices(supported) |
407 | - options = [ |
408 | - ('%s/%s' % (osystem.name, name), title) |
409 | - for name, title in options |
410 | - ] |
411 | - choices += options |
412 | - return choices |
413 | - |
414 | - |
415 | -def list_commisioning_choices(): |
416 | - releases = DEFAULT_OS.get_supported_commissioning_releases() |
417 | - options = DEFAULT_OS.format_release_choices(releases) |
418 | - return [(name, title) for name, title in options] |
419 | ->>>>>>> MERGE-SOURCE |
420 | |
421 | |
422 | CONFIG_ITEMS = { |
423 | |
424 | === removed file 'src/maasserver/migrations/0077_add_osystem_to_node.py' |
425 | --- src/maasserver/migrations/0077_add_osystem_to_node.py 2014-05-29 18:21:27 +0000 |
426 | +++ src/maasserver/migrations/0077_add_osystem_to_node.py 1970-01-01 00:00:00 +0000 |
427 | @@ -1,264 +0,0 @@ |
428 | -# -*- coding: utf-8 -*- |
429 | -from south.utils import datetime_utils as datetime |
430 | -from south.db import db |
431 | -from south.v2 import SchemaMigration |
432 | -from django.db import models |
433 | - |
434 | - |
435 | -class Migration(SchemaMigration): |
436 | - |
437 | - def forwards(self, orm): |
438 | - # Adding field 'Node.osystem' |
439 | - db.add_column(u'maasserver_node', 'osystem', |
440 | - self.gf('django.db.models.fields.CharField')(default=u'', max_length=20, null=True, blank=True), |
441 | - keep_default=False) |
442 | - |
443 | - |
444 | - def backwards(self, orm): |
445 | - # Deleting field 'Node.osystem' |
446 | - db.delete_column(u'maasserver_node', 'osystem') |
447 | - |
448 | - |
449 | - models = { |
450 | - u'auth.group': { |
451 | - 'Meta': {'object_name': 'Group'}, |
452 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
453 | - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
454 | - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
455 | - }, |
456 | - u'auth.permission': { |
457 | - 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, |
458 | - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
459 | - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), |
460 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
461 | - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
462 | - }, |
463 | - u'auth.user': { |
464 | - 'Meta': {'object_name': 'User'}, |
465 | - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
466 | - 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}), |
467 | - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
468 | - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), |
469 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
470 | - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
471 | - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
472 | - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
473 | - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
474 | - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
475 | - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
476 | - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), |
477 | - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
478 | - }, |
479 | - u'contenttypes.contenttype': { |
480 | - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
481 | - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
482 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
483 | - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
484 | - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
485 | - }, |
486 | - u'maasserver.bootimage': { |
487 | - 'Meta': {'unique_together': "((u'nodegroup', u'osystem', u'architecture', u'subarchitecture', u'release', u'purpose', u'label'),)", 'object_name': 'BootImage'}, |
488 | - 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
489 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
490 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
491 | - 'label': ('django.db.models.fields.CharField', [], {'default': "u'release'", 'max_length': '255'}), |
492 | - 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
493 | - 'osystem': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
494 | - 'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
495 | - 'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
496 | - 'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
497 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
498 | - }, |
499 | - u'maasserver.bootsource': { |
500 | - 'Meta': {'object_name': 'BootSource'}, |
501 | - 'cluster': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}), |
502 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
503 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
504 | - 'keyring_data': ('django.db.models.fields.BinaryField', [], {'blank': 'True'}), |
505 | - 'keyring_filename': ('django.db.models.fields.FilePathField', [], {'max_length': '100', 'blank': 'True'}), |
506 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
507 | - 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'}) |
508 | - }, |
509 | - u'maasserver.bootsourceselection': { |
510 | - 'Meta': {'object_name': 'BootSourceSelection'}, |
511 | - 'arches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), |
512 | - 'boot_source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.BootSource']"}), |
513 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
514 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
515 | - 'labels': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), |
516 | - 'release': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'null': 'True', 'blank': 'True'}), |
517 | - 'subarches': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'text'", 'null': 'True', 'blank': 'True'}), |
518 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
519 | - }, |
520 | - u'maasserver.componenterror': { |
521 | - 'Meta': {'object_name': 'ComponentError'}, |
522 | - 'component': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}), |
523 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
524 | - 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000'}), |
525 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
526 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
527 | - }, |
528 | - u'maasserver.config': { |
529 | - 'Meta': {'object_name': 'Config'}, |
530 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
531 | - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
532 | - 'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'}) |
533 | - }, |
534 | - u'maasserver.dhcplease': { |
535 | - 'Meta': {'object_name': 'DHCPLease'}, |
536 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
537 | - 'ip': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15'}), |
538 | - 'mac': ('maasserver.fields.MACAddressField', [], {}), |
539 | - 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}) |
540 | - }, |
541 | - u'maasserver.downloadprogress': { |
542 | - 'Meta': {'object_name': 'DownloadProgress'}, |
543 | - 'bytes_downloaded': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
544 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
545 | - 'error': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'blank': 'True'}), |
546 | - 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
547 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
548 | - 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
549 | - 'size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
550 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
551 | - }, |
552 | - u'maasserver.filestorage': { |
553 | - 'Meta': {'unique_together': "((u'filename', u'owner'),)", 'object_name': 'FileStorage'}, |
554 | - 'content': ('metadataserver.fields.BinaryField', [], {'blank': 'True'}), |
555 | - 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
556 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
557 | - 'key': ('django.db.models.fields.CharField', [], {'default': "u'9bbf01e4-cbbd-11e3-afb3-bcee7b78dc5b'", 'unique': 'True', 'max_length': '36'}), |
558 | - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
559 | - }, |
560 | - u'maasserver.macaddress': { |
561 | - 'Meta': {'object_name': 'MACAddress'}, |
562 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
563 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
564 | - 'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}), |
565 | - 'networks': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Network']", 'symmetrical': 'False', 'blank': 'True'}), |
566 | - 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}), |
567 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
568 | - }, |
569 | - u'maasserver.network': { |
570 | - 'Meta': {'object_name': 'Network'}, |
571 | - 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
572 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
573 | - 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'unique': 'True', 'max_length': '39'}), |
574 | - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), |
575 | - 'netmask': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), |
576 | - 'vlan_tag': ('django.db.models.fields.PositiveSmallIntegerField', [], {'unique': 'True', 'null': 'True', 'blank': 'True'}) |
577 | - }, |
578 | - u'maasserver.node': { |
579 | - 'Meta': {'object_name': 'Node'}, |
580 | - 'agent_name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'null': 'True', 'blank': 'True'}), |
581 | - 'architecture': ('django.db.models.fields.CharField', [], {'max_length': '31'}), |
582 | - 'cpu_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
583 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
584 | - 'distro_series': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'null': 'True', 'blank': 'True'}), |
585 | - 'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
586 | - 'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'unique': 'True', 'max_length': '255', 'blank': 'True'}), |
587 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
588 | - 'memory': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
589 | - 'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
590 | - 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}), |
591 | - 'osystem': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '20', 'null': 'True', 'blank': 'True'}), |
592 | - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), |
593 | - 'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}), |
594 | - 'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}), |
595 | - 'routers': ('djorm_pgarray.fields.ArrayField', [], {'default': 'None', 'dbtype': "u'macaddr'", 'null': 'True', 'blank': 'True'}), |
596 | - 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}), |
597 | - 'storage': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
598 | - 'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-9bbde11a-cbbd-11e3-afb3-bcee7b78dc5b'", 'unique': 'True', 'max_length': '41'}), |
599 | - 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['maasserver.Tag']", 'symmetrical': 'False'}), |
600 | - 'token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'null': 'True'}), |
601 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
602 | - 'zone': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Zone']", 'on_delete': 'models.SET_DEFAULT'}) |
603 | - }, |
604 | - u'maasserver.nodegroup': { |
605 | - 'Meta': {'object_name': 'NodeGroup'}, |
606 | - 'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}), |
607 | - 'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Token']", 'unique': 'True'}), |
608 | - 'cluster_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'}), |
609 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
610 | - 'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
611 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
612 | - 'maas_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
613 | - 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}), |
614 | - 'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
615 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
616 | - 'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}) |
617 | - }, |
618 | - u'maasserver.nodegroupinterface': { |
619 | - 'Meta': {'unique_together': "((u'nodegroup', u'interface'),)", 'object_name': 'NodeGroupInterface'}, |
620 | - 'broadcast_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
621 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
622 | - 'foreign_dhcp_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
623 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
624 | - 'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), |
625 | - 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}), |
626 | - 'ip_range_high': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
627 | - 'ip_range_low': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
628 | - 'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
629 | - 'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}), |
630 | - 'router_ip': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
631 | - 'subnet_mask': ('django.db.models.fields.GenericIPAddressField', [], {'default': 'None', 'max_length': '39', 'null': 'True', 'blank': 'True'}), |
632 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
633 | - }, |
634 | - u'maasserver.sshkey': { |
635 | - 'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'}, |
636 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
637 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
638 | - 'key': ('django.db.models.fields.TextField', [], {}), |
639 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}), |
640 | - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) |
641 | - }, |
642 | - u'maasserver.tag': { |
643 | - 'Meta': {'object_name': 'Tag'}, |
644 | - 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
645 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
646 | - 'definition': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
647 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
648 | - 'kernel_opts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
649 | - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
650 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
651 | - }, |
652 | - u'maasserver.userprofile': { |
653 | - 'Meta': {'object_name': 'UserProfile'}, |
654 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
655 | - 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) |
656 | - }, |
657 | - u'maasserver.zone': { |
658 | - 'Meta': {'ordering': "[u'name']", 'object_name': 'Zone'}, |
659 | - 'created': ('django.db.models.fields.DateTimeField', [], {}), |
660 | - 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
661 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
662 | - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}), |
663 | - 'updated': ('django.db.models.fields.DateTimeField', [], {}) |
664 | - }, |
665 | - u'piston.consumer': { |
666 | - 'Meta': {'object_name': 'Consumer'}, |
667 | - 'description': ('django.db.models.fields.TextField', [], {}), |
668 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
669 | - 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
670 | - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), |
671 | - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
672 | - 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}), |
673 | - 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': u"orm['auth.User']"}) |
674 | - }, |
675 | - u'piston.token': { |
676 | - 'Meta': {'object_name': 'Token'}, |
677 | - 'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), |
678 | - 'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
679 | - 'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['piston.Consumer']"}), |
680 | - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
681 | - 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
682 | - 'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}), |
683 | - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
684 | - 'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1398350097L'}), |
685 | - 'token_type': ('django.db.models.fields.IntegerField', [], {}), |
686 | - 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': u"orm['auth.User']"}), |
687 | - 'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'}) |
688 | - } |
689 | - } |
690 | - |
691 | - complete_apps = ['maasserver'] |
692 | \ No newline at end of file |
693 | |
694 | === modified file 'src/maasserver/models/bootsource.py' |
695 | --- src/maasserver/models/bootsource.py 2014-05-22 13:42:52 +0000 |
696 | +++ src/maasserver/models/bootsource.py 2014-05-29 18:21:27 +0000 |
697 | @@ -29,6 +29,14 @@ |
698 | from maasserver.fields import EditableBinaryField |
699 | from maasserver.models.cleansave import CleanSave |
700 | from maasserver.models.timestampedmodel import TimestampedModel |
701 | +from provisioningserver.driver.os_ubuntu import UbuntuOS |
702 | + |
703 | + |
704 | +def list_release_choices(): |
705 | + """Return Django "choices" list for Ubuntu releases.""" |
706 | + osystem = UbuntuOS() |
707 | + releases = osystem.get_supported_releases() |
708 | + return osystem.format_release_choices(releases) |
709 | |
710 | |
711 | class BootSource(CleanSave, TimestampedModel): |
712 | |
713 | === modified file 'src/maasserver/models/bootsourceselection.py' |
714 | --- src/maasserver/models/bootsourceselection.py 2014-05-16 11:41:18 +0000 |
715 | +++ src/maasserver/models/bootsourceselection.py 2014-05-29 18:21:27 +0000 |
716 | @@ -24,12 +24,16 @@ |
717 | ) |
718 | import djorm_pgarray.fields |
719 | from maasserver import DefaultMeta |
720 | -from maasserver.enum import ( |
721 | - DISTRO_SERIES, |
722 | - DISTRO_SERIES_CHOICES, |
723 | - ) |
724 | from maasserver.models.cleansave import CleanSave |
725 | from maasserver.models.timestampedmodel import TimestampedModel |
726 | +from provisioningserver.driver.os_ubuntu import UbuntuOS |
727 | + |
728 | + |
729 | +def list_release_choices(): |
730 | + """Return Django "choices" list for Ubuntu releases.""" |
731 | + osystem = UbuntuOS() |
732 | + releases = osystem.get_supported_releases() |
733 | + return osystem.format_release_choices(releases) |
734 | |
735 | |
736 | class BootSourceSelectionManager(Manager): |
737 | @@ -47,8 +51,8 @@ |
738 | boot_source = ForeignKey('maasserver.BootSource', blank=False) |
739 | |
740 | release = CharField( |
741 | - max_length=20, choices=DISTRO_SERIES_CHOICES, blank=True, |
742 | - default=DISTRO_SERIES.default, |
743 | + max_length=20, choices=list_release_choices(), blank=True, |
744 | + default='', |
745 | help_text="The Ubuntu release for which to import resources.") |
746 | |
747 | arches = djorm_pgarray.fields.ArrayField(dbtype="text") |
748 | |
749 | === modified file 'src/maasserver/models/node.py' |
750 | --- src/maasserver/models/node.py 2014-05-29 18:21:27 +0000 |
751 | +++ src/maasserver/models/node.py 2014-05-29 18:21:27 +0000 |
752 | @@ -508,23 +508,11 @@ |
753 | owner = ForeignKey( |
754 | User, default=None, blank=True, null=True, editable=False) |
755 | |
756 | -<<<<<<< TREE |
757 | osystem = CharField( |
758 | max_length=20, blank=True, default='') |
759 | |
760 | -======= |
761 | - osystem = CharField( |
762 | - max_length=20, null=True, blank=True, default='', |
763 | - validators=[validate_osystem]) |
764 | - |
765 | ->>>>>>> MERGE-SOURCE |
766 | distro_series = CharField( |
767 | -<<<<<<< TREE |
768 | max_length=20, blank=True, default='') |
769 | -======= |
770 | - max_length=20, null=True, blank=True, default='', |
771 | - validators=[validate_distro_series]) |
772 | ->>>>>>> MERGE-SOURCE |
773 | |
774 | architecture = CharField(max_length=31, blank=False) |
775 | |
776 | @@ -831,7 +819,6 @@ |
777 | """The name of the queue for tasks specific to this node.""" |
778 | return self.nodegroup.work_queue |
779 | |
780 | -<<<<<<< TREE |
781 | def get_osystem(self): |
782 | """Return the operating system to install that node.""" |
783 | use_default_osystem = (self.osystem is None or self.osystem == '') |
784 | @@ -840,39 +827,15 @@ |
785 | else: |
786 | return self.osystem |
787 | |
788 | -======= |
789 | - def get_osystem(self): |
790 | - """Return the operating system to install that node.""" |
791 | - use_default_osystem = ( |
792 | - not self.osystem or |
793 | - self.osystem == '') |
794 | - if use_default_osystem: |
795 | - return Config.objects.get_config('default_osystem') |
796 | - else: |
797 | - return self.osystem |
798 | - |
799 | ->>>>>>> MERGE-SOURCE |
800 | def get_distro_series(self): |
801 | """Return the distro series to install that node.""" |
802 | -<<<<<<< TREE |
803 | use_default_osystem = ( |
804 | self.osystem is None or |
805 | self.osystem == '') |
806 | -======= |
807 | - use_default_osystem = ( |
808 | - not self.osystem or |
809 | - self.osystem == '') |
810 | ->>>>>>> MERGE-SOURCE |
811 | use_default_distro_series = ( |
812 | -<<<<<<< TREE |
813 | self.distro_series is None or |
814 | self.distro_series == '') |
815 | if use_default_osystem and use_default_distro_series: |
816 | -======= |
817 | - not self.distro_series or |
818 | - self.distro_series == '') |
819 | - if use_default_osystem and use_default_distro_series: |
820 | ->>>>>>> MERGE-SOURCE |
821 | return Config.objects.get_config('default_distro_series') |
822 | elif use_default_distro_series: |
823 | osystem = OperatingSystemRegistry[self.osystem] |
824 | @@ -880,25 +843,6 @@ |
825 | else: |
826 | return self.distro_series |
827 | |
828 | -<<<<<<< TREE |
829 | -======= |
830 | - def set_osystem(self, osystem=''): |
831 | - """Set the operating system to install that node.""" |
832 | - self.osystem = osystem |
833 | - self.save() |
834 | - |
835 | - def set_distro_series(self, series=''): |
836 | - """Set the distro series to install that node.""" |
837 | - self.distro_series = series |
838 | - self.save() |
839 | - |
840 | - def set_osystem_and_distro_series(self, osystem='', series=''): |
841 | - """Set the oeprating system to install that node.""" |
842 | - self.osystem = osystem |
843 | - self.distro_series = series |
844 | - self.save() |
845 | - |
846 | ->>>>>>> MERGE-SOURCE |
847 | def get_effective_power_parameters(self): |
848 | """Return effective power parameters, including any defaults.""" |
849 | if self.power_parameters: |
850 | |
851 | === modified file 'src/maasserver/models/tests/test_node.py' |
852 | --- src/maasserver/models/tests/test_node.py 2014-05-29 18:21:27 +0000 |
853 | +++ src/maasserver/models/tests/test_node.py 2014-05-29 17:03:20 +0000 |
854 | @@ -269,7 +269,6 @@ |
855 | offset += timedelta(1) |
856 | self.assertEqual(macs[0], node.get_primary_mac().mac_address) |
857 | |
858 | -<<<<<<< TREE |
859 | def test_get_osystem_returns_default_osystem(self): |
860 | node = factory.make_node(osystem='') |
861 | osystem = Config.objects.get_config('default_osystem') |
862 | @@ -278,41 +277,6 @@ |
863 | def test_get_distro_series_returns_default_series(self): |
864 | node = factory.make_node(distro_series='') |
865 | series = Config.objects.get_config('default_distro_series') |
866 | -======= |
867 | - def test_get_osystem_returns_default_osystem_and_series(self): |
868 | - node = factory.make_node() |
869 | - osystem = Config.objects.get_config('default_osystem') |
870 | - series = Config.objects.get_config('default_distro_series') |
871 | - self.assertEqual(osystem, node.get_osystem()) |
872 | - self.assertEqual(series, node.get_distro_series()) |
873 | - |
874 | - def test_get_series_returns_default_for_osystem(self): |
875 | - node = factory.make_node() |
876 | - osystem = factory.getRandomOS() |
877 | - series = osystem.get_default_release() |
878 | - node.set_osystem(osystem.name) |
879 | - self.assertEqual(series, node.get_distro_series()) |
880 | - |
881 | - def test_set_get_osystem_returns_osystem(self): |
882 | - osystem = factory.getRandomOS() |
883 | - node = factory.make_node() |
884 | - node.set_osystem(osystem.name) |
885 | - self.assertEqual(osystem.name, node.get_osystem()) |
886 | - |
887 | - def test_set_get_distro_series_returns_series(self): |
888 | - osystem = factory.getRandomOS() |
889 | - series = factory.getRandomRelease(osystem) |
890 | - node = factory.make_node() |
891 | - node.set_distro_series(series) |
892 | ->>>>>>> MERGE-SOURCE |
893 | - self.assertEqual(series, node.get_distro_series()) |
894 | - |
895 | - def test_set_get_osystem_and_distro_series_returns_valid(self): |
896 | - osystem = factory.getRandomOS() |
897 | - series = factory.getRandomRelease(osystem) |
898 | - node = factory.make_node() |
899 | - node.set_osystem_and_distro_series(osystem.name, series) |
900 | - self.assertEqual(osystem.name, node.get_osystem()) |
901 | self.assertEqual(series, node.get_distro_series()) |
902 | |
903 | def test_delete_node_deletes_related_mac(self): |
904 | |
905 | === modified file 'src/maasserver/static/js/node_add.js' |
906 | --- src/maasserver/static/js/node_add.js 2014-05-29 18:21:27 +0000 |
907 | +++ src/maasserver/static/js/node_add.js 2014-05-16 15:22:54 +0000 |
908 | @@ -234,7 +234,6 @@ |
909 | }, |
910 | |
911 | /** |
912 | -<<<<<<< TREE |
913 | * If the 'distro_series' field is present, link it to operating |
914 | * system field. |
915 | * |
916 | @@ -251,24 +250,6 @@ |
917 | }, |
918 | |
919 | /** |
920 | -======= |
921 | - * If the 'distro_series' field is present, setup the linked |
922 | - * 'distro_series' field. |
923 | - * |
924 | - * @method setUpDistroSeriesField |
925 | - */ |
926 | - setUpDistroSeriesField: function() { |
927 | - if (Y.Lang.isValue(Y.one('#id_distro_series'))) { |
928 | - var widget = new Y.maas.os_distro_select.OSReleaseWidget({ |
929 | - srcNode: '#id_distro_series' |
930 | - }); |
931 | - widget.bindTo(Y.one('#id_osystem'), 'change'); |
932 | - widget.render(); |
933 | - } |
934 | - }, |
935 | - |
936 | - /** |
937 | ->>>>>>> MERGE-SOURCE |
938 | * If the 'power_type' field is present, setup the linked |
939 | * 'power_parameter' field. |
940 | * |
941 | |
942 | === renamed file 'src/maasserver/static/js/os_distro_select.js.moved' => 'src/maasserver/static/js/os_distro_select.js' |
943 | === removed file 'src/maasserver/static/js/os_distro_select.js' |
944 | --- src/maasserver/static/js/os_distro_select.js 2014-05-29 18:21:27 +0000 |
945 | +++ src/maasserver/static/js/os_distro_select.js 1970-01-01 00:00:00 +0000 |
946 | @@ -1,131 +0,0 @@ |
947 | -/* Copyright 2012-2014 Canonical Ltd. This software is licensed under the |
948 | - * GNU Affero General Public License version 3 (see the file LICENSE). |
949 | - * |
950 | - * OS/Release seletion utilities. |
951 | - * |
952 | - * @module Y.maas.power_parameter |
953 | - */ |
954 | - |
955 | -YUI.add('maas.os_distro_select', function(Y) { |
956 | - |
957 | -Y.log('loading maas.os_distro_select'); |
958 | -var module = Y.namespace('maas.os_distro_select'); |
959 | - |
960 | -// Only used to mockup io in tests. |
961 | -module._io = new Y.IO(); |
962 | - |
963 | -var OSReleaseWidget; |
964 | - |
965 | -/** |
966 | - * A widget class used to have the content of a node's release <select> |
967 | - * modified based on the selected operating system. |
968 | - * |
969 | - */ |
970 | -OSReleaseWidget = function() { |
971 | - OSReleaseWidget.superclass.constructor.apply(this, arguments); |
972 | -}; |
973 | - |
974 | -OSReleaseWidget.NAME = 'os-release-widget'; |
975 | - |
976 | -Y.extend(OSReleaseWidget, Y.Widget, { |
977 | - |
978 | - /** |
979 | - * Initialize the widget. |
980 | - * - cfg.srcNode is the node which will be updated when the selected |
981 | - * value of the 'os node' will change. |
982 | - * - cfg.osNode is the node containing a 'select' element. When |
983 | - * the selected element will change, the srcNode HTML will be |
984 | - * updated. |
985 | - * |
986 | - * @method initializer |
987 | - */ |
988 | - initializer: function(cfg) { |
989 | - this.initialSkip = true; |
990 | - }, |
991 | - |
992 | - /** |
993 | - * Bind the widget to events (to name 'event_name') generated by the given |
994 | - * 'osNode'. |
995 | - * |
996 | - * @method bindTo |
997 | - */ |
998 | - bindTo: function(osNode, event_name) { |
999 | - var self = this; |
1000 | - Y.one(osNode).on(event_name, function(e) { |
1001 | - var osValue = e.currentTarget.get('value'); |
1002 | - self.switchTo(osValue); |
1003 | - }); |
1004 | - var osValue = Y.one(osNode).get('value'); |
1005 | - self.switchTo(osValue); |
1006 | - }, |
1007 | - |
1008 | - /** |
1009 | - * React to a new value of the os node: update the HTML of |
1010 | - * 'srcNode'. |
1011 | - * |
1012 | - * @method switchTo |
1013 | - */ |
1014 | - switchTo: function(newOSValue) { |
1015 | - var srcNode = this.get('srcNode'); |
1016 | - var options = srcNode.all('option'); |
1017 | - var selected = false; |
1018 | - options.each(function(option) { |
1019 | - var value = option.get('value'); |
1020 | - var split_value = value.split("/"); |
1021 | - |
1022 | - // Only show the default option, if that |
1023 | - // is the selected os option as well. |
1024 | - if(newOSValue == '') { |
1025 | - if(value == '') { |
1026 | - option.removeClass('hidden'); |
1027 | - option.set('selected', 'selected'); |
1028 | - } |
1029 | - else { |
1030 | - option.addClass('hidden'); |
1031 | - } |
1032 | - } |
1033 | - else { |
1034 | - if(split_value[0] == newOSValue) { |
1035 | - option.removeClass('hidden'); |
1036 | - if(split_value[1] == '') { |
1037 | - selected = true; |
1038 | - option.set('selected', 'selected'); |
1039 | - } |
1040 | - } |
1041 | - else { |
1042 | - option.addClass('hidden'); |
1043 | - } |
1044 | - } |
1045 | - }); |
1046 | - |
1047 | - // See if this was the inital skip. As the following |
1048 | - // should only be done, after the first load, as the |
1049 | - // initial will already be selected correctly. |
1050 | - if(this.initialSkip == true) { |
1051 | - this.initialSkip = false; |
1052 | - return; |
1053 | - } |
1054 | - |
1055 | - // See if a selection was made, if not then we need |
1056 | - // to select the first visible as a default is not |
1057 | - // present. |
1058 | - if(!selected) { |
1059 | - var first_option = null; |
1060 | - options.each(function(option) { |
1061 | - if(!option.hasClass('hidden')) { |
1062 | - if(first_option == null) { |
1063 | - first_option = option; |
1064 | - } |
1065 | - } |
1066 | - }); |
1067 | - if(first_option != null) { |
1068 | - first_option.set('selected', 'selected'); |
1069 | - } |
1070 | - } |
1071 | - } |
1072 | -}); |
1073 | - |
1074 | -module.OSReleaseWidget = OSReleaseWidget; |
1075 | - |
1076 | -}, '0.1', {'requires': ['widget', 'io']} |
1077 | -); |
1078 | |
1079 | === renamed file 'src/maasserver/static/js/tests/test_os_distro_select.html.moved' => 'src/maasserver/static/js/tests/test_os_distro_select.html' |
1080 | === removed file 'src/maasserver/static/js/tests/test_os_distro_select.html' |
1081 | --- src/maasserver/static/js/tests/test_os_distro_select.html 2014-05-29 18:21:27 +0000 |
1082 | +++ src/maasserver/static/js/tests/test_os_distro_select.html 1970-01-01 00:00:00 +0000 |
1083 | @@ -1,38 +0,0 @@ |
1084 | -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
1085 | -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> |
1086 | - <head> |
1087 | - <title>Test maas.os_distro_select</title> |
1088 | - |
1089 | - <!-- YUI and test setup --> |
1090 | - <script type="text/javascript" src="../testing/yui_test_conf.js"></script> |
1091 | - <script type="text/javascript" src="/usr/share/javascript/yui3/yui/yui.js"></script> |
1092 | - <script type="text/javascript" src="../testing/testrunner.js"></script> |
1093 | - <script type="text/javascript" src="../testing/testing.js"></script> |
1094 | - <!-- The module under test --> |
1095 | - <script type="text/javascript" src="../os_distro_select.js"></script> |
1096 | - <!-- The test suite --> |
1097 | - <script type="text/javascript" src="test_os_distro_select.js"></script> |
1098 | - </head> |
1099 | - <body> |
1100 | - <span id="suite">maas.os_distro_select.tests</span> |
1101 | - <script type="text/x-template" id="select_node"> |
1102 | - <select id="id_osystem"> |
1103 | - <option value="" selected="selected">Default OS</option> |
1104 | - <option value="value1">Value1</option> |
1105 | - <option value="value2">Value2</option> |
1106 | - </select> |
1107 | - </script> |
1108 | - <script type="text/x-template" id="target_node"> |
1109 | - <select id="id_distro_series"> |
1110 | - <option value="" selected="selected">Default Release</option> |
1111 | - <option value="value1/series1">Value1Series1</option> |
1112 | - <option value="value1/series2">Value1Series2</option> |
1113 | - <option value="value1/series3">Value1Series3</option> |
1114 | - <option value="value2/series1">Value2Series1</option> |
1115 | - <option value="value2/series2">Value2Series2</option> |
1116 | - <option value="value2/series3">Value2Series3</option> |
1117 | - </select> |
1118 | - </script> |
1119 | - <div id="placeholder"></div> |
1120 | - </body> |
1121 | -</html> |
1122 | |
1123 | === renamed file 'src/maasserver/static/js/tests/test_os_distro_select.js.moved' => 'src/maasserver/static/js/tests/test_os_distro_select.js' |
1124 | === removed file 'src/maasserver/static/js/tests/test_os_distro_select.js' |
1125 | --- src/maasserver/static/js/tests/test_os_distro_select.js 2014-05-29 18:21:27 +0000 |
1126 | +++ src/maasserver/static/js/tests/test_os_distro_select.js 1970-01-01 00:00:00 +0000 |
1127 | @@ -1,106 +0,0 @@ |
1128 | -/* Copyright 2012 Canonical Ltd. This software is licensed under the |
1129 | - * GNU Affero General Public License version 3 (see the file LICENSE). |
1130 | - */ |
1131 | - |
1132 | -YUI({ useBrowserConsole: true }).add( |
1133 | - 'maas.os_distro_select.tests', function(Y) { |
1134 | - |
1135 | -Y.log('loading maas.os_distro_select.tests'); |
1136 | -var namespace = Y.namespace('maas.os_distro_select.tests'); |
1137 | - |
1138 | -var module = Y.maas.os_distro_select; |
1139 | -var suite = new Y.Test.Suite("maas.os_distro_select Tests"); |
1140 | - |
1141 | -var select_node_template = Y.one('#select_node').getContent(); |
1142 | -var target_node_template = Y.one('#target_node').getContent(); |
1143 | - |
1144 | -suite.add(new Y.maas.testing.TestCase({ |
1145 | - name: 'test-os_distro_select', |
1146 | - |
1147 | - setUp: function () { |
1148 | - Y.one('#placeholder').empty().append( |
1149 | - Y.Node.create(select_node_template).append( |
1150 | - Y.Node.create(target_node_template))); |
1151 | - }, |
1152 | - |
1153 | - testBindToDefaultShowsDefaultRelease: function() { |
1154 | - var widget = new Y.maas.os_distro_select.OSReleaseWidget({ |
1155 | - srcNode: '#id_distro_series', |
1156 | - }); |
1157 | - widget.bindTo(Y.one('#id_osystem'), 'change'); |
1158 | - |
1159 | - var options = Y.one('#id_distro_series').all('option'); |
1160 | - options.each(function(option) { |
1161 | - var value = option.get('value'); |
1162 | - if(value == '') { |
1163 | - Y.Assert.isFalse(option.hasClass('hidden')); |
1164 | - } |
1165 | - else { |
1166 | - Y.Assert.isTrue(option.hasClass('hidden')); |
1167 | - } |
1168 | - }); |
1169 | - }, |
1170 | - |
1171 | - testNonDefaultShowsRelatedReleases: function() { |
1172 | - var node = Y.one('#id_distro_series'); |
1173 | - node.append('<option value="value1/">Value1Default</option>'); |
1174 | - |
1175 | - var widget = new Y.maas.os_distro_select.OSReleaseWidget({ |
1176 | - srcNode: '#id_distro_series', |
1177 | - }); |
1178 | - widget.bindTo(Y.one('#id_osystem'), 'change'); |
1179 | - |
1180 | - var newValue = 'value1'; |
1181 | - var select = Y.one('#id_osystem'); |
1182 | - select.set('value', newValue); |
1183 | - select.simulate('change'); |
1184 | - |
1185 | - var options = Y.one('#id_distro_series').all('option'); |
1186 | - options.each(function(option) { |
1187 | - var value = option.get('value'); |
1188 | - if(value == '') { |
1189 | - Y.Assert.isTrue(option.hasClass('hidden')); |
1190 | - } |
1191 | - else { |
1192 | - var split_value = value.split('/'); |
1193 | - if(split_value[0] == newValue) { |
1194 | - Y.Assert.isFalse(option.hasClass('hidden')); |
1195 | - } |
1196 | - else { |
1197 | - Y.Assert.isTrue(option.hasClass('hidden')); |
1198 | - } |
1199 | - } |
1200 | - }); |
1201 | - |
1202 | - Y.Assert.areEqual( |
1203 | - newValue + '/', Y.one('#id_distro_series').get('value')) |
1204 | - }, |
1205 | - |
1206 | - testInitialSkipOnFirstChange: function() { |
1207 | - var newValue = 'value1/series1'; |
1208 | - var select = Y.one('#id_osystem'); |
1209 | - select.set('value', 'value1'); |
1210 | - select.simulate('change'); |
1211 | - select = Y.one('#id_distro_series'); |
1212 | - select.set('value', newValue); |
1213 | - select.simulate('change'); |
1214 | - |
1215 | - var widget = new Y.maas.os_distro_select.OSReleaseWidget({ |
1216 | - srcNode: '#id_distro_series', |
1217 | - }); |
1218 | - |
1219 | - Y.Assert.isTrue(widget.initialSkip); |
1220 | - widget.bindTo(Y.one('#id_osystem'), 'change'); |
1221 | - Y.Assert.isFalse(widget.initialSkip); |
1222 | - |
1223 | - Y.Assert.areEqual( |
1224 | - newValue, Y.one('#id_distro_series').get('value')) |
1225 | - } |
1226 | - |
1227 | -})); |
1228 | - |
1229 | -namespace.suite = suite; |
1230 | - |
1231 | -}, '0.1', {'requires': [ |
1232 | - 'node-event-simulate', 'test', 'maas.testing', 'maas.os_distro_select']} |
1233 | -); |
1234 | |
1235 | === modified file 'src/maasserver/testing/factory.py' |
1236 | --- src/maasserver/testing/factory.py 2014-05-29 17:03:20 +0000 |
1237 | +++ src/maasserver/testing/factory.py 2014-05-29 18:21:27 +0000 |
1238 | @@ -23,7 +23,6 @@ |
1239 | from django.contrib.auth.models import User |
1240 | from maasserver.clusterrpc.power_parameters import get_power_types |
1241 | from maasserver.enum import ( |
1242 | - DISTRO_SERIES_CHOICES, |
1243 | NODE_STATUS, |
1244 | NODEGROUP_STATUS, |
1245 | NODEGROUPINTERFACE_MANAGEMENT, |
1246 | @@ -62,6 +61,7 @@ |
1247 | # XXX 2014-05-13 blake-rouse bug=1319143 |
1248 | # Need to not import directly, use RPC to info from cluster. |
1249 | from provisioningserver.driver import OperatingSystemRegistry |
1250 | +from provisioningserver.driver.os_ubuntu import UbuntuOS |
1251 | |
1252 | # We have a limited number of public keys: |
1253 | # src/maasserver/tests/data/test_rsa{0, 1, 2, 3, 4}.pub |
1254 | @@ -717,7 +717,8 @@ |
1255 | if boot_source is None: |
1256 | boot_source = self.make_boot_source() |
1257 | if release is None: |
1258 | - release = self.getRandomChoice(DISTRO_SERIES_CHOICES) |
1259 | + ubuntu_os = UbuntuOS() |
1260 | + release = self.getRandomRelease(ubuntu_os) |
1261 | if arches is None: |
1262 | arch_count = random.randint(1, 10) |
1263 | arches = [self.make_name("arch") for i in range(arch_count)] |
1264 | |
1265 | === modified file 'src/maasserver/tests/test_api_boot_source_selections.py' |
1266 | --- src/maasserver/tests/test_api_boot_source_selections.py 2014-05-27 13:53:21 +0000 |
1267 | +++ src/maasserver/tests/test_api_boot_source_selections.py 2014-05-29 18:21:27 +0000 |
1268 | @@ -19,11 +19,11 @@ |
1269 | |
1270 | from django.core.urlresolvers import reverse |
1271 | from maasserver.api import DISPLAYED_BOOTSOURCESELECTION_FIELDS |
1272 | -from maasserver.enum import DISTRO_SERIES_CHOICES |
1273 | from maasserver.models import BootSourceSelection |
1274 | from maasserver.testing import reload_object |
1275 | from maasserver.testing.api import APITestCase |
1276 | from maasserver.testing.factory import factory |
1277 | +from provisioningserver.driver.os_ubuntu import UbuntuOS |
1278 | from testtools.matchers import MatchesStructure |
1279 | |
1280 | |
1281 | @@ -98,8 +98,8 @@ |
1282 | def test_PUT_updates_boot_source_selection(self): |
1283 | self.become_admin() |
1284 | boot_source_selection = factory.make_boot_source_selection() |
1285 | - new_release = factory.getRandomChoice( |
1286 | - DISTRO_SERIES_CHOICES) |
1287 | + ubuntu_os = UbuntuOS() |
1288 | + new_release = factory.getRandomRelease(ubuntu_os) |
1289 | new_values = { |
1290 | 'release': new_release, |
1291 | 'arches': [factory.make_name('arch'), factory.make_name('arch')], |
1292 | @@ -160,8 +160,8 @@ |
1293 | def test_POST_creates_boot_source_selection(self): |
1294 | self.become_admin() |
1295 | boot_source = factory.make_boot_source() |
1296 | - new_release = factory.getRandomChoice( |
1297 | - DISTRO_SERIES_CHOICES) |
1298 | + ubuntu_os = UbuntuOS() |
1299 | + new_release = factory.getRandomRelease(ubuntu_os) |
1300 | params = { |
1301 | 'release': new_release, |
1302 | 'arches': [factory.make_name('arch'), factory.make_name('arch')], |
1303 | @@ -182,8 +182,8 @@ |
1304 | |
1305 | def test_POST_requires_admin(self): |
1306 | boot_source = factory.make_boot_source() |
1307 | - new_release = factory.getRandomChoice( |
1308 | - DISTRO_SERIES_CHOICES) |
1309 | + ubuntu_os = UbuntuOS() |
1310 | + new_release = factory.getRandomRelease(ubuntu_os) |
1311 | params = { |
1312 | 'release': new_release, |
1313 | 'arches': [factory.make_name('arch'), factory.make_name('arch')], |
1314 | |
1315 | === modified file 'src/maasserver/tests/test_api_node.py' |
1316 | --- src/maasserver/tests/test_api_node.py 2014-05-29 18:21:27 +0000 |
1317 | +++ src/maasserver/tests/test_api_node.py 2014-05-28 19:06:28 +0000 |
1318 | @@ -48,7 +48,6 @@ |
1319 | NodeUserData, |
1320 | ) |
1321 | from metadataserver.nodeinituser import get_node_init_user |
1322 | -from provisioningserver.driver.os_ubuntu import UbuntuOS |
1323 | |
1324 | |
1325 | class NodeAnonAPITest(MAASServerTestCase): |
1326 | @@ -222,7 +221,6 @@ |
1327 | self.assertEqual( |
1328 | node.system_id, json.loads(response.content)['system_id']) |
1329 | |
1330 | -<<<<<<< TREE |
1331 | def test_POST_start_sets_osystem_and_distro_series(self): |
1332 | node = factory.make_node( |
1333 | owner=self.logged_in_user, mac=True, |
1334 | @@ -230,91 +228,18 @@ |
1335 | architecture=make_usable_architecture(self)) |
1336 | osystem = make_usable_osystem(self) |
1337 | distro_series = factory.getRandomRelease(osystem) |
1338 | -======= |
1339 | - def test_POST_start_sets_osystem(self): |
1340 | - node = factory.make_node( |
1341 | - owner=self.logged_in_user, mac=True, |
1342 | - power_type='ether_wake') |
1343 | - osystem = factory.getRandomOS() |
1344 | - response = self.client.post( |
1345 | - self.get_node_uri(node), |
1346 | - {'op': 'start', 'osystem': osystem.name}) |
1347 | - self.assertEqual( |
1348 | - (httplib.OK, node.system_id), |
1349 | - (response.status_code, json.loads(response.content)['system_id'])) |
1350 | - self.assertEqual( |
1351 | - osystem.name, reload_object(node).osystem) |
1352 | - |
1353 | - def test_POST_start_accepts_os(self): |
1354 | - node = factory.make_node( |
1355 | - owner=self.logged_in_user, mac=True, |
1356 | - power_type='ether_wake') |
1357 | - osystem = factory.getRandomOS() |
1358 | - response = self.client.post( |
1359 | - self.get_node_uri(node), |
1360 | - {'op': 'start', 'os': osystem.name}) |
1361 | - self.assertEqual( |
1362 | - (httplib.OK, node.system_id), |
1363 | - (response.status_code, json.loads(response.content)['system_id'])) |
1364 | - self.assertEqual( |
1365 | - osystem.name, reload_object(node).osystem) |
1366 | - |
1367 | - def test_POST_start_sets_osystem_and_distro_series(self): |
1368 | - node = factory.make_node( |
1369 | - owner=self.logged_in_user, mac=True, |
1370 | - power_type='ether_wake') |
1371 | - osystem = factory.getRandomOS() |
1372 | - distro_series = factory.getRandomRelease(osystem) |
1373 | - response = self.client.post( |
1374 | - self.get_node_uri(node), { |
1375 | - 'op': 'start', |
1376 | - 'osystem': osystem.name, |
1377 | - 'distro_series': distro_series |
1378 | - }) |
1379 | - self.assertEqual( |
1380 | - (httplib.OK, node.system_id), |
1381 | - (response.status_code, json.loads(response.content)['system_id'])) |
1382 | - self.assertEqual( |
1383 | - osystem.name, reload_object(node).osystem) |
1384 | - self.assertEqual( |
1385 | - distro_series, reload_object(node).distro_series) |
1386 | - |
1387 | - def test_POST_start_sets_distro_series_defaults_ubuntu(self): |
1388 | - node = factory.make_node( |
1389 | - owner=self.logged_in_user, mac=True, |
1390 | - power_type='ether_wake') |
1391 | - osystem = UbuntuOS() |
1392 | - distro_series = factory.getRandomRelease(osystem) |
1393 | ->>>>>>> MERGE-SOURCE |
1394 | - response = self.client.post( |
1395 | - self.get_node_uri(node), { |
1396 | - 'op': 'start', |
1397 | - 'distro_series': distro_series |
1398 | - }) |
1399 | - self.assertEqual( |
1400 | - (httplib.OK, node.system_id), |
1401 | - (response.status_code, json.loads(response.content)['system_id'])) |
1402 | - self.assertEqual( |
1403 | - osystem.name, reload_object(node).osystem) |
1404 | - self.assertEqual( |
1405 | - distro_series, reload_object(node).distro_series) |
1406 | - |
1407 | - def test_POST_start_validates_osystem(self): |
1408 | - node = factory.make_node( |
1409 | - owner=self.logged_in_user, mac=True, |
1410 | - power_type='ether_wake') |
1411 | - invalid_osystem = factory.getRandomString() |
1412 | - response = self.client.post( |
1413 | - self.get_node_uri(node), |
1414 | - {'op': 'start', 'osystem': invalid_osystem}) |
1415 | - self.assertEqual( |
1416 | - ( |
1417 | - httplib.BAD_REQUEST, |
1418 | - {'osystem': [ |
1419 | - "Value u'%s' is not a valid choice." % |
1420 | - invalid_osystem]} |
1421 | - ), |
1422 | - (response.status_code, json.loads(response.content))) |
1423 | + response = self.client.post( |
1424 | + self.get_node_uri(node), { |
1425 | + 'op': 'start', |
1426 | + 'distro_series': distro_series |
1427 | + }) |
1428 | + self.assertEqual( |
1429 | + (httplib.OK, node.system_id), |
1430 | + (response.status_code, json.loads(response.content)['system_id'])) |
1431 | + self.assertEqual( |
1432 | + osystem.name, reload_object(node).osystem) |
1433 | + self.assertEqual( |
1434 | + distro_series, reload_object(node).distro_series) |
1435 | |
1436 | def test_POST_start_validates_distro_series(self): |
1437 | node = factory.make_node( |
1438 | |
1439 | === modified file 'src/maasserver/tests/test_api_pxeconfig.py' |
1440 | --- src/maasserver/tests/test_api_pxeconfig.py 2014-05-29 18:21:27 +0000 |
1441 | +++ src/maasserver/tests/test_api_pxeconfig.py 2014-05-29 17:03:20 +0000 |
1442 | @@ -310,7 +310,6 @@ |
1443 | node.use_fastpath_installer() |
1444 | for name, value in parameters.items(): |
1445 | setattr(node, name, value) |
1446 | -<<<<<<< TREE |
1447 | osystem = node.get_osystem() |
1448 | series = node.get_distro_series() |
1449 | arch, subarch = node.architecture.split('/') |
1450 | @@ -334,15 +333,6 @@ |
1451 | 'install', |
1452 | api.get_boot_purpose( |
1453 | node, node_os, arch, subarch, node_series, None)) |
1454 | -======= |
1455 | - osystem = node.get_osystem() |
1456 | - series = node.get_distro_series() |
1457 | - arch, subarch = node.architecture.split('/') |
1458 | - self.assertEqual( |
1459 | - purpose, |
1460 | - api.get_boot_purpose( |
1461 | - node, osystem, arch, subarch, series, None)) |
1462 | ->>>>>>> MERGE-SOURCE |
1463 | |
1464 | def test_pxeconfig_uses_boot_purpose(self): |
1465 | fake_boot_purpose = factory.make_name("purpose") |
1466 | |
1467 | === modified file 'src/maasserver/tests/test_compose_preseed.py' |
1468 | --- src/maasserver/tests/test_compose_preseed.py 2013-10-07 09:12:40 +0000 |
1469 | +++ src/maasserver/tests/test_compose_preseed.py 2014-05-29 18:21:27 +0000 |
1470 | @@ -17,8 +17,10 @@ |
1471 | from maasserver.compose_preseed import compose_preseed |
1472 | from maasserver.enum import NODE_STATUS |
1473 | from maasserver.testing.factory import factory |
1474 | +from maasserver.testing.osystems import make_usable_osystem |
1475 | from maasserver.testing.testcase import MAASServerTestCase |
1476 | from maasserver.utils import absolute_reverse |
1477 | +from maastesting.matchers import MockCalledOnceWith |
1478 | from metadataserver.models import NodeKey |
1479 | from testtools.matchers import ( |
1480 | KeysEqual, |
1481 | @@ -110,3 +112,16 @@ |
1482 | self.assertEqual( |
1483 | absolute_reverse('curtin-metadata'), |
1484 | preseed['datasource']['MAAS']['metadata_url']) |
1485 | + |
1486 | + def test_compose_preseed_with_osystem_compose_preseed(self): |
1487 | + osystem = make_usable_osystem(self) |
1488 | + mock_compose = self.patch(osystem, 'compose_preseed') |
1489 | + node = factory.make_node( |
1490 | + osystem=osystem.name, status=NODE_STATUS.READY) |
1491 | + |
1492 | + token = NodeKey.objects.get_token_for_node(node) |
1493 | + url = absolute_reverse('metadata') |
1494 | + compose_preseed(node) |
1495 | + self.assertThat( |
1496 | + mock_compose, |
1497 | + MockCalledOnceWith(node, token, url)) |
1498 | |
1499 | === modified file 'src/maasserver/tests/test_forms.py' |
1500 | --- src/maasserver/tests/test_forms.py 2014-05-29 18:21:27 +0000 |
1501 | +++ src/maasserver/tests/test_forms.py 2014-05-29 18:21:27 +0000 |
1502 | @@ -29,7 +29,6 @@ |
1503 | from django.http import QueryDict |
1504 | from maasserver.clusterrpc.power_parameters import get_power_type_choices |
1505 | from maasserver.enum import ( |
1506 | - DISTRO_SERIES_CHOICES, |
1507 | NODE_STATUS, |
1508 | NODEGROUP_STATUS, |
1509 | NODEGROUPINTERFACE_MANAGEMENT, |
1510 | @@ -99,11 +98,6 @@ |
1511 | make_usable_architecture, |
1512 | patch_usable_architectures, |
1513 | ) |
1514 | -from maasserver.testing.osystems import ( |
1515 | - make_osystem_with_releases, |
1516 | - make_usable_osystem, |
1517 | - patch_usable_osystems, |
1518 | - ) |
1519 | from maasserver.testing.factory import factory |
1520 | from maasserver.testing.osystems import ( |
1521 | make_osystem_with_releases, |
1522 | @@ -117,6 +111,7 @@ |
1523 | from metadataserver.models import CommissioningScript |
1524 | from netaddr import IPNetwork |
1525 | from provisioningserver import tasks |
1526 | +from provisioningserver.driver.os_ubuntu import UbuntuOS |
1527 | from testtools import TestCase |
1528 | from testtools.matchers import ( |
1529 | AllMatch, |
1530 | @@ -153,14 +148,9 @@ |
1531 | release = factory.make_name('release') |
1532 | for purpose in ['install', 'commissioning']: |
1533 | factory.make_boot_image( |
1534 | -<<<<<<< TREE |
1535 | nodegroup=nodegroup, osystem=osystem, architecture=arch, |
1536 | subarchitecture=subarchitecture, release=release, |
1537 | purpose=purpose) |
1538 | -======= |
1539 | - nodegroup=nodegroup, osystem=osystem, architecture=arch, |
1540 | - subarchitecture=subarchitecture, release=release, purpose=purpose) |
1541 | ->>>>>>> MERGE-SOURCE |
1542 | |
1543 | def test_initialize_node_group_leaves_nodegroup_reference_intact(self): |
1544 | preselected_nodegroup = factory.make_node_group() |
1545 | @@ -223,131 +213,67 @@ |
1546 | arches = [factory.make_name('arch') for _ in range(5)] |
1547 | self.assertEqual(arches[0], pick_default_architecture(arches)) |
1548 | |
1549 | -<<<<<<< TREE |
1550 | - def test_list_all_usable_osystems_combines_nodegroups(self): |
1551 | - osystem_names = [factory.make_name('os') for _ in range(3)] |
1552 | - expected = [] |
1553 | - for name in osystem_names: |
1554 | - self.make_usable_boot_images(osystem=name) |
1555 | - expected.append(make_usable_osystem(self, name)) |
1556 | - self.assertItemsEqual(expected, list_all_usable_osystems()) |
1557 | - |
1558 | - def test_list_all_usable_osystems_sorts_output(self): |
1559 | - osystem_names = [factory.make_name('os') for _ in range(3)] |
1560 | - expected = [] |
1561 | - for name in osystem_names: |
1562 | - self.make_usable_boot_images(osystem=name) |
1563 | - expected.append(make_usable_osystem(self, name)) |
1564 | - expected = sorted(expected, key=lambda osystem: osystem.title) |
1565 | - self.assertEqual(expected, list_all_usable_osystems()) |
1566 | - |
1567 | - def test_list_all_usable_osystems_returns_no_duplicates(self): |
1568 | - os_name = factory.make_name('os') |
1569 | - self.make_usable_boot_images(osystem=os_name) |
1570 | - self.make_usable_boot_images(osystem=os_name) |
1571 | - osystem = make_usable_osystem(self, os_name) |
1572 | - self.assertEqual( |
1573 | - [osystem], list_all_usable_osystems()) |
1574 | - |
1575 | - def test_list_all_usable_releases_combines_nodegroups(self): |
1576 | - expected = {} |
1577 | - osystems = [] |
1578 | - os_names = [factory.make_name('os') for _ in range(3)] |
1579 | - for name in os_names: |
1580 | - releases = [factory.make_name('release') for _ in range(3)] |
1581 | - for release in releases: |
1582 | - self.make_usable_boot_images(osystem=name, release=release) |
1583 | - osystems.append( |
1584 | - make_usable_osystem(self, name, releases=releases)) |
1585 | - expected[name] = releases |
1586 | - self.assertItemsEqual(expected, list_all_usable_releases(osystems)) |
1587 | - |
1588 | - def test_list_all_usable_releases_sorts_output(self): |
1589 | - expected = {} |
1590 | - osystems = [] |
1591 | - os_names = [factory.make_name('os') for _ in range(3)] |
1592 | - for name in os_names: |
1593 | - releases = [factory.make_name('release') for _ in range(3)] |
1594 | - for release in releases: |
1595 | - self.make_usable_boot_images(osystem=name, release=release) |
1596 | - osystems.append( |
1597 | - make_usable_osystem(self, name, releases=releases)) |
1598 | - expected[name] = sorted(releases) |
1599 | - self.assertEqual(expected, list_all_usable_releases(osystems)) |
1600 | - |
1601 | - def test_list_all_usable_releases_returns_no_duplicates(self): |
1602 | - os_name = factory.make_name('os') |
1603 | - release = factory.make_name('release') |
1604 | - self.make_usable_boot_images(osystem=os_name, release=release) |
1605 | - self.make_usable_boot_images(osystem=os_name, release=release) |
1606 | - osystem = make_usable_osystem(self, os_name, releases=[release]) |
1607 | - expected = {} |
1608 | - expected[os_name] = [release] |
1609 | - self.assertEqual(expected, list_all_usable_releases([osystem])) |
1610 | - |
1611 | -======= |
1612 | - def test_list_all_usable_osystems_combines_nodegroups(self): |
1613 | - osystem_names = [factory.make_name('os') for _ in range(3)] |
1614 | - expected = [] |
1615 | - for name in osystem_names: |
1616 | - self.make_usable_boot_images(osystem=name) |
1617 | - expected.append(make_usable_osystem(self, name)) |
1618 | - self.assertItemsEqual(expected, list_all_usable_osystems()) |
1619 | - |
1620 | - def test_list_all_usable_osystems_sorts_output(self): |
1621 | - osystem_names = [factory.make_name('os') for _ in range(3)] |
1622 | - expected = [] |
1623 | - for name in osystem_names: |
1624 | - self.make_usable_boot_images(osystem=name) |
1625 | - expected.append(make_usable_osystem(self, name)) |
1626 | - expected = sorted(expected, key=lambda osystem: osystem.title) |
1627 | - self.assertEqual(expected, list_all_usable_osystems()) |
1628 | - |
1629 | - def test_list_all_usable_osystems_returns_no_duplicates(self): |
1630 | - os_name = factory.make_name('os') |
1631 | - self.make_usable_boot_images(osystem=os_name) |
1632 | - self.make_usable_boot_images(osystem=os_name) |
1633 | - osystem = make_usable_osystem(self, os_name) |
1634 | - self.assertEqual( |
1635 | - [osystem], list_all_usable_osystems()) |
1636 | - |
1637 | - def test_list_all_usable_releases_combines_nodegroups(self): |
1638 | - expected = {} |
1639 | - osystems = [] |
1640 | - os_names = [factory.make_name('os') for _ in range(3)] |
1641 | - for name in os_names: |
1642 | - releases = [factory.make_name('release') for _ in range(3)] |
1643 | - for release in releases: |
1644 | - self.make_usable_boot_images(osystem=name, release=release) |
1645 | - osystems.append( |
1646 | - make_usable_osystem(self, name, releases=releases)) |
1647 | - expected[name] = releases |
1648 | - self.assertItemsEqual(expected, list_all_usable_releases(osystems)) |
1649 | - |
1650 | - def test_list_all_usable_releases_sorts_output(self): |
1651 | - expected = {} |
1652 | - osystems = [] |
1653 | - os_names = [factory.make_name('os') for _ in range(3)] |
1654 | - for name in os_names: |
1655 | - releases = [factory.make_name('release') for _ in range(3)] |
1656 | - for release in releases: |
1657 | - self.make_usable_boot_images(osystem=name, release=release) |
1658 | - osystems.append( |
1659 | - make_usable_osystem(self, name, releases=releases)) |
1660 | - expected[name] = sorted(releases) |
1661 | - self.assertEqual(expected, list_all_usable_releases(osystems)) |
1662 | - |
1663 | - def test_list_all_usable_releases_returns_no_duplicates(self): |
1664 | - os_name = factory.make_name('os') |
1665 | - release = factory.make_name('release') |
1666 | - self.make_usable_boot_images(osystem=os_name, release=release) |
1667 | - self.make_usable_boot_images(osystem=os_name, release=release) |
1668 | - osystem = make_usable_osystem(self, os_name, releases=[release]) |
1669 | - expected = {} |
1670 | - expected[os_name] = [release] |
1671 | - self.assertEqual(expected, list_all_usable_releases([osystem])) |
1672 | - |
1673 | ->>>>>>> MERGE-SOURCE |
1674 | + def test_list_all_usable_osystems_combines_nodegroups(self): |
1675 | + osystem_names = [factory.make_name('os') for _ in range(3)] |
1676 | + expected = [] |
1677 | + for name in osystem_names: |
1678 | + self.make_usable_boot_images(osystem=name) |
1679 | + expected.append(make_usable_osystem(self, name)) |
1680 | + self.assertItemsEqual(expected, list_all_usable_osystems()) |
1681 | + |
1682 | + def test_list_all_usable_osystems_sorts_output(self): |
1683 | + osystem_names = [factory.make_name('os') for _ in range(3)] |
1684 | + expected = [] |
1685 | + for name in osystem_names: |
1686 | + self.make_usable_boot_images(osystem=name) |
1687 | + expected.append(make_usable_osystem(self, name)) |
1688 | + expected = sorted(expected, key=lambda osystem: osystem.title) |
1689 | + self.assertEqual(expected, list_all_usable_osystems()) |
1690 | + |
1691 | + def test_list_all_usable_osystems_returns_no_duplicates(self): |
1692 | + os_name = factory.make_name('os') |
1693 | + self.make_usable_boot_images(osystem=os_name) |
1694 | + self.make_usable_boot_images(osystem=os_name) |
1695 | + osystem = make_usable_osystem(self, os_name) |
1696 | + self.assertEqual( |
1697 | + [osystem], list_all_usable_osystems()) |
1698 | + |
1699 | + def test_list_all_usable_releases_combines_nodegroups(self): |
1700 | + expected = {} |
1701 | + osystems = [] |
1702 | + os_names = [factory.make_name('os') for _ in range(3)] |
1703 | + for name in os_names: |
1704 | + releases = [factory.make_name('release') for _ in range(3)] |
1705 | + for release in releases: |
1706 | + self.make_usable_boot_images(osystem=name, release=release) |
1707 | + osystems.append( |
1708 | + make_usable_osystem(self, name, releases=releases)) |
1709 | + expected[name] = releases |
1710 | + self.assertItemsEqual(expected, list_all_usable_releases(osystems)) |
1711 | + |
1712 | + def test_list_all_usable_releases_sorts_output(self): |
1713 | + expected = {} |
1714 | + osystems = [] |
1715 | + os_names = [factory.make_name('os') for _ in range(3)] |
1716 | + for name in os_names: |
1717 | + releases = [factory.make_name('release') for _ in range(3)] |
1718 | + for release in releases: |
1719 | + self.make_usable_boot_images(osystem=name, release=release) |
1720 | + osystems.append( |
1721 | + make_usable_osystem(self, name, releases=releases)) |
1722 | + expected[name] = sorted(releases) |
1723 | + self.assertEqual(expected, list_all_usable_releases(osystems)) |
1724 | + |
1725 | + def test_list_all_usable_releases_returns_no_duplicates(self): |
1726 | + os_name = factory.make_name('os') |
1727 | + release = factory.make_name('release') |
1728 | + self.make_usable_boot_images(osystem=os_name, release=release) |
1729 | + self.make_usable_boot_images(osystem=os_name, release=release) |
1730 | + osystem = make_usable_osystem(self, os_name, releases=[release]) |
1731 | + expected = {} |
1732 | + expected[os_name] = [release] |
1733 | + self.assertEqual(expected, list_all_usable_releases([osystem])) |
1734 | + |
1735 | def test_remove_None_values_removes_None_values_in_dict(self): |
1736 | random_input = factory.getRandomString() |
1737 | self.assertEqual( |
1738 | @@ -2136,8 +2062,8 @@ |
1739 | |
1740 | def test_edits_boot_source_selection_object(self): |
1741 | boot_source_selection = factory.make_boot_source_selection() |
1742 | - new_release = factory.getRandomChoice( |
1743 | - DISTRO_SERIES_CHOICES) |
1744 | + ubuntu_os = UbuntuOS() |
1745 | + new_release = factory.getRandomRelease(ubuntu_os) |
1746 | params = { |
1747 | 'release': new_release, |
1748 | 'arches': [factory.make_name('arch'), factory.make_name('arch')], |
1749 | @@ -2154,8 +2080,8 @@ |
1750 | |
1751 | def test_creates_boot_source_selection_object(self): |
1752 | boot_source = factory.make_boot_source() |
1753 | - new_release = factory.getRandomChoice( |
1754 | - DISTRO_SERIES_CHOICES) |
1755 | + ubuntu_os = UbuntuOS() |
1756 | + new_release = factory.getRandomRelease(ubuntu_os) |
1757 | params = { |
1758 | 'release': new_release, |
1759 | 'arches': [factory.make_name('arch'), factory.make_name('arch')], |
1760 | |
1761 | === modified file 'src/maasserver/views/tests/test_settings.py' |
1762 | --- src/maasserver/views/tests/test_settings.py 2014-05-29 18:21:27 +0000 |
1763 | +++ src/maasserver/views/tests/test_settings.py 2014-05-15 19:48:27 +0000 |
1764 | @@ -20,10 +20,6 @@ |
1765 | from django.contrib.auth.models import User |
1766 | from django.core.urlresolvers import reverse |
1767 | from lxml.html import fromstring |
1768 | -<<<<<<< TREE |
1769 | -======= |
1770 | -from maasserver.models.config import DEFAULT_OS |
1771 | ->>>>>>> MERGE-SOURCE |
1772 | from maasserver.models import ( |
1773 | Config, |
1774 | UserProfile, |
1775 | |
1776 | === modified file 'src/provisioningserver/driver/__init__.py' |
1777 | --- src/provisioningserver/driver/__init__.py 2014-05-15 17:27:53 +0000 |
1778 | +++ src/provisioningserver/driver/__init__.py 2014-05-29 18:21:27 +0000 |
1779 | @@ -158,6 +158,18 @@ |
1780 | :returns: list of supported purposes |
1781 | """ |
1782 | |
1783 | + def compose_preseed(self, node, token, metadata_url): |
1784 | + """Composes the preseed for the given node. |
1785 | + |
1786 | + :param node: Node preseed needs generating. |
1787 | + :param token: OAuth token for url. |
1788 | + :param metadata_url: Metdata url for node. |
1789 | + :returns: Preseed for node. |
1790 | + :raise: |
1791 | + NotImplementedError: doesn't implement a custom preseed |
1792 | + """ |
1793 | + raise NotImplementedError() |
1794 | + |
1795 | |
1796 | class HardwareDiscoverContext: |
1797 |
Thanks for doing this work. It's good to see those old hard-coded enums go at last. Unfortunately there's that pervasive problem of the registries living in a different process, possibly on a different machine. We should probably have been a lot clearer about that.
This branch answers a question I asked on one of your other merge proposals: does OperatingSystem really need to be a base class? If it's going to have a method for generating preseed data, and if it really can't be addressed with simple text templating, then it probably does.
An uncomfortable number of dimensional axes are coming together in compose_preseed: commissioning vs. deployment, operating systems with and without preseed code, conventional installer or fastpath (or whatever install options some other OS might have in their place). Gotta nip that sort of burgeoning complexity in the bud, or it will eventually stifle further development. Fight it with fire because by nature it will just keep growing.
So, assuming that we really do need OS-specific preseed-generating code, I would look for a way to move the Ubuntu-specific parts into the Ubuntu OperatingSystem. Or if most-but-not-all operating systems can generate their preseeds using templates, delegate most operating systems' implementations to a single template-based helper. (If templates can reasonably do the job for all operating systems we have in mind, of course, just use templating and stick with generic code.)
The hasattr check is best avoided, I think. If you're going to rely on dynamic dispatch for this, go all the way. You can accommodate some kind of sensible default in the dynamic method's definition, but an unconditional code path is going to be more manageable than special-casing in the code. Conditionals are our profession's equivalent of moving parts: they introduce complexity and risk.
There also seems to be a tacit rule in there that the commissioning OS must be, or work like, Ubuntu. That is worth being very explicit about. This isn't just a series of "if" statements to make things work, it's defining how a node can or cannot be installed. The details of of how these conditions interact are crucial, and yet I don't see any tests covering things like “we generate the customary Ubuntu preseed for a commissioning node, even if it's to be deployed with a different OS.”
By the way, I do appreciate how this branch is small and focused and not thrown in with a large bag of other changes!