Skip to main content

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}