Merge lp:~rvb/maas/check-commissioning-bug-1084292 into lp:~maas-committers/maas/trunk

Proposed by Raphaël Badin
Status: Merged
Approved by: Raphaël Badin
Approved revision: no longer in the source branch.
Merged at revision: 1392
Proposed branch: lp:~rvb/maas/check-commissioning-bug-1084292
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 110 lines (+38/-23)
2 files modified
src/maasserver/api.py (+17/-15)
src/maasserver/tests/test_api.py (+21/-8)
To merge this branch: bzr merge lp:~rvb/maas/check-commissioning-bug-1084292
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+137526@code.launchpad.net

Commit message

Fix the check_commissioning API call: make it available to the logged-in users only and return the changed nodes (which is in accordance with what the docs already say).

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

This branch looks fine.

I found the assert of two constructed tuples on line 70, 82, and 97 odd.
Wouldn't two simple assertions be clearer and simpler?

The one on line 97 would especially benefit from unrolling.

review: Approve (code)
Revision history for this message
Raphaël Badin (rvb) wrote :

> This branch looks fine.

Thanks for the review!

> I found the assert of two constructed tuples on line 70, 82, and 97 odd.
> Wouldn't two simple assertions be clearer and simpler?

Well, we've try to do it this way in MAAS in order to get the maximum information out of a failure. This way, when something goes wrong (i.e. a test does not pass), we have all the failures revealed in one go as opposed to the tedious process of: having a failing assertion, fixing the problem, running the test again, fixing the next failing assertion, etc.

> The one on line 97 would especially benefit from unrolling.

I've fixed the style for that one a bit.

Revision history for this message
Benji York (benji) wrote :

On Mon, Dec 3, 2012 at 4:09 PM, Raphaël Badin
<email address hidden> wrote:
>> This branch looks fine.
>
> Thanks for the review!
>
>> I found the assert of two constructed tuples on line 70, 82, and 97 odd.
>> Wouldn't two simple assertions be clearer and simpler?
>
> Well, we've try to do it this way in MAAS in order to get the maximum
> information out of a failure. This way, when something goes wrong
> (i.e. a test does not pass), we have all the failures revealed in one
> go as opposed to the tedious process of: having a failing assertion,
> fixing the problem, running the test again, fixing the next failing
> assertion, etc.

Interesting. To me the cure seems worse than the disease, but that is
certainly open to debate. Ether way, it strikes me that stopping the
test on the first failure may be a design flaw in unittest (arising
form the use of exceptions to report failures).

Thinking out loud: doctest does not stop on failures and (by default)
reports them all, which is the effect you get with this pattern. Of
course, any positive that comes from that is normally marred by most
doctests lacking inter-test isolation.
--
Benji York

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/api.py'
2--- src/maasserver/api.py 2012-11-29 11:30:37 +0000
3+++ src/maasserver/api.py 2012-12-03 16:08:24 +0000
4@@ -458,21 +458,6 @@
5 """Accept a node's enlistment: not allowed to anonymous users."""
6 raise Unauthorized("You must be logged in to accept nodes.")
7
8- @operation(idempotent=False)
9- def check_commissioning(self, request):
10- """Check all commissioning nodes to see if they are taking too long.
11-
12- Anything that has been commissioning for longer than
13- settings.COMMISSIONING_TIMEOUT is moved into the FAILED_TESTS status.
14- """
15- interval = timedelta(minutes=settings.COMMISSIONING_TIMEOUT)
16- cutoff = datetime.now() - interval
17- query = Node.objects.filter(
18- status=NODE_STATUS.COMMISSIONING, updated__lte=cutoff)
19- query.update(status=NODE_STATUS.FAILED_TESTS)
20- # Note that Django doesn't call save() on updated nodes here,
21- # but I don't think anything requires its effects anyway.
22-
23 @classmethod
24 def resource_uri(cls, *args, **kwargs):
25 return ('nodes_handler', [])
26@@ -591,6 +576,23 @@
27 return filter(None, nodes)
28
29 @operation(idempotent=False)
30+ def check_commissioning(self, request):
31+ """Check all commissioning nodes to see if they are taking too long.
32+
33+ Anything that has been commissioning for longer than
34+ settings.COMMISSIONING_TIMEOUT is moved into the FAILED_TESTS status.
35+ """
36+ interval = timedelta(minutes=settings.COMMISSIONING_TIMEOUT)
37+ cutoff = datetime.now() - interval
38+ query = Node.objects.filter(
39+ status=NODE_STATUS.COMMISSIONING, updated__lte=cutoff)
40+ results = list(query)
41+ query.update(status=NODE_STATUS.FAILED_TESTS)
42+ # Note that Django doesn't call save() on updated nodes here,
43+ # but I don't think anything requires its effects anyway.
44+ return results
45+
46+ @operation(idempotent=False)
47 def release(self, request):
48 """Release multiple nodes.
49
50
51=== modified file 'src/maasserver/tests/test_api.py'
52--- src/maasserver/tests/test_api.py 2012-11-28 15:37:34 +0000
53+++ src/maasserver/tests/test_api.py 2012-12-03 16:08:24 +0000
54@@ -3284,24 +3284,28 @@
55 (response.status_code, response.content))
56
57
58-class TestAnonymousCommissioningTimeout(APIv10TestMixin, TestCase):
59+class TestCommissioningTimeout(APIv10TestMixin, LoggedInTestCase):
60 """Testing of commissioning timeout API."""
61
62 def test_check_with_no_action(self):
63 node = factory.make_node(status=NODE_STATUS.READY)
64- self.client.post(
65+ response = self.client.post(
66 self.get_uri('nodes/'), {'op': 'check_commissioning'})
67 # Anything that's not commissioning should be ignored.
68 node = reload_object(node)
69- self.assertEqual(NODE_STATUS.READY, node.status)
70+ self.assertEqual(
71+ (httplib.OK, NODE_STATUS.READY),
72+ (response.status_code, node.status))
73
74 def test_check_with_commissioning_but_not_expired_node(self):
75 node = factory.make_node(
76 status=NODE_STATUS.COMMISSIONING)
77- self.client.post(
78+ response = self.client.post(
79 self.get_uri('nodes/'), {'op': 'check_commissioning'})
80 node = reload_object(node)
81- self.assertEqual(NODE_STATUS.COMMISSIONING, node.status)
82+ self.assertEqual(
83+ (httplib.OK, NODE_STATUS.COMMISSIONING),
84+ (response.status_code, node.status))
85
86 def test_check_with_commissioning_and_expired_node(self):
87 # Have an interval 1 second longer than the timeout.
88@@ -3311,10 +3315,19 @@
89 status=NODE_STATUS.COMMISSIONING, created=datetime.now(),
90 updated=updated_at)
91
92- self.client.post(
93+ response = self.client.post(
94 self.get_uri('nodes/'), {'op': 'check_commissioning'})
95- node = reload_object(node)
96- self.assertEqual(NODE_STATUS.FAILED_TESTS, node.status)
97+ self.assertEqual(
98+ (
99+ httplib.OK,
100+ NODE_STATUS.FAILED_TESTS,
101+ [node.system_id]
102+ ),
103+ (
104+ response.status_code,
105+ reload_object(node).status,
106+ [node['system_id'] for node in json.loads(response.content)],
107+ ))
108
109
110 class TestPXEConfigAPI(AnonAPITestCase):