Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions docs/Functest-Engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ See [`lib/engines/functest/tests/cases/driver_sign_check.json`](../lib/engines/f

## Step Types

> Each step object must have **exactly one** step-type field (`guest_run`, `guest_run_file`, `guest_reboot`, `host_run`, `host_run_file`, `files_action`, `qmp_command`, `qmp_wait_event`, `barrier`, `set_variable`). All other fields are optional modifiers.
> Each step object must have **exactly one** step-type field (`guest_run`, `guest_run_file`, `guest_reboot`, `host_run`, `host_run_file`, `files_action`, `qmp_command`, `qmp_wait_event`, `wait_client_online`, `barrier`, `set_variable`). All other fields are optional modifiers.

### Common Step Fields

Expand All @@ -276,7 +276,7 @@ See [`lib/engines/functest/tests/cases/driver_sign_check.json`](../lib/engines/f
| `expected_output_contains` | The step fails if the output does not contain this string. Only for `guest_run`/`guest_run_file`/`host_run`/`host_run_file`. Checked against every client the step ran on. |
| `expected_output_matches` | The step fails if the output does not match this regex. Only for `guest_run`/`guest_run_file`/`host_run`/`host_run_file`. Checked against every client the step ran on. |
| `expected_output_matches_encoding` | Controls regex encoding options for `expected_output_matches`. Currently only `Regexp::NOENCODING` is supported (binary match). Do not set to use default encoding. |
| `clients` | Client ids (e.g. `[2]`) this step targets. Applies to `guest_run`/`guest_run_file`, `guest_reboot`, `files_action`, `qmp_command`, `qmp_wait_event`; `host_run`/`host_run_file` always run once on the host regardless. Omitted/empty broadcasts to every client booted for the test case. See [Multi-Client Support](#multi-client-support). |
| `clients` | Client ids (e.g. `[2]`) this step targets. Applies to `guest_run`/`guest_run_file`, `guest_reboot`, `files_action`, `qmp_command`, `qmp_wait_event`, `wait_client_online`; `host_run`/`host_run_file` always run once on the host regardless. Omitted/empty broadcasts to every client booted for the test case. See [Multi-Client Support](#multi-client-support). |

---

Expand Down Expand Up @@ -433,6 +433,22 @@ Blocks until one of the given QEMU events is received from the client VM. `event

---

### `wait_client_online`

Blocks until WinRM is reachable on the target client(s). Use after an intentional guest ACPI poweroff / QMP `SHUTDOWN` when the VM was started with functest `keep_alive` (QEMU restarts; Windows must finish booting before the next step or minidump collection).

```json
{
"desc": "Wait for client online after keep_alive QEMU restart",
"wait_client_online": true,
"timeout": 600
}
```

If the guest never returns, the step fails (and the run stops) — dump collection stays strict and is not skipped.

---

### `barrier`

A named synchronization point. It only logs the barrier name and does nothing else.
Expand Down
11 changes: 10 additions & 1 deletion lib/auxiliary/command_execution_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class CommandExecutionManager

STEP_TYPE_FIELDS = T.let(%i[
guest_run guest_run_file guest_reboot files_action host_run host_run_file
barrier set_variable qmp_command qmp_wait_event
barrier set_variable qmp_command qmp_wait_event wait_client_online
].freeze, T::Array[Symbol])

sig { params(init_opts: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
Expand Down Expand Up @@ -91,6 +91,7 @@ def execute(command_info, replacement: ReplacementMap.new)
execute_barrier(command_info) if command_info.barrier
result[:qmp_result] = execute_qmp_command(command_info, replacement) if command_info.qmp_command
result[:qmp_event] = execute_qmp_wait_event(command_info) if command_info.qmp_wait_event
execute_wait_client_online(command_info) if command_info.wait_client_online

result
end
Expand Down Expand Up @@ -295,6 +296,14 @@ def execute_qmp_wait_event(command_info)
outputs
end

sig { params(command_info: Models::CommandInfo).void }
def execute_wait_client_online(command_info)
target_machines(command_info).each do |machine_name|
@logger.info("Waiting for client #{machine_name} to come online")
@tools.wait_for_client_online(machine_name)
end
end
Comment on lines +299 to +305

sig do
params(
command_info: Models::CommandInfo,
Expand Down
5 changes: 4 additions & 1 deletion lib/engines/functest/functest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ def boot_clients(scope)
'in the platform configuration'
end

@project.setup_manager.run_functest_client(scope, client.name)
# Same as HCKTest run_clients(..., keep_alive: true): guest ACPI
# poweroff / QMP SHUTDOWN exits QEMU; restart so mid-batch cases
# (e.g. netkvm_hotplug_shutdown) can continue on a live client.
@project.setup_manager.run_functest_client(scope, client.name, { keep_alive: true })
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/engines/functest/step_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def step_type_set?(step, field)
value = step.public_send(field)
case field
when :files_action then value.any?
when :guest_reboot then value == true
when :guest_reboot, :wait_client_online then value == true
when :set_variable then value.is_a?(Hash) && !value.empty?
when :guest_run, :guest_run_file, :host_run, :host_run_file, :barrier
value.is_a?(String) ? !value.empty? : !value.nil?
Expand Down
6 changes: 6 additions & 0 deletions lib/engines/hcktest/tools.rb
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,12 @@ def restart_machine_and_wait(_machine)
raise ToolsHCKError, 'Unimplemented method: restart_machine_and_wait'
end

# Poll until WinRM is reachable. FunctestTools implements this; used by
# the wait_client_online step after ACPI poweroff + keep_alive restart.
def wait_for_client_online(_machine)
raise ToolsHCKError, 'Unimplemented method: wait_for_client_online'
end

def restart_machine(machine)
retries ||= 0
act_with_tools { _1.machine_shutdown(machine, restart: true) }
Expand Down
3 changes: 3 additions & 0 deletions lib/models/command_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ class CommandInfo < T::Struct
const :set_variable, T.nilable(T::Hash[String, String])
const :qmp_command, T.nilable(QmpCommandConfig)
const :qmp_wait_event, T.nilable(QmpWaitEventConfig)
# Block until WinRM is reachable again (e.g. after ACPI poweroff when
# the VM was started with keep_alive and QEMU has restarted).
const :wait_client_online, T::Boolean, default: false

const :expected_output_contains, T.nilable(String)
const :expected_output_matches, T.nilable(String)
Expand Down
Loading