)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"b7d7780131642798a26ec6b8cf0ed46886d89d76","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"e653deeb_a3b2f22e","updated":"2026-07-20 17:53:35.000000000","message":"I have one consideration about negative values... Otherwise the improvements looks good to me.","commit_id":"3b4afbb3bef3ef29b6c6944361910c61323c107b"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"6025936f1f2f9fa90a36993123ebdc6752a15d24","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"d64a272b_5a416056","updated":"2026-07-15 10:45:51.000000000","message":"lgtm, thanks for the changes!","commit_id":"3b4afbb3bef3ef29b6c6944361910c61323c107b"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ef286a258f75cc23d314926bda2af17411c00a6d","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":4,"id":"04dbfc54_fde61f24","updated":"2026-07-23 20:03:49.000000000","message":"Thanks, I didn\u0027t had a change to test the fix in a emulated env this time, but the patch is consistent and your results show that is a good improvement to have. Thanks","commit_id":"2a07f2b484f45e4636fa041a91244f691aa12ccd"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"73e991e2e1a069e2d09b62df909d5ab45398659b","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":4,"id":"fb4673f4_cbc345b6","updated":"2026-07-23 09:19:54.000000000","message":"recheck","commit_id":"2a07f2b484f45e4636fa041a91244f691aa12ccd"}],"watcher/decision_engine/strategy/strategies/vm_workload_consolidation.py":[{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"f25120b0b3398b81908c1a257d5a53da4cf250ac","unresolved":true,"context_lines":[{"line_number":605,"context_line":"                instance \u003d self.compute_model.get_instance_by_uuid("},{"line_number":606,"context_line":"                    instance_uuid"},{"line_number":607,"context_line":"                )"},{"line_number":608,"context_line":"                # add_migration will update the node_utilization_cache"},{"line_number":609,"context_line":"                # so we need to adjust the values here for the deleted"},{"line_number":610,"context_line":"                # actions so that the node_utilization_cache remains"},{"line_number":611,"context_line":"                # consistent."}],"source_content_type":"text/x-python","patch_set":1,"id":"f2c23373_bb801276","line":608,"updated":"2026-06-19 12:39:33.000000000","message":"I think we should move this inside the if block below. IIUC we have two possibilities, we can have cancelling migrations for an instance (the `A-\u003eB, B-\u003eA`) example in the docstring or superseeding migrations for an instance (the `A-\u003eB, B-\u003eC`) example in the docstring. For the superseeeding case this would be correct, the migrations in and out of node B would be compensated and this would fix the previous changes from add_migration and redo them. However, for the cancelling case the changes from the add_migration calls would cancel each other, so compensating them here would invalidate the cache. Moving this inside the block would only change the cache when we have an actual migration, not two that cancel each other.\n\nAlso this method seems to implicitely assume that the we would only have a chain of two migration and not something like A-\u003eB, B-\u003eC, C-\u003eA, would that be possible with this strategy?","commit_id":"547b959fe1acd343c70da5a352c41afb86ef7fd9"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"f8293ccc9bee37ce7b28921347631563c977b7dd","unresolved":false,"context_lines":[{"line_number":605,"context_line":"                instance \u003d self.compute_model.get_instance_by_uuid("},{"line_number":606,"context_line":"                    instance_uuid"},{"line_number":607,"context_line":"                )"},{"line_number":608,"context_line":"                # add_migration will update the node_utilization_cache"},{"line_number":609,"context_line":"                # so we need to adjust the values here for the deleted"},{"line_number":610,"context_line":"                # actions so that the node_utilization_cache remains"},{"line_number":611,"context_line":"                # consistent."}],"source_content_type":"text/x-python","patch_set":1,"id":"fd7aed3e_559cd4b6","line":608,"in_reply_to":"5c9d9c96_bbe4a9ce","updated":"2026-06-25 10:23:49.000000000","message":"mmh you\u0027re right, I\u0027m not sure why I missed the fact that the destination nodes in the intermediate steps would also be compensated in add_migration, the logic is correct, thanks","commit_id":"547b959fe1acd343c70da5a352c41afb86ef7fd9"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"6b4c360f22de530ec383c547ff0c39bf9e35e2bd","unresolved":true,"context_lines":[{"line_number":605,"context_line":"                instance \u003d self.compute_model.get_instance_by_uuid("},{"line_number":606,"context_line":"                    instance_uuid"},{"line_number":607,"context_line":"                )"},{"line_number":608,"context_line":"                # add_migration will update the node_utilization_cache"},{"line_number":609,"context_line":"                # so we need to adjust the values here for the deleted"},{"line_number":610,"context_line":"                # actions so that the node_utilization_cache remains"},{"line_number":611,"context_line":"                # consistent."}],"source_content_type":"text/x-python","patch_set":1,"id":"5c9d9c96_bbe4a9ce","line":608,"in_reply_to":"f2c23373_bb801276","updated":"2026-06-24 10:52:37.000000000","message":"About he cancelling case. in that case, dst_node and src_node are the same, so It\u0027d be first substracting and then adding the same value, resulting in + 0 which is correct. Said that, It\u0027s doing unneeded calculation, so moving it inside the below `if` should be good. I will update.\n\nAbout the more-than-two steps, I think all the intermediary steps are compensated properly in the add_migration. Let\u0027s say we have `A-\u003eB, B-\u003eC, C-\u003eD`, that will be consolidated in a single `A-\u003eD`. The utilization cache of B and C have been properly tracked, i.e. for B, in 1st add_migration has been added and in 2nd has been substracted, and the same for C, so we only need to manage A and D in the optimization. The same applies if it\u0027s circular. Am i missing something?","commit_id":"547b959fe1acd343c70da5a352c41afb86ef7fd9"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"f25120b0b3398b81908c1a257d5a53da4cf250ac","unresolved":true,"context_lines":[{"line_number":744,"context_line":"                        self.add_migration(instance, node, destination_node)"},{"line_number":745,"context_line":"                        break"},{"line_number":746,"context_line":"                    else:"},{"line_number":747,"context_line":"                        node_util \u003d self.get_node_utilization(destination_node)"},{"line_number":748,"context_line":"                        node_cap \u003d self.get_node_capacity(destination_node)"},{"line_number":749,"context_line":"                        # For memory, it may happen that remaining memory is"},{"line_number":750,"context_line":"                        # less than the lower ram in flavors. That would mean"}],"source_content_type":"text/x-python","patch_set":1,"id":"9c4c396b_8a9d9eb2","line":747,"updated":"2026-06-19 12:39:33.000000000","message":"this check seems to duplicate the logic of the `is_overloaded` but substracting an offset, I think it might make sense to add a parameter in the method and use it here.\n\nAlso, is there any specific significance to the 128MB value?","commit_id":"547b959fe1acd343c70da5a352c41afb86ef7fd9"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"6025936f1f2f9fa90a36993123ebdc6752a15d24","unresolved":false,"context_lines":[{"line_number":744,"context_line":"                        self.add_migration(instance, node, destination_node)"},{"line_number":745,"context_line":"                        break"},{"line_number":746,"context_line":"                    else:"},{"line_number":747,"context_line":"                        node_util \u003d self.get_node_utilization(destination_node)"},{"line_number":748,"context_line":"                        node_cap \u003d self.get_node_capacity(destination_node)"},{"line_number":749,"context_line":"                        # For memory, it may happen that remaining memory is"},{"line_number":750,"context_line":"                        # less than the lower ram in flavors. That would mean"}],"source_content_type":"text/x-python","patch_set":1,"id":"2c682ec9_20e9d624","line":747,"in_reply_to":"5da28e4d_88072502","updated":"2026-07-15 10:45:51.000000000","message":"Done","commit_id":"547b959fe1acd343c70da5a352c41afb86ef7fd9"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"f8293ccc9bee37ce7b28921347631563c977b7dd","unresolved":true,"context_lines":[{"line_number":744,"context_line":"                        self.add_migration(instance, node, destination_node)"},{"line_number":745,"context_line":"                        break"},{"line_number":746,"context_line":"                    else:"},{"line_number":747,"context_line":"                        node_util \u003d self.get_node_utilization(destination_node)"},{"line_number":748,"context_line":"                        node_cap \u003d self.get_node_capacity(destination_node)"},{"line_number":749,"context_line":"                        # For memory, it may happen that remaining memory is"},{"line_number":750,"context_line":"                        # less than the lower ram in flavors. That would mean"}],"source_content_type":"text/x-python","patch_set":1,"id":"5da28e4d_88072502","line":747,"in_reply_to":"78ed96fe_94916593","updated":"2026-06-25 10:23:49.000000000","message":"I have not review the following patch in the chain, but having a separate method seems reasonable to me. I\u0027m ok to keep the current check and move to the method later. About the 128MB value, I replied to one of the AI comments, that I think having a bit more context on the value would be helpful","commit_id":"547b959fe1acd343c70da5a352c41afb86ef7fd9"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"6b4c360f22de530ec383c547ff0c39bf9e35e2bd","unresolved":true,"context_lines":[{"line_number":744,"context_line":"                        self.add_migration(instance, node, destination_node)"},{"line_number":745,"context_line":"                        break"},{"line_number":746,"context_line":"                    else:"},{"line_number":747,"context_line":"                        node_util \u003d self.get_node_utilization(destination_node)"},{"line_number":748,"context_line":"                        node_cap \u003d self.get_node_capacity(destination_node)"},{"line_number":749,"context_line":"                        # For memory, it may happen that remaining memory is"},{"line_number":750,"context_line":"                        # less than the lower ram in flavors. That would mean"}],"source_content_type":"text/x-python","patch_set":1,"id":"78ed96fe_94916593","line":747,"in_reply_to":"9c4c396b_8a9d9eb2","updated":"2026-06-24 10:52:37.000000000","message":"It\u0027s not exactly the same, although close (actually, in the patch on top of this i\u0027ve created a is_saturated).\n\nThe purpose of is_overloaded is to identify hosts with utilization \u003e that the permitted, to be managed in the offload phase. Actually, i have doubts about the criteria of marking a node as overloaded to be managed in offload and we may want to modify that in future, but that\u0027s a different topic that i don\u0027t want to mix.\n\nThe purpose of this logic is to identify the saturated nodes, that should not be considered as destination for the migrations, that means current usage (or allocations, (as implemented in https://review.opendev.org/c/openstack/watcher/+/993088 ) does not allow to add even small vms.\n\nAlthough seems to be similar, imo, they look for different criteria and diverge enough to manage it separatedly (i.e. by setting a minimal ram to be considered non-saturated !\u003d 0). Actually, in future we may consider that the criteria of is_overloaded (for offload phase) would not include the overcommit level which currently includes or at least allow to configure a certain threshold.\n\nThe purpose of the 128MB is just to be a minimum usual memory size in openstack deployments. I want to avoid the situation where a node is not marked as saturated because memory is \u003e 0 but is too small for realistic vms.","commit_id":"547b959fe1acd343c70da5a352c41afb86ef7fd9"},{"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":"d22bbdb7b8be8b6c346aa6c76a4cad3679c16f50","unresolved":false,"context_lines":[{"line_number":277,"context_line":"                instance.memory"},{"line_number":278,"context_line":"            )"},{"line_number":279,"context_line":""},{"line_number":280,"context_line":"            if source_node.hostname in self.node_utilization_cache:"},{"line_number":281,"context_line":"                s \u003d self.node_utilization_cache[source_node.hostname]"},{"line_number":282,"context_line":"                self.node_utilization_cache[source_node.hostname] \u003d ("},{"line_number":283,"context_line":"                    s[0] - instance_util[\u0027cpu\u0027],"}],"source_content_type":"text/x-python","patch_set":2,"id":"bb08fe90_ba04d0be","line":280,"updated":"2026-06-24 13:08:06.000000000","message":"The cache invalidation pattern in add_migration uses short variable names \u0027s\u0027 and \u0027d\u0027 for source and destination cache entries, reducing readability.\n\n**Severity**: SUGGESTION | **Confidence**: 0.8\n\n**Benefit**: Using descriptive variable names like \u0027src_cached\u0027 and \u0027dst_cached\u0027 improves maintainability, especially since the same pattern is duplicated in optimize_solution.\n\n**Recommendation**:\nRename \u0027s\u0027 to \u0027src_cached\u0027 and \u0027d\u0027 to \u0027dst_cached\u0027 in both add_migration and optimize_solution. If a shared helper method is extracted (per the critical issue), this becomes moot.","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"abf8bbcfd7a4ec21fee93d452b8eb09e67825b3f","unresolved":false,"context_lines":[{"line_number":277,"context_line":"                instance.memory"},{"line_number":278,"context_line":"            )"},{"line_number":279,"context_line":""},{"line_number":280,"context_line":"            if source_node.hostname in self.node_utilization_cache:"},{"line_number":281,"context_line":"                s \u003d self.node_utilization_cache[source_node.hostname]"},{"line_number":282,"context_line":"                self.node_utilization_cache[source_node.hostname] \u003d ("},{"line_number":283,"context_line":"                    s[0] - instance_util[\u0027cpu\u0027],"}],"source_content_type":"text/x-python","patch_set":2,"id":"f8ec3649_6fb4d697","line":280,"in_reply_to":"bb08fe90_ba04d0be","updated":"2026-06-30 15:02:13.000000000","message":"done","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"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":"d22bbdb7b8be8b6c346aa6c76a4cad3679c16f50","unresolved":false,"context_lines":[{"line_number":400,"context_line":"        :param aggr: string"},{"line_number":401,"context_line":"        :return: dict(cpu(number of cores used), ram(MB used), disk(B used))"},{"line_number":402,"context_line":"        \"\"\""},{"line_number":403,"context_line":"        if node.hostname in self.node_utilization_cache:"},{"line_number":404,"context_line":"            node_cpu_util, node_ram_util, node_disk_util \u003d ("},{"line_number":405,"context_line":"                self.node_utilization_cache[node.hostname]"},{"line_number":406,"context_line":"            )"}],"source_content_type":"text/x-python","patch_set":2,"id":"9522c3ec_005407d4","line":403,"updated":"2026-06-24 13:08:06.000000000","message":"Cache entries are stored as tuples (cpu, ram, disk) but get_node_utilization returns a dict with keys cpu/ram/disk. The tuple unpacking at lines 404-406 relies on positional ordering, making it fragile if the data shape changes.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: If someone adds a fourth metric or reorders the tuple construction, the positional unpacking will silently produce wrong results with no type error.\n\n**Suggestion**:\nConsider storing cache entries as dicts with named keys (e.g. {\u0027cpu\u0027: ..., \u0027ram\u0027: ..., \u0027disk\u0027: ...}) to match the return type of get_node_utilization, or use a namedtuple. This makes the data shape self-documenting and resistant to reordering bugs.","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"abf8bbcfd7a4ec21fee93d452b8eb09e67825b3f","unresolved":false,"context_lines":[{"line_number":400,"context_line":"        :param aggr: string"},{"line_number":401,"context_line":"        :return: dict(cpu(number of cores used), ram(MB used), disk(B used))"},{"line_number":402,"context_line":"        \"\"\""},{"line_number":403,"context_line":"        if node.hostname in self.node_utilization_cache:"},{"line_number":404,"context_line":"            node_cpu_util, node_ram_util, node_disk_util \u003d ("},{"line_number":405,"context_line":"                self.node_utilization_cache[node.hostname]"},{"line_number":406,"context_line":"            )"}],"source_content_type":"text/x-python","patch_set":2,"id":"703a4be7_4fe1244a","line":403,"in_reply_to":"9522c3ec_005407d4","updated":"2026-06-30 15:02:13.000000000","message":"makes sense, converted into a dict with `cpu`, `ram` and `disk` as keys.","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"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":"d22bbdb7b8be8b6c346aa6c76a4cad3679c16f50","unresolved":false,"context_lines":[{"line_number":608,"context_line":"                if self.compute_model.migrate_instance("},{"line_number":609,"context_line":"                    instance, dst_node, src_node"},{"line_number":610,"context_line":"                ):"},{"line_number":611,"context_line":"                    # add_migration will update the node_utilization_cache"},{"line_number":612,"context_line":"                    # so we need to adjust the values here for the deleted"},{"line_number":613,"context_line":"                    # actions so that the node_utilization_cache remains"},{"line_number":614,"context_line":"                    # consistent."}],"source_content_type":"text/x-python","patch_set":2,"id":"9571f463_1ce36674","line":611,"updated":"2026-06-24 13:08:06.000000000","message":"Cache invalidation logic is duplicated across add_migration and optimize_solution. The manual cache adjustment in optimize_solution inverts the sign convention from add_migration. If instance_util values differ between calls, the cache silently drifts from reality.\n\n**Severity**: CRITICAL | **Confidence**: 0.8\n\n**Risk**: Stale cache values cause incorrect node utilization calculations for all subsequent decisions (capacity checks, saturation detection, relative utilization), leading to suboptimal or invalid migration plans at runtime\n\n**Priority**: Before merge\n**Why This Matters**: The optimize_solution method collapses multi-hop migrations. If the cache is not perfectly reversed for collapsed actions before re-applying via add_migration, all downstream capacity decisions and efficacy indicators will use wrong utilization data.\n\n**Recommendation**:\nExtract the cache update into a single private method, e.g. _adjust_cache_for_migration(instance_util, src_hostname, dst_hostname), and call it from both add_migration and optimize_solution. This eliminates the duplicated tuple arithmetic and ensures the sign convention is always consistent. Add a unit test that exercises optimize_solution with cached nodes and verifies cache consistency after optimization.","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"abf8bbcfd7a4ec21fee93d452b8eb09e67825b3f","unresolved":false,"context_lines":[{"line_number":608,"context_line":"                if self.compute_model.migrate_instance("},{"line_number":609,"context_line":"                    instance, dst_node, src_node"},{"line_number":610,"context_line":"                ):"},{"line_number":611,"context_line":"                    # add_migration will update the node_utilization_cache"},{"line_number":612,"context_line":"                    # so we need to adjust the values here for the deleted"},{"line_number":613,"context_line":"                    # actions so that the node_utilization_cache remains"},{"line_number":614,"context_line":"                    # consistent."}],"source_content_type":"text/x-python","patch_set":2,"id":"72b12d54_f32431d5","line":611,"in_reply_to":"90481d68_e5bcab39","updated":"2026-06-30 15:02:13.000000000","message":"you are right. Also, wrt duplication between add_migration and optimize_solution., there is not duplication actually, it\u0027s doing reverse. Doing a helper private as mentioned and reversing destination and source may make it harder to read imo.","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"f8293ccc9bee37ce7b28921347631563c977b7dd","unresolved":false,"context_lines":[{"line_number":608,"context_line":"                if self.compute_model.migrate_instance("},{"line_number":609,"context_line":"                    instance, dst_node, src_node"},{"line_number":610,"context_line":"                ):"},{"line_number":611,"context_line":"                    # add_migration will update the node_utilization_cache"},{"line_number":612,"context_line":"                    # so we need to adjust the values here for the deleted"},{"line_number":613,"context_line":"                    # actions so that the node_utilization_cache remains"},{"line_number":614,"context_line":"                    # consistent."}],"source_content_type":"text/x-python","patch_set":2,"id":"90481d68_e5bcab39","line":611,"in_reply_to":"9571f463_1ce36674","updated":"2026-06-25 10:23:49.000000000","message":"IIUC this is not a problem currently since the instance utilization is cached as well, so it will not drift while the strategy runs","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"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":"d22bbdb7b8be8b6c346aa6c76a4cad3679c16f50","unresolved":false,"context_lines":[{"line_number":710,"context_line":"            self.get_available_compute_nodes().values(),"},{"line_number":711,"context_line":"            key\u003dlambda x: self.get_node_utilization(x)[\u0027cpu\u0027],"},{"line_number":712,"context_line":"        )"},{"line_number":713,"context_line":"        saturated_nodes \u003d set()"},{"line_number":714,"context_line":"        asc \u003d 0"},{"line_number":715,"context_line":"        for node in sorted_nodes:"},{"line_number":716,"context_line":"            instances \u003d sorted("}],"source_content_type":"text/x-python","patch_set":2,"id":"82d412cf_d2655276","line":713,"updated":"2026-06-24 13:08:06.000000000","message":"The saturated_nodes set in consolidation_phase tracks hostname strings but could benefit from a comment clarifying it uses hostnames (not UUIDs) for consistency with the cache keys.\n\n**Severity**: SUGGESTION | **Confidence**: 0.7\n\n**Benefit**: Prevents confusion between hostname-keyed collections (node_utilization_cache, saturated_nodes) and UUID-keyed collections (get_available_compute_nodes returns UUID keys).\n\n**Recommendation**:\nAdd a brief inline comment: # saturated_nodes tracks hostnames to match node_utilization_cache keys","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"abf8bbcfd7a4ec21fee93d452b8eb09e67825b3f","unresolved":false,"context_lines":[{"line_number":710,"context_line":"            self.get_available_compute_nodes().values(),"},{"line_number":711,"context_line":"            key\u003dlambda x: self.get_node_utilization(x)[\u0027cpu\u0027],"},{"line_number":712,"context_line":"        )"},{"line_number":713,"context_line":"        saturated_nodes \u003d set()"},{"line_number":714,"context_line":"        asc \u003d 0"},{"line_number":715,"context_line":"        for node in sorted_nodes:"},{"line_number":716,"context_line":"            instances \u003d sorted("}],"source_content_type":"text/x-python","patch_set":2,"id":"fa54706f_c824fcfc","line":713,"in_reply_to":"82d412cf_d2655276","updated":"2026-06-30 15:02:13.000000000","message":"done","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"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":"d22bbdb7b8be8b6c346aa6c76a4cad3679c16f50","unresolved":false,"context_lines":[{"line_number":745,"context_line":"                    else:"},{"line_number":746,"context_line":"                        node_util \u003d self.get_node_utilization(destination_node)"},{"line_number":747,"context_line":"                        node_cap \u003d self.get_node_capacity(destination_node)"},{"line_number":748,"context_line":"                        # For memory, it may happen that remaining memory is"},{"line_number":749,"context_line":"                        # less than the lower ram in flavors. That would mean"},{"line_number":750,"context_line":"                        # that the node is actually saturated but not detected"},{"line_number":751,"context_line":"                        # as such. For that case we are subsctracting 128MB"}],"source_content_type":"text/x-python","patch_set":2,"id":"61a04457_621bacb9","line":748,"updated":"2026-06-24 13:08:06.000000000","message":"The magic number 128 in metrics_subs is used for RAM saturation detection without a clear derivation. The comment explains the intent but the specific 128MB value is unexplained and the word \u0027subsctracting\u0027 is misspelled.\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: The saturation threshold is sensitive to this constant. If the smallest flavor RAM changes or clusters have different minimum flavor sizes, the 128MB heuristic may not apply, causing either premature saturation marking (skipping valid destinations) or failure to mark truly saturated nodes.\n\n**Suggestion**:\nFix the typo \u0027subsctracting\u0027 to \u0027subtracting\u0027. Extract 128 into a named constant (e.g. RAM_SATURATION_THRESHOLD_MB \u003d 128) with a comment explaining the derivation, or make it configurable via input_parameters. Consider documenting that 128MB is the assumed minimum flavor RAM overhead.","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"f8293ccc9bee37ce7b28921347631563c977b7dd","unresolved":true,"context_lines":[{"line_number":745,"context_line":"                    else:"},{"line_number":746,"context_line":"                        node_util \u003d self.get_node_utilization(destination_node)"},{"line_number":747,"context_line":"                        node_cap \u003d self.get_node_capacity(destination_node)"},{"line_number":748,"context_line":"                        # For memory, it may happen that remaining memory is"},{"line_number":749,"context_line":"                        # less than the lower ram in flavors. That would mean"},{"line_number":750,"context_line":"                        # that the node is actually saturated but not detected"},{"line_number":751,"context_line":"                        # as such. For that case we are subsctracting 128MB"}],"source_content_type":"text/x-python","patch_set":2,"id":"cef77762_1ffc2a9a","line":748,"in_reply_to":"61a04457_621bacb9","updated":"2026-06-25 10:23:49.000000000","message":"+1 for this personally. I don\u0027t think we need to make it configurable, but considering it\u0027s an arbitrary but reasonable value, we should document that explicitely, and preferably avoid naked constants","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"abf8bbcfd7a4ec21fee93d452b8eb09e67825b3f","unresolved":false,"context_lines":[{"line_number":745,"context_line":"                    else:"},{"line_number":746,"context_line":"                        node_util \u003d self.get_node_utilization(destination_node)"},{"line_number":747,"context_line":"                        node_cap \u003d self.get_node_capacity(destination_node)"},{"line_number":748,"context_line":"                        # For memory, it may happen that remaining memory is"},{"line_number":749,"context_line":"                        # less than the lower ram in flavors. That would mean"},{"line_number":750,"context_line":"                        # that the node is actually saturated but not detected"},{"line_number":751,"context_line":"                        # as such. For that case we are subsctracting 128MB"}],"source_content_type":"text/x-python","patch_set":2,"id":"2132a805_fd932c2d","line":748,"in_reply_to":"cef77762_1ffc2a9a","updated":"2026-06-30 15:02:13.000000000","message":"I hope new comment clarifies it.","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"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":"d22bbdb7b8be8b6c346aa6c76a4cad3679c16f50","unresolved":false,"context_lines":[{"line_number":786,"context_line":""},{"line_number":787,"context_line":"        # Offloading phase"},{"line_number":788,"context_line":"        self.offload_phase(cc)"},{"line_number":789,"context_line":"        LOG.info("},{"line_number":790,"context_line":"            \u0027Offload phase complete, migrations: %s\u0027, self.number_of_migrations"},{"line_number":791,"context_line":"        )"},{"line_number":792,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"d333af53_2b361ff3","line":789,"updated":"2026-06-24 13:08:06.000000000","message":"Several new LOG.info calls were added throughout execute() and phase methods. The change from LOG.debug(info) to LOG.info(info) at line 820 elevates the entire execution summary dict to INFO on every audit run, which may be noisy in automated audit pipelines.\n\n**Severity**: WARNING | **Confidence**: 0.7\n\n**Impact**: The phase-level logging is reasonable (one line per phase), but the summary dict at INFO level on every audit could produce excessive log volume in production environments running frequent audits.\n\n**Suggestion**:\nConsider keeping the final summary dict at LOG.debug and only logging the individual phase milestones (migration counts) at LOG.info. Alternatively, use LOG.info with a structured format string rather than logging the raw dict.","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"abf8bbcfd7a4ec21fee93d452b8eb09e67825b3f","unresolved":false,"context_lines":[{"line_number":786,"context_line":""},{"line_number":787,"context_line":"        # Offloading phase"},{"line_number":788,"context_line":"        self.offload_phase(cc)"},{"line_number":789,"context_line":"        LOG.info("},{"line_number":790,"context_line":"            \u0027Offload phase complete, migrations: %s\u0027, self.number_of_migrations"},{"line_number":791,"context_line":"        )"},{"line_number":792,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"92602042_7905cfdd","line":789,"in_reply_to":"d333af53_2b361ff3","updated":"2026-06-30 15:02:13.000000000","message":"I think the aded info messages are relevant and make sense to keep as info.","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"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":"d22bbdb7b8be8b6c346aa6c76a4cad3679c16f50","unresolved":false,"context_lines":[{"line_number":817,"context_line":"            \u0027relative_cluster_utilization_after\u0027: str(rcu_after),"},{"line_number":818,"context_line":"        }"},{"line_number":819,"context_line":""},{"line_number":820,"context_line":"        LOG.info(info)"},{"line_number":821,"context_line":""},{"line_number":822,"context_line":"    def post_execute(self):"},{"line_number":823,"context_line":"        self.solution.set_efficacy_indicators("}],"source_content_type":"text/x-python","patch_set":2,"id":"fa7fd83f_32d30aa4","line":820,"updated":"2026-06-24 13:08:06.000000000","message":"LOG.info(info) logs a raw dict object. While oslo_log can handle this, the quick rules recommend lazy %-style formatting for logging.\n\n**Severity**: WARNING | **Confidence**: 0.7\n\n**Impact**: Logging a dict directly means the string representation is computed eagerly and may not be consistent with the project\u0027s log formatting conventions.\n\n**Suggestion**:\nUse LOG.info(\u0027Strategy execution info: %s\u0027, info) for lazy evaluation, consistent with the other LOG.info calls added in this patch that use %-style formatting.","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"abf8bbcfd7a4ec21fee93d452b8eb09e67825b3f","unresolved":false,"context_lines":[{"line_number":817,"context_line":"            \u0027relative_cluster_utilization_after\u0027: str(rcu_after),"},{"line_number":818,"context_line":"        }"},{"line_number":819,"context_line":""},{"line_number":820,"context_line":"        LOG.info(info)"},{"line_number":821,"context_line":""},{"line_number":822,"context_line":"    def post_execute(self):"},{"line_number":823,"context_line":"        self.solution.set_efficacy_indicators("}],"source_content_type":"text/x-python","patch_set":2,"id":"55cc1d0e_9d725e3d","line":820,"in_reply_to":"fa7fd83f_32d30aa4","updated":"2026-06-30 15:02:13.000000000","message":"done","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"b7d7780131642798a26ec6b8cf0ed46886d89d76","unresolved":true,"context_lines":[{"line_number":280,"context_line":"            if source_node.hostname in self.node_utilization_cache:"},{"line_number":281,"context_line":"                source \u003d self.node_utilization_cache[source_node.hostname]"},{"line_number":282,"context_line":"                self.node_utilization_cache[source_node.hostname] \u003d dict("},{"line_number":283,"context_line":"                    cpu\u003dsource[\u0027cpu\u0027] - instance_util[\u0027cpu\u0027],"},{"line_number":284,"context_line":"                    ram\u003dsource[\u0027ram\u0027] - instance_util[\u0027ram\u0027],"},{"line_number":285,"context_line":"                    disk\u003dsource[\u0027disk\u0027] - instance_util[\u0027disk\u0027],"},{"line_number":286,"context_line":"                )"},{"line_number":287,"context_line":"            if destination_node.hostname in self.node_utilization_cache:"},{"line_number":288,"context_line":"                dest \u003d self.node_utilization_cache[destination_node.hostname]"}],"source_content_type":"text/x-python","patch_set":3,"id":"c9795887_2944e7e0","line":285,"range":{"start_line":283,"start_character":0,"end_line":285,"end_character":63},"updated":"2026-07-20 17:53:35.000000000","message":"I think we need to guarantee that we don\u0027t get negative numbers here, and set to 0 in case this results in value \u003c 0.","commit_id":"3b4afbb3bef3ef29b6c6944361910c61323c107b"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"79b3548e63ca1b87c488c6a427a9c18bccd3a6f3","unresolved":false,"context_lines":[{"line_number":280,"context_line":"            if source_node.hostname in self.node_utilization_cache:"},{"line_number":281,"context_line":"                source \u003d self.node_utilization_cache[source_node.hostname]"},{"line_number":282,"context_line":"                self.node_utilization_cache[source_node.hostname] \u003d dict("},{"line_number":283,"context_line":"                    cpu\u003dsource[\u0027cpu\u0027] - instance_util[\u0027cpu\u0027],"},{"line_number":284,"context_line":"                    ram\u003dsource[\u0027ram\u0027] - instance_util[\u0027ram\u0027],"},{"line_number":285,"context_line":"                    disk\u003dsource[\u0027disk\u0027] - instance_util[\u0027disk\u0027],"},{"line_number":286,"context_line":"                )"},{"line_number":287,"context_line":"            if destination_node.hostname in self.node_utilization_cache:"},{"line_number":288,"context_line":"                dest \u003d self.node_utilization_cache[destination_node.hostname]"}],"source_content_type":"text/x-python","patch_set":3,"id":"c3a82a3a_3c938f5c","line":285,"range":{"start_line":283,"start_character":0,"end_line":285,"end_character":63},"in_reply_to":"54b94129_9597789f","updated":"2026-07-23 06:20:14.000000000","message":"Done","commit_id":"3b4afbb3bef3ef29b6c6944361910c61323c107b"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"ed683b284de2694633794b523995cc33f5a8d7bc","unresolved":true,"context_lines":[{"line_number":280,"context_line":"            if source_node.hostname in self.node_utilization_cache:"},{"line_number":281,"context_line":"                source \u003d self.node_utilization_cache[source_node.hostname]"},{"line_number":282,"context_line":"                self.node_utilization_cache[source_node.hostname] \u003d dict("},{"line_number":283,"context_line":"                    cpu\u003dsource[\u0027cpu\u0027] - instance_util[\u0027cpu\u0027],"},{"line_number":284,"context_line":"                    ram\u003dsource[\u0027ram\u0027] - instance_util[\u0027ram\u0027],"},{"line_number":285,"context_line":"                    disk\u003dsource[\u0027disk\u0027] - instance_util[\u0027disk\u0027],"},{"line_number":286,"context_line":"                )"},{"line_number":287,"context_line":"            if destination_node.hostname in self.node_utilization_cache:"},{"line_number":288,"context_line":"                dest \u003d self.node_utilization_cache[destination_node.hostname]"}],"source_content_type":"text/x-python","patch_set":3,"id":"54b94129_9597789f","line":285,"range":{"start_line":283,"start_character":0,"end_line":285,"end_character":63},"in_reply_to":"c9795887_2944e7e0","updated":"2026-07-23 06:06:52.000000000","message":"Valid point. It\u0027s good to add a check to make sure resulting value is \u003e\u003d 0, in case metrics are inconsistent between instance and host in any case.","commit_id":"3b4afbb3bef3ef29b6c6944361910c61323c107b"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"b7d7780131642798a26ec6b8cf0ed46886d89d76","unresolved":true,"context_lines":[{"line_number":606,"context_line":"                )"},{"line_number":607,"context_line":"                if self.compute_model.migrate_instance("},{"line_number":608,"context_line":"                    instance, dst_node, src_node"},{"line_number":609,"context_line":"                ):"},{"line_number":610,"context_line":"                    # add_migration will update the node_utilization_cache"},{"line_number":611,"context_line":"                    # so we need to adjust the values here for the deleted"},{"line_number":612,"context_line":"                    # actions so that the node_utilization_cache remains"},{"line_number":613,"context_line":"                    # consistent."},{"line_number":614,"context_line":"                    instance_util \u003d self.get_instance_utilization(instance)"},{"line_number":615,"context_line":"                    if dst_node.hostname in self.node_utilization_cache:"},{"line_number":616,"context_line":"                        dest \u003d self.node_utilization_cache[dst_node.hostname]"}],"source_content_type":"text/x-python","patch_set":3,"id":"e4eaf759_12187d65","line":613,"range":{"start_line":609,"start_character":18,"end_line":613,"end_character":33},"updated":"2026-07-20 17:53:35.000000000","message":"I think that make sense to do this before, since A -\u003e B and B -\u003e C were already calculated, it is expected that C utilization is greater than instance_util. It will remove here, and add it again in add_migration.","commit_id":"3b4afbb3bef3ef29b6c6944361910c61323c107b"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ef286a258f75cc23d314926bda2af17411c00a6d","unresolved":false,"context_lines":[{"line_number":606,"context_line":"                )"},{"line_number":607,"context_line":"                if self.compute_model.migrate_instance("},{"line_number":608,"context_line":"                    instance, dst_node, src_node"},{"line_number":609,"context_line":"                ):"},{"line_number":610,"context_line":"                    # add_migration will update the node_utilization_cache"},{"line_number":611,"context_line":"                    # so we need to adjust the values here for the deleted"},{"line_number":612,"context_line":"                    # actions so that the node_utilization_cache remains"},{"line_number":613,"context_line":"                    # consistent."},{"line_number":614,"context_line":"                    instance_util \u003d self.get_instance_utilization(instance)"},{"line_number":615,"context_line":"                    if dst_node.hostname in self.node_utilization_cache:"},{"line_number":616,"context_line":"                        dest \u003d self.node_utilization_cache[dst_node.hostname]"}],"source_content_type":"text/x-python","patch_set":3,"id":"0f3c8845_da203547","line":613,"range":{"start_line":609,"start_character":18,"end_line":613,"end_character":33},"in_reply_to":"e4eaf759_12187d65","updated":"2026-07-23 20:03:49.000000000","message":"Done","commit_id":"3b4afbb3bef3ef29b6c6944361910c61323c107b"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"b7d7780131642798a26ec6b8cf0ed46886d89d76","unresolved":true,"context_lines":[{"line_number":614,"context_line":"                    instance_util \u003d self.get_instance_utilization(instance)"},{"line_number":615,"context_line":"                    if dst_node.hostname in self.node_utilization_cache:"},{"line_number":616,"context_line":"                        dest \u003d self.node_utilization_cache[dst_node.hostname]"},{"line_number":617,"context_line":"                        self.node_utilization_cache[dst_node.hostname] \u003d dict("},{"line_number":618,"context_line":"                            cpu\u003ddest[\u0027cpu\u0027] - instance_util[\u0027cpu\u0027],"},{"line_number":619,"context_line":"                            ram\u003ddest[\u0027ram\u0027] - instance_util[\u0027ram\u0027],"},{"line_number":620,"context_line":"                            disk\u003ddest[\u0027disk\u0027] - instance_util[\u0027disk\u0027],"},{"line_number":621,"context_line":"                        )"},{"line_number":622,"context_line":"                    if src_node.hostname in self.node_utilization_cache:"},{"line_number":623,"context_line":"                        source \u003d self.node_utilization_cache[src_node.hostname]"},{"line_number":624,"context_line":"                        self.node_utilization_cache[src_node.hostname] \u003d dict("}],"source_content_type":"text/x-python","patch_set":3,"id":"f730e812_49309e54","line":621,"range":{"start_line":617,"start_character":24,"end_line":621,"end_character":25},"updated":"2026-07-20 17:53:35.000000000","message":"same thing about negative numbers.","commit_id":"3b4afbb3bef3ef29b6c6944361910c61323c107b"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"79b3548e63ca1b87c488c6a427a9c18bccd3a6f3","unresolved":false,"context_lines":[{"line_number":614,"context_line":"                    instance_util \u003d self.get_instance_utilization(instance)"},{"line_number":615,"context_line":"                    if dst_node.hostname in self.node_utilization_cache:"},{"line_number":616,"context_line":"                        dest \u003d self.node_utilization_cache[dst_node.hostname]"},{"line_number":617,"context_line":"                        self.node_utilization_cache[dst_node.hostname] \u003d dict("},{"line_number":618,"context_line":"                            cpu\u003ddest[\u0027cpu\u0027] - instance_util[\u0027cpu\u0027],"},{"line_number":619,"context_line":"                            ram\u003ddest[\u0027ram\u0027] - instance_util[\u0027ram\u0027],"},{"line_number":620,"context_line":"                            disk\u003ddest[\u0027disk\u0027] - instance_util[\u0027disk\u0027],"},{"line_number":621,"context_line":"                        )"},{"line_number":622,"context_line":"                    if src_node.hostname in self.node_utilization_cache:"},{"line_number":623,"context_line":"                        source \u003d self.node_utilization_cache[src_node.hostname]"},{"line_number":624,"context_line":"                        self.node_utilization_cache[src_node.hostname] \u003d dict("}],"source_content_type":"text/x-python","patch_set":3,"id":"35c27ce3_d844187d","line":621,"range":{"start_line":617,"start_character":24,"end_line":621,"end_character":25},"in_reply_to":"f730e812_49309e54","updated":"2026-07-23 06:20:14.000000000","message":"Done","commit_id":"3b4afbb3bef3ef29b6c6944361910c61323c107b"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"b7d7780131642798a26ec6b8cf0ed46886d89d76","unresolved":true,"context_lines":[{"line_number":753,"context_line":"                        # saturation threshold is created with 128MB value"},{"line_number":754,"context_line":"                        # for memory which is a usual minimum ram size of"},{"line_number":755,"context_line":"                        # real world flavors"},{"line_number":756,"context_line":"                        saturation_limit \u003d {\u0027cpu\u0027: 0, \u0027ram\u0027: 128, \u0027disk\u0027: 0}"},{"line_number":757,"context_line":"                        for m in (\u0027cpu\u0027, \u0027ram\u0027, \u0027disk\u0027):"},{"line_number":758,"context_line":"                            if ("},{"line_number":759,"context_line":"                                node_util[m]"}],"source_content_type":"text/x-python","patch_set":3,"id":"7af21ea2_3f520659","line":756,"range":{"start_line":756,"start_character":24,"end_line":756,"end_character":76},"updated":"2026-07-20 17:53:35.000000000","message":"not sure if this is really going to optimize the strategy in real env, but it is fine to include it here.","commit_id":"3b4afbb3bef3ef29b6c6944361910c61323c107b"},{"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":"eb78aaec309e36c196f94419dbcc698de20d7b43","unresolved":false,"context_lines":[{"line_number":809,"context_line":""},{"line_number":810,"context_line":"        # disable unused nodes"},{"line_number":811,"context_line":"        self.disable_unused_nodes()"},{"line_number":812,"context_line":"        LOG.info(\u0027Disabled %s nodes\u0027, self.number_of_released_nodes)"},{"line_number":813,"context_line":""},{"line_number":814,"context_line":"        rcu_after \u003d self.get_relative_cluster_utilization()"},{"line_number":815,"context_line":"        info \u003d {"}],"source_content_type":"text/x-python","patch_set":3,"id":"9649bd93_d69e8f8f","line":812,"updated":"2026-06-30 15:18:10.000000000","message":"Misleading log: LOG.info(\u0027Disabled %s nodes\u0027, self.number_of_released_nodes) uses a NET count (disabled minus re-enabled), which can be negative. It is not the count of nodes disabled in disable_unused_nodes and accumulates across the entire strategy run.\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: Operators reading the log may see \u0027Disabled -1 nodes\u0027 and misunderstand the value. The counter is net released nodes (disabled minus re-enabled across the whole run), not the count of nodes disabled in disable_unused_nodes. This degrades observability for debugging consolidation at scale.\n\n**Suggestion**:\nReword the message to reflect what the value actually means, e.g. LOG.info(\u0027Net released nodes: %s\u0027, self.number_of_released_nodes) or LOG.info(\u0027Released %s compute nodes (net)\u0027, self.number_of_released_nodes). The efficacy indicator key \u0027released_compute_nodes_count\u0027 already uses the correct \u0027released\u0027 terminology.","commit_id":"3b4afbb3bef3ef29b6c6944361910c61323c107b"}],"watcher/tests/unit/decision_engine/model/data/scenario_2_with_metrics.xml":[{"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":"d22bbdb7b8be8b6c346aa6c76a4cad3679c16f50","unresolved":false,"context_lines":[{"line_number":1,"context_line":"\u003cModelRoot\u003e"},{"line_number":2,"context_line":"  \u003cComputeNode hostname\u003d\"hostname_0\" uuid\u003d\"Node_0\" id\u003d\"0\" state\u003d\"up\" status\u003d\"enabled\" vcpus\u003d\"16\" vcpu_reserved\u003d\"0\" vcpu_ratio\u003d\"1\" disk\u003d\"250\" disk_gb_reserved\u003d\"0\" disk_ratio\u003d\"1\" disk_capacity\u003d\"250\" memory\u003d\"64000\" memory_mb_reserved\u003d\"0\" memory_ratio\u003d\"1\"\u003e"},{"line_number":3,"context_line":"    \u003cInstance watcher_exclude\u003d\"False\" name\u003d\"\" state\u003d\"active\" uuid\u003d\"INSTANCE_0\" vcpus\u003d\"10\" disk\u003d\"20\" disk_capacity\u003d\"20\" memory\u003d\"2048\" metadata\u003d\u0027{\"optimize\": true,\"top\": \"floor\", \"nested\": {\"x\": \"y\"}}\u0027 project_id\u003d\"91FFFE30-78A0-4152-ACD2-8310FF274DC9\"/\u003e"},{"line_number":4,"context_line":"    \u003cInstance watcher_exclude\u003d\"False\" name\u003d\"\" state\u003d\"active\" uuid\u003d\"INSTANCE_1\" vcpus\u003d\"10\" disk\u003d\"20\" disk_capacity\u003d\"20\" memory\u003d\"2048\" metadata\u003d\u0027{\"optimize\": true,\"top\": \"floor\", \"nested\": {\"x\": \"y\"}}\u0027 project_id\u003d\"91FFFE30-78A0-4152-ACD2-8310FF274DC9\"/\u003e"}],"source_content_type":"application/xml","patch_set":2,"id":"9762a408_60da447d","line":1,"updated":"2026-06-24 13:08:06.000000000","message":"Memory units in test scenario fixtures changed from 64/2 to 64000/2048, which appears to fix a unit mismatch (values were previously in GB or some other unit, now in MB). This changes the semantics of shared test fixtures that may be used by other test classes.\n\n**Severity**: HIGH | **Confidence**: 0.8\n\n**Risk**: Other tests that load these scenario XML files may have assertions calibrated to the old memory values, causing test failures or silently masking regressions\n\n**Priority**: Before merge\n**Why This Matters**: The scenario XML files are shared test data. Changing memory from 64 to 64000 (and instance memory from 2 to 2048) is a 1000x change. Any test computing memory utilization, capacity ratios, or migration fitting on these scenarios will produce different results.\n\n**Recommendation**:\nRun the full watcher test suite to verify no other tests depend on the old memory values. Search for all references to scenario_2_with_metrics and scenario_3_with_metrics across the test suite. If any test breaks, either create dedicated fixtures for this change or update those test assertions in the same patch.","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"abf8bbcfd7a4ec21fee93d452b8eb09e67825b3f","unresolved":false,"context_lines":[{"line_number":1,"context_line":"\u003cModelRoot\u003e"},{"line_number":2,"context_line":"  \u003cComputeNode hostname\u003d\"hostname_0\" uuid\u003d\"Node_0\" id\u003d\"0\" state\u003d\"up\" status\u003d\"enabled\" vcpus\u003d\"16\" vcpu_reserved\u003d\"0\" vcpu_ratio\u003d\"1\" disk\u003d\"250\" disk_gb_reserved\u003d\"0\" disk_ratio\u003d\"1\" disk_capacity\u003d\"250\" memory\u003d\"64000\" memory_mb_reserved\u003d\"0\" memory_ratio\u003d\"1\"\u003e"},{"line_number":3,"context_line":"    \u003cInstance watcher_exclude\u003d\"False\" name\u003d\"\" state\u003d\"active\" uuid\u003d\"INSTANCE_0\" vcpus\u003d\"10\" disk\u003d\"20\" disk_capacity\u003d\"20\" memory\u003d\"2048\" metadata\u003d\u0027{\"optimize\": true,\"top\": \"floor\", \"nested\": {\"x\": \"y\"}}\u0027 project_id\u003d\"91FFFE30-78A0-4152-ACD2-8310FF274DC9\"/\u003e"},{"line_number":4,"context_line":"    \u003cInstance watcher_exclude\u003d\"False\" name\u003d\"\" state\u003d\"active\" uuid\u003d\"INSTANCE_1\" vcpus\u003d\"10\" disk\u003d\"20\" disk_capacity\u003d\"20\" memory\u003d\"2048\" metadata\u003d\u0027{\"optimize\": true,\"top\": \"floor\", \"nested\": {\"x\": \"y\"}}\u0027 project_id\u003d\"91FFFE30-78A0-4152-ACD2-8310FF274DC9\"/\u003e"}],"source_content_type":"application/xml","patch_set":2,"id":"b7634dca_a0b10a03","line":1,"in_reply_to":"9762a408_60da447d","updated":"2026-06-30 15:02:13.000000000","message":"all tests pass","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"}],"watcher/tests/unit/decision_engine/strategy/strategies/test_vm_workload_consolidation.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":"d22bbdb7b8be8b6c346aa6c76a4cad3679c16f50","unresolved":false,"context_lines":[{"line_number":513,"context_line":"        )"},{"line_number":514,"context_line":""},{"line_number":515,"context_line":"        result2 \u003d self.strategy.get_node_utilization(node_0)"},{"line_number":516,"context_line":"        self.assertEqual(result, result2)"},{"line_number":517,"context_line":""},{"line_number":518,"context_line":"    def test_node_utilization_cache_updated_after_migration(self):"},{"line_number":519,"context_line":"        model \u003d self.fake_c_cluster.generate_scenario_1()"}],"source_content_type":"text/x-python","patch_set":2,"id":"dd7f6c13_c9560f1a","line":516,"updated":"2026-06-24 13:08:06.000000000","message":"The new test test_node_utilization_cache_updated_after_migration tests add_migration directly but does not test cache consistency after optimize_solution, which is where the most complex cache invalidation logic lives.\n\n**Severity**: SUGGESTION | **Confidence**: 0.8\n\n**Benefit**: A test that sets up a multi-hop migration scenario and verifies cache consistency after optimize_solution would catch regressions in the most error-prone code path.\n\n**Recommendation**:\nAdd a test that creates multiple migrations for the same instance (e.g. Node_0 -\u003e Node_1 -\u003e Node_2), calls optimize_solution, and asserts the cache reflects only the final Node_0 -\u003e Node_2 migration.","commit_id":"6e511796e108d26a2e8fa71f12d485e7066d135b"}]}
