Merge lp:~allenap/maas/ntp-service-in-rack into lp:~maas-committers/maas/trunk

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 5279
Proposed branch: lp:~allenap/maas/ntp-service-in-rack
Merge into: lp:~maas-committers/maas/trunk
Prerequisite: lp:~allenap/maas/ntp-service-in-rack-skeleton
Diff against target: 357 lines (+156/-42)
9 files modified
src/maastesting/factory.py (+13/-5)
src/provisioningserver/plugin.py (+9/-4)
src/provisioningserver/rackdservices/testing.py (+36/-0)
src/provisioningserver/rackdservices/tests/test_ntp.py (+4/-28)
src/provisioningserver/rackdservices/tests/test_service_monitor_service.py (+9/-1)
src/provisioningserver/service_monitor.py (+27/-0)
src/provisioningserver/tests/test_plugin.py (+1/-1)
src/provisioningserver/tests/test_service_monitor.py (+56/-2)
src/provisioningserver/utils/service_monitor.py (+1/-1)
To merge this branch: bzr merge lp:~allenap/maas/ntp-service-in-rack
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+303380@code.launchpad.net

Commit message

Monitor the NTP service in the rack when it's not also a region controller.

To post a comment you must log in.
Revision history for this message
Blake Rouse (blake-rouse) wrote :

Looks good.

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :
Download full text (125.8 KiB)

The attempt to merge lp:~allenap/maas/ntp-service-in-rack into lp:maas failed. Below is the output from the failed tests.

Hit:1 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial InRelease
Get:2 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-updates InRelease [95.7 kB]
Get:3 http://security.ubuntu.com/ubuntu xenial-security InRelease [94.5 kB]
Hit:4 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-backports InRelease
Fetched 190 kB in 0s (439 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
    --no-install-recommends install apache2 archdetect-deb authbind bash bind9 bind9utils build-essential bzr bzr-builddeb chromium-browser chromium-chromedriver curl daemontools debhelper dh-apport dh-systemd distro-info dnsutils firefox freeipmi-tools git gjs ipython isc-dhcp-common isc-dhcp-server libjs-angularjs libjs-jquery libjs-jquery-hotkeys libjs-yui3-full libjs-yui3-min libpq-dev make nodejs-legacy npm postgresql pxelinux python3-all python3-apt python3-attr python3-bson python3-convoy python3-crochet python3-cssselect python3-curtin python3-dev python3-distro-info python3-django python3-django-nose python3-django-piston3 python3-dnspython python3-docutils python3-formencode python3-hivex python3-httplib2 python3-jinja2 python3-jsonschema python3-lxml python3-netaddr python3-netifaces python3-novaclient python3-oauth python3-oauthlib python3-openssl python3-paramiko python3-petname python3-pexpect python3-psycopg2 python3-pyinotify python3-pyparsing python3-pyvmomi python3-requests python3-seamicroclient python3-setuptools python3-simplestreams python3-sphinx python3-tempita python3-twisted python3-txtftp python3-tz python3-yaml python3-zope.interface python-bson python-crochet python-django python-django-piston python-djorm-ext-pgarray python-formencode python-lxml python-netaddr python-netifaces python-pocket-lint python-psycopg2 python-simplejson python-tempita python-twisted python-yaml socat syslinux-common tgt ubuntu-cloudimage-keyring wget xvfb
Reading package lists...
Building dependency tree...
Reading state information...
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
build-essential is already the newest version (12.1ubuntu2).
debhelper is already the newest version (9.20160115ubuntu3).
distro-info is already the newest version (0.14build1).
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest version (1:2.7.4-0ubuntu1).
libjs-angularjs is already the newest version (1.2.28-1ubuntu2).
libjs-jquery is already the newest version (1.11.3+dfsg-4).
libjs-yui3-full is already the newest version (3.5.1-1ubuntu3).
libjs-yui3-min is already the newest version (3.5.1-1ubuntu3).
make is already the newest version (4.1-6).
postgresql is already the newest version (9.5+173).
pxelinux is already the newest version (3:6.03+dfsg-11ubuntu1).
python-formencode is already the newest version (1.3.0-0ubuntu5).
python-lxml is already the newest version (3.5.0-1build1).
python-netaddr is already the newest version (0.7.18-1).
python-netifaces is already the newest version (0.10.4-...

Revision history for this message
MAAS Lander (maas-lander) wrote :
Download full text (125.8 KiB)

The attempt to merge lp:~allenap/maas/ntp-service-in-rack into lp:maas failed. Below is the output from the failed tests.

Hit:1 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial InRelease
Hit:2 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-updates InRelease
Hit:3 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-backports InRelease
Hit:4 http://security.ubuntu.com/ubuntu xenial-security InRelease
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
    --no-install-recommends install apache2 archdetect-deb authbind bash bind9 bind9utils build-essential bzr bzr-builddeb chromium-browser chromium-chromedriver curl daemontools debhelper dh-apport dh-systemd distro-info dnsutils firefox freeipmi-tools git gjs ipython isc-dhcp-common isc-dhcp-server libjs-angularjs libjs-jquery libjs-jquery-hotkeys libjs-yui3-full libjs-yui3-min libpq-dev make nodejs-legacy npm postgresql pxelinux python3-all python3-apt python3-attr python3-bson python3-convoy python3-crochet python3-cssselect python3-curtin python3-dev python3-distro-info python3-django python3-django-nose python3-django-piston3 python3-dnspython python3-docutils python3-formencode python3-hivex python3-httplib2 python3-jinja2 python3-jsonschema python3-lxml python3-netaddr python3-netifaces python3-novaclient python3-oauth python3-oauthlib python3-openssl python3-paramiko python3-petname python3-pexpect python3-psycopg2 python3-pyinotify python3-pyparsing python3-pyvmomi python3-requests python3-seamicroclient python3-setuptools python3-simplestreams python3-sphinx python3-tempita python3-twisted python3-txtftp python3-tz python3-yaml python3-zope.interface python-bson python-crochet python-django python-django-piston python-djorm-ext-pgarray python-formencode python-lxml python-netaddr python-netifaces python-pocket-lint python-psycopg2 python-simplejson python-tempita python-twisted python-yaml socat syslinux-common tgt ubuntu-cloudimage-keyring wget xvfb
Reading package lists...
Building dependency tree...
Reading state information...
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
build-essential is already the newest version (12.1ubuntu2).
debhelper is already the newest version (9.20160115ubuntu3).
distro-info is already the newest version (0.14build1).
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest version (1:2.7.4-0ubuntu1).
libjs-angularjs is already the newest version (1.2.28-1ubuntu2).
libjs-jquery is already the newest version (1.11.3+dfsg-4).
libjs-yui3-full is already the newest version (3.5.1-1ubuntu3).
libjs-yui3-min is already the newest version (3.5.1-1ubuntu3).
make is already the newest version (4.1-6).
postgresql is already the newest version (9.5+173).
pxelinux is already the newest version (3:6.03+dfsg-11ubuntu1).
python-formencode is already the newest version (1.3.0-0ubuntu5).
python-lxml is already the newest version (3.5.0-1build1).
python-netaddr is already the newest version (0.7.18-1).
python-netifaces is already the newest version (0.10.4-0.1build2).
python-psycopg2 is already the newest ve...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maastesting/factory.py'
2--- src/maastesting/factory.py 2016-08-09 15:56:46 +0000
3+++ src/maastesting/factory.py 2016-08-19 13:45:36 +0000
4@@ -10,6 +10,7 @@
5 ]
6
7 import datetime
8+from enum import Enum
9 from functools import partial
10 import http.client
11 import io
12@@ -168,15 +169,22 @@
13 def pick_enum(self, enum, *, but_not=EMPTY_SET):
14 """Pick a random item from an enumeration class.
15
16- :param enum: An enumeration class such as `NODE_STATUS`.
17+ :param enum: An enumeration class such as `NODE_STATUS`. Can also be
18+ an `enum.Enum` subclass.
19 :return: The value of one of its items.
20 :param but_not: A list of choices' IDs to exclude.
21 :type but_not: Sequence.
22 """
23- return random.choice([
24- value for key, value in vars(enum).items()
25- if not key.startswith('_') and value not in but_not
26- ])
27+ if issubclass(enum, Enum):
28+ return random.choice([
29+ value for value in enum
30+ if value not in but_not
31+ ])
32+ else:
33+ return random.choice([
34+ value for key, value in vars(enum).items()
35+ if not key.startswith('_') and value not in but_not
36+ ])
37
38 def pick_port(self, port_min=1024, port_max=65535):
39 assert port_min >= 0 and port_max <= 65535
40
41=== modified file 'src/provisioningserver/plugin.py'
42--- src/provisioningserver/plugin.py 2016-08-09 16:45:55 +0000
43+++ src/provisioningserver/plugin.py 2016-08-19 13:45:36 +0000
44@@ -150,6 +150,12 @@
45 service_monitor.setName("service_monitor")
46 return service_monitor
47
48+ def _makeNetworkTimeProtocolService(self, rpc_service):
49+ from provisioningserver.rackdservices import ntp
50+ ntp_service = ntp.RackNetworkTimeProtocolService(rpc_service, reactor)
51+ ntp_service.setName("ntp")
52+ return ntp_service
53+
54 def _makeServices(self, tftp_root, tftp_port):
55 # Several services need to make use of the RPC service.
56 rpc_service = self._makeRPCService()
57@@ -160,12 +166,11 @@
58 yield self._makeLeaseSocketService(rpc_service)
59 yield self._makeNodePowerMonitorService()
60 yield self._makeServiceMonitorService(rpc_service)
61- yield self._makeImageDownloadService(
62- rpc_service, tftp_root)
63+ yield self._makeImageDownloadService(rpc_service, tftp_root)
64+ yield self._makeNetworkTimeProtocolService(rpc_service)
65 # The following are network-accessible services.
66 yield self._makeImageService(tftp_root)
67- yield self._makeTFTPService(
68- tftp_root, tftp_port, rpc_service)
69+ yield self._makeTFTPService(tftp_root, tftp_port, rpc_service)
70
71 def _configureCrochet(self):
72 # Prevent other libraries from starting the reactor via crochet.
73
74=== added file 'src/provisioningserver/rackdservices/testing.py'
75--- src/provisioningserver/rackdservices/testing.py 1970-01-01 00:00:00 +0000
76+++ src/provisioningserver/rackdservices/testing.py 2016-08-19 13:45:36 +0000
77@@ -0,0 +1,36 @@
78+# Copyright 2016 Canonical Ltd. This software is licensed under the
79+# GNU Affero General Public License version 3 (see the file LICENSE).
80+
81+"""Testing resources for `provisioningserver.rackdservices`."""
82+
83+__all__ = [
84+ "prepareRegionForGetControllerType",
85+]
86+
87+from maastesting.factory import factory
88+from maastesting.twisted import always_succeed_with
89+from provisioningserver import services
90+from provisioningserver.rpc import region
91+from provisioningserver.rpc.testing import MockLiveClusterToRegionRPCFixture
92+
93+
94+def prepareRegionForGetControllerType(test, is_region=False, is_rack=True):
95+ """Set up a mock region controller that responds to `GetControllerType`.
96+
97+ In addition it sets the MAAS ID to a new random value. It arranges for
98+ tear-down at the end of the test.
99+
100+ :return: The running RPC service, and the protocol instance.
101+ """
102+ fixture = test.useFixture(MockLiveClusterToRegionRPCFixture())
103+ protocol, connecting = fixture.makeEventLoop(region.GetControllerType)
104+ protocol.RegisterRackController.side_effect = always_succeed_with(
105+ {"system_id": factory.make_name("maas-id")})
106+ protocol.GetControllerType.side_effect = always_succeed_with({
107+ "is_region": is_region, "is_rack": is_rack})
108+
109+ def connected(teardown):
110+ test.addCleanup(teardown)
111+ return services.getServiceNamed("rpc"), protocol
112+
113+ return connecting.addCallback(connected)
114
115=== modified file 'src/provisioningserver/rackdservices/tests/test_ntp.py'
116--- src/provisioningserver/rackdservices/tests/test_ntp.py 2016-08-17 14:48:54 +0000
117+++ src/provisioningserver/rackdservices/tests/test_ntp.py 2016-08-19 13:45:36 +0000
118@@ -19,18 +19,16 @@
119 MAASTestCase,
120 MAASTwistedRunTest,
121 )
122-from maastesting.twisted import (
123- always_succeed_with,
124- TwistedLoggerFixture,
125-)
126-from provisioningserver import services
127+from maastesting.twisted import TwistedLoggerFixture
128 from provisioningserver.rackdservices import ntp
129+from provisioningserver.rackdservices.testing import (
130+ prepareRegionForGetControllerType,
131+)
132 from provisioningserver.rpc import (
133 common,
134 exceptions,
135 region,
136 )
137-from provisioningserver.rpc.testing import MockLiveClusterToRegionRPCFixture
138 from provisioningserver.rpc.testing.doubles import FakeConnectionToRegion
139 from testtools.matchers import (
140 Equals,
141@@ -80,28 +78,6 @@
142 return common.Client(conn)
143
144
145-def prepareRegionForGetControllerType(test, is_region=False, is_rack=True):
146- """Set up a mock region controller that responds to `GetControllerType`.
147-
148- In addition it sets the MAAS ID to a new random value. It arranges for
149- tear-down at the end of the test.
150-
151- :return: The running RPC service, and the protocol instance.
152- """
153- fixture = test.useFixture(MockLiveClusterToRegionRPCFixture())
154- protocol, connecting = fixture.makeEventLoop(region.GetControllerType)
155- protocol.RegisterRackController.side_effect = always_succeed_with(
156- {"system_id": factory.make_name("maas-id")})
157- protocol.GetControllerType.side_effect = always_succeed_with({
158- "is_region": is_region, "is_rack": is_rack})
159-
160- def connected(teardown):
161- test.addCleanup(teardown)
162- return services.getServiceNamed("rpc"), protocol
163-
164- return connecting.addCallback(connected)
165-
166-
167 class TestRackNetworkTimeProtocolService(MAASTestCase):
168 """Tests for `RackNetworkTimeProtocolService`."""
169
170
171=== modified file 'src/provisioningserver/rackdservices/tests/test_service_monitor_service.py'
172--- src/provisioningserver/rackdservices/tests/test_service_monitor_service.py 2016-07-30 01:17:54 +0000
173+++ src/provisioningserver/rackdservices/tests/test_service_monitor_service.py 2016-08-19 13:45:36 +0000
174@@ -30,6 +30,7 @@
175 from provisioningserver.rpc.testing import MockLiveClusterToRegionRPCFixture
176 from provisioningserver.service_monitor import service_monitor
177 from provisioningserver.utils.service_monitor import (
178+ AlwaysOnService,
179 SERVICE_STATE,
180 ServiceState,
181 )
182@@ -111,7 +112,14 @@
183 protocol, connecting = self.patch_rpc_methods()
184 self.addCleanup((yield connecting))
185
186- service = self.pick_service()
187+ class ExampleService(AlwaysOnService):
188+ name = service_name = factory.make_name("service")
189+
190+ service = ExampleService()
191+ # Inveigle this new service into the service monitor.
192+ self.addCleanup(service_monitor._services.pop, service.name)
193+ service_monitor._services[service.name] = service
194+
195 state = ServiceState(SERVICE_STATE.ON, "running")
196 mock_ensureServices = self.patch(service_monitor, "ensureServices")
197 mock_ensureServices.return_value = succeed({
198
199=== modified file 'src/provisioningserver/service_monitor.py'
200--- src/provisioningserver/service_monitor.py 2016-08-18 15:21:07 +0000
201+++ src/provisioningserver/service_monitor.py 2016-08-19 13:45:36 +0000
202@@ -6,6 +6,9 @@
203 __all__ = [
204 ]
205
206+from provisioningserver.rpc import getRegionClient
207+from provisioningserver.rpc.exceptions import NoConnectionsAvailable
208+from provisioningserver.rpc.region import GetControllerType
209 from provisioningserver.utils.service_monitor import (
210 AlwaysOnService,
211 Service,
212@@ -61,10 +64,34 @@
213 service_name = "tgt"
214
215
216+class NTPService(Service):
217+ """Monitored NTP service."""
218+
219+ name = "ntp"
220+ service_name = "ntp"
221+
222+ def getExpectedState(self):
223+ try:
224+ client = getRegionClient()
225+ except NoConnectionsAvailable:
226+ return SERVICE_STATE.ANY, None
227+ else:
228+ d = client(GetControllerType, system_id=client.localIdent)
229+ d.addCallback(self._getExpectedStateForControllerType)
230+ return d
231+
232+ def _getExpectedStateForControllerType(self, controller_type):
233+ if controller_type["is_rack"] and not controller_type["is_region"]:
234+ return SERVICE_STATE.ON, None
235+ else:
236+ return SERVICE_STATE.ANY, None
237+
238+
239 # Global service monitor for rackd. NOTE that changes to this need to be
240 # mirrored in maasserver.model.services.
241 service_monitor = ServiceMonitor(
242 DHCPv4Service(),
243 DHCPv6Service(),
244+ NTPService(),
245 TGTService(),
246 )
247
248=== modified file 'src/provisioningserver/tests/test_plugin.py'
249--- src/provisioningserver/tests/test_plugin.py 2016-07-30 01:17:54 +0000
250+++ src/provisioningserver/tests/test_plugin.py 2016-08-19 13:45:36 +0000
251@@ -99,7 +99,7 @@
252 self.assertIsInstance(service, MultiService)
253 expected_services = [
254 "dhcp_probe", "networks_monitor", "image_download",
255- "lease_socket_service", "node_monitor", "rpc", "tftp",
256+ "lease_socket_service", "node_monitor", "ntp", "rpc", "tftp",
257 "image_service", "service_monitor",
258 ]
259 self.assertItemsEqual(expected_services, service.namedServices)
260
261=== modified file 'src/provisioningserver/tests/test_service_monitor.py'
262--- src/provisioningserver/tests/test_service_monitor.py 2016-08-18 15:21:07 +0000
263+++ src/provisioningserver/tests/test_service_monitor.py 2016-08-19 13:45:36 +0000
264@@ -8,15 +8,24 @@
265 from unittest.mock import sentinel
266
267 from maastesting.factory import factory
268-from maastesting.testcase import MAASTestCase
269+from maastesting.testcase import (
270+ MAASTestCase,
271+ MAASTwistedRunTest,
272+)
273+from provisioningserver.rackdservices.testing import (
274+ prepareRegionForGetControllerType,
275+)
276 from provisioningserver.service_monitor import (
277 DHCPService,
278 DHCPv4Service,
279 DHCPv6Service,
280+ NTPService,
281 service_monitor,
282 TGTService,
283 )
284 from provisioningserver.utils.service_monitor import SERVICE_STATE
285+from testtools.matchers import Equals
286+from twisted.internet.defer import inlineCallbacks
287
288
289 class TestDHCPService(MAASTestCase):
290@@ -99,8 +108,53 @@
291 self.assertEqual((SERVICE_STATE.ON, None), tgt.getExpectedState())
292
293
294+class TestNTPService(MAASTestCase):
295+
296+ def test_name_and_service_name(self):
297+ ntp = NTPService()
298+ self.assertEqual("ntp", ntp.service_name)
299+ self.assertEqual("ntp", ntp.name)
300+
301+
302+class TestNTPService_Scenarios(MAASTestCase):
303+
304+ run_tests_with = MAASTwistedRunTest.make_factory(timeout=5)
305+
306+ scenarios = (
307+ ("rack", dict(
308+ is_region=False, is_rack=True,
309+ expected_state=SERVICE_STATE.ON,
310+ )),
311+ ("region", dict(
312+ is_region=True, is_rack=False,
313+ expected_state=SERVICE_STATE.ANY,
314+ )),
315+ ("region+rack", dict(
316+ is_region=True, is_rack=True,
317+ expected_state=SERVICE_STATE.ANY,
318+ )),
319+ ("machine", dict(
320+ is_region=False, is_rack=False,
321+ expected_state=SERVICE_STATE.ANY,
322+ )),
323+ )
324+
325+ def setUp(self):
326+ super(TestNTPService_Scenarios, self).setUp()
327+ return prepareRegionForGetControllerType(
328+ self, is_region=self.is_region, is_rack=self.is_rack)
329+
330+ @inlineCallbacks
331+ def test_getExpectedState(self):
332+ ntp = NTPService()
333+ self.assertThat(
334+ (yield ntp.getExpectedState()),
335+ Equals((self.expected_state, None)))
336+
337+
338 class TestGlobalServiceMonitor(MAASTestCase):
339
340 def test__includes_all_services(self):
341 self.assertItemsEqual(
342- ["dhcpd", "dhcpd6", "tgt"], service_monitor._services.keys())
343+ ["dhcpd", "dhcpd6", "ntp", "tgt"],
344+ service_monitor._services)
345
346=== modified file 'src/provisioningserver/utils/service_monitor.py'
347--- src/provisioningserver/utils/service_monitor.py 2016-08-18 15:33:43 +0000
348+++ src/provisioningserver/utils/service_monitor.py 2016-08-19 13:45:36 +0000
349@@ -107,7 +107,7 @@
350 """
351 def deriveStatusInfo(expected_state_and_info, service):
352 expected_state, status_info = expected_state_and_info
353- _check_service_state_observed(expected_state)
354+ _check_service_state_expected(expected_state)
355 if status_info is None:
356 status_info = ""
357 if self.active_state == SERVICE_STATE.UNKNOWN: