)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":6618,"name":"Ruby Loo","email":"opensrloo@gmail.com","username":"rloo"},"change_message_id":"fe42b3d2d82afdda4e06868f125b48e98d1eeb6c","unresolved":false,"context_lines":[{"line_number":10,"context_line":"plain object, in order for it to be serialised to JSON. Currently,"},{"line_number":11,"context_line":"nested object fields and object list fields are not converted to dict"},{"line_number":12,"context_line":"format. This caused problems during cleaning, when the node object\u0027s"},{"line_number":13,"context_line":"as_dict representation is JSON encoded and sent to IPA."},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"This change adds support for calling as_dict() on nested objects and"},{"line_number":16,"context_line":"list objects, to ensure these are also returned in dict form."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":3,"id":"3fa0c359_60416a39","line":13,"updated":"2018-02-20 19:23:39.000000000","message":"yet another reason why we shouldn\u0027t be blindly sending the entire node info to IPA. oh well.","commit_id":"fccfb24a42178729c77e6bbf0c8f2b89cb1be1a4"},{"author":{"_account_id":6618,"name":"Ruby Loo","email":"opensrloo@gmail.com","username":"rloo"},"change_message_id":"91423beefb78e9844aa62803bcb05ecda06fb0a8","unresolved":false,"context_lines":[{"line_number":10,"context_line":"plain object, in order for it to be serialised to JSON. Currently,"},{"line_number":11,"context_line":"nested object fields and object list fields are not converted to dict"},{"line_number":12,"context_line":"format. This caused problems during cleaning, when the node object\u0027s"},{"line_number":13,"context_line":"as_dict representation is JSON encoded and sent to IPA."},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"This change adds support for calling as_dict() on nested objects and"},{"line_number":16,"context_line":"list objects, to ensure these are also returned in dict form."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":3,"id":"3fa0c359_e5f7b78a","line":13,"in_reply_to":"3fa0c359_4dd7b7ef","updated":"2018-02-21 13:55:41.000000000","message":"We had a security issue too. I don\u0027t think IPA needs all the node info, but I think it was easiest at the time, to send it all, just in case something else was needed...","commit_id":"fccfb24a42178729c77e6bbf0c8f2b89cb1be1a4"},{"author":{"_account_id":14826,"name":"Mark Goddard","email":"markgoddard86@gmail.com","username":"mgoddard"},"change_message_id":"db976e36eda149164c2312546383276577f3ff8c","unresolved":false,"context_lines":[{"line_number":10,"context_line":"plain object, in order for it to be serialised to JSON. Currently,"},{"line_number":11,"context_line":"nested object fields and object list fields are not converted to dict"},{"line_number":12,"context_line":"format. This caused problems during cleaning, when the node object\u0027s"},{"line_number":13,"context_line":"as_dict representation is JSON encoded and sent to IPA."},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"This change adds support for calling as_dict() on nested objects and"},{"line_number":16,"context_line":"list objects, to ensure these are also returned in dict form."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":3,"id":"3fa0c359_4dd7b7ef","line":13,"in_reply_to":"3fa0c359_60416a39","updated":"2018-02-21 12:19:24.000000000","message":"Yeah I guess the object representation should be standardised, perhaps shared via ironic-lib.","commit_id":"fccfb24a42178729c77e6bbf0c8f2b89cb1be1a4"},{"author":{"_account_id":6618,"name":"Ruby Loo","email":"opensrloo@gmail.com","username":"rloo"},"change_message_id":"149cc6304f34888144a2611139cdd39ba499258b","unresolved":false,"context_lines":[{"line_number":17,"context_line":""},{"line_number":18,"context_line":"We also change the method used in as_dict() for checking whether an"},{"line_number":19,"context_line":"object has an attribute. The hasattr() function used previously has"},{"line_number":20,"context_line":"problems when used with properties in python 2 [1], in that any"},{"line_number":21,"context_line":"exceptions raised in the property getter result in hasattr() returning"},{"line_number":22,"context_line":"False. Instead we use obj_attr_is_set() to determine whether the object"},{"line_number":23,"context_line":"has a particular attribute."}],"source_content_type":"text/x-gerrit-commit-message","patch_set":4,"id":"3fa0c359_f37d361c","line":20,"updated":"2018-02-21 21:08:52.000000000","message":"very interesting!","commit_id":"fda5f0ed1056c94a0aab446e597b9c6a71a018d6"},{"author":{"_account_id":14760,"name":"John L. Villalovos","email":"openstack.org@sodarock.com","username":"jlvillal"},"change_message_id":"ae98e4faff5b3d81fa0298cbe26073976b036ac2","unresolved":false,"context_lines":[{"line_number":15,"context_line":"This change adds support for calling as_dict() on nested objects and"},{"line_number":16,"context_line":"list objects, to ensure these are also returned in dict form."},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"We also change the method used in as_dict() for checking whether an"},{"line_number":19,"context_line":"object has an attribute. The hasattr() function used previously has"},{"line_number":20,"context_line":"problems when used with properties in python 2 [1], in that any"},{"line_number":21,"context_line":"exceptions raised in the property getter result in hasattr() returning"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":5,"id":"3fa0c359_5a60b6a5","line":18,"updated":"2018-02-22 02:37:20.000000000","message":"So I saw this:\n\nhttps://github.com/openstack/oslo.versionedobjects/blob/7da2437b4b5f3cf8b766513022874dde78aa4e34/oslo_versionedobjects/base.py#L674-L685\n\nSeems like obj_attr_is_set() uses hasattr() is that an issue?","commit_id":"c66679f14b0b5f34462a896c3bff5a9f483a9a83"}],"ironic/objects/base.py":[{"author":{"_account_id":13689,"name":"Hironori Shiina","email":"Hironori.Shiina@fujitsu.com","username":"shiina"},"change_message_id":"90239f3480a4a105d1479701a181eb9ad008f960","unresolved":false,"context_lines":[{"line_number":61,"context_line":"    fields \u003d {"},{"line_number":62,"context_line":"        \u0027created_at\u0027: object_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":63,"context_line":"        \u0027updated_at\u0027: object_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":64,"context_line":"    }"},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"    def as_dict(self):"},{"line_number":67,"context_line":"        \"\"\"Return the object represented as a dict."}],"source_content_type":"text/x-python","patch_set":2,"id":"3fa0c359_6de1772a","line":64,"updated":"2018-02-20 04:27:58.000000000","message":"I\u0027m not fully sure how these fields affect when inherited to TraitList. However, it doesn\u0027t cause any problem as far as I looked at codes.","commit_id":"c786e742f311972bf14782f907598b29c403f626"},{"author":{"_account_id":14826,"name":"Mark Goddard","email":"markgoddard86@gmail.com","username":"mgoddard"},"change_message_id":"ddc1697b0c90977d7d6bd767e5bb66f34283b9d8","unresolved":false,"context_lines":[{"line_number":61,"context_line":"    fields \u003d {"},{"line_number":62,"context_line":"        \u0027created_at\u0027: object_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":63,"context_line":"        \u0027updated_at\u0027: object_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":64,"context_line":"    }"},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"    def as_dict(self):"},{"line_number":67,"context_line":"        \"\"\"Return the object represented as a dict."}],"source_content_type":"text/x-python","patch_set":2,"id":"3fa0c359_bfa47d09","line":64,"in_reply_to":"3fa0c359_6de1772a","updated":"2018-02-20 10:21:05.000000000","message":"TraitList is a bit odd, as there is no list object in the DB - only Trait objects are saved. I think these fields just get ignored for TraitList.","commit_id":"c786e742f311972bf14782f907598b29c403f626"},{"author":{"_account_id":13689,"name":"Hironori Shiina","email":"Hironori.Shiina@fujitsu.com","username":"shiina"},"change_message_id":"90239f3480a4a105d1479701a181eb9ad008f960","unresolved":false,"context_lines":[{"line_number":343,"context_line":"        return changes"},{"line_number":344,"context_line":""},{"line_number":345,"context_line":""},{"line_number":346,"context_line":"class IronicObjectListBase(object_base.ObjectListBase):"},{"line_number":347,"context_line":""},{"line_number":348,"context_line":"    def as_dict(self):"},{"line_number":349,"context_line":"        \"\"\"Return the object represented as a dict."}],"source_content_type":"text/x-python","patch_set":2,"id":"3fa0c359_4a77c13b","line":346,"updated":"2018-02-20 04:27:58.000000000","message":"It might be better to set IronicObject here as a superclass rather than at subclasses to get as_dict() always called at L75.","commit_id":"c786e742f311972bf14782f907598b29c403f626"},{"author":{"_account_id":14826,"name":"Mark Goddard","email":"markgoddard86@gmail.com","username":"mgoddard"},"change_message_id":"ddc1697b0c90977d7d6bd767e5bb66f34283b9d8","unresolved":false,"context_lines":[{"line_number":343,"context_line":"        return changes"},{"line_number":344,"context_line":""},{"line_number":345,"context_line":""},{"line_number":346,"context_line":"class IronicObjectListBase(object_base.ObjectListBase):"},{"line_number":347,"context_line":""},{"line_number":348,"context_line":"    def as_dict(self):"},{"line_number":349,"context_line":"        \"\"\"Return the object represented as a dict."}],"source_content_type":"text/x-python","patch_set":2,"id":"3fa0c359_bf6f9dc3","line":346,"in_reply_to":"3fa0c359_4a77c13b","updated":"2018-02-20 10:21:05.000000000","message":"The reason I made this class is that TraitList.objects is a plain python list. This means that the check on L75 does not work. Rather than checking for lists in IronicObject.as_dict, I thought it might be better to override the list mixin.\n\nI didn\u0027t inherit from IronicObject here because object_base.ObjectListBase is designed to be a mixin, and I thought I should keep the usage the same.","commit_id":"c786e742f311972bf14782f907598b29c403f626"},{"author":{"_account_id":13689,"name":"Hironori Shiina","email":"Hironori.Shiina@fujitsu.com","username":"shiina"},"change_message_id":"488702d2d33117a1c151630f965ce2806b9ba2ec","unresolved":false,"context_lines":[{"line_number":343,"context_line":"        return changes"},{"line_number":344,"context_line":""},{"line_number":345,"context_line":""},{"line_number":346,"context_line":"class IronicObjectListBase(object_base.ObjectListBase):"},{"line_number":347,"context_line":""},{"line_number":348,"context_line":"    def as_dict(self):"},{"line_number":349,"context_line":"        \"\"\"Return the object represented as a dict."}],"source_content_type":"text/x-python","patch_set":2,"id":"3fa0c359_189b3218","line":346,"in_reply_to":"3fa0c359_bf6f9dc3","updated":"2018-02-21 05:12:18.000000000","message":"I see. Thanks.","commit_id":"c786e742f311972bf14782f907598b29c403f626"},{"author":{"_account_id":6618,"name":"Ruby Loo","email":"opensrloo@gmail.com","username":"rloo"},"change_message_id":"149cc6304f34888144a2611139cdd39ba499258b","unresolved":false,"context_lines":[{"line_number":78,"context_line":""},{"line_number":79,"context_line":"        return dict((k, _attr_as_dict(k))"},{"line_number":80,"context_line":"                    for k in self.fields"},{"line_number":81,"context_line":"                    if self.obj_attr_is_set(k))"},{"line_number":82,"context_line":""},{"line_number":83,"context_line":"    def obj_refresh(self, loaded_object):"},{"line_number":84,"context_line":"        \"\"\"Applies updates for objects that inherit from base.IronicObject."}],"source_content_type":"text/x-python","patch_set":4,"id":"3fa0c359_931952d8","line":81,"updated":"2018-02-21 21:08:52.000000000","message":"almost makes me want to just change this to a try: getattr() except:, since hasattr is being called from .obj_attr_is_set().\n\nBUT outside the scope of this patch!","commit_id":"fda5f0ed1056c94a0aab446e597b9c6a71a018d6"}],"ironic/objects/trait.py":[{"author":{"_account_id":6618,"name":"Ruby Loo","email":"opensrloo@gmail.com","username":"rloo"},"change_message_id":"fe42b3d2d82afdda4e06868f125b48e98d1eeb6c","unresolved":false,"context_lines":[{"line_number":98,"context_line":""},{"line_number":99,"context_line":""},{"line_number":100,"context_line":"@base.IronicObjectRegistry.register"},{"line_number":101,"context_line":"class TraitList(base.IronicObjectListBase, base.IronicObject):"},{"line_number":102,"context_line":"    # Version 1.0: Initial version"},{"line_number":103,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":104,"context_line":""}],"source_content_type":"text/x-python","patch_set":3,"id":"3fa0c359_80a03692","line":101,"updated":"2018-02-20 19:23:39.000000000","message":"do we have a test for a TraitList (that isn\u0027t empty), to show that it can be jsonified?","commit_id":"fccfb24a42178729c77e6bbf0c8f2b89cb1be1a4"},{"author":{"_account_id":14826,"name":"Mark Goddard","email":"markgoddard86@gmail.com","username":"mgoddard"},"change_message_id":"db976e36eda149164c2312546383276577f3ff8c","unresolved":false,"context_lines":[{"line_number":98,"context_line":""},{"line_number":99,"context_line":""},{"line_number":100,"context_line":"@base.IronicObjectRegistry.register"},{"line_number":101,"context_line":"class TraitList(base.IronicObjectListBase, base.IronicObject):"},{"line_number":102,"context_line":"    # Version 1.0: Initial version"},{"line_number":103,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":104,"context_line":""}],"source_content_type":"text/x-python","patch_set":3,"id":"3fa0c359_cdfc2774","line":101,"in_reply_to":"3fa0c359_80a03692","updated":"2018-02-21 12:19:24.000000000","message":"I\u0027ll add one.","commit_id":"fccfb24a42178729c77e6bbf0c8f2b89cb1be1a4"}],"ironic/tests/unit/objects/test_node.py":[{"author":{"_account_id":6618,"name":"Ruby Loo","email":"opensrloo@gmail.com","username":"rloo"},"change_message_id":"fe42b3d2d82afdda4e06868f125b48e98d1eeb6c","unresolved":false,"context_lines":[{"line_number":43,"context_line":"        self.assertEqual(\u0027fake\u0027, d[\u0027driver_info\u0027][\u0027ipmi_password\u0027])"},{"line_number":44,"context_line":"        self.assertEqual(\u0027data\u0027, d[\u0027instance_info\u0027][\u0027configdrive\u0027])"},{"line_number":45,"context_line":"        # Ensure the node can be serialised."},{"line_number":46,"context_line":"        json.dumps(d)"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"    def test_as_dict_secure(self):"},{"line_number":49,"context_line":"        self.node.driver_info[\u0027ipmi_password\u0027] \u003d \u0027fake\u0027"}],"source_content_type":"text/x-python","patch_set":3,"id":"3fa0c359_000c26a3","line":46,"updated":"2018-02-20 19:23:39.000000000","message":"I guess I am a bit confused (or just too lazy to look into the details of the bug). This node has a TraitsList that is empty; it would have failed this w/o your change.\n\nBut the bug is that things fail if there is at least one trait specified. Do we have a test that shows that it failed and now passes?","commit_id":"fccfb24a42178729c77e6bbf0c8f2b89cb1be1a4"},{"author":{"_account_id":13689,"name":"Hironori Shiina","email":"Hironori.Shiina@fujitsu.com","username":"shiina"},"change_message_id":"488702d2d33117a1c151630f965ce2806b9ba2ec","unresolved":false,"context_lines":[{"line_number":43,"context_line":"        self.assertEqual(\u0027fake\u0027, d[\u0027driver_info\u0027][\u0027ipmi_password\u0027])"},{"line_number":44,"context_line":"        self.assertEqual(\u0027data\u0027, d[\u0027instance_info\u0027][\u0027configdrive\u0027])"},{"line_number":45,"context_line":"        # Ensure the node can be serialised."},{"line_number":46,"context_line":"        json.dumps(d)"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"    def test_as_dict_secure(self):"},{"line_number":49,"context_line":"        self.node.driver_info[\u0027ipmi_password\u0027] \u003d \u0027fake\u0027"}],"source_content_type":"text/x-python","patch_set":3,"id":"3fa0c359_f862063e","line":46,"in_reply_to":"3fa0c359_000c26a3","updated":"2018-02-21 05:12:18.000000000","message":"In the bug report case, an empty traits field doesn\u0027t cause any error due to to_primitive() in oslo_serialization.jsonutils.dumps()[1] though I haven\u0027t dug this method.\n\nSo, It might be better to add a test with a non-empty traits field and jsonutils.dumps().\n\n[1] https://github.com/openstack/oslo.serialization/blob/master/oslo_serialization/jsonutils.py#L200","commit_id":"fccfb24a42178729c77e6bbf0c8f2b89cb1be1a4"},{"author":{"_account_id":14826,"name":"Mark Goddard","email":"markgoddard86@gmail.com","username":"mgoddard"},"change_message_id":"db976e36eda149164c2312546383276577f3ff8c","unresolved":false,"context_lines":[{"line_number":43,"context_line":"        self.assertEqual(\u0027fake\u0027, d[\u0027driver_info\u0027][\u0027ipmi_password\u0027])"},{"line_number":44,"context_line":"        self.assertEqual(\u0027data\u0027, d[\u0027instance_info\u0027][\u0027configdrive\u0027])"},{"line_number":45,"context_line":"        # Ensure the node can be serialised."},{"line_number":46,"context_line":"        json.dumps(d)"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"    def test_as_dict_secure(self):"},{"line_number":49,"context_line":"        self.node.driver_info[\u0027ipmi_password\u0027] \u003d \u0027fake\u0027"}],"source_content_type":"text/x-python","patch_set":3,"id":"3fa0c359_b825570b","line":46,"in_reply_to":"3fa0c359_f862063e","updated":"2018-02-21 12:19:24.000000000","message":"Good points from both of you.\n\nWithout my changes to as_dict, these tests fail, e.g.:\n\nironic.tests.unit.objects.test_node.TestNodeObject.test_as_dict_insecure\n------------------------------------------------------------------------\n\nCaptured traceback:\n~~~~~~~~~~~~~~~~~~~\n    Traceback (most recent call last):\n      File \"ironic/tests/unit/objects/test_node.py\", line 46, in test_as_dict_insecure\n        json.dumps(d)\n      File \"/usr/lib/python2.7/json/__init__.py\", line 244, in dumps\n        return _default_encoder.encode(obj)\n      File \"/usr/lib/python2.7/json/encoder.py\", line 207, in encode\n        chunks \u003d self.iterencode(o, _one_shot\u003dTrue)\n      File \"/usr/lib/python2.7/json/encoder.py\", line 270, in iterencode\n        return _iterencode(o, 0)\n      File \"/usr/lib/python2.7/json/encoder.py\", line 184, in default\n        raise TypeError(repr(o) + \" is not JSON serializable\")\n    TypeError: TraitList(created_at\u003d\u003c?\u003e,objects\u003d[],updated_at\u003d\u003c?\u003e) is not JSON serializable\n\nHironori is correct - if I change json.dumps to jsonutils.dumps, the test passes. I guess we ought to be using that function for a representative test.\n\nI\u0027ll add a test with some traits.","commit_id":"fccfb24a42178729c77e6bbf0c8f2b89cb1be1a4"}],"ironic/tests/unit/objects/test_objects.py":[{"author":{"_account_id":14826,"name":"Mark Goddard","email":"markgoddard86@gmail.com","username":"mgoddard"},"change_message_id":"db976e36eda149164c2312546383276577f3ff8c","unresolved":false,"context_lines":[{"line_number":679,"context_line":"        dt \u003d datetime.datetime(1955, 11, 5, 0, 0, tzinfo\u003diso8601.UTC)"},{"line_number":680,"context_line":"        obj \u003d MyObj(self.context)"},{"line_number":681,"context_line":"        obj.foo \u003d 1"},{"line_number":682,"context_line":"        obj.created_at \u003d obj.updated_at \u003d dt"},{"line_number":683,"context_line":"        result \u003d obj.as_dict()"},{"line_number":684,"context_line":"        expected \u003d {\u0027foo\u0027: 1, \u0027bar\u0027: \u0027loaded!\u0027, \u0027missing\u0027: \u0027loaded!\u0027,"},{"line_number":685,"context_line":"                    \u0027nested_object\u0027: \u0027loaded!\u0027,"}],"source_content_type":"text/x-python","patch_set":3,"id":"3fa0c359_5873f316","line":682,"updated":"2018-02-21 12:19:24.000000000","message":"I spent a while fighting these fields. I think the problem is that MyObj.obj_load_attr sets them to \u0027loaded!\u0027, which is not a valid datetime. I think I also hit an odd issue with hasattr() in python2, where if there is any exception when loading a property, hasattr returns False. This is fixed in py3, and is why only the py3 unit tests were failing.\n\nhttps://hynek.me/articles/hasattr/","commit_id":"fccfb24a42178729c77e6bbf0c8f2b89cb1be1a4"},{"author":{"_account_id":6618,"name":"Ruby Loo","email":"opensrloo@gmail.com","username":"rloo"},"change_message_id":"91423beefb78e9844aa62803bcb05ecda06fb0a8","unresolved":false,"context_lines":[{"line_number":679,"context_line":"        dt \u003d datetime.datetime(1955, 11, 5, 0, 0, tzinfo\u003diso8601.UTC)"},{"line_number":680,"context_line":"        obj \u003d MyObj(self.context)"},{"line_number":681,"context_line":"        obj.foo \u003d 1"},{"line_number":682,"context_line":"        obj.created_at \u003d obj.updated_at \u003d dt"},{"line_number":683,"context_line":"        result \u003d obj.as_dict()"},{"line_number":684,"context_line":"        expected \u003d {\u0027foo\u0027: 1, \u0027bar\u0027: \u0027loaded!\u0027, \u0027missing\u0027: \u0027loaded!\u0027,"},{"line_number":685,"context_line":"                    \u0027nested_object\u0027: \u0027loaded!\u0027,"}],"source_content_type":"text/x-python","patch_set":3,"id":"3fa0c359_c56ddb32","line":682,"in_reply_to":"3fa0c359_5873f316","updated":"2018-02-21 13:55:41.000000000","message":"The code was written to test other things (like lazy loading I think). I think this is fine since we don\u0027t care what their values are, as long as they are valid. I suppose you could add a comment above, eg \u0027set to valid datetimes, otherwise they default to \u0027loaded!\u0027.","commit_id":"fccfb24a42178729c77e6bbf0c8f2b89cb1be1a4"},{"author":{"_account_id":14826,"name":"Mark Goddard","email":"markgoddard86@gmail.com","username":"mgoddard"},"change_message_id":"2f44d884dde5912ae9d2f21abbc8aace333b8238","unresolved":false,"context_lines":[{"line_number":679,"context_line":"        dt \u003d datetime.datetime(1955, 11, 5, 0, 0, tzinfo\u003diso8601.UTC)"},{"line_number":680,"context_line":"        obj \u003d MyObj(self.context)"},{"line_number":681,"context_line":"        obj.foo \u003d 1"},{"line_number":682,"context_line":"        obj.created_at \u003d obj.updated_at \u003d dt"},{"line_number":683,"context_line":"        result \u003d obj.as_dict()"},{"line_number":684,"context_line":"        expected \u003d {\u0027foo\u0027: 1, \u0027bar\u0027: \u0027loaded!\u0027, \u0027missing\u0027: \u0027loaded!\u0027,"},{"line_number":685,"context_line":"                    \u0027nested_object\u0027: \u0027loaded!\u0027,"}],"source_content_type":"text/x-python","patch_set":3,"id":"3fa0c359_c8c6529b","line":682,"in_reply_to":"3fa0c359_c56ddb32","updated":"2018-02-21 14:14:02.000000000","message":"I\u0027m glad I typed out this explanation. It made me realise what I couldn\u0027t with my head buried in code. as_dict is using hasattr, and shouldn\u0027t be - any missing fields will cause exceptions to be raised. We should instead be using obj_attr_is_set.","commit_id":"fccfb24a42178729c77e6bbf0c8f2b89cb1be1a4"},{"author":{"_account_id":6618,"name":"Ruby Loo","email":"opensrloo@gmail.com","username":"rloo"},"change_message_id":"fe42b3d2d82afdda4e06868f125b48e98d1eeb6c","unresolved":false,"context_lines":[{"line_number":722,"context_line":"        result \u003d obj2.as_dict()"},{"line_number":723,"context_line":"        expected \u003d {\u0027objects\u0027: [{\u0027foo\u0027: 1, \u0027bar\u0027: \u0027loaded!\u0027,"},{"line_number":724,"context_line":"                                 \u0027missing\u0027: \u0027loaded!\u0027,"},{"line_number":725,"context_line":"                                 \u0027nested_object\u0027: \u0027loaded!\u0027,"},{"line_number":726,"context_line":"                                 \u0027created_at\u0027: dt, \u0027updated_at\u0027: dt}]}"},{"line_number":727,"context_line":"        self.assertEqual(expected, result)"},{"line_number":728,"context_line":""}],"source_content_type":"text/x-python","patch_set":3,"id":"3fa0c359_209b6273","line":725,"updated":"2018-02-20 19:23:39.000000000","message":"Ugh, we should have just supported nested objects from day 1!","commit_id":"fccfb24a42178729c77e6bbf0c8f2b89cb1be1a4"}],"ironic/tests/unit/objects/test_trait.py":[{"author":{"_account_id":12356,"name":"Vladyslav Drok","email":"vdrok@mirantis.com","username":"vdrok"},"change_message_id":"892d6303076a57c0b530af941844a68adbf341e0","unresolved":false,"context_lines":[{"line_number":102,"context_line":""},{"line_number":103,"context_line":"        self.assertEqual([self.fake_trait[\u0027trait\u0027]], result)"},{"line_number":104,"context_line":""},{"line_number":105,"context_line":"    def test_to_dict(self):"},{"line_number":106,"context_line":"        trait \u003d objects.Trait(context\u003dself.context,"},{"line_number":107,"context_line":"                              node_id\u003dself.fake_trait[\u0027node_id\u0027],"},{"line_number":108,"context_line":"                              trait\u003dself.fake_trait[\u0027trait\u0027])"}],"source_content_type":"text/x-python","patch_set":4,"id":"3fa0c359_c5186c77","line":105,"range":{"start_line":105,"start_character":13,"end_line":105,"end_character":15},"updated":"2018-02-21 23:58:18.000000000","message":"as :)","commit_id":"fda5f0ed1056c94a0aab446e597b9c6a71a018d6"},{"author":{"_account_id":14826,"name":"Mark Goddard","email":"markgoddard86@gmail.com","username":"mgoddard"},"change_message_id":"d90a3a8c9782173e8bf4d3cb12a2437b78f26046","unresolved":false,"context_lines":[{"line_number":102,"context_line":""},{"line_number":103,"context_line":"        self.assertEqual([self.fake_trait[\u0027trait\u0027]], result)"},{"line_number":104,"context_line":""},{"line_number":105,"context_line":"    def test_to_dict(self):"},{"line_number":106,"context_line":"        trait \u003d objects.Trait(context\u003dself.context,"},{"line_number":107,"context_line":"                              node_id\u003dself.fake_trait[\u0027node_id\u0027],"},{"line_number":108,"context_line":"                              trait\u003dself.fake_trait[\u0027trait\u0027])"}],"source_content_type":"text/x-python","patch_set":4,"id":"3fa0c359_e5655eb6","line":105,"range":{"start_line":105,"start_character":13,"end_line":105,"end_character":15},"in_reply_to":"3fa0c359_c5186c77","updated":"2018-02-22 10:24:23.000000000","message":"doh","commit_id":"fda5f0ed1056c94a0aab446e597b9c6a71a018d6"}],"releasenotes/notes/fix-cleaning-with-traits-3a54faa70d594fd0.yaml":[{"author":{"_account_id":13689,"name":"Hironori Shiina","email":"Hironori.Shiina@fujitsu.com","username":"shiina"},"change_message_id":"90239f3480a4a105d1479701a181eb9ad008f960","unresolved":false,"context_lines":[{"line_number":3,"context_line":"  - |"},{"line_number":4,"context_line":"    Fixes an issue seen during cleaning when the node being cleaned has one or"},{"line_number":5,"context_line":"    more traits assigned. This issue caused cleaning to fail, and the node to"},{"line_number":6,"context_line":"    enter the CLEAN_FAILED state.  See `bug 1750027"},{"line_number":7,"context_line":"    \u003chttps://bugs.launchpad.net/ironic/+bug/1750027\u003e`_ for details."}],"source_content_type":"text/x-yaml","patch_set":2,"id":"3fa0c359_eae9edb6","line":6,"range":{"start_line":6,"start_character":14,"end_line":6,"end_character":26},"updated":"2018-02-20 04:27:58.000000000","message":"``clean failed`` would be better because this is displayed to users.","commit_id":"c786e742f311972bf14782f907598b29c403f626"},{"author":{"_account_id":14826,"name":"Mark Goddard","email":"markgoddard86@gmail.com","username":"mgoddard"},"change_message_id":"ddc1697b0c90977d7d6bd767e5bb66f34283b9d8","unresolved":false,"context_lines":[{"line_number":3,"context_line":"  - |"},{"line_number":4,"context_line":"    Fixes an issue seen during cleaning when the node being cleaned has one or"},{"line_number":5,"context_line":"    more traits assigned. This issue caused cleaning to fail, and the node to"},{"line_number":6,"context_line":"    enter the CLEAN_FAILED state.  See `bug 1750027"},{"line_number":7,"context_line":"    \u003chttps://bugs.launchpad.net/ironic/+bug/1750027\u003e`_ for details."}],"source_content_type":"text/x-yaml","patch_set":2,"id":"3fa0c359_9fa981de","line":6,"range":{"start_line":6,"start_character":14,"end_line":6,"end_character":26},"in_reply_to":"3fa0c359_eae9edb6","updated":"2018-02-20 10:21:05.000000000","message":"Done","commit_id":"c786e742f311972bf14782f907598b29c403f626"}]}
