1use crate::common::CommonArch;
18use flowey::node::prelude::*;
19use std::collections::BTreeMap;
20use std::collections::BTreeSet;
21
22#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
25pub enum LinuxTestKernelVersion {
26 Linux6_1,
27 Linux6_18,
28 CcaV15,
36 SnpGuest,
38}
39
40impl LinuxTestKernelVersion {
41 pub fn artifact_tag(self) -> &'static str {
44 match self {
45 Self::Linux6_1 => "6.1",
46 Self::Linux6_18 => "6.18",
47 Self::CcaV15 => "cca-v15",
48 Self::SnpGuest => "snp-guest",
49 }
50 }
51
52 pub fn is_available_for(self, arch: CommonArch) -> bool {
57 match self {
58 Self::Linux6_1 | Self::Linux6_18 => true,
59 Self::CcaV15 => matches!(arch, CommonArch::Aarch64),
60 Self::SnpGuest => matches!(arch, CommonArch::X86_64),
61 }
62 }
63}
64
65#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
67pub enum OpenvmmTestKernelFile {
68 Kernel,
70 BzImage,
72}
73
74impl OpenvmmTestKernelFile {
75 pub fn is_available_for(self, arch: CommonArch) -> bool {
77 match self {
78 Self::Kernel => true,
79 Self::BzImage => matches!(arch, CommonArch::X86_64),
80 }
81 }
82
83 pub fn filename(self, arch: CommonArch) -> &'static str {
85 match self {
86 Self::Kernel => match arch {
87 CommonArch::X86_64 => "vmlinux",
88 CommonArch::Aarch64 => "Image",
89 },
90 Self::BzImage => "bzImage",
91 }
92 }
93}
94
95pub const DEFAULT_LINUX_TEST_KERNEL_VERSION: LinuxTestKernelVersion =
98 LinuxTestKernelVersion::Linux6_18;
99
100pub const CCA_LINUX_TEST_KERNEL_VERSION: LinuxTestKernelVersion = LinuxTestKernelVersion::CcaV15;
107
108pub const SNP_GUEST_LINUX_TEST_KERNEL_VERSION: LinuxTestKernelVersion =
110 LinuxTestKernelVersion::SnpGuest;
111
112flowey_config! {
113 pub struct Config {
115 pub version: Option<String>,
117 pub local_paths: BTreeMap<(CommonArch, LinuxTestKernelVersion), ConfigVar<PathBuf>>,
120 }
121}
122
123flowey_request! {
124 pub enum Request {
125 Get(
127 OpenvmmTestKernelFile,
128 CommonArch,
129 LinuxTestKernelVersion,
130 WriteVar<PathBuf>,
131 ),
132 }
133}
134
135new_flow_node_with_config!(struct Node);
136
137impl FlowNodeWithConfig for Node {
138 type Request = Request;
139 type Config = Config;
140
141 fn imports(ctx: &mut ImportCtx<'_>) {
142 ctx.import::<flowey_lib_common::install_dist_pkg::Node>();
143 ctx.import::<flowey_lib_common::download_gh_release::Node>();
144 }
145
146 fn emit(
147 config: Config,
148 requests: Vec<Self::Request>,
149 ctx: &mut NodeCtx<'_>,
150 ) -> anyhow::Result<()> {
151 let Config {
152 version,
153 local_paths,
154 } = config;
155 let mut deps: BTreeMap<
156 (OpenvmmTestKernelFile, CommonArch, LinuxTestKernelVersion),
157 Vec<WriteVar<PathBuf>>,
158 > = BTreeMap::new();
159
160 for req in requests {
161 match req {
162 Request::Get(file, arch, kver, var) => {
163 if !kver.is_available_for(arch) {
164 anyhow::bail!(
165 "test kernel {:?} is not published for {arch:?}",
166 kver.artifact_tag()
167 );
168 }
169 if !file.is_available_for(arch) {
170 anyhow::bail!(
171 "{file:?} is not available in the openvmm-test-linux archive for {arch:?}"
172 );
173 }
174 deps.entry((file, arch, kver)).or_default().push(var);
175 }
176 }
177 }
178
179 if version.is_some() && !local_paths.is_empty() {
180 anyhow::bail!("Cannot specify both Version and LocalPath requests");
181 }
182
183 if version.is_none() && local_paths.is_empty() {
184 anyhow::bail!("Must specify a Version or LocalPath request");
185 }
186
187 if deps.is_empty() {
190 return Ok(());
191 }
192
193 if !local_paths.is_empty() {
194 ctx.emit_rust_step("use local openvmm-test-linux", |ctx| {
195 let deps = deps.claim(ctx);
196 let local_paths: BTreeMap<_, _> = local_paths
197 .into_iter()
198 .map(|(key, var)| (key, var.claim(ctx)))
199 .collect();
200 move |rt| {
201 let resolved_paths: BTreeMap<(CommonArch, LinuxTestKernelVersion), PathBuf> =
202 local_paths
203 .into_iter()
204 .map(|(key, var)| (key, rt.read(var)))
205 .collect();
206
207 for ((file, arch, kver), vars) in deps {
208 let base_dir = resolved_paths.get(&(arch, kver)).ok_or_else(|| {
209 anyhow::anyhow!("No local path specified for ({:?}, {:?})", arch, kver)
210 })?;
211 let path = base_dir.join(file.filename(arch));
212 rt.write_all(vars, &path)
213 }
214
215 Ok(())
216 }
217 });
218
219 return Ok(());
220 }
221
222 let needed_archives: BTreeSet<(CommonArch, LinuxTestKernelVersion)> =
226 deps.keys().map(|(_, arch, kver)| (*arch, *kver)).collect();
227 let version = version.context("missing openvmm-test-linux version")?;
228
229 let mut archives = BTreeMap::new();
230 for (arch, kver) in needed_archives {
231 let arch_str = match arch {
232 CommonArch::X86_64 => "x86_64",
233 CommonArch::Aarch64 => "aarch64",
234 };
235 let kver_str = kver.artifact_tag();
236 let archive = ctx.reqv(|v| flowey_lib_common::download_gh_release::Request {
237 repo_owner: crate::common::OPENVMM_GITHUB_OWNER.into(),
238 repo_name: crate::common::OPENVMM_DEPS_GITHUB_REPO.into(),
239 needs_auth: false,
240 tag: version.clone(),
241 file_name: format!("openvmm-test-linux-{kver_str}.{arch_str}.{version}.tar.gz"),
242 path: v,
243 });
244 archives.insert((arch, kver), archive);
245 }
246
247 let persistent_dir = ctx.persistent_dir();
248
249 ctx.emit_rust_step("unpack openvmm-test-linux archives", |ctx| {
250 let persistent_dir = persistent_dir.claim(ctx);
251 let archives = archives.claim(ctx);
252 let deps = deps.claim(ctx);
253 let version = version.clone();
254 move |rt| {
255 let persistent_dir = persistent_dir.map(|d| rt.read(d));
256
257 let mut extract_dirs = BTreeMap::new();
258 for (key, archive) in archives {
259 let file = rt.read(archive);
260 let dir = flowey_lib_common::_util::extract::extract_tar_gz_if_new(
261 rt,
262 persistent_dir.as_deref(),
263 &file,
264 &version,
265 )?;
266 extract_dirs.insert(key, dir);
267 }
268
269 for ((file, arch, kver), vars) in deps {
270 let path = extract_dirs
271 .get(&(arch, kver))
272 .with_context(|| {
273 format!(
274 "missing extracted openvmm-test-linux archive for {arch:?} {kver:?}"
275 )
276 })?
277 .join(file.filename(arch));
278 rt.write_all(vars, &path)
279 }
280
281 Ok(())
282 }
283 });
284
285 Ok(())
286 }
287}