)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"5ee84f497dc0ca7a979b553ec30ba9a7bbcb9683","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":4,"id":"9f210522_ae76f3ee","updated":"2026-07-02 19:12:01.000000000","message":"Adding some general comments about the approach taken and to understand if we have alternatives, but nothing specific to the implementation itseld. Thanks Alfredo","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"fa532f3be31e982ead9effff4c6bcbdd2132000e","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":8,"id":"c6d20009_29e4b20e","updated":"2026-07-30 18:01:34.000000000","message":"this is better but i have only skimed it","commit_id":"dbdec1c78dd821c728d8ad8e17dfcad7264364e1"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"632efb10eb035060d33d91afd7c46d4ee381480e","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":12,"id":"85a63713_fca6c7dd","updated":"2026-07-31 12:51:15.000000000","message":"i think at this point my main conder with the direction here is the use of flask\n\nwhile i personally prefer flask to pecan and would not be agaisnt eventully rewriting the api in flask i find it hard to justfy using it for the emulator/fixture just because the ergonmics are nicer\n\ni have given some example of gemini werwring that into pecan so we can avoid pulling in yet another web framework\n\nwhat do you think? can we refactor this ot use the classmods and pecan?","commit_id":"90a0228a6ee437da5037820eca07bb6ee9b1c415"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"251e29fa91b44c2a619a5b60c410bb730b3207d8","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":12,"id":"8273109d_87a9a6e7","in_reply_to":"73ca7b7d_99c3dd6f","updated":"2026-07-31 15:43:48.000000000","message":"we do not need ot test wiht ssl in the functional tests that very much out of scope.\n\nwsgi-intercept is there to allow us to avoid actully spwanign a server in the fucntional test\n\nhttps://pypi.org/project/wsgi-intercept/\n\nfor the standalone emulator case we do not need ssl and if you wanted to add that you could jsut use apache or similr to proxy that on top\n\nlets keep this simple and not pull crypto into this","commit_id":"90a0228a6ee437da5037820eca07bb6ee9b1c415"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"7cc9c6971b884718d1b4626e380c2367c6725800","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":12,"id":"1b03c650_278d55a9","in_reply_to":"8273109d_87a9a6e7","updated":"2026-08-03 14:05:58.000000000","message":"It\u0027s requirement for standalone execution so that we can use the standalone emulator as drop-in replacement for environments where nova endpoing is https.","commit_id":"90a0228a6ee437da5037820eca07bb6ee9b1c415"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"2968f7e522f997557127a13d45ec283e407e7e9b","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":12,"id":"73ca7b7d_99c3dd6f","in_reply_to":"85a63713_fca6c7dd","updated":"2026-07-31 14:55:52.000000000","message":"flask is really convenient to create this although i see the point of being consistent to the real api. I will try to get it converted. We need to cover the ssl case also, let\u0027s see how much AI can help me on this...","commit_id":"90a0228a6ee437da5037820eca07bb6ee9b1c415"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"262c2b12c26eb2da63401e81e9410bf6757e1ee4","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":15,"id":"127015ef_386b147c","updated":"2026-08-03 15:30:51.000000000","message":"+1 for the new approach using topology builder methods. I still need to review everything again, but the new direction looks good. Thank you for updating based on reviewers feedback!","commit_id":"1d8771252415cd84986e07b2b6447f1a7eeb3c62"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"dc1a93a1ca2dc048d8372bae79fe70489e1c79a8","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":15,"id":"97ac7a98_14d919a8","updated":"2026-08-03 15:23:21.000000000","message":"recheck","commit_id":"1d8771252415cd84986e07b2b6447f1a7eeb3c62"}],"doc/source/contributor/testing.rst":[{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"1032c5d5ce76724b706cce895700dce39823e3f1","unresolved":true,"context_lines":[{"line_number":335,"context_line":""},{"line_number":336,"context_line":"The ``NovaPlacementFixture`` (in ``watcher/tests/local_fixtures/nova.py``)"},{"line_number":337,"context_line":"handles the wiring: it creates both emulators, installs the WSGI"},{"line_number":338,"context_line":"intercepts, and patches ``OpenStackClients`` so the decision engine\u0027s"},{"line_number":339,"context_line":"collectors build a real cluster data model from the emulated APIs."},{"line_number":340,"context_line":""},{"line_number":341,"context_line":"Defining topology inline (Python dicts)"}],"source_content_type":"text/x-rst","patch_set":5,"id":"68a5f5b1_77401b6c","line":338,"updated":"2026-07-08 10:15:25.000000000","message":"this is outdated and a bit more complicated since the `OpenstackClients` class is not used to connect to Nova or Placement anymore, but NovaHelper does use it to create a cinder client","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":false,"context_lines":[{"line_number":335,"context_line":""},{"line_number":336,"context_line":"The ``NovaPlacementFixture`` (in ``watcher/tests/local_fixtures/nova.py``)"},{"line_number":337,"context_line":"handles the wiring: it creates both emulators, installs the WSGI"},{"line_number":338,"context_line":"intercepts, and patches ``OpenStackClients`` so the decision engine\u0027s"},{"line_number":339,"context_line":"collectors build a real cluster data model from the emulated APIs."},{"line_number":340,"context_line":""},{"line_number":341,"context_line":"Defining topology inline (Python dicts)"}],"source_content_type":"text/x-rst","patch_set":5,"id":"1f504ec0_eae3b404","line":338,"in_reply_to":"68a5f5b1_77401b6c","updated":"2026-07-30 15:51:25.000000000","message":"Done","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"17cf3fa93dc22ab02a80b2636b0a19ce5f074f92","unresolved":true,"context_lines":[{"line_number":606,"context_line":""},{"line_number":607,"context_line":"    \u003cInstance uuid\u003d\"INST_1\" name\u003d\"vm-bfv\" vcpus\u003d\"2\" memory\u003d\"4096\""},{"line_number":608,"context_line":"              disk\u003d\"80\" state\u003d\"active\" bfv\u003d\"True\" /\u003e"},{"line_number":609,"context_line":""},{"line_number":610,"context_line":"Loading topology from XML model files"},{"line_number":611,"context_line":"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"},{"line_number":612,"context_line":""},{"line_number":613,"context_line":"Both emulators can load topology from the same XML model files used by"}],"source_content_type":"text/x-rst","patch_set":5,"id":"00e90d10_83290e64","line":610,"range":{"start_line":609,"start_character":1,"end_line":610,"end_character":37},"updated":"2026-07-03 16:09:35.000000000","message":"i storngly dislike this becuase i storng dislike the xmls we areadly have\nthey are quite hard to reason about and work with\n\nwith tha said w ecan supprot this but it shoudl not be the priamrly way we use this in my opiopon\n\ni twoudl be nice if these evecutlly went away in favor of programabtly buildign teh toplogy.","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":true,"context_lines":[{"line_number":606,"context_line":""},{"line_number":607,"context_line":"    \u003cInstance uuid\u003d\"INST_1\" name\u003d\"vm-bfv\" vcpus\u003d\"2\" memory\u003d\"4096\""},{"line_number":608,"context_line":"              disk\u003d\"80\" state\u003d\"active\" bfv\u003d\"True\" /\u003e"},{"line_number":609,"context_line":""},{"line_number":610,"context_line":"Loading topology from XML model files"},{"line_number":611,"context_line":"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"},{"line_number":612,"context_line":""},{"line_number":613,"context_line":"Both emulators can load topology from the same XML model files used by"}],"source_content_type":"text/x-rst","patch_set":5,"id":"ec69b1e9_4c98bf27","line":610,"range":{"start_line":609,"start_character":1,"end_line":610,"end_character":37},"in_reply_to":"00e90d10_83290e64","updated":"2026-07-30 15:51:25.000000000","message":"I want to keep the option to load topologies from xml or json files for the case of running the emulators in standalone mode, but i don\u0027t plan to use it for the functional tests in-code. I\u0027ve made it pretty clear in the documentation.","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"fa532f3be31e982ead9effff4c6bcbdd2132000e","unresolved":false,"context_lines":[{"line_number":606,"context_line":""},{"line_number":607,"context_line":"    \u003cInstance uuid\u003d\"INST_1\" name\u003d\"vm-bfv\" vcpus\u003d\"2\" memory\u003d\"4096\""},{"line_number":608,"context_line":"              disk\u003d\"80\" state\u003d\"active\" bfv\u003d\"True\" /\u003e"},{"line_number":609,"context_line":""},{"line_number":610,"context_line":"Loading topology from XML model files"},{"line_number":611,"context_line":"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"},{"line_number":612,"context_line":""},{"line_number":613,"context_line":"Both emulators can load topology from the same XML model files used by"}],"source_content_type":"text/x-rst","patch_set":5,"id":"26711244_7a7b9e11","line":610,"range":{"start_line":609,"start_character":1,"end_line":610,"end_character":37},"in_reply_to":"ec69b1e9_4c98bf27","updated":"2026-07-30 18:01:34.000000000","message":"Acknowledged","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"17cf3fa93dc22ab02a80b2636b0a19ce5f074f92","unresolved":true,"context_lines":[{"line_number":648,"context_line":"The Placement emulator extracts the allocation ratios and reserved values"},{"line_number":649,"context_line":"from the XML, while the Nova emulator extracts hypervisor and server state."},{"line_number":650,"context_line":""},{"line_number":651,"context_line":"Per-test topology loading"},{"line_number":652,"context_line":"~~~~~~~~~~~~~~~~~~~~~~~~~"},{"line_number":653,"context_line":""},{"line_number":654,"context_line":"When ``USE_NOVA_EMULATOR \u003d True`` is set on the test class, each test"}],"source_content_type":"text/x-rst","patch_set":5,"id":"f5bc9cca_88608ea1","line":651,"range":{"start_line":651,"start_character":0,"end_line":651,"end_character":2},"updated":"2026-07-03 16:09:35.000000000","message":"since your are supprotign this it hlep a bit but still i think we shoudl be buildign thse topploties programaticly\n\ni..e with function t add compute nodes and instance and volume ectra","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":false,"context_lines":[{"line_number":648,"context_line":"The Placement emulator extracts the allocation ratios and reserved values"},{"line_number":649,"context_line":"from the XML, while the Nova emulator extracts hypervisor and server state."},{"line_number":650,"context_line":""},{"line_number":651,"context_line":"Per-test topology loading"},{"line_number":652,"context_line":"~~~~~~~~~~~~~~~~~~~~~~~~~"},{"line_number":653,"context_line":""},{"line_number":654,"context_line":"When ``USE_NOVA_EMULATOR \u003d True`` is set on the test class, each test"}],"source_content_type":"text/x-rst","patch_set":5,"id":"9514ba4c_4b1928f5","line":651,"range":{"start_line":651,"start_character":0,"end_line":651,"end_character":2},"in_reply_to":"f5bc9cca_88608ea1","updated":"2026-07-30 15:51:25.000000000","message":"Done","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"17cf3fa93dc22ab02a80b2636b0a19ce5f074f92","unresolved":true,"context_lines":[{"line_number":706,"context_line":"Both emulators can also run as standalone Flask servers for manual testing"},{"line_number":707,"context_line":"or debugging outside the test framework::"},{"line_number":708,"context_line":""},{"line_number":709,"context_line":"    $ python watcher/tests/local_fixtures/nova_api_emulator.py \\"},{"line_number":710,"context_line":"        --model watcher/tests/unit/decision_engine/model/data/scenario_1.xml \\"},{"line_number":711,"context_line":"        --port 8774 --debug"},{"line_number":712,"context_line":""},{"line_number":713,"context_line":"    $ python watcher/tests/local_fixtures/placement_api_emulator.py \\"},{"line_number":714,"context_line":"        --model watcher/tests/unit/decision_engine/model/data/scenario_1.xml \\"}],"source_content_type":"text/x-rst","patch_set":5,"id":"7fc43e21_819bf399","line":711,"range":{"start_line":709,"start_character":5,"end_line":711,"end_character":27},"updated":"2026-07-03 16:09:35.000000000","message":"tox -e venv python -m watcher.tests.functional.nova_api_emulator \\\n  --model watcher/tests/unit/decision_engine/model/data/scenario_1.xml\n  \nit would be better not to assueme  watcher is installed in the system path with its depencies and doucemnt this via tox instead.","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":false,"context_lines":[{"line_number":706,"context_line":"Both emulators can also run as standalone Flask servers for manual testing"},{"line_number":707,"context_line":"or debugging outside the test framework::"},{"line_number":708,"context_line":""},{"line_number":709,"context_line":"    $ python watcher/tests/local_fixtures/nova_api_emulator.py \\"},{"line_number":710,"context_line":"        --model watcher/tests/unit/decision_engine/model/data/scenario_1.xml \\"},{"line_number":711,"context_line":"        --port 8774 --debug"},{"line_number":712,"context_line":""},{"line_number":713,"context_line":"    $ python watcher/tests/local_fixtures/placement_api_emulator.py \\"},{"line_number":714,"context_line":"        --model watcher/tests/unit/decision_engine/model/data/scenario_1.xml \\"}],"source_content_type":"text/x-rst","patch_set":5,"id":"3a558c87_6266ad2c","line":711,"range":{"start_line":709,"start_character":5,"end_line":711,"end_character":27},"in_reply_to":"7fc43e21_819bf399","updated":"2026-07-30 15:51:25.000000000","message":"Done","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"fa532f3be31e982ead9effff4c6bcbdd2132000e","unresolved":true,"context_lines":[{"line_number":365,"context_line":"that matter for their scenario:"},{"line_number":366,"context_line":""},{"line_number":367,"context_line":".. code-block:: python"},{"line_number":368,"context_line":""},{"line_number":369,"context_line":"    from watcher.tests.functional import base"},{"line_number":370,"context_line":"    from watcher.tests.functional.topology import add_computes"},{"line_number":371,"context_line":"    from watcher.tests.functional.topology import add_instances"},{"line_number":372,"context_line":"    from watcher.tests.functional.topology import ComputeTopology"},{"line_number":373,"context_line":""},{"line_number":374,"context_line":""},{"line_number":375,"context_line":"    MY_TOPOLOGY \u003d ComputeTopology()"}],"source_content_type":"text/x-rst","patch_set":8,"id":"ca6e3d6d_7aa9ffcd","line":372,"range":{"start_line":368,"start_character":1,"end_line":372,"end_character":65},"updated":"2026-07-30 18:01:34.000000000","message":"so you sould never import function or classes like that in openstack\nhttps://github.com/openstack/hacking/blob/master/HACKING.rst?plain\u003d1#L59\n\n\n```suggestion\n\n    from watcher.tests.functional import base\n    from watcher.tests.functional import topology\n```","commit_id":"dbdec1c78dd821c728d8ad8e17dfcad7264364e1"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"68a6ed7c5b4ebf73061a6197647442f1babda697","unresolved":false,"context_lines":[{"line_number":365,"context_line":"that matter for their scenario:"},{"line_number":366,"context_line":""},{"line_number":367,"context_line":".. code-block:: python"},{"line_number":368,"context_line":""},{"line_number":369,"context_line":"    from watcher.tests.functional import base"},{"line_number":370,"context_line":"    from watcher.tests.functional.topology import add_computes"},{"line_number":371,"context_line":"    from watcher.tests.functional.topology import add_instances"},{"line_number":372,"context_line":"    from watcher.tests.functional.topology import ComputeTopology"},{"line_number":373,"context_line":""},{"line_number":374,"context_line":""},{"line_number":375,"context_line":"    MY_TOPOLOGY \u003d ComputeTopology()"}],"source_content_type":"text/x-rst","patch_set":8,"id":"b074b859_aabbb1d5","line":372,"range":{"start_line":368,"start_character":1,"end_line":372,"end_character":65},"in_reply_to":"ca6e3d6d_7aa9ffcd","updated":"2026-07-31 10:30:58.000000000","message":"Done","commit_id":"dbdec1c78dd821c728d8ad8e17dfcad7264364e1"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"fa532f3be31e982ead9effff4c6bcbdd2132000e","unresolved":true,"context_lines":[{"line_number":372,"context_line":"    from watcher.tests.functional.topology import ComputeTopology"},{"line_number":373,"context_line":""},{"line_number":374,"context_line":""},{"line_number":375,"context_line":"    MY_TOPOLOGY \u003d ComputeTopology()"},{"line_number":376,"context_line":"    add_computes(MY_TOPOLOGY, count\u003d2)"},{"line_number":377,"context_line":"    add_instances(MY_TOPOLOGY, computes\u003d[\u0027compute-1\u0027], count\u003d2, vcpus\u003d2)"},{"line_number":378,"context_line":"    add_instances(MY_TOPOLOGY, computes\u003d[\u0027compute-2\u0027], count\u003d1, vcpus\u003d2)"},{"line_number":379,"context_line":""},{"line_number":380,"context_line":""},{"line_number":381,"context_line":"    class TestMyStrategy(base.WatcherFunctionalTestCase):"}],"source_content_type":"text/x-rst","patch_set":8,"id":"04a14247_3ab99332","line":378,"range":{"start_line":375,"start_character":4,"end_line":378,"end_character":72},"updated":"2026-07-30 18:01:34.000000000","message":"```suggestion\n    MY_TOPOLOGY \u003d topology.ComputeTopology()\n    topology.add_computes(MY_TOPOLOGY, count\u003d2)\n    topology.add_instances(MY_TOPOLOGY, computes\u003d[\u0027compute-1\u0027], count\u003d2, vcpus\u003d2)\n    topology.add_instances(MY_TOPOLOGY, computes\u003d[\u0027compute-2\u0027], count\u003d1, vcpus\u003d2)\n```\n\nalthough if the funciton were method on the class\n\n```\n    MY_TOPOLOGY \u003d topology.ComputeTopology()\n    MY_TOPOLOGY.add_computes(count\u003d2)\n    MY_TOPOLOGY.add_instances(computes\u003d[\u0027compute-1\u0027], count\u003d2, vcpus\u003d2)\n    MY_TOPOLOGY.add_instances(computes\u003d[\u0027compute-2\u0027], count\u003d1, vcpus\u003d2)\n```\n\nand if you use the builer pathen properly in pyton each method shoudl return self ]\nso you can do this\n\n```\n    MY_TOPOLOGY \u003d (\n        topology.ComputeTopology()\n        .add_computes(count\u003d2)\n        .add_instances(computes\u003d[\u0027compute-1\u0027], count\u003d2, vcpus\u003d2)\n        .add_instances(computes\u003d[\u0027compute-2\u0027], count\u003d1, vcpus\u003d2)\n    )\n```\n\nhttps://www.freecodecamp.org/news/how-to-use-the-builder-pattern-in-python-a-practical-guide-for-devs/","commit_id":"dbdec1c78dd821c728d8ad8e17dfcad7264364e1"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"68a6ed7c5b4ebf73061a6197647442f1babda697","unresolved":false,"context_lines":[{"line_number":372,"context_line":"    from watcher.tests.functional.topology import ComputeTopology"},{"line_number":373,"context_line":""},{"line_number":374,"context_line":""},{"line_number":375,"context_line":"    MY_TOPOLOGY \u003d ComputeTopology()"},{"line_number":376,"context_line":"    add_computes(MY_TOPOLOGY, count\u003d2)"},{"line_number":377,"context_line":"    add_instances(MY_TOPOLOGY, computes\u003d[\u0027compute-1\u0027], count\u003d2, vcpus\u003d2)"},{"line_number":378,"context_line":"    add_instances(MY_TOPOLOGY, computes\u003d[\u0027compute-2\u0027], count\u003d1, vcpus\u003d2)"},{"line_number":379,"context_line":""},{"line_number":380,"context_line":""},{"line_number":381,"context_line":"    class TestMyStrategy(base.WatcherFunctionalTestCase):"}],"source_content_type":"text/x-rst","patch_set":8,"id":"cbeadb6b_7db64e3a","line":378,"range":{"start_line":375,"start_character":4,"end_line":378,"end_character":72},"in_reply_to":"04a14247_3ab99332","updated":"2026-07-31 10:30:58.000000000","message":"Done","commit_id":"dbdec1c78dd821c728d8ad8e17dfcad7264364e1"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"ba8f7a71b05159c9c4d8983312a1e61916f40da7","unresolved":false,"context_lines":[{"line_number":345,"context_line":"~~~~~~~~~~~~~~~~~~~~~~"},{"line_number":346,"context_line":""},{"line_number":347,"context_line":"The ``NovaAPIEmulator`` and ``PlacementAPIEmulator`` (in"},{"line_number":348,"context_line":"``watcher/tests/local_fixtures/``) are lightweight Flask apps that serve the"},{"line_number":349,"context_line":"subset of Nova v2.1 and Placement APIs that Watcher\u0027s collectors and"},{"line_number":350,"context_line":"actions use. They are wired into the test process via ``wsgi-intercept``"},{"line_number":351,"context_line":"so that all HTTP requests from openstacksdk and keystoneauth1 are routed"}],"source_content_type":"text/x-rst","patch_set":14,"id":"d3a777cf_323f19b7","line":348,"updated":"2026-08-03 14:11:44.000000000","message":"The testing.rst documentation added by this patch describes the Nova and Placement emulators as \u0027lightweight Flask apps\u0027 and \u0027standalone Flask servers\u0027, but the emulator code uses Pecan throughout (pecan.make_app, pecan.rest.RestController, pecan.expose, etc.). This could confuse contributors who...\n\n**Severity**: SUGGESTION | **Confidence**: 0.9\n\n**Benefit**: Contributors reading the documentation will expect Flask-based code and may waste time looking for Flask patterns, blueprints, or configuration when the emulators are actually Pecan-based.\n\n**Recommendation**:\nReplace \u0027Flask apps\u0027 with \u0027Pecan apps\u0027 on line 348 and \u0027standalone Flask servers\u0027 with \u0027standalone Pecan/Werkzeug servers\u0027 on line 799. The standalone server uses werkzeug.serving.run_simple to serve the Pecan WSGI app.","commit_id":"2bc5822ef7897e2ae19a70746f03018d1dede3a2"}],"watcher/tests/fixtures/nova_api_emulator.py":[{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"4c6c0584cc44385652785ef34c88759b3e6a10c2","unresolved":false,"context_lines":[{"line_number":342,"context_line":""},{"line_number":343,"context_line":"    # ── Helper methods ───────────────────────────────────────────"},{"line_number":344,"context_line":""},{"line_number":345,"context_line":"    def _get_servers_on_host(self, hostname):"},{"line_number":346,"context_line":"        return [s for s in self.servers.values()"},{"line_number":347,"context_line":"                if s[\u0027OS-EXT-SRV-ATTR:host\u0027] \u003d\u003d hostname]"},{"line_number":348,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"33346ed9_dad971e2","line":345,"updated":"2026-06-15 13:25:05.000000000","message":"pep8: N321: jsonutils.load must be used instead of json.load","commit_id":"6b5c909ef91652a8045f90697f079e534f132a38"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"5ee84f497dc0ca7a979b553ec30ba9a7bbcb9683","unresolved":true,"context_lines":[{"line_number":65,"context_line":"    return str(uuid.uuid5(uuid.NAMESPACE_DNS, seed))"},{"line_number":66,"context_line":""},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"class NovaAPIEmulator:"},{"line_number":69,"context_line":"    \"\"\"In-memory Nova API emulator."},{"line_number":70,"context_line":""},{"line_number":71,"context_line":"    Manages cluster state (servers, hypervisors, services, flavors,"}],"source_content_type":"text/x-python","patch_set":4,"id":"9ca36a69_7811693f","line":68,"range":{"start_line":68,"start_character":6,"end_line":68,"end_character":21},"updated":"2026-07-02 19:12:01.000000000","message":"Looks good, the endpoints and standalone app seems to be what I was expecting. I am just conflicted with the adoption of the topology as python dicts intead of python object, or even reuse the dataclass that we already have in the code. I understand that our dataclass don\u0027t represent 1:1 from nova api, but still we could make it compatible i think. I see that these topologies as dict will be hard to update/maintain in a long term, as also hard to build bigger topologies. Take as an example the toplogies xml that we have already in the code, where some of them are missing new attributes while others were updated.\nI would like to know the pros that you see on following this approach.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"27ecc79e836225e44e80e42b6d66d111bcf9707a","unresolved":true,"context_lines":[{"line_number":65,"context_line":"    return str(uuid.uuid5(uuid.NAMESPACE_DNS, seed))"},{"line_number":66,"context_line":""},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"class NovaAPIEmulator:"},{"line_number":69,"context_line":"    \"\"\"In-memory Nova API emulator."},{"line_number":70,"context_line":""},{"line_number":71,"context_line":"    Manages cluster state (servers, hypervisors, services, flavors,"}],"source_content_type":"text/x-python","patch_set":4,"id":"556fc1d2_b7d7d203","line":68,"range":{"start_line":68,"start_character":6,"end_line":68,"end_character":21},"in_reply_to":"2ee4b4a2_a7ca8274","updated":"2026-07-31 08:43:09.000000000","message":"Sorry, I don\u0027t follow what you mean in this comment. You mean we should declare the topologies in te fixture in the tests? I don\u0027t see the point of that.\n\nYou also pointed to https://review.opendev.org/c/openstack/nova/+/980641/5/tools/startup-perf/topologies/multi-service-ironic.yaml which is a topology description in yaml format which is exactly what you were claiming against.\n\nAlso, i think we agreed not to mock clients other than the keystone, and mock the actual external services instead.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"8aa64e6f3542978ca8e25245670360ac0e648f30","unresolved":true,"context_lines":[{"line_number":65,"context_line":"    return str(uuid.uuid5(uuid.NAMESPACE_DNS, seed))"},{"line_number":66,"context_line":""},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"class NovaAPIEmulator:"},{"line_number":69,"context_line":"    \"\"\"In-memory Nova API emulator."},{"line_number":70,"context_line":""},{"line_number":71,"context_line":"    Manages cluster state (servers, hypervisors, services, flavors,"}],"source_content_type":"text/x-python","patch_set":4,"id":"c1598582_1bd927f4","line":68,"range":{"start_line":68,"start_character":6,"end_line":68,"end_character":21},"in_reply_to":"4b91c72f_74fba3f1","updated":"2026-07-31 10:58:12.000000000","message":"We have two cases:\n\n1. Define topologies in-code for tests in code. For that, i\u0027m proposing to use the new helpers to build the topology. imo it provides flexibility to compose complex topologies, easy to review even for big topologies and flexible. I may also add 100 compute nodes with just one `add_computes` call. I don\u0027t see the need of a declarative DSL on top of that. Is that fine for you?\n\n2. For loading from xml (or json). Intended to be used when running in standalone mode. One of the requirements for me is to be able to use the XML representation of the model as shown in the logs of decision-engine so that we can  directly simulate real scenarios from users logs, so we need to keep that functionality in the emulator.\n\nWe can create a tool to build xml models from a declarative definition, but at this point i don\u0027t think that should be a blocker for this patch and may be done in separate patch.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":true,"context_lines":[{"line_number":65,"context_line":"    return str(uuid.uuid5(uuid.NAMESPACE_DNS, seed))"},{"line_number":66,"context_line":""},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"class NovaAPIEmulator:"},{"line_number":69,"context_line":"    \"\"\"In-memory Nova API emulator."},{"line_number":70,"context_line":""},{"line_number":71,"context_line":"    Manages cluster state (servers, hypervisors, services, flavors,"}],"source_content_type":"text/x-python","patch_set":4,"id":"c6e3fa92_cffb81e0","line":68,"range":{"start_line":68,"start_character":6,"end_line":68,"end_character":21},"in_reply_to":"529d065c_974d3e6e","updated":"2026-07-30 15:51:25.000000000","message":"I\u0027ve created some new dataclasses specific to the functional tests.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"fa532f3be31e982ead9effff4c6bcbdd2132000e","unresolved":true,"context_lines":[{"line_number":65,"context_line":"    return str(uuid.uuid5(uuid.NAMESPACE_DNS, seed))"},{"line_number":66,"context_line":""},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"class NovaAPIEmulator:"},{"line_number":69,"context_line":"    \"\"\"In-memory Nova API emulator."},{"line_number":70,"context_line":""},{"line_number":71,"context_line":"    Manages cluster state (servers, hypervisors, services, flavors,"}],"source_content_type":"text/x-python","patch_set":4,"id":"2ee4b4a2_a7ca8274","line":68,"range":{"start_line":68,"start_character":6,"end_line":68,"end_character":21},"in_reply_to":"529d065c_974d3e6e","updated":"2026-07-30 18:01:34.000000000","message":"while https://gist.github.com/SeanMooney/43afa55282d2286a312eae7f3c7709e2#commit-23-external-service-fixtures-nova does have example like that i was personally expecting to rewrite that when impleeiting it.\neither to something like this https://review.opendev.org/c/openstack/nova/+/980641/5/tools/startup-perf/topologies/multi-service-ironic.yaml\n\nor for it to be in teh fixture teh way nvoa does it \nhttps://github.com/openstack/nova/blob/master/nova/tests/fixtures/neutron.py#L63\nnot in the test class.\n\nnova on ly uses dicts like that when its modling raw api respocnes that will then get converted into the real classes as needed.\n\nthose fixture are mocking the neutronclient functions\n\nhttps://github.com/openstack/nova/blob/master/nova/tests/fixtures/neutron.py#L850-L853\n\nwhich return dicts.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"6a46910ebbd43eb1c087eb31880234e67697407e","unresolved":true,"context_lines":[{"line_number":65,"context_line":"    return str(uuid.uuid5(uuid.NAMESPACE_DNS, seed))"},{"line_number":66,"context_line":""},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"class NovaAPIEmulator:"},{"line_number":69,"context_line":"    \"\"\"In-memory Nova API emulator."},{"line_number":70,"context_line":""},{"line_number":71,"context_line":"    Manages cluster state (servers, hypervisors, services, flavors,"}],"source_content_type":"text/x-python","patch_set":4,"id":"4b91c72f_74fba3f1","line":68,"range":{"start_line":68,"start_character":6,"end_line":68,"end_character":21},"in_reply_to":"556fc1d2_b7d7d203","updated":"2026-07-31 10:36:52.000000000","message":"https://review.opendev.org/c/openstack/nova/+/980641/5/tools/startup-perf/topologies/multi-service-ironic.yaml descibe an abstract toplogy of sike an count\n\nnot the raw data which si very diffent\n\n```\n - host: ironic-compute-0\n    nodes: 100\n    resource_class: CUSTOM_BAREMETAL_GPU\n    traits:\n      - CUSTOM_HW_GPU\n    vcpus: 4\n    memory_mb: 32768\n    disk_gb: 0\n```\n\nis  a recibpy for how to build the ojbect not a object by it slef\n\nits asking for a comptue sevice that manage 100 ironic nodes\neach of which uses the CUSTOM_BAREMETAL_GPU resocue class\nand has the CUSTOM_HW_GPU triat.\n\nenach node havign 4 cpus and 32 GB of ram.\n\nthat result in 1 compute service reoced and 100 ironic node in its api.\nwiht 100 corresponding placemetn RPs and 100 compute node record in the nova db when evernything is contructed.\n\nthat is very diffent then listing each resouce directly 100 times in a file and just parting the file to create the topogy directly form data\nwhich is what our model xmls currently do\nhttps://github.com/openstack/watcher/blob/master/watcher/tests/unit/decision_engine/model/data/scenario_1.xml\n\ninstead you  ues the file to specify how to constuct the toplogy and loop over it in pyton generating the classes and data based on the sepcification.\n\nthe current test xml we have only work for very small examples\nthey are generally hard to read and maintian.\n\nwith the simulator i effectivly defeind a mini dsl or macro to geenrate the large data set from determisitcly.\n\nthat allows us in tests to ether use the compatct epression vai the data driven approch or to trigially build any topogy we want by invoking the funcitons directly in a test.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"e1984db3b3212adbf55c0a2eed0a04690df5d036","unresolved":true,"context_lines":[{"line_number":65,"context_line":"    return str(uuid.uuid5(uuid.NAMESPACE_DNS, seed))"},{"line_number":66,"context_line":""},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"class NovaAPIEmulator:"},{"line_number":69,"context_line":"    \"\"\"In-memory Nova API emulator."},{"line_number":70,"context_line":""},{"line_number":71,"context_line":"    Manages cluster state (servers, hypervisors, services, flavors,"}],"source_content_type":"text/x-python","patch_set":4,"id":"529d065c_974d3e6e","line":68,"range":{"start_line":68,"start_character":6,"end_line":68,"end_character":21},"in_reply_to":"9ca36a69_7811693f","updated":"2026-07-03 19:30:13.000000000","message":"I\u0027d prefer not to use in functional tests the same objects we are using in the clients helpers. I see functional tests as a testing agent external to the watcher implementation itself, and I\u0027d like to minimize the imports of watcher code in the functional tests as much as possible, although we may create new objects, i\u0027m not sure it\u0027s worthy. I took the idea of using plane dicts from https://gist.github.com/SeanMooney/43afa55282d2286a312eae7f3c7709e2 but I\u0027d like to hear the opinion from others.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"632efb10eb035060d33d91afd7c46d4ee381480e","unresolved":true,"context_lines":[{"line_number":65,"context_line":"    return str(uuid.uuid5(uuid.NAMESPACE_DNS, seed))"},{"line_number":66,"context_line":""},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"class NovaAPIEmulator:"},{"line_number":69,"context_line":"    \"\"\"In-memory Nova API emulator."},{"line_number":70,"context_line":""},{"line_number":71,"context_line":"    Manages cluster state (servers, hypervisors, services, flavors,"}],"source_content_type":"text/x-python","patch_set":4,"id":"1309e225_e45d1908","line":68,"range":{"start_line":68,"start_character":6,"end_line":68,"end_character":21},"in_reply_to":"c1598582_1bd927f4","updated":"2026-07-31 12:51:15.000000000","message":"yes im still reviewing it but im more or less ok with v12 and the builder funcitons as the pimrary way to build the topolices in our test and addtionally supproting xml/json loading in standalone mode for the \"repoduce form logs\" usecase.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"30dc6bb20d643f21d6bef6731b596f8518d5c2cf","unresolved":false,"context_lines":[{"line_number":278,"context_line":"                hyp[\u0027vcpus_used\u0027] +\u003d vcpus"},{"line_number":279,"context_line":"                hyp[\u0027memory_used\u0027] +\u003d ram"},{"line_number":280,"context_line":"                hyp[\u0027memory_mb_used\u0027] +\u003d ram"},{"line_number":281,"context_line":"                hyp[\u0027local_gb_used\u0027] +\u003d disk"},{"line_number":282,"context_line":"                hyp[\u0027running_vms\u0027] +\u003d 1"},{"line_number":283,"context_line":"                hyp[\u0027servers\u0027].append("},{"line_number":284,"context_line":"                    {\u0027uuid\u0027: inst_uuid, \u0027name\u0027: server[\u0027name\u0027]}"}],"source_content_type":"text/x-python","patch_set":4,"id":"731ba498_8d185bc9","line":281,"updated":"2026-06-25 09:19:21.000000000","message":"Nova emulator local_gb_used (line 281) accumulates only root disk, while the Placement emulator DISK_GB usage (line 143) includes root + ephemeral + ceil(swap/1024). This creates an inconsistency between the two emulators for disk accounting on hypervisors.\n\n**Severity**: SUGGESTION | **Confidence**: 0.9\n\n**Benefit**: Emulator fidelity: hypervisor local_gb_used would match Placement DISK_GB usage, preventing confusion when debugging capacity-related test failures and ensuring the emulator matches real Nova behavior.\n\n**Recommendation**:\nCompute full disk (root + ephemeral + ceil(swap/1024)) when updating local_gb_used in load_topology, matching the Placement emulator disk_gb calculation. This keeps the two emulators consistent.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"30dc6bb20d643f21d6bef6731b596f8518d5c2cf","unresolved":false,"context_lines":[{"line_number":379,"context_line":"                return svc"},{"line_number":380,"context_line":"        return None"},{"line_number":381,"context_line":""},{"line_number":382,"context_line":"    def _move_server(self, server_id, dest_hostname):"},{"line_number":383,"context_line":"        \"\"\"Move a server to a new host in the emulator state.\"\"\""},{"line_number":384,"context_line":"        server \u003d self.servers.get(server_id)"},{"line_number":385,"context_line":"        if not server:"}],"source_content_type":"text/x-python","patch_set":4,"id":"66fc4e16_b44741e9","line":382,"updated":"2026-06-25 09:19:21.000000000","message":"_move_server updates Nova emulator state but does not update Placement allocations. After migration the Placement emulator still reports allocations on the source host, so future tests checking Placement usages post-migration would get incorrect data.\n\n**Severity**: SUGGESTION | **Confidence**: 0.8\n\n**Benefit**: Placement allocations stay consistent with Nova state after migrations, enabling future tests that validate capacity or placement behavior post-migration without surprising failures.\n\n**Recommendation**:\nAfter moving the server in _move_server, also move the consumer allocation in the Placement emulator. At minimum, add a comment documenting that Placement allocations are not updated on migration.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"30dc6bb20d643f21d6bef6731b596f8518d5c2cf","unresolved":false,"context_lines":[{"line_number":640,"context_line":"                if item[\u0027id\u0027] \u003d\u003d marker:"},{"line_number":641,"context_line":"                    found \u003d True"},{"line_number":642,"context_line":"            items \u003d filtered"},{"line_number":643,"context_line":"        if limit and limit \u003e 0 and limit \u003c len(items):"},{"line_number":644,"context_line":"            return items[:limit], True"},{"line_number":645,"context_line":"        return items, False"},{"line_number":646,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"44230107_8667ddaa","line":643,"updated":"2026-06-25 09:19:21.000000000","message":"_paginate returns has_more\u003dFalse when limit equals the item count (has_more\u003dTrue only when limit \u003c len). Real Nova returns a next link when the page is full. While functionally fine for the collector, this differs from real API pagination behavior.\n\n**Severity**: SUGGESTION | **Confidence**: 0.8\n\n**Benefit**: Pagination emulation matches real Nova more closely, preventing subtle test-environment differences if a test relies on pagination semantics.\n\n**Recommendation**:\nChange the condition to limit \u003c\u003d len(items) to return has_more\u003dTrue when the page is exactly full, matching Nova behavior of providing a next-page link when the limit is reached.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"}],"watcher/tests/fixtures/placement_api_emulator.py":[{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"4c6c0584cc44385652785ef34c88759b3e6a10c2","unresolved":false,"context_lines":[{"line_number":204,"context_line":""},{"line_number":205,"context_line":"    def _register_routes(self):"},{"line_number":206,"context_line":"        app \u003d self.app"},{"line_number":207,"context_line":""},{"line_number":208,"context_line":"        @app.route(\u0027/resource_providers\u0027, methods\u003d[\u0027GET\u0027])"},{"line_number":209,"context_line":"        def list_resource_providers():"},{"line_number":210,"context_line":"            name \u003d request.args.get(\u0027name\u0027)"}],"source_content_type":"text/x-python","patch_set":1,"id":"2e8a0fb2_15fd657e","line":207,"updated":"2026-06-15 13:25:05.000000000","message":"pep8: N321: jsonutils.load must be used instead of json.load","commit_id":"6b5c909ef91652a8045f90697f079e534f132a38"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"5ee84f497dc0ca7a979b553ec30ba9a7bbcb9683","unresolved":true,"context_lines":[{"line_number":38,"context_line":"from oslo_serialization import jsonutils"},{"line_number":39,"context_line":""},{"line_number":40,"context_line":""},{"line_number":41,"context_line":"class PlacementAPIEmulator:"},{"line_number":42,"context_line":"    \"\"\"In-memory Placement API emulator."},{"line_number":43,"context_line":""},{"line_number":44,"context_line":"    Provides resource provider inventories, traits, usages, and"}],"source_content_type":"text/x-python","patch_set":4,"id":"bc00a538_0f3af6e5","line":41,"range":{"start_line":41,"start_character":6,"end_line":41,"end_character":26},"updated":"2026-07-02 19:12:01.000000000","message":"It seems that emulators follow an implementation pattern that we could implement in a base class, to avoid duplication and make it easier to maintain/extend/understand. WDYT?","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"e1984db3b3212adbf55c0a2eed0a04690df5d036","unresolved":true,"context_lines":[{"line_number":38,"context_line":"from oslo_serialization import jsonutils"},{"line_number":39,"context_line":""},{"line_number":40,"context_line":""},{"line_number":41,"context_line":"class PlacementAPIEmulator:"},{"line_number":42,"context_line":"    \"\"\"In-memory Placement API emulator."},{"line_number":43,"context_line":""},{"line_number":44,"context_line":"    Provides resource provider inventories, traits, usages, and"}],"source_content_type":"text/x-python","patch_set":4,"id":"d95daabb_76915eab","line":41,"range":{"start_line":41,"start_character":6,"end_line":41,"end_character":26},"in_reply_to":"bc00a538_0f3af6e5","updated":"2026-07-03 19:30:13.000000000","message":"It make sense. I can probably share part of the logic between","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":false,"context_lines":[{"line_number":38,"context_line":"from oslo_serialization import jsonutils"},{"line_number":39,"context_line":""},{"line_number":40,"context_line":""},{"line_number":41,"context_line":"class PlacementAPIEmulator:"},{"line_number":42,"context_line":"    \"\"\"In-memory Placement API emulator."},{"line_number":43,"context_line":""},{"line_number":44,"context_line":"    Provides resource provider inventories, traits, usages, and"}],"source_content_type":"text/x-python","patch_set":4,"id":"1cef3647_f8b53103","line":41,"range":{"start_line":41,"start_character":6,"end_line":41,"end_character":26},"in_reply_to":"d95daabb_76915eab","updated":"2026-07-30 15:51:25.000000000","message":"Done","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"}],"watcher/tests/functional/base.py":[{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"1032c5d5ce76724b706cce895700dce39823e3f1","unresolved":true,"context_lines":[{"line_number":306,"context_line":"        \"\"\"Load or replace cluster topology for the current test."},{"line_number":307,"context_line":""},{"line_number":308,"context_line":"        Requires USE_NOVA_EMULATOR \u003d True (or COMPUTE_NODES set) on"},{"line_number":309,"context_line":"        the test class so the Nova/Placement emulator fixture exists."},{"line_number":310,"context_line":"        \"\"\""},{"line_number":311,"context_line":"        self.env.nova_fixture.reload_topology("},{"line_number":312,"context_line":"            compute_nodes\u003dcompute_nodes,"}],"source_content_type":"text/x-python","patch_set":5,"id":"21464ad6_edb54b7d","line":309,"updated":"2026-07-08 10:15:25.000000000","message":"I think we shoul enforce this with a check here and throw an error if USE_NOVA_EMULATOR is false, to make it easier to debug if someone tries to use the method but forgets to set the variable","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":true,"context_lines":[{"line_number":306,"context_line":"        \"\"\"Load or replace cluster topology for the current test."},{"line_number":307,"context_line":""},{"line_number":308,"context_line":"        Requires USE_NOVA_EMULATOR \u003d True (or COMPUTE_NODES set) on"},{"line_number":309,"context_line":"        the test class so the Nova/Placement emulator fixture exists."},{"line_number":310,"context_line":"        \"\"\""},{"line_number":311,"context_line":"        self.env.nova_fixture.reload_topology("},{"line_number":312,"context_line":"            compute_nodes\u003dcompute_nodes,"}],"source_content_type":"text/x-python","patch_set":5,"id":"5a47c34e_a8b86aad","line":309,"in_reply_to":"21464ad6_edb54b7d","updated":"2026-07-30 15:51:25.000000000","message":"actually, i will make it to start the emulater if a compute topology is added.","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"780e43980195c48c2abd2e561816a2fbc3cef778","unresolved":false,"context_lines":[{"line_number":306,"context_line":"        \"\"\"Load or replace cluster topology for the current test."},{"line_number":307,"context_line":""},{"line_number":308,"context_line":"        Requires USE_NOVA_EMULATOR \u003d True (or COMPUTE_NODES set) on"},{"line_number":309,"context_line":"        the test class so the Nova/Placement emulator fixture exists."},{"line_number":310,"context_line":"        \"\"\""},{"line_number":311,"context_line":"        self.env.nova_fixture.reload_topology("},{"line_number":312,"context_line":"            compute_nodes\u003dcompute_nodes,"}],"source_content_type":"text/x-python","patch_set":5,"id":"b19f7edd_7d5da18b","line":309,"in_reply_to":"5a47c34e_a8b86aad","updated":"2026-08-03 12:27:52.000000000","message":"Done","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"}],"watcher/tests/functional/gabbi_fixture.py":[{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"1032c5d5ce76724b706cce895700dce39823e3f1","unresolved":true,"context_lines":[{"line_number":62,"context_line":"        self.env.cleanUp()"},{"line_number":63,"context_line":""},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"class WatcherWithTopologyFixture(gabbi_fixture.GabbiFixture):"},{"line_number":66,"context_line":"    \"\"\"Watcher environment with emulated Nova/Placement APIs."},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"    Provides a 3-node cluster topology for gabbi tests that exercise"}],"source_content_type":"text/x-python","patch_set":5,"id":"24be0b26_80068f57","line":65,"updated":"2026-07-08 10:15:25.000000000","message":"```suggestion\nclass WatcherGabbyWithTopologyFixture(gabbi_fixture.GabbiFixture):\n```\nI think noting that the Fixture is strictly related to Gabbi tests in its name would be helpful","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":false,"context_lines":[{"line_number":62,"context_line":"        self.env.cleanUp()"},{"line_number":63,"context_line":""},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"class WatcherWithTopologyFixture(gabbi_fixture.GabbiFixture):"},{"line_number":66,"context_line":"    \"\"\"Watcher environment with emulated Nova/Placement APIs."},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"    Provides a 3-node cluster topology for gabbi tests that exercise"}],"source_content_type":"text/x-python","patch_set":5,"id":"263eeb7c_e46b6fff","line":65,"in_reply_to":"24be0b26_80068f57","updated":"2026-07-30 15:51:25.000000000","message":"Done","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"17cf3fa93dc22ab02a80b2636b0a19ce5f074f92","unresolved":true,"context_lines":[{"line_number":84,"context_line":"          - WatcherWithTopologyFixture"},{"line_number":85,"context_line":"    \"\"\""},{"line_number":86,"context_line":""},{"line_number":87,"context_line":"    COMPUTE_NODES \u003d ["},{"line_number":88,"context_line":"        {"},{"line_number":89,"context_line":"            \u0027uuid\u0027: \u002773b09e16-35b7-4922-804e-e8f5d9b740fc\u0027,"},{"line_number":90,"context_line":"            \u0027hostname\u0027: \u0027compute-1\u0027,"},{"line_number":91,"context_line":"            \u0027vcpus\u0027: 16,"},{"line_number":92,"context_line":"            \u0027memory\u0027: 32768,"},{"line_number":93,"context_line":"            \u0027disk\u0027: 500,"},{"line_number":94,"context_line":"            \u0027state\u0027: \u0027up\u0027,"},{"line_number":95,"context_line":"            \u0027status\u0027: \u0027enabled\u0027,"},{"line_number":96,"context_line":"        },"},{"line_number":97,"context_line":"        {"},{"line_number":98,"context_line":"            \u0027uuid\u0027: \u0027cef19ce0-0ca2-11e6-a747-00012c99e920\u0027,"},{"line_number":99,"context_line":"            \u0027hostname\u0027: \u0027compute-2\u0027,"},{"line_number":100,"context_line":"            \u0027vcpus\u0027: 16,"},{"line_number":101,"context_line":"            \u0027memory\u0027: 32768,"},{"line_number":102,"context_line":"            \u0027disk\u0027: 500,"},{"line_number":103,"context_line":"            \u0027state\u0027: \u0027up\u0027,"},{"line_number":104,"context_line":"            \u0027status\u0027: \u0027enabled\u0027,"},{"line_number":105,"context_line":"        },"},{"line_number":106,"context_line":"        {"},{"line_number":107,"context_line":"            \u0027uuid\u0027: \u0027a5e1f723-1a4b-4e3c-9a0b-2d3f4e5a6b7c\u0027,"},{"line_number":108,"context_line":"            \u0027hostname\u0027: \u0027compute-3\u0027,"},{"line_number":109,"context_line":"            \u0027vcpus\u0027: 16,"},{"line_number":110,"context_line":"            \u0027memory\u0027: 32768,"},{"line_number":111,"context_line":"            \u0027disk\u0027: 500,"},{"line_number":112,"context_line":"            \u0027state\u0027: \u0027up\u0027,"},{"line_number":113,"context_line":"            \u0027status\u0027: \u0027enabled\u0027,"},{"line_number":114,"context_line":"        },"},{"line_number":115,"context_line":"    ]"},{"line_number":116,"context_line":""},{"line_number":117,"context_line":"    INSTANCES \u003d ["},{"line_number":118,"context_line":"        {"},{"line_number":119,"context_line":"            \u0027uuid\u0027: \u002711111111-1111-1111-1111-111111111111\u0027,"}],"source_content_type":"text/x-python","patch_set":5,"id":"07238ea0_0aabacda","line":116,"range":{"start_line":87,"start_character":3,"end_line":116,"end_character":1},"updated":"2026-07-03 16:09:35.000000000","message":"i dont like that this is just freeform data isntaed of actully using object or types\nthis makes it very easy to typeo \n\nif we are takign that approch i think you shoudl at least provide jsonshemea for this","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":false,"context_lines":[{"line_number":84,"context_line":"          - WatcherWithTopologyFixture"},{"line_number":85,"context_line":"    \"\"\""},{"line_number":86,"context_line":""},{"line_number":87,"context_line":"    COMPUTE_NODES \u003d ["},{"line_number":88,"context_line":"        {"},{"line_number":89,"context_line":"            \u0027uuid\u0027: \u002773b09e16-35b7-4922-804e-e8f5d9b740fc\u0027,"},{"line_number":90,"context_line":"            \u0027hostname\u0027: \u0027compute-1\u0027,"},{"line_number":91,"context_line":"            \u0027vcpus\u0027: 16,"},{"line_number":92,"context_line":"            \u0027memory\u0027: 32768,"},{"line_number":93,"context_line":"            \u0027disk\u0027: 500,"},{"line_number":94,"context_line":"            \u0027state\u0027: \u0027up\u0027,"},{"line_number":95,"context_line":"            \u0027status\u0027: \u0027enabled\u0027,"},{"line_number":96,"context_line":"        },"},{"line_number":97,"context_line":"        {"},{"line_number":98,"context_line":"            \u0027uuid\u0027: \u0027cef19ce0-0ca2-11e6-a747-00012c99e920\u0027,"},{"line_number":99,"context_line":"            \u0027hostname\u0027: \u0027compute-2\u0027,"},{"line_number":100,"context_line":"            \u0027vcpus\u0027: 16,"},{"line_number":101,"context_line":"            \u0027memory\u0027: 32768,"},{"line_number":102,"context_line":"            \u0027disk\u0027: 500,"},{"line_number":103,"context_line":"            \u0027state\u0027: \u0027up\u0027,"},{"line_number":104,"context_line":"            \u0027status\u0027: \u0027enabled\u0027,"},{"line_number":105,"context_line":"        },"},{"line_number":106,"context_line":"        {"},{"line_number":107,"context_line":"            \u0027uuid\u0027: \u0027a5e1f723-1a4b-4e3c-9a0b-2d3f4e5a6b7c\u0027,"},{"line_number":108,"context_line":"            \u0027hostname\u0027: \u0027compute-3\u0027,"},{"line_number":109,"context_line":"            \u0027vcpus\u0027: 16,"},{"line_number":110,"context_line":"            \u0027memory\u0027: 32768,"},{"line_number":111,"context_line":"            \u0027disk\u0027: 500,"},{"line_number":112,"context_line":"            \u0027state\u0027: \u0027up\u0027,"},{"line_number":113,"context_line":"            \u0027status\u0027: \u0027enabled\u0027,"},{"line_number":114,"context_line":"        },"},{"line_number":115,"context_line":"    ]"},{"line_number":116,"context_line":""},{"line_number":117,"context_line":"    INSTANCES \u003d ["},{"line_number":118,"context_line":"        {"},{"line_number":119,"context_line":"            \u0027uuid\u0027: \u002711111111-1111-1111-1111-111111111111\u0027,"}],"source_content_type":"text/x-python","patch_set":5,"id":"35458ccc_e0a54096","line":116,"range":{"start_line":87,"start_character":3,"end_line":116,"end_character":1},"in_reply_to":"07238ea0_0aabacda","updated":"2026-07-30 15:51:25.000000000","message":"Done","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"1b801a94a13e27f14d5d093d917217880adc1859","unresolved":false,"context_lines":[{"line_number":152,"context_line":"    def start_fixture(self):"},{"line_number":153,"context_line":"        from oslo_config import cfg"},{"line_number":154,"context_line":""},{"line_number":155,"context_line":"        cfg.CONF.set_override(\u0027migration_max_retries\u0027, 5, group\u003d\u0027nova\u0027)"},{"line_number":156,"context_line":"        cfg.CONF.set_override(\u0027migration_interval\u0027, 0.1, group\u003d\u0027nova\u0027)"},{"line_number":157,"context_line":""},{"line_number":158,"context_line":"        self.env \u003d WatcherEnvironment("}],"source_content_type":"text/x-python","patch_set":5,"id":"f75ab6b5_9956881f","line":155,"updated":"2026-07-02 19:37:54.000000000","message":"WatcherWithTopologyFixture.start_fixture sets migration_max_retries and migration_interval via cfg.CONF.set_override directly without clearing them in stop_fixture, unlike the Python test path which uses self.flags() with addCleanup. If a gabbi process executes multiple fixtures these overrides leak across test groups.\n\n**Severity**: WARNING | **Confidence**: 0.7\n\n**Impact**: Configuration overrides may leak between gabbi test groups if the process is reused, leading to non-deterministic timing behavior.\n\n**Suggestion**:\nIn stop_fixture, clear the two overrides (cfg.CONF.clear_override(\u0027migration_max_retries\u0027, group\u003d\u0027nova\u0027) and likewise for migration_interval). Gabbi fixtures are per-process so risk is limited, but explicit cleanup matches the pattern used elsewhere.","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"632efb10eb035060d33d91afd7c46d4ee381480e","unresolved":false,"context_lines":[{"line_number":104,"context_line":"    compute-3: 0 instances"},{"line_number":105,"context_line":"    \"\"\""},{"line_number":106,"context_line":""},{"line_number":107,"context_line":"    COMPUTE_TOPOLOGY \u003d ("},{"line_number":108,"context_line":"        topology.ComputeTopology()"},{"line_number":109,"context_line":"        .add_computes(count\u003d3)"},{"line_number":110,"context_line":"        .add_instances(computes\u003d[\u0027compute-1\u0027], count\u003d2, vcpus\u003d2)"},{"line_number":111,"context_line":"        .add_instances(computes\u003d[\u0027compute-2\u0027], count\u003d1, vcpus\u003d2)"},{"line_number":112,"context_line":"    )"}],"source_content_type":"text/x-python","patch_set":12,"id":"355ba4ea_501ec843","line":112,"range":{"start_line":107,"start_character":0,"end_line":112,"end_character":5},"updated":"2026-07-31 12:51:15.000000000","message":"this is a lot more concise thanks","commit_id":"90a0228a6ee437da5037820eca07bb6ee9b1c415"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"262c2b12c26eb2da63401e81e9410bf6757e1ee4","unresolved":true,"context_lines":[{"line_number":96,"context_line":"        self.env.cleanUp()"},{"line_number":97,"context_line":""},{"line_number":98,"context_line":""},{"line_number":99,"context_line":"class WatcherGabbiWithTopologyFixture(_GabbiTopologyFixtureBase):"},{"line_number":100,"context_line":"    \"\"\"3-node cluster for host_maintenance gabbi tests."},{"line_number":101,"context_line":""},{"line_number":102,"context_line":"    compute-1: 2 active instances (vm-1, vm-2)"}],"source_content_type":"text/x-python","patch_set":14,"id":"22565d73_dd4c2abb","line":99,"range":{"start_line":99,"start_character":6,"end_line":99,"end_character":37},"updated":"2026-08-03 15:30:51.000000000","message":"Looks nice! The class name don\u0027t represent too much the topology. I guess that we will build more classes and we could name them differently. It is fine by now.","commit_id":"2bc5822ef7897e2ae19a70746f03018d1dede3a2"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"1f671d447c2aa7e4344fdbcd83568abc92a9c96d","unresolved":false,"context_lines":[{"line_number":81,"context_line":"    def start_fixture(self):"},{"line_number":82,"context_line":"        from oslo_config import cfg"},{"line_number":83,"context_line":""},{"line_number":84,"context_line":"        cfg.CONF.set_override(\u0027migration_max_retries\u0027, 5, group\u003d\u0027nova\u0027)"},{"line_number":85,"context_line":"        cfg.CONF.set_override(\u0027migration_interval\u0027, 0.1, group\u003d\u0027nova\u0027)"},{"line_number":86,"context_line":""},{"line_number":87,"context_line":"        self.env \u003d WatcherEnvironment("}],"source_content_type":"text/x-python","patch_set":15,"id":"1dfc3da5_5b8bf827","line":84,"updated":"2026-08-03 14:46:51.000000000","message":"_GabbiTopologyFixtureBase.start_fixture sets migration_max_retries and migration_interval overrides on cfg.CONF but stop_fixture never calls clear_override, causing these to persist in global CONF after cleanup.\n\n**Severity**: SUGGESTION | **Confidence**: 0.8\n\n**Benefit**: If multiple gabbi YAML files run in the same process, subsequent tests that do not explicitly set migration_max_retries or migration_interval will inherit the leaked values (5 and 0.1) instead of defaults. This could cause subtle test pollution or unexpected timing behavior.\n\n**Recommendation**:\nEither move the set_override calls into WatcherEnvironment.setUp() so they are cleaned up automatically via addCleanup, or add explicit clear_override calls in stop_fixture: def stop_fixture(self): self.env.cleanUp() cfg.CONF.clear_override(\u0027migration_max_retries\u0027, group\u003d\u0027nova\u0027) cfg.CONF.clear_override(\u0027migration_interval\u0027, group\u003d\u0027nova\u0027)","commit_id":"1d8771252415cd84986e07b2b6447f1a7eeb3c62"}],"watcher/tests/functional/gabbits/host-maintenance.yaml":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"30dc6bb20d643f21d6bef6731b596f8518d5c2cf","unresolved":false,"context_lines":[{"line_number":63,"context_line":"    status: 200"},{"line_number":64,"context_line":"    response_json_paths:"},{"line_number":65,"context_line":"      $.actions.`len`: 3"},{"line_number":66,"context_line":"      $.actions[0].action_type: change_nova_service_state"},{"line_number":67,"context_line":"      $.actions[1].action_type: migrate"},{"line_number":68,"context_line":"      $.actions[2].action_type: migrate"},{"line_number":69,"context_line":""}],"source_content_type":"text/x-yaml","patch_set":4,"id":"b210cfd3_850168c4","line":66,"updated":"2026-06-25 09:19:21.000000000","message":"Gabbi test asserts positional ordering of actions (actions[0]\u003ddisable, actions[1]\u003dmigrate, actions[2]\u003dmigrate). The Watcher actions API does not guarantee a stable sort order; if the planner or DB returns actions in a different order the test will fail spuriously.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: Test may fail intermittently if action ordering changes in the planner or DB query, creating a flaky gate that is hard to diagnose.\n\n**Suggestion**:\nSort actions by action_type before asserting positionally, or use gabbi response_json_paths with a filter/match-any construct to verify the presence of each action type without relying on array index.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"}],"watcher/tests/functional/test_host_maintenance.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"e64f4e412f9ef1139d9e2cced1a5f7ede0c72686","unresolved":false,"context_lines":[{"line_number":285,"context_line":"        )"},{"line_number":286,"context_line":""},{"line_number":287,"context_line":"        for inst in MIXED_STATE_TOPOLOGY.instances:"},{"line_number":288,"context_line":"            if inst.state \u003d\u003d \u0027started\u0027:"},{"line_number":289,"context_line":"                self._assert_action("},{"line_number":290,"context_line":"                    actions,"},{"line_number":291,"context_line":"                    \u0027migrate\u0027,"}],"source_content_type":"text/x-python","patch_set":12,"id":"0fc63dad_7ac5d096","line":288,"updated":"2026-07-31 11:35:24.000000000","message":"Multiple test methods in TestHostMaintenance iterate over MIXED_STATE_TOPOLOGY.instances and check `if inst.state \u003d\u003d \u0027started\u0027` to assert behavior for active VMs. However, the Instance dataclass default state is \u0027active\u0027 (topology.py line 70), and add_instances() creates instances with that defau...\n\n**Severity**: HIGH | **Confidence**: 1.0\n\n**Risk**: If the host_maintenance strategy incorrectly generates cold migrations instead of live migrations for active instances (or stops them when it shouldn\u0027t), these tests would still pass because the per-instance assertions are never reached. This creates a false sense of coverage for the migration-ty...\n\n**Priority**: Before merge\n**Why This Matters**: If the host_maintenance strategy incorrectly generates cold migrations instead of live migrations for active instances (or stops them when it shouldn\u0027t), these tests would still pass because the per-instance assertions are never reached. This creates a false sense of coverage for the migration-ty...\n\n**Recommendation**:\nChange all three occurrences of `inst.state \u003d\u003d \u0027started\u0027` to `inst.state \u003d\u003d \u0027active\u0027` on lines 288, 363, and 400 of test_host_maintenance.py so the assertions for active instances are actually executed.","commit_id":"90a0228a6ee437da5037820eca07bb6ee9b1c415"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"a6e6a3ea2eced1cf9c05b1f04f60053c17ae5a30","unresolved":false,"context_lines":[{"line_number":285,"context_line":"        )"},{"line_number":286,"context_line":""},{"line_number":287,"context_line":"        for inst in MIXED_STATE_TOPOLOGY.instances:"},{"line_number":288,"context_line":"            if inst.state \u003d\u003d \u0027started\u0027:"},{"line_number":289,"context_line":"                self._assert_action("},{"line_number":290,"context_line":"                    actions,"},{"line_number":291,"context_line":"                    \u0027migrate\u0027,"}],"source_content_type":"text/x-python","patch_set":12,"id":"7b15a64d_940c0c76","line":288,"in_reply_to":"0fc63dad_7ac5d096","updated":"2026-07-31 12:07:38.000000000","message":"Thanks teim! you were right, fixed.","commit_id":"90a0228a6ee437da5037820eca07bb6ee9b1c415"}],"watcher/tests/functional/test_host_maintenance_scoped.py":[{"author":{"_account_id":22348,"name":"Zuul","username":"zuul","tags":["SERVICE_USER"]},"tag":"autogenerated:zuul:check","change_message_id":"4c6c0584cc44385652785ef34c88759b3e6a10c2","unresolved":false,"context_lines":[{"line_number":375,"context_line":"        )"},{"line_number":376,"context_line":""},{"line_number":377,"context_line":"    # ── Negative: maintenance_node is excluded ──────────────────"},{"line_number":378,"context_line":""},{"line_number":379,"context_line":"    def test_scope_maintenance_node_excluded(self):"},{"line_number":380,"context_line":"        \"\"\"maintenance_node in exclude list causes audit FAILED."},{"line_number":381,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"441e1a0a_de960a4c","line":378,"updated":"2026-06-15 13:25:05.000000000","message":"pep8: H101: Use TODO(NAME)","commit_id":"6b5c909ef91652a8045f90697f079e534f132a38"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"1b801a94a13e27f14d5d093d917217880adc1859","unresolved":false,"context_lines":[{"line_number":378,"context_line":"        # TODO(amoralej) uncomment when bug #2154805 is fixed"},{"line_number":379,"context_line":"        # self.assertEqual(2, len(migrate_actions))"},{"line_number":380,"context_line":"        # self.assertEqual(3, len(actions))"},{"line_number":381,"context_line":"        self.assertEqual(3, len(migrate_actions))"},{"line_number":382,"context_line":"        self.assertEqual(4, len(actions))"},{"line_number":383,"context_line":"        self._assert_action("},{"line_number":384,"context_line":"            actions,"}],"source_content_type":"text/x-python","patch_set":5,"id":"2e278314_183e0348","line":381,"updated":"2026-07-02 19:37:54.000000000","message":"test_scope_exclude_instance actively asserts the buggy behavior (vm-1 is migrated despite being excluded) at line 381-390, with only commented-out lines showing the correct expected behavior. This encodes the bug as the passing contract; a future reader may mistake the live assertion for desired behavior.\n\n**Severity**: SUGGESTION | **Confidence**: 0.7\n\n**Benefit**: Makes the test\u0027s intent unambiguous: it documents a known bug rather than asserting desired behavior.\n\n**Recommendation**:\nAdd a brief inline comment at line 381 stating \u0027This assertion documents bug #2154805 and will need updating when the bug is fixed\u0027 so the assertion is not mistaken for desired behavior. The existing block comment above helps but a one-line note at the assertion improves clarity.","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"}],"watcher/tests/functional/test_nova_model.py":[{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"5ee84f497dc0ca7a979b553ec30ba9a7bbcb9683","unresolved":true,"context_lines":[{"line_number":73,"context_line":"    },"},{"line_number":74,"context_line":"]"},{"line_number":75,"context_line":""},{"line_number":76,"context_line":"INSTANCES \u003d ["},{"line_number":77,"context_line":"    # compute-1: mix of regular, BFV, ephemeral, swap"},{"line_number":78,"context_line":"    {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: \u0027bbbb0001-0000-0000-0000-000000000001\u0027,"}],"source_content_type":"text/x-python","patch_set":4,"id":"dc4e71d6_77f29be6","line":76,"range":{"start_line":76,"start_character":0,"end_line":76,"end_character":9},"updated":"2026-07-02 19:12:01.000000000","message":"IMHO this approach of declaring the full topology in each file is more difficult to maintain/read/review. Even considering that we have llm that can help us on updating everything when needed. What about having topologies defined in a common file and have test classes creating a copy and modifying the attributes needed to adapt to specific use cases?","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"1032c5d5ce76724b706cce895700dce39823e3f1","unresolved":true,"context_lines":[{"line_number":73,"context_line":"    },"},{"line_number":74,"context_line":"]"},{"line_number":75,"context_line":""},{"line_number":76,"context_line":"INSTANCES \u003d ["},{"line_number":77,"context_line":"    # compute-1: mix of regular, BFV, ephemeral, swap"},{"line_number":78,"context_line":"    {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: \u0027bbbb0001-0000-0000-0000-000000000001\u0027,"}],"source_content_type":"text/x-python","patch_set":4,"id":"d27e9343_187a200c","line":76,"range":{"start_line":76,"start_character":0,"end_line":76,"end_character":9},"in_reply_to":"1b1b0af4_1646c8c9","updated":"2026-07-08 10:15:25.000000000","message":"I think that having the resources defined close to the tests will make it easier to debug or infer how the tests works. The first time I looked at the zone migration unit tests it took me a while to find where the model content was defined and understand how it was used","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"262c2b12c26eb2da63401e81e9410bf6757e1ee4","unresolved":true,"context_lines":[{"line_number":73,"context_line":"    },"},{"line_number":74,"context_line":"]"},{"line_number":75,"context_line":""},{"line_number":76,"context_line":"INSTANCES \u003d ["},{"line_number":77,"context_line":"    # compute-1: mix of regular, BFV, ephemeral, swap"},{"line_number":78,"context_line":"    {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: \u0027bbbb0001-0000-0000-0000-000000000001\u0027,"}],"source_content_type":"text/x-python","patch_set":4,"id":"f27762e5_06d9b792","line":76,"range":{"start_line":76,"start_character":0,"end_line":76,"end_character":9},"in_reply_to":"82109bb5_c7057ae4","updated":"2026-08-03 15:30:51.000000000","message":"It will depend on each topology, somethimes might be easier to create all with defaults and just modify specific parameters. We can improve lot of things later with builder methods, but in general I like the way it is progressing now.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"632efb10eb035060d33d91afd7c46d4ee381480e","unresolved":true,"context_lines":[{"line_number":73,"context_line":"    },"},{"line_number":74,"context_line":"]"},{"line_number":75,"context_line":""},{"line_number":76,"context_line":"INSTANCES \u003d ["},{"line_number":77,"context_line":"    # compute-1: mix of regular, BFV, ephemeral, swap"},{"line_number":78,"context_line":"    {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: \u0027bbbb0001-0000-0000-0000-000000000001\u0027,"}],"source_content_type":"text/x-python","patch_set":4,"id":"82109bb5_c7057ae4","line":76,"range":{"start_line":76,"start_character":0,"end_line":76,"end_character":9},"in_reply_to":"823063ae_d312f2fc","updated":"2026-07-31 12:51:15.000000000","message":"the builder method help alot.\n\nim not agisnt manually creting them like this when that is something you need to do but i would personaly use the build methods when possibel\n\nsupproting both forms is ok as is having the ablity to expore to a file and import it later for freestandign simulator type usage.\n\nits condiertion like ensuring every instance has a unique uuid and that the","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":true,"context_lines":[{"line_number":73,"context_line":"    },"},{"line_number":74,"context_line":"]"},{"line_number":75,"context_line":""},{"line_number":76,"context_line":"INSTANCES \u003d ["},{"line_number":77,"context_line":"    # compute-1: mix of regular, BFV, ephemeral, swap"},{"line_number":78,"context_line":"    {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: \u0027bbbb0001-0000-0000-0000-000000000001\u0027,"}],"source_content_type":"text/x-python","patch_set":4,"id":"823063ae_d312f2fc","line":76,"range":{"start_line":76,"start_character":0,"end_line":76,"end_character":9},"in_reply_to":"d27e9343_187a200c","updated":"2026-07-30 15:51:25.000000000","message":"I\u0027ve added some helper functions to  define in a much easier way the topologies in the tests classes. Let me know your thoughts.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"e1984db3b3212adbf55c0a2eed0a04690df5d036","unresolved":true,"context_lines":[{"line_number":73,"context_line":"    },"},{"line_number":74,"context_line":"]"},{"line_number":75,"context_line":""},{"line_number":76,"context_line":"INSTANCES \u003d ["},{"line_number":77,"context_line":"    # compute-1: mix of regular, BFV, ephemeral, swap"},{"line_number":78,"context_line":"    {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: \u0027bbbb0001-0000-0000-0000-000000000001\u0027,"}],"source_content_type":"text/x-python","patch_set":4,"id":"1b1b0af4_1646c8c9","line":76,"range":{"start_line":76,"start_character":0,"end_line":76,"end_character":9},"in_reply_to":"dc4e71d6_77f29be6","updated":"2026-07-03 19:30:13.000000000","message":"Nothing prevent us of having shared topologies that we may import in some cases and specific per-test topologies in others, it\u0027s just feeding it in the setup load_topology call. I\u0027d keep this open to both cases. My experience working in unit tests is that having topologies in separate files make difficult to read and require to be moving from file to file all the time, although in the other side, i understand repeating the same topologies many times is against DRY principle and may. I don\u0027t like to force to get the topologies from a specific file or set of files, although we may agree on a set of reusable topologies by convention.","commit_id":"7e4e533afdd216499e24bee34365b1f3147448fc"}],"watcher/tests/local_fixtures/base_emulator.py":[{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"632efb10eb035060d33d91afd7c46d4ee381480e","unresolved":true,"context_lines":[{"line_number":94,"context_line":"            if isinstance(val, dict) and not attr.startswith(\u0027_\u0027):"},{"line_number":95,"context_line":"                val.clear()"},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"    def load_topology(self, topology\u003dNone):"},{"line_number":98,"context_line":"        raise NotImplementedError"},{"line_number":99,"context_line":""},{"line_number":100,"context_line":"    # Model loading (auto-detect format)"},{"line_number":101,"context_line":""},{"line_number":102,"context_line":"    def load_model(self, model_path):"},{"line_number":103,"context_line":"        \"\"\"Load topology from a file, auto-detecting XML or JSON format."},{"line_number":104,"context_line":""},{"line_number":105,"context_line":"        Tries JSON first, then XML.  Raises ValueError if neither"},{"line_number":106,"context_line":"        parser succeeds."},{"line_number":107,"context_line":"        \"\"\""},{"line_number":108,"context_line":"        try:"},{"line_number":109,"context_line":"            self.load_from_json(model_path)"},{"line_number":110,"context_line":"            return"},{"line_number":111,"context_line":"        except (ValueError, UnicodeDecodeError):"},{"line_number":112,"context_line":"            pass"},{"line_number":113,"context_line":"        try:"},{"line_number":114,"context_line":"            self.load_from_xml(model_path)"},{"line_number":115,"context_line":"            return"},{"line_number":116,"context_line":"        except ElementTree.ParseError:"},{"line_number":117,"context_line":"            pass"},{"line_number":118,"context_line":"        raise ValueError(\u0027Unable to parse %s as JSON or XML\u0027 % model_path)"},{"line_number":119,"context_line":""},{"line_number":120,"context_line":"    # XML loading (default implementation for compute topology)"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    def load_from_xml(self, xml_path):"},{"line_number":123,"context_line":"        \"\"\"Load topology from a watcher scenario XML model file."},{"line_number":124,"context_line":""},{"line_number":125,"context_line":"        Field names are discovered dynamically from the ``ComputeNode``"},{"line_number":126,"context_line":"        and ``Instance`` dataclasses.  Only ``hostname`` (for nodes) and"},{"line_number":127,"context_line":"        ``name``/``host`` (for instances) are mandatory; everything else"},{"line_number":128,"context_line":"        uses the dataclass defaults when absent from the XML."},{"line_number":129,"context_line":"        \"\"\""},{"line_number":130,"context_line":"        tree \u003d ElementTree.parse(xml_path)"},{"line_number":131,"context_line":"        root \u003d tree.getroot()"},{"line_number":132,"context_line":""},{"line_number":133,"context_line":"        compute_nodes \u003d []"},{"line_number":134,"context_line":"        instances \u003d []"},{"line_number":135,"context_line":""},{"line_number":136,"context_line":"        for node in root.iter(\u0027ComputeNode\u0027):"},{"line_number":137,"context_line":"            compute_nodes.append("},{"line_number":138,"context_line":"                _parse_xml_element(node, topo_mod.ComputeNode)"},{"line_number":139,"context_line":"            )"},{"line_number":140,"context_line":"            hostname \u003d node.attrib[\u0027hostname\u0027]"},{"line_number":141,"context_line":"            for inst in node.findall(\u0027Instance\u0027):"},{"line_number":142,"context_line":"                instances.append("},{"line_number":143,"context_line":"                    _parse_xml_element(inst, topo_mod.Instance, host\u003dhostname)"},{"line_number":144,"context_line":"                )"},{"line_number":145,"context_line":""},{"line_number":146,"context_line":"        self.load_topology("},{"line_number":147,"context_line":"            topo_mod.ComputeTopology("},{"line_number":148,"context_line":"                compute_nodes\u003dcompute_nodes, instances\u003dinstances"},{"line_number":149,"context_line":"            )"},{"line_number":150,"context_line":"        )"},{"line_number":151,"context_line":""},{"line_number":152,"context_line":"    # JSON loading (default implementation for compute topology)"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":"    def load_from_json(self, json_path):"},{"line_number":155,"context_line":"        \"\"\"Load topology from a JSON file."},{"line_number":156,"context_line":""},{"line_number":157,"context_line":"        Only fields defined on ``ComputeNode``, ``Instance``, and"},{"line_number":158,"context_line":"        ``Aggregate`` are used; unknown keys are silently ignored."},{"line_number":159,"context_line":"        Missing fields use the dataclass defaults."},{"line_number":160,"context_line":"        \"\"\""},{"line_number":161,"context_line":"        with open(json_path, \u0027rb\u0027) as f:"},{"line_number":162,"context_line":"            data \u003d jsonutils.load(f)"},{"line_number":163,"context_line":"        self.load_topology("},{"line_number":164,"context_line":"            topo_mod.ComputeTopology("},{"line_number":165,"context_line":"                compute_nodes\u003d["},{"line_number":166,"context_line":"                    _from_dict(topo_mod.ComputeNode, d)"},{"line_number":167,"context_line":"                    for d in data.get(\u0027compute_nodes\u0027, [])"},{"line_number":168,"context_line":"                ],"},{"line_number":169,"context_line":"                instances\u003d["},{"line_number":170,"context_line":"                    _from_dict(topo_mod.Instance, d)"},{"line_number":171,"context_line":"                    for d in data.get(\u0027instances\u0027, [])"},{"line_number":172,"context_line":"                ],"},{"line_number":173,"context_line":"                aggregates\u003d["},{"line_number":174,"context_line":"                    _from_dict(topo_mod.Aggregate, d)"},{"line_number":175,"context_line":"                    for d in data.get(\u0027aggregates\u0027, [])"},{"line_number":176,"context_line":"                ],"},{"line_number":177,"context_line":"            )"},{"line_number":178,"context_line":"        )"},{"line_number":179,"context_line":""},{"line_number":180,"context_line":""},{"line_number":181,"context_line":"def create_app(emulator_class, model_path\u003dNone, topology\u003dNone):"},{"line_number":182,"context_line":"    \"\"\"Factory: create an emulator, optionally loading topology.\"\"\""},{"line_number":183,"context_line":"    emulator \u003d emulator_class()"},{"line_number":184,"context_line":"    if model_path:"},{"line_number":185,"context_line":"        emulator.load_model(model_path)"},{"line_number":186,"context_line":"    elif topology:"},{"line_number":187,"context_line":"        emulator.load_topology(topology)"},{"line_number":188,"context_line":"    return emulator"},{"line_number":189,"context_line":""},{"line_number":190,"context_line":""},{"line_number":191,"context_line":"def run_standalone(emulator_class, description, default_port):"}],"source_content_type":"text/x-python","patch_set":12,"id":"22f18e41_9bd09171","line":188,"range":{"start_line":97,"start_character":1,"end_line":188,"end_character":19},"updated":"2026-07-31 12:51:15.000000000","message":"these are really factory method or constuctor fucntions so the more idomatic way to do this woudl be to ues classmethod that constuc the class internaly and return the populated instnace of the class.\n\nlike this https://paste.opendev.org/show/bXP1KuRJUZqix9hrDspl/\n\nbut i also want to question the use of Flask here\n\nin watcher Pecan https://github.com/pecan/pecan is our wsgi object framewrok and serve the same role as flask\n\nso if we were to use only or existing deps gemini thinks it shoudl look like this\n\nhttps://paste.opendev.org/show/bKJcf9vGiPP44DRzCEIw/\n\nthat allows us to avoid takeing flask as a depency just for this usecase","commit_id":"90a0228a6ee437da5037820eca07bb6ee9b1c415"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"5b321c96539741983231773a230f99ac80f51737","unresolved":false,"context_lines":[{"line_number":194,"context_line":"    parser.add_argument("},{"line_number":195,"context_line":"        \u0027--model\u0027,"},{"line_number":196,"context_line":"        default\u003dNone,"},{"line_number":197,"context_line":"        help\u003d\u0027Path to model file (XML or JSON, auto-detected by extension)\u0027,"},{"line_number":198,"context_line":"    )"},{"line_number":199,"context_line":"    parser.add_argument(\u0027--host\u0027, default\u003d\u0027127.0.0.1\u0027)"},{"line_number":200,"context_line":"    parser.add_argument(\u0027--port\u0027, type\u003dint, default\u003ddefault_port)"}],"source_content_type":"text/x-python","patch_set":13,"id":"78d1613b_81c71a4a","line":197,"updated":"2026-07-31 13:36:49.000000000","message":"The argparse help string for the --model flag in run_standalone() says the format is \u0027auto-detected by extension\u0027, but load_model() actually detects format by attempting JSON parsing first and falling back to XML parsing — a content-based approach, not extension-based. The testing.rst documentati...\n\n**Severity**: SUGGESTION | **Confidence**: 0.9\n\n**Benefit**: Developers running the standalone emulator may expect extension-based detection (e.g., naming a file .json to force JSON parsing) and be confused when a .xml-named file containing JSON is parsed as JSON anyway, or vice versa. The actual behavior is more robust than described but differs from the...\n\n**Recommendation**:\nChange the help text to match the implementation and rst documentation, e.g.: help\u003d\u0027Path to model file (XML or JSON, format auto-detected from file content)\u0027","commit_id":"06911d1125fe8d5c68a62df5e0e12e6557c9fbd6"}],"watcher/tests/local_fixtures/nova.py":[{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"1032c5d5ce76724b706cce895700dce39823e3f1","unresolved":true,"context_lines":[{"line_number":92,"context_line":"    - ``clients.get_sdk_connection()`` so that ``NovaHelper`` gets an"},{"line_number":93,"context_line":"      openstacksdk ``Connection`` with ``compute.endpoint_override``"},{"line_number":94,"context_line":"      pointing at the in-process Nova API emulator."},{"line_number":95,"context_line":"    - ``OpenStackClients._placement`` on every instance so that"},{"line_number":96,"context_line":"      ``placement()`` returns our keystoneauth1 Adapter without auth."},{"line_number":97,"context_line":"    - ``OpenStackClients._cinder`` on every instance so that"},{"line_number":98,"context_line":"      ``NovaHelper.__init__`` does not fail trying to authenticate."}],"source_content_type":"text/x-python","patch_set":5,"id":"12ab16f2_48e59ab8","line":95,"updated":"2026-07-08 10:15:25.000000000","message":"as noted elsewhere this is outdated","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":false,"context_lines":[{"line_number":92,"context_line":"    - ``clients.get_sdk_connection()`` so that ``NovaHelper`` gets an"},{"line_number":93,"context_line":"      openstacksdk ``Connection`` with ``compute.endpoint_override``"},{"line_number":94,"context_line":"      pointing at the in-process Nova API emulator."},{"line_number":95,"context_line":"    - ``OpenStackClients._placement`` on every instance so that"},{"line_number":96,"context_line":"      ``placement()`` returns our keystoneauth1 Adapter without auth."},{"line_number":97,"context_line":"    - ``OpenStackClients._cinder`` on every instance so that"},{"line_number":98,"context_line":"      ``NovaHelper.__init__`` does not fail trying to authenticate."}],"source_content_type":"text/x-python","patch_set":5,"id":"a0687ed7_be8d0924","line":95,"in_reply_to":"12ab16f2_48e59ab8","updated":"2026-07-30 15:51:25.000000000","message":"Done","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"17cf3fa93dc22ab02a80b2636b0a19ce5f074f92","unresolved":true,"context_lines":[{"line_number":132,"context_line":"        # Create emulators"},{"line_number":133,"context_line":""},{"line_number":134,"context_line":"        self.nova_emulator \u003d NovaAPIEmulator()"},{"line_number":135,"context_line":"        self.placement_emulator \u003d PlacementAPIEmulator()"},{"line_number":136,"context_line":""},{"line_number":137,"context_line":"        if self._xml_path:"},{"line_number":138,"context_line":"            self.nova_emulator.load_from_xml(self._xml_path)"}],"source_content_type":"text/x-python","patch_set":5,"id":"05fb6b2f_2ef7cddf","line":135,"range":{"start_line":135,"start_character":4,"end_line":135,"end_character":56},"updated":"2026-07-03 16:09:35.000000000","message":"so placement has a reusabel fixture\nhttps://github.com/openstack/placement/blob/master/placement/tests/functional/fixtures/placement.py\nthat is intended ot be used by other services\nhttps://github.com/openstack/nova/blob/master/tox.ini#L115\n\nnova imports and uses it in its tests\nhttps://github.com/openstack/nova/blob/master/nova/tests/functional/fixtures.py#L23-L91 as needed\n\nwe will proably want to do the same","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":true,"context_lines":[{"line_number":132,"context_line":"        # Create emulators"},{"line_number":133,"context_line":""},{"line_number":134,"context_line":"        self.nova_emulator \u003d NovaAPIEmulator()"},{"line_number":135,"context_line":"        self.placement_emulator \u003d PlacementAPIEmulator()"},{"line_number":136,"context_line":""},{"line_number":137,"context_line":"        if self._xml_path:"},{"line_number":138,"context_line":"            self.nova_emulator.load_from_xml(self._xml_path)"}],"source_content_type":"text/x-python","patch_set":5,"id":"0bff811d_0da35eef","line":135,"range":{"start_line":135,"start_character":4,"end_line":135,"end_character":56},"in_reply_to":"05fb6b2f_2ef7cddf","updated":"2026-07-30 15:51:25.000000000","message":"I have doubts about that. I think if we use the fixtures provided by placement, we\u0027ll lose the capability of running the placement emulator in standalone mode and that was a requirement for me, as it provides a very useful tool for debug and testing while developing features. For consistency, I\u0027d propose to keep current implementation. Placement api is pretty simple for the watcher use case and i\u0027d expect it to be relatively low-effort to maintain.\n\nAdditionally, that\u0027d force us to install placement as functional testing dependency, which is probably doable, but not a usual one, i think.","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"1032c5d5ce76724b706cce895700dce39823e3f1","unresolved":true,"context_lines":[{"line_number":181,"context_line":"        auth \u003d noauth.NoAuth(endpoint\u003dnova_endpoint)"},{"line_number":182,"context_line":"        sess \u003d ks_session.Session(auth\u003dauth)"},{"line_number":183,"context_line":""},{"line_number":184,"context_line":"        # Placement: keystoneauth1 Adapter (used by PlacementHelper)"},{"line_number":185,"context_line":"        placement_adapter \u003d ka_adapter.Adapter("},{"line_number":186,"context_line":"            session\u003dsess, endpoint_override\u003dplacement_endpoint"},{"line_number":187,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":5,"id":"22fb1b24_e7a9bca3","line":184,"updated":"2026-07-08 10:15:25.000000000","message":"this is now outdated, PlacementHelper uses an openstacksdk connection and `OpenStackClients` does not have a `_placement` attribute anymore https://github.com/openstack/watcher/blob/7ac19c8a2180f84f4a341d4183d6a6bcd7589426/watcher/common/clients.py#L129","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":false,"context_lines":[{"line_number":181,"context_line":"        auth \u003d noauth.NoAuth(endpoint\u003dnova_endpoint)"},{"line_number":182,"context_line":"        sess \u003d ks_session.Session(auth\u003dauth)"},{"line_number":183,"context_line":""},{"line_number":184,"context_line":"        # Placement: keystoneauth1 Adapter (used by PlacementHelper)"},{"line_number":185,"context_line":"        placement_adapter \u003d ka_adapter.Adapter("},{"line_number":186,"context_line":"            session\u003dsess, endpoint_override\u003dplacement_endpoint"},{"line_number":187,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":5,"id":"6406b4f5_45d2ba25","line":184,"in_reply_to":"22fb1b24_e7a9bca3","updated":"2026-07-30 15:51:25.000000000","message":"Done","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"1032c5d5ce76724b706cce895700dce39823e3f1","unresolved":true,"context_lines":[{"line_number":190,"context_line":"        # Placement: openstacksdk proxy (used by PlacementHelper)"},{"line_number":191,"context_line":"        # Endpoint overrides must be set via the constructor so"},{"line_number":192,"context_line":"        # they are available during service version discovery."},{"line_number":193,"context_line":"        sdk_conn \u003d os_connection.Connection("},{"line_number":194,"context_line":"            session\u003dsess,"},{"line_number":195,"context_line":"            compute_endpoint_override\u003dnova_endpoint,"},{"line_number":196,"context_line":"            placement_endpoint_override\u003dplacement_endpoint,"}],"source_content_type":"text/x-python","patch_set":5,"id":"27e20eaf_c985c5c6","line":193,"updated":"2026-07-08 10:15:25.000000000","message":"I have a question to future-proof this. During this cycle, we\u0027ll hopefully merge the changes to use openstacksdk in the CinderHelper, since we\u0027ll have a separate fixture for cinder in https://review.opendev.org/c/openstack/watcher/+/993634 will patching get_sdk_connection independently in here and the future Cinde fixture work? i.e will the Connection have all the required endpoints?","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":true,"context_lines":[{"line_number":190,"context_line":"        # Placement: openstacksdk proxy (used by PlacementHelper)"},{"line_number":191,"context_line":"        # Endpoint overrides must be set via the constructor so"},{"line_number":192,"context_line":"        # they are available during service version discovery."},{"line_number":193,"context_line":"        sdk_conn \u003d os_connection.Connection("},{"line_number":194,"context_line":"            session\u003dsess,"},{"line_number":195,"context_line":"            compute_endpoint_override\u003dnova_endpoint,"},{"line_number":196,"context_line":"            placement_endpoint_override\u003dplacement_endpoint,"}],"source_content_type":"text/x-python","patch_set":5,"id":"a55bdaf5_9974586a","line":193,"in_reply_to":"27e20eaf_c985c5c6","updated":"2026-07-30 15:51:25.000000000","message":"I\u0027m not sure i\u0027m following you. I think openstacksdk has generic `\u003cservice\u003e_endpoint_override`. When we implement the cinder fixture we will have to do a similar sdk connection that overrides cinder_endpoint_override. Given that the patch to move out of cinderclient is merged I will check it soon.","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"780e43980195c48c2abd2e561816a2fbc3cef778","unresolved":true,"context_lines":[{"line_number":190,"context_line":"        # Placement: openstacksdk proxy (used by PlacementHelper)"},{"line_number":191,"context_line":"        # Endpoint overrides must be set via the constructor so"},{"line_number":192,"context_line":"        # they are available during service version discovery."},{"line_number":193,"context_line":"        sdk_conn \u003d os_connection.Connection("},{"line_number":194,"context_line":"            session\u003dsess,"},{"line_number":195,"context_line":"            compute_endpoint_override\u003dnova_endpoint,"},{"line_number":196,"context_line":"            placement_endpoint_override\u003dplacement_endpoint,"}],"source_content_type":"text/x-python","patch_set":5,"id":"e235c773_ae185a17","line":193,"in_reply_to":"a55bdaf5_9974586a","updated":"2026-08-03 12:27:52.000000000","message":"my worry was that in cases where we might want to use both the nova and cinder fixtures, if each fixture patches the get_sdk_connection will that work well? Let\u0027s say we first load the nova fixture and then the cinder one, when we run the test and create a Nova Helper, will it get a working connection or will it get the one from the cinder fixture?","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"780e43980195c48c2abd2e561816a2fbc3cef778","unresolved":true,"context_lines":[{"line_number":140,"context_line":"            compute_endpoint_override\u003dnova_endpoint,"},{"line_number":141,"context_line":"            placement_endpoint_override\u003dplacement_endpoint,"},{"line_number":142,"context_line":"        )"},{"line_number":143,"context_line":""},{"line_number":144,"context_line":"        # Patch at the instance level"},{"line_number":145,"context_line":""},{"line_number":146,"context_line":"        from watcher.common import clients"},{"line_number":147,"context_line":""},{"line_number":148,"context_line":"        # Wrap OpenStackClients.__init__ to pre-set"},{"line_number":149,"context_line":"        # and _cinder so the real methods return our objects"},{"line_number":150,"context_line":"        # without trying to authenticate against Keystone."},{"line_number":151,"context_line":"        orig_init \u003d clients.OpenStackClients.__init__"},{"line_number":152,"context_line":""},{"line_number":153,"context_line":"        def patched_init(osc_self):"},{"line_number":154,"context_line":"            orig_init(osc_self)"},{"line_number":155,"context_line":"            osc_self._cinder \u003d mock.MagicMock()"},{"line_number":156,"context_line":""},{"line_number":157,"context_line":"        patcher_init \u003d mock.patch.object("},{"line_number":158,"context_line":"            clients.OpenStackClients, \u0027__init__\u0027, patched_init"},{"line_number":159,"context_line":"        )"},{"line_number":160,"context_line":"        patcher_init.start()"},{"line_number":161,"context_line":"        self.addCleanup(patcher_init.stop)"},{"line_number":162,"context_line":""},{"line_number":163,"context_line":"        # Patch get_sdk_connection to return our pre-configured"},{"line_number":164,"context_line":"        # Connection with compute.endpoint_override set."}],"source_content_type":"text/x-python","patch_set":13,"id":"b887cfdd_4085b7eb","line":161,"range":{"start_line":143,"start_character":0,"end_line":161,"end_character":42},"updated":"2026-08-03 12:27:52.000000000","message":"I think we can now drop this, since we dropped the cinder client from the nova helper in https://github.com/openstack/watcher/commit/534517fb6fb6a52782e82cd27601aac0c9c67da1#diff-4f806c6f9b013ceff09e50cb80e1d6b0e07ff6bcc2543884ce85ec5808b59a8b","commit_id":"06911d1125fe8d5c68a62df5e0e12e6557c9fbd6"}],"watcher/tests/local_fixtures/nova_api_emulator.py":[{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"17cf3fa93dc22ab02a80b2636b0a19ce5f074f92","unresolved":true,"context_lines":[{"line_number":65,"context_line":"    return str(uuid.uuid5(uuid.NAMESPACE_DNS, seed))"},{"line_number":66,"context_line":""},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"class NovaAPIEmulator:"},{"line_number":69,"context_line":"    \"\"\"In-memory Nova API emulator."},{"line_number":70,"context_line":""},{"line_number":71,"context_line":"    Manages cluster state (servers, hypervisors, services, flavors,"}],"source_content_type":"text/x-python","patch_set":5,"id":"2cffa180_09b34813","line":68,"range":{"start_line":68,"start_character":6,"end_line":68,"end_character":21},"updated":"2026-07-03 16:09:35.000000000","message":"sop these are really fixtures\n\nim surpese your not inhreititng form that","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"b70dfb90b33af32aa40d039f88d28c3d18466cf8","unresolved":false,"context_lines":[{"line_number":65,"context_line":"    return str(uuid.uuid5(uuid.NAMESPACE_DNS, seed))"},{"line_number":66,"context_line":""},{"line_number":67,"context_line":""},{"line_number":68,"context_line":"class NovaAPIEmulator:"},{"line_number":69,"context_line":"    \"\"\"In-memory Nova API emulator."},{"line_number":70,"context_line":""},{"line_number":71,"context_line":"    Manages cluster state (servers, hypervisors, services, flavors,"}],"source_content_type":"text/x-python","patch_set":5,"id":"d568ea80_09889875","line":68,"range":{"start_line":68,"start_character":6,"end_line":68,"end_character":21},"in_reply_to":"2cffa180_09b34813","updated":"2026-07-30 15:51:25.000000000","message":"Done","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"1b801a94a13e27f14d5d093d917217880adc1859","unresolved":false,"context_lines":[{"line_number":174,"context_line":"                \u0027hypervisor_type\u0027: \u0027libvirt\u0027,"},{"line_number":175,"context_line":"                \u0027state\u0027: state,"},{"line_number":176,"context_line":"                \u0027status\u0027: status,"},{"line_number":177,"context_line":"                \u0027vcpus\u0027: int(node_def[\u0027vcpus\u0027]),"},{"line_number":178,"context_line":"                \u0027vcpus_used\u0027: 0,"},{"line_number":179,"context_line":"                \u0027memory_size\u0027: int(node_def[\u0027memory\u0027]),"},{"line_number":180,"context_line":"                \u0027memory_mb\u0027: int(node_def[\u0027memory\u0027]),"}],"source_content_type":"text/x-python","patch_set":5,"id":"5968ec46_7c003932","line":177,"updated":"2026-07-02 19:37:54.000000000","message":"load_topology performs int()/float() conversions on topology dict keys (e.g. int(node_def[\u0027vcpus\u0027])) without validating required keys first. A topology dict missing a required key raises a raw KeyError/TypeError with no context about which node or which field failed, making test authoring and failure diagnosis harder.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: A malformed topology dict produces a cryptic traceback instead of a clear error, slowing down test development.\n\n**Suggestion**:\nAdd a small validation helper that checks each node_def/inst_def for required keys and raises ValueError naming the offending item and missing field, or at minimum wrap the int() conversions to produce readable errors.","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"1b801a94a13e27f14d5d093d917217880adc1859","unresolved":false,"context_lines":[{"line_number":379,"context_line":"                return svc"},{"line_number":380,"context_line":"        return None"},{"line_number":381,"context_line":""},{"line_number":382,"context_line":"    def _move_server(self, server_id, dest_hostname):"},{"line_number":383,"context_line":"        \"\"\"Move a server to a new host in the emulator state.\"\"\""},{"line_number":384,"context_line":"        server \u003d self.servers.get(server_id)"},{"line_number":385,"context_line":"        if not server:"}],"source_content_type":"text/x-python","patch_set":5,"id":"8b9f67c2_da4001f4","line":382,"updated":"2026-07-02 19:37:54.000000000","message":"The Nova emulator and Placement emulator are independent instances with no state synchronization. When _move_server updates Nova hypervisor usage on migration, the Placement emulator\u0027s inventories/usages/allocations are not updated. This fidelity gap is undocumented in the emulator docstrings or testing.rst, so a future test that reloads the data model after an applier migration and consults Placement usages would see stale data.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: Future tests asserting Placement usage changes post-migration will silently see stale data and pass or fail incorrectly.\n\n**Suggestion**:\nDocument this limitation in the NovaAPIEmulator._move_server docstring and in testing.rst. If full fidelity is needed later, have NovaPlacementFixture hold a reference to both emulators and add a post-move hook that updates Placement allocations/usages.","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"1b801a94a13e27f14d5d093d917217880adc1859","unresolved":false,"context_lines":[{"line_number":558,"context_line":"                hyp \u003d self._find_hypervisor_by_hostname(svc[\u0027host\u0027])"},{"line_number":559,"context_line":"                if hyp:"},{"line_number":560,"context_line":"                    hyp[\u0027status\u0027] \u003d body[\u0027status\u0027]"},{"line_number":561,"context_line":"                    hyp[\u0027service\u0027][\u0027disabled_reason\u0027] \u003d body.get("},{"line_number":562,"context_line":"                        \u0027disabled_reason\u0027"},{"line_number":563,"context_line":"                    )"},{"line_number":564,"context_line":"            if \u0027disabled_reason\u0027 in body:"}],"source_content_type":"text/x-python","patch_set":5,"id":"43cb2236_23263534","line":561,"updated":"2026-07-02 19:37:54.000000000","message":"update_service overwrites hypervisor disabled_reason with None when only \u0027status\u0027 is provided. When \u0027status\u0027 is present in the PUT body, line 559-561 unconditionally sets hyp[\u0027service\u0027][\u0027disabled_reason\u0027] \u003d body.get(\u0027disabled_reason\u0027), which returns None if the caller did not supply disabled_reason. This clobbers any previously-configured reason. Real Nova only updates disabled_reason when the field is explicitly sent. When watcher\u0027s change_nova_service_state action enables/disables a service it sends only \u0027status\u0027, so a node disabled-with-reason loses that reason, and emulator state diverges from real Nova semantics.\n\n**Severity**: HIGH | **Confidence**: 0.8\n\n**Risk**: Emulator state diverges from real Nova, so tests built on it could pass while real Nova behaves differently, or fail to exercise the disabled_reason path the collector/applier rely on.\n\n**Priority**: Before merge\n**Why This Matters**: Fidelity of the emulator is the whole point of this change; incorrect disabled_reason handling can mask real strategy bugs around disabled compute nodes.\n\n**Recommendation**:\nOnly update the hypervisor\u0027s service disabled_reason when \u0027disabled_reason\u0027 is present in the body, e.g. guard lines 559-561 inside `if \u0027disabled_reason\u0027 in body:` (the block at 564-565 already handles the service-level field correctly).","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"632efb10eb035060d33d91afd7c46d4ee381480e","unresolved":true,"context_lines":[{"line_number":44,"context_line":"import hashlib"},{"line_number":45,"context_line":"import uuid"},{"line_number":46,"context_line":""},{"line_number":47,"context_line":"from flask import abort"},{"line_number":48,"context_line":"from flask import jsonify"},{"line_number":49,"context_line":"from flask import request"},{"line_number":50,"context_line":""},{"line_number":51,"context_line":"from watcher.tests.functional import topology as topo_mod"},{"line_number":52,"context_line":"from watcher.tests.local_fixtures import base_emulator"}],"source_content_type":"text/x-python","patch_set":12,"id":"287a37f9_b571440b","line":49,"range":{"start_line":47,"start_character":0,"end_line":49,"end_character":25},"updated":"2026-07-31 12:51:15.000000000","message":"of course if we remove flask in the base emulator we also need to replace it here\n\nhttps://paste.opendev.org/show/bZIAaa2jFuDgXoHgsDvy/\n\n\nwhile i personally prefer flaks routing approch via simple funcitons and decorators\n\nunless we plan to adopt flask for the main api it does nto feel like a good idea ot use flask in the functional tests and pecan in the procudtion code.","commit_id":"90a0228a6ee437da5037820eca07bb6ee9b1c415"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"00781f5fcde3c226bc9d864280c6356afdb57ba6","unresolved":false,"context_lines":[{"line_number":57,"context_line":"def _flavor_id(vcpus, ram, disk):"},{"line_number":58,"context_line":"    \"\"\"Deterministic flavor id derived from resource dimensions.\"\"\""},{"line_number":59,"context_line":"    key \u003d f\"{vcpus}-{ram}-{disk}\""},{"line_number":60,"context_line":"    return hashlib.md5(key.encode()).hexdigest()[:8]"},{"line_number":61,"context_line":""},{"line_number":62,"context_line":""},{"line_number":63,"context_line":"def _deterministic_uuid(seed):"}],"source_content_type":"text/x-python","patch_set":15,"id":"ca3ead4f_ff4e34b9","line":60,"updated":"2026-08-03 15:32:48.000000000","message":"The _flavor_id() helper in nova_api_emulator.py calls hashlib.md5(key.encode()) without the usedforsecurity\u003dFalse parameter. OpenStack CI environments commonly run in FIPS mode where unrestricted MD5 usage raises ValueError: [digital envelope routines] unsupported. This would cause the emulator t...\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: If watcher adds or already has FIPS-enabled CI jobs, all functional tests using NovaPlacementFixture will fail with ValueError during emulator setup. Even without FIPS jobs today, using usedforsecurity\u003dFalse is the established OpenStack convention for non-security hashing and prevents future brea...\n\n**Suggestion**:\nChange line 60 to: return hashlib.md5(key.encode(), usedforsecurity\u003dFalse).hexdigest()[:8]","commit_id":"1d8771252415cd84986e07b2b6447f1a7eeb3c62"}],"watcher/tests/local_fixtures/placement_api_emulator.py":[{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"17cf3fa93dc22ab02a80b2636b0a19ce5f074f92","unresolved":true,"context_lines":[{"line_number":158,"context_line":"            }"},{"line_number":159,"context_line":""},{"line_number":160,"context_line":"    def load_from_xml(self, xml_path):"},{"line_number":161,"context_line":"        \"\"\"Load topology from a watcher scenario XML model file.\"\"\""},{"line_number":162,"context_line":"        tree \u003d ElementTree.parse(xml_path)"},{"line_number":163,"context_line":"        root \u003d tree.getroot()"},{"line_number":164,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"3736d308_831c858c","line":161,"range":{"start_line":161,"start_character":8,"end_line":161,"end_character":67},"updated":"2026-07-03 16:09:35.000000000","message":"for what its worth i condier those file to be technial debt that we shoudl be tryign to remvoe not build on\n\nim not going to block on this but i dont think this is godo for us to contineu using long term","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"632efb10eb035060d33d91afd7c46d4ee381480e","unresolved":false,"context_lines":[{"line_number":158,"context_line":"            }"},{"line_number":159,"context_line":""},{"line_number":160,"context_line":"    def load_from_xml(self, xml_path):"},{"line_number":161,"context_line":"        \"\"\"Load topology from a watcher scenario XML model file.\"\"\""},{"line_number":162,"context_line":"        tree \u003d ElementTree.parse(xml_path)"},{"line_number":163,"context_line":"        root \u003d tree.getroot()"},{"line_number":164,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"8622b9f5_836693e9","line":161,"range":{"start_line":161,"start_character":8,"end_line":161,"end_character":67},"in_reply_to":"3736d308_831c858c","updated":"2026-07-31 12:51:15.000000000","message":"Acknowledged","commit_id":"84372a10e06a1f559a071cc02fd091f826351483"}]}
