Merge lp:~andreserl/maas/lp1664248_webui into lp:~maas-committers/maas/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
Reviewer Review Type Date Requested Status
MAAS Maintainers Pending
Review via email: mp+320124@code.launchpad.net
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
=== modified file 'src/maasserver/api/packagerepositories.py'
--- src/maasserver/api/packagerepositories.py 2017-02-17 14:23:04 +0000
+++ src/maasserver/api/packagerepositories.py 2017-03-16 22:02:44 +0000
@@ -22,6 +22,7 @@
22 'url',22 'url',
23 'distributions',23 'distributions',
24 'disabled_pockets',24 'disabled_pockets',
25 'disabled_components',
25 'components',26 'components',
26 'arches',27 'arches',
27 'key',28 'key',
@@ -70,7 +71,11 @@
7071
71 :param disabled_pockets: The list of pockets to disable.72 :param disabled_pockets: The list of pockets to disable.
7273
73 :param components: The list of components to enable.74 :param disabled_components: The list of components to disable. Only
75 applicable to the default Ubuntu repositories.
76
77 :param components: The list of components to enable. Only applicable
78 to custom repositories.
7479
75 :param arches: The list of supported architectures.80 :param arches: The list of supported architectures.
7681
@@ -129,7 +134,11 @@
129134
130 :param disabled_pockets: The list of pockets to disable.135 :param disabled_pockets: The list of pockets to disable.
131136
132 :param components: The list of components to enable.137 :param disabled_components: The list of components to disable. Only
138 applicable to the default Ubuntu repositories.
139
140 :param components: The list of components to enable. Only applicable
141 to custom repositories.
133142
134 :param arches: The list of supported architectures.143 :param arches: The list of supported architectures.
135144
136145
=== modified file 'src/maasserver/api/tests/test_packagerepositories.py'
--- src/maasserver/api/tests/test_packagerepositories.py 2017-01-28 00:51:47 +0000
+++ src/maasserver/api/tests/test_packagerepositories.py 2017-03-16 22:02:44 +0000
@@ -72,27 +72,142 @@
72 self.assertEqual(72 self.assertEqual(
73 http.client.NOT_FOUND, response.status_code, response.content)73 http.client.NOT_FOUND, response.status_code, response.content)
7474
75 def test_update(self):75 def test_update_custom_repository(self):
76 self.become_admin()76 """Updates a custom repository"""
77 package_repository = factory.make_PackageRepository()77 self.become_admin()
78 new_values = {78 # Creates a repository which is not 'default'.
79 'url': factory.make_url(scheme='http'),79 package_repository = factory.make_PackageRepository()
80 'distributions': [80 new_values = {
81 factory.make_name("distribution%d" % i) for i in range(3)],81 'url': factory.make_url(scheme='http'),
82 'disabled_pockets': [82 'distributions': [
83 factory.make_name("disabled_pocket%d" % i) for i in range(1)],83 factory.make_name("distribution%d" % i) for i in range(3)],
84 'components': [factory.make_name("comp%d" % i) for i in range(4)],84 'components': [factory.make_name("comp%d" % i) for i in range(4)],
85 'arches': [85 'arches': [
86 random.choice(PackageRepository.KNOWN_ARCHES),86 random.choice(PackageRepository.KNOWN_ARCHES),
87 random.choice(PackageRepository.KNOWN_ARCHES),87 random.choice(PackageRepository.KNOWN_ARCHES),
88 ]88 ]
89 }89 }
90 response = self.client.put(90 response = self.client.put(
91 self.get_package_repository_uri(package_repository), new_values)91 self.get_package_repository_uri(package_repository), new_values)
92 self.assertEqual(92 self.assertEqual(
93 http.client.OK, response.status_code, response.content)93 http.client.OK, response.status_code, response.content)
94 package_repository = reload_object(package_repository)94 package_repository = reload_object(package_repository)
95 self.assertAttributes(package_repository, new_values)95 self.assertAttributes(package_repository, new_values)
96
97 def test_update_custom_repository_fails_if_disabled_components(self):
98 """Test that updating a custom repository fails if specifying
99 'disabled_components'. This is only needed when the repository
100 is an Ubuntu repository"""
101 self.become_admin()
102 # Creates a repository which is not 'default'.
103 package_repository = factory.make_PackageRepository()
104 new_values = {
105 'url': factory.make_url(scheme='http'),
106 'distributions': [
107 factory.make_name("distribution%d" % i) for i in range(3)],
108 'components': [factory.make_name("comp%d" % i) for i in range(4)],
109 'disabled_components': [
110 factory.make_name("comp%d" % i) for i in range(4)],
111 'arches': [
112 random.choice(PackageRepository.KNOWN_ARCHES),
113 random.choice(PackageRepository.KNOWN_ARCHES),
114 ]
115 }
116 response = self.client.put(
117 self.get_package_repository_uri(package_repository), new_values)
118 self.assertEqual(
119 http.client.BAD_REQUEST, response.status_code, response.content)
120
121 def test_update_ubuntu_mirror(self):
122 """Updates a Ubuntu mirror"""
123 self.become_admin()
124 # Create an Ubuntu mirror without components
125 package_repository = factory.make_PackageRepository(
126 default=True, components=[])
127 new_values = {
128 'url': factory.make_url(scheme='http'),
129 'distributions': [
130 factory.make_name("distribution%d" % i) for i in range(3)],
131 'disabled_pockets': ["updates", "security"],
132 'disabled_components': ["universe", "multiverse"],
133 'arches': [
134 random.choice(PackageRepository.KNOWN_ARCHES),
135 random.choice(PackageRepository.KNOWN_ARCHES),
136 ]
137 }
138 response = self.client.put(
139 self.get_package_repository_uri(package_repository), new_values)
140 self.assertEqual(
141 http.client.OK, response.status_code, response.content)
142 package_repository = reload_object(package_repository)
143 self.assertAttributes(package_repository, new_values)
144
145 def test_update_ubuntu_mirror_fail_with_invalid_disabled_pockets(self):
146 """Test that updating an Ubuntu mirror with invalid pockets fails"""
147 self.become_admin()
148 # Create an Ubuntu mirror without components
149 package_repository = factory.make_PackageRepository(
150 default=True, components=[])
151 new_values = {
152 'url': factory.make_url(scheme='http'),
153 'distributions': [
154 factory.make_name("distribution%d" % i) for i in range(3)],
155 'disabled_pockets': ["updateses"],
156 'arches': [
157 random.choice(PackageRepository.KNOWN_ARCHES),
158 random.choice(PackageRepository.KNOWN_ARCHES),
159 ]
160 }
161 response = self.client.put(
162 self.get_package_repository_uri(package_repository), new_values)
163 self.assertEqual(
164 http.client.BAD_REQUEST, response.status_code, response.content)
165
166 def test_update_ubuntu_mirror_fail_with_invalid_disabled_components(self):
167 """Test that updating an Ubuntu mirror with invalid components fails"""
168 self.become_admin()
169 # Create an Ubuntu mirror without components
170 package_repository = factory.make_PackageRepository(
171 default=True, components=[])
172 new_values = {
173 'url': factory.make_url(scheme='http'),
174 'distributions': [
175 factory.make_name("distribution%d" % i) for i in range(3)],
176 'disabled_components': ['universes'],
177 'arches': [
178 random.choice(PackageRepository.KNOWN_ARCHES),
179 random.choice(PackageRepository.KNOWN_ARCHES),
180 ]
181 }
182 response = self.client.put(
183 self.get_package_repository_uri(package_repository), new_values)
184 self.assertEqual(
185 http.client.BAD_REQUEST, response.status_code, response.content)
186
187 def test_update_ubuntu_mirror_fails_if_components_are_passed(self):
188 """Test that updating a Ubuntu mirror fails if specifying
189 'components'. This is only needed when the repository is not
190 an Ubuntu repository"""
191 self.become_admin()
192 # Create an Ubuntu mirror without components
193 package_repository = factory.make_PackageRepository(
194 default=True, components=[])
195 new_values = {
196 'url': factory.make_url(scheme='http'),
197 'distributions': [
198 factory.make_name("distribution%d" % i) for i in range(3)],
199 'components': [factory.make_name("comp%d" % i) for i in range(4)],
200 'disabled_components': [
201 factory.make_name("comp%d" % i) for i in range(4)],
202 'arches': [
203 random.choice(PackageRepository.KNOWN_ARCHES),
204 random.choice(PackageRepository.KNOWN_ARCHES),
205 ]
206 }
207 response = self.client.put(
208 self.get_package_repository_uri(package_repository), new_values)
209 self.assertEqual(
210 http.client.BAD_REQUEST, response.status_code, response.content)
96211
97 def test_update_admin_only(self):212 def test_update_admin_only(self):
98 package_repository = factory.make_PackageRepository()213 package_repository = factory.make_PackageRepository()
99214
=== modified file 'src/maasserver/compose_preseed.py'
--- src/maasserver/compose_preseed.py 2017-02-17 14:23:04 +0000
+++ src/maasserver/compose_preseed.py 2017-03-16 22:02:44 +0000
@@ -61,25 +61,41 @@
61 apt_proxy = get_apt_proxy_for_node(node)61 apt_proxy = get_apt_proxy_for_node(node)
6262
63 # Process the default Ubuntu Archives or Mirror.63 # Process the default Ubuntu Archives or Mirror.
64 archives = {64 archives = {}
65 'apt': {65 archives['apt'] = {}
66 'preserve_sources_list': preserve_sources,66 archives['apt']['preserve_sources_list'] = preserve_sources
67 'primary': [67 # If disabled_components exist, build a custom list of repositories
68 {68 if archive.disabled_components:
69 'arches': ['default'],69 urls = ''
70 'uri': archive.url70 components = archive.KNOWN_COMPONENTS[:]
71 },71
72 ],72 for comp in archive.COMPONENTS_TO_DISABLE:
73 'security': [73 if comp in archive.disabled_components:
74 {74 components.remove(comp)
75 'arches': ['default'],75 urls += 'deb %s $RELEASE %s\n' % (
76 'uri': archive.url76 archive.url, ' '.join(components))
77 },77
78 ],78 for pocket in archive.POCKETS_TO_DISABLE:
79 },79 if pocket not in archive.disabled_pockets:
80 }80 urls += 'deb %s $RELEASE-%s %s\n' % (
81 if archive.disabled_pockets:81 archive.url, pocket, ' '.join(components))
82 archives['apt']['disable_suites'] = archive.disabled_pockets82
83 archives['apt']['sources_list'] = urls
84 else:
85 archives['apt']['primary'] = [
86 {
87 'arches': ['default'],
88 'uri': archive.url
89 }
90 ]
91 archives['apt']['security'] = [
92 {
93 'arches': ['default'],
94 'uri': archive.url
95 }
96 ]
97 if archive.disabled_pockets:
98 archives['apt']['disable_suites'] = archive.disabled_pockets
83 if apt_proxy:99 if apt_proxy:
84 archives['apt']['proxy'] = apt_proxy100 archives['apt']['proxy'] = apt_proxy
85 if archive.key:101 if archive.key:
@@ -94,7 +110,7 @@
94 if repo.url.startswith('ppa:'):110 if repo.url.startswith('ppa:'):
95 url = repo.url111 url = repo.url
96 elif 'ppa.launchpad.net' in repo.url:112 elif 'ppa.launchpad.net' in repo.url:
97 url = 'deb %s %s main' % (repo.url, node.distro_series)113 url = 'deb %s $RELEASE main' % (repo.url)
98 else:114 else:
99 components = ''115 components = ''
100 if not repo.components:116 if not repo.components:
@@ -105,8 +121,8 @@
105 components = components.strip()121 components = components.strip()
106122
107 if not repo.distributions:123 if not repo.distributions:
108 url = 'deb %s %s %s' % (124 url = 'deb %s $RELEASE %s' % (
109 repo.url, node.distro_series, components)125 repo.url, components)
110 else:126 else:
111 url = ''127 url = ''
112 for dist in repo.distributions:128 for dist in repo.distributions:
@@ -120,11 +136,11 @@
120 if repo.key:136 if repo.key:
121 archives['apt']['sources'][repo_name] = {137 archives['apt']['sources'][repo_name] = {
122 'key': repo.key,138 'key': repo.key,
123 'source': url139 'source': url.strip()
124 }140 }
125 else:141 else:
126 archives['apt']['sources'][repo_name] = {142 archives['apt']['sources'][repo_name] = {
127 'source': url143 'source': url.strip()
128 }144 }
129145
130 return archives146 return archives
131147
=== modified file 'src/maasserver/forms/packagerepository.py'
--- src/maasserver/forms/packagerepository.py 2017-02-17 14:23:04 +0000
+++ src/maasserver/forms/packagerepository.py 2017-03-16 22:02:44 +0000
@@ -27,6 +27,7 @@
27 'url',27 'url',
28 'distributions',28 'distributions',
29 'disabled_pockets',29 'disabled_pockets',
30 'disabled_components',
30 'components',31 'components',
31 'arches',32 'arches',
32 'key',33 'key',
@@ -50,6 +51,9 @@
50 disabled_pockets = UnconstrainedMultipleChoiceField(51 disabled_pockets = UnconstrainedMultipleChoiceField(
51 label="Disabled Pocket list")52 label="Disabled Pocket list")
5253
54 disabled_components = UnconstrainedMultipleChoiceField(
55 label="Disabled Component list")
56
53 components = UnconstrainedMultipleChoiceField(label="Component list")57 components = UnconstrainedMultipleChoiceField(label="Component list")
5458
55 arches = UnconstrainedMultipleChoiceField(label="Architecture list")59 arches = UnconstrainedMultipleChoiceField(label="Architecture list")
@@ -99,10 +103,54 @@
99 values = []103 values = []
100 for value in self.cleaned_data.get('disabled_pockets', []):104 for value in self.cleaned_data.get('disabled_pockets', []):
101 values.extend([s.strip() for s in value.split(',')])105 values.extend([s.strip() for s in value.split(',')])
106 # This allows to reset the values of disabled_pockets if one of the
107 # following is passed over the API:
108 # disabled_pockets=
109 # disabled_pockets=''
110 # disabled_pockets=None
111 # disabled_pockets=[]
112 if values == [''] or values == ['None'] or values == ['[]']:
113 return []
114 # Check that a valid pocket is being disable.
115 for pocket in values:
116 if pocket not in PackageRepository.POCKETS_TO_DISABLE:
117 raise ValidationError(
118 "'%s' is not a valid Ubuntu archive pocket. You "
119 "can only disable %s." % (
120 pocket, PackageRepository.POCKETS_TO_DISABLE))
121 return values
122
123 def clean_disabled_components(self):
124 values = []
125 for value in self.cleaned_data.get('disabled_components', []):
126 values.extend([s.strip() for s in value.split(',')])
127 # This allows to reset the values of disabled_components if one of the
128 # following is passed over the API:
129 # disabled_components=
130 # disabled_components=''
131 # disabled_components=None
132 # disabled_components=[]
133 if values == [''] or values == ['None'] or values == ['[]']:
134 return []
135 if self.instance is not None and not self.instance.default and values:
136 raise ValidationError(
137 "This is a custom repository. Please update 'components' "
138 "instead.")
139 # Check that a valid component is being passed.
140 for component in values:
141 if component not in PackageRepository.COMPONENTS_TO_DISABLE:
142 raise ValidationError(
143 "'%s' is not a valid Ubuntu archive component. You "
144 "can only disable %s." % (
145 component, PackageRepository.COMPONENTS_TO_DISABLE))
102 return values146 return values
103147
104 def clean_components(self):148 def clean_components(self):
105 values = []149 values = []
106 for value in self.cleaned_data.get('components', []):150 for value in self.cleaned_data.get('components', []):
107 values.extend([s.strip() for s in value.split(',')])151 values.extend([s.strip() for s in value.split(',')])
152 if self.instance is not None and self.instance.default and values:
153 raise ValidationError(
154 "This is a default Ubuntu repository. Please update "
155 "'disabled_components' instead.")
108 return values156 return values
109157
=== modified file 'src/maasserver/forms/tests/test_packagerepository.py'
--- src/maasserver/forms/tests/test_packagerepository.py 2017-02-17 14:23:04 +0000
+++ src/maasserver/forms/tests/test_packagerepository.py 2017-03-16 22:02:44 +0000
@@ -27,10 +27,10 @@
27 arch2 = random.choice(PackageRepository.KNOWN_ARCHES)27 arch2 = random.choice(PackageRepository.KNOWN_ARCHES)
28 dist1 = factory.make_name('dist')28 dist1 = factory.make_name('dist')
29 dist2 = factory.make_name('dist')29 dist2 = factory.make_name('dist')
30 pock1 = factory.make_name('pock')30 pock1 = 'updates'
31 pock2 = factory.make_name('pock')31 pock2 = 'backports'
32 comp1 = factory.make_name('comp')32 comp1 = 'universe'
33 comp2 = factory.make_name('comp')33 comp2 = 'multiverse'
34 enabled = factory.pick_bool()34 enabled = factory.pick_bool()
35 params = {35 params = {
36 'name': name,36 'name': name,
@@ -195,18 +195,40 @@
195 package_repository = factory.make_PackageRepository()195 package_repository = factory.make_PackageRepository()
196 form = PackageRepositoryForm(196 form = PackageRepositoryForm(
197 instance=package_repository,197 instance=package_repository,
198 data={'disabled_pockets': ['val1,val2']})198 data={'disabled_pockets': ['updates,backports']})
199 repo = form.save()199 repo = form.save()
200 self.assertItemsEqual(['val1', 'val2'], repo.disabled_pockets)200 self.assertItemsEqual(['updates', 'backports'], repo.disabled_pockets)
201 form = PackageRepositoryForm(201 form = PackageRepositoryForm(
202 instance=package_repository,202 instance=package_repository,
203 data={'disabled_pockets': ['val1, val2']})203 data={'disabled_pockets': ['updates, backports']})
204 repo = form.save()204 repo = form.save()
205 self.assertItemsEqual(['val1', 'val2'], repo.disabled_pockets)205 self.assertItemsEqual(['updates', 'backports'], repo.disabled_pockets)
206 form = PackageRepositoryForm(206 form = PackageRepositoryForm(
207 instance=package_repository, data={'disabled_pockets': ['val1']})207 instance=package_repository,
208 repo = form.save()208 data={'disabled_pockets': ['updates']})
209 self.assertItemsEqual(['val1'], repo.disabled_pockets)209 repo = form.save()
210 self.assertItemsEqual(['updates'], repo.disabled_pockets)
211
212 def test__disabled_component_comma_cleaning(self):
213 package_repository = factory.make_PackageRepository(
214 default=True, components=[])
215 form = PackageRepositoryForm(
216 instance=package_repository,
217 data={'disabled_components': ['universe,multiverse']})
218 repo = form.save()
219 self.assertItemsEqual(
220 ['universe', 'multiverse'], repo.disabled_components)
221 form = PackageRepositoryForm(
222 instance=package_repository,
223 data={'disabled_components': ['universe, multiverse']})
224 repo = form.save()
225 self.assertItemsEqual(
226 ['universe', 'multiverse'], repo.disabled_components)
227 form = PackageRepositoryForm(
228 instance=package_repository,
229 data={'disabled_components': ['universe']})
230 repo = form.save()
231 self.assertItemsEqual(['universe'], repo.disabled_components)
210232
211 def test__component_comma_cleaning(self):233 def test__component_comma_cleaning(self):
212 package_repository = factory.make_PackageRepository()234 package_repository = factory.make_PackageRepository()
213235
=== added file 'src/maasserver/migrations/builtin/maasserver/0116_add_disabled_components_for_mirrors.py'
--- src/maasserver/migrations/builtin/maasserver/0116_add_disabled_components_for_mirrors.py 1970-01-01 00:00:00 +0000
+++ src/maasserver/migrations/builtin/maasserver/0116_add_disabled_components_for_mirrors.py 2017-03-16 22:02:44 +0000
@@ -0,0 +1,20 @@
1# -*- coding: utf-8 -*-
2from __future__ import unicode_literals
3
4from django.db import migrations, models
5import django.contrib.postgres.fields
6
7
8class Migration(migrations.Migration):
9
10 dependencies = [
11 ('maasserver', '0115_additional_boot_resource_filetypes'),
12 ]
13
14 operations = [
15 migrations.AddField(
16 model_name='packagerepository',
17 name='disabled_components',
18 field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), size=None, default=list, blank=True, null=True),
19 ),
20 ]
021
=== modified file 'src/maasserver/models/packagerepository.py'
--- src/maasserver/models/packagerepository.py 2016-09-01 13:12:15 +0000
+++ src/maasserver/models/packagerepository.py 2017-03-16 22:02:44 +0000
@@ -74,6 +74,9 @@
74 def get_pockets_to_disable(self):74 def get_pockets_to_disable(self):
75 return PackageRepository.POCKETS_TO_DISABLE75 return PackageRepository.POCKETS_TO_DISABLE
7676
77 def get_components_to_disable(self):
78 return PackageRepository.COMPONENTS_TO_DISABLE
79
77 def get_default_archive(self, arch):80 def get_default_archive(self, arch):
78 return PackageRepository.objects.filter(81 return PackageRepository.objects.filter(
79 arches__contains=[arch],82 arches__contains=[arch],
@@ -94,6 +97,8 @@
94 PORTS_ARCHES = ['armhf', 'arm64', 'ppc64el']97 PORTS_ARCHES = ['armhf', 'arm64', 'ppc64el']
95 KNOWN_ARCHES = MAIN_ARCHES + PORTS_ARCHES98 KNOWN_ARCHES = MAIN_ARCHES + PORTS_ARCHES
96 POCKETS_TO_DISABLE = ['updates', 'security', 'backports']99 POCKETS_TO_DISABLE = ['updates', 'security', 'backports']
100 COMPONENTS_TO_DISABLE = ['restricted', 'universe', 'multiverse']
101 KNOWN_COMPONENTS = ['main', 'restricted', 'universe', 'multiverse']
97102
98 class Meta(DefaultMeta):103 class Meta(DefaultMeta):
99 """Needed for South to recognize this model."""104 """Needed for South to recognize this model."""
@@ -111,6 +116,9 @@
111 disabled_pockets = ArrayField(116 disabled_pockets = ArrayField(
112 TextField(), blank=True, null=True, default=list)117 TextField(), blank=True, null=True, default=list)
113118
119 disabled_components = ArrayField(
120 TextField(), blank=True, null=True, default=list)
121
114 components = ArrayField(TextField(), blank=True, null=True, default=list)122 components = ArrayField(TextField(), blank=True, null=True, default=list)
115123
116 arches = ArrayField(TextField(), blank=True, null=True, default=list)124 arches = ArrayField(TextField(), blank=True, null=True, default=list)
117125
=== modified file 'src/maasserver/models/tests/test_packagerepository.py'
--- src/maasserver/models/tests/test_packagerepository.py 2016-08-25 19:31:20 +0000
+++ src/maasserver/models/tests/test_packagerepository.py 2017-03-16 22:02:44 +0000
@@ -61,3 +61,8 @@
61 self.assertEqual(61 self.assertEqual(
62 PackageRepository.objects.get_pockets_to_disable(),62 PackageRepository.objects.get_pockets_to_disable(),
63 PackageRepository.POCKETS_TO_DISABLE)63 PackageRepository.POCKETS_TO_DISABLE)
64
65 def test_get_components_to_disable(self):
66 self.assertEqual(
67 PackageRepository.objects.get_components_to_disable(),
68 PackageRepository.COMPONENTS_TO_DISABLE)
6469
=== modified file 'src/maasserver/static/js/angular/controllers/settings.js'
--- src/maasserver/static/js/angular/controllers/settings.js 2016-09-21 14:49:23 +0000
+++ src/maasserver/static/js/angular/controllers/settings.js 2017-03-16 22:02:44 +0000
@@ -30,6 +30,8 @@
30 GeneralManager.getData("known_architectures");30 GeneralManager.getData("known_architectures");
31 $scope.pockets_to_disable =31 $scope.pockets_to_disable =
32 GeneralManager.getData("pockets_to_disable");32 GeneralManager.getData("pockets_to_disable");
33 $scope.components_to_disable =
34 GeneralManager.getData("components_to_disable");
33 $scope.packageRepositoriesManager = PackageRepositoriesManager;35 $scope.packageRepositoriesManager = PackageRepositoriesManager;
34 $scope.repositories =36 $scope.repositories =
35 PackageRepositoriesManager.getItems();37 PackageRepositoriesManager.getItems();
3638
=== modified file 'src/maasserver/static/js/angular/controllers/tests/test_settings.js'
--- src/maasserver/static/js/angular/controllers/tests/test_settings.js 2016-09-22 15:05:38 +0000
+++ src/maasserver/static/js/angular/controllers/tests/test_settings.js 2017-03-16 22:02:44 +0000
@@ -116,6 +116,8 @@
116 GeneralManager.getData("known_architectures"));116 GeneralManager.getData("known_architectures"));
117 expect($scope.pockets_to_disable).toBe(117 expect($scope.pockets_to_disable).toBe(
118 GeneralManager.getData("pockets_to_disable"));118 GeneralManager.getData("pockets_to_disable"));
119 expect($scope.components_to_disable).toBe(
120 GeneralManager.getData("components_to_disable"));
119 expect($scope.packageRepositoriesManager).toBe(121 expect($scope.packageRepositoriesManager).toBe(
120 PackageRepositoriesManager);122 PackageRepositoriesManager);
121 expect($scope.repositories).toBe(123 expect($scope.repositories).toBe(
122124
=== modified file 'src/maasserver/static/js/angular/factories/general.js'
--- src/maasserver/static/js/angular/factories/general.js 2016-09-22 15:05:38 +0000
+++ src/maasserver/static/js/angular/factories/general.js 2017-03-16 22:02:44 +0000
@@ -77,6 +77,13 @@
77 polling: false,77 polling: false,
78 nextPromise: null78 nextPromise: null
79 },79 },
80 components_to_disable: {
81 method: "general.components_to_disable",
82 data: [],
83 loaded: false,
84 polling: false,
85 nextPromise: null
86 },
80 hwe_kernels: {87 hwe_kernels: {
81 method: "general.hwe_kernels",88 method: "general.hwe_kernels",
82 data: [],89 data: [],
8390
=== modified file 'src/maasserver/static/js/angular/factories/tests/test_general.js'
--- src/maasserver/static/js/angular/factories/tests/test_general.js 2016-08-22 04:10:43 +0000
+++ src/maasserver/static/js/angular/factories/tests/test_general.js 2017-03-16 22:02:44 +0000
@@ -53,9 +53,9 @@
53 ["machine_actions", "device_actions", "region_controller_actions",53 ["machine_actions", "device_actions", "region_controller_actions",
54 "rack_controller_actions", "region_and_rack_controller_actions",54 "rack_controller_actions", "region_and_rack_controller_actions",
55 "architectures", "known_architectures", "pockets_to_disable",55 "architectures", "known_architectures", "pockets_to_disable",
56 "hwe_kernels", "min_hwe_kernels", "default_min_hwe_kernel",56 "components_to_disable", "hwe_kernels", "min_hwe_kernels",
57 "osinfo", "bond_options", "version", "power_types",57 "default_min_hwe_kernel", "osinfo", "bond_options",
58 "release_options"]);58 "version", "power_types", "release_options"]);
59 });59 });
6060
61 it("_data.machine_actions has correct data", function() {61 it("_data.machine_actions has correct data", function() {
@@ -136,6 +136,15 @@
136 expect(ptd.nextPromise).toBeNull();136 expect(ptd.nextPromise).toBeNull();
137 });137 });
138138
139 it("_data.components_to_disable has correct data", function() {
140 var ptd = GeneralManager._data.components_to_disable;
141 expect(ptd.method).toBe("general.components_to_disable");
142 expect(ptd.data).toEqual([]);
143 expect(ptd.loaded).toBe(false);
144 expect(ptd.polling).toBe(false);
145 expect(ptd.nextPromise).toBeNull();
146 });
147
139 it("_data.hwe_kernels has correct data", function() {148 it("_data.hwe_kernels has correct data", function() {
140 var hwe_kernels = GeneralManager._data.hwe_kernels;149 var hwe_kernels = GeneralManager._data.hwe_kernels;
141 expect(hwe_kernels.method).toBe("general.hwe_kernels");150 expect(hwe_kernels.method).toBe("general.hwe_kernels");
@@ -302,6 +311,7 @@
302 GeneralManager._data.architectures.loaded = true;311 GeneralManager._data.architectures.loaded = true;
303 GeneralManager._data.known_architectures.loaded = true;312 GeneralManager._data.known_architectures.loaded = true;
304 GeneralManager._data.pockets_to_disable.loaded = true;313 GeneralManager._data.pockets_to_disable.loaded = true;
314 GeneralManager._data.components_to_disable.loaded = true;
305 GeneralManager._data.hwe_kernels.loaded = true;315 GeneralManager._data.hwe_kernels.loaded = true;
306 GeneralManager._data.osinfo.loaded = true;316 GeneralManager._data.osinfo.loaded = true;
307 GeneralManager._data.bond_options.loaded = true;317 GeneralManager._data.bond_options.loaded = true;
@@ -321,6 +331,7 @@
321 GeneralManager._data.architectures.loaded = true;331 GeneralManager._data.architectures.loaded = true;
322 GeneralManager._data.known_architectures.loaded = true;332 GeneralManager._data.known_architectures.loaded = true;
323 GeneralManager._data.pockets_to_disable.loaded = true;333 GeneralManager._data.pockets_to_disable.loaded = true;
334 GeneralManager._data.components_to_disable.loaded = true;
324 GeneralManager._data.hwe_kernels.loaded = true;335 GeneralManager._data.hwe_kernels.loaded = true;
325 GeneralManager._data.min_hwe_kernels.loaded = true;336 GeneralManager._data.min_hwe_kernels.loaded = true;
326 GeneralManager._data.default_min_hwe_kernel.loaded = true;337 GeneralManager._data.default_min_hwe_kernel.loaded = true;
@@ -353,6 +364,7 @@
353 GeneralManager._data.architectures.polling = false;364 GeneralManager._data.architectures.polling = false;
354 GeneralManager._data.known_architectures.polling = false;365 GeneralManager._data.known_architectures.polling = false;
355 GeneralManager._data.pockets_to_disable.polling = false;366 GeneralManager._data.pockets_to_disable.polling = false;
367 GeneralManager._data.components_to_disable.polling = false;
356 GeneralManager._data.hwe_kernels.polling = false;368 GeneralManager._data.hwe_kernels.polling = false;
357 GeneralManager._data.osinfo.polling = false;369 GeneralManager._data.osinfo.polling = false;
358 expect(GeneralManager.isPolling()).toBe(true);370 expect(GeneralManager.isPolling()).toBe(true);
@@ -363,6 +375,7 @@
363 GeneralManager._data.architectures.polling = true;375 GeneralManager._data.architectures.polling = true;
364 GeneralManager._data.known_architectures.polling = true;376 GeneralManager._data.known_architectures.polling = true;
365 GeneralManager._data.pockets_to_disable.polling = true;377 GeneralManager._data.pockets_to_disable.polling = true;
378 GeneralManager._data.components_to_disable.polling = true;
366 GeneralManager._data.hwe_kernels.polling = true;379 GeneralManager._data.hwe_kernels.polling = true;
367 GeneralManager._data.osinfo.polling = true;380 GeneralManager._data.osinfo.polling = true;
368 expect(GeneralManager.isPolling()).toBe(true);381 expect(GeneralManager.isPolling()).toBe(true);
369382
=== modified file 'src/maasserver/static/partials/settings.html'
--- src/maasserver/static/partials/settings.html 2017-02-17 14:23:04 +0000
+++ src/maasserver/static/partials/settings.html 2017-03-16 22:02:44 +0000
@@ -488,6 +488,11 @@
488 <maas-obj-field type="checkboxes" key="disabled_pockets" label="Disabled Pockets"488 <maas-obj-field type="checkboxes" key="disabled_pockets" label="Disabled Pockets"
489 label-width="two" input-width="three" values="pockets_to_disable"></maas-obj-field>489 label-width="two" input-width="three" values="pockets_to_disable"></maas-obj-field>
490 </div>490 </div>
491 <div class="form__group u-align--left-mobile"
492 data-ng-if="isMirror(editRepository) && !isPPA(editRepository)">
493 <maas-obj-field type="checkboxes" key="disabled_components" label="Disabled Components"
494 label-width="two" input-width="three" values="components_to_disable"></maas-obj-field>
495 </div>
491 </div>496 </div>
492 </div>497 </div>
493 <div class="table__row is-active">498 <div class="table__row is-active">
494499
=== modified file 'src/maasserver/testing/factory.py'
--- src/maasserver/testing/factory.py 2017-03-15 16:40:59 +0000
+++ src/maasserver/testing/factory.py 2017-03-16 22:02:44 +0000
@@ -2037,7 +2037,8 @@
20372037
2038 def make_PackageRepository(2038 def make_PackageRepository(
2039 self, name=None, url=None, arches=None, default=False, key=None,2039 self, name=None, url=None, arches=None, default=False, key=None,
2040 distributions=None, components=None, disabled_pockets=None):2040 distributions=None, components=None, disabled_pockets=None,
2041 disabled_components=None):
2041 if name is None:2042 if name is None:
2042 name = self.make_name("name")2043 name = self.make_name("name")
2043 if url is None:2044 if url is None:
@@ -2051,7 +2052,8 @@
2051 return PackageRepository.objects.create(2052 return PackageRepository.objects.create(
2052 name=name, url=url,2053 name=name, url=url,
2053 distributions=distributions, disabled_pockets=disabled_pockets,2054 distributions=distributions, disabled_pockets=disabled_pockets,
2054 components=components, arches=arches, key=key, default=default)2055 components=components, arches=arches, key=key, default=default,
2056 disabled_components=disabled_components)
20552057
2056 def make_Notification(2058 def make_Notification(
2057 self, message=None, *, ident=None, user=None, users=False,2059 self, message=None, *, ident=None, user=None, users=False,
20582060
=== modified file 'src/maasserver/tests/test_preseed.py'
--- src/maasserver/tests/test_preseed.py 2017-03-09 08:55:12 +0000
+++ src/maasserver/tests/test_preseed.py 2017-03-16 22:02:44 +0000
@@ -1230,6 +1230,31 @@
1230 preseed['apt']['disable_suites'],1230 preseed['apt']['disable_suites'],
1231 archive.disabled_pockets)1231 archive.disabled_pockets)
12321232
1233 def test_compose_curtin_archive_config_with_disabled_pockets(self):
1234 """Test that main archive has a configuration that includes
1235 disabled_pockets. If so, MAAS will create its own sources_list
1236 instead of letting curtin/cloud-init create it based on its own
1237 template"""
1238 PackageRepository.objects.all().delete()
1239 node = self.make_fastpath_node('amd64')
1240 node.osystem = 'ubuntu'
1241 node.distro_series = 'xenial'
1242 main_url = 'http://us.archive.ubuntu.com/ubuntu'
1243 factory.make_PackageRepository(
1244 url=main_url, default=True, arches=['i386', 'amd64'],
1245 disabled_pockets=['updates', 'backports'],
1246 disabled_components=['universe', 'multiverse'])
1247 self.configure_get_boot_images_for_node(node, 'xinstall')
1248 # compose_curtin_archive_config returns a list.
1249 userdata = compose_curtin_archive_config(node)
1250 preseed = yaml.safe_load(userdata[0])
1251 self.assertThat(
1252 preseed['apt']['sources_list'],
1253 Contains('$RELEASE main restricted'))
1254 self.assertThat(
1255 preseed['apt']['sources_list'],
1256 Contains('$RELEASE-security main restricted'))
1257
1233 def test_compose_curtin_archive_config_has_ppa(self):1258 def test_compose_curtin_archive_config_has_ppa(self):
1234 node = self.make_fastpath_node('i386')1259 node = self.make_fastpath_node('i386')
1235 node.osystem = 'ubuntu'1260 node.osystem = 'ubuntu'
@@ -1283,7 +1308,7 @@
1283 )1308 )
1284 self.assertThat(1309 self.assertThat(
1285 preseed['apt']['sources'][ppa_name]['source'],1310 preseed['apt']['sources'][ppa_name]['source'],
1286 ContainsAll("deb %s %s main" % (ppa_first.url, node.distro_series))1311 ContainsAll("deb %s $RELEASE main" % ppa_first.url)
1287 )1312 )
1288 # Clean up PPA name1313 # Clean up PPA name
1289 ppa_name = make_clean_repo_name(ppa_second)1314 ppa_name = make_clean_repo_name(ppa_second)
@@ -1293,8 +1318,7 @@
1293 )1318 )
1294 self.assertThat(1319 self.assertThat(
1295 preseed['apt']['sources'][ppa_name]['source'],1320 preseed['apt']['sources'][ppa_name]['source'],
1296 ContainsAll("deb %s %s main" % (1321 ContainsAll("deb %s $RELEASE main" % ppa_second.url))
1297 ppa_second.url, node.distro_series)))
12981322
1299 def test_compose_curtin_archive_config_has_custom_repository(self):1323 def test_compose_curtin_archive_config_has_custom_repository(self):
1300 node = self.make_fastpath_node('i386')1324 node = self.make_fastpath_node('i386')
@@ -1318,8 +1342,7 @@
1318 )1342 )
1319 self.assertThat(1343 self.assertThat(
1320 preseed['apt']['sources'][repo_name]['source'],1344 preseed['apt']['sources'][repo_name]['source'],
1321 ContainsAll("deb %s %s main" % (1345 ContainsAll("deb %s $RELEASE main" % repository.url))
1322 repository.url, node.distro_series)))
13231346
1324 def test_compose_curtin_archive_config_custom_repo_with_components(self):1347 def test_compose_curtin_archive_config_custom_repo_with_components(self):
1325 node = self.make_fastpath_node('i386')1348 node = self.make_fastpath_node('i386')
@@ -1348,8 +1371,8 @@
1348 )1371 )
1349 self.assertThat(1372 self.assertThat(
1350 preseed['apt']['sources'][repo_name]['source'],1373 preseed['apt']['sources'][repo_name]['source'],
1351 ContainsAll("deb %s %s %s" % (1374 ContainsAll("deb %s $RELEASE %s" % (
1352 repository.url, node.distro_series, components)))1375 repository.url, components)))
13531376
1354 def test_compose_curtin_archive_config_custom_repo_components_dists(self):1377 def test_compose_curtin_archive_config_custom_repo_components_dists(self):
1355 node = self.make_fastpath_node('i386')1378 node = self.make_fastpath_node('i386')
13561379
=== modified file 'src/maasserver/websockets/handlers/general.py'
--- src/maasserver/websockets/handlers/general.py 2017-01-28 00:51:47 +0000
+++ src/maasserver/websockets/handlers/general.py 2017-03-16 22:02:44 +0000
@@ -46,6 +46,7 @@
46 'architectures',46 'architectures',
47 'known_architectures',47 'known_architectures',
48 'pockets_to_disable',48 'pockets_to_disable',
49 'components_to_disable',
49 'hwe_kernels',50 'hwe_kernels',
50 'min_hwe_kernels',51 'min_hwe_kernels',
51 'default_min_hwe_kernel',52 'default_min_hwe_kernel',
@@ -74,6 +75,10 @@
74 """Return pockets that can be disabled."""75 """Return pockets that can be disabled."""
75 return PackageRepository.objects.get_pockets_to_disable()76 return PackageRepository.objects.get_pockets_to_disable()
7677
78 def components_to_disable(self, params):
79 "Return compoennts that can be disable for default Ubuntu archives"
80 return PackageRepository.objects.get_components_to_disable()
81
77 def hwe_kernels(self, params):82 def hwe_kernels(self, params):
78 """Return all supported hwe_kernels."""83 """Return all supported hwe_kernels."""
79 return list_hwe_kernel_choices(84 return list_hwe_kernel_choices(
8085
=== modified file 'src/maasserver/websockets/handlers/tests/test_general.py'
--- src/maasserver/websockets/handlers/tests/test_general.py 2017-03-02 09:41:37 +0000
+++ src/maasserver/websockets/handlers/tests/test_general.py 2017-03-16 22:02:44 +0000
@@ -103,6 +103,12 @@
103 PackageRepository.objects.get_pockets_to_disable(),103 PackageRepository.objects.get_pockets_to_disable(),
104 handler.pockets_to_disable({}))104 handler.pockets_to_disable({}))
105105
106 def test_components_to_disable(self):
107 handler = GeneralHandler(factory.make_User(), {})
108 self.assertEqual(
109 PackageRepository.objects.get_components_to_disable(),
110 handler.components_to_disable({}))
111
106 def test_hwe_kernels(self):112 def test_hwe_kernels(self):
107 expected_output = self.make_boot_sources()113 expected_output = self.make_boot_sources()
108 handler = GeneralHandler(factory.make_User(), {})114 handler = GeneralHandler(factory.make_User(), {})
109115
=== modified file 'src/maasserver/websockets/handlers/tests/test_packagerepository.py'
--- src/maasserver/websockets/handlers/tests/test_packagerepository.py 2017-02-17 14:23:04 +0000
+++ src/maasserver/websockets/handlers/tests/test_packagerepository.py 2017-03-16 22:02:44 +0000
@@ -34,6 +34,7 @@
34 'url': package_repository.url,34 'url': package_repository.url,
35 'distributions': package_repository.distributions,35 'distributions': package_repository.distributions,
36 'disabled_pockets': package_repository.disabled_pockets,36 'disabled_pockets': package_repository.disabled_pockets,
37 'disabled_components': package_repository.disabled_components,
37 'components': package_repository.components,38 'components': package_repository.components,
38 'arches': package_repository.arches,39 'arches': package_repository.arches,
39 'key': package_repository.key,40 'key': package_repository.key,