~ricgard/maas/maas-docs-link-1.5

« back to all changes in this revision

Viewing changes to src/maasserver/tests/test_api_commissioning.py

  • Committer: MaaS Lander
  • Author(s): jtv at canonical
  • Date: 2014-08-08 20:26:02 UTC
  • mfrom: (2295.1.1 1.5-bug-1354511)
  • Revision ID: maas_lander-20140808202602-soo30bd57ogh34b2
[r=jtv][bug=1354511][author=jtv] Backport trunk r2668: When timing out nodes that haven't finished commissioning, compare the last-update timestamp to the database clock (from which it was also generated), not to the Python-side clock. Timezone skew between the two could cause nodes to time out immediately, or hours late.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
__all__ = []
16
16
 
17
17
from base64 import b64encode
18
 
from datetime import (
19
 
    datetime,
20
 
    timedelta,
21
 
    )
22
18
import httplib
23
19
import json
24
20
 
25
21
from django.conf import settings
26
22
from django.core.urlresolvers import reverse
 
23
from django.db import connection
27
24
from maasserver.enum import NODE_STATUS
28
25
from maasserver.testing import reload_object
29
26
from maasserver.testing.api import APITestCase
36
33
class TestCommissioningTimeout(MAASServerTestCase):
37
34
    """Testing of commissioning timeout API."""
38
35
 
 
36
    def age_node(self, node, minutes, cursor=None):
 
37
        """Set back the update/creation timestamps on `node` by `minutes`."""
 
38
        if cursor is None:
 
39
            cursor = connection.cursor()
 
40
        cursor.execute("""
 
41
            UPDATE maasserver_node
 
42
            SET
 
43
                created = created - interval '%f minutes',
 
44
                updated = updated - interval '%f minutes'
 
45
            WHERE id = %d
 
46
            """ % (minutes, minutes, node.id))
 
47
 
39
48
    def test_check_with_no_action(self):
40
49
        self.client_log_in()
41
50
        node = factory.make_node(status=NODE_STATUS.READY)
 
51
        self.age_node(node, settings.COMMISSIONING_TIMEOUT + 100)
42
52
        response = self.client.post(
43
53
            reverse('nodes_handler'), {'op': 'check_commissioning'})
44
54
        # Anything that's not commissioning should be ignored.
49
59
 
50
60
    def test_check_with_commissioning_but_not_expired_node(self):
51
61
        self.client_log_in()
52
 
        node = factory.make_node(
53
 
            status=NODE_STATUS.COMMISSIONING)
 
62
        node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
 
63
        self.age_node(node, settings.COMMISSIONING_TIMEOUT - 1)
54
64
        response = self.client.post(
55
65
            reverse('nodes_handler'), {'op': 'check_commissioning'})
56
66
        node = reload_object(node)
60
70
 
61
71
    def test_check_with_commissioning_and_expired_node(self):
62
72
        self.client_log_in()
63
 
        # Have an interval 1 second longer than the timeout.
64
 
        interval = timedelta(seconds=1, minutes=settings.COMMISSIONING_TIMEOUT)
65
 
        updated_at = datetime.now() - interval
66
 
        node = factory.make_node(
67
 
            status=NODE_STATUS.COMMISSIONING, created=datetime.now(),
68
 
            updated=updated_at)
 
73
        node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
 
74
        self.age_node(node, settings.COMMISSIONING_TIMEOUT + 1)
69
75
 
70
76
        response = self.client.post(
71
77
            reverse('nodes_handler'), {'op': 'check_commissioning'})
82
88
                 for response_node in json.loads(response.content)],
83
89
            ))
84
90
 
 
91
    def test_check_ignores_timezone_skew_between_python_and_database(self):
 
92
        cursor = connection.cursor()
 
93
        # Set time zone, for the duration of the ongoing transaction.
 
94
        cursor.execute("SET LOCAL TIME ZONE +13")
 
95
        self.client_log_in()
 
96
        late_node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
 
97
        self.age_node(
 
98
            late_node, settings.COMMISSIONING_TIMEOUT + 1, cursor=cursor)
 
99
        timely_node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
 
100
        self.age_node(
 
101
            timely_node, settings.COMMISSIONING_TIMEOUT - 1, cursor=cursor)
 
102
 
 
103
        response = self.client.post(
 
104
            reverse('nodes_handler'), {'op': 'check_commissioning'})
 
105
        self.assertEqual(
 
106
            (httplib.OK, [late_node.system_id]),
 
107
            (
 
108
                response.status_code,
 
109
                [
 
110
                    response_node['system_id']
 
111
                    for response_node in json.loads(response.content)
 
112
                ],
 
113
            ))
 
114
        self.assertEqual(
 
115
            NODE_STATUS.FAILED_TESTS,
 
116
            reload_object(late_node).status)
 
117
        self.assertEqual(
 
118
            NODE_STATUS.COMMISSIONING,
 
119
            reload_object(timely_node).status)
 
120
 
85
121
 
86
122
class AdminCommissioningScriptsAPITest(MAASServerTestCase):
87
123
    """Tests for `CommissioningScriptsHandler`."""