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
=== modified file 'src/maasserver/api.py'
--- src/maasserver/api.py 2012-11-29 11:30:37 +0000
+++ src/maasserver/api.py 2012-12-03 16:08:24 +0000
@@ -458,21 +458,6 @@
458 """Accept a node's enlistment: not allowed to anonymous users."""458 """Accept a node's enlistment: not allowed to anonymous users."""
459 raise Unauthorized("You must be logged in to accept nodes.")459 raise Unauthorized("You must be logged in to accept nodes.")
460460
461 @operation(idempotent=False)
462 def check_commissioning(self, request):
463 """Check all commissioning nodes to see if they are taking too long.
464
465 Anything that has been commissioning for longer than
466 settings.COMMISSIONING_TIMEOUT is moved into the FAILED_TESTS status.
467 """
468 interval = timedelta(minutes=settings.COMMISSIONING_TIMEOUT)
469 cutoff = datetime.now() - interval
470 query = Node.objects.filter(
471 status=NODE_STATUS.COMMISSIONING, updated__lte=cutoff)
472 query.update(status=NODE_STATUS.FAILED_TESTS)
473 # Note that Django doesn't call save() on updated nodes here,
474 # but I don't think anything requires its effects anyway.
475
476 @classmethod461 @classmethod
477 def resource_uri(cls, *args, **kwargs):462 def resource_uri(cls, *args, **kwargs):
478 return ('nodes_handler', [])463 return ('nodes_handler', [])
@@ -591,6 +576,23 @@
591 return filter(None, nodes)576 return filter(None, nodes)
592577
593 @operation(idempotent=False)578 @operation(idempotent=False)
579 def check_commissioning(self, request):
580 """Check all commissioning nodes to see if they are taking too long.
581
582 Anything that has been commissioning for longer than
583 settings.COMMISSIONING_TIMEOUT is moved into the FAILED_TESTS status.
584 """
585 interval = timedelta(minutes=settings.COMMISSIONING_TIMEOUT)
586 cutoff = datetime.now() - interval
587 query = Node.objects.filter(
588 status=NODE_STATUS.COMMISSIONING, updated__lte=cutoff)
589 results = list(query)
590 query.update(status=NODE_STATUS.FAILED_TESTS)
591 # Note that Django doesn't call save() on updated nodes here,
592 # but I don't think anything requires its effects anyway.
593 return results
594
595 @operation(idempotent=False)
594 def release(self, request):596 def release(self, request):
595 """Release multiple nodes.597 """Release multiple nodes.
596598
597599
=== modified file 'src/maasserver/tests/test_api.py'
--- src/maasserver/tests/test_api.py 2012-11-28 15:37:34 +0000
+++ src/maasserver/tests/test_api.py 2012-12-03 16:08:24 +0000
@@ -3284,24 +3284,28 @@
3284 (response.status_code, response.content))3284 (response.status_code, response.content))
32853285
32863286
3287class TestAnonymousCommissioningTimeout(APIv10TestMixin, TestCase):3287class TestCommissioningTimeout(APIv10TestMixin, LoggedInTestCase):
3288 """Testing of commissioning timeout API."""3288 """Testing of commissioning timeout API."""
32893289
3290 def test_check_with_no_action(self):3290 def test_check_with_no_action(self):
3291 node = factory.make_node(status=NODE_STATUS.READY)3291 node = factory.make_node(status=NODE_STATUS.READY)
3292 self.client.post(3292 response = self.client.post(
3293 self.get_uri('nodes/'), {'op': 'check_commissioning'})3293 self.get_uri('nodes/'), {'op': 'check_commissioning'})
3294 # Anything that's not commissioning should be ignored.3294 # Anything that's not commissioning should be ignored.
3295 node = reload_object(node)3295 node = reload_object(node)
3296 self.assertEqual(NODE_STATUS.READY, node.status)3296 self.assertEqual(
3297 (httplib.OK, NODE_STATUS.READY),
3298 (response.status_code, node.status))
32973299
3298 def test_check_with_commissioning_but_not_expired_node(self):3300 def test_check_with_commissioning_but_not_expired_node(self):
3299 node = factory.make_node(3301 node = factory.make_node(
3300 status=NODE_STATUS.COMMISSIONING)3302 status=NODE_STATUS.COMMISSIONING)
3301 self.client.post(3303 response = self.client.post(
3302 self.get_uri('nodes/'), {'op': 'check_commissioning'})3304 self.get_uri('nodes/'), {'op': 'check_commissioning'})
3303 node = reload_object(node)3305 node = reload_object(node)
3304 self.assertEqual(NODE_STATUS.COMMISSIONING, node.status)3306 self.assertEqual(
3307 (httplib.OK, NODE_STATUS.COMMISSIONING),
3308 (response.status_code, node.status))
33053309
3306 def test_check_with_commissioning_and_expired_node(self):3310 def test_check_with_commissioning_and_expired_node(self):
3307 # Have an interval 1 second longer than the timeout.3311 # Have an interval 1 second longer than the timeout.
@@ -3311,10 +3315,19 @@
3311 status=NODE_STATUS.COMMISSIONING, created=datetime.now(),3315 status=NODE_STATUS.COMMISSIONING, created=datetime.now(),
3312 updated=updated_at)3316 updated=updated_at)
33133317
3314 self.client.post(3318 response = self.client.post(
3315 self.get_uri('nodes/'), {'op': 'check_commissioning'})3319 self.get_uri('nodes/'), {'op': 'check_commissioning'})
3316 node = reload_object(node)3320 self.assertEqual(
3317 self.assertEqual(NODE_STATUS.FAILED_TESTS, node.status)3321 (
3322 httplib.OK,
3323 NODE_STATUS.FAILED_TESTS,
3324 [node.system_id]
3325 ),
3326 (
3327 response.status_code,
3328 reload_object(node).status,
3329 [node['system_id'] for node in json.loads(response.content)],
3330 ))
33183331
33193332
3320class TestPXEConfigAPI(AnonAPITestCase):3333class TestPXEConfigAPI(AnonAPITestCase):