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::CommonTriple;
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 flowey::node::prelude::*;
13use std::collections::BTreeMap;
14
15flowey_request! {
16    pub struct Params {
17        pub label: String,
18        pub target: CommonTriple,
19        pub runner: ReadVar<VmmPerfOutput>,
20        pub openvmm: ReadVar<OpenvmmOutput>,
21        pub profiles: Vec<String>,
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            target,
50            runner,
51            openvmm,
52            profiles,
53            vm_sizes_json,
54            parameters_json,
55            runtime_archive,
56            root_dir,
57            hugetlb_2mb_overcommit_pages,
58            done,
59        } = request;
60
61        if root_dir.is_some() && !matches!(ctx.backend(), FlowBackend::Local) {
62            anyhow::bail!("custom VMM.Perf root directories are local-only");
63        }
64        if runtime_archive.is_some() && !matches!(ctx.backend(), FlowBackend::Local) {
65            anyhow::bail!("custom VMM.Perf runtime archives are local-only");
66        }
67
68        let external_deps = match ctx.platform() {
69            FlowPlatform::Windows => VmmTestsExternalDeps::Windows(VmmTestsExternalDepsWindows {
70                hyperv: true,
71                whp: true,
72                hardware_isolation: false,
73            }),
74            FlowPlatform::Linux(_) => VmmTestsExternalDeps::Linux(VmmTestsExternalDepsLinux {
75                hugetlb_2mb_overcommit_pages,
76                prepare_vhost_vsock: false,
77            }),
78            _ => anyhow::bail!("VMM.Perf is unsupported on this platform"),
79        };
80        ctx.config(crate::install_vmm_tests_external_deps::Config {
81            selections: Some(external_deps),
82            auto_install: None,
83        });
84        let pre_run_deps = vec![ctx.reqv(crate::install_vmm_tests_external_deps::Request::Install)];
85
86        let target_is_windows = matches!(
87            target.as_triple().operating_system,
88            target_lexicon::OperatingSystem::Windows
89        );
90        anyhow::ensure!(
91            target_is_windows == matches!(ctx.platform(), FlowPlatform::Windows),
92            "VMM.Perf target {target} does not match job platform {:?}",
93            ctx.platform()
94        );
95        let arch = target.common_arch()?;
96        let firmware = ctx.reqv(|v| crate::download_uefi_mu_msvm::Request::GetMsvmFd {
97            arch,
98            flavor: None,
99            msvm_fd: v,
100        });
101        let runtime_archive = match runtime_archive {
102            Some(runtime_archive) => runtime_archive,
103            None => ctx.reqv(|v| crate::download_vmm_perf_runtime::Request::Get {
104                arch,
105                runtime_archive: v,
106            }),
107        };
108        let job_root = match ctx.backend() {
109            FlowBackend::Local => root_dir
110                .ok_or_else(|| anyhow::anyhow!("local VMM.Perf runs require a root directory"))?,
111            FlowBackend::Ado => ctx
112                .get_ado_variable(AdoRuntimeVar::PIPELINE_WORKSPACE)
113                .map(ctx, |root| PathBuf::from(root).join("vp")),
114            FlowBackend::Github => ctx
115                .get_gh_context_var()
116                .global()
117                .runner_temp()
118                .map(ctx, |root| PathBuf::from(root).join("vp")),
119        };
120        let output_dir = job_root.clone().map(ctx, |root| root.join("results"));
121        let temp_dir = Some(job_root.map(ctx, |root| root.join("t")));
122
123        let result = ctx.reqv(|v| crate::run_vmm_perf::Request {
124            runner,
125            openvmm,
126            firmware,
127            runtime_archive,
128            output_dir,
129            temp_dir,
130            profiles,
131            vm_sizes_json,
132            parameters_json,
133            pre_run_deps,
134            output: v,
135        });
136
137        let publish_done = if matches!(ctx.backend(), FlowBackend::Local) {
138            result.clone().into_side_effect()
139        } else {
140            let results_dir = result.clone().map(ctx, |result| result.results_dir);
141            let test_results = result.clone().map(ctx, |result| {
142                flowey_lib_common::run_cargo_nextest_run::TestResults {
143                    all_tests_passed: result.success,
144                    junit_xml: None,
145                }
146            });
147            ctx.reqv(|v| flowey_lib_common::publish_test_results::Request {
148                test_results,
149                test_label: label,
150                attachments: BTreeMap::from([("results".into(), (results_dir, false))]),
151                output_dir: None,
152                upload_logs_on_success: true,
153                done: v,
154            })
155        };
156
157        ctx.emit_rust_step("report VMM.Perf result", |ctx| {
158            let result = result.claim(ctx);
159            publish_done.claim(ctx);
160            done.claim(ctx);
161            move |rt| {
162                let result = rt.read(result);
163                anyhow::ensure!(
164                    result.success,
165                    "VMM.Perf failed with exit code {}",
166                    result
167                        .exit_code
168                        .map_or_else(|| "unknown".into(), |code| code.to_string())
169                );
170                Ok(())
171            }
172        });
173
174        Ok(())
175    }
176}