openstack_cli_image/v2/image/file/download.rs
1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12//
13// SPDX-License-Identifier: Apache-2.0
14//
15// WARNING: This file is automatically generated from OpenAPI schema using
16// `openstack-codegenerator`.
17
18//! Download File command
19//!
20//! Wraps invoking of the `v2/images/{image_id}/file` with `GET` method
21
22use clap::Args;
23use tracing::info;
24
25use openstack_cli_core::cli::CliArgs;
26use openstack_cli_core::error::OpenStackCliError;
27use openstack_cli_core::output::OutputProcessor;
28use openstack_sdk::AsyncOpenStack;
29
30use openstack_cli_core::common::download_file;
31use openstack_sdk::api::QueryAsync;
32use openstack_sdk::api::download;
33use openstack_sdk::api::find;
34use openstack_sdk::api::image::v2::image::file::download;
35use openstack_sdk::api::image::v2::image::find;
36
37/// Downloads binary image data. *(Since Image API v2.0)*
38///
39/// Starting with the 2026.1 (Gazpacho) release, a store preference feature is
40/// introduced to allow services to suggest which stores to try when
41/// downloading images. This enables services like Nova and Cinder to prefer
42/// stores that are close to them or have better performance characteristics.
43///
44/// An optional `prefer` query parameter may be added to the request. When
45/// present, it contains a comma-separated list of store identifiers suggesting
46/// the ordering of stores to try when downloading the image. Glance will try
47/// stores from the list in order. If the image is not found in any of the
48/// specified stores, the system will fall back to the default behavior (trying
49/// all stores).
50///
51/// Example call:
52/// `curl -i -X GET -H "X-Auth-Token: $token" $image_url/v2/images/{image_id}/file`
53///
54/// Example call with store preference:
55/// `curl -i -X GET -H "X-Auth-Token: $token" $image_url/v2/images/{image_id}/file?prefer=local-store,backup-store`
56///
57/// The response body contains the raw binary data that represents the actual
58/// virtual disk. The `Content-Type` header contains the
59/// `application/octet-stream` value. The `Content-MD5` header contains an MD5
60/// checksum of the image data. Use this checksum to verify the integrity of
61/// the image data.
62///
63/// **Preconditions**
64///
65/// **Synchronous Postconditions**
66///
67/// Normal response codes: 200, 204, 206
68///
69/// Error response codes: 400, 403, 404, 416
70///
71/// **Error Responses**
72#[derive(Args)]
73#[command(about = "Download binary image data")]
74pub struct FileCommand {
75 /// Request Query parameters
76 #[command(flatten)]
77 query: QueryParameters,
78
79 /// Request Headers parameters
80 #[command(flatten)]
81 headers: HeaderParameters,
82
83 /// Path parameters
84 #[command(flatten)]
85 path: PathParameters,
86
87 /// Destination filename (using "-" will print object to stdout)
88 #[arg(long)]
89 file: Option<String>,
90}
91
92/// Query parameters
93#[derive(Args)]
94struct QueryParameters {}
95
96/// Header parameters
97#[derive(Args)]
98struct HeaderParameters {
99 /// The range of image data requested. Note that multi range requests are
100 /// not supported.
101 #[arg(long)]
102 range: Option<String>,
103}
104
105/// Path parameters
106#[derive(Args)]
107struct PathParameters {
108 /// image_id parameter for /v2/images/{image_id}/file API
109 #[arg(
110 help_heading = "Path parameters",
111 id = "path_param_image_id",
112 value_name = "IMAGE_ID"
113 )]
114 image_id: String,
115}
116
117impl FileCommand {
118 /// Perform command action
119 pub async fn take_action<C: CliArgs>(
120 &self,
121 parsed_args: &C,
122 client: &mut AsyncOpenStack,
123 ) -> Result<(), OpenStackCliError> {
124 info!("Download File");
125
126 let op =
127 OutputProcessor::from_args(parsed_args, Some("image.image/file"), Some("download"));
128 op.validate_args(parsed_args)?;
129 let find_ep = find::Request::builder()
130 .id(&self.path.image_id)
131 .build()
132 .map_err(|x| OpenStackCliError::EndpointBuild(x.to_string()))?;
133 let image_data: serde_json::Value = find(find_ep).query_async(client).await?;
134
135 let image_id = image_data["id"]
136 .as_str()
137 .ok_or_else(|| eyre::eyre!("image ID must be a string"))?
138 .to_string();
139 let image_name = image_data["name"]
140 .as_str()
141 .ok_or_else(|| eyre::eyre!("image name must be a string"))?
142 .to_string();
143
144 let ep = download::Request::builder()
145 .image_id(image_id)
146 .build()
147 .map_err(|x| OpenStackCliError::EndpointBuild(x.to_string()))?;
148 let (headers, data) = download(ep).query_async(client).await?;
149
150 let size: u64 = headers
151 .get("content-length")
152 .and_then(|x| {
153 x.to_str()
154 .inspect_err(|e| {
155 tracing::warn!("content-length header cannot be treated as string: {}", e)
156 })
157 .ok()
158 })
159 .unwrap_or("0")
160 .parse()
161 .inspect_err(|e| {
162 tracing::warn!("content-length header mut represent u64 number: {}", e)
163 })
164 .unwrap_or_default();
165 download_file(self.file.clone().unwrap_or(image_name), size, data).await?;
166 // Show command specific hints
167 op.show_command_hint()?;
168 Ok(())
169 }
170}