← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~rvb/maas/views-bug-973215-5 into lp:maas

 

Raphaël Badin has proposed merging lp:~rvb/maas/views-bug-973215-5 into lp:maas with lp:~rvb/maas/views-bug-973215-4 as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #973215 in MAAS: "src/maasserver/views.py is huge."
  https://bugs.launchpad.net/maas/+bug/973215

For more details, see:
https://code.launchpad.net/~rvb/maas/views-bug-973215-5/+merge/103104

This branch is the fifth (and last) in a series of branches that will refactor src/maasserver/views.py into modules in src/maasserver/views/.

This branch follows up on the work done in https://code.launchpad.net/~rvb/maas/views-bug-973215-4/+merge/103098.

It moves settings views to src/maasserver/views/settings.py. It also moves the related tests in src/maasserver/tests/test_views_settings.py.

It also moves some tests I forgot which belong to src/maasserver/tests/test_views_nodes.py.
-- 
https://code.launchpad.net/~rvb/maas/views-bug-973215-5/+merge/103104
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/maas/views-bug-973215-5 into lp:maas.
=== modified file 'src/maasserver/tests/test_views.py'
--- src/maasserver/tests/test_views.py	2012-04-23 14:05:38 +0000
+++ src/maasserver/tests/test_views.py	2012-04-23 14:05:38 +0000
@@ -17,7 +17,6 @@
 from xmlrpclib import Fault
 
 from django.conf.urls.defaults import patterns
-from django.contrib.auth.models import User
 from django.core.exceptions import PermissionDenied
 from django.core.urlresolvers import reverse
 from django.http import Http404
@@ -26,16 +25,7 @@
 from lxml.html import fromstring
 from maasserver import components
 from maasserver.components import register_persistent_error
-from maasserver.enum import NODE_AFTER_COMMISSIONING_ACTION
 from maasserver.exceptions import ExternalComponentException
-from maasserver.models import (
-    Config,
-    UserProfile,
-    )
-from maasserver.testing import (
-    get_prefixed_form_data,
-    reload_object,
-    )
 from maasserver.testing.enum import map_enum
 from maasserver.testing.factory import factory
 from maasserver.testing.testcase import (
@@ -44,10 +34,7 @@
     )
 from maasserver.views import HelpfulDeleteView
 from maasserver.views.nodes import NodeEdit
-from provisioningserver.enum import (
-    POWER_TYPE_CHOICES,
-    PSERV_FAULT,
-    )
+from provisioningserver.enum import PSERV_FAULT
 from testtools.matchers import (
     Contains,
     MatchesAll,
@@ -224,15 +211,6 @@
             view.compose_feedback_deleted(view.obj))
 
 
-class AdminLoggedInTestCase(LoggedInTestCase):
-
-    def setUp(self):
-        super(AdminLoggedInTestCase, self).setUp()
-        # Promote the logged-in user to admin.
-        self.logged_in_user.is_superuser = True
-        self.logged_in_user.save()
-
-
 class MAASExceptionHandledInView(LoggedInTestCase):
 
     def test_raised_MAASException_redirects(self):
@@ -270,268 +248,6 @@
             [message.message for message in response.context['messages']])
 
 
-class AdminNodeViewsTest(AdminLoggedInTestCase):
-
-    def test_admin_can_edit_nodes(self):
-        node = factory.make_node(owner=factory.make_user())
-        node_edit_link = reverse('node-edit', args=[node.system_id])
-        params = {
-            'hostname': factory.getRandomString(),
-            'after_commissioning_action': factory.getRandomEnum(
-                NODE_AFTER_COMMISSIONING_ACTION),
-            'power_type': factory.getRandomChoice(POWER_TYPE_CHOICES),
-        }
-        response = self.client.post(node_edit_link, params)
-
-        node = reload_object(node)
-        self.assertEqual(httplib.FOUND, response.status_code)
-        self.assertAttributes(node, params)
-
-
-class SettingsTest(AdminLoggedInTestCase):
-
-    def test_settings_list_users(self):
-        # The settings page displays a list of the users with links to view,
-        # delete or edit each user. Note that the link to delete the the
-        # logged-in user is not display.
-        [factory.make_user() for i in range(3)]
-        users = UserProfile.objects.all_users()
-        response = self.client.get('/settings/')
-        doc = fromstring(response.content)
-        tab = doc.cssselect('#users')[0]
-        all_links = [elem.get('href') for elem in tab.cssselect('a')]
-        # "Add a user" link.
-        self.assertIn(reverse('accounts-add'), all_links)
-        for user in users:
-            rows = tab.cssselect('tr#%s' % user.username)
-            # Only one row for the user.
-            self.assertEqual(1, len(rows))
-            row = rows[0]
-            links = [elem.get('href') for elem in row.cssselect('a')]
-            # The username is shown...
-            self.assertSequenceEqual(
-                [user.username],
-                [link.text.strip() for link in row.cssselect('a.user')])
-            # ...with a link to view the user's profile.
-            self.assertSequenceEqual(
-                [reverse('accounts-view', args=[user.username])],
-                [link.get('href') for link in row.cssselect('a.user')])
-            # A link to edit the user is shown.
-            self.assertIn(
-                reverse('accounts-edit', args=[user.username]), links)
-            if user != self.logged_in_user:
-                # A link to delete the user is shown.
-                self.assertIn(
-                    reverse('accounts-del', args=[user.username]), links)
-            else:
-                # No link to delete the user is shown if the user is the
-                # logged-in user.
-                self.assertNotIn(
-                    reverse('accounts-del', args=[user.username]), links)
-
-    def test_settings_maas_and_network_POST(self):
-        new_name = factory.getRandomString()
-        new_domain = factory.getRandomString()
-        response = self.client.post(
-            '/settings/',
-            get_prefixed_form_data(
-                prefix='maas_and_network',
-                data={
-                    'maas_name': new_name,
-                    'enlistment_domain': new_domain,
-                }))
-
-        self.assertEqual(httplib.FOUND, response.status_code)
-        self.assertEqual(new_name, Config.objects.get_config('maas_name'))
-        self.assertEqual(
-            new_domain, Config.objects.get_config('enlistment_domain'))
-
-    def test_settings_commissioning_POST(self):
-        new_after_commissioning = factory.getRandomEnum(
-            NODE_AFTER_COMMISSIONING_ACTION)
-        new_check_compatibility = factory.getRandomBoolean()
-        response = self.client.post(
-            '/settings/',
-            get_prefixed_form_data(
-                prefix='commissioning',
-                data={
-                    'after_commissioning': new_after_commissioning,
-                    'check_compatibility': new_check_compatibility,
-                }))
-
-        self.assertEqual(httplib.FOUND, response.status_code)
-        self.assertEqual(
-            new_after_commissioning,
-            Config.objects.get_config('after_commissioning'))
-        self.assertEqual(
-            new_check_compatibility,
-            Config.objects.get_config('check_compatibility'))
-
-    def test_settings_ubuntu_POST(self):
-        new_fallback_master_archive = factory.getRandomBoolean()
-        new_keep_mirror_list_uptodate = factory.getRandomBoolean()
-        new_fetch_new_releases = factory.getRandomBoolean()
-        choices = Config.objects.get_config('update_from_choice')
-        new_update_from = factory.getRandomChoice(choices)
-        response = self.client.post(
-            '/settings/',
-            get_prefixed_form_data(
-                prefix='ubuntu',
-                data={
-                    'fallback_master_archive': new_fallback_master_archive,
-                    'keep_mirror_list_uptodate': new_keep_mirror_list_uptodate,
-                    'fetch_new_releases': new_fetch_new_releases,
-                    'update_from': new_update_from,
-                }))
-
-        self.assertEqual(httplib.FOUND, response.status_code)
-        self.assertEqual(
-            new_fallback_master_archive,
-            Config.objects.get_config('fallback_master_archive'))
-        self.assertEqual(
-            new_keep_mirror_list_uptodate,
-            Config.objects.get_config('keep_mirror_list_uptodate'))
-        self.assertEqual(
-            new_fetch_new_releases,
-            Config.objects.get_config('fetch_new_releases'))
-        self.assertEqual(
-            new_update_from, Config.objects.get_config('update_from'))
-
-    def test_settings_add_archive_POST(self):
-        choices = Config.objects.get_config('update_from_choice')
-        response = self.client.post(
-            '/settings/archives/add/',
-            data={'archive_name': 'my.hostname.com'}
-        )
-        new_choices = Config.objects.get_config('update_from_choice')
-
-        self.assertEqual(httplib.FOUND, response.status_code)
-        self.assertItemsEqual(
-            choices + [['my.hostname.com', 'my.hostname.com']],
-            new_choices)
-
-
-# Settable attributes on User.
-user_attributes = [
-    'email',
-    'is_superuser',
-    'last_name',
-    'username',
-    ]
-
-
-def make_user_attribute_params(user):
-    """Compose a dict of form parameters for a user's account data.
-
-    By default, each attribute in the dict maps to the user's existing value
-    for that atrribute.
-    """
-    return {
-        attr: getattr(user, attr)
-        for attr in user_attributes
-        }
-
-
-def make_password_params(password):
-    """Create a dict of parameters for setting a given password."""
-    return {
-        'password1': password,
-        'password2': password,
-    }
-
-
-def subset_dict(input_dict, keys_subset):
-    """Return a subset of `input_dict` restricted to `keys_subset`.
-
-    All keys in `keys_subset` must be in `input_dict`.
-    """
-    return {key: input_dict[key] for key in keys_subset}
-
-
-class UserManagementTest(AdminLoggedInTestCase):
-
-    def test_add_user_POST(self):
-        params = {
-            'username': factory.getRandomString(),
-            'last_name': factory.getRandomString(30),
-            'email': factory.getRandomEmail(),
-            'is_superuser': factory.getRandomBoolean(),
-        }
-        password = factory.getRandomString()
-        params.update(make_password_params(password))
-
-        response = self.client.post(reverse('accounts-add'), params)
-        self.assertEqual(httplib.FOUND, response.status_code)
-        user = User.objects.get(username=params['username'])
-        self.assertAttributes(user, subset_dict(params, user_attributes))
-        self.assertTrue(user.check_password(password))
-
-    def test_edit_user_POST_profile_updates_attributes(self):
-        user = factory.make_user()
-        params = make_user_attribute_params(user)
-        params.update({
-            'last_name': 'Newname-%s' % factory.getRandomString(),
-            'email': 'new-%s@xxxxxxxxxxx' % factory.getRandomString(),
-            'is_superuser': True,
-            'username': 'newname-%s' % factory.getRandomString(),
-            })
-
-        response = self.client.post(
-            reverse('accounts-edit', args=[user.username]),
-            get_prefixed_form_data('profile', params))
-
-        self.assertEqual(httplib.FOUND, response.status_code)
-        self.assertAttributes(
-            reload_object(user), subset_dict(params, user_attributes))
-
-    def test_edit_user_POST_updates_password(self):
-        user = factory.make_user()
-        new_password = factory.getRandomString()
-        params = make_password_params(new_password)
-        response = self.client.post(
-            reverse('accounts-edit', args=[user.username]),
-            get_prefixed_form_data('password', params))
-        self.assertEqual(httplib.FOUND, response.status_code)
-        self.assertTrue(reload_object(user).check_password(new_password))
-
-    def test_delete_user_GET(self):
-        # The user delete page displays a confirmation page with a form.
-        user = factory.make_user()
-        del_link = reverse('accounts-del', args=[user.username])
-        response = self.client.get(del_link)
-        doc = fromstring(response.content)
-        confirmation_message = (
-            'Are you sure you want to delete the user "%s"?' %
-            user.username)
-        self.assertSequenceEqual(
-            [confirmation_message],
-            [elem.text.strip() for elem in doc.cssselect('h2')])
-        # The page features a form that submits to itself.
-        self.assertSequenceEqual(
-            ['.'],
-            [elem.get('action').strip() for elem in doc.cssselect(
-                '#content form')])
-
-    def test_delete_user_POST(self):
-        # A POST request to the user delete finally deletes the user.
-        user = factory.make_user()
-        user_id = user.id
-        del_link = reverse('accounts-del', args=[user.username])
-        response = self.client.post(del_link, {'post': 'yes'})
-        self.assertEqual(httplib.FOUND, response.status_code)
-        self.assertItemsEqual([], User.objects.filter(id=user_id))
-
-    def test_view_user(self):
-        # The user page feature the basic information about the user.
-        user = factory.make_user()
-        del_link = reverse('accounts-view', args=[user.username])
-        response = self.client.get(del_link)
-        doc = fromstring(response.content)
-        content_text = doc.cssselect('#content')[0].text_content()
-        self.assertIn(user.username, content_text)
-        self.assertIn(user.email, content_text)
-
-
 class PermanentErrorDisplayTest(LoggedInTestCase):
 
     def test_permanent_error_displayed(self):

=== modified file 'src/maasserver/tests/test_views_nodes.py'
--- src/maasserver/tests/test_views_nodes.py	2012-04-23 14:05:38 +0000
+++ src/maasserver/tests/test_views_nodes.py	2012-04-23 14:05:38 +0000
@@ -32,12 +32,14 @@
 from maasserver.testing.enum import map_enum
 from maasserver.testing.factory import factory
 from maasserver.testing.testcase import (
+    AdminLoggedInTestCase,
     LoggedInTestCase,
     TestCase,
     )
 from maasserver.views import nodes as nodes_views
 from maasserver.views.nodes import get_longpoll_context
 from maastesting.rabbit import uses_rabbit_fixture
+from provisioningserver.enum import POWER_TYPE_CHOICES
 
 
 class NodeViewsTest(LoggedInTestCase):
@@ -299,6 +301,24 @@
             [message.message for message in response.context['messages']])
 
 
+class AdminNodeViewsTest(AdminLoggedInTestCase):
+
+    def test_admin_can_edit_nodes(self):
+        node = factory.make_node(owner=factory.make_user())
+        node_edit_link = reverse('node-edit', args=[node.system_id])
+        params = {
+            'hostname': factory.getRandomString(),
+            'after_commissioning_action': factory.getRandomEnum(
+                NODE_AFTER_COMMISSIONING_ACTION),
+            'power_type': factory.getRandomChoice(POWER_TYPE_CHOICES),
+        }
+        response = self.client.post(node_edit_link, params)
+
+        node = reload_object(node)
+        self.assertEqual(httplib.FOUND, response.status_code)
+        self.assertAttributes(node, params)
+
+
 class TestGetLongpollContext(TestCase):
 
     def test_get_longpoll_context_empty_if_rabbitmq_publish_is_none(self):

=== added file 'src/maasserver/tests/test_views_settings.py'
--- src/maasserver/tests/test_views_settings.py	1970-01-01 00:00:00 +0000
+++ src/maasserver/tests/test_views_settings.py	2012-04-23 14:05:38 +0000
@@ -0,0 +1,274 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Test maasserver settings views."""
+
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = []
+
+import httplib
+
+from django.contrib.auth.models import User
+from django.core.urlresolvers import reverse
+from lxml.html import fromstring
+from maasserver.enum import NODE_AFTER_COMMISSIONING_ACTION
+from maasserver.models import (
+    Config,
+    UserProfile,
+    )
+from maasserver.testing import (
+    get_prefixed_form_data,
+    reload_object,
+    )
+from maasserver.testing.factory import factory
+from maasserver.testing.testcase import AdminLoggedInTestCase
+
+
+class SettingsTest(AdminLoggedInTestCase):
+
+    def test_settings_list_users(self):
+        # The settings page displays a list of the users with links to view,
+        # delete or edit each user. Note that the link to delete the the
+        # logged-in user is not display.
+        [factory.make_user() for i in range(3)]
+        users = UserProfile.objects.all_users()
+        response = self.client.get('/settings/')
+        doc = fromstring(response.content)
+        tab = doc.cssselect('#users')[0]
+        all_links = [elem.get('href') for elem in tab.cssselect('a')]
+        # "Add a user" link.
+        self.assertIn(reverse('accounts-add'), all_links)
+        for user in users:
+            rows = tab.cssselect('tr#%s' % user.username)
+            # Only one row for the user.
+            self.assertEqual(1, len(rows))
+            row = rows[0]
+            links = [elem.get('href') for elem in row.cssselect('a')]
+            # The username is shown...
+            self.assertSequenceEqual(
+                [user.username],
+                [link.text.strip() for link in row.cssselect('a.user')])
+            # ...with a link to view the user's profile.
+            self.assertSequenceEqual(
+                [reverse('accounts-view', args=[user.username])],
+                [link.get('href') for link in row.cssselect('a.user')])
+            # A link to edit the user is shown.
+            self.assertIn(
+                reverse('accounts-edit', args=[user.username]), links)
+            if user != self.logged_in_user:
+                # A link to delete the user is shown.
+                self.assertIn(
+                    reverse('accounts-del', args=[user.username]), links)
+            else:
+                # No link to delete the user is shown if the user is the
+                # logged-in user.
+                self.assertNotIn(
+                    reverse('accounts-del', args=[user.username]), links)
+
+    def test_settings_maas_and_network_POST(self):
+        new_name = factory.getRandomString()
+        new_domain = factory.getRandomString()
+        response = self.client.post(
+            '/settings/',
+            get_prefixed_form_data(
+                prefix='maas_and_network',
+                data={
+                    'maas_name': new_name,
+                    'enlistment_domain': new_domain,
+                }))
+
+        self.assertEqual(httplib.FOUND, response.status_code)
+        self.assertEqual(new_name, Config.objects.get_config('maas_name'))
+        self.assertEqual(
+            new_domain, Config.objects.get_config('enlistment_domain'))
+
+    def test_settings_commissioning_POST(self):
+        new_after_commissioning = factory.getRandomEnum(
+            NODE_AFTER_COMMISSIONING_ACTION)
+        new_check_compatibility = factory.getRandomBoolean()
+        response = self.client.post(
+            '/settings/',
+            get_prefixed_form_data(
+                prefix='commissioning',
+                data={
+                    'after_commissioning': new_after_commissioning,
+                    'check_compatibility': new_check_compatibility,
+                }))
+
+        self.assertEqual(httplib.FOUND, response.status_code)
+        self.assertEqual(
+            new_after_commissioning,
+            Config.objects.get_config('after_commissioning'))
+        self.assertEqual(
+            new_check_compatibility,
+            Config.objects.get_config('check_compatibility'))
+
+    def test_settings_ubuntu_POST(self):
+        new_fallback_master_archive = factory.getRandomBoolean()
+        new_keep_mirror_list_uptodate = factory.getRandomBoolean()
+        new_fetch_new_releases = factory.getRandomBoolean()
+        choices = Config.objects.get_config('update_from_choice')
+        new_update_from = factory.getRandomChoice(choices)
+        response = self.client.post(
+            '/settings/',
+            get_prefixed_form_data(
+                prefix='ubuntu',
+                data={
+                    'fallback_master_archive': new_fallback_master_archive,
+                    'keep_mirror_list_uptodate': new_keep_mirror_list_uptodate,
+                    'fetch_new_releases': new_fetch_new_releases,
+                    'update_from': new_update_from,
+                }))
+
+        self.assertEqual(httplib.FOUND, response.status_code)
+        self.assertEqual(
+            new_fallback_master_archive,
+            Config.objects.get_config('fallback_master_archive'))
+        self.assertEqual(
+            new_keep_mirror_list_uptodate,
+            Config.objects.get_config('keep_mirror_list_uptodate'))
+        self.assertEqual(
+            new_fetch_new_releases,
+            Config.objects.get_config('fetch_new_releases'))
+        self.assertEqual(
+            new_update_from, Config.objects.get_config('update_from'))
+
+    def test_settings_add_archive_POST(self):
+        choices = Config.objects.get_config('update_from_choice')
+        response = self.client.post(
+            '/settings/archives/add/',
+            data={'archive_name': 'my.hostname.com'}
+        )
+        new_choices = Config.objects.get_config('update_from_choice')
+
+        self.assertEqual(httplib.FOUND, response.status_code)
+        self.assertItemsEqual(
+            choices + [['my.hostname.com', 'my.hostname.com']],
+            new_choices)
+
+
+# Settable attributes on User.
+user_attributes = [
+    'email',
+    'is_superuser',
+    'last_name',
+    'username',
+    ]
+
+
+def make_user_attribute_params(user):
+    """Compose a dict of form parameters for a user's account data.
+
+    By default, each attribute in the dict maps to the user's existing value
+    for that atrribute.
+    """
+    return {
+        attr: getattr(user, attr)
+        for attr in user_attributes
+        }
+
+
+def make_password_params(password):
+    """Create a dict of parameters for setting a given password."""
+    return {
+        'password1': password,
+        'password2': password,
+    }
+
+
+def subset_dict(input_dict, keys_subset):
+    """Return a subset of `input_dict` restricted to `keys_subset`.
+
+    All keys in `keys_subset` must be in `input_dict`.
+    """
+    return {key: input_dict[key] for key in keys_subset}
+
+
+class UserManagementTest(AdminLoggedInTestCase):
+
+    def test_add_user_POST(self):
+        params = {
+            'username': factory.getRandomString(),
+            'last_name': factory.getRandomString(30),
+            'email': factory.getRandomEmail(),
+            'is_superuser': factory.getRandomBoolean(),
+        }
+        password = factory.getRandomString()
+        params.update(make_password_params(password))
+
+        response = self.client.post(reverse('accounts-add'), params)
+        self.assertEqual(httplib.FOUND, response.status_code)
+        user = User.objects.get(username=params['username'])
+        self.assertAttributes(user, subset_dict(params, user_attributes))
+        self.assertTrue(user.check_password(password))
+
+    def test_edit_user_POST_profile_updates_attributes(self):
+        user = factory.make_user()
+        params = make_user_attribute_params(user)
+        params.update({
+            'last_name': 'Newname-%s' % factory.getRandomString(),
+            'email': 'new-%s@xxxxxxxxxxx' % factory.getRandomString(),
+            'is_superuser': True,
+            'username': 'newname-%s' % factory.getRandomString(),
+            })
+
+        response = self.client.post(
+            reverse('accounts-edit', args=[user.username]),
+            get_prefixed_form_data('profile', params))
+
+        self.assertEqual(httplib.FOUND, response.status_code)
+        self.assertAttributes(
+            reload_object(user), subset_dict(params, user_attributes))
+
+    def test_edit_user_POST_updates_password(self):
+        user = factory.make_user()
+        new_password = factory.getRandomString()
+        params = make_password_params(new_password)
+        response = self.client.post(
+            reverse('accounts-edit', args=[user.username]),
+            get_prefixed_form_data('password', params))
+        self.assertEqual(httplib.FOUND, response.status_code)
+        self.assertTrue(reload_object(user).check_password(new_password))
+
+    def test_delete_user_GET(self):
+        # The user delete page displays a confirmation page with a form.
+        user = factory.make_user()
+        del_link = reverse('accounts-del', args=[user.username])
+        response = self.client.get(del_link)
+        doc = fromstring(response.content)
+        confirmation_message = (
+            'Are you sure you want to delete the user "%s"?' %
+            user.username)
+        self.assertSequenceEqual(
+            [confirmation_message],
+            [elem.text.strip() for elem in doc.cssselect('h2')])
+        # The page features a form that submits to itself.
+        self.assertSequenceEqual(
+            ['.'],
+            [elem.get('action').strip() for elem in doc.cssselect(
+                '#content form')])
+
+    def test_delete_user_POST(self):
+        # A POST request to the user delete finally deletes the user.
+        user = factory.make_user()
+        user_id = user.id
+        del_link = reverse('accounts-del', args=[user.username])
+        response = self.client.post(del_link, {'post': 'yes'})
+        self.assertEqual(httplib.FOUND, response.status_code)
+        self.assertItemsEqual([], User.objects.filter(id=user_id))
+
+    def test_view_user(self):
+        # The user page feature the basic information about the user.
+        user = factory.make_user()
+        del_link = reverse('accounts-view', args=[user.username])
+        response = self.client.get(del_link)
+        doc = fromstring(response.content)
+        content_text = doc.cssselect('#content')[0].text_content()
+        self.assertIn(user.username, content_text)
+        self.assertIn(user.email, content_text)

=== modified file 'src/maasserver/urls.py'
--- src/maasserver/urls.py	2012-04-23 14:05:38 +0000
+++ src/maasserver/urls.py	2012-04-23 14:05:38 +0000
@@ -27,14 +27,6 @@
     )
 from maasserver.maasavahi import setup_maas_avahi_service
 from maasserver.models import Node
-from maasserver.views import (
-    AccountsAdd,
-    AccountsDelete,
-    AccountsEdit,
-    AccountsView,
-    settings,
-    settings_add_archive,
-    )
 from maasserver.views.account import (
     login,
     logout,
@@ -51,6 +43,14 @@
     SSHKeyDeleteView,
     userprefsview,
     )
+from maasserver.views.settings import (
+    AccountsAdd,
+    AccountsDelete,
+    AccountsEdit,
+    AccountsView,
+    settings,
+    settings_add_archive,
+    )
 
 
 def adminurl(regexp, view, *args, **kwargs):

=== modified file 'src/maasserver/views/__init__.py'
--- src/maasserver/views/__init__.py	2012-04-23 14:05:38 +0000
+++ src/maasserver/views/__init__.py	2012-04-23 14:05:38 +0000
@@ -11,8 +11,8 @@
 
 __metaclass__ = type
 __all__ = [
-    "AccountsAdd",
-    "AccountsDelete",
+    "HelpfulDeleteView",
+    "process_form",
     "AccountsEdit",
     "AccountsView",
     "settings",
@@ -25,36 +25,11 @@
     )
 
 from django.contrib import messages
-from django.contrib.auth.forms import AdminPasswordChangeForm
-from django.contrib.auth.models import User
-from django.core.urlresolvers import reverse
 from django.http import (
     Http404,
     HttpResponseRedirect,
     )
-from django.shortcuts import (
-    get_object_or_404,
-    render_to_response,
-    )
-from django.template import RequestContext
-from django.views.generic import (
-    CreateView,
-    DeleteView,
-    DetailView,
-    )
-from django.views.generic.base import TemplateView
-from django.views.generic.detail import SingleObjectTemplateResponseMixin
-from django.views.generic.edit import ModelFormMixin
-from maasserver.exceptions import CannotDeleteUserException
-from maasserver.forms import (
-    AddArchiveForm,
-    CommissioningForm,
-    EditUserForm,
-    MAASAndNetworkForm,
-    NewUserCreationForm,
-    UbuntuForm,
-    )
-from maasserver.models import UserProfile
+from django.views.generic import DeleteView
 
 
 class HelpfulDeleteView(DeleteView):
@@ -172,153 +147,3 @@
     else:
         form = form_class(prefix=prefix, **form_kwargs)
     return form, None
-
-
-class AccountsView(DetailView):
-    """Read-only view of user's account information."""
-
-    template_name = 'maasserver/user_view.html'
-
-    context_object_name = 'view_user'
-
-    def get_object(self):
-        username = self.kwargs.get('username', None)
-        user = get_object_or_404(User, username=username)
-        return user
-
-
-class AccountsAdd(CreateView):
-    """Add-user view."""
-
-    form_class = NewUserCreationForm
-
-    template_name = 'maasserver/user_add.html'
-
-    context_object_name = 'new_user'
-
-    def get_success_url(self):
-        return reverse('settings')
-
-    def form_valid(self, form):
-        messages.info(self.request, "User added.")
-        return super(AccountsAdd, self).form_valid(form)
-
-
-class AccountsDelete(DeleteView):
-
-    template_name = 'maasserver/user_confirm_delete.html'
-    context_object_name = 'user_to_delete'
-
-    def get_object(self):
-        username = self.kwargs.get('username', None)
-        user = get_object_or_404(User, username=username)
-        return user.get_profile()
-
-    def get_next_url(self):
-        return reverse('settings')
-
-    def delete(self, request, *args, **kwargs):
-        profile = self.get_object()
-        username = profile.user.username
-        try:
-            profile.delete()
-            messages.info(request, "User %s deleted." % username)
-        except CannotDeleteUserException as e:
-            messages.info(request, unicode(e))
-        return HttpResponseRedirect(self.get_next_url())
-
-
-class AccountsEdit(TemplateView, ModelFormMixin,
-                   SingleObjectTemplateResponseMixin):
-
-    model = User
-    template_name = 'maasserver/user_edit.html'
-
-    def get_object(self):
-        username = self.kwargs.get('username', None)
-        return get_object_or_404(User, username=username)
-
-    def respond(self, request, profile_form, password_form):
-        """Generate a response."""
-        return self.render_to_response({
-            'profile_form': profile_form,
-            'password_form': password_form,
-            })
-
-    def get(self, request, *args, **kwargs):
-        """Called by `TemplateView`: handle a GET request."""
-        self.object = user = self.get_object()
-        profile_form = EditUserForm(instance=user, prefix='profile')
-        password_form = AdminPasswordChangeForm(user=user, prefix='password')
-        return self.respond(request, profile_form, password_form)
-
-    def post(self, request, *args, **kwargs):
-        """Called by `TemplateView`: handle a POST request."""
-        self.object = user = self.get_object()
-        next_page = reverse('settings')
-
-        # Process the profile-editing form, if that's what was submitted.
-        profile_form, response = process_form(
-            request, EditUserForm, next_page, 'profile', "Profile updated.",
-            {'instance': user})
-        if response is not None:
-            return response
-
-        # Process the password change form, if that's what was submitted.
-        password_form, response = process_form(
-            request, AdminPasswordChangeForm, next_page, 'password',
-            "Password updated.", {'user': user})
-        if response is not None:
-            return response
-
-        return self.respond(request, profile_form, password_form)
-
-
-def settings(request):
-    user_list = UserProfile.objects.all_users().order_by('username')
-    # Process the MAAS & network form.
-    maas_and_network_form, response = process_form(
-        request, MAASAndNetworkForm, reverse('settings'), 'maas_and_network',
-        "Configuration updated.")
-    if response is not None:
-        return response
-
-    # Process the Commissioning form.
-    commissioning_form, response = process_form(
-        request, CommissioningForm, reverse('settings'), 'commissioning',
-        "Configuration updated.")
-    if response is not None:
-        return response
-
-    # Process the Ubuntu form.
-    ubuntu_form, response = process_form(
-        request, UbuntuForm, reverse('settings'), 'ubuntu',
-        "Configuration updated.")
-    if response is not None:
-        return response
-
-    return render_to_response(
-        'maasserver/settings.html',
-        {
-            'user_list': user_list,
-            'maas_and_network_form': maas_and_network_form,
-            'commissioning_form': commissioning_form,
-            'ubuntu_form': ubuntu_form,
-        },
-        context_instance=RequestContext(request))
-
-
-def settings_add_archive(request):
-    if request.method == 'POST':
-        form = AddArchiveForm(request.POST)
-        if form.is_valid():
-            form.save()
-            messages.info(request, "Archive added.")
-            return HttpResponseRedirect(reverse('settings'))
-    else:
-        form = AddArchiveForm()
-
-    return render_to_response(
-        'maasserver/settings_add_archive.html',
-        {'form': form},
-        context_instance=RequestContext(request))

=== added file 'src/maasserver/views/settings.py'
--- src/maasserver/views/settings.py	1970-01-01 00:00:00 +0000
+++ src/maasserver/views/settings.py	2012-04-23 14:05:38 +0000
@@ -0,0 +1,200 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Settings views."""
+
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = [
+    "AccountsAdd",
+    "AccountsDelete",
+    "AccountsEdit",
+    "AccountsView",
+    "settings",
+    "settings_add_archive",
+    ]
+
+from django.contrib import messages
+from django.contrib.auth.forms import AdminPasswordChangeForm
+from django.contrib.auth.models import User
+from django.core.urlresolvers import reverse
+from django.http import HttpResponseRedirect
+from django.shortcuts import (
+    get_object_or_404,
+    render_to_response,
+    )
+from django.template import RequestContext
+from django.views.generic import (
+    CreateView,
+    DeleteView,
+    DetailView,
+    )
+from django.views.generic.base import TemplateView
+from django.views.generic.detail import SingleObjectTemplateResponseMixin
+from django.views.generic.edit import ModelFormMixin
+from maasserver.exceptions import CannotDeleteUserException
+from maasserver.forms import (
+    AddArchiveForm,
+    CommissioningForm,
+    EditUserForm,
+    MAASAndNetworkForm,
+    NewUserCreationForm,
+    UbuntuForm,
+    )
+from maasserver.models import UserProfile
+from maasserver.views import process_form
+
+
+class AccountsView(DetailView):
+    """Read-only view of user's account information."""
+
+    template_name = 'maasserver/user_view.html'
+
+    context_object_name = 'view_user'
+
+    def get_object(self):
+        username = self.kwargs.get('username', None)
+        user = get_object_or_404(User, username=username)
+        return user
+
+
+class AccountsAdd(CreateView):
+    """Add-user view."""
+
+    form_class = NewUserCreationForm
+
+    template_name = 'maasserver/user_add.html'
+
+    context_object_name = 'new_user'
+
+    def get_success_url(self):
+        return reverse('settings')
+
+    def form_valid(self, form):
+        messages.info(self.request, "User added.")
+        return super(AccountsAdd, self).form_valid(form)
+
+
+class AccountsDelete(DeleteView):
+
+    template_name = 'maasserver/user_confirm_delete.html'
+    context_object_name = 'user_to_delete'
+
+    def get_object(self):
+        username = self.kwargs.get('username', None)
+        user = get_object_or_404(User, username=username)
+        return user.get_profile()
+
+    def get_next_url(self):
+        return reverse('settings')
+
+    def delete(self, request, *args, **kwargs):
+        profile = self.get_object()
+        username = profile.user.username
+        try:
+            profile.delete()
+            messages.info(request, "User %s deleted." % username)
+        except CannotDeleteUserException as e:
+            messages.info(request, unicode(e))
+        return HttpResponseRedirect(self.get_next_url())
+
+
+class AccountsEdit(TemplateView, ModelFormMixin,
+                   SingleObjectTemplateResponseMixin):
+
+    model = User
+    template_name = 'maasserver/user_edit.html'
+
+    def get_object(self):
+        username = self.kwargs.get('username', None)
+        return get_object_or_404(User, username=username)
+
+    def respond(self, request, profile_form, password_form):
+        """Generate a response."""
+        return self.render_to_response({
+            'profile_form': profile_form,
+            'password_form': password_form,
+            })
+
+    def get(self, request, *args, **kwargs):
+        """Called by `TemplateView`: handle a GET request."""
+        self.object = user = self.get_object()
+        profile_form = EditUserForm(instance=user, prefix='profile')
+        password_form = AdminPasswordChangeForm(user=user, prefix='password')
+        return self.respond(request, profile_form, password_form)
+
+    def post(self, request, *args, **kwargs):
+        """Called by `TemplateView`: handle a POST request."""
+        self.object = user = self.get_object()
+        next_page = reverse('settings')
+
+        # Process the profile-editing form, if that's what was submitted.
+        profile_form, response = process_form(
+            request, EditUserForm, next_page, 'profile', "Profile updated.",
+            {'instance': user})
+        if response is not None:
+            return response
+
+        # Process the password change form, if that's what was submitted.
+        password_form, response = process_form(
+            request, AdminPasswordChangeForm, next_page, 'password',
+            "Password updated.", {'user': user})
+        if response is not None:
+            return response
+
+        return self.respond(request, profile_form, password_form)
+
+
+def settings(request):
+    user_list = UserProfile.objects.all_users().order_by('username')
+    # Process the MAAS & network form.
+    maas_and_network_form, response = process_form(
+        request, MAASAndNetworkForm, reverse('settings'), 'maas_and_network',
+        "Configuration updated.")
+    if response is not None:
+        return response
+
+    # Process the Commissioning form.
+    commissioning_form, response = process_form(
+        request, CommissioningForm, reverse('settings'), 'commissioning',
+        "Configuration updated.")
+    if response is not None:
+        return response
+
+    # Process the Ubuntu form.
+    ubuntu_form, response = process_form(
+        request, UbuntuForm, reverse('settings'), 'ubuntu',
+        "Configuration updated.")
+    if response is not None:
+        return response
+
+    return render_to_response(
+        'maasserver/settings.html',
+        {
+            'user_list': user_list,
+            'maas_and_network_form': maas_and_network_form,
+            'commissioning_form': commissioning_form,
+            'ubuntu_form': ubuntu_form,
+        },
+        context_instance=RequestContext(request))
+
+
+def settings_add_archive(request):
+    if request.method == 'POST':
+        form = AddArchiveForm(request.POST)
+        if form.is_valid():
+            form.save()
+            messages.info(request, "Archive added.")
+            return HttpResponseRedirect(reverse('settings'))
+    else:
+        form = AddArchiveForm()
+
+    return render_to_response(
+        'maasserver/settings_add_archive.html',
+        {'form': form},
+        context_instance=RequestContext(request))