From 3945fa4c2bda59ceac7c08a1228c09970f4685eb Mon Sep 17 00:00:00 2001 From: Ruby Loo Date: Tue, 25 Aug 2020 18:11:04 +0000 Subject: [PATCH] driver_internal_info in provision notifications Adds the node's driver_internal_info to the baremetal.node.provision_set.* notifications. The driver_internal_info includes useful information such as deploy and clean steps. Change-Id: I98784f72e6f93cbc602753ef2da0dbef5ad2c9cf Story: #2008054 --- ironic/objects/node.py | 7 +- ironic/objects/notification.py | 12 +-- ironic/tests/unit/objects/test_node.py | 2 + .../tests/unit/objects/test_notification.py | 83 +++++++++++++++++++ ironic/tests/unit/objects/test_objects.py | 2 +- ...driver_internal_info-3012f9834b6ade6b.yaml | 5 ++ 6 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 releasenotes/notes/notifications_driver_internal_info-3012f9834b6ade6b.yaml diff --git a/ironic/objects/node.py b/ironic/objects/node.py index 0392ee283c..013d1b50d6 100644 --- a/ironic/objects/node.py +++ b/ironic/objects/node.py @@ -916,13 +916,16 @@ class NodeSetProvisionStatePayload(NodePayload): # Version 1.13: Parent NodePayload version 1.13 # Version 1.14: Parent NodePayload version 1.14 # Version 1.15: Parent NodePayload version 1.15 - VERSION = '1.15' + # Version 1.16: add driver_internal_info + VERSION = '1.16' SCHEMA = dict(NodePayload.SCHEMA, - **{'instance_info': ('node', 'instance_info')}) + **{'instance_info': ('node', 'instance_info'), + 'driver_internal_info': ('node', 'driver_internal_info')}) fields = { 'instance_info': object_fields.FlexibleDictField(nullable=True), + 'driver_internal_info': object_fields.FlexibleDictField(nullable=True), 'event': object_fields.StringField(nullable=True), 'previous_provision_state': object_fields.StringField(nullable=True), 'previous_target_provision_state': diff --git a/ironic/objects/notification.py b/ironic/objects/notification.py index 076396c417..e8ac9b667e 100644 --- a/ironic/objects/notification.py +++ b/ironic/objects/notification.py @@ -188,11 +188,13 @@ class NotificationPublisher(base.IronicObject): def mask_secrets(payload): """Remove secrets from payload object.""" mask = '******' + + dict_fields = ['instance_info', 'driver_info', 'driver_internal_info'] + for f in dict_fields: + if hasattr(payload, f): + masked = strutils.mask_dict_password(getattr(payload, f), mask) + setattr(payload, f, masked) + if hasattr(payload, 'instance_info'): - payload.instance_info = strutils.mask_dict_password( - payload.instance_info, mask) if 'image_url' in payload.instance_info: payload.instance_info['image_url'] = mask - if hasattr(payload, 'driver_info'): - payload.driver_info = strutils.mask_dict_password( - payload.driver_info, mask) diff --git a/ironic/tests/unit/objects/test_node.py b/ironic/tests/unit/objects/test_node.py index 707d09e8d7..dd23995b9d 100644 --- a/ironic/tests/unit/objects/test_node.py +++ b/ironic/tests/unit/objects/test_node.py @@ -1282,6 +1282,8 @@ class TestNodePayloads(db_base.DbTestCase): 'DEPLOYING', 'DEPLOY') self._test_node_payload(payload) self.assertEqual(self.node.instance_info, payload.instance_info) + self.assertEqual(self.node.driver_internal_info, + payload.driver_internal_info) self.assertEqual('DEPLOY', payload.event) self.assertEqual('AVAILABLE', payload.previous_provision_state) self.assertEqual('DEPLOYING', payload.previous_target_provision_state) diff --git a/ironic/tests/unit/objects/test_notification.py b/ironic/tests/unit/objects/test_notification.py index 82c2a8dd92..255845debf 100644 --- a/ironic/tests/unit/objects/test_notification.py +++ b/ironic/tests/unit/objects/test_notification.py @@ -36,6 +36,16 @@ class TestNotificationBase(test_base.TestCase): 'fake_field_1': fields.StringField(nullable=True), } + @base.IronicObjectRegistry.register_if(False) + class TestObjectMaskSecrets(base.IronicObject): + VERSION = '1.0' + fields = { + 'instance_info': fields.FlexibleDictField(nullable=True), + 'driver_info': fields.FlexibleDictField(nullable=True), + 'driver_internal_info': fields.FlexibleDictField(nullable=True), + 'some_dict': fields.FlexibleDictField(nullable=True), + } + @base.IronicObjectRegistry.register_if(False) class TestNotificationPayload(notification.NotificationPayloadBase): VERSION = '1.0' @@ -61,6 +71,25 @@ class TestNotificationBase(test_base.TestCase): 'fake_field': fields.StringField() } + @base.IronicObjectRegistry.register_if(False) + class TestNotificationPayloadMaskSecrets( + notification.NotificationPayloadBase): + VERSION = '1.0' + + SCHEMA = { + 'instance_info': ('test_obj', 'instance_info'), + 'driver_info': ('test_obj', 'driver_info'), + 'driver_internal_info': ('test_obj', 'driver_internal_info'), + 'some_dict': ('test_obj', 'some_dict'), + } + + fields = { + 'instance_info': fields.FlexibleDictField(nullable=True), + 'driver_info': fields.FlexibleDictField(nullable=True), + 'driver_internal_info': fields.FlexibleDictField(nullable=True), + 'some_dict': fields.FlexibleDictField(nullable=True), + } + @base.IronicObjectRegistry.register_if(False) class TestNotification(notification.NotificationBase): VERSION = '1.0' @@ -281,3 +310,57 @@ class TestNotificationBase(test_base.TestCase): event_type = notification.EventType( object='test_object', action='test', status='start') self.assertRaises(ValueError, make_status_invalid) + + def test_mask_secrets_not_affected(self): + payload = self.TestNotificationPayload(an_extra_field='extra', + an_optional_field=1) + payload.populate_schema(test_obj=self.fake_obj) + notification.mask_secrets(payload) + self.assertEqual('extra', payload.an_extra_field) + self.assertEqual(1, payload.an_optional_field) + self.assertEqual(self.fake_obj.fake_field_1, payload.fake_field_a) + self.assertEqual(self.fake_obj.fake_field_2, payload.fake_field_b) + + def test_mask_secrets_no_secrets(self): + instance_info = {'inst1': 'v1'} + driver_info = {'driver_i1': 'd1'} + driver_internal_info = {'driver_int1': 'dii1'} + some_dict = {'key1': 'v1'} + test_obj = self.TestObjectMaskSecrets( + instance_info=instance_info, + driver_info=driver_info, + driver_internal_info=driver_internal_info, + some_dict=some_dict) + payload = self.TestNotificationPayloadMaskSecrets() + payload.populate_schema(test_obj=test_obj) + notification.mask_secrets(payload) + self.assertEqual(test_obj.instance_info, payload.instance_info) + self.assertEqual(test_obj.driver_info, payload.driver_info) + self.assertEqual(test_obj.driver_internal_info, + payload.driver_internal_info) + self.assertEqual(test_obj.some_dict, payload.some_dict) + + def test_mask_secrets_has_secrets(self): + instance_info = {'configdrive': 'somestuffhere', + 'image_url': 'http://image_to_fetch'} + driver_info = {'password': 'some password'} + driver_internal_info = {'agent_secret_token': '123532234145'} + some_dict = {'password': 'another password'} + test_obj = self.TestObjectMaskSecrets( + instance_info=instance_info, + driver_info=driver_info, + driver_internal_info=driver_internal_info, + some_dict=some_dict) + payload = self.TestNotificationPayloadMaskSecrets() + payload.populate_schema(test_obj=test_obj) + notification.mask_secrets(payload) + self.assertNotEqual(test_obj.instance_info, payload.instance_info) + self.assertEqual('******', payload.instance_info['configdrive']) + self.assertEqual('******', payload.instance_info['image_url']) + self.assertNotEqual(test_obj.driver_info, payload.driver_info) + self.assertEqual('******', payload.driver_info['password']) + self.assertNotEqual(test_obj.driver_internal_info, + payload.driver_internal_info) + self.assertEqual('******', + payload.driver_internal_info['agent_secret_token']) + self.assertEqual(test_obj.some_dict, payload.some_dict) diff --git a/ironic/tests/unit/objects/test_objects.py b/ironic/tests/unit/objects/test_objects.py index ffdf375fda..3852a9abd3 100644 --- a/ironic/tests/unit/objects/test_objects.py +++ b/ironic/tests/unit/objects/test_objects.py @@ -692,7 +692,7 @@ expected_object_fingerprints = { 'NodeCorrectedPowerStatePayload': '1.15-59a224a9191cdc9f1acc2e0dcd2d3adb', 'NodeSetProvisionStateNotification': '1.0-59acc533c11d306f149846f922739c15', - 'NodeSetProvisionStatePayload': '1.15-488a3d62a0643d17e288ecf89ed5bbb4', + 'NodeSetProvisionStatePayload': '1.16-c5a8eea43c514baf721fc61ce5d9d5a4', 'VolumeConnector': '1.0-3e0252c0ab6e6b9d158d09238a577d97', 'VolumeTarget': '1.0-0b10d663d8dae675900b2c7548f76f5e', 'ChassisCRUDNotification': '1.0-59acc533c11d306f149846f922739c15', diff --git a/releasenotes/notes/notifications_driver_internal_info-3012f9834b6ade6b.yaml b/releasenotes/notes/notifications_driver_internal_info-3012f9834b6ade6b.yaml new file mode 100644 index 0000000000..aaf39ddd79 --- /dev/null +++ b/releasenotes/notes/notifications_driver_internal_info-3012f9834b6ade6b.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Adds ``driver_internal_info`` field to the node-related notification + ``baremetal.node.provision_set.*``, new payload version 1.16.