openstack_sdk/api/compute/v2/server/
create_233.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//! Creates a server.
19//!
20//! The progress of this operation depends on the location of the requested
21//! image, network I/O, host load, selected flavor, and other factors.
22//!
23//! To check the progress of the request, make a `GET /servers/{id}` request.
24//! This call returns a progress attribute, which is a percentage value from 0
25//! to 100.
26//!
27//! The `Location` header returns the full URL to the newly created server and
28//! is available as a `self` and `bookmark` link in the server representation.
29//!
30//! When you create a server, the response shows only the server ID, its links,
31//! and the admin password. You can get additional attributes through
32//! subsequent `GET` requests on the server.
33//!
34//! Include the `block_device_mapping_v2` parameter in the create request body
35//! to boot a server from a volume.
36//!
37//! Include the `key_name` parameter in the create request body to add a
38//! keypair to the server when you create it. To create a keypair, make a
39//! [create keypair](https://docs.openstack.org/api-ref/compute/#create-or-import-keypair)
40//! request.
41//!
42//! **Preconditions**
43//!
44//! **Asynchronous postconditions**
45//!
46//! **Troubleshooting**
47//!
48//! Normal response codes: 202
49//!
50//! Error response codes: badRequest(400), unauthorized(401), forbidden(403),
51//! itemNotFound(404), conflict(409)
52//!
53use derive_builder::Builder;
54use http::{HeaderMap, HeaderName, HeaderValue};
55
56use crate::api::rest_endpoint_prelude::*;
57
58use serde::Deserialize;
59use serde::Serialize;
60use serde_json::Value;
61use std::borrow::Cow;
62use std::collections::BTreeMap;
63
64#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
65#[builder(setter(strip_option))]
66pub struct Networks<'a> {
67    /// Schedule the server on a host in the network specified with this
68    /// parameter and a cidr (`os:scheduler_hints.cidr`). It is available when
69    /// `SimpleCIDRAffinityFilter` is available on cloud side.
70    #[serde(skip_serializing_if = "Option::is_none")]
71    #[builder(default, setter(into))]
72    pub(crate) fixed_ip: Option<Cow<'a, str>>,
73
74    #[serde(skip_serializing_if = "Option::is_none")]
75    #[builder(default, setter(into))]
76    pub(crate) port: Option<Option<Cow<'a, str>>>,
77
78    #[serde(skip_serializing_if = "Option::is_none")]
79    #[builder(default, setter(into))]
80    pub(crate) tag: Option<Cow<'a, str>>,
81
82    #[serde(skip_serializing_if = "Option::is_none")]
83    #[builder(default, setter(into))]
84    pub(crate) uuid: Option<Cow<'a, str>>,
85}
86
87#[derive(Debug, Deserialize, Clone, Serialize)]
88pub enum OsDcfDiskConfig {
89    #[serde(rename = "AUTO")]
90    Auto,
91    #[serde(rename = "MANUAL")]
92    Manual,
93}
94
95#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
96#[builder(setter(strip_option))]
97pub struct Personality<'a> {
98    #[serde(skip_serializing_if = "Option::is_none")]
99    #[builder(default, setter(into))]
100    pub(crate) contents: Option<Cow<'a, str>>,
101
102    #[serde(skip_serializing_if = "Option::is_none")]
103    #[builder(default, setter(into))]
104    pub(crate) path: Option<Cow<'a, str>>,
105}
106
107#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
108#[builder(setter(strip_option))]
109pub struct BlockDeviceMapping<'a> {
110    #[serde(skip_serializing_if = "Option::is_none")]
111    #[builder(default, setter(into))]
112    pub(crate) connection_info: Option<Cow<'a, str>>,
113
114    /// Indicates whether a config drive enables metadata injection. The
115    /// config_drive setting provides information about a drive that the
116    /// instance can mount at boot time. The instance reads files from the
117    /// drive to get information that is normally available through the
118    /// metadata service. This metadata is different from the user data. Not
119    /// all cloud providers enable the `config_drive`. Read more in the
120    /// [OpenStack End User Guide](https://docs.openstack.org/nova/latest/user/config-drive.html).
121    #[serde(skip_serializing_if = "Option::is_none")]
122    #[builder(default, setter(into))]
123    pub(crate) delete_on_termination: Option<bool>,
124
125    #[serde(skip_serializing_if = "Option::is_none")]
126    #[builder(default, setter(into))]
127    pub(crate) device_name: Option<Cow<'a, str>>,
128
129    #[serde(skip_serializing_if = "Option::is_none")]
130    #[builder(default, setter(into))]
131    pub(crate) no_device: Option<Value>,
132
133    #[serde(skip_serializing_if = "Option::is_none")]
134    #[builder(default, setter(into))]
135    pub(crate) snapshot_id: Option<Cow<'a, str>>,
136
137    #[serde(skip_serializing_if = "Option::is_none")]
138    #[builder(default, setter(into))]
139    pub(crate) virtual_name: Option<Cow<'a, str>>,
140
141    #[serde(skip_serializing_if = "Option::is_none")]
142    #[builder(default, setter(into))]
143    pub(crate) volume_id: Option<Cow<'a, str>>,
144
145    #[serde(skip_serializing_if = "Option::is_none")]
146    #[builder(default, setter(into))]
147    pub(crate) volume_size: Option<i32>,
148}
149
150#[derive(Debug, Deserialize, Clone, Serialize)]
151pub enum SourceType {
152    #[serde(rename = "blank")]
153    Blank,
154    #[serde(rename = "image")]
155    Image,
156    #[serde(rename = "snapshot")]
157    Snapshot,
158    #[serde(rename = "volume")]
159    Volume,
160}
161
162#[derive(Debug, Deserialize, Clone, Serialize)]
163pub enum DestinationType {
164    #[serde(rename = "local")]
165    Local,
166    #[serde(rename = "volume")]
167    Volume,
168}
169
170#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
171#[builder(setter(strip_option))]
172pub struct BlockDeviceMappingV2<'a> {
173    #[serde(skip_serializing_if = "Option::is_none")]
174    #[builder(default, setter(into))]
175    pub(crate) boot_index: Option<Option<Cow<'a, str>>>,
176
177    #[serde(skip_serializing_if = "Option::is_none")]
178    #[builder(default, setter(into))]
179    pub(crate) connection_info: Option<Cow<'a, str>>,
180
181    #[serde(skip_serializing_if = "Option::is_none")]
182    #[builder(default, setter(into))]
183    pub(crate) delete_on_termination: Option<bool>,
184
185    #[serde(skip_serializing_if = "Option::is_none")]
186    #[builder(default)]
187    pub(crate) destination_type: Option<DestinationType>,
188
189    #[serde(skip_serializing_if = "Option::is_none")]
190    #[builder(default, setter(into))]
191    pub(crate) device_name: Option<Cow<'a, str>>,
192
193    #[serde(skip_serializing_if = "Option::is_none")]
194    #[builder(default, setter(into))]
195    pub(crate) device_type: Option<Cow<'a, str>>,
196
197    #[serde(skip_serializing_if = "Option::is_none")]
198    #[builder(default, setter(into))]
199    pub(crate) disk_bus: Option<Cow<'a, str>>,
200
201    #[serde(skip_serializing_if = "Option::is_none")]
202    #[builder(default, setter(into))]
203    pub(crate) guest_format: Option<Cow<'a, str>>,
204
205    #[serde(skip_serializing_if = "Option::is_none")]
206    #[builder(default, setter(into))]
207    pub(crate) image_id: Option<Cow<'a, str>>,
208
209    #[serde(skip_serializing_if = "Option::is_none")]
210    #[builder(default, setter(into))]
211    pub(crate) no_device: Option<Value>,
212
213    #[serde(skip_serializing_if = "Option::is_none")]
214    #[builder(default, setter(into))]
215    pub(crate) snapshot_id: Option<Cow<'a, str>>,
216
217    #[serde(skip_serializing_if = "Option::is_none")]
218    #[builder(default)]
219    pub(crate) source_type: Option<SourceType>,
220
221    #[serde(skip_serializing_if = "Option::is_none")]
222    #[builder(default, setter(into))]
223    pub(crate) uuid: Option<Cow<'a, str>>,
224
225    #[serde(skip_serializing_if = "Option::is_none")]
226    #[builder(default, setter(into))]
227    pub(crate) virtual_name: Option<Cow<'a, str>>,
228
229    #[serde(skip_serializing_if = "Option::is_none")]
230    #[builder(default, setter(into))]
231    pub(crate) volume_id: Option<Cow<'a, str>>,
232
233    #[serde(skip_serializing_if = "Option::is_none")]
234    #[builder(default, setter(into))]
235    pub(crate) volume_size: Option<i32>,
236}
237
238#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
239#[builder(setter(strip_option))]
240pub struct SecurityGroups<'a> {
241    /// A target cell name. Schedule the server in a host in the cell
242    /// specified. It is available when `TargetCellFilter` is available on
243    /// cloud side that is cell v1 environment.
244    #[serde(skip_serializing_if = "Option::is_none")]
245    #[builder(default, setter(into))]
246    pub(crate) name: Option<Cow<'a, str>>,
247}
248
249/// A `server` object.
250#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
251#[builder(setter(strip_option))]
252pub struct Server<'a> {
253    /// IPv4 address that should be used to access this server.
254    #[serde(rename = "accessIPv4", skip_serializing_if = "Option::is_none")]
255    #[builder(default, setter(into))]
256    pub(crate) access_ipv4: Option<Cow<'a, str>>,
257
258    /// IPv6 address that should be used to access this server.
259    #[serde(rename = "accessIPv6", skip_serializing_if = "Option::is_none")]
260    #[builder(default, setter(into))]
261    pub(crate) access_ipv6: Option<Cow<'a, str>>,
262
263    /// The administrative password of the server. If you omit this parameter,
264    /// the operation generates a new password.
265    #[serde(rename = "adminPass", skip_serializing_if = "Option::is_none")]
266    #[builder(default, setter(into))]
267    pub(crate) admin_pass: Option<Cow<'a, str>>,
268
269    /// A target cell name. Schedule the server in a host in the cell
270    /// specified. It is available when `TargetCellFilter` is available on
271    /// cloud side that is cell v1 environment.
272    #[serde(skip_serializing_if = "Option::is_none")]
273    #[builder(default, setter(into))]
274    pub(crate) availability_zone: Option<Cow<'a, str>>,
275
276    #[serde(skip_serializing_if = "Option::is_none")]
277    #[builder(default, setter(into))]
278    pub(crate) block_device_mapping: Option<Vec<BlockDeviceMapping<'a>>>,
279
280    /// Enables fine grained control of the block device mapping for an
281    /// instance. This is typically used for booting servers from volumes. An
282    /// example format would look as follows:
283    ///
284    /// > ```text
285    /// > "block_device_mapping_v2": [{
286    /// >     "boot_index": "0",
287    /// >     "uuid": "ac408821-c95a-448f-9292-73986c790911",
288    /// >     "source_type": "image",
289    /// >     "volume_size": "25",
290    /// >     "destination_type": "volume",
291    /// >     "delete_on_termination": true,
292    /// >     "tag": "disk1",
293    /// >     "disk_bus": "scsi"}]
294    /// >
295    /// > ```
296    ///
297    /// In microversion 2.32, `tag` is an optional string attribute that can be
298    /// used to assign a tag to the block device. This tag is then exposed to
299    /// the guest in the metadata API and the config drive and is associated to
300    /// hardware metadata for that block device, such as bus (ex: SCSI), bus
301    /// address (ex: 1:0:2:0), and serial.
302    ///
303    /// A bug has caused the `tag` attribute to no longer be accepted starting
304    /// with version 2.33. It has been restored in version 2.42.
305    #[serde(skip_serializing_if = "Option::is_none")]
306    #[builder(default, setter(into))]
307    pub(crate) block_device_mapping_v2: Option<Vec<BlockDeviceMappingV2<'a>>>,
308
309    /// Indicates whether a config drive enables metadata injection. The
310    /// config_drive setting provides information about a drive that the
311    /// instance can mount at boot time. The instance reads files from the
312    /// drive to get information that is normally available through the
313    /// metadata service. This metadata is different from the user data. Not
314    /// all cloud providers enable the `config_drive`. Read more in the
315    /// [OpenStack End User Guide](https://docs.openstack.org/nova/latest/user/config-drive.html).
316    #[serde(skip_serializing_if = "Option::is_none")]
317    #[builder(default, setter(into))]
318    pub(crate) config_drive: Option<bool>,
319
320    /// A free form description of the server. Limited to 255 characters in
321    /// length. Before microversion 2.19 this was set to the server name.
322    ///
323    /// **New in version 2.19**
324    #[serde(skip_serializing_if = "Option::is_none")]
325    #[builder(default, setter(into))]
326    pub(crate) description: Option<Option<Cow<'a, str>>>,
327
328    /// The flavor reference, as an ID (including a UUID) or full URL, for the
329    /// flavor for your server instance.
330    #[serde(rename = "flavorRef")]
331    #[builder(setter(into))]
332    pub(crate) flavor_ref: Cow<'a, str>,
333
334    /// The UUID of the image to use for your server instance. This is not
335    /// required in case of boot from volume. In all other cases it is required
336    /// and must be a valid UUID otherwise API will return 400.
337    #[serde(rename = "imageRef", skip_serializing_if = "Option::is_none")]
338    #[builder(default, setter(into))]
339    pub(crate) image_ref: Option<Cow<'a, str>>,
340
341    /// A target cell name. Schedule the server in a host in the cell
342    /// specified. It is available when `TargetCellFilter` is available on
343    /// cloud side that is cell v1 environment.
344    #[serde(skip_serializing_if = "Option::is_none")]
345    #[builder(default, setter(into))]
346    pub(crate) key_name: Option<Cow<'a, str>>,
347
348    #[serde(skip_serializing_if = "Option::is_none")]
349    #[builder(default, setter(into))]
350    pub(crate) max_count: Option<i32>,
351
352    /// Metadata key and value pairs. The maximum size of the metadata key and
353    /// value is 255 bytes each.
354    #[serde(skip_serializing_if = "Option::is_none")]
355    #[builder(default, private, setter(into, name = "_metadata"))]
356    pub(crate) metadata: Option<BTreeMap<Cow<'a, str>, Cow<'a, str>>>,
357
358    #[serde(skip_serializing_if = "Option::is_none")]
359    #[builder(default, setter(into))]
360    pub(crate) min_count: Option<i32>,
361
362    /// A target cell name. Schedule the server in a host in the cell
363    /// specified. It is available when `TargetCellFilter` is available on
364    /// cloud side that is cell v1 environment.
365    #[serde()]
366    #[builder(setter(into))]
367    pub(crate) name: Cow<'a, str>,
368
369    /// A list of `network` object. Required parameter when there are multiple
370    /// networks defined for the tenant. When you do not specify the networks
371    /// parameter, the server attaches to the only network created for the
372    /// current tenant. Optionally, you can create one or more NICs on the
373    /// server. To provision the server instance with a NIC for a network,
374    /// specify the UUID of the network in the `uuid` attribute in a `networks`
375    /// object. To provision the server instance with a NIC for an already
376    /// existing port, specify the port-id in the `port` attribute in a
377    /// `networks` object.
378    ///
379    /// If multiple networks are defined, the order in which they appear in the
380    /// guest operating system will not necessarily reflect the order in which
381    /// they are given in the server boot request. Guests should therefore not
382    /// depend on device order to deduce any information about their network
383    /// devices. Instead, device role tags should be used: introduced in 2.32,
384    /// broken in 2.37, and re-introduced and fixed in 2.42, the `tag` is an
385    /// optional, string attribute that can be used to assign a tag to a
386    /// virtual network interface. This tag is then exposed to the guest in the
387    /// metadata API and the config drive and is associated to hardware
388    /// metadata for that network interface, such as bus (ex: PCI), bus address
389    /// (ex: 0000:00:02.0), and MAC address.
390    ///
391    /// A bug has caused the `tag` attribute to no longer be accepted starting
392    /// with version 2.37. Therefore, network interfaces could only be tagged
393    /// in versions 2.32 to 2.36 inclusively. Version 2.42 has restored the
394    /// `tag` attribute.
395    ///
396    /// Starting with microversion 2.37, this field is required and the special
397    /// string values *auto* and *none* can be specified for networks. *auto*
398    /// tells the Compute service to use a network that is available to the
399    /// project, if one exists. If one does not exist, the Compute service will
400    /// attempt to automatically allocate a network for the project (if
401    /// possible). *none* tells the Compute service to not allocate a network
402    /// for the instance. The *auto* and *none* values cannot be used with any
403    /// other network values, including other network uuids, ports, fixed IPs
404    /// or device tags. These are requested as strings for the networks value,
405    /// not in a list. See the associated example.
406    #[serde(skip_serializing_if = "Option::is_none")]
407    #[builder(default, setter(into))]
408    pub(crate) networks: Option<Vec<Networks<'a>>>,
409
410    /// Controls how the API partitions the disk when you create, rebuild, or
411    /// resize servers. A server inherits the `OS-DCF:diskConfig` value from
412    /// the image from which it was created, and an image inherits the
413    /// `OS-DCF:diskConfig` value from the server from which it was created. To
414    /// override the inherited setting, you can include this attribute in the
415    /// request body of a server create, rebuild, or resize request. If the
416    /// `OS-DCF:diskConfig` value for an image is `MANUAL`, you cannot create a
417    /// server from that image and set its `OS-DCF:diskConfig` value to `AUTO`.
418    /// A valid value is:
419    ///
420    /// - `AUTO`. The API builds the server with a single partition the size of
421    ///   the target flavor disk. The API automatically adjusts the file system
422    ///   to fit the entire partition.
423    /// - `MANUAL`. The API builds the server by using whatever partition
424    ///   scheme and file system is in the source image. If the target flavor
425    ///   disk is larger, the API does not partition the remaining disk space.
426    #[serde(rename = "OS-DCF:diskConfig", skip_serializing_if = "Option::is_none")]
427    #[builder(default)]
428    pub(crate) os_dcf_disk_config: Option<OsDcfDiskConfig>,
429
430    /// The file path and contents, text only, to inject into the server at
431    /// launch. The maximum size of the file path data is 255 bytes. The
432    /// maximum limit is the number of allowed bytes in the decoded, rather
433    /// than encoded, data.
434    ///
435    /// **Available until version 2.56**
436    #[serde(skip_serializing_if = "Option::is_none")]
437    #[builder(default, setter(into))]
438    pub(crate) personality: Option<Vec<Personality<'a>>>,
439
440    /// Indicates whether a config drive enables metadata injection. The
441    /// config_drive setting provides information about a drive that the
442    /// instance can mount at boot time. The instance reads files from the
443    /// drive to get information that is normally available through the
444    /// metadata service. This metadata is different from the user data. Not
445    /// all cloud providers enable the `config_drive`. Read more in the
446    /// [OpenStack End User Guide](https://docs.openstack.org/nova/latest/user/config-drive.html).
447    #[serde(skip_serializing_if = "Option::is_none")]
448    #[builder(default, setter(into))]
449    pub(crate) return_reservation_id: Option<bool>,
450
451    /// One or more security groups. Specify the name of the security group in
452    /// the `name` attribute. If you omit this attribute, the API creates the
453    /// server in the `default` security group. Requested security groups are
454    /// not applied to pre-existing ports.
455    #[serde(skip_serializing_if = "Option::is_none")]
456    #[builder(default, setter(into))]
457    pub(crate) security_groups: Option<Vec<SecurityGroups<'a>>>,
458
459    /// Configuration information or scripts to use upon launch. Must be Base64
460    /// encoded. Restricted to 65535 bytes.
461    ///
462    /// Note
463    ///
464    /// The `null` value allowed in Nova legacy v2 API, but due to the strict
465    /// input validation, it isn’t allowed in Nova v2.1 API.
466    #[serde(skip_serializing_if = "Option::is_none")]
467    #[builder(default, setter(into))]
468    pub(crate) user_data: Option<Cow<'a, str>>,
469}
470
471impl<'a> ServerBuilder<'a> {
472    /// Metadata key and value pairs. The maximum size of the metadata key and
473    /// value is 255 bytes each.
474    pub fn metadata<I, K, V>(&mut self, iter: I) -> &mut Self
475    where
476        I: Iterator<Item = (K, V)>,
477        K: Into<Cow<'a, str>>,
478        V: Into<Cow<'a, str>>,
479    {
480        self.metadata
481            .get_or_insert(None)
482            .get_or_insert_with(BTreeMap::new)
483            .extend(iter.map(|(k, v)| (k.into(), v.into())));
484        self
485    }
486}
487
488/// The dictionary of data to send to the scheduler. Alternatively, you can
489/// specify `OS-SCH-HNT:scheduler_hints` as the key in the request body.
490///
491/// Note
492///
493/// This is a top-level key in the request body, not part of the server portion
494/// of the request body.
495///
496/// There are a few caveats with scheduler hints:
497///
498/// - The request validation schema is per hint. For example, some require a
499///   single string value, and some accept a list of values.
500/// - Hints are only used based on the cloud scheduler configuration, which
501///   varies per deployment.
502/// - Hints are pluggable per deployment, meaning that a cloud can have custom
503///   hints which may not be available in another cloud.
504///
505/// For these reasons, it is important to consult each cloud’s user
506/// documentation to know what is available for scheduler hints.
507#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
508#[builder(setter(strip_option))]
509pub struct OsSchedulerHints<'a> {
510    /// Schedule the server on a host in the network specified with this
511    /// parameter and a cidr (`os:scheduler_hints.cidr`). It is available when
512    /// `SimpleCIDRAffinityFilter` is available on cloud side.
513    #[serde(skip_serializing_if = "Option::is_none")]
514    #[builder(default, setter(into))]
515    pub(crate) build_near_host_ip: Option<Cow<'a, str>>,
516
517    /// Schedule the server on a host in the network specified with an IP
518    /// address (`os:scheduler_hints:build_near_host_ip`) and this parameter.
519    /// If `os:scheduler_hints:build_near_host_ip` is specified and this
520    /// parameter is omitted, `/24` is used. It is available when
521    /// `SimpleCIDRAffinityFilter` is available on cloud side.
522    #[serde(skip_serializing_if = "Option::is_none")]
523    #[builder(default, setter(into))]
524    pub(crate) cidr: Option<Cow<'a, str>>,
525
526    /// A list of cell routes or a cell route (string). Schedule the server in
527    /// a cell that is not specified. It is available when
528    /// `DifferentCellFilter` is available on cloud side that is cell v1
529    /// environment.
530    #[serde(skip_serializing_if = "Option::is_none")]
531    #[builder(default, setter(into))]
532    pub(crate) different_cell: Option<Vec<Cow<'a, str>>>,
533
534    /// A list of server UUIDs or a server UUID. Schedule the server on a
535    /// different host from a set of servers. It is available when
536    /// `DifferentHostFilter` is available on cloud side.
537    #[serde(skip_serializing_if = "Option::is_none")]
538    #[builder(default, setter(into))]
539    pub(crate) different_host: Option<Vec<Cow<'a, str>>>,
540
541    /// The server group UUID. Schedule the server according to a policy of the
542    /// server group (`anti-affinity`, `affinity`, `soft-anti-affinity` or
543    /// `soft-affinity`). It is available when `ServerGroupAffinityFilter`,
544    /// `ServerGroupAntiAffinityFilter`, `ServerGroupSoftAntiAffinityWeigher`,
545    /// `ServerGroupSoftAffinityWeigher` are available on cloud side.
546    #[serde(skip_serializing_if = "Option::is_none")]
547    #[builder(default, setter(into))]
548    pub(crate) group: Option<Cow<'a, str>>,
549
550    /// Schedule the server by using a custom filter in JSON format. For
551    /// example:
552    ///
553    /// ```text
554    /// "query": "[\">=\",\"$free_ram_mb\",1024]"
555    ///
556    /// ```
557    ///
558    /// It is available when `JsonFilter` is available on cloud side.
559    #[serde(skip_serializing_if = "Option::is_none")]
560    #[builder(default, setter(into))]
561    pub(crate) query: Option<Value>,
562
563    /// A list of server UUIDs or a server UUID. Schedule the server on the
564    /// same host as another server in a set of servers. It is available when
565    /// `SameHostFilter` is available on cloud side.
566    #[serde(skip_serializing_if = "Option::is_none")]
567    #[builder(default, setter(into))]
568    pub(crate) same_host: Option<Vec<Cow<'a, str>>>,
569
570    /// A target cell name. Schedule the server in a host in the cell
571    /// specified. It is available when `TargetCellFilter` is available on
572    /// cloud side that is cell v1 environment.
573    #[serde(skip_serializing_if = "Option::is_none")]
574    #[builder(default, setter(into))]
575    pub(crate) target_cell: Option<Cow<'a, str>>,
576
577    #[builder(setter(name = "_properties"), default, private)]
578    #[serde(flatten)]
579    _properties: BTreeMap<Cow<'a, str>, Value>,
580}
581
582impl<'a> OsSchedulerHintsBuilder<'a> {
583    pub fn properties<I, K, V>(&mut self, iter: I) -> &mut Self
584    where
585        I: Iterator<Item = (K, V)>,
586        K: Into<Cow<'a, str>>,
587        V: Into<Value>,
588    {
589        self._properties
590            .get_or_insert_with(BTreeMap::new)
591            .extend(iter.map(|(k, v)| (k.into(), v.into())));
592        self
593    }
594}
595
596#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
597#[builder(setter(strip_option))]
598pub struct OsSchHntSchedulerHints<'a> {
599    /// Schedule the server on a host in the network specified with this
600    /// parameter and a cidr (`os:scheduler_hints.cidr`). It is available when
601    /// `SimpleCIDRAffinityFilter` is available on cloud side.
602    #[serde(skip_serializing_if = "Option::is_none")]
603    #[builder(default, setter(into))]
604    pub(crate) build_near_host_ip: Option<Cow<'a, str>>,
605
606    /// Schedule the server on a host in the network specified with an IP
607    /// address (`os:scheduler_hints:build_near_host_ip`) and this parameter.
608    /// If `os:scheduler_hints:build_near_host_ip` is specified and this
609    /// parameter is omitted, `/24` is used. It is available when
610    /// `SimpleCIDRAffinityFilter` is available on cloud side.
611    #[serde(skip_serializing_if = "Option::is_none")]
612    #[builder(default, setter(into))]
613    pub(crate) cidr: Option<Cow<'a, str>>,
614
615    /// A list of cell routes or a cell route (string). Schedule the server in
616    /// a cell that is not specified. It is available when
617    /// `DifferentCellFilter` is available on cloud side that is cell v1
618    /// environment.
619    #[serde(skip_serializing_if = "Option::is_none")]
620    #[builder(default, setter(into))]
621    pub(crate) different_cell: Option<Vec<Cow<'a, str>>>,
622
623    /// A list of server UUIDs or a server UUID. Schedule the server on a
624    /// different host from a set of servers. It is available when
625    /// `DifferentHostFilter` is available on cloud side.
626    #[serde(skip_serializing_if = "Option::is_none")]
627    #[builder(default, setter(into))]
628    pub(crate) different_host: Option<Vec<Cow<'a, str>>>,
629
630    /// The server group UUID. Schedule the server according to a policy of the
631    /// server group (`anti-affinity`, `affinity`, `soft-anti-affinity` or
632    /// `soft-affinity`). It is available when `ServerGroupAffinityFilter`,
633    /// `ServerGroupAntiAffinityFilter`, `ServerGroupSoftAntiAffinityWeigher`,
634    /// `ServerGroupSoftAffinityWeigher` are available on cloud side.
635    #[serde(skip_serializing_if = "Option::is_none")]
636    #[builder(default, setter(into))]
637    pub(crate) group: Option<Cow<'a, str>>,
638
639    /// Schedule the server by using a custom filter in JSON format. For
640    /// example:
641    ///
642    /// ```text
643    /// "query": "[\">=\",\"$free_ram_mb\",1024]"
644    ///
645    /// ```
646    ///
647    /// It is available when `JsonFilter` is available on cloud side.
648    #[serde(skip_serializing_if = "Option::is_none")]
649    #[builder(default, setter(into))]
650    pub(crate) query: Option<Value>,
651
652    /// A list of server UUIDs or a server UUID. Schedule the server on the
653    /// same host as another server in a set of servers. It is available when
654    /// `SameHostFilter` is available on cloud side.
655    #[serde(skip_serializing_if = "Option::is_none")]
656    #[builder(default, setter(into))]
657    pub(crate) same_host: Option<Vec<Cow<'a, str>>>,
658
659    /// A target cell name. Schedule the server in a host in the cell
660    /// specified. It is available when `TargetCellFilter` is available on
661    /// cloud side that is cell v1 environment.
662    #[serde(skip_serializing_if = "Option::is_none")]
663    #[builder(default, setter(into))]
664    pub(crate) target_cell: Option<Cow<'a, str>>,
665
666    #[builder(setter(name = "_properties"), default, private)]
667    #[serde(flatten)]
668    _properties: BTreeMap<Cow<'a, str>, Value>,
669}
670
671impl<'a> OsSchHntSchedulerHintsBuilder<'a> {
672    pub fn properties<I, K, V>(&mut self, iter: I) -> &mut Self
673    where
674        I: Iterator<Item = (K, V)>,
675        K: Into<Cow<'a, str>>,
676        V: Into<Value>,
677    {
678        self._properties
679            .get_or_insert_with(BTreeMap::new)
680            .extend(iter.map(|(k, v)| (k.into(), v.into())));
681        self
682    }
683}
684
685#[derive(Builder, Debug, Clone)]
686#[builder(setter(strip_option))]
687pub struct Request<'a> {
688    #[builder(default, setter(into))]
689    pub(crate) os_sch_hnt_scheduler_hints: Option<OsSchHntSchedulerHints<'a>>,
690
691    /// The dictionary of data to send to the scheduler. Alternatively, you can
692    /// specify `OS-SCH-HNT:scheduler_hints` as the key in the request body.
693    ///
694    /// Note
695    ///
696    /// This is a top-level key in the request body, not part of the server
697    /// portion of the request body.
698    ///
699    /// There are a few caveats with scheduler hints:
700    ///
701    /// - The request validation schema is per hint. For example, some require
702    ///   a single string value, and some accept a list of values.
703    /// - Hints are only used based on the cloud scheduler configuration, which
704    ///   varies per deployment.
705    /// - Hints are pluggable per deployment, meaning that a cloud can have
706    ///   custom hints which may not be available in another cloud.
707    ///
708    /// For these reasons, it is important to consult each cloud’s user
709    /// documentation to know what is available for scheduler hints.
710    #[builder(default, setter(into))]
711    pub(crate) os_scheduler_hints: Option<OsSchedulerHints<'a>>,
712
713    /// A `server` object.
714    #[builder(setter(into))]
715    pub(crate) server: Server<'a>,
716
717    #[builder(setter(name = "_headers"), default, private)]
718    _headers: Option<HeaderMap>,
719}
720impl<'a> Request<'a> {
721    /// Create a builder for the endpoint.
722    pub fn builder() -> RequestBuilder<'a> {
723        RequestBuilder::default()
724    }
725}
726
727impl RequestBuilder<'_> {
728    /// Add a single header to the Server.
729    pub fn header(&mut self, header_name: &'static str, header_value: &'static str) -> &mut Self
730where {
731        self._headers
732            .get_or_insert(None)
733            .get_or_insert_with(HeaderMap::new)
734            .insert(header_name, HeaderValue::from_static(header_value));
735        self
736    }
737
738    /// Add multiple headers.
739    pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
740    where
741        I: Iterator<Item = T>,
742        T: Into<(Option<HeaderName>, HeaderValue)>,
743    {
744        self._headers
745            .get_or_insert(None)
746            .get_or_insert_with(HeaderMap::new)
747            .extend(iter.map(Into::into));
748        self
749    }
750}
751
752impl RestEndpoint for Request<'_> {
753    fn method(&self) -> http::Method {
754        http::Method::POST
755    }
756
757    fn endpoint(&self) -> Cow<'static, str> {
758        "servers".to_string().into()
759    }
760
761    fn parameters(&self) -> QueryParams {
762        QueryParams::default()
763    }
764
765    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
766        let mut params = JsonBodyParams::default();
767
768        params.push("server", serde_json::to_value(&self.server)?);
769        if let Some(val) = &self.os_scheduler_hints {
770            params.push("os:scheduler_hints", serde_json::to_value(val)?);
771        }
772        if let Some(val) = &self.os_sch_hnt_scheduler_hints {
773            params.push("OS-SCH-HNT:scheduler_hints", serde_json::to_value(val)?);
774        }
775
776        params.into_body()
777    }
778
779    fn service_type(&self) -> ServiceType {
780        ServiceType::Compute
781    }
782
783    fn response_key(&self) -> Option<Cow<'static, str>> {
784        Some("server".into())
785    }
786
787    /// Returns headers to be set into the request
788    fn request_headers(&self) -> Option<&HeaderMap> {
789        self._headers.as_ref()
790    }
791
792    /// Returns required API version
793    fn api_version(&self) -> Option<ApiVersion> {
794        Some(ApiVersion::new(2, 33))
795    }
796}
797
798#[cfg(test)]
799mod tests {
800    use super::*;
801    #[cfg(feature = "sync")]
802    use crate::api::Query;
803    use crate::test::client::FakeOpenStackClient;
804    use crate::types::ServiceType;
805    use http::{HeaderName, HeaderValue};
806    use httpmock::MockServer;
807    use serde_json::json;
808
809    #[test]
810    fn test_service_type() {
811        assert_eq!(
812            Request::builder()
813                .server(
814                    ServerBuilder::default()
815                        .flavor_ref("foo")
816                        .name("foo")
817                        .build()
818                        .unwrap()
819                )
820                .build()
821                .unwrap()
822                .service_type(),
823            ServiceType::Compute
824        );
825    }
826
827    #[test]
828    fn test_response_key() {
829        assert_eq!(
830            Request::builder()
831                .server(
832                    ServerBuilder::default()
833                        .flavor_ref("foo")
834                        .name("foo")
835                        .build()
836                        .unwrap()
837                )
838                .build()
839                .unwrap()
840                .response_key()
841                .unwrap(),
842            "server"
843        );
844    }
845
846    #[cfg(feature = "sync")]
847    #[test]
848    fn endpoint() {
849        let server = MockServer::start();
850        let client = FakeOpenStackClient::new(server.base_url());
851        let mock = server.mock(|when, then| {
852            when.method(httpmock::Method::POST)
853                .path("/servers".to_string());
854
855            then.status(200)
856                .header("content-type", "application/json")
857                .json_body(json!({ "server": {} }));
858        });
859
860        let endpoint = Request::builder()
861            .server(
862                ServerBuilder::default()
863                    .flavor_ref("foo")
864                    .name("foo")
865                    .build()
866                    .unwrap(),
867            )
868            .build()
869            .unwrap();
870        let _: serde_json::Value = endpoint.query(&client).unwrap();
871        mock.assert();
872    }
873
874    #[cfg(feature = "sync")]
875    #[test]
876    fn endpoint_headers() {
877        let server = MockServer::start();
878        let client = FakeOpenStackClient::new(server.base_url());
879        let mock = server.mock(|when, then| {
880            when.method(httpmock::Method::POST)
881                .path("/servers".to_string())
882                .header("foo", "bar")
883                .header("not_foo", "not_bar");
884            then.status(200)
885                .header("content-type", "application/json")
886                .json_body(json!({ "server": {} }));
887        });
888
889        let endpoint = Request::builder()
890            .server(
891                ServerBuilder::default()
892                    .flavor_ref("foo")
893                    .name("foo")
894                    .build()
895                    .unwrap(),
896            )
897            .headers(
898                [(
899                    Some(HeaderName::from_static("foo")),
900                    HeaderValue::from_static("bar"),
901                )]
902                .into_iter(),
903            )
904            .header("not_foo", "not_bar")
905            .build()
906            .unwrap();
907        let _: serde_json::Value = endpoint.query(&client).unwrap();
908        mock.assert();
909    }
910}