launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #07381
[Merge] lp:~julian-edwards/maas/commission-timeout-api into lp:maas
Julian Edwards has proposed merging lp:~julian-edwards/maas/commission-timeout-api into lp:maas.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~julian-edwards/maas/commission-timeout-api/+merge/103794
I think everyone has been involved with the direction of this change at some point or other. Despite where we end up driving this change, the basic API here is sound and tested. It cannot go anywhere else; we want the appservers to always be in control of database changes so that we can rely on transactional integrity in a single request/reply.
--
https://code.launchpad.net/~julian-edwards/maas/commission-timeout-api/+merge/103794
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~julian-edwards/maas/commission-timeout-api into lp:maas.
=== modified file 'src/maas/settings.py'
--- src/maas/settings.py 2012-04-18 10:51:03 +0000
+++ src/maas/settings.py 2012-04-27 03:09:21 +0000
@@ -276,5 +276,9 @@
# doing.
COMMISSIONING_SCRIPT = 'etc/maas/commissioning-user-data'
+# The duration, in minutes, after which we consider a commissioning node
+# to have failed and mark it as FAILED_TESTS.
+COMMISSIONING_TIMEOUT=60
+
# Allow the user to override settings in maas_local_settings.
import_local_settings()
=== modified file 'src/maasserver/api.py'
--- src/maasserver/api.py 2012-04-24 16:46:33 +0000
+++ src/maasserver/api.py 2012-04-27 03:09:21 +0000
@@ -68,12 +68,17 @@
]
from base64 import b64decode
+from datetime import (
+ datetime,
+ timedelta,
+ )
import httplib
import json
import sys
from textwrap import dedent
import types
+from django.conf import settings
from django.core.exceptions import (
PermissionDenied,
ValidationError,
@@ -488,6 +493,21 @@
"""Accept a node's enlistment: not allowed to anonymous users."""
raise Unauthorized("You must be logged in to accept nodes.")
+ @api_exported("check_commissioning", "POST")
+ def check_commissioning(self, request):
+ """Check all commissioning nodes to see if they are taking too long.
+
+ Anything that has been commissioning for longer than
+ settings.COMMISSIONING_TIMEOUT is moved into the FAILED_TESTS status.
+ """
+ interval = timedelta(minutes=settings.COMMISSIONING_TIMEOUT)
+ cutoff = datetime.now() - interval
+ query = Node.objects.filter(
+ status=NODE_STATUS.COMMISSIONING, updated__lte=cutoff)
+ query.update(status=NODE_STATUS.FAILED_TESTS)
+ # Note that Django doesn't call save() on updated nodes here,
+ # but I don't think anything requires its effects anyway.
+
@classmethod
def resource_uri(cls, *args, **kwargs):
return ('nodes_handler', [])
=== modified file 'src/maasserver/tests/test_api.py'
--- src/maasserver/tests/test_api.py 2012-04-26 05:44:05 +0000
+++ src/maasserver/tests/test_api.py 2012-04-27 03:09:21 +0000
@@ -13,6 +13,10 @@
__all__ = []
from base64 import b64encode
+from datetime import (
+ datetime,
+ timedelta,
+ )
from collections import namedtuple
import httplib
import json
@@ -21,6 +25,7 @@
import shutil
from django.conf import settings
+from django.db import models
from django.db.models.signals import post_save
from django.http import QueryDict
from fixtures import Fixture
@@ -1799,3 +1804,39 @@
(response.status_code, response.content))
self.assertRaises(
Node.DoesNotExist, Node.objects.get, hostname=hostname)
+
+
+class TestAnonymousCommissioningTimeout(APIv10TestMixin, TestCase):
+ """Testing of commissioning timeout API."""
+
+ def test_check_with_no_action(self):
+ node = factory.make_node(status=NODE_STATUS.READY)
+ response = self.client.post(
+ self.get_uri('nodes/'), {'op': 'check_commissioning'})
+ # Anything that's not commissioning should be ignored.
+ self.assertEqual(NODE_STATUS.READY, node.status)
+
+ def test_check_with_commissioning_but_not_expired_node(self):
+ node = factory.make_node(
+ status=NODE_STATUS.COMMISSIONING)
+ response = self.client.post(
+ self.get_uri('nodes/'), {'op': 'check_commissioning'})
+ self.assertEqual(NODE_STATUS.COMMISSIONING, node.status)
+
+ def test_check_with_commissioning_and_expired_node(self):
+ # Remove the custom save() method that updates "updated".
+ def fake_save(self, skip_check=None, *args, **kwargs):
+ models.Model.save(self, *args, **kwargs)
+ self.patch(Node, 'save', fake_save)
+
+ # Have an interval 1 second longer than the timeout.
+ interval = timedelta(seconds=1, minutes=settings.COMMISSIONING_TIMEOUT)
+ updated_at = updated=datetime.now() - interval
+ node = factory.make_node(
+ status=NODE_STATUS.COMMISSIONING, created=datetime.now(),
+ updated=updated_at)
+
+ response = self.client.post(
+ self.get_uri('nodes/'), {'op': 'check_commissioning'})
+ node = reload_object(node)
+ self.assertEqual(NODE_STATUS.FAILED_TESTS, node.status)