Merge lp:~andreserl/maas/lp1664248_webui into lp:~maas-committers/maas/trunk
- lp1664248_webui
- Merge into trunk
Proposed by
Andres Rodriguez
Status: | Superseded | ||||||||
---|---|---|---|---|---|---|---|---|---|
Proposed branch: | lp:~andreserl/maas/lp1664248_webui | ||||||||
Merge into: | lp:~maas-committers/maas/trunk | ||||||||
Diff against target: |
801 lines (+384/-75) 18 files modified
src/maasserver/api/packagerepositories.py (+11/-2) src/maasserver/api/tests/test_packagerepositories.py (+136/-21) src/maasserver/compose_preseed.py (+40/-24) src/maasserver/forms/packagerepository.py (+48/-0) src/maasserver/forms/tests/test_packagerepository.py (+38/-16) src/maasserver/migrations/builtin/maasserver/0116_add_disabled_components_for_mirrors.py (+20/-0) src/maasserver/models/packagerepository.py (+8/-0) src/maasserver/models/tests/test_packagerepository.py (+5/-0) src/maasserver/static/js/angular/controllers/settings.js (+2/-0) src/maasserver/static/js/angular/controllers/tests/test_settings.js (+2/-0) src/maasserver/static/js/angular/factories/general.js (+7/-0) src/maasserver/static/js/angular/factories/tests/test_general.js (+16/-3) src/maasserver/static/partials/settings.html (+5/-0) src/maasserver/testing/factory.py (+4/-2) src/maasserver/tests/test_preseed.py (+30/-7) src/maasserver/websockets/handlers/general.py (+5/-0) src/maasserver/websockets/handlers/tests/test_general.py (+6/-0) src/maasserver/websockets/handlers/tests/test_packagerepository.py (+1/-0) |
||||||||
To merge this branch: | bzr merge lp:~andreserl/maas/lp1664248_webui | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
MAAS Maintainers | Pending | ||
Review via email: mp+320124@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/maasserver/api/packagerepositories.py' |
2 | --- src/maasserver/api/packagerepositories.py 2017-02-17 14:23:04 +0000 |
3 | +++ src/maasserver/api/packagerepositories.py 2017-03-16 22:02:44 +0000 |
4 | @@ -22,6 +22,7 @@ |
5 | 'url', |
6 | 'distributions', |
7 | 'disabled_pockets', |
8 | + 'disabled_components', |
9 | 'components', |
10 | 'arches', |
11 | 'key', |
12 | @@ -70,7 +71,11 @@ |
13 | |
14 | :param disabled_pockets: The list of pockets to disable. |
15 | |
16 | - :param components: The list of components to enable. |
17 | + :param disabled_components: The list of components to disable. Only |
18 | + applicable to the default Ubuntu repositories. |
19 | + |
20 | + :param components: The list of components to enable. Only applicable |
21 | + to custom repositories. |
22 | |
23 | :param arches: The list of supported architectures. |
24 | |
25 | @@ -129,7 +134,11 @@ |
26 | |
27 | :param disabled_pockets: The list of pockets to disable. |
28 | |
29 | - :param components: The list of components to enable. |
30 | + :param disabled_components: The list of components to disable. Only |
31 | + applicable to the default Ubuntu repositories. |
32 | + |
33 | + :param components: The list of components to enable. Only applicable |
34 | + to custom repositories. |
35 | |
36 | :param arches: The list of supported architectures. |
37 | |
38 | |
39 | === modified file 'src/maasserver/api/tests/test_packagerepositories.py' |
40 | --- src/maasserver/api/tests/test_packagerepositories.py 2017-01-28 00:51:47 +0000 |
41 | +++ src/maasserver/api/tests/test_packagerepositories.py 2017-03-16 22:02:44 +0000 |
42 | @@ -72,27 +72,142 @@ |
43 | self.assertEqual( |
44 | http.client.NOT_FOUND, response.status_code, response.content) |
45 | |
46 | - def test_update(self): |
47 | - self.become_admin() |
48 | - package_repository = factory.make_PackageRepository() |
49 | - new_values = { |
50 | - 'url': factory.make_url(scheme='http'), |
51 | - 'distributions': [ |
52 | - factory.make_name("distribution%d" % i) for i in range(3)], |
53 | - 'disabled_pockets': [ |
54 | - factory.make_name("disabled_pocket%d" % i) for i in range(1)], |
55 | - 'components': [factory.make_name("comp%d" % i) for i in range(4)], |
56 | - 'arches': [ |
57 | - random.choice(PackageRepository.KNOWN_ARCHES), |
58 | - random.choice(PackageRepository.KNOWN_ARCHES), |
59 | - ] |
60 | - } |
61 | - response = self.client.put( |
62 | - self.get_package_repository_uri(package_repository), new_values) |
63 | - self.assertEqual( |
64 | - http.client.OK, response.status_code, response.content) |
65 | - package_repository = reload_object(package_repository) |
66 | - self.assertAttributes(package_repository, new_values) |
67 | + def test_update_custom_repository(self): |
68 | + """Updates a custom repository""" |
69 | + self.become_admin() |
70 | + # Creates a repository which is not 'default'. |
71 | + package_repository = factory.make_PackageRepository() |
72 | + new_values = { |
73 | + 'url': factory.make_url(scheme='http'), |
74 | + 'distributions': [ |
75 | + factory.make_name("distribution%d" % i) for i in range(3)], |
76 | + 'components': [factory.make_name("comp%d" % i) for i in range(4)], |
77 | + 'arches': [ |
78 | + random.choice(PackageRepository.KNOWN_ARCHES), |
79 | + random.choice(PackageRepository.KNOWN_ARCHES), |
80 | + ] |
81 | + } |
82 | + response = self.client.put( |
83 | + self.get_package_repository_uri(package_repository), new_values) |
84 | + self.assertEqual( |
85 | + http.client.OK, response.status_code, response.content) |
86 | + package_repository = reload_object(package_repository) |
87 | + self.assertAttributes(package_repository, new_values) |
88 | + |
89 | + def test_update_custom_repository_fails_if_disabled_components(self): |
90 | + """Test that updating a custom repository fails if specifying |
91 | + 'disabled_components'. This is only needed when the repository |
92 | + is an Ubuntu repository""" |
93 | + self.become_admin() |
94 | + # Creates a repository which is not 'default'. |
95 | + package_repository = factory.make_PackageRepository() |
96 | + new_values = { |
97 | + 'url': factory.make_url(scheme='http'), |
98 | + 'distributions': [ |
99 | + factory.make_name("distribution%d" % i) for i in range(3)], |
100 | + 'components': [factory.make_name("comp%d" % i) for i in range(4)], |
101 | + 'disabled_components': [ |
102 | + factory.make_name("comp%d" % i) for i in range(4)], |
103 | + 'arches': [ |
104 | + random.choice(PackageRepository.KNOWN_ARCHES), |
105 | + random.choice(PackageRepository.KNOWN_ARCHES), |
106 | + ] |
107 | + } |
108 | + response = self.client.put( |
109 | + self.get_package_repository_uri(package_repository), new_values) |
110 | + self.assertEqual( |
111 | + http.client.BAD_REQUEST, response.status_code, response.content) |
112 | + |
113 | + def test_update_ubuntu_mirror(self): |
114 | + """Updates a Ubuntu mirror""" |
115 | + self.become_admin() |
116 | + # Create an Ubuntu mirror without components |
117 | + package_repository = factory.make_PackageRepository( |
118 | + default=True, components=[]) |
119 | + new_values = { |
120 | + 'url': factory.make_url(scheme='http'), |
121 | + 'distributions': [ |
122 | + factory.make_name("distribution%d" % i) for i in range(3)], |
123 | + 'disabled_pockets': ["updates", "security"], |
124 | + 'disabled_components': ["universe", "multiverse"], |
125 | + 'arches': [ |
126 | + random.choice(PackageRepository.KNOWN_ARCHES), |
127 | + random.choice(PackageRepository.KNOWN_ARCHES), |
128 | + ] |
129 | + } |
130 | + response = self.client.put( |
131 | + self.get_package_repository_uri(package_repository), new_values) |
132 | + self.assertEqual( |
133 | + http.client.OK, response.status_code, response.content) |
134 | + package_repository = reload_object(package_repository) |
135 | + self.assertAttributes(package_repository, new_values) |
136 | + |
137 | + def test_update_ubuntu_mirror_fail_with_invalid_disabled_pockets(self): |
138 | + """Test that updating an Ubuntu mirror with invalid pockets fails""" |
139 | + self.become_admin() |
140 | + # Create an Ubuntu mirror without components |
141 | + package_repository = factory.make_PackageRepository( |
142 | + default=True, components=[]) |
143 | + new_values = { |
144 | + 'url': factory.make_url(scheme='http'), |
145 | + 'distributions': [ |
146 | + factory.make_name("distribution%d" % i) for i in range(3)], |
147 | + 'disabled_pockets': ["updateses"], |
148 | + 'arches': [ |
149 | + random.choice(PackageRepository.KNOWN_ARCHES), |
150 | + random.choice(PackageRepository.KNOWN_ARCHES), |
151 | + ] |
152 | + } |
153 | + response = self.client.put( |
154 | + self.get_package_repository_uri(package_repository), new_values) |
155 | + self.assertEqual( |
156 | + http.client.BAD_REQUEST, response.status_code, response.content) |
157 | + |
158 | + def test_update_ubuntu_mirror_fail_with_invalid_disabled_components(self): |
159 | + """Test that updating an Ubuntu mirror with invalid components fails""" |
160 | + self.become_admin() |
161 | + # Create an Ubuntu mirror without components |
162 | + package_repository = factory.make_PackageRepository( |
163 | + default=True, components=[]) |
164 | + new_values = { |
165 | + 'url': factory.make_url(scheme='http'), |
166 | + 'distributions': [ |
167 | + factory.make_name("distribution%d" % i) for i in range(3)], |
168 | + 'disabled_components': ['universes'], |
169 | + 'arches': [ |
170 | + random.choice(PackageRepository.KNOWN_ARCHES), |
171 | + random.choice(PackageRepository.KNOWN_ARCHES), |
172 | + ] |
173 | + } |
174 | + response = self.client.put( |
175 | + self.get_package_repository_uri(package_repository), new_values) |
176 | + self.assertEqual( |
177 | + http.client.BAD_REQUEST, response.status_code, response.content) |
178 | + |
179 | + def test_update_ubuntu_mirror_fails_if_components_are_passed(self): |
180 | + """Test that updating a Ubuntu mirror fails if specifying |
181 | + 'components'. This is only needed when the repository is not |
182 | + an Ubuntu repository""" |
183 | + self.become_admin() |
184 | + # Create an Ubuntu mirror without components |
185 | + package_repository = factory.make_PackageRepository( |
186 | + default=True, components=[]) |
187 | + new_values = { |
188 | + 'url': factory.make_url(scheme='http'), |
189 | + 'distributions': [ |
190 | + factory.make_name("distribution%d" % i) for i in range(3)], |
191 | + 'components': [factory.make_name("comp%d" % i) for i in range(4)], |
192 | + 'disabled_components': [ |
193 | + factory.make_name("comp%d" % i) for i in range(4)], |
194 | + 'arches': [ |
195 | + random.choice(PackageRepository.KNOWN_ARCHES), |
196 | + random.choice(PackageRepository.KNOWN_ARCHES), |
197 | + ] |
198 | + } |
199 | + response = self.client.put( |
200 | + self.get_package_repository_uri(package_repository), new_values) |
201 | + self.assertEqual( |
202 | + http.client.BAD_REQUEST, response.status_code, response.content) |
203 | |
204 | def test_update_admin_only(self): |
205 | package_repository = factory.make_PackageRepository() |
206 | |
207 | === modified file 'src/maasserver/compose_preseed.py' |
208 | --- src/maasserver/compose_preseed.py 2017-02-17 14:23:04 +0000 |
209 | +++ src/maasserver/compose_preseed.py 2017-03-16 22:02:44 +0000 |
210 | @@ -61,25 +61,41 @@ |
211 | apt_proxy = get_apt_proxy_for_node(node) |
212 | |
213 | # Process the default Ubuntu Archives or Mirror. |
214 | - archives = { |
215 | - 'apt': { |
216 | - 'preserve_sources_list': preserve_sources, |
217 | - 'primary': [ |
218 | - { |
219 | - 'arches': ['default'], |
220 | - 'uri': archive.url |
221 | - }, |
222 | - ], |
223 | - 'security': [ |
224 | - { |
225 | - 'arches': ['default'], |
226 | - 'uri': archive.url |
227 | - }, |
228 | - ], |
229 | - }, |
230 | - } |
231 | - if archive.disabled_pockets: |
232 | - archives['apt']['disable_suites'] = archive.disabled_pockets |
233 | + archives = {} |
234 | + archives['apt'] = {} |
235 | + archives['apt']['preserve_sources_list'] = preserve_sources |
236 | + # If disabled_components exist, build a custom list of repositories |
237 | + if archive.disabled_components: |
238 | + urls = '' |
239 | + components = archive.KNOWN_COMPONENTS[:] |
240 | + |
241 | + for comp in archive.COMPONENTS_TO_DISABLE: |
242 | + if comp in archive.disabled_components: |
243 | + components.remove(comp) |
244 | + urls += 'deb %s $RELEASE %s\n' % ( |
245 | + archive.url, ' '.join(components)) |
246 | + |
247 | + for pocket in archive.POCKETS_TO_DISABLE: |
248 | + if pocket not in archive.disabled_pockets: |
249 | + urls += 'deb %s $RELEASE-%s %s\n' % ( |
250 | + archive.url, pocket, ' '.join(components)) |
251 | + |
252 | + archives['apt']['sources_list'] = urls |
253 | + else: |
254 | + archives['apt']['primary'] = [ |
255 | + { |
256 | + 'arches': ['default'], |
257 | + 'uri': archive.url |
258 | + } |
259 | + ] |
260 | + archives['apt']['security'] = [ |
261 | + { |
262 | + 'arches': ['default'], |
263 | + 'uri': archive.url |
264 | + } |
265 | + ] |
266 | + if archive.disabled_pockets: |
267 | + archives['apt']['disable_suites'] = archive.disabled_pockets |
268 | if apt_proxy: |
269 | archives['apt']['proxy'] = apt_proxy |
270 | if archive.key: |
271 | @@ -94,7 +110,7 @@ |
272 | if repo.url.startswith('ppa:'): |
273 | url = repo.url |
274 | elif 'ppa.launchpad.net' in repo.url: |
275 | - url = 'deb %s %s main' % (repo.url, node.distro_series) |
276 | + url = 'deb %s $RELEASE main' % (repo.url) |
277 | else: |
278 | components = '' |
279 | if not repo.components: |
280 | @@ -105,8 +121,8 @@ |
281 | components = components.strip() |
282 | |
283 | if not repo.distributions: |
284 | - url = 'deb %s %s %s' % ( |
285 | - repo.url, node.distro_series, components) |
286 | + url = 'deb %s $RELEASE %s' % ( |
287 | + repo.url, components) |
288 | else: |
289 | url = '' |
290 | for dist in repo.distributions: |
291 | @@ -120,11 +136,11 @@ |
292 | if repo.key: |
293 | archives['apt']['sources'][repo_name] = { |
294 | 'key': repo.key, |
295 | - 'source': url |
296 | + 'source': url.strip() |
297 | } |
298 | else: |
299 | archives['apt']['sources'][repo_name] = { |
300 | - 'source': url |
301 | + 'source': url.strip() |
302 | } |
303 | |
304 | return archives |
305 | |
306 | === modified file 'src/maasserver/forms/packagerepository.py' |
307 | --- src/maasserver/forms/packagerepository.py 2017-02-17 14:23:04 +0000 |
308 | +++ src/maasserver/forms/packagerepository.py 2017-03-16 22:02:44 +0000 |
309 | @@ -27,6 +27,7 @@ |
310 | 'url', |
311 | 'distributions', |
312 | 'disabled_pockets', |
313 | + 'disabled_components', |
314 | 'components', |
315 | 'arches', |
316 | 'key', |
317 | @@ -50,6 +51,9 @@ |
318 | disabled_pockets = UnconstrainedMultipleChoiceField( |
319 | label="Disabled Pocket list") |
320 | |
321 | + disabled_components = UnconstrainedMultipleChoiceField( |
322 | + label="Disabled Component list") |
323 | + |
324 | components = UnconstrainedMultipleChoiceField(label="Component list") |
325 | |
326 | arches = UnconstrainedMultipleChoiceField(label="Architecture list") |
327 | @@ -99,10 +103,54 @@ |
328 | values = [] |
329 | for value in self.cleaned_data.get('disabled_pockets', []): |
330 | values.extend([s.strip() for s in value.split(',')]) |
331 | + # This allows to reset the values of disabled_pockets if one of the |
332 | + # following is passed over the API: |
333 | + # disabled_pockets= |
334 | + # disabled_pockets='' |
335 | + # disabled_pockets=None |
336 | + # disabled_pockets=[] |
337 | + if values == [''] or values == ['None'] or values == ['[]']: |
338 | + return [] |
339 | + # Check that a valid pocket is being disable. |
340 | + for pocket in values: |
341 | + if pocket not in PackageRepository.POCKETS_TO_DISABLE: |
342 | + raise ValidationError( |
343 | + "'%s' is not a valid Ubuntu archive pocket. You " |
344 | + "can only disable %s." % ( |
345 | + pocket, PackageRepository.POCKETS_TO_DISABLE)) |
346 | + return values |
347 | + |
348 | + def clean_disabled_components(self): |
349 | + values = [] |
350 | + for value in self.cleaned_data.get('disabled_components', []): |
351 | + values.extend([s.strip() for s in value.split(',')]) |
352 | + # This allows to reset the values of disabled_components if one of the |
353 | + # following is passed over the API: |
354 | + # disabled_components= |
355 | + # disabled_components='' |
356 | + # disabled_components=None |
357 | + # disabled_components=[] |
358 | + if values == [''] or values == ['None'] or values == ['[]']: |
359 | + return [] |
360 | + if self.instance is not None and not self.instance.default and values: |
361 | + raise ValidationError( |
362 | + "This is a custom repository. Please update 'components' " |
363 | + "instead.") |
364 | + # Check that a valid component is being passed. |
365 | + for component in values: |
366 | + if component not in PackageRepository.COMPONENTS_TO_DISABLE: |
367 | + raise ValidationError( |
368 | + "'%s' is not a valid Ubuntu archive component. You " |
369 | + "can only disable %s." % ( |
370 | + component, PackageRepository.COMPONENTS_TO_DISABLE)) |
371 | return values |
372 | |
373 | def clean_components(self): |
374 | values = [] |
375 | for value in self.cleaned_data.get('components', []): |
376 | values.extend([s.strip() for s in value.split(',')]) |
377 | + if self.instance is not None and self.instance.default and values: |
378 | + raise ValidationError( |
379 | + "This is a default Ubuntu repository. Please update " |
380 | + "'disabled_components' instead.") |
381 | return values |
382 | |
383 | === modified file 'src/maasserver/forms/tests/test_packagerepository.py' |
384 | --- src/maasserver/forms/tests/test_packagerepository.py 2017-02-17 14:23:04 +0000 |
385 | +++ src/maasserver/forms/tests/test_packagerepository.py 2017-03-16 22:02:44 +0000 |
386 | @@ -27,10 +27,10 @@ |
387 | arch2 = random.choice(PackageRepository.KNOWN_ARCHES) |
388 | dist1 = factory.make_name('dist') |
389 | dist2 = factory.make_name('dist') |
390 | - pock1 = factory.make_name('pock') |
391 | - pock2 = factory.make_name('pock') |
392 | - comp1 = factory.make_name('comp') |
393 | - comp2 = factory.make_name('comp') |
394 | + pock1 = 'updates' |
395 | + pock2 = 'backports' |
396 | + comp1 = 'universe' |
397 | + comp2 = 'multiverse' |
398 | enabled = factory.pick_bool() |
399 | params = { |
400 | 'name': name, |
401 | @@ -195,18 +195,40 @@ |
402 | package_repository = factory.make_PackageRepository() |
403 | form = PackageRepositoryForm( |
404 | instance=package_repository, |
405 | - data={'disabled_pockets': ['val1,val2']}) |
406 | - repo = form.save() |
407 | - self.assertItemsEqual(['val1', 'val2'], repo.disabled_pockets) |
408 | - form = PackageRepositoryForm( |
409 | - instance=package_repository, |
410 | - data={'disabled_pockets': ['val1, val2']}) |
411 | - repo = form.save() |
412 | - self.assertItemsEqual(['val1', 'val2'], repo.disabled_pockets) |
413 | - form = PackageRepositoryForm( |
414 | - instance=package_repository, data={'disabled_pockets': ['val1']}) |
415 | - repo = form.save() |
416 | - self.assertItemsEqual(['val1'], repo.disabled_pockets) |
417 | + data={'disabled_pockets': ['updates,backports']}) |
418 | + repo = form.save() |
419 | + self.assertItemsEqual(['updates', 'backports'], repo.disabled_pockets) |
420 | + form = PackageRepositoryForm( |
421 | + instance=package_repository, |
422 | + data={'disabled_pockets': ['updates, backports']}) |
423 | + repo = form.save() |
424 | + self.assertItemsEqual(['updates', 'backports'], repo.disabled_pockets) |
425 | + form = PackageRepositoryForm( |
426 | + instance=package_repository, |
427 | + data={'disabled_pockets': ['updates']}) |
428 | + repo = form.save() |
429 | + self.assertItemsEqual(['updates'], repo.disabled_pockets) |
430 | + |
431 | + def test__disabled_component_comma_cleaning(self): |
432 | + package_repository = factory.make_PackageRepository( |
433 | + default=True, components=[]) |
434 | + form = PackageRepositoryForm( |
435 | + instance=package_repository, |
436 | + data={'disabled_components': ['universe,multiverse']}) |
437 | + repo = form.save() |
438 | + self.assertItemsEqual( |
439 | + ['universe', 'multiverse'], repo.disabled_components) |
440 | + form = PackageRepositoryForm( |
441 | + instance=package_repository, |
442 | + data={'disabled_components': ['universe, multiverse']}) |
443 | + repo = form.save() |
444 | + self.assertItemsEqual( |
445 | + ['universe', 'multiverse'], repo.disabled_components) |
446 | + form = PackageRepositoryForm( |
447 | + instance=package_repository, |
448 | + data={'disabled_components': ['universe']}) |
449 | + repo = form.save() |
450 | + self.assertItemsEqual(['universe'], repo.disabled_components) |
451 | |
452 | def test__component_comma_cleaning(self): |
453 | package_repository = factory.make_PackageRepository() |
454 | |
455 | === added file 'src/maasserver/migrations/builtin/maasserver/0116_add_disabled_components_for_mirrors.py' |
456 | --- src/maasserver/migrations/builtin/maasserver/0116_add_disabled_components_for_mirrors.py 1970-01-01 00:00:00 +0000 |
457 | +++ src/maasserver/migrations/builtin/maasserver/0116_add_disabled_components_for_mirrors.py 2017-03-16 22:02:44 +0000 |
458 | @@ -0,0 +1,20 @@ |
459 | +# -*- coding: utf-8 -*- |
460 | +from __future__ import unicode_literals |
461 | + |
462 | +from django.db import migrations, models |
463 | +import django.contrib.postgres.fields |
464 | + |
465 | + |
466 | +class Migration(migrations.Migration): |
467 | + |
468 | + dependencies = [ |
469 | + ('maasserver', '0115_additional_boot_resource_filetypes'), |
470 | + ] |
471 | + |
472 | + operations = [ |
473 | + migrations.AddField( |
474 | + model_name='packagerepository', |
475 | + name='disabled_components', |
476 | + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), size=None, default=list, blank=True, null=True), |
477 | + ), |
478 | + ] |
479 | |
480 | === modified file 'src/maasserver/models/packagerepository.py' |
481 | --- src/maasserver/models/packagerepository.py 2016-09-01 13:12:15 +0000 |
482 | +++ src/maasserver/models/packagerepository.py 2017-03-16 22:02:44 +0000 |
483 | @@ -74,6 +74,9 @@ |
484 | def get_pockets_to_disable(self): |
485 | return PackageRepository.POCKETS_TO_DISABLE |
486 | |
487 | + def get_components_to_disable(self): |
488 | + return PackageRepository.COMPONENTS_TO_DISABLE |
489 | + |
490 | def get_default_archive(self, arch): |
491 | return PackageRepository.objects.filter( |
492 | arches__contains=[arch], |
493 | @@ -94,6 +97,8 @@ |
494 | PORTS_ARCHES = ['armhf', 'arm64', 'ppc64el'] |
495 | KNOWN_ARCHES = MAIN_ARCHES + PORTS_ARCHES |
496 | POCKETS_TO_DISABLE = ['updates', 'security', 'backports'] |
497 | + COMPONENTS_TO_DISABLE = ['restricted', 'universe', 'multiverse'] |
498 | + KNOWN_COMPONENTS = ['main', 'restricted', 'universe', 'multiverse'] |
499 | |
500 | class Meta(DefaultMeta): |
501 | """Needed for South to recognize this model.""" |
502 | @@ -111,6 +116,9 @@ |
503 | disabled_pockets = ArrayField( |
504 | TextField(), blank=True, null=True, default=list) |
505 | |
506 | + disabled_components = ArrayField( |
507 | + TextField(), blank=True, null=True, default=list) |
508 | + |
509 | components = ArrayField(TextField(), blank=True, null=True, default=list) |
510 | |
511 | arches = ArrayField(TextField(), blank=True, null=True, default=list) |
512 | |
513 | === modified file 'src/maasserver/models/tests/test_packagerepository.py' |
514 | --- src/maasserver/models/tests/test_packagerepository.py 2016-08-25 19:31:20 +0000 |
515 | +++ src/maasserver/models/tests/test_packagerepository.py 2017-03-16 22:02:44 +0000 |
516 | @@ -61,3 +61,8 @@ |
517 | self.assertEqual( |
518 | PackageRepository.objects.get_pockets_to_disable(), |
519 | PackageRepository.POCKETS_TO_DISABLE) |
520 | + |
521 | + def test_get_components_to_disable(self): |
522 | + self.assertEqual( |
523 | + PackageRepository.objects.get_components_to_disable(), |
524 | + PackageRepository.COMPONENTS_TO_DISABLE) |
525 | |
526 | === modified file 'src/maasserver/static/js/angular/controllers/settings.js' |
527 | --- src/maasserver/static/js/angular/controllers/settings.js 2016-09-21 14:49:23 +0000 |
528 | +++ src/maasserver/static/js/angular/controllers/settings.js 2017-03-16 22:02:44 +0000 |
529 | @@ -30,6 +30,8 @@ |
530 | GeneralManager.getData("known_architectures"); |
531 | $scope.pockets_to_disable = |
532 | GeneralManager.getData("pockets_to_disable"); |
533 | + $scope.components_to_disable = |
534 | + GeneralManager.getData("components_to_disable"); |
535 | $scope.packageRepositoriesManager = PackageRepositoriesManager; |
536 | $scope.repositories = |
537 | PackageRepositoriesManager.getItems(); |
538 | |
539 | === modified file 'src/maasserver/static/js/angular/controllers/tests/test_settings.js' |
540 | --- src/maasserver/static/js/angular/controllers/tests/test_settings.js 2016-09-22 15:05:38 +0000 |
541 | +++ src/maasserver/static/js/angular/controllers/tests/test_settings.js 2017-03-16 22:02:44 +0000 |
542 | @@ -116,6 +116,8 @@ |
543 | GeneralManager.getData("known_architectures")); |
544 | expect($scope.pockets_to_disable).toBe( |
545 | GeneralManager.getData("pockets_to_disable")); |
546 | + expect($scope.components_to_disable).toBe( |
547 | + GeneralManager.getData("components_to_disable")); |
548 | expect($scope.packageRepositoriesManager).toBe( |
549 | PackageRepositoriesManager); |
550 | expect($scope.repositories).toBe( |
551 | |
552 | === modified file 'src/maasserver/static/js/angular/factories/general.js' |
553 | --- src/maasserver/static/js/angular/factories/general.js 2016-09-22 15:05:38 +0000 |
554 | +++ src/maasserver/static/js/angular/factories/general.js 2017-03-16 22:02:44 +0000 |
555 | @@ -77,6 +77,13 @@ |
556 | polling: false, |
557 | nextPromise: null |
558 | }, |
559 | + components_to_disable: { |
560 | + method: "general.components_to_disable", |
561 | + data: [], |
562 | + loaded: false, |
563 | + polling: false, |
564 | + nextPromise: null |
565 | + }, |
566 | hwe_kernels: { |
567 | method: "general.hwe_kernels", |
568 | data: [], |
569 | |
570 | === modified file 'src/maasserver/static/js/angular/factories/tests/test_general.js' |
571 | --- src/maasserver/static/js/angular/factories/tests/test_general.js 2016-08-22 04:10:43 +0000 |
572 | +++ src/maasserver/static/js/angular/factories/tests/test_general.js 2017-03-16 22:02:44 +0000 |
573 | @@ -53,9 +53,9 @@ |
574 | ["machine_actions", "device_actions", "region_controller_actions", |
575 | "rack_controller_actions", "region_and_rack_controller_actions", |
576 | "architectures", "known_architectures", "pockets_to_disable", |
577 | - "hwe_kernels", "min_hwe_kernels", "default_min_hwe_kernel", |
578 | - "osinfo", "bond_options", "version", "power_types", |
579 | - "release_options"]); |
580 | + "components_to_disable", "hwe_kernels", "min_hwe_kernels", |
581 | + "default_min_hwe_kernel", "osinfo", "bond_options", |
582 | + "version", "power_types", "release_options"]); |
583 | }); |
584 | |
585 | it("_data.machine_actions has correct data", function() { |
586 | @@ -136,6 +136,15 @@ |
587 | expect(ptd.nextPromise).toBeNull(); |
588 | }); |
589 | |
590 | + it("_data.components_to_disable has correct data", function() { |
591 | + var ptd = GeneralManager._data.components_to_disable; |
592 | + expect(ptd.method).toBe("general.components_to_disable"); |
593 | + expect(ptd.data).toEqual([]); |
594 | + expect(ptd.loaded).toBe(false); |
595 | + expect(ptd.polling).toBe(false); |
596 | + expect(ptd.nextPromise).toBeNull(); |
597 | + }); |
598 | + |
599 | it("_data.hwe_kernels has correct data", function() { |
600 | var hwe_kernels = GeneralManager._data.hwe_kernels; |
601 | expect(hwe_kernels.method).toBe("general.hwe_kernels"); |
602 | @@ -302,6 +311,7 @@ |
603 | GeneralManager._data.architectures.loaded = true; |
604 | GeneralManager._data.known_architectures.loaded = true; |
605 | GeneralManager._data.pockets_to_disable.loaded = true; |
606 | + GeneralManager._data.components_to_disable.loaded = true; |
607 | GeneralManager._data.hwe_kernels.loaded = true; |
608 | GeneralManager._data.osinfo.loaded = true; |
609 | GeneralManager._data.bond_options.loaded = true; |
610 | @@ -321,6 +331,7 @@ |
611 | GeneralManager._data.architectures.loaded = true; |
612 | GeneralManager._data.known_architectures.loaded = true; |
613 | GeneralManager._data.pockets_to_disable.loaded = true; |
614 | + GeneralManager._data.components_to_disable.loaded = true; |
615 | GeneralManager._data.hwe_kernels.loaded = true; |
616 | GeneralManager._data.min_hwe_kernels.loaded = true; |
617 | GeneralManager._data.default_min_hwe_kernel.loaded = true; |
618 | @@ -353,6 +364,7 @@ |
619 | GeneralManager._data.architectures.polling = false; |
620 | GeneralManager._data.known_architectures.polling = false; |
621 | GeneralManager._data.pockets_to_disable.polling = false; |
622 | + GeneralManager._data.components_to_disable.polling = false; |
623 | GeneralManager._data.hwe_kernels.polling = false; |
624 | GeneralManager._data.osinfo.polling = false; |
625 | expect(GeneralManager.isPolling()).toBe(true); |
626 | @@ -363,6 +375,7 @@ |
627 | GeneralManager._data.architectures.polling = true; |
628 | GeneralManager._data.known_architectures.polling = true; |
629 | GeneralManager._data.pockets_to_disable.polling = true; |
630 | + GeneralManager._data.components_to_disable.polling = true; |
631 | GeneralManager._data.hwe_kernels.polling = true; |
632 | GeneralManager._data.osinfo.polling = true; |
633 | expect(GeneralManager.isPolling()).toBe(true); |
634 | |
635 | === modified file 'src/maasserver/static/partials/settings.html' |
636 | --- src/maasserver/static/partials/settings.html 2017-02-17 14:23:04 +0000 |
637 | +++ src/maasserver/static/partials/settings.html 2017-03-16 22:02:44 +0000 |
638 | @@ -488,6 +488,11 @@ |
639 | <maas-obj-field type="checkboxes" key="disabled_pockets" label="Disabled Pockets" |
640 | label-width="two" input-width="three" values="pockets_to_disable"></maas-obj-field> |
641 | </div> |
642 | + <div class="form__group u-align--left-mobile" |
643 | + data-ng-if="isMirror(editRepository) && !isPPA(editRepository)"> |
644 | + <maas-obj-field type="checkboxes" key="disabled_components" label="Disabled Components" |
645 | + label-width="two" input-width="three" values="components_to_disable"></maas-obj-field> |
646 | + </div> |
647 | </div> |
648 | </div> |
649 | <div class="table__row is-active"> |
650 | |
651 | === modified file 'src/maasserver/testing/factory.py' |
652 | --- src/maasserver/testing/factory.py 2017-03-15 16:40:59 +0000 |
653 | +++ src/maasserver/testing/factory.py 2017-03-16 22:02:44 +0000 |
654 | @@ -2037,7 +2037,8 @@ |
655 | |
656 | def make_PackageRepository( |
657 | self, name=None, url=None, arches=None, default=False, key=None, |
658 | - distributions=None, components=None, disabled_pockets=None): |
659 | + distributions=None, components=None, disabled_pockets=None, |
660 | + disabled_components=None): |
661 | if name is None: |
662 | name = self.make_name("name") |
663 | if url is None: |
664 | @@ -2051,7 +2052,8 @@ |
665 | return PackageRepository.objects.create( |
666 | name=name, url=url, |
667 | distributions=distributions, disabled_pockets=disabled_pockets, |
668 | - components=components, arches=arches, key=key, default=default) |
669 | + components=components, arches=arches, key=key, default=default, |
670 | + disabled_components=disabled_components) |
671 | |
672 | def make_Notification( |
673 | self, message=None, *, ident=None, user=None, users=False, |
674 | |
675 | === modified file 'src/maasserver/tests/test_preseed.py' |
676 | --- src/maasserver/tests/test_preseed.py 2017-03-09 08:55:12 +0000 |
677 | +++ src/maasserver/tests/test_preseed.py 2017-03-16 22:02:44 +0000 |
678 | @@ -1230,6 +1230,31 @@ |
679 | preseed['apt']['disable_suites'], |
680 | archive.disabled_pockets) |
681 | |
682 | + def test_compose_curtin_archive_config_with_disabled_pockets(self): |
683 | + """Test that main archive has a configuration that includes |
684 | + disabled_pockets. If so, MAAS will create its own sources_list |
685 | + instead of letting curtin/cloud-init create it based on its own |
686 | + template""" |
687 | + PackageRepository.objects.all().delete() |
688 | + node = self.make_fastpath_node('amd64') |
689 | + node.osystem = 'ubuntu' |
690 | + node.distro_series = 'xenial' |
691 | + main_url = 'http://us.archive.ubuntu.com/ubuntu' |
692 | + factory.make_PackageRepository( |
693 | + url=main_url, default=True, arches=['i386', 'amd64'], |
694 | + disabled_pockets=['updates', 'backports'], |
695 | + disabled_components=['universe', 'multiverse']) |
696 | + self.configure_get_boot_images_for_node(node, 'xinstall') |
697 | + # compose_curtin_archive_config returns a list. |
698 | + userdata = compose_curtin_archive_config(node) |
699 | + preseed = yaml.safe_load(userdata[0]) |
700 | + self.assertThat( |
701 | + preseed['apt']['sources_list'], |
702 | + Contains('$RELEASE main restricted')) |
703 | + self.assertThat( |
704 | + preseed['apt']['sources_list'], |
705 | + Contains('$RELEASE-security main restricted')) |
706 | + |
707 | def test_compose_curtin_archive_config_has_ppa(self): |
708 | node = self.make_fastpath_node('i386') |
709 | node.osystem = 'ubuntu' |
710 | @@ -1283,7 +1308,7 @@ |
711 | ) |
712 | self.assertThat( |
713 | preseed['apt']['sources'][ppa_name]['source'], |
714 | - ContainsAll("deb %s %s main" % (ppa_first.url, node.distro_series)) |
715 | + ContainsAll("deb %s $RELEASE main" % ppa_first.url) |
716 | ) |
717 | # Clean up PPA name |
718 | ppa_name = make_clean_repo_name(ppa_second) |
719 | @@ -1293,8 +1318,7 @@ |
720 | ) |
721 | self.assertThat( |
722 | preseed['apt']['sources'][ppa_name]['source'], |
723 | - ContainsAll("deb %s %s main" % ( |
724 | - ppa_second.url, node.distro_series))) |
725 | + ContainsAll("deb %s $RELEASE main" % ppa_second.url)) |
726 | |
727 | def test_compose_curtin_archive_config_has_custom_repository(self): |
728 | node = self.make_fastpath_node('i386') |
729 | @@ -1318,8 +1342,7 @@ |
730 | ) |
731 | self.assertThat( |
732 | preseed['apt']['sources'][repo_name]['source'], |
733 | - ContainsAll("deb %s %s main" % ( |
734 | - repository.url, node.distro_series))) |
735 | + ContainsAll("deb %s $RELEASE main" % repository.url)) |
736 | |
737 | def test_compose_curtin_archive_config_custom_repo_with_components(self): |
738 | node = self.make_fastpath_node('i386') |
739 | @@ -1348,8 +1371,8 @@ |
740 | ) |
741 | self.assertThat( |
742 | preseed['apt']['sources'][repo_name]['source'], |
743 | - ContainsAll("deb %s %s %s" % ( |
744 | - repository.url, node.distro_series, components))) |
745 | + ContainsAll("deb %s $RELEASE %s" % ( |
746 | + repository.url, components))) |
747 | |
748 | def test_compose_curtin_archive_config_custom_repo_components_dists(self): |
749 | node = self.make_fastpath_node('i386') |
750 | |
751 | === modified file 'src/maasserver/websockets/handlers/general.py' |
752 | --- src/maasserver/websockets/handlers/general.py 2017-01-28 00:51:47 +0000 |
753 | +++ src/maasserver/websockets/handlers/general.py 2017-03-16 22:02:44 +0000 |
754 | @@ -46,6 +46,7 @@ |
755 | 'architectures', |
756 | 'known_architectures', |
757 | 'pockets_to_disable', |
758 | + 'components_to_disable', |
759 | 'hwe_kernels', |
760 | 'min_hwe_kernels', |
761 | 'default_min_hwe_kernel', |
762 | @@ -74,6 +75,10 @@ |
763 | """Return pockets that can be disabled.""" |
764 | return PackageRepository.objects.get_pockets_to_disable() |
765 | |
766 | + def components_to_disable(self, params): |
767 | + "Return compoennts that can be disable for default Ubuntu archives" |
768 | + return PackageRepository.objects.get_components_to_disable() |
769 | + |
770 | def hwe_kernels(self, params): |
771 | """Return all supported hwe_kernels.""" |
772 | return list_hwe_kernel_choices( |
773 | |
774 | === modified file 'src/maasserver/websockets/handlers/tests/test_general.py' |
775 | --- src/maasserver/websockets/handlers/tests/test_general.py 2017-03-02 09:41:37 +0000 |
776 | +++ src/maasserver/websockets/handlers/tests/test_general.py 2017-03-16 22:02:44 +0000 |
777 | @@ -103,6 +103,12 @@ |
778 | PackageRepository.objects.get_pockets_to_disable(), |
779 | handler.pockets_to_disable({})) |
780 | |
781 | + def test_components_to_disable(self): |
782 | + handler = GeneralHandler(factory.make_User(), {}) |
783 | + self.assertEqual( |
784 | + PackageRepository.objects.get_components_to_disable(), |
785 | + handler.components_to_disable({})) |
786 | + |
787 | def test_hwe_kernels(self): |
788 | expected_output = self.make_boot_sources() |
789 | handler = GeneralHandler(factory.make_User(), {}) |
790 | |
791 | === modified file 'src/maasserver/websockets/handlers/tests/test_packagerepository.py' |
792 | --- src/maasserver/websockets/handlers/tests/test_packagerepository.py 2017-02-17 14:23:04 +0000 |
793 | +++ src/maasserver/websockets/handlers/tests/test_packagerepository.py 2017-03-16 22:02:44 +0000 |
794 | @@ -34,6 +34,7 @@ |
795 | 'url': package_repository.url, |
796 | 'distributions': package_repository.distributions, |
797 | 'disabled_pockets': package_repository.disabled_pockets, |
798 | + 'disabled_components': package_repository.disabled_components, |
799 | 'components': package_repository.components, |
800 | 'arches': package_repository.arches, |
801 | 'key': package_repository.key, |