openstack_sdk/api/compute/v2/server/
rebuild_21.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
18use derive_builder::Builder;
19use http::{HeaderMap, HeaderName, HeaderValue};
20
21use crate::api::rest_endpoint_prelude::*;
22
23use serde::Deserialize;
24use serde::Serialize;
25use std::borrow::Cow;
26use std::collections::BTreeMap;
27
28#[derive(Debug, Deserialize, Clone, Serialize)]
29pub enum OsDcfDiskConfig {
30    #[serde(rename = "AUTO")]
31    Auto,
32    #[serde(rename = "MANUAL")]
33    Manual,
34}
35
36#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
37#[builder(setter(strip_option))]
38pub struct Personality<'a> {
39    #[serde(skip_serializing_if = "Option::is_none")]
40    #[builder(default, setter(into))]
41    pub(crate) contents: Option<Cow<'a, str>>,
42
43    #[serde(skip_serializing_if = "Option::is_none")]
44    #[builder(default, setter(into))]
45    pub(crate) path: Option<Cow<'a, str>>,
46}
47
48/// The action to rebuild a server.
49#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
50#[builder(setter(strip_option))]
51pub struct Rebuild<'a> {
52    /// IPv4 address that should be used to access this server.
53    #[serde(rename = "accessIPv4", skip_serializing_if = "Option::is_none")]
54    #[builder(default, setter(into))]
55    pub(crate) access_ipv4: Option<Cow<'a, str>>,
56
57    /// IPv6 address that should be used to access this server.
58    #[serde(rename = "accessIPv6", skip_serializing_if = "Option::is_none")]
59    #[builder(default, setter(into))]
60    pub(crate) access_ipv6: Option<Cow<'a, str>>,
61
62    /// The administrative password of the server. If you omit this parameter,
63    /// the operation generates a new password.
64    #[serde(rename = "adminPass", skip_serializing_if = "Option::is_none")]
65    #[builder(default, setter(into))]
66    pub(crate) admin_pass: Option<Cow<'a, str>>,
67
68    /// The UUID of the image to rebuild for your server instance. It must be a
69    /// valid UUID otherwise API will return 400. To rebuild a volume-backed
70    /// server with a new image, at least microversion 2.93 needs to be
71    /// provided in the request else the request will fall back to old
72    /// behaviour i.e. the API will return 400 (for an image different from the
73    /// image used when creating the volume). For non-volume-backed servers,
74    /// specifying a new image will result in validating that the image is
75    /// acceptable for the current compute host on which the server exists. If
76    /// the new image is not valid, the server will go into `ERROR` status.
77    #[serde(rename = "imageRef")]
78    #[builder(setter(into))]
79    pub(crate) image_ref: Cow<'a, str>,
80
81    /// Metadata key and value pairs. The maximum size of the metadata key and
82    /// value is 255 bytes each.
83    #[serde(skip_serializing_if = "Option::is_none")]
84    #[builder(default, private, setter(into, name = "_metadata"))]
85    pub(crate) metadata: Option<BTreeMap<Cow<'a, str>, Cow<'a, str>>>,
86
87    /// The server name.
88    #[serde(skip_serializing_if = "Option::is_none")]
89    #[builder(default, setter(into))]
90    pub(crate) name: Option<Cow<'a, str>>,
91
92    /// Controls how the API partitions the disk when you create, rebuild, or
93    /// resize servers. A server inherits the `OS-DCF:diskConfig` value from
94    /// the image from which it was created, and an image inherits the
95    /// `OS-DCF:diskConfig` value from the server from which it was created. To
96    /// override the inherited setting, you can include this attribute in the
97    /// request body of a server create, rebuild, or resize request. If the
98    /// `OS-DCF:diskConfig` value for an image is `MANUAL`, you cannot create a
99    /// server from that image and set its `OS-DCF:diskConfig` value to `AUTO`.
100    /// A valid value is:
101    ///
102    /// - `AUTO`. The API builds the server with a single partition the size of
103    ///   the target flavor disk. The API automatically adjusts the file system
104    ///   to fit the entire partition.
105    /// - `MANUAL`. The API builds the server by using whatever partition
106    ///   scheme and file system is in the source image. If the target flavor
107    ///   disk is larger, the API does not partition the remaining disk space.
108    #[serde(rename = "OS-DCF:diskConfig", skip_serializing_if = "Option::is_none")]
109    #[builder(default)]
110    pub(crate) os_dcf_disk_config: Option<OsDcfDiskConfig>,
111
112    /// The file path and contents, text only, to inject into the server at
113    /// launch. The maximum size of the file path data is 255 bytes. The
114    /// maximum limit is the number of allowed bytes in the decoded, rather
115    /// than encoded, data.
116    ///
117    /// **Available until version 2.56**
118    #[serde(skip_serializing_if = "Option::is_none")]
119    #[builder(default, setter(into))]
120    pub(crate) personality: Option<Vec<Personality<'a>>>,
121
122    /// Indicates whether the server is rebuilt with the preservation of the
123    /// ephemeral partition (`true`).
124    ///
125    /// Note
126    ///
127    /// This only works with baremetal servers provided by Ironic. Passing it
128    /// to any other server instance results in a fault and will prevent the
129    /// rebuild from happening.
130    #[serde(skip_serializing_if = "Option::is_none")]
131    #[builder(default, setter(into))]
132    pub(crate) preserve_ephemeral: Option<bool>,
133}
134
135impl<'a> RebuildBuilder<'a> {
136    /// Metadata key and value pairs. The maximum size of the metadata key and
137    /// value is 255 bytes each.
138    pub fn metadata<I, K, V>(&mut self, iter: I) -> &mut Self
139    where
140        I: Iterator<Item = (K, V)>,
141        K: Into<Cow<'a, str>>,
142        V: Into<Cow<'a, str>>,
143    {
144        self.metadata
145            .get_or_insert(None)
146            .get_or_insert_with(BTreeMap::new)
147            .extend(iter.map(|(k, v)| (k.into(), v.into())));
148        self
149    }
150}
151
152#[derive(Builder, Debug, Clone)]
153#[builder(setter(strip_option))]
154pub struct Request<'a> {
155    /// The action to rebuild a server.
156    #[builder(setter(into))]
157    pub(crate) rebuild: Rebuild<'a>,
158
159    /// id parameter for /v2.1/servers/{id}/action API
160    #[builder(default, setter(into))]
161    id: Cow<'a, str>,
162
163    #[builder(setter(name = "_headers"), default, private)]
164    _headers: Option<HeaderMap>,
165}
166impl<'a> Request<'a> {
167    /// Create a builder for the endpoint.
168    pub fn builder() -> RequestBuilder<'a> {
169        RequestBuilder::default()
170    }
171}
172
173impl<'a> RequestBuilder<'a> {
174    /// Add a single header to the Server.
175    pub fn header<K, V>(&mut self, header_name: K, header_value: V) -> &mut Self
176    where
177        K: Into<HeaderName>,
178        V: Into<HeaderValue>,
179    {
180        self._headers
181            .get_or_insert(None)
182            .get_or_insert_with(HeaderMap::new)
183            .insert(header_name.into(), header_value.into());
184        self
185    }
186
187    /// Add multiple headers.
188    pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
189    where
190        I: Iterator<Item = T>,
191        T: Into<(Option<HeaderName>, HeaderValue)>,
192    {
193        self._headers
194            .get_or_insert(None)
195            .get_or_insert_with(HeaderMap::new)
196            .extend(iter.map(Into::into));
197        self
198    }
199}
200
201impl RestEndpoint for Request<'_> {
202    fn method(&self) -> http::Method {
203        http::Method::POST
204    }
205
206    fn endpoint(&self) -> Cow<'static, str> {
207        format!("servers/{id}/action", id = self.id.as_ref(),).into()
208    }
209
210    fn parameters(&self) -> QueryParams<'_> {
211        QueryParams::default()
212    }
213
214    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
215        let mut params = JsonBodyParams::default();
216
217        params.push("rebuild", serde_json::to_value(&self.rebuild)?);
218
219        params.into_body()
220    }
221
222    fn service_type(&self) -> ServiceType {
223        ServiceType::Compute
224    }
225
226    fn response_key(&self) -> Option<Cow<'static, str>> {
227        None
228    }
229
230    /// Returns headers to be set into the request
231    fn request_headers(&self) -> Option<&HeaderMap> {
232        self._headers.as_ref()
233    }
234
235    /// Returns required API version
236    fn api_version(&self) -> Option<ApiVersion> {
237        Some(ApiVersion::new(2, 1))
238    }
239}
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244    #[cfg(feature = "sync")]
245    use crate::api::Query;
246    use crate::test::client::FakeOpenStackClient;
247    use crate::types::ServiceType;
248    use http::{HeaderName, HeaderValue};
249    use httpmock::MockServer;
250    use serde_json::json;
251
252    #[test]
253    fn test_service_type() {
254        assert_eq!(
255            Request::builder()
256                .rebuild(RebuildBuilder::default().image_ref("foo").build().unwrap())
257                .build()
258                .unwrap()
259                .service_type(),
260            ServiceType::Compute
261        );
262    }
263
264    #[test]
265    fn test_response_key() {
266        assert!(Request::builder()
267            .rebuild(RebuildBuilder::default().image_ref("foo").build().unwrap())
268            .build()
269            .unwrap()
270            .response_key()
271            .is_none())
272    }
273
274    #[cfg(feature = "sync")]
275    #[test]
276    fn endpoint() {
277        let server = MockServer::start();
278        let client = FakeOpenStackClient::new(server.base_url());
279        let mock = server.mock(|when, then| {
280            when.method(httpmock::Method::POST)
281                .path(format!("/servers/{id}/action", id = "id",));
282
283            then.status(200)
284                .header("content-type", "application/json")
285                .json_body(json!({ "dummy": {} }));
286        });
287
288        let endpoint = Request::builder()
289            .id("id")
290            .rebuild(RebuildBuilder::default().image_ref("foo").build().unwrap())
291            .build()
292            .unwrap();
293        let _: serde_json::Value = endpoint.query(&client).unwrap();
294        mock.assert();
295    }
296
297    #[cfg(feature = "sync")]
298    #[test]
299    fn endpoint_headers() {
300        let server = MockServer::start();
301        let client = FakeOpenStackClient::new(server.base_url());
302        let mock = server.mock(|when, then| {
303            when.method(httpmock::Method::POST)
304                .path(format!("/servers/{id}/action", id = "id",))
305                .header("foo", "bar")
306                .header("not_foo", "not_bar");
307            then.status(200)
308                .header("content-type", "application/json")
309                .json_body(json!({ "dummy": {} }));
310        });
311
312        let endpoint = Request::builder()
313            .id("id")
314            .rebuild(RebuildBuilder::default().image_ref("foo").build().unwrap())
315            .headers(
316                [(
317                    Some(HeaderName::from_static("foo")),
318                    HeaderValue::from_static("bar"),
319                )]
320                .into_iter(),
321            )
322            .header(
323                HeaderName::from_static("not_foo"),
324                HeaderValue::from_static("not_bar"),
325            )
326            .build()
327            .unwrap();
328        let _: serde_json::Value = endpoint.query(&client).unwrap();
329        mock.assert();
330    }
331}