)]}'
{"/COMMIT_MSG":[{"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":"cfc94284ced5e4d9ba52d1b9911d63f03c5ead31","unresolved":false,"context_lines":[{"line_number":1,"context_line":"Parent:     08a307c8 (Fix baremetal collector notification_endpoints returns None)"},{"line_number":2,"context_line":"Author:     jgilaber \u003cjgilaber@redhat.com\u003e"},{"line_number":3,"context_line":"AuthorDate: 2026-07-16 15:56:11 +0200"},{"line_number":4,"context_line":"Commit:     jgilaber \u003cjgilaber@redhat.com\u003e"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":4,"id":"95976958_fe44813b","line":1,"updated":"2026-09-17 10:11:34.000000000","message":"The permanent commit message contains two spelling defects: \u0027to get servers information for one node usings its id\u0027 should read \u0027using its id\u0027, and \u0027the running_vms attributed is deprecated\u0027 should read \u0027attribute is deprecated\u0027.\n\n**Severity**: SUGGESTION | **Confidence**: 0.9\n\n**Impact**: Minor readability defect in the permanent change history; meaning remains clear.\n\n**Recommendation**:\nAmend the commit message to \u0027using its id\u0027 and \u0027the running_vms attribute is deprecated\u0027.","commit_id":"6f9905b484d3b675e9b33016f025d8b70b1148dd"},{"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":"67964ebcd3750b93a40995d46466cd690907e2aa","unresolved":false,"context_lines":[{"line_number":1,"context_line":"Parent:     551d9500 (Fix baremetal collector notification_endpoints returns None)"},{"line_number":2,"context_line":"Author:     jgilaber \u003cjgilaber@redhat.com\u003e"},{"line_number":3,"context_line":"AuthorDate: 2026-07-16 15:56:11 +0200"},{"line_number":4,"context_line":"Commit:     jgilaber \u003cjgilaber@redhat.com\u003e"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":5,"id":"1db9f0ea_498d6734","line":1,"updated":"2026-09-18 11:50:08.000000000","message":"The commit body reads \u0027to be able to get servers information for one node usings its id, since the running_vms attributed is deprecated since microversion 2.88\u0027. \u0027usings\u0027 should be \u0027using\u0027 and \u0027attributed\u0027 should be \u0027attribute\u0027.\n\n**Severity**: SUGGESTION | **Confidence**: 0.95\n\n**Impact**: Permanent change history contains spelling errors; the intended meaning remains obvious.\n\n**Recommendation**:\nReword to \u0027...for one node using its id, since the running_vms attribute is deprecated since microversion 2.88\u0027. While editing, consider noting the microversion requirement from CF-001.","commit_id":"9dd3cb35df5512414387018d0ff7cb03b1ddc2df"}],"watcher/common/metal_helper/ironic.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":"67964ebcd3750b93a40995d46466cd690907e2aa","unresolved":false,"context_lines":[{"line_number":83,"context_line":"                )"},{"line_number":84,"context_line":"                continue"},{"line_number":85,"context_line":""},{"line_number":86,"context_line":"            hypervisor_node \u003d self.nova_client.get_compute_node_by_uuid("},{"line_number":87,"context_line":"                hypervisor_id, with_servers\u003dwith_servers"},{"line_number":88,"context_line":"            )"},{"line_number":89,"context_line":"            if hypervisor_node is None:"}],"source_content_type":"text/x-python","patch_set":5,"id":"182cf15c_ac4dc71a","line":86,"updated":"2026-09-18 11:50:08.000000000","message":"get_compute_node_by_uuid(with_servers\u003dTrue) lists the entire hypervisor inventory with per-hypervisor server lists and filters locally by uuid. IronicHelper.list_compute_nodes invokes it once per ironic node, and saving_energy.get_hosts_pool requests with_servers\u003dTrue on every audit, so an N-node deployment performs N full-inventory calls, each returning N hypervisors plus their server lists (O(N^2) transferred data). Before this change each node required a single-record get_hypervisor response.\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: The decision-engine audit path and the applier action path now transfer an amount of data that grows with the square of the number of hypervisors; on a large baremetal/compute fleet each saving_energy audit and each power-state action costs a full inventory download including server lists.\n\n**Suggestion**:\nFetch the inventory once and match locally: have IronicHelper.list_compute_nodes call hypervisors(with_servers\u003dTrue) a single time and match the ironic nodes\u0027 compute_node_id values against that list, keeping the single-node method (or an indexed lookup) for change_node_power_state.","commit_id":"9dd3cb35df5512414387018d0ff7cb03b1ddc2df"}],"watcher/common/nova_helper.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":"cfc94284ced5e4d9ba52d1b9911d63f03c5ead31","unresolved":false,"context_lines":[{"line_number":506,"context_line":"            hypervisor"},{"line_number":507,"context_line":"        :returns: Hypervisor wrapper object if found, None if not found"},{"line_number":508,"context_line":"        \"\"\""},{"line_number":509,"context_line":"        if with_servers:"},{"line_number":510,"context_line":"            # the get_hypervisor method does not return the hypervisor\u0027s"},{"line_number":511,"context_line":"            # servers information, we can get it by doing a scan of the"},{"line_number":512,"context_line":"            # hypervisor list with the \u0027with_servers\u0027 option set and matching"}],"source_content_type":"text/x-python","patch_set":4,"id":"c8c07d31_d7114e58","line":509,"updated":"2026-09-17 10:11:34.000000000","message":"The new with_servers path in get_compute_node_by_uuid retrieves the full hypervisor listing with embedded per-hypervisor server lists and linear-scans it for one uuid. IronicHelper.list_compute_nodes calls it once per ironic node, so listing N baremetal nodes performs N full-fleet listings, each transferring all M hypervisors plus their servers. The previous code did a single-hypervisor GET per node.\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: Saving-energy audits over large ironic deployments now transfer and parse O(N x M) hypervisor/server records instead of O(N), and every power-state action fetches unrelated hypervisors\u0027 server data. On large fleets this adds noticeable Nova load and audit latency on every run.\n\n**Suggestion**:\nIn IronicHelper.list_compute_nodes, request hypervisors(with_servers\u003dTrue) once and match each node\u0027s compute_node_id against that in-memory list (a small helper or a batch variant), instead of calling get_compute_node_by_uuid(with_servers\u003dTrue) per node.","commit_id":"6f9905b484d3b675e9b33016f025d8b70b1148dd"},{"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":"67964ebcd3750b93a40995d46466cd690907e2aa","unresolved":false,"context_lines":[{"line_number":506,"context_line":"            hypervisor"},{"line_number":507,"context_line":"        :returns: Hypervisor wrapper object if found, None if not found"},{"line_number":508,"context_line":"        \"\"\""},{"line_number":509,"context_line":"        if with_servers:"},{"line_number":510,"context_line":"            # the get_hypervisor method does not return the hypervisor\u0027s"},{"line_number":511,"context_line":"            # servers information, we can get it by doing a scan of the"},{"line_number":512,"context_line":"            # hypervisor list with the \u0027with_servers\u0027 option set and matching"}],"source_content_type":"text/x-python","patch_set":5,"id":"41239a9b_67206231","line":509,"updated":"2026-09-18 11:50:08.000000000","message":"The new with_servers path calls self.connection.compute.hypervisors(with_servers\u003dTrue) and counts servers. Nova only honors the with_servers query parameter at compute microversion 2.88 or later (the same microversion the commit message cites as deprecating running_vms); below 2.88 the parameter is silently ignored and hypervisors are returned without a servers list. Watcher creates its openstacksdk connection without any compute_api_version, so the SDK default (2.1) applies. The [nova]/api_version option (default 2.56) is only used by check_min_nova_api_version and is_pinned_az_available; it is never passed to the SDK connection. Hypervisor.from_openstacksdk then normalizes the missing servers to [], so len(compute_node.servers or []) is always 0.\n\n**Severity**: HIGH | **Confidence**: 0.75\n\n**Impact**: When servers is never populated, every hypervisor looks like it has 0 running VMs: saving_energy.get_hosts_pool classifies hosts with VMs as free and powers them off, and the change_node_power_state guard passes, hard power-offing compute nodes with running VMs. This defeats exactly the guard this change implements, and the failure is silent because the node is still found in the scan.\n\n**Priority**: Before merge\n**Recommendation**:\nRequest the required microversion from the nova SDK connection, e.g. pass compute_api_version when creating the connection and derive/enforce it from configuration (raising the enforced minimum to 2.88 for deployments using this feature). Update the get_compute_node_by_uuid docstring, which still claims \u0027None if not found\u0027 although the with_servers path raises ComputeNodeNotFound, and note the microversion requirement for operators.","commit_id":"9dd3cb35df5512414387018d0ff7cb03b1ddc2df"}],"watcher/decision_engine/strategy/strategies/saving_energy.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":"0642d888f7d4e2e92206715ffe082810147330db","unresolved":false,"context_lines":[{"line_number":184,"context_line":"                )"},{"line_number":185,"context_line":"                continue"},{"line_number":186,"context_line":"            else:"},{"line_number":187,"context_line":"                running_vms \u003d len(hypervisor_node.servers or [])"},{"line_number":188,"context_line":"                if running_vms \u003d\u003d 0:"},{"line_number":189,"context_line":"                    power_state \u003d node.get_power_state()"},{"line_number":190,"context_line":"                    if power_state \u003d\u003d metal_constants.PowerState.ON:"}],"source_content_type":"text/x-python","patch_set":2,"id":"822bccd0_6e505cce","line":187,"updated":"2026-07-31 10:45:10.000000000","message":"The change replaces the old running_vms integer with len(hypervisor_node.servers or []) to count running VMs. However, the Hypervisor dataclass\u0027s servers field is populated from nova_hypervisor.servers, which is only filled when with_servers\u003dTrue is passed to the Nova API. None of the metal_helpe...\n\n**Severity**: CRITICAL | **Confidence**: 0.9\n\n**Risk**: In saving_energy strategy: all nodes will be treated as having 0 running VMs. Nodes with running VMs will be classified as free and may be powered off, causing VM downtime. In change_node_power_state action: the safety check before powering off will always pass, allowing nodes with running VMs to...\n\n**Priority**: Immediate\n**Why This Matters**: In saving_energy strategy: all nodes will be treated as having 0 running VMs. Nodes with running VMs will be classified as free and may be powered off, causing VM downtime. In change_node_power_state action: the safety check before powering off will always pass, allowing nodes with running VMs to...\n\n**Recommendation**:\nAdd a running_vms field to the Hypervisor dataclass mapped from nova_hypervisor.running_vms (which is always included in the Nova API hypervisor response). Then use hypervisor_node.running_vms directly instead of len(servers). Alternatively, ensure all metal_helper code paths pass with_servers\u003dTrue to get_compute_node_by_name/get_compute_node_list, but this adds API overhead and is less efficient than using the already-present running_vms field.","commit_id":"e5df79b9a4d92c128f006e73a70a1b30aa033e32"},{"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":"2f24025917a75867fc3638982671877cdb84ec9a","unresolved":false,"context_lines":[{"line_number":184,"context_line":"                )"},{"line_number":185,"context_line":"                continue"},{"line_number":186,"context_line":"            else:"},{"line_number":187,"context_line":"                running_vms \u003d len(hypervisor_node.servers or [])"},{"line_number":188,"context_line":"                if running_vms \u003d\u003d 0:"},{"line_number":189,"context_line":"                    power_state \u003d node.get_power_state()"},{"line_number":190,"context_line":"                    if power_state \u003d\u003d metal_constants.PowerState.ON:"}],"source_content_type":"text/x-python","patch_set":3,"id":"e74d7b65_1d2cd6e9","line":187,"updated":"2026-09-16 16:37:43.000000000","message":"The fix replaces nova\u0027s running_vms stat with len(hypervisor_node.servers or []) in SavingEnergy.get_hosts_pool() and ChangeNodePowerState._node_manage_power(), but the Hypervisor objects on these paths are fetched without the servers attribute: NovaHelper.get_compute_node_list() calls compute.hypervisors(details\u003dTrue) with no with_servers, get_compute_node_by_uuid() calls compute.get_hypervisor(), and get_compute_node_by_name() defaults with_servers\u003dFalse. Nova only includes hypervisor \u0027servers\u0027 when explicitly requested (with_servers, microversion 2.53+), and Hypervisor.from_openstacksdk() normalizes the missing value to []. In production the count is therefore always 0, while the updated fake_metal_helper fabricates a servers list, hiding this from the unit tests.\n\n**Severity**: HIGH | **Confidence**: 0.78\n\n**Impact**: Every node is classified as having zero running VMs. SavingEnergy fills only the free pools (with_vms_node_pool stays empty), so save_energy() can add change_node_power_state OFF actions for hosts that are actually running instances, and the applier guard that must refuse to power off a node with running VMs always passes. The crash fixed here becomes silent misbehavior that can power off baremetal compute hosts running workloads, disrupting guest instances.\n\n**Priority**: Before merge\n**Recommendation**:\nMake the running VM source real: either populate servers in the NovaHelper paths feeding the metal helpers (e.g., pass with_servers\u003dTrue in get_compute_node_list()/get_compute_node_by_uuid(), noting microversion 2.53+), or extend the Hypervisor dataclass to carry nova\u0027s running_vms (present in every hypervisor response) and use it here, or fall back to a populated indicator such as vcpus_used. Also add a test that builds Hypervisor via from_openstacksdk() from a servers-less response and asserts busy nodes are neither pooled as free nor powered off.","commit_id":"f04470a8a7f95193a608b0592922ec2820d3fadc"},{"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":"cfc94284ced5e4d9ba52d1b9911d63f03c5ead31","unresolved":false,"context_lines":[{"line_number":163,"context_line":""},{"line_number":164,"context_line":"        \"\"\""},{"line_number":165,"context_line":""},{"line_number":166,"context_line":"        node_list \u003d self.metal_helper.list_compute_nodes(with_servers\u003dTrue)"},{"line_number":167,"context_line":"        for node in node_list:"},{"line_number":168,"context_line":"            hypervisor_node \u003d node.get_hypervisor_node()"},{"line_number":169,"context_line":"            host_name \u003d hypervisor_node.service_host"}],"source_content_type":"text/x-python","patch_set":4,"id":"2cce3bb8_429c2b41","line":166,"updated":"2026-09-17 10:11:34.000000000","message":"The change passes with_servers\u003dTrue to metal helper methods, but only IronicHelper was updated. metal_helper_factory.get_helper() returns MaasHelper when CONF.maas_client.url is set, and MaasHelper.list_compute_nodes(self) and MaasHelper.get_node(self, node_id) do not accept the keyword. On a MaaS deployment both call sites raise TypeError. The abstract BaseMetalHelper signatures were also left unchanged.\n\n**Severity**: HIGH | **Confidence**: 0.85\n\n**Impact**: With MaaS configured, saving_energy audits and change_node_power_state actions fail immediately with TypeError (\u0027unexpected keyword argument with_servers\u0027) instead of running; the bug this change closes stays unfixed for that backend. A signature-only \u0027fix\u0027 that ignores the flag would be worse: MaaS nodes get servers data never populated, so running_vms would compute as 0 and nodes with VMs could be powered off.\n\n**Priority**: Before merge\n**Recommendation**:\nExtend the metal helper interface for with_servers: add the parameter to MaasHelper.list_compute_nodes/get_node and the BaseMetalHelper abstract methods, and in MaasHelper populate servers data (e.g. pass with_servers/servers through its nova lookups) so server counts are real rather than always empty.","commit_id":"6f9905b484d3b675e9b33016f025d8b70b1148dd"}]}
