1
# Copyright 2016 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
"""Tests for service monitoring in the regiond."""
10
from crochet import wait_for
11
from maasserver import service_monitor as service_monitor_module
12
from maasserver.enum import SERVICE_STATUS
13
from maasserver.models.service import Service
14
from maasserver.service_monitor import (
16
ServiceMonitorService,
18
from maasserver.testing.testcase import MAASTransactionServerTestCase
19
from maasserver.utils.orm import transactional
20
from maasserver.utils.threads import deferToDatabase
21
from maastesting.factory import factory
22
from maastesting.matchers import (
26
from maastesting.testcase import MAASTestCase
27
from maastesting.twisted import TwistedLoggerFixture
32
from provisioningserver.utils.service_monitor import (
36
from provisioningserver.utils.twisted import DeferredValue
37
from testtools.matchers import MatchesStructure
38
from twisted.internet.defer import (
43
from twisted.internet.task import Clock
46
wait_for_reactor = wait_for(30) # 30 seconds.
49
class TestGlobalServiceMonitor(MAASTestCase):
51
def test__includes_all_services(self):
52
self.assertItemsEqual(
53
["bind9", "proxy"], service_monitor._services.keys())
56
class TestServiceMonitorService(MAASTransactionServerTestCase):
58
def pick_service(self):
59
return random.choice(list(service_monitor._services.values()))
61
def test_init_sets_up_timer_correctly(self):
62
monitor_service = ServiceMonitorService(
63
sentinel.advertisingService, sentinel.clock)
64
self.assertThat(monitor_service, MatchesStructure.byEquality(
65
call=(monitor_service.monitorServices, (), {}),
66
step=(60), advertisingService=sentinel.advertisingService,
67
clock=sentinel.clock))
69
def test_monitorServices_does_not_do_anything_in_dev_environment(self):
70
# Belt-n-braces make sure we're in a development environment.
71
self.assertTrue(service_monitor_module.is_dev_environment())
73
monitor_service = ServiceMonitorService(
74
sentinel.advertisingService, Clock())
75
mock_ensureServices = self.patch(service_monitor, "ensureServices")
76
with TwistedLoggerFixture() as logger:
77
monitor_service.monitorServices()
78
self.assertThat(mock_ensureServices, MockNotCalled())
79
self.assertDocTestMatches(
80
"Skipping check of services; they're not running under the "
81
"supervision of systemd.", logger.output)
83
def test_monitorServices_calls_ensureServices(self):
84
# Pretend we're in a production environment.
86
service_monitor_module, "is_dev_environment").return_value = False
88
monitor_service = ServiceMonitorService(
89
sentinel.advertisingService, Clock())
90
mock_ensureServices = self.patch(service_monitor, "ensureServices")
91
monitor_service.monitorServices()
96
def test_monitorServices_handles_failure(self):
97
# Pretend we're in a production environment.
99
service_monitor_module, "is_dev_environment").return_value = False
101
monitor_service = ServiceMonitorService(
102
sentinel.advertisingService, Clock())
103
mock_ensureServices = self.patch(service_monitor, "ensureServices")
104
mock_ensureServices.return_value = fail(factory.make_exception())
105
with TwistedLoggerFixture() as logger:
106
monitor_service.monitorServices()
107
self.assertDocTestMatches("""\
108
Failed to monitor services and update database.
109
Traceback (most recent call last):
110
...""", logger.output)
113
def test_updates_services_in_database(self):
114
# Pretend we're in a production environment.
116
service_monitor_module, "is_dev_environment").return_value = False
118
service = self.pick_service()
119
state = ServiceState(SERVICE_STATE.ON, "running")
120
mock_ensureServices = self.patch(service_monitor, "ensureServices")
121
mock_ensureServices.return_value = succeed({
125
advertisingService = Mock()
126
advertisingService.processId = DeferredValue()
127
monitor_service = ServiceMonitorService(
128
advertisingService, Clock())
129
yield monitor_service.startService()
131
region = yield deferToDatabase(
132
transactional(factory.make_RegionController))
133
region_process = yield deferToDatabase(
134
transactional(factory.make_RegionControllerProcess), region)
135
advertisingService.processId.set(region_process.id)
136
yield monitor_service.stopService()
138
service = yield deferToDatabase(
139
transactional(Service.objects.get), node=region, name=service.name)
142
MatchesStructure.byEquality(
143
name=service.name, status=SERVICE_STATUS.RUNNING,
147
def test__buildServices_builds_services_list(self):
148
monitor_service = ServiceMonitorService(
149
sentinel.advertisingService, Clock())
150
service = self.pick_service()
151
state = ServiceState(SERVICE_STATE.ON, "running")
152
observed_services = yield monitor_service._buildServices({
155
expected_services = [{
156
"name": service.name,
160
self.assertEquals(expected_services, observed_services)