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