Merge ~bjornt/maas:peer-proxy-ui into maas:master

Proposed by Björn Tillenius
Status: Merged
Approved by: Björn Tillenius
Approved revision: 4b3a4a04c6ae0f0afc967f301d873f130e1d4f28
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~bjornt/maas:peer-proxy-ui
Merge into: maas:master
Diff against target: 582 lines (+410/-14)
10 files modified
Makefile (+1/-0)
src/maasserver/forms/__init__.py (+11/-2)
src/maasserver/static/js/angular/directives/proxy_settings.js (+41/-0)
src/maasserver/static/js/angular/directives/tests/test_proxy_settings.js (+190/-0)
src/maasserver/static/partials/proxy-settings.html (+48/-0)
src/maasserver/templates/maasserver/settings.html (+38/-4)
src/maasserver/views/combo.py (+1/-0)
src/maasserver/views/settings.py (+22/-4)
src/maasserver/views/tests/test_settings.py (+45/-2)
src/maastesting/karma.conf.js (+13/-2)
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Andres Rodriguez (community) Needs Fixing
Review via email: mp+327591@code.launchpad.net

Commit message

Change the proxy settings UI to enable configuring a peer proxy.

The network part of the settings page was split into "proxy", "ntp", and "dns", since it was getting too big.

Description of the change

The UI for setting proxy uses angular, so that the widget gets a bit more dynamic and shows the field for proxy URL only when needed. It still renders the required input fields so that the django form still is used to update the proxy settings. Converting it fully to angular was too much work.

Note that the single Javascript test I added is disabled. I couldn't get the tests to work. I'm proposing this for merge anyway, so that the rest of the code can be reviewed, and for someone else to figure out how to get the Javascript tests to work.

The problem I had with the Javascript tests is that the scope doesn't seem to be connected to the template. Inside the template, all scope variables are empty for some reason.

Note also that the UI needs some love to make it look nicer. I wanted to get some general feedback fore spending time on that, though.

To post a comment you must log in.
Revision history for this message
Lee Trager (ltrager) wrote :

I really like splitting NTP and DNS into different sections, it makes the page much easier to read. I think we may want to use a drop down to select the proxy mode. The text field would be disabled for don't use and built-in. I'd ask Maria from design this before reworking incase I'm wrong.

I poked around and can't figure out why MAAS.templates is currently broken and wasn't able to come up with anything, Blake might know.

Revision history for this message
Andres Rodriguez (andreserl) wrote :
Download full text (6.1 KiB)

Maria's design and preference is what we have, but I'll talk to her
tomorrow!

On Fri, Jul 21, 2017 at 1:45 AM Lee Trager <email address hidden> wrote:

> I really like splitting NTP and DNS into different sections, it makes the
> page much easier to read. I think we may want to use a drop down to select
> the proxy mode. The text field would be disabled for don't use and
> built-in. I'd ask Maria from design this before reworking incase I'm wrong.
>
> I poked around and can't figure out why MAAS.templates is currently broken
> and wasn't able to come up with anything, Blake might know.
>
> Diff comments:
>
> > diff --git
> a/src/maasserver/static/js/angular/directives/proxy_settings.js
> b/src/maasserver/static/js/angular/directives/proxy_settings.js
> > new file mode 100644
> > index 0000000..0850058
> > --- /dev/null
> > +++ b/src/maasserver/static/js/angular/directives/proxy_settings.js
> > @@ -0,0 +1,43 @@
> > +/* Copyright 2018 Canonical Ltd. This software is licensed under the
>
> Pulling code from the future? ;)
>
> > + * GNU Affero General Public License version 3 (see the file LICENSE).
> > + *
> > + * Proxy settings directive.
> > +*/
> > +
> > +angular.module('MAAS').directive('maasProxySettings', [
> > + '$sce', 'ConfigsManager', 'ManagerHelperService', 'JSONService',
> > + function($sce, ConfigsManager, ManagerHelperService, JSONService) {
> > + return {
> > + restrict: "E",
> > + scope: {},
> > + templateUrl: 'static/partials/proxy-settings.html',
> > + controller: function($scope, $rootScope, $element,
> $document) {
> > + $scope.loading = true;
> > + $scope.configManager = ConfigsManager;
> > + var managers = [ConfigsManager];
> > + ManagerHelperService.loadManagers(
> > + $scope, managers).then(function() {
> > + $scope.loading = false;
> > + $scope.httpProxy = ConfigsManager.getItemFromList(
> > + "http_proxy");
> > + $scope.enableHttpProxy =
> ConfigsManager.getItemFromList(
> > + "enable_http_proxy");
> > + $scope.usePeerProxy =
> ConfigsManager.getItemFromList(
> > + "use_peer_proxy");
> > + if ($scope.enableHttpProxy.value) {
> > + if ($scope.httpProxy.value) {
> > + if ($scope.usePeerProxy.value) {
> > + $scope.proxy_type = "peer-proxy";
> > + } else {
> > + $scope.proxy_type = "external-proxy";
> > + }
> > + } else {
> > + $scope.proxy_type = "builtin-proxy";
> > + }
> > + } else {
> > + $scope.proxy_type = "no-proxy";
> > + }
> > + });
> > + }
> > + };
> > + }]);
> > diff --git
> a/src/maasserver/static/js/angular/directives/tests/test_proxy_settings.js
> b/src/maasserver/static/js/angula...

Read more...

Revision history for this message
Blake Rouse (blake-rouse) wrote :

Looks like your using "{{ }}" in the angular directives in the HTML. I am very surprised that it works, since we set the template tag parser differently in MAAS so it works with Django. "{{ }}" is also used by Django, so we change it to "{$ $}".

@Lee - The issue is that you need the new dependency. So if you do "make clean && make" you would get the new dependency.

review: Needs Fixing
Revision history for this message
Blake Rouse (blake-rouse) wrote :

Also looks like you have debugger statement in JS test.

./src/maasserver/static/js/angular/directives/tests/test_proxy_settings.js
      62: Unexpected 'debugger'.
      62: Line contains a call to debugger.
Makefile:336: recipe for target 'lint-js' failed
make: *** [lint-js] Error 123

review: Needs Fixing
Revision history for this message
Andres Rodriguez (andreserl) wrote :

A few inline fixes wrt wording.

review: Needs Fixing
Revision history for this message
Björn Tillenius (bjornt) wrote :

> Looks like your using "{{ }}" in the angular directives in the HTML. I am very
> surprised that it works, since we set the template tag parser differently in
> MAAS so it works with Django. "{{ }}" is also used by Django, so we change it
> to "{$ $}".
>
> @Lee - The issue is that you need the new dependency. So if you do "make clean
> && make" you would get the new dependency.

Oh. Yeah, it seems to work with {{ }} as well, but I've changed it to {$ $} now. I was hoping that it would be the cause for the tests not working, but I still can't get the tests to work.

Revision history for this message
Björn Tillenius (bjornt) wrote :

> Also looks like you have debugger statement in JS test.
>
> ./src/maasserver/static/js/angular/directives/tests/test_proxy_settings.js
> 62: Unexpected 'debugger'.
> 62: Line contains a call to debugger.
> Makefile:336: recipe for target 'lint-js' failed
> make: *** [lint-js] Error 123

Fixed. I tried to debug the tests, but had no luck doing so.

Revision history for this message
Björn Tillenius (bjornt) wrote :

I addressed all the inlines comments. Will work on adding more Javascript tests, since Blake provided a patch to get the tests working.

Revision history for this message
Björn Tillenius (bjornt) wrote :

I've pushed the Javascript tests now, so it's ready for re-review.

Revision history for this message
Blake Rouse (blake-rouse) wrote :

Looks good other then the "{{}}" which should be fixed before landing this.

review: Approve
Revision history for this message
Björn Tillenius (bjornt) wrote :

I replaced {{ }} with {$ $}. I also addressed the review comments from design as well.

Revision history for this message
MAAS Lander (maas-lander) wrote :
~bjornt/maas:peer-proxy-ui updated
4b3a4a0... by Björn Tillenius

Format imports.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/Makefile b/Makefile
2index 488fa82..9bbab61 100644
3--- a/Makefile
4+++ b/Makefile
5@@ -187,6 +187,7 @@ define karma-deps
6 karma-opera-launcher@0.3.0
7 karma-phantomjs-launcher@0.2.3
8 karma-failed-reporter@0.0.3
9+ karma-ng-html2js-preprocessor@1.0.0
10 phantomjs@2.1.7
11 endef
12
13diff --git a/src/maasserver/forms/__init__.py b/src/maasserver/forms/__init__.py
14index a7314d0..35832d6 100644
15--- a/src/maasserver/forms/__init__.py
16+++ b/src/maasserver/forms/__init__.py
17@@ -1418,12 +1418,21 @@ class MAASForm(ConfigForm):
18 enable_analytics = get_config_field('enable_analytics')
19
20
21-class NetworkForm(ConfigForm):
22- """Settings page, Network section."""
23+class ProxyForm(ConfigForm):
24+ """Settings page, Proxy section."""
25 enable_http_proxy = get_config_field('enable_http_proxy')
26+ use_peer_proxy = get_config_field('use_peer_proxy')
27 http_proxy = get_config_field('http_proxy')
28+
29+
30+class DNSForm(ConfigForm):
31+ """Settings page, DNS section."""
32 upstream_dns = get_config_field('upstream_dns')
33 dnssec_validation = get_config_field('dnssec_validation')
34+
35+
36+class NTPForm(ConfigForm):
37+ """Settings page, NTP section."""
38 ntp_servers = get_config_field('ntp_servers')
39 ntp_external_only = get_config_field('ntp_external_only')
40
41diff --git a/src/maasserver/static/js/angular/directives/proxy_settings.js b/src/maasserver/static/js/angular/directives/proxy_settings.js
42new file mode 100644
43index 0000000..3e97777
44--- /dev/null
45+++ b/src/maasserver/static/js/angular/directives/proxy_settings.js
46@@ -0,0 +1,41 @@
47+/* Copyright 2017 Canonical Ltd. This software is licensed under the
48+ * GNU Affero General Public License version 3 (see the file LICENSE).
49+ *
50+ * Proxy settings directive.
51+*/
52+
53+angular.module('MAAS').directive('maasProxySettings', [
54+ '$sce', 'ConfigsManager', 'ManagerHelperService', 'JSONService',
55+ function($sce, ConfigsManager, ManagerHelperService, JSONService) {
56+ return {
57+ restrict: "E",
58+ scope: {},
59+ templateUrl: 'static/partials/proxy-settings.html',
60+ controller: function($scope, $rootScope, $element, $document) {
61+ $scope.loading = true;
62+ ManagerHelperService.loadManager(
63+ $scope, ConfigsManager).then(function() {
64+ $scope.loading = false;
65+ $scope.httpProxy = ConfigsManager.getItemFromList(
66+ "http_proxy");
67+ $scope.enableHttpProxy = ConfigsManager.getItemFromList(
68+ "enable_http_proxy");
69+ $scope.usePeerProxy = ConfigsManager.getItemFromList(
70+ "use_peer_proxy");
71+ if ($scope.enableHttpProxy.value) {
72+ if ($scope.httpProxy.value) {
73+ if ($scope.usePeerProxy.value) {
74+ $scope.proxy_type = "peer-proxy";
75+ } else {
76+ $scope.proxy_type = "external-proxy";
77+ }
78+ } else {
79+ $scope.proxy_type = "builtin-proxy";
80+ }
81+ } else {
82+ $scope.proxy_type = "no-proxy";
83+ }
84+ });
85+ }
86+ };
87+ }]);
88diff --git a/src/maasserver/static/js/angular/directives/tests/test_proxy_settings.js b/src/maasserver/static/js/angular/directives/tests/test_proxy_settings.js
89new file mode 100644
90index 0000000..ce06777
91--- /dev/null
92+++ b/src/maasserver/static/js/angular/directives/tests/test_proxy_settings.js
93@@ -0,0 +1,190 @@
94+/* Copyright 2017 Canonical Ltd. This software is licensed under the
95+ * GNU Affero General Public License version 3 (see the file LICENSE).
96+ *
97+ * Unit tests for MAAS proxy settings directive.
98+ */
99+
100+describe("maasProxySettings", function() {
101+
102+ // Load the MAAS module.
103+ beforeEach(module("MAAS"));
104+ // Make the templates available.
105+ beforeEach(module("MAAS.templates"));
106+
107+ // Get required angular pieces and create a new scope before each test.
108+ var $scope, $compile, $q, ConfigsManager, ManagerHelperService;
109+ beforeEach(inject(function($rootScope, $injector) {
110+ $scope = $rootScope.$new();
111+ $compile = $injector.get("$compile");
112+ $q = $injector.get("$q");
113+ ConfigsManager = $injector.get("ConfigsManager");
114+ ManagerHelperService = $injector.get("ManagerHelperService");
115+ }));
116+
117+ // Create the config options.
118+ var httpProxy, enableHttpProxy, usePeerProxy;
119+ beforeEach(function() {
120+ httpProxy = {
121+ name: 'http_proxy',
122+ value: ''
123+ };
124+ enableHttpProxy = {
125+ name: 'enable_http_proxy',
126+ value: true
127+ };
128+ usePeerProxy = {
129+ name: 'use_peer_proxy',
130+ value: false
131+ };
132+ ConfigsManager._items = [httpProxy, enableHttpProxy, usePeerProxy];
133+ });
134+
135+ // Return the compiled directive.
136+ function compileDirective() {
137+ var loadManagerDefer = $q.defer();
138+ spyOn(ManagerHelperService, "loadManager").and.returnValue(
139+ loadManagerDefer.promise);
140+
141+ var html = "<div><maas-proxy-settings></maas-proxy-settings></div>";
142+ var directive = $compile(html)($scope);
143+ $scope.$digest();
144+ loadManagerDefer.resolve();
145+ $scope.$digest();
146+
147+ return angular.element(
148+ directive.find("maas-proxy-settings"));
149+ }
150+
151+ it("no-proxy without http proxy", function() {
152+ enableHttpProxy.value = false;
153+ usePeerProxy.value = false;
154+ httpProxy.value = '';
155+ var directive = compileDirective(
156+ '<maas-proxy-settings></maas-proxy-settings>');
157+ var scope = directive.isolateScope();
158+ var enableProxyField = angular.element(directive.find(
159+ "#id_proxy-enable_http_proxy"));
160+ var httpProxyField = angular.element(directive.find(
161+ "#id_proxy-http_proxy"));
162+ var usePeerProxyField = angular.element(directive.find(
163+ "#id_proxy-use_peer_proxy"));
164+ expect(scope.proxy_type).toBe("no-proxy");
165+ expect(enableProxyField.attr("value")).toBe("False");
166+ expect(httpProxyField.attr("value")).toBe(undefined);
167+ expect(usePeerProxyField.attr("value")).toBe("False");
168+ });
169+
170+ it("no-proxy with http_proxy set", function() {
171+ enableHttpProxy.value = false;
172+ usePeerProxy.value = false;
173+ httpProxy.value = 'http://proxy.example.com/';
174+ var directive = compileDirective(
175+ '<maas-proxy-settings></maas-proxy-settings>');
176+ var scope = directive.isolateScope();
177+ var enableProxyField = angular.element(directive.find(
178+ "#id_proxy-enable_http_proxy"));
179+ var httpProxyField = angular.element(directive.find(
180+ "#id_proxy-http_proxy"));
181+ var usePeerProxyField = angular.element(directive.find(
182+ "#id_proxy-use_peer_proxy"));
183+ expect(scope.proxy_type).toBe("no-proxy");
184+ expect(enableProxyField.attr("value")).toBe("False");
185+ expect(httpProxyField.attr("value")).toBe(undefined);
186+ expect(usePeerProxyField.attr("value")).toBe("False");
187+ });
188+
189+ it("no-proxy with use_peer_proxy set", function() {
190+ enableHttpProxy.value = false;
191+ usePeerProxy.value = true;
192+ httpProxy.value = 'http://peer-proxy.example.com/';
193+ var directive = compileDirective(
194+ '<maas-proxy-settings></maas-proxy-settings>');
195+ var scope = directive.isolateScope();
196+ var enableProxyField = angular.element(directive.find(
197+ "#id_proxy-enable_http_proxy"));
198+ var httpProxyField = angular.element(directive.find(
199+ "#id_proxy-http_proxy"));
200+ var usePeerProxyField = angular.element(directive.find(
201+ "#id_proxy-use_peer_proxy"));
202+ expect(scope.proxy_type).toBe("no-proxy");
203+ expect(enableProxyField.attr("value")).toBe("False");
204+ expect(httpProxyField.attr("value")).toBe(undefined);
205+ expect(usePeerProxyField.attr("value")).toBe("False");
206+ });
207+
208+ it("builtin-proxy", function() {
209+ enableHttpProxy.value = true;
210+ usePeerProxy.value = false;
211+ httpProxy.value = '';
212+ var directive = compileDirective(
213+ '<maas-proxy-settings></maas-proxy-settings>');
214+ var scope = directive.isolateScope();
215+ var enableProxyField = angular.element(directive.find(
216+ "#id_proxy-enable_http_proxy"));
217+ var httpProxyField = angular.element(directive.find(
218+ "#id_proxy-http_proxy"));
219+ var usePeerProxyField = angular.element(directive.find(
220+ "#id_proxy-use_peer_proxy"));
221+ expect(scope.proxy_type).toBe("builtin-proxy");
222+ expect(enableProxyField.attr("value")).toBe("True");
223+ expect(httpProxyField.attr("value")).toBe(undefined);
224+ expect(usePeerProxyField.attr("value")).toBe("False");
225+ });
226+
227+ it("builtin-proxy with use_peer_proxy set", function() {
228+ enableHttpProxy.value = true;
229+ usePeerProxy.value = true;
230+ httpProxy.value = '';
231+ var directive = compileDirective(
232+ '<maas-proxy-settings></maas-proxy-settings>');
233+ var scope = directive.isolateScope();
234+ var enableProxyField = angular.element(directive.find(
235+ "#id_proxy-enable_http_proxy"));
236+ var httpProxyField = angular.element(directive.find(
237+ "#id_proxy-http_proxy"));
238+ var usePeerProxyField = angular.element(directive.find(
239+ "#id_proxy-use_peer_proxy"));
240+ expect(scope.proxy_type).toBe("builtin-proxy");
241+ expect(enableProxyField.attr("value")).toBe("True");
242+ expect(httpProxyField.attr("value")).toBe(undefined);
243+ expect(usePeerProxyField.attr("value")).toBe("False");
244+ });
245+
246+ it("external-proxy set", function() {
247+ enableHttpProxy.value = true;
248+ usePeerProxy.value = false;
249+ httpProxy.value = 'http://proxy.example.com/';
250+ var directive = compileDirective(
251+ '<maas-proxy-settings></maas-proxy-settings>');
252+ var scope = directive.isolateScope();
253+ var enableProxyField = angular.element(directive.find(
254+ "#id_proxy-enable_http_proxy"));
255+ var httpProxyField = angular.element(directive.find(
256+ "#id_proxy-http_proxy"));
257+ var usePeerProxyField = angular.element(directive.find(
258+ "#id_proxy-use_peer_proxy"));
259+ expect(scope.proxy_type).toBe("external-proxy");
260+ expect(enableProxyField.attr("value")).toBe("True");
261+ expect(httpProxyField.attr("value")).toBe("http://proxy.example.com/");
262+ expect(usePeerProxyField.attr("value")).toBe("False");
263+ });
264+
265+ it("peer-proxy set", function() {
266+ enableHttpProxy.value = true;
267+ usePeerProxy.value = true;
268+ httpProxy.value = 'http://proxy.example.com/';
269+ var directive = compileDirective(
270+ '<maas-proxy-settings></maas-proxy-settings>');
271+ var scope = directive.isolateScope();
272+ var enableProxyField = angular.element(directive.find(
273+ "#id_proxy-enable_http_proxy"));
274+ var httpProxyField = angular.element(directive.find(
275+ "#id_proxy-http_proxy"));
276+ var usePeerProxyField = angular.element(directive.find(
277+ "#id_proxy-use_peer_proxy"));
278+ expect(scope.proxy_type).toBe("peer-proxy");
279+ expect(enableProxyField.attr("value")).toBe("True");
280+ expect(httpProxyField.attr("value")).toBe("http://proxy.example.com/");
281+ expect(usePeerProxyField.attr("value")).toBe("True");
282+ });
283+});
284diff --git a/src/maasserver/static/partials/proxy-settings.html b/src/maasserver/static/partials/proxy-settings.html
285new file mode 100644
286index 0000000..a3ee21b
287--- /dev/null
288+++ b/src/maasserver/static/partials/proxy-settings.html
289@@ -0,0 +1,48 @@
290+<li class="help-msg">
291+<div>
292+ <label>HTTP proxy used by MAAS to download images, and by provisioned machines for APT packages.</label>
293+</div>
294+<div>
295+ <input type="radio" ng-model="proxy_type" name="maas_proxy"
296+ id="maas_no_proxy" value="no-proxy">
297+ <label for="maas_no_proxy">Don't use a proxy</label>
298+</div>
299+<div>
300+ <input type="radio" ng-model="proxy_type" name="maas_proxy"
301+ id="maas_builtin_proxy" value="builtin-proxy">
302+ <label for="maas_builtin_proxy">MAAS Built-in</label>
303+</div>
304+<div>
305+ <input type="radio" ng-model="proxy_type" name="maas_proxy" id="maas_external_proxy" value="external-proxy">
306+ <label for="maas_external_proxy">External</label>
307+ <input type="url" ng-if="proxy_type == 'external-proxy'"
308+ id="id_proxy-http_proxy" name="proxy-http_proxy"
309+ ng-model="httpProxy.value" value="{$ httpProxy.value $}">
310+ <span ng-if="proxy_type == 'external-proxy'" class="help u-display--block">
311+ <small>
312+ Enter the external proxy URL MAAS will use to download images and
313+ machines to download APT packages.
314+ </small>
315+ </span>
316+</div>
317+<div>
318+ <input type="radio" ng-model="proxy_type" name="maas_proxy" id="maas_peer_proxy" value="peer-proxy">
319+ <label for="maas_peer_proxy">Peer</label>
320+ <input type="url" ng-if="proxy_type == 'peer-proxy'"
321+ id="id_proxy-http_proxy" name="proxy-http_proxy"
322+ ng-model="httpProxy.value" value="{$ httpProxy.value $}">
323+ <span ng-if="proxy_type == 'peer-proxy'" class="help u-display--block">
324+ <small>
325+ Enter the external proxy URL that the MAAS built-in proxy will use
326+ as an upstream cache peer. Machines will be configured to use MAAS'
327+ built-in proxy to download APT packages.
328+ </small>
329+ </span>
330+</div>
331+<input type="hidden" id="id_proxy-enable_http_proxy"
332+ name="proxy-enable_http_proxy"
333+ value="{$ proxy_type == 'no-proxy' ? 'False' : 'True' $}" >
334+<input type="hidden" id="id_proxy-use_peer_proxy"
335+ name="proxy-use_peer_proxy"
336+ value="{$ proxy_type == 'peer-proxy' ? 'True' : 'False' $}" >
337+</li>
338diff --git a/src/maasserver/templates/maasserver/settings.html b/src/maasserver/templates/maasserver/settings.html
339index 9c82b3d..2f87f22 100644
340--- a/src/maasserver/templates/maasserver/settings.html
341+++ b/src/maasserver/templates/maasserver/settings.html
342@@ -5,6 +5,8 @@
343 {% block title %}Settings{% endblock %}
344 {% block page-title %}Settings{% endblock %}
345
346+{% block ng-app %}data-ng-app="MAAS"{% endblock %}
347+
348 {% block head %}
349 {% endblock %}
350
351@@ -157,14 +159,46 @@
352 </div>
353 </div>
354 <div class="eight-col u-border--bottom">
355- <div id="network" class="eight-col">
356- <h2>Network Configuration</h2>
357+ <div id="proxy" class="eight-col">
358+ <h2>Proxy</h2>
359+ <span class="u-text--loading" data-ng-if="loading"><i class="icon icon--loading u-animation--spin"></i> Loading...</span>
360+
361+ <form action="{% url 'settings' %}" method="post">
362+ {% csrf_token %}
363+ <ul>
364+ <maas-proxy-settings data-ng-if="!loading"></maas-proxy-settings>
365+ </ul>
366+ <input type="hidden" name="proxy_submit" value="1" />
367+ <button type="submit" class="button--positive button--inline u-float--right">Save</button>
368+ </form>
369+ </div>
370+ </div>
371+ <div class="eight-col u-border--bottom">
372+ <div id="dns" class="eight-col">
373+ <h2>DNS</h2>
374+ <form action="{% url 'settings' %}" method="post">
375+ {% csrf_token %}
376+ <ul>
377+ {% for field in dns_form %}
378+ {% include "maasserver/form_field.html" %}
379+ {% endfor %}
380+ </ul>
381+ <input type="hidden" name="dns_submit" value="1" />
382+ <button type="submit" class="button--positive button--inline u-float--right">Save</button>
383+ </form>
384+ </div>
385+ </div>
386+ <div class="eight-col u-border--bottom">
387+ <div id="ntp" class="eight-col">
388+ <h2>NTP</h2>
389 <form action="{% url 'settings' %}" method="post">
390 {% csrf_token %}
391 <ul>
392- {% for field in network_form %} {% include "maasserver/form_field.html" %} {% endfor %}
393+ {% for field in ntp_form %}
394+ {% include "maasserver/form_field.html" %}
395+ {% endfor %}
396 </ul>
397- <input type="hidden" name="network_submit" value="1" />
398+ <input type="hidden" name="ntp_submit" value="1" />
399 <button type="submit" class="button--positive button--inline u-float--right">Save</button>
400 </form>
401 </div>
402diff --git a/src/maasserver/views/combo.py b/src/maasserver/views/combo.py
403index b1d6615..55e2d2d 100644
404--- a/src/maasserver/views/combo.py
405+++ b/src/maasserver/views/combo.py
406@@ -119,6 +119,7 @@ MERGE_VIEWS = {
407 "js/angular/directives/placeholder.js",
408 "js/angular/directives/pod_parameters.js",
409 "js/angular/directives/power_parameters.js",
410+ "js/angular/directives/proxy_settings.js",
411 "js/angular/directives/release_name.js",
412 "js/angular/directives/release_options.js",
413 "js/angular/directives/script_select.js",
414diff --git a/src/maasserver/views/settings.py b/src/maasserver/views/settings.py
415index 2a410fa..6549d34 100644
416--- a/src/maasserver/views/settings.py
417+++ b/src/maasserver/views/settings.py
418@@ -33,12 +33,14 @@ from maasserver.exceptions import CannotDeleteUserException
419 from maasserver.forms import (
420 CommissioningForm,
421 DeployForm,
422+ DNSForm,
423 EditUserForm,
424 GlobalKernelOptsForm,
425 MAASForm,
426 NetworkDiscoveryForm,
427- NetworkForm,
428 NewUserCreationForm,
429+ NTPForm,
430+ ProxyForm,
431 StorageSettingsForm,
432 ThirdPartyDriversForm,
433 UbuntuForm,
434@@ -211,8 +213,22 @@ def settings(request):
435 return response
436
437 # Process the network form.
438- network_form, response = process_form(
439- request, NetworkForm, reverse('settings'), 'network',
440+ proxy_form, response = process_form(
441+ request, ProxyForm, reverse('settings'), 'proxy',
442+ "Configuration updated.")
443+ if response is not None:
444+ return response
445+
446+ # Process the DNS form.
447+ dns_form, response = process_form(
448+ request, DNSForm, reverse('settings'), 'dns',
449+ "Configuration updated.")
450+ if response is not None:
451+ return response
452+
453+ # Process the NTP form.
454+ ntp_form, response = process_form(
455+ request, NTPForm, reverse('settings'), 'ntp',
456 "Configuration updated.")
457 if response is not None:
458 return response
459@@ -279,7 +295,9 @@ def settings(request):
460 'show_license_keys': show_license_keys,
461 'license_keys': license_keys,
462 'maas_form': maas_form,
463- 'network_form': network_form,
464+ 'proxy_form': proxy_form,
465+ 'dns_form': dns_form,
466+ 'ntp_form': ntp_form,
467 'network_discovery_form': network_discovery_form,
468 'third_party_drivers_form': third_party_drivers_form,
469 'storage_settings_form': storage_settings_form,
470diff --git a/src/maasserver/views/tests/test_settings.py b/src/maasserver/views/tests/test_settings.py
471index 1f6a941..c7ba9b0 100644
472--- a/src/maasserver/views/tests/test_settings.py
473+++ b/src/maasserver/views/tests/test_settings.py
474@@ -107,7 +107,7 @@ class SettingsTest(MAASServerTestCase):
475 self.assertEqual(
476 new_name, Config.objects.get_config('maas_name'))
477
478- def test_settings_network_POST(self):
479+ def test_proxy_proxy_POST(self):
480 # Disable boot source cache signals.
481 self.addCleanup(bootsources.signals.enable)
482 bootsources.signals.disable()
483@@ -116,13 +116,56 @@ class SettingsTest(MAASServerTestCase):
484 response = self.client.post(
485 reverse('settings'),
486 get_prefixed_form_data(
487- prefix='network',
488+ prefix='proxy',
489 data={
490 'http_proxy': new_proxy,
491+ 'enable_http_proxy': True,
492+ 'use_peer_proxy': True,
493 }))
494 self.assertEqual(
495 http.client.FOUND, response.status_code, response.content)
496 self.assertEqual(new_proxy, Config.objects.get_config('http_proxy'))
497+ self.assertTrue(Config.objects.get_config('enable_http_proxy'))
498+ self.assertTrue(Config.objects.get_config('use_peer_proxy'))
499+
500+ def test_settings_dns_POST(self):
501+ # Disable boot source cache signals.
502+ self.addCleanup(bootsources.signals.enable)
503+ bootsources.signals.disable()
504+ self.client_log_in(as_admin=True)
505+ new_upstream = "8.8.8.8"
506+ response = self.client.post(
507+ reverse('settings'),
508+ get_prefixed_form_data(
509+ prefix='dns',
510+ data={
511+ 'upstream_dns': new_upstream,
512+ 'dnssec_validation': 'no',
513+ }))
514+ self.assertEqual(
515+ http.client.FOUND, response.status_code, response.content)
516+ self.assertEqual(
517+ new_upstream, Config.objects.get_config('upstream_dns'))
518+ self.assertEqual('no', Config.objects.get_config('dnssec_validation'))
519+
520+ def test_settings_ntp_POST(self):
521+ # Disable boot source cache signals.
522+ self.addCleanup(bootsources.signals.enable)
523+ bootsources.signals.disable()
524+ self.client_log_in(as_admin=True)
525+ new_servers = "ntp.example.com"
526+ response = self.client.post(
527+ reverse('settings'),
528+ get_prefixed_form_data(
529+ prefix='ntp',
530+ data={
531+ 'ntp_servers': new_servers,
532+ 'ntp_external_only': True,
533+ }))
534+ self.assertEqual(
535+ http.client.FOUND, response.status_code, response.content)
536+ self.assertEqual(new_servers, Config.objects.get_config('ntp_servers'))
537+ self.assertTrue(Config.objects.get_config('ntp_external_only'))
538
539 def test_settings_commissioning_POST(self):
540 self.client_log_in(as_admin=True)
541diff --git a/src/maastesting/karma.conf.js b/src/maastesting/karma.conf.js
542index 66fe913..33e5f5f 100644
543--- a/src/maastesting/karma.conf.js
544+++ b/src/maastesting/karma.conf.js
545@@ -24,7 +24,8 @@ module.exports = function(config) {
546 '../../src/maasserver/static/js/angular/maas.js',
547 '../../src/maasserver/static/js/angular/testing/*.js',
548 '../../src/maasserver/static/js/angular/*/*.js',
549- '../../src/maasserver/static/js/angular/*/tests/test_*.js'
550+ '../../src/maasserver/static/js/angular/*/tests/test_*.js',
551+ '../../src/maasserver/static/partials/*.html'
552 ],
553
554
555@@ -36,8 +37,17 @@ module.exports = function(config) {
556 // preprocess matching files before serving them to the browser
557 // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
558 preprocessors: {
559+ '../../src/maasserver/static/partials/*.html': ['ng-html2js']
560 },
561
562+ ngHtml2JsPreprocessor: {
563+ // If your build process changes the path to your templates,
564+ // use stripPrefix and prependPrefix to adjust it.
565+ stripPrefix: ".*src/maasserver/",
566+
567+ // the name of the Angular module to create
568+ moduleName: 'MAAS.templates'
569+ },
570
571 // test results reporter to use
572 // possible values: 'dots', 'progress'
573@@ -79,7 +89,8 @@ module.exports = function(config) {
574 'karma-firefox-launcher',
575 'karma-opera-launcher',
576 'karma-phantomjs-launcher',
577- 'karma-failed-reporter'
578+ 'karma-failed-reporter',
579+ 'karma-ng-html2js-preprocessor'
580 ]
581 });
582 };

Subscribers

People subscribed via source and target branches