Merge lp:~blake-rouse/maas/license-key-form into lp:~maas-committers/maas/trunk
- license-key-form
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Blake Rouse | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 2499 | ||||
Proposed branch: | lp:~blake-rouse/maas/license-key-form | ||||
Merge into: | lp:~maas-committers/maas/trunk | ||||
Prerequisite: | lp:~blake-rouse/maas/node-effective-license-key | ||||
Diff against target: |
602 lines (+419/-48) 5 files modified
src/maasserver/forms.py (+109/-0) src/maasserver/models/licensekey.py (+12/-1) src/maasserver/models/tests/test_licensekey.py (+9/-0) src/maasserver/tests/test_forms.py (+188/-0) src/maasserver/utils/osystems.py (+101/-47) |
||||
To merge this branch: | bzr merge lp:~blake-rouse/maas/license-key-form | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Raphaël Badin (community) | Approve | ||
Review via email: mp+224663@code.launchpad.net |
Commit message
Add LicenseKeyForm for the LicenseKey model.
Description of the change
This is the third part of a series of changes to allow a global registry of license key's in MAAS. The end result will allow a user to set a license key for each operating system and series globally. When a booting operating system and series requires a license key, first the node will be check to see if a specific key has been set in the license_key field. If not then this LicenseKey model will be used to see if one is set globally for the selected operating system and series.
This branch includes the form that will be used in the WebUI and API.
Blake Rouse (blake-rouse) wrote : | # |
Quick question before I work on the fixes, question is in the inline comments.
Raphaël Badin (rvb) wrote : | # |
Replies inline…
Blake Rouse (blake-rouse) wrote : | # |
I have fixed the items you requested. I did not add any more tests for the utils/osystems.py, as I will move the tests from test_forms into utils/tests/
Raphaël Badin (rvb) wrote : | # |
A couple more remarks but nothing major. Thanks for all the work on this branch… I'm looking forward to the follow-up branch with the cleanups ;).
Blake Rouse (blake-rouse) wrote : | # |
Updated! Comment inline about the clean fields check.
Preview Diff
1 | === modified file 'src/maasserver/forms.py' |
2 | --- src/maasserver/forms.py 2014-06-27 20:02:49 +0000 |
3 | +++ src/maasserver/forms.py 2014-07-02 14:55:33 +0000 |
4 | @@ -98,6 +98,7 @@ |
5 | BootSourceSelection, |
6 | Config, |
7 | DownloadProgress, |
8 | + LicenseKey, |
9 | MACAddress, |
10 | Network, |
11 | Node, |
12 | @@ -119,6 +120,7 @@ |
13 | from maasserver.utils.osystems import ( |
14 | get_distro_series_inital, |
15 | get_release_requires_key, |
16 | + list_all_releases_requiring_keys, |
17 | list_all_usable_osystems, |
18 | list_all_usable_releases, |
19 | list_osystem_choices, |
20 | @@ -1826,3 +1828,110 @@ |
21 | if kwargs.get('commit', True): |
22 | boot_source_selection.save(*args, **kwargs) |
23 | return boot_source_selection |
24 | + |
25 | + |
26 | +class LicenseKeyForm(ModelForm): |
27 | + """Form for global license keys.""" |
28 | + |
29 | + class Meta: |
30 | + model = LicenseKey |
31 | + fields = ( |
32 | + 'osystem', |
33 | + 'distro_series', |
34 | + 'license_key', |
35 | + ) |
36 | + |
37 | + def __init__(self, *args, **kwargs): |
38 | + super(LicenseKeyForm, self).__init__(*args, **kwargs) |
39 | + self.set_up_osystem_and_distro_series_fields(kwargs.get('instance')) |
40 | + |
41 | + def set_up_osystem_and_distro_series_fields(self, instance): |
42 | + """Create the `osystem` and `distro_series` fields. |
43 | + |
44 | + This needs to be done on the fly so that we can pass a dynamic list of |
45 | + usable operating systems and distro_series. |
46 | + """ |
47 | + osystems = list_all_usable_osystems(have_images=False) |
48 | + releases = list_all_releases_requiring_keys(osystems) |
49 | + |
50 | + # Remove the operating systems that do not have any releases that |
51 | + # require license keys. Don't want them to show up in the UI or be |
52 | + # used in the API. |
53 | + osystems = [ |
54 | + osystem |
55 | + for osystem in osystems |
56 | + if osystem.name in releases |
57 | + ] |
58 | + |
59 | + os_choices = list_osystem_choices(osystems, include_default=False) |
60 | + distro_choices = list_release_choices( |
61 | + releases, include_default=False, include_latest=False, |
62 | + with_key_required=False) |
63 | + invalid_osystem_message = compose_invalid_choice_text( |
64 | + 'osystem', os_choices) |
65 | + invalid_distro_series_message = compose_invalid_choice_text( |
66 | + 'distro_series', distro_choices) |
67 | + self.fields['osystem'] = forms.ChoiceField( |
68 | + label="OS", choices=os_choices, required=True, |
69 | + error_messages={'invalid_choice': invalid_osystem_message}) |
70 | + self.fields['distro_series'] = forms.ChoiceField( |
71 | + label="Release", choices=distro_choices, required=True, |
72 | + error_messages={'invalid_choice': invalid_distro_series_message}) |
73 | + if instance is not None: |
74 | + initial_value = get_distro_series_inital( |
75 | + instance, with_key_required=False) |
76 | + if instance is not None: |
77 | + self.initial['distro_series'] = initial_value |
78 | + |
79 | + def full_clean(self): |
80 | + # When this form is used from the API, the distro_series field will |
81 | + # not be formatted correctly. This is to make it easy on the user, and |
82 | + # not have to call the api with distro_series=os/series. This occurs |
83 | + # in full_clean, so the value is correct before validation occurs on |
84 | + # the distro_series field. |
85 | + if 'distro_series' in self.data and 'osystem' in self.data: |
86 | + if '/' not in self.data['distro_series']: |
87 | + self.data['distro_series'] = '%s/%s' % ( |
88 | + self.data['osystem'], |
89 | + self.data['distro_series'], |
90 | + ) |
91 | + super(LicenseKeyForm, self).full_clean() |
92 | + |
93 | + def clean(self): |
94 | + """Validate distro_series and osystem match, and license_key is valid |
95 | + for selected operating system and series.""" |
96 | + # Get the clean_data, check that all of the fields we need are |
97 | + # present. If not then the form will error, so no reason to continue. |
98 | + cleaned_data = super(LicenseKeyForm, self).clean() |
99 | + required_fields = ['license_key', 'osystem', 'distro_series'] |
100 | + for field in required_fields: |
101 | + if field not in cleaned_data: |
102 | + return cleaned_data |
103 | + cleaned_data['distro_series'] = self.clean_osystem_distro_series_field( |
104 | + cleaned_data) |
105 | + self.validate_license_key(cleaned_data) |
106 | + return cleaned_data |
107 | + |
108 | + def clean_osystem_distro_series_field(self, cleaned_data): |
109 | + """Validate that os/distro_series matches osystem, and update the |
110 | + distro_series field, to remove the leading os/.""" |
111 | + cleaned_osystem = cleaned_data['osystem'] |
112 | + cleaned_series = cleaned_data['distro_series'] |
113 | + series_os, release = cleaned_series.split('/', 1) |
114 | + if series_os != cleaned_osystem: |
115 | + raise ValidationError( |
116 | + "%s in distro_series does not match with " |
117 | + "operating system %s" % (release, cleaned_osystem)) |
118 | + return release |
119 | + |
120 | + def validate_license_key(self, cleaned_data): |
121 | + """Validates that the license key is valid.""" |
122 | + cleaned_key = cleaned_data['license_key'] |
123 | + cleaned_osystem = cleaned_data['osystem'] |
124 | + cleaned_series = cleaned_data['distro_series'] |
125 | + os_obj = OperatingSystemRegistry.get_item(cleaned_osystem) |
126 | + if os_obj is None: |
127 | + raise ValidationError( |
128 | + "Failed to retrieve %s from os registry." % cleaned_osystem) |
129 | + elif not os_obj.validate_license_key(cleaned_series, cleaned_key): |
130 | + raise ValidationError("Invalid license key.") |
131 | |
132 | === modified file 'src/maasserver/models/licensekey.py' |
133 | --- src/maasserver/models/licensekey.py 2014-06-26 14:09:40 +0000 |
134 | +++ src/maasserver/models/licensekey.py 2014-07-02 14:55:33 +0000 |
135 | @@ -74,10 +74,21 @@ |
136 | distro_series = CharField(max_length=255, blank=False) |
137 | |
138 | # License key for the osystem/distro_series combo. |
139 | - license_key = CharField(max_length=255, blank=False) |
140 | + license_key = CharField( |
141 | + max_length=255, blank=False, verbose_name="License Key", help_text=( |
142 | + "License key for operating system")) |
143 | |
144 | def __repr__(self): |
145 | return "<LicenseKey %s/%s>" % ( |
146 | self.osystem, |
147 | self.distro_series, |
148 | ) |
149 | + |
150 | + def unique_error_message(self, model_class, unique_check): |
151 | + if unique_check == ('osystem', 'distro_series'): |
152 | + return '%s %s' % ( |
153 | + "License key with this operating system and distro series", |
154 | + "already exists.", |
155 | + ) |
156 | + return super( |
157 | + LicenseKey, self).unique_error_message(model_class, unique_check) |
158 | |
159 | === modified file 'src/maasserver/models/tests/test_licensekey.py' |
160 | --- src/maasserver/models/tests/test_licensekey.py 2014-06-26 13:17:00 +0000 |
161 | +++ src/maasserver/models/tests/test_licensekey.py 2014-07-02 14:55:33 +0000 |
162 | @@ -14,6 +14,7 @@ |
163 | __metaclass__ = type |
164 | __all__ = [] |
165 | |
166 | +from django.db import IntegrityError |
167 | from maasserver.models import LicenseKey |
168 | from maasserver.testing.factory import factory |
169 | from maasserver.testing.testcase import MAASServerTestCase |
170 | @@ -46,3 +47,11 @@ |
171 | self.assertFalse( |
172 | LicenseKey.objects.has_license_key( |
173 | osystem, series)) |
174 | + |
175 | + def test_errors_on_not_unique(self): |
176 | + key = factory.make_license_key() |
177 | + new_key = factory.make_name('key') |
178 | + self.assertRaises( |
179 | + IntegrityError, |
180 | + LicenseKey.objects.create, osystem=key.osystem, |
181 | + distro_series=key.distro_series, license_key=new_key) |
182 | |
183 | === modified file 'src/maasserver/tests/test_forms.py' |
184 | --- src/maasserver/tests/test_forms.py 2014-07-02 07:05:24 +0000 |
185 | +++ src/maasserver/tests/test_forms.py 2014-07-02 14:55:33 +0000 |
186 | @@ -56,6 +56,7 @@ |
187 | initialize_node_group, |
188 | InstanceListField, |
189 | INTERFACES_VALIDATION_ERROR_MESSAGE, |
190 | + LicenseKeyForm, |
191 | list_all_usable_architectures, |
192 | list_all_usable_osystems, |
193 | list_all_usable_releases, |
194 | @@ -83,6 +84,7 @@ |
195 | ) |
196 | from maasserver.models import ( |
197 | Config, |
198 | + LicenseKey, |
199 | MACAddress, |
200 | Network, |
201 | Node, |
202 | @@ -120,6 +122,7 @@ |
203 | IPRange, |
204 | ) |
205 | from provisioningserver import tasks |
206 | +from provisioningserver.drivers.osystem import OperatingSystemRegistry |
207 | from provisioningserver.drivers.osystem.ubuntu import UbuntuOS |
208 | from testtools import TestCase |
209 | from testtools.matchers import ( |
210 | @@ -2253,3 +2256,188 @@ |
211 | } |
212 | form = DeployForm(data=params) |
213 | self.assertTrue(form.is_valid()) |
214 | + |
215 | + |
216 | +class TestLicenseKeyForm(MAASServerTestCase): |
217 | + """Tests for `LicenseKeyForm`.""" |
218 | + |
219 | + def make_os_with_license_key(self, osystem=None, has_key=True, |
220 | + key_is_valid=True, patch_registry=True): |
221 | + """Makes a fake operating system that requires a license key, and |
222 | + validates to key_is_valid.""" |
223 | + if osystem is None: |
224 | + osystem = make_usable_osystem(self) |
225 | + self.patch(osystem, 'requires_license_key').return_value = has_key |
226 | + self.patch(osystem, 'validate_license_key').return_value = key_is_valid |
227 | + if patch_registry: |
228 | + self.patch( |
229 | + OperatingSystemRegistry, 'get_item').return_value = osystem |
230 | + return osystem |
231 | + |
232 | + def make_all_osystems_require_key(self, key_is_valid=True): |
233 | + """Patches all operating systems in the registry, to require a key.""" |
234 | + for _, osystem in OperatingSystemRegistry: |
235 | + self.patch( |
236 | + osystem, 'requires_license_key').return_value = True |
237 | + self.patch( |
238 | + osystem, 'validate_license_key').return_value = key_is_valid |
239 | + |
240 | + def make_only_one_osystem_require_key(self, key_is_valid=True): |
241 | + """Patches a random operating system from the registry, to require a |
242 | + key. Patches the remaining operating systems to not require a key.""" |
243 | + osystem = factory.getRandomOS() |
244 | + self.patch( |
245 | + osystem, 'requires_license_key').return_value = True |
246 | + self.patch( |
247 | + osystem, 'validate_license_key').return_value = key_is_valid |
248 | + for _, other_osystem in OperatingSystemRegistry: |
249 | + if osystem == other_osystem: |
250 | + continue |
251 | + self.patch( |
252 | + other_osystem, 'requires_license_key').return_value = False |
253 | + return osystem |
254 | + |
255 | + def test_creates_license_key(self): |
256 | + osystem = self.make_os_with_license_key() |
257 | + series = factory.getRandomRelease(osystem) |
258 | + key = factory.make_name('key') |
259 | + definition = { |
260 | + 'osystem': osystem.name, |
261 | + 'distro_series': series, |
262 | + 'license_key': key, |
263 | + } |
264 | + data = definition.copy() |
265 | + data['distro_series'] = '%s/%s' % (osystem.name, series) |
266 | + form = LicenseKeyForm(data=data) |
267 | + form.save() |
268 | + license_key_obj = LicenseKey.objects.get( |
269 | + osystem=osystem.name, distro_series=series) |
270 | + self.assertAttributes(license_key_obj, definition) |
271 | + |
272 | + def test_updates_license_key(self): |
273 | + osystem = self.make_os_with_license_key() |
274 | + series = factory.getRandomRelease(osystem) |
275 | + license_key = factory.make_license_key( |
276 | + osystem=osystem.name, distro_series=series, |
277 | + license_key=factory.make_name('key')) |
278 | + new_key = factory.make_name('key') |
279 | + form = LicenseKeyForm( |
280 | + data={'license_key': new_key}, instance=license_key) |
281 | + form.save() |
282 | + license_key = reload_object(license_key) |
283 | + self.assertEqual(new_key, license_key.license_key) |
284 | + |
285 | + def test_validates_license_key(self): |
286 | + osystem = self.make_os_with_license_key(key_is_valid=False) |
287 | + series = factory.getRandomRelease(osystem) |
288 | + license_key = factory.make_license_key( |
289 | + osystem=osystem.name, distro_series=series, |
290 | + license_key=factory.make_name('key')) |
291 | + new_key = factory.make_name('key') |
292 | + form = LicenseKeyForm( |
293 | + data={'license_key': new_key}, instance=license_key) |
294 | + self.assertFalse(form.is_valid(), form.errors) |
295 | + self.assertEqual( |
296 | + {'__all__': ['Invalid license key.']}, |
297 | + form.errors) |
298 | + |
299 | + def test_handles_missing_osystem_in_distro_series(self): |
300 | + osystem = self.make_os_with_license_key() |
301 | + series = factory.getRandomRelease(osystem) |
302 | + key = factory.make_name('key') |
303 | + definition = { |
304 | + 'osystem': osystem.name, |
305 | + 'distro_series': series, |
306 | + 'license_key': key, |
307 | + } |
308 | + form = LicenseKeyForm(data=definition.copy()) |
309 | + form.save() |
310 | + license_key_obj = LicenseKey.objects.get( |
311 | + osystem=osystem.name, distro_series=series) |
312 | + self.assertAttributes(license_key_obj, definition) |
313 | + |
314 | + def test_requires_all_fields(self): |
315 | + form = LicenseKeyForm(data={}) |
316 | + self.assertFalse(form.is_valid(), form.errors) |
317 | + self.assertItemsEqual( |
318 | + ['osystem', 'distro_series', 'license_key'], |
319 | + form.errors.keys()) |
320 | + |
321 | + def test_errors_on_not_unique(self): |
322 | + osystem = self.make_os_with_license_key() |
323 | + series = factory.getRandomRelease(osystem) |
324 | + key = factory.make_name('key') |
325 | + factory.make_license_key( |
326 | + osystem=osystem.name, distro_series=series, license_key=key) |
327 | + definition = { |
328 | + 'osystem': osystem.name, |
329 | + 'distro_series': series, |
330 | + 'license_key': key, |
331 | + } |
332 | + form = LicenseKeyForm(data=definition) |
333 | + self.assertFalse(form.is_valid(), form.errors) |
334 | + self.assertEqual({ |
335 | + '__all__': ['%s %s' % ( |
336 | + "License key with this operating system and distro series", |
337 | + "already exists.")]}, |
338 | + form.errors) |
339 | + |
340 | + def test_doesnt_include_default_osystem(self): |
341 | + form = LicenseKeyForm() |
342 | + self.assertNotIn(('', 'Default OS'), form.fields['osystem'].choices) |
343 | + |
344 | + def test_includes_all_osystems(self): |
345 | + self.make_all_osystems_require_key() |
346 | + osystems = [osystem for _, osystem in OperatingSystemRegistry] |
347 | + expected = [ |
348 | + (osystem.name, osystem.title) |
349 | + for osystem in osystems |
350 | + ] |
351 | + form = LicenseKeyForm() |
352 | + self.assertItemsEqual(expected, form.fields['osystem'].choices) |
353 | + |
354 | + def test_includes_all_osystems_sorted(self): |
355 | + self.make_all_osystems_require_key() |
356 | + osystems = [osystem for _, osystem in OperatingSystemRegistry] |
357 | + osystems = sorted(osystems, key=lambda osystem: osystem.title) |
358 | + expected = [ |
359 | + (osystem.name, osystem.title) |
360 | + for osystem in osystems |
361 | + ] |
362 | + form = LicenseKeyForm() |
363 | + self.assertEqual(expected, form.fields['osystem'].choices) |
364 | + |
365 | + def test_includes_only_osystems_that_require_license_keys(self): |
366 | + osystem = self.make_only_one_osystem_require_key() |
367 | + expected = [(osystem.name, osystem.title)] |
368 | + form = LicenseKeyForm() |
369 | + self.assertEquals(expected, form.fields['osystem'].choices) |
370 | + |
371 | + def test_doesnt_include_default_distro_series(self): |
372 | + form = LicenseKeyForm() |
373 | + self.assertNotIn( |
374 | + ('', 'Default OS Release'), form.fields['distro_series'].choices) |
375 | + |
376 | + def test_includes_all_distro_series(self): |
377 | + self.make_all_osystems_require_key() |
378 | + osystems = [osystem for _, osystem in OperatingSystemRegistry] |
379 | + expected = [] |
380 | + for osystem in osystems: |
381 | + releases = osystem.get_supported_releases() |
382 | + for name, title in osystem.format_release_choices(releases): |
383 | + expected.append(( |
384 | + '%s/%s' % (osystem.name, name), |
385 | + title |
386 | + )) |
387 | + form = LicenseKeyForm() |
388 | + self.assertItemsEqual(expected, form.fields['distro_series'].choices) |
389 | + |
390 | + def test_includes_only_distro_series_that_require_license_keys(self): |
391 | + osystem = self.make_only_one_osystem_require_key() |
392 | + releases = osystem.get_supported_releases() |
393 | + expected = [ |
394 | + ('%s/%s' % (osystem.name, name), title) |
395 | + for name, title in osystem.format_release_choices(releases) |
396 | + ] |
397 | + form = LicenseKeyForm() |
398 | + self.assertEquals(expected, form.fields['distro_series'].choices) |
399 | |
400 | === modified file 'src/maasserver/utils/osystems.py' |
401 | --- src/maasserver/utils/osystems.py 2014-06-16 14:25:56 +0000 |
402 | +++ src/maasserver/utils/osystems.py 2014-07-02 14:55:33 +0000 |
403 | @@ -14,6 +14,7 @@ |
404 | __all__ = [ |
405 | 'get_distro_series_inital', |
406 | 'get_release_requires_key', |
407 | + 'list_all_releases_requiring_keys', |
408 | 'list_all_usable_osystems', |
409 | 'list_all_usable_releases', |
410 | 'list_osystem_choices', |
411 | @@ -28,31 +29,42 @@ |
412 | from provisioningserver.drivers.osystem import OperatingSystemRegistry |
413 | |
414 | |
415 | -def list_all_usable_osystems(): |
416 | +def list_all_usable_osystems(have_images=True): |
417 | """Return all operating systems that can be used for nodes. |
418 | |
419 | - These are the operating systems for which any nodegroup has the boot images |
420 | - required to boot the node. |
421 | + :param have_images: If set to true then its only the operating systems for |
422 | + which any nodegroup has the boot images available for that operating |
423 | + system. |
424 | """ |
425 | - # The Node edit form offers all usable operating systems as options for the |
426 | - # osystem field. Not all of these may be available in the node's |
427 | - # nodegroup, but to represent that accurately in the UI would depend on |
428 | - # the currently selected nodegroup. Narrowing the options down further |
429 | - # would have to happen browser-side. |
430 | - osystems = set() |
431 | - for nodegroup in NodeGroup.objects.all(): |
432 | - osystems = osystems.union( |
433 | - BootImage.objects.get_usable_osystems(nodegroup)) |
434 | - osystems = [ |
435 | - OperatingSystemRegistry[osystem] for osystem in osystems |
436 | - if osystem in OperatingSystemRegistry |
437 | - ] |
438 | + if not have_images: |
439 | + osystems = set([osystem for _, osystem in OperatingSystemRegistry]) |
440 | + else: |
441 | + # The Node edit form offers all usable operating systems as options for |
442 | + # the osystem field. Not all of these may be available in the node's |
443 | + # nodegroup, but to represent that accurately in the UI would depend on |
444 | + # the currently selected nodegroup. Narrowing the options down further |
445 | + # would have to happen browser-side. |
446 | + osystems = set() |
447 | + for nodegroup in NodeGroup.objects.all(): |
448 | + osystems = osystems.union( |
449 | + BootImage.objects.get_usable_osystems(nodegroup)) |
450 | + osystems = [ |
451 | + OperatingSystemRegistry[osystem] for osystem in osystems |
452 | + if osystem in OperatingSystemRegistry |
453 | + ] |
454 | return sorted(osystems, key=lambda osystem: osystem.title) |
455 | |
456 | |
457 | -def list_osystem_choices(osystems): |
458 | - """Return Django "choices" list for `osystem`.""" |
459 | - choices = [('', 'Default OS')] |
460 | +def list_osystem_choices(osystems, include_default=True): |
461 | + """Return Django "choices" list for `osystem`. |
462 | + |
463 | + :param include_default: When true includes the 'Default OS' in choice |
464 | + selection. |
465 | + """ |
466 | + if include_default: |
467 | + choices = [('', 'Default OS')] |
468 | + else: |
469 | + choices = [] |
470 | choices += [ |
471 | (osystem.name, osystem.title) |
472 | for osystem in osystems |
473 | @@ -60,19 +72,42 @@ |
474 | return choices |
475 | |
476 | |
477 | -def list_all_usable_releases(osystems): |
478 | - """Return dictionary of usable `releases` for each opertaing system.""" |
479 | +def list_all_usable_releases(osystems, have_images=True): |
480 | + """Return dictionary of usable `releases` for each operating system. |
481 | + |
482 | + :param have_images: If set to true then its only the releases for |
483 | + which any nodegroup has the boot images available for that release. |
484 | + """ |
485 | distro_series = {} |
486 | nodegroups = list(NodeGroup.objects.all()) |
487 | for osystem in osystems: |
488 | releases = set() |
489 | - for nodegroup in nodegroups: |
490 | - releases = releases.union( |
491 | - BootImage.objects.get_usable_releases(nodegroup, osystem.name)) |
492 | + if not have_images: |
493 | + releases = releases.union(osystem.get_supported_releases()) |
494 | + else: |
495 | + for nodegroup in nodegroups: |
496 | + releases = releases.union( |
497 | + BootImage.objects.get_usable_releases( |
498 | + nodegroup, osystem.name)) |
499 | distro_series[osystem.name] = sorted(releases) |
500 | return distro_series |
501 | |
502 | |
503 | +def list_all_releases_requiring_keys(osystems): |
504 | + """Return dictionary of OS name mapping to `releases` that require |
505 | + license keys.""" |
506 | + distro_series = {} |
507 | + for osystem in osystems: |
508 | + releases = [ |
509 | + release |
510 | + for release in osystem.get_supported_releases() |
511 | + if osystem.requires_license_key(release) |
512 | + ] |
513 | + if len(releases) > 0: |
514 | + distro_series[osystem.name] = sorted(releases) |
515 | + return distro_series |
516 | + |
517 | + |
518 | def get_release_requires_key(osystem, release): |
519 | """Return astrisk for any release that requires |
520 | a license key. |
521 | @@ -84,39 +119,58 @@ |
522 | return '' |
523 | |
524 | |
525 | -def list_release_choices(releases): |
526 | - """Return Django "choices" list for `releases`.""" |
527 | - choices = [('', 'Default OS Release')] |
528 | +def list_release_choices(releases, include_default=True, include_latest=True, |
529 | + with_key_required=True): |
530 | + """Return Django "choices" list for `releases`. |
531 | + |
532 | + :param include_default: When true includes the 'Default OS Release' in |
533 | + choice selection. |
534 | + :param include_latest: When true includes the 'Latest OS Release' in |
535 | + choice selection. |
536 | + :param with_key_required: When true includes the release_requires_key in |
537 | + the choice. |
538 | + """ |
539 | + if include_default: |
540 | + choices = [('', 'Default OS Release')] |
541 | + else: |
542 | + choices = [] |
543 | for key, value in releases.items(): |
544 | osystem = OperatingSystemRegistry[key] |
545 | options = osystem.format_release_choices(value) |
546 | - requires_key = get_release_requires_key(osystem, '') |
547 | - choices += [( |
548 | - '%s/%s' % (osystem.name, requires_key), |
549 | - 'Latest %s Release' % osystem.title |
550 | - )] |
551 | - choices += [( |
552 | - '%s/%s%s' % ( |
553 | - osystem.name, |
554 | - name, |
555 | - get_release_requires_key(osystem, name) |
556 | - ), |
557 | - title |
558 | - ) |
559 | - for name, title in options |
560 | - ] |
561 | + if with_key_required: |
562 | + requires_key = get_release_requires_key(osystem, '') |
563 | + else: |
564 | + requires_key = '' |
565 | + if include_latest: |
566 | + choices.append(( |
567 | + '%s/%s' % (osystem.name, requires_key), |
568 | + 'Latest %s Release' % osystem.title |
569 | + )) |
570 | + for name, title in options: |
571 | + if with_key_required: |
572 | + requires_key = get_release_requires_key(osystem, name) |
573 | + else: |
574 | + requires_key = '' |
575 | + choices.append(( |
576 | + '%s/%s%s' % (osystem.name, name, requires_key), |
577 | + title |
578 | + )) |
579 | return choices |
580 | |
581 | |
582 | -def get_distro_series_inital(instance): |
583 | - """Returns the distro_series initial value for the instance.""" |
584 | +def get_distro_series_inital(instance, with_key_required=True): |
585 | + """Returns the distro_series initial value for the instance. |
586 | + |
587 | + :param with_key_required: When true includes the release_requires_key in |
588 | + the choice. |
589 | + """ |
590 | osystem = instance.osystem |
591 | series = instance.distro_series |
592 | os_obj = OperatingSystemRegistry.get_item(osystem) |
593 | - if os_obj is not None: |
594 | + if not with_key_required: |
595 | + key_required = '' |
596 | + elif os_obj is not None: |
597 | key_required = get_release_requires_key(os_obj, series) |
598 | - else: |
599 | - key_required = '' |
600 | if osystem is not None and osystem != '': |
601 | if series is None: |
602 | series = '' |
I see what you're trying to do here and you're on the right path… but I've spotted problems in the form you're adding here, see my comments inline. Also, the code of the list_all_* utilities is really starting to look like spaghetti code; since these methods are well-encapsulated helpers I wouldn't mind so much if they were clearly documented and well tested but it doesn't look like it's the case; I'd like you to have another look at this and see if you can improve the situation and reply to my questions: see the inline comments.