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