Skip to main content

flowey_lib_hvlite/_jobs/
setup_and_run_vmm_perf.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Set up, run, and publish a standalone VMM.Perf job.
5
6use crate::build_openvmm::OpenvmmOutput;
7use crate::build_vmm_perf::VmmPerfOutput;
8use crate::common::CommonArch;
9use crate::install_vmm_tests_external_deps::VmmTestsExternalDeps;
10use crate::install_vmm_tests_external_deps::VmmTestsExternalDepsLinux;
11use crate::install_vmm_tests_external_deps::VmmTestsExternalDepsWindows;
12use crate::run_vmm_perf::VmmPerfProfile;
13use flowey::node::prelude::*;
14use std::collections::BTreeMap;
15
16flowey_request! {
17    pub struct Params {
18        pub label: String,
19        pub runner: ReadVar<VmmPerfOutput>,
20        pub openvmm: ReadVar<OpenvmmOutput>,
21        pub profiles: Vec<VmmPerfProfile>,
22        pub vm_sizes_json: Option<String>,
23        pub parameters_json: Option<String>,
24        /// Local runtime archive override. CI always uses the pinned download.
25        pub runtime_archive: Option<ReadVar<PathBuf>>,
26        /// Local-only root directory. CI uses a job-local staging directory.
27        pub root_dir: Option<ReadVar<PathBuf>>,
28        pub hugetlb_2mb_overcommit_pages: Option<u64>,
29        pub done: WriteVar<SideEffect>,
30    }
31}
32
33new_simple_flow_node!(struct Node);
34
35impl SimpleFlowNode for Node {
36    type Request = Params;
37
38    fn imports(ctx: &mut ImportCtx<'_>) {
39        ctx.import::<crate::download_uefi_mu_msvm::Node>();
40        ctx.import::<crate::download_vmm_perf_runtime::Node>();
41        ctx.import::<crate::install_vmm_tests_external_deps::Node>();
42        ctx.import::<crate::run_vmm_perf::Node>();
43        ctx.import::<flowey_lib_common::publish_test_results::Node>();
44    }
45
46    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
47        let Params {
48            label,
49            runner,
50            openvmm,
51            profiles,
52            vm_sizes_json,
53            parameters_json,
54            runtime_archive,
55            root_dir,
56            hugetlb_2mb_overcommit_pages,
57            done,
58        } = request;
59
60        if root_dir.is_some() && !matches!(ctx.backend(), FlowBackend::Local) {
61            anyhow::bail!("custom VMM.Perf root directories are local-only");
62        }
63        if runtime_archive.is_some() && !matches!(ctx.backend(), FlowBackend::Local) {
64            anyhow::bail!("custom VMM.Perf runtime archives are local-only");
65        }
66
67        let external_deps = match ctx.platform() {
68            FlowPlatform::Windows => VmmTestsExternalDeps::Windows(VmmTestsExternalDepsWindows {
69                hyperv: true,
70                whp: true,
71                hardware_isolation: false,
72            }),
73            FlowPlatform::Linux(_) => VmmTestsExternalDeps::Linux(VmmTestsExternalDepsLinux {
74                hugetlb_2mb_overcommit_pages,
75                prepare_vhost_vsock: false,
76            }),
77            _ => anyhow::bail!("VMM.Perf is unsupported on this platform"),
78        };
79        ctx.config(crate::install_vmm_tests_external_deps::Config {
80            selections: Some(external_deps),
81            auto_install: None,
82        });
83        let pre_run_deps = vec![ctx.reqv(crate::install_vmm_tests_external_deps::Request::Install)];
84
85        let firmware = ctx.reqv(|v| crate::download_uefi_mu_msvm::Request::GetMsvmFd {
86            arch: CommonArch::X86_64,
87            msvm_fd: v,
88        });
89        let runtime_archive = match runtime_archive {
90            Some(runtime_archive) => runtime_archive,
91            None => ctx.reqv(|v| crate::download_vmm_perf_runtime::Request::Get {
92                arch: CommonArch::X86_64,
93                runtime_archive: v,
94            }),
95        };
96        let job_root = match ctx.backend() {
97            FlowBackend::Local => root_dir
98                .ok_or_else(|| anyhow::anyhow!("local VMM.Perf runs require a root directory"))?,
99            FlowBackend::Ado => ctx
100                .get_ado_variable(AdoRuntimeVar::PIPELINE_WORKSPACE)
101                .map(ctx, |root| PathBuf::from(root).join("vp")),
102            FlowBackend::Github => ctx
103                .get_gh_context_var()
104                .global()
105                .runner_temp()
106                .map(ctx, |root| PathBuf::from(root).join("vp")),
107        };
108        let output_dir = job_root.clone().map(ctx, |root| root.join("results"));
109        let temp_dir = Some(job_root.map(ctx, |root| root.join("t")));
110
111        let result = ctx.reqv(|v| crate::run_vmm_perf::Request {
112            runner,
113            openvmm,
114            firmware,
115            runtime_archive,
116            output_dir,
117            temp_dir,
118            profiles,
119            vm_sizes_json,
120            parameters_json,
121            pre_run_deps,
122            output: v,
123        });
124
125        let publish_done = if matches!(ctx.backend(), FlowBackend::Local) {
126            result.clone().into_side_effect()
127        } else {
128            let results_dir = result.clone().map(ctx, |result| result.results_dir);
129            let test_results = result.clone().map(ctx, |result| {
130                flowey_lib_common::run_cargo_nextest_run::TestResults {
131                    all_tests_passed: result.success,
132                    junit_xml: None,
133                }
134            });
135            ctx.reqv(|v| flowey_lib_common::publish_test_results::Request {
136                test_results,
137                test_label: label,
138                attachments: BTreeMap::from([("results".into(), (results_dir, false))]),
139                output_dir: None,
140                upload_logs_on_success: true,
141                done: v,
142            })
143        };
144
145        ctx.emit_rust_step("report VMM.Perf result", |ctx| {
146            let result = result.claim(ctx);
147            publish_done.claim(ctx);
148            done.claim(ctx);
149            move |rt| {
150                let result = rt.read(result);
151                anyhow::ensure!(
152                    result.success,
153                    "VMM.Perf failed with exit code {}",
154                    result
155                        .exit_code
156                        .map_or_else(|| "unknown".into(), |code| code.to_string())
157                );
158                Ok(())
159            }
160        });
161
162        Ok(())
163    }
164}