Merge ~newell-jensen/maas:curtin-cloud-init-status-message-events into maas:master

Proposed by Newell Jensen
Status: Merged
Approved by: Newell Jensen
Approved revision: 100c1efc94de192dab10bf76d3d7e9fe1829079f
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~newell-jensen/maas:curtin-cloud-init-status-message-events
Merge into: maas:master
Diff against target: 277 lines (+122/-13)
5 files modified
src/metadataserver/api.py (+24/-5)
src/metadataserver/api_twisted.py (+14/-3)
src/metadataserver/tests/test_api.py (+21/-3)
src/metadataserver/tests/test_api_twisted.py (+19/-1)
src/provisioningserver/events.py (+44/-1)
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+365616@code.launchpad.net

Commit message

Add new event types and events for cloud-init and curtin messages that we want to user in conjunction with status messages for a more granular boot process.

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

Overall this looks good, just a question. More about the code not about how it was implemented. Implementation seems correct, just as we discussed.

review: Needs Information
Revision history for this message
Newell Jensen (newell-jensen) wrote :

Replied inline.

c2cc50a... by Newell Jensen

Fix typos.

100c1ef... by Newell Jensen

Key off the action instead of the curtin and cloudinit description. Update unit tests.

Revision history for this message
Blake Rouse (blake-rouse) wrote :

Looks good, thanks for the fixes.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/metadataserver/api.py b/src/metadataserver/api.py
2index 3470a29..59c12fa 100644
3--- a/src/metadataserver/api.py
4+++ b/src/metadataserver/api.py
5@@ -1,4 +1,4 @@
6-# Copyright 2012-2018 Canonical Ltd. This software is licensed under the
7+# Copyright 2012-2019 Canonical Ltd. This software is licensed under the
8 # GNU Affero General Public License version 3 (see the file LICENSE).
9
10 """Metadata API."""
11@@ -102,6 +102,7 @@ from metadataserver.vendor_data import get_vendor_data
12 from piston3.utils import rc
13 from provisioningserver.events import (
14 EVENT_DETAILS,
15+ EVENT_STATUS_MESSAGES,
16 EVENT_TYPES,
17 )
18 from provisioningserver.logger import LegacyLogger
19@@ -190,7 +191,8 @@ def check_version(version):
20
21
22 def add_event_to_node_event_log(
23- node, origin, action, description, result=None, created=None):
24+ node, origin, action, description,
25+ event_type, result=None, created=None):
26 """Add an entry to the node's event log."""
27 if node.status == NODE_STATUS.COMMISSIONING:
28 if result in ['SUCCESS', None]:
29@@ -216,10 +218,27 @@ def add_event_to_node_event_log(
30 else:
31 type_name = EVENT_TYPES.NODE_STATUS_EVENT
32
33- event_details = EVENT_DETAILS[type_name]
34+ # Create an extra event for the machine status messages.
35+ if action in EVENT_STATUS_MESSAGES:
36+ # cloud-init sends the action 'modules-final' for
37+ # both commissioning and deployment but we are only interested
38+ # in setting the message for deployment.
39+ if ((action == 'modules-final' and
40+ node.status == NODE_STATUS.DEPLOYING and
41+ event_type == 'finish') or (
42+ action != 'modules-final' and event_type == 'start')):
43+ Event.objects.register_event_and_event_type(
44+ EVENT_STATUS_MESSAGES[action],
45+ type_level=EVENT_DETAILS[
46+ EVENT_STATUS_MESSAGES[action]].level,
47+ type_description=EVENT_DETAILS[
48+ EVENT_STATUS_MESSAGES[action]].description,
49+ event_action=action, event_description='',
50+ system_id=node.system_id, created=created)
51+
52 return Event.objects.register_event_and_event_type(
53- type_name, type_level=event_details.level,
54- type_description=event_details.description,
55+ type_name, type_level=EVENT_DETAILS[type_name].level,
56+ type_description=EVENT_DETAILS[type_name].description,
57 event_action=action,
58 event_description="'%s' %s" % (origin, description),
59 system_id=node.system_id, created=created)
60diff --git a/src/metadataserver/api_twisted.py b/src/metadataserver/api_twisted.py
61index 0659d30..ddad25e 100644
62--- a/src/metadataserver/api_twisted.py
63+++ b/src/metadataserver/api_twisted.py
64@@ -1,4 +1,4 @@
65-# Copyright 2017-2018 Canonical Ltd. This software is licensed under the
66+# Copyright 2017-2019 Canonical Ltd. This software is licensed under the
67 # GNU Affero General Public License version 3 (see the file LICENSE).
68
69 """Metadata API that runs in the Twisted reactor."""
70@@ -33,6 +33,7 @@ from metadataserver.api import (
71 )
72 from metadataserver.enum import SCRIPT_STATUS
73 from metadataserver.models import NodeKey
74+from provisioningserver.events import EVENT_STATUS_MESSAGES
75 from provisioningserver.logger import LegacyLogger
76 from provisioningserver.utils.twisted import deferred
77 from twisted.application.internet import TimerService
78@@ -307,7 +308,7 @@ class StatusWorkerService(TimerService, object):
79
80 # Add this event to the node event log.
81 add_event_to_node_event_log(
82- node, origin, activity_name, description, result,
83+ node, origin, activity_name, description, event_type, result,
84 message['timestamp'])
85
86 # Group files together with the ScriptResult they belong.
87@@ -494,8 +495,18 @@ class StatusWorkerService(TimerService, object):
88 ] and
89 message['event_type'] in ['start', 'finish'] and
90 message['origin'] == 'curtin')
91+ is_status_message_event = (
92+ message['name'] in EVENT_STATUS_MESSAGES and
93+ message['event_type'] == 'start')
94+ # modules-final is a cloudinit event that is part of
95+ # EVENT_STATUS_MESSAGES but we want to create an event for
96+ # the 'finish' event_type.
97+ is_modules_final_event = (
98+ message['name'] == 'modules-final' and
99+ message['event_type'] == 'finish')
100 if (is_starting_event or is_final_event or has_files or
101- is_curtin_early_late):
102+ is_curtin_early_late or is_status_message_event or
103+ is_modules_final_event):
104 d = deferToDatabase(
105 self._processMessageNow, authorization, message)
106 d.addErrback(
107diff --git a/src/metadataserver/tests/test_api.py b/src/metadataserver/tests/test_api.py
108index 68d8b2f..1c48a98 100644
109--- a/src/metadataserver/tests/test_api.py
110+++ b/src/metadataserver/tests/test_api.py
111@@ -1,4 +1,4 @@
112-# Copyright 2012-2018 Canonical Ltd. This software is licensed under the
113+# Copyright 2012-2019 Canonical Ltd. This software is licensed under the
114 # GNU Affero General Public License version 3 (see the file LICENSE).
115
116 """Tests for the metadata API."""
117@@ -103,6 +103,7 @@ from netaddr import IPNetwork
118 from provisioningserver.events import (
119 EVENT_DETAILS,
120 EVENT_TYPES,
121+ EVENT_STATUS_MESSAGES,
122 )
123 from provisioningserver.refresh.node_info_scripts import (
124 NODE_INFO_SCRIPTS,
125@@ -227,7 +228,8 @@ class TestHelpers(MAASServerTestCase):
126 origin = factory.make_name('origin')
127 action = factory.make_name('action')
128 description = factory.make_name('description')
129- add_event_to_node_event_log(node, origin, action, description)
130+ add_event_to_node_event_log(
131+ node, origin, action, description, event_type='')
132 event = Event.objects.get(node=node)
133
134 self.assertEqual(node, event.node)
135@@ -236,12 +238,28 @@ class TestHelpers(MAASServerTestCase):
136 self.assertIn(description, event.description)
137 self.assertEqual(expected_type[node.status], event.type.name)
138
139+ def test_add_event_to_node_event_log_creates_events_status_messages(self):
140+ for action in EVENT_STATUS_MESSAGES.keys():
141+ node = factory.make_Node(status=NODE_STATUS.DEPLOYING)
142+ origin = factory.make_name('origin')
143+ description = factory.make_name('description')
144+ add_event_to_node_event_log(
145+ node, origin, action, description,
146+ event_type='start' if action != 'modules-final' else 'finish')
147+ event = Event.objects.filter(node=node).first()
148+
149+ self.assertEqual(node, event.node)
150+ self.assertEqual(action, event.action)
151+ self.assertEqual(EVENT_DETAILS[EVENT_STATUS_MESSAGES[
152+ action]].description, event.type.description)
153+ self.assertEqual('', event.description)
154+
155 def test_add_event_to_node_event_log_logs_rack_refresh(self):
156 rack = factory.make_RackController()
157 origin = factory.make_name('origin')
158 action = factory.make_name('action')
159 description = factory.make_name('description')
160- add_event_to_node_event_log(rack, origin, action, description)
161+ add_event_to_node_event_log(rack, origin, action, description, '')
162 event = Event.objects.get(node=rack)
163
164 self.assertEqual(rack, event.node)
165diff --git a/src/metadataserver/tests/test_api_twisted.py b/src/metadataserver/tests/test_api_twisted.py
166index 65e5014..81b6fbf 100644
167--- a/src/metadataserver/tests/test_api_twisted.py
168+++ b/src/metadataserver/tests/test_api_twisted.py
169@@ -1,4 +1,4 @@
170-# Copyright 2017-2018 Canonical Ltd. This software is licensed under the
171+# Copyright 2017-2019 Canonical Ltd. This software is licensed under the
172 # GNU Affero General Public License version 3 (see the file LICENSE).
173
174 """Tests for the twisted metadata API."""
175@@ -64,6 +64,7 @@ from metadataserver.enum import (
176 SCRIPT_STATUS,
177 )
178 from metadataserver.models import NodeKey
179+from provisioningserver.events import EVENT_STATUS_MESSAGES
180 from testtools import ExpectedException
181 from testtools.matchers import (
182 Equals,
183@@ -299,6 +300,23 @@ class TestStatusWorkerServiceTransactional(MAASTransactionServerTestCase):
184
185 @wait_for_reactor
186 @inlineCallbacks
187+ def test_queueMessages_processes_top_level_status_messages_instantly(self):
188+ for name in EVENT_STATUS_MESSAGES.keys():
189+ worker = StatusWorkerService(sentinel.dbtasks)
190+ mock_processMessage = self.patch(worker, "_processMessage")
191+ message = self.make_message()
192+ message['event_type'] = 'start'
193+ message['name'] = name
194+ nodes_with_tokens = yield deferToDatabase(
195+ self.make_nodes_with_tokens)
196+ node, token = nodes_with_tokens[0]
197+ yield worker.queueMessage(token.key, message)
198+ self.assertThat(
199+ mock_processMessage,
200+ MockCalledOnceWith(node, message))
201+
202+ @wait_for_reactor
203+ @inlineCallbacks
204 def test_queueMessages_processes_files_message_instantly(self):
205 worker = StatusWorkerService(sentinel.dbtasks)
206 mock_processMessage = self.patch(worker, "_processMessage")
207diff --git a/src/provisioningserver/events.py b/src/provisioningserver/events.py
208index a1e2415..e72f281 100644
209--- a/src/provisioningserver/events.py
210+++ b/src/provisioningserver/events.py
211@@ -1,10 +1,11 @@
212-# Copyright 2014-2018 Canonical Ltd. This software is licensed under the
213+# Copyright 2014-2019 Canonical Ltd. This software is licensed under the
214 # GNU Affero General Public License version 3 (see the file LICENSE).
215
216 """Event catalog."""
217
218 __all__ = [
219 'EVENT_DETAILS',
220+ 'EVENT_STATUS_MESSAGES',
221 'EVENT_TYPES',
222 'send_node_event',
223 'send_node_event_mac_address',
224@@ -141,6 +142,24 @@ class EVENT_TYPES:
225 NETWORKING = "NETWORKING"
226 # Zones events
227 ZONES = "ZONES"
228+ # Status message events
229+ CONFIGURING_STORAGE = "CONFIGURING_STORAGE"
230+ CONFIGURING_NETWORKING = "CONFIGURING_NETWORKING"
231+ INSTALLING_OS = "INSTALLING_OS"
232+ CONFIGURING_OS = "CONFIGURING_OS"
233+ APPLYING_POST_INSTALLATION_CONFIG = "APPLYING_POST_INSTALLATION_CONFIG"
234+ REBOOTING_MACHINE = "REBOOTING_MACHINE"
235+
236+# Used to create new events used for the machine's status.
237+# The keys are the messages sent from cloud-init and curtin
238+# to the metadataserver.
239+EVENT_STATUS_MESSAGES = {
240+ 'cmd-install/stage-partitioning': EVENT_TYPES.CONFIGURING_STORAGE,
241+ 'cmd-install/stage-network': EVENT_TYPES.CONFIGURING_NETWORKING,
242+ 'cmd-install/stage-extract': EVENT_TYPES.INSTALLING_OS,
243+ 'cmd-install/stage-curthooks': EVENT_TYPES.CONFIGURING_OS,
244+ 'modules-final': EVENT_TYPES.APPLYING_POST_INSTALLATION_CONFIG,
245+}
246
247
248 EventDetail = namedtuple("EventDetail", ("description", "level"))
249@@ -401,6 +420,30 @@ EVENT_DETAILS = {
250 description=("Zones"),
251 level=AUDIT,
252 ),
253+ EVENT_TYPES.CONFIGURING_STORAGE: EventDetail(
254+ description=("Configuring storage"),
255+ level=INFO,
256+ ),
257+ EVENT_TYPES.CONFIGURING_NETWORKING: EventDetail(
258+ description=("Configuring networking"),
259+ level=INFO,
260+ ),
261+ EVENT_TYPES.INSTALLING_OS: EventDetail(
262+ description=("Installing OS"),
263+ level=INFO,
264+ ),
265+ EVENT_TYPES.CONFIGURING_OS: EventDetail(
266+ description=("Configuring OS"),
267+ level=INFO,
268+ ),
269+ EVENT_TYPES.APPLYING_POST_INSTALLATION_CONFIG: EventDetail(
270+ description=("Applying post installation config"),
271+ level=INFO,
272+ ),
273+ EVENT_TYPES.REBOOTING_MACHINE: EventDetail(
274+ description=("Rebooting machine"),
275+ level=INFO,
276+ ),
277 }
278
279

Subscribers

People subscribed via source and target branches