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
diff --git a/src/metadataserver/api.py b/src/metadataserver/api.py
index 3470a29..59c12fa 100644
--- a/src/metadataserver/api.py
+++ b/src/metadataserver/api.py
@@ -1,4 +1,4 @@
1# Copyright 2012-2018 Canonical Ltd. This software is licensed under the1# Copyright 2012-2019 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Metadata API."""4"""Metadata API."""
@@ -102,6 +102,7 @@ from metadataserver.vendor_data import get_vendor_data
102from piston3.utils import rc102from piston3.utils import rc
103from provisioningserver.events import (103from provisioningserver.events import (
104 EVENT_DETAILS,104 EVENT_DETAILS,
105 EVENT_STATUS_MESSAGES,
105 EVENT_TYPES,106 EVENT_TYPES,
106)107)
107from provisioningserver.logger import LegacyLogger108from provisioningserver.logger import LegacyLogger
@@ -190,7 +191,8 @@ def check_version(version):
190191
191192
192def add_event_to_node_event_log(193def add_event_to_node_event_log(
193 node, origin, action, description, result=None, created=None):194 node, origin, action, description,
195 event_type, result=None, created=None):
194 """Add an entry to the node's event log."""196 """Add an entry to the node's event log."""
195 if node.status == NODE_STATUS.COMMISSIONING:197 if node.status == NODE_STATUS.COMMISSIONING:
196 if result in ['SUCCESS', None]:198 if result in ['SUCCESS', None]:
@@ -216,10 +218,27 @@ def add_event_to_node_event_log(
216 else:218 else:
217 type_name = EVENT_TYPES.NODE_STATUS_EVENT219 type_name = EVENT_TYPES.NODE_STATUS_EVENT
218220
219 event_details = EVENT_DETAILS[type_name]221 # Create an extra event for the machine status messages.
222 if action in EVENT_STATUS_MESSAGES:
223 # cloud-init sends the action 'modules-final' for
224 # both commissioning and deployment but we are only interested
225 # in setting the message for deployment.
226 if ((action == 'modules-final' and
227 node.status == NODE_STATUS.DEPLOYING and
228 event_type == 'finish') or (
229 action != 'modules-final' and event_type == 'start')):
230 Event.objects.register_event_and_event_type(
231 EVENT_STATUS_MESSAGES[action],
232 type_level=EVENT_DETAILS[
233 EVENT_STATUS_MESSAGES[action]].level,
234 type_description=EVENT_DETAILS[
235 EVENT_STATUS_MESSAGES[action]].description,
236 event_action=action, event_description='',
237 system_id=node.system_id, created=created)
238
220 return Event.objects.register_event_and_event_type(239 return Event.objects.register_event_and_event_type(
221 type_name, type_level=event_details.level,240 type_name, type_level=EVENT_DETAILS[type_name].level,
222 type_description=event_details.description,241 type_description=EVENT_DETAILS[type_name].description,
223 event_action=action,242 event_action=action,
224 event_description="'%s' %s" % (origin, description),243 event_description="'%s' %s" % (origin, description),
225 system_id=node.system_id, created=created)244 system_id=node.system_id, created=created)
diff --git a/src/metadataserver/api_twisted.py b/src/metadataserver/api_twisted.py
index 0659d30..ddad25e 100644
--- a/src/metadataserver/api_twisted.py
+++ b/src/metadataserver/api_twisted.py
@@ -1,4 +1,4 @@
1# Copyright 2017-2018 Canonical Ltd. This software is licensed under the1# Copyright 2017-2019 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Metadata API that runs in the Twisted reactor."""4"""Metadata API that runs in the Twisted reactor."""
@@ -33,6 +33,7 @@ from metadataserver.api import (
33)33)
34from metadataserver.enum import SCRIPT_STATUS34from metadataserver.enum import SCRIPT_STATUS
35from metadataserver.models import NodeKey35from metadataserver.models import NodeKey
36from provisioningserver.events import EVENT_STATUS_MESSAGES
36from provisioningserver.logger import LegacyLogger37from provisioningserver.logger import LegacyLogger
37from provisioningserver.utils.twisted import deferred38from provisioningserver.utils.twisted import deferred
38from twisted.application.internet import TimerService39from twisted.application.internet import TimerService
@@ -307,7 +308,7 @@ class StatusWorkerService(TimerService, object):
307308
308 # Add this event to the node event log.309 # Add this event to the node event log.
309 add_event_to_node_event_log(310 add_event_to_node_event_log(
310 node, origin, activity_name, description, result,311 node, origin, activity_name, description, event_type, result,
311 message['timestamp'])312 message['timestamp'])
312313
313 # Group files together with the ScriptResult they belong.314 # Group files together with the ScriptResult they belong.
@@ -494,8 +495,18 @@ class StatusWorkerService(TimerService, object):
494 ] and495 ] and
495 message['event_type'] in ['start', 'finish'] and496 message['event_type'] in ['start', 'finish'] and
496 message['origin'] == 'curtin')497 message['origin'] == 'curtin')
498 is_status_message_event = (
499 message['name'] in EVENT_STATUS_MESSAGES and
500 message['event_type'] == 'start')
501 # modules-final is a cloudinit event that is part of
502 # EVENT_STATUS_MESSAGES but we want to create an event for
503 # the 'finish' event_type.
504 is_modules_final_event = (
505 message['name'] == 'modules-final' and
506 message['event_type'] == 'finish')
497 if (is_starting_event or is_final_event or has_files or507 if (is_starting_event or is_final_event or has_files or
498 is_curtin_early_late):508 is_curtin_early_late or is_status_message_event or
509 is_modules_final_event):
499 d = deferToDatabase(510 d = deferToDatabase(
500 self._processMessageNow, authorization, message)511 self._processMessageNow, authorization, message)
501 d.addErrback(512 d.addErrback(
diff --git a/src/metadataserver/tests/test_api.py b/src/metadataserver/tests/test_api.py
index 68d8b2f..1c48a98 100644
--- a/src/metadataserver/tests/test_api.py
+++ b/src/metadataserver/tests/test_api.py
@@ -1,4 +1,4 @@
1# Copyright 2012-2018 Canonical Ltd. This software is licensed under the1# Copyright 2012-2019 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for the metadata API."""4"""Tests for the metadata API."""
@@ -103,6 +103,7 @@ from netaddr import IPNetwork
103from provisioningserver.events import (103from provisioningserver.events import (
104 EVENT_DETAILS,104 EVENT_DETAILS,
105 EVENT_TYPES,105 EVENT_TYPES,
106 EVENT_STATUS_MESSAGES,
106)107)
107from provisioningserver.refresh.node_info_scripts import (108from provisioningserver.refresh.node_info_scripts import (
108 NODE_INFO_SCRIPTS,109 NODE_INFO_SCRIPTS,
@@ -227,7 +228,8 @@ class TestHelpers(MAASServerTestCase):
227 origin = factory.make_name('origin')228 origin = factory.make_name('origin')
228 action = factory.make_name('action')229 action = factory.make_name('action')
229 description = factory.make_name('description')230 description = factory.make_name('description')
230 add_event_to_node_event_log(node, origin, action, description)231 add_event_to_node_event_log(
232 node, origin, action, description, event_type='')
231 event = Event.objects.get(node=node)233 event = Event.objects.get(node=node)
232234
233 self.assertEqual(node, event.node)235 self.assertEqual(node, event.node)
@@ -236,12 +238,28 @@ class TestHelpers(MAASServerTestCase):
236 self.assertIn(description, event.description)238 self.assertIn(description, event.description)
237 self.assertEqual(expected_type[node.status], event.type.name)239 self.assertEqual(expected_type[node.status], event.type.name)
238240
241 def test_add_event_to_node_event_log_creates_events_status_messages(self):
242 for action in EVENT_STATUS_MESSAGES.keys():
243 node = factory.make_Node(status=NODE_STATUS.DEPLOYING)
244 origin = factory.make_name('origin')
245 description = factory.make_name('description')
246 add_event_to_node_event_log(
247 node, origin, action, description,
248 event_type='start' if action != 'modules-final' else 'finish')
249 event = Event.objects.filter(node=node).first()
250
251 self.assertEqual(node, event.node)
252 self.assertEqual(action, event.action)
253 self.assertEqual(EVENT_DETAILS[EVENT_STATUS_MESSAGES[
254 action]].description, event.type.description)
255 self.assertEqual('', event.description)
256
239 def test_add_event_to_node_event_log_logs_rack_refresh(self):257 def test_add_event_to_node_event_log_logs_rack_refresh(self):
240 rack = factory.make_RackController()258 rack = factory.make_RackController()
241 origin = factory.make_name('origin')259 origin = factory.make_name('origin')
242 action = factory.make_name('action')260 action = factory.make_name('action')
243 description = factory.make_name('description')261 description = factory.make_name('description')
244 add_event_to_node_event_log(rack, origin, action, description)262 add_event_to_node_event_log(rack, origin, action, description, '')
245 event = Event.objects.get(node=rack)263 event = Event.objects.get(node=rack)
246264
247 self.assertEqual(rack, event.node)265 self.assertEqual(rack, event.node)
diff --git a/src/metadataserver/tests/test_api_twisted.py b/src/metadataserver/tests/test_api_twisted.py
index 65e5014..81b6fbf 100644
--- a/src/metadataserver/tests/test_api_twisted.py
+++ b/src/metadataserver/tests/test_api_twisted.py
@@ -1,4 +1,4 @@
1# Copyright 2017-2018 Canonical Ltd. This software is licensed under the1# Copyright 2017-2019 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for the twisted metadata API."""4"""Tests for the twisted metadata API."""
@@ -64,6 +64,7 @@ from metadataserver.enum import (
64 SCRIPT_STATUS,64 SCRIPT_STATUS,
65)65)
66from metadataserver.models import NodeKey66from metadataserver.models import NodeKey
67from provisioningserver.events import EVENT_STATUS_MESSAGES
67from testtools import ExpectedException68from testtools import ExpectedException
68from testtools.matchers import (69from testtools.matchers import (
69 Equals,70 Equals,
@@ -299,6 +300,23 @@ class TestStatusWorkerServiceTransactional(MAASTransactionServerTestCase):
299300
300 @wait_for_reactor301 @wait_for_reactor
301 @inlineCallbacks302 @inlineCallbacks
303 def test_queueMessages_processes_top_level_status_messages_instantly(self):
304 for name in EVENT_STATUS_MESSAGES.keys():
305 worker = StatusWorkerService(sentinel.dbtasks)
306 mock_processMessage = self.patch(worker, "_processMessage")
307 message = self.make_message()
308 message['event_type'] = 'start'
309 message['name'] = name
310 nodes_with_tokens = yield deferToDatabase(
311 self.make_nodes_with_tokens)
312 node, token = nodes_with_tokens[0]
313 yield worker.queueMessage(token.key, message)
314 self.assertThat(
315 mock_processMessage,
316 MockCalledOnceWith(node, message))
317
318 @wait_for_reactor
319 @inlineCallbacks
302 def test_queueMessages_processes_files_message_instantly(self):320 def test_queueMessages_processes_files_message_instantly(self):
303 worker = StatusWorkerService(sentinel.dbtasks)321 worker = StatusWorkerService(sentinel.dbtasks)
304 mock_processMessage = self.patch(worker, "_processMessage")322 mock_processMessage = self.patch(worker, "_processMessage")
diff --git a/src/provisioningserver/events.py b/src/provisioningserver/events.py
index a1e2415..e72f281 100644
--- a/src/provisioningserver/events.py
+++ b/src/provisioningserver/events.py
@@ -1,10 +1,11 @@
1# Copyright 2014-2018 Canonical Ltd. This software is licensed under the1# Copyright 2014-2019 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Event catalog."""4"""Event catalog."""
55
6__all__ = [6__all__ = [
7 'EVENT_DETAILS',7 'EVENT_DETAILS',
8 'EVENT_STATUS_MESSAGES',
8 'EVENT_TYPES',9 'EVENT_TYPES',
9 'send_node_event',10 'send_node_event',
10 'send_node_event_mac_address',11 'send_node_event_mac_address',
@@ -141,6 +142,24 @@ class EVENT_TYPES:
141 NETWORKING = "NETWORKING"142 NETWORKING = "NETWORKING"
142 # Zones events143 # Zones events
143 ZONES = "ZONES"144 ZONES = "ZONES"
145 # Status message events
146 CONFIGURING_STORAGE = "CONFIGURING_STORAGE"
147 CONFIGURING_NETWORKING = "CONFIGURING_NETWORKING"
148 INSTALLING_OS = "INSTALLING_OS"
149 CONFIGURING_OS = "CONFIGURING_OS"
150 APPLYING_POST_INSTALLATION_CONFIG = "APPLYING_POST_INSTALLATION_CONFIG"
151 REBOOTING_MACHINE = "REBOOTING_MACHINE"
152
153# Used to create new events used for the machine's status.
154# The keys are the messages sent from cloud-init and curtin
155# to the metadataserver.
156EVENT_STATUS_MESSAGES = {
157 'cmd-install/stage-partitioning': EVENT_TYPES.CONFIGURING_STORAGE,
158 'cmd-install/stage-network': EVENT_TYPES.CONFIGURING_NETWORKING,
159 'cmd-install/stage-extract': EVENT_TYPES.INSTALLING_OS,
160 'cmd-install/stage-curthooks': EVENT_TYPES.CONFIGURING_OS,
161 'modules-final': EVENT_TYPES.APPLYING_POST_INSTALLATION_CONFIG,
162}
144163
145164
146EventDetail = namedtuple("EventDetail", ("description", "level"))165EventDetail = namedtuple("EventDetail", ("description", "level"))
@@ -401,6 +420,30 @@ EVENT_DETAILS = {
401 description=("Zones"),420 description=("Zones"),
402 level=AUDIT,421 level=AUDIT,
403 ),422 ),
423 EVENT_TYPES.CONFIGURING_STORAGE: EventDetail(
424 description=("Configuring storage"),
425 level=INFO,
426 ),
427 EVENT_TYPES.CONFIGURING_NETWORKING: EventDetail(
428 description=("Configuring networking"),
429 level=INFO,
430 ),
431 EVENT_TYPES.INSTALLING_OS: EventDetail(
432 description=("Installing OS"),
433 level=INFO,
434 ),
435 EVENT_TYPES.CONFIGURING_OS: EventDetail(
436 description=("Configuring OS"),
437 level=INFO,
438 ),
439 EVENT_TYPES.APPLYING_POST_INSTALLATION_CONFIG: EventDetail(
440 description=("Applying post installation config"),
441 level=INFO,
442 ),
443 EVENT_TYPES.REBOOTING_MACHINE: EventDetail(
444 description=("Rebooting machine"),
445 level=INFO,
446 ),
404}447}
405448
406449

Subscribers

People subscribed via source and target branches