Skip to main content

flowey_lib_hvlite/
test_nextest_vmm_tests_archive.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Run cargo-nextest based VMM tests from a pre-built archive.
5//!
6//! NOTE: The caller is responsible for setting `extra_env` and
7//! `pre_run_deps` to ensure that all tests filtered by
8//! `nextest_filter_expr` are able to run successfully.
9
10use crate::build_nextest_vmm_tests::NextestVmmTestsArchive;
11use crate::run_cargo_nextest_run::NextestProfile;
12use flowey::node::prelude::*;
13use flowey_lib_common::run_cargo_nextest_run::TestResults;
14use std::collections::BTreeMap;
15
16flowey_request! {
17    pub struct Request {
18        /// Pre-built VMM tests nextest archive
19        pub nextest_archive_file: ReadVar<NextestVmmTestsArchive>,
20        /// nextest filter expression for what VMM tests to run
21        pub nextest_filter_expr: Option<String>,
22        /// Nextest profile to use when running the source code
23        pub nextest_profile: NextestProfile,
24        /// Nextest working directory (defaults to repo root)
25        pub nextest_working_dir: Option<ReadVar<PathBuf>>,
26        /// Nextest configuration file (defaults to config in repo)
27        pub nextest_config_file: Option<ReadVar<PathBuf>>,
28        /// Optionally provide the nextest bin to use
29        pub nextest_bin: Option<ReadVar<PathBuf>>,
30        /// Target for the tests to run on
31        pub target: Option<ReadVar<target_lexicon::Triple>>,
32        /// Additional env vars set when executing the tests.
33        pub extra_env: ReadVar<BTreeMap<String, String>>,
34        /// Wait for specified side-effects to resolve before building / running
35        /// any tests. (e.g: to allow for some ambient packages / dependencies
36        /// to get installed).
37        pub pre_run_deps: Vec<ReadVar<SideEffect>>,
38        /// If set, configure this 2 MiB hugetlb surplus page overcommit limit before running tests.
39        pub hugetlb_2mb_overcommit_pages: Option<u64>,
40        /// Results of running the tests
41        pub results: WriteVar<TestResults>,
42    }
43}
44
45new_simple_flow_node!(struct Node);
46
47impl SimpleFlowNode for Node {
48    type Request = Request;
49
50    fn imports(ctx: &mut ImportCtx<'_>) {
51        ctx.import::<crate::run_cargo_nextest_run::Node>();
52    }
53
54    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
55        let Request {
56            nextest_archive_file,
57            nextest_filter_expr,
58            nextest_profile,
59            nextest_working_dir,
60            nextest_config_file,
61            nextest_bin,
62            target,
63            mut extra_env,
64            mut pre_run_deps,
65            hugetlb_2mb_overcommit_pages,
66            results,
67        } = request;
68
69        if hugetlb_2mb_overcommit_pages.is_some() {
70            extra_env = extra_env.map(ctx, |mut env| {
71                env.insert("OPENVMM_REQUIRE_2MB_HUGETLB".into(), "1".into());
72                env
73            });
74        }
75
76        if !matches!(ctx.backend(), FlowBackend::Local)
77            && matches!(ctx.platform(), FlowPlatform::Linux(_))
78        {
79            pre_run_deps.push({
80                ctx.emit_rust_step("ensure hypervisor device is accessible", |_| {
81                    |rt| {
82                        // Make whichever hypervisor device exists accessible.
83                        // KVM machines have /dev/kvm, MSHV machines have /dev/mshv.
84                        if Path::new("/dev/kvm").exists() {
85                            flowey::shell_cmd!(rt, "sudo chmod a+rw /dev/kvm").run()?;
86                        }
87                        if Path::new("/dev/mshv").exists() {
88                            flowey::shell_cmd!(rt, "sudo chmod a+rw /dev/mshv").run()?;
89                        }
90                        Ok(())
91                    }
92                })
93            });
94
95            if let Some(overcommit_pages) = hugetlb_2mb_overcommit_pages {
96                pre_run_deps.push({
97                    ctx.emit_rust_step("ensure 2 MiB hugetlb pages are available", move |_| {
98                        move |rt| {
99                            let hugepages_dir =
100                                Path::new("/sys/kernel/mm/hugepages/hugepages-2048kB");
101
102                            let read_counter = |name: &str| -> anyhow::Result<u64> {
103                                let path = hugepages_dir.join(name);
104                                let value = fs_err::read_to_string(&path)?;
105                                Ok(value.trim().parse()?)
106                            };
107
108                            let write_overcommit_script = format!(
109                                "echo {overcommit_pages} | sudo tee {path}",
110                                path = hugepages_dir.join("nr_overcommit_hugepages").display(),
111                            );
112                            flowey::shell_cmd!(rt, "sh -c {write_overcommit_script}").run()?;
113
114                            let nr_hugepages = read_counter("nr_hugepages")?;
115                            let free_hugepages = read_counter("free_hugepages")?;
116                            let nr_overcommit_hugepages = read_counter("nr_overcommit_hugepages")?;
117                            let surplus_hugepages = read_counter("surplus_hugepages")?;
118
119                            log::info!("2 MiB hugetlb nr_hugepages={nr_hugepages}");
120                            log::info!("2 MiB hugetlb free_hugepages={free_hugepages}");
121                            log::info!(
122                                "2 MiB hugetlb nr_overcommit_hugepages={nr_overcommit_hugepages}"
123                            );
124                            log::info!("2 MiB hugetlb surplus_hugepages={surplus_hugepages}");
125
126                            if nr_overcommit_hugepages < overcommit_pages {
127                                anyhow::bail!(
128                                    "2 MiB hugetlb overcommit remains {}, below requested {}",
129                                    nr_overcommit_hugepages,
130                                    overcommit_pages
131                                );
132                            }
133
134                            Ok(())
135                        }
136                    })
137                });
138            }
139        }
140
141        let nextest_archive = nextest_archive_file.map(ctx, |x| x.archive_file);
142
143        ctx.req(crate::run_cargo_nextest_run::Request {
144            friendly_name: "vmm_tests".into(),
145            run_kind: flowey_lib_common::run_cargo_nextest_run::NextestRunKind::RunFromArchive {
146                archive_file: nextest_archive,
147                target,
148                nextest_bin,
149            },
150            nextest_profile,
151            nextest_filter_expr,
152            nextest_working_dir,
153            nextest_config_file,
154            run_ignored: false,
155            extra_env: Some(extra_env),
156            pre_run_deps,
157            results,
158        });
159
160        Ok(())
161    }
162}