openstack_sdk/api/compute/v2/server/create_242.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
402#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
403#[builder(setter(strip_option))]
404pub struct Networks<'a> {
405 #[serde(skip_serializing_if = "Option::is_none")]
406 #[builder(default, setter(into))]
407 pub(crate) fixed_ip: Option<Cow<'a, str>>,
408
409 #[serde(skip_serializing_if = "Option::is_none")]
410 #[builder(default, setter(into))]
411 pub(crate) port: Option<Option<Cow<'a, str>>>,
412
413 #[serde(skip_serializing_if = "Option::is_none")]
414 #[builder(default, setter(into))]
415 pub(crate) tag: Option<Cow<'a, str>>,
416
417 #[serde(skip_serializing_if = "Option::is_none")]
418 #[builder(default, setter(into))]
419 pub(crate) uuid: Option<Cow<'a, str>>,
420}
421
422#[derive(Debug, Deserialize, Clone, Serialize)]
423pub enum NetworksStringEnum {
424 #[serde(rename = "auto")]
425 Auto,
426 #[serde(rename = "none")]
427 None,
428}
429
430#[derive(Debug, Deserialize, Clone, Serialize)]
431#[serde(untagged)]
432pub enum ServerNetworks<'a> {
433 F1(Vec<Networks<'a>>),
434 F2(NetworksStringEnum),
435}
436
437#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
438#[builder(setter(strip_option))]
439pub struct Personality<'a> {
440 #[serde(skip_serializing_if = "Option::is_none")]
441 #[builder(default, setter(into))]
442 pub(crate) contents: Option<Cow<'a, str>>,
443
444 #[serde(skip_serializing_if = "Option::is_none")]
445 #[builder(default, setter(into))]
446 pub(crate) path: Option<Cow<'a, str>>,
447}
448
449#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
450#[builder(setter(strip_option))]
451pub struct SecurityGroups<'a> {
452 /// A target cell name. Schedule the server in a host in the cell
453 /// specified. It is available when `TargetCellFilter` is available on
454 /// cloud side that is cell v1 environment.
455 #[serde(skip_serializing_if = "Option::is_none")]
456 #[builder(default, setter(into))]
457 pub(crate) name: Option<Cow<'a, str>>,
458}
459
460/// A `server` object.
461#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
462#[builder(setter(strip_option))]
463pub struct Server<'a> {
464 /// IPv4 address that should be used to access this server.
465 #[serde(rename = "accessIPv4", skip_serializing_if = "Option::is_none")]
466 #[builder(default, setter(into))]
467 pub(crate) access_ipv4: Option<Cow<'a, str>>,
468
469 /// IPv6 address that should be used to access this server.
470 #[serde(rename = "accessIPv6", skip_serializing_if = "Option::is_none")]
471 #[builder(default, setter(into))]
472 pub(crate) access_ipv6: Option<Cow<'a, str>>,
473
474 /// The administrative password of the server. If you omit this parameter,
475 /// the operation generates a new password.
476 #[serde(rename = "adminPass", skip_serializing_if = "Option::is_none")]
477 #[builder(default, setter(into))]
478 pub(crate) admin_pass: Option<Cow<'a, str>>,
479
480 /// A target cell name. Schedule the server in a host in the cell
481 /// specified. It is available when `TargetCellFilter` is available on
482 /// cloud side that is cell v1 environment.
483 #[serde(skip_serializing_if = "Option::is_none")]
484 #[builder(default, setter(into))]
485 pub(crate) availability_zone: Option<Cow<'a, str>>,
486
487 #[serde(skip_serializing_if = "Option::is_none")]
488 #[builder(default, setter(into))]
489 pub(crate) block_device_mapping: Option<Vec<BlockDeviceMapping<'a>>>,
490
491 /// Enables fine grained control of the block device mapping for an
492 /// instance. This is typically used for booting servers from volumes. An
493 /// example format would look as follows:
494 ///
495 /// > ```text
496 /// > "block_device_mapping_v2": [{
497 /// > "boot_index": "0",
498 /// > "uuid": "ac408821-c95a-448f-9292-73986c790911",
499 /// > "source_type": "image",
500 /// > "volume_size": "25",
501 /// > "destination_type": "volume",
502 /// > "delete_on_termination": true,
503 /// > "tag": "disk1",
504 /// > "disk_bus": "scsi"}]
505 /// > ```
506 ///
507 /// In microversion 2.32, `tag` is an optional string attribute that can be
508 /// used to assign a tag to the block device. This tag is then exposed to
509 /// the guest in the metadata API and the config drive and is associated to
510 /// hardware metadata for that block device, such as bus (ex: SCSI), bus
511 /// address (ex: 1:0:2:0), and serial.
512 ///
513 /// A bug has caused the `tag` attribute to no longer be accepted starting
514 /// with version 2.33. It has been restored in version 2.42.
515 #[serde(skip_serializing_if = "Option::is_none")]
516 #[builder(default, setter(into))]
517 pub(crate) block_device_mapping_v2: Option<Vec<BlockDeviceMappingV2<'a>>>,
518
519 /// Indicates whether a config drive enables metadata injection. The
520 /// config_drive setting provides information about a drive that the
521 /// instance can mount at boot time. The instance reads files from the
522 /// drive to get information that is normally available through the
523 /// metadata service. This metadata is different from the user data. Not
524 /// all cloud providers enable the `config_drive`. Read more in the
525 /// [OpenStack End User Guide](https://docs.openstack.org/nova/latest/user/config-drive.html).
526 #[serde(skip_serializing_if = "Option::is_none")]
527 #[builder(default, setter(into))]
528 pub(crate) config_drive: Option<bool>,
529
530 /// A free form description of the server. Limited to 255 characters in
531 /// length. Before microversion 2.19 this was set to the server name.
532 ///
533 /// **New in version 2.19**
534 #[serde(skip_serializing_if = "Option::is_none")]
535 #[builder(default, setter(into))]
536 pub(crate) description: Option<Option<Cow<'a, str>>>,
537
538 /// The flavor reference, as an ID (including a UUID) or full URL, for the
539 /// flavor for your server instance.
540 #[serde(rename = "flavorRef")]
541 #[builder(setter(into))]
542 pub(crate) flavor_ref: Cow<'a, str>,
543
544 /// The UUID of the image to use for your server instance. This is not
545 /// required in case of boot from volume. In all other cases it is required
546 /// and must be a valid UUID otherwise API will return 400.
547 #[serde(rename = "imageRef", skip_serializing_if = "Option::is_none")]
548 #[builder(default, setter(into))]
549 pub(crate) image_ref: Option<Cow<'a, str>>,
550
551 /// A target cell name. Schedule the server in a host in the cell
552 /// specified. It is available when `TargetCellFilter` is available on
553 /// cloud side that is cell v1 environment.
554 #[serde(skip_serializing_if = "Option::is_none")]
555 #[builder(default, setter(into))]
556 pub(crate) key_name: Option<Cow<'a, str>>,
557
558 #[serde(skip_serializing_if = "Option::is_none")]
559 #[builder(default, setter(into))]
560 pub(crate) max_count: Option<i32>,
561
562 /// Metadata key and value pairs. The maximum size of the metadata key and
563 /// value is 255 bytes each.
564 #[serde(skip_serializing_if = "Option::is_none")]
565 #[builder(default, private, setter(into, name = "_metadata"))]
566 pub(crate) metadata: Option<BTreeMap<Cow<'a, str>, Cow<'a, str>>>,
567
568 #[serde(skip_serializing_if = "Option::is_none")]
569 #[builder(default, setter(into))]
570 pub(crate) min_count: Option<i32>,
571
572 /// A target cell name. Schedule the server in a host in the cell
573 /// specified. It is available when `TargetCellFilter` is available on
574 /// cloud side that is cell v1 environment.
575 #[serde()]
576 #[builder(setter(into))]
577 pub(crate) name: Cow<'a, str>,
578
579 /// A list of `network` object. Required parameter when there are multiple
580 /// networks defined for the tenant. When you do not specify the networks
581 /// parameter, the server attaches to the only network created for the
582 /// current tenant. Optionally, you can create one or more NICs on the
583 /// server. To provision the server instance with a NIC for a network,
584 /// specify the UUID of the network in the `uuid` attribute in a `networks`
585 /// object. To provision the server instance with a NIC for an already
586 /// existing port, specify the port-id in the `port` attribute in a
587 /// `networks` object.
588 ///
589 /// If multiple networks are defined, the order in which they appear in the
590 /// guest operating system will not necessarily reflect the order in which
591 /// they are given in the server boot request. Guests should therefore not
592 /// depend on device order to deduce any information about their network
593 /// devices. Instead, device role tags should be used: introduced in 2.32,
594 /// broken in 2.37, and re-introduced and fixed in 2.42, the `tag` is an
595 /// optional, string attribute that can be used to assign a tag to a
596 /// virtual network interface. This tag is then exposed to the guest in the
597 /// metadata API and the config drive and is associated to hardware
598 /// metadata for that network interface, such as bus (ex: PCI), bus address
599 /// (ex: 0000:00:02.0), and MAC address.
600 ///
601 /// A bug has caused the `tag` attribute to no longer be accepted starting
602 /// with version 2.37. Therefore, network interfaces could only be tagged
603 /// in versions 2.32 to 2.36 inclusively. Version 2.42 has restored the
604 /// `tag` attribute.
605 ///
606 /// Starting with microversion 2.37, this field is required and the special
607 /// string values *auto* and *none* can be specified for networks. *auto*
608 /// tells the Compute service to use a network that is available to the
609 /// project, if one exists. If one does not exist, the Compute service will
610 /// attempt to automatically allocate a network for the project (if
611 /// possible). *none* tells the Compute service to not allocate a network
612 /// for the instance. The *auto* and *none* values cannot be used with any
613 /// other network values, including other network uuids, ports, fixed IPs
614 /// or device tags. These are requested as strings for the networks value,
615 /// not in a list. See the associated example.
616 #[serde()]
617 #[builder(setter(into))]
618 pub(crate) networks: ServerNetworks<'a>,
619
620 /// Controls how the API partitions the disk when you create, rebuild, or
621 /// resize servers. A server inherits the `OS-DCF:diskConfig` value from
622 /// the image from which it was created, and an image inherits the
623 /// `OS-DCF:diskConfig` value from the server from which it was created. To
624 /// override the inherited setting, you can include this attribute in the
625 /// request body of a server create, rebuild, or resize request. If the
626 /// `OS-DCF:diskConfig` value for an image is `MANUAL`, you cannot create a
627 /// server from that image and set its `OS-DCF:diskConfig` value to `AUTO`.
628 /// A valid value is:
629 ///
630 /// - `AUTO`. The API builds the server with a single partition the size of
631 /// the target flavor disk. The API automatically adjusts the file system
632 /// to fit the entire partition.
633 /// - `MANUAL`. The API builds the server by using whatever partition
634 /// scheme and file system is in the source image. If the target flavor
635 /// disk is larger, the API does not partition the remaining disk space.
636 #[serde(rename = "OS-DCF:diskConfig", skip_serializing_if = "Option::is_none")]
637 #[builder(default)]
638 pub(crate) os_dcf_disk_config: Option<OsDcfDiskConfig>,
639
640 /// The file path and contents, text only, to inject into the server at
641 /// launch. The maximum size of the file path data is 255 bytes. The
642 /// maximum limit is the number of allowed bytes in the decoded, rather
643 /// than encoded, data.
644 ///
645 /// **Available until version 2.56**
646 #[serde(skip_serializing_if = "Option::is_none")]
647 #[builder(default, setter(into))]
648 pub(crate) personality: Option<Vec<Personality<'a>>>,
649
650 /// Indicates whether a config drive enables metadata injection. The
651 /// config_drive setting provides information about a drive that the
652 /// instance can mount at boot time. The instance reads files from the
653 /// drive to get information that is normally available through the
654 /// metadata service. This metadata is different from the user data. Not
655 /// all cloud providers enable the `config_drive`. Read more in the
656 /// [OpenStack End User Guide](https://docs.openstack.org/nova/latest/user/config-drive.html).
657 #[serde(skip_serializing_if = "Option::is_none")]
658 #[builder(default, setter(into))]
659 pub(crate) return_reservation_id: Option<bool>,
660
661 /// One or more security groups. Specify the name of the security group in
662 /// the `name` attribute. If you omit this attribute, the API creates the
663 /// server in the `default` security group. Requested security groups are
664 /// not applied to pre-existing ports.
665 #[serde(skip_serializing_if = "Option::is_none")]
666 #[builder(default, setter(into))]
667 pub(crate) security_groups: Option<Vec<SecurityGroups<'a>>>,
668
669 /// Configuration information or scripts to use upon launch. Must be Base64
670 /// encoded. Restricted to 65535 bytes.
671 ///
672 /// Note
673 ///
674 /// The `null` value allowed in Nova legacy v2 API, but due to the strict
675 /// input validation, it isn’t allowed in Nova v2.1 API.
676 #[serde(skip_serializing_if = "Option::is_none")]
677 #[builder(default, setter(into))]
678 pub(crate) user_data: Option<Cow<'a, str>>,
679}
680
681impl<'a> ServerBuilder<'a> {
682 /// Metadata key and value pairs. The maximum size of the metadata key and
683 /// value is 255 bytes each.
684 pub fn metadata<I, K, V>(&mut self, iter: I) -> &mut Self
685 where
686 I: Iterator<Item = (K, V)>,
687 K: Into<Cow<'a, str>>,
688 V: Into<Cow<'a, str>>,
689 {
690 self.metadata
691 .get_or_insert(None)
692 .get_or_insert_with(BTreeMap::new)
693 .extend(iter.map(|(k, v)| (k.into(), v.into())));
694 self
695 }
696}
697
698#[derive(Builder, Debug, Clone)]
699#[builder(setter(strip_option))]
700pub struct Request<'a> {
701 #[builder(default, setter(into))]
702 pub(crate) os_sch_hnt_scheduler_hints: Option<OsSchHntSchedulerHints<'a>>,
703
704 /// The dictionary of data to send to the scheduler. Alternatively, you can
705 /// specify `OS-SCH-HNT:scheduler_hints` as the key in the request body.
706 ///
707 /// Note
708 ///
709 /// This is a top-level key in the request body, not part of the server
710 /// portion of the request body.
711 ///
712 /// There are a few caveats with scheduler hints:
713 ///
714 /// - The request validation schema is per hint. For example, some require
715 /// a single string value, and some accept a list of values.
716 /// - Hints are only used based on the cloud scheduler configuration, which
717 /// varies per deployment.
718 /// - Hints are pluggable per deployment, meaning that a cloud can have
719 /// custom hints which may not be available in another cloud.
720 ///
721 /// For these reasons, it is important to consult each cloud’s user
722 /// documentation to know what is available for scheduler hints.
723 #[builder(default, setter(into))]
724 pub(crate) os_scheduler_hints: Option<OsSchedulerHints<'a>>,
725
726 /// A `server` object.
727 #[builder(setter(into))]
728 pub(crate) server: Server<'a>,
729
730 #[builder(setter(name = "_headers"), default, private)]
731 _headers: Option<HeaderMap>,
732}
733impl<'a> Request<'a> {
734 /// Create a builder for the endpoint.
735 pub fn builder() -> RequestBuilder<'a> {
736 RequestBuilder::default()
737 }
738}
739
740impl<'a> RequestBuilder<'a> {
741 /// Add a single header to the Server.
742 pub fn header<K, V>(&mut self, header_name: K, header_value: V) -> &mut Self
743 where
744 K: Into<HeaderName>,
745 V: Into<HeaderValue>,
746 {
747 self._headers
748 .get_or_insert(None)
749 .get_or_insert_with(HeaderMap::new)
750 .insert(header_name.into(), header_value.into());
751 self
752 }
753
754 /// Add multiple headers.
755 pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
756 where
757 I: Iterator<Item = T>,
758 T: Into<(Option<HeaderName>, HeaderValue)>,
759 {
760 self._headers
761 .get_or_insert(None)
762 .get_or_insert_with(HeaderMap::new)
763 .extend(iter.map(Into::into));
764 self
765 }
766}
767
768impl RestEndpoint for Request<'_> {
769 fn method(&self) -> http::Method {
770 http::Method::POST
771 }
772
773 fn endpoint(&self) -> Cow<'static, str> {
774 "servers".to_string().into()
775 }
776
777 fn parameters(&self) -> QueryParams<'_> {
778 QueryParams::default()
779 }
780
781 fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
782 let mut params = JsonBodyParams::default();
783
784 if let Some(val) = &self.os_sch_hnt_scheduler_hints {
785 params.push("OS-SCH-HNT:scheduler_hints", serde_json::to_value(val)?);
786 }
787 if let Some(val) = &self.os_scheduler_hints {
788 params.push("os:scheduler_hints", serde_json::to_value(val)?);
789 }
790 params.push("server", serde_json::to_value(&self.server)?);
791
792 params.into_body()
793 }
794
795 fn service_type(&self) -> ServiceType {
796 ServiceType::Compute
797 }
798
799 fn response_key(&self) -> Option<Cow<'static, str>> {
800 Some("server".into())
801 }
802
803 /// Returns headers to be set into the request
804 fn request_headers(&self) -> Option<&HeaderMap> {
805 self._headers.as_ref()
806 }
807
808 /// Returns required API version
809 fn api_version(&self) -> Option<ApiVersion> {
810 Some(ApiVersion::new(2, 42))
811 }
812}
813
814#[cfg(test)]
815mod tests {
816 use super::*;
817 #[cfg(feature = "sync")]
818 use crate::api::Query;
819 use crate::test::client::FakeOpenStackClient;
820 use crate::types::ServiceType;
821 use http::{HeaderName, HeaderValue};
822 use httpmock::MockServer;
823 use serde_json::json;
824
825 #[test]
826 fn test_service_type() {
827 assert_eq!(
828 Request::builder()
829 .server(
830 ServerBuilder::default()
831 .flavor_ref("foo")
832 .name("foo")
833 .networks(ServerNetworks::F1(Vec::from([NetworksBuilder::default()
834 .build()
835 .unwrap()])))
836 .build()
837 .unwrap()
838 )
839 .build()
840 .unwrap()
841 .service_type(),
842 ServiceType::Compute
843 );
844 }
845
846 #[test]
847 fn test_response_key() {
848 assert_eq!(
849 Request::builder()
850 .server(
851 ServerBuilder::default()
852 .flavor_ref("foo")
853 .name("foo")
854 .networks(ServerNetworks::F1(Vec::from([NetworksBuilder::default()
855 .build()
856 .unwrap()])))
857 .build()
858 .unwrap()
859 )
860 .build()
861 .unwrap()
862 .response_key()
863 .unwrap(),
864 "server"
865 );
866 }
867
868 #[cfg(feature = "sync")]
869 #[test]
870 fn endpoint() {
871 let server = MockServer::start();
872 let client = FakeOpenStackClient::new(server.base_url());
873 let mock = server.mock(|when, then| {
874 when.method(httpmock::Method::POST)
875 .path("/servers".to_string());
876
877 then.status(200)
878 .header("content-type", "application/json")
879 .json_body(json!({ "server": {} }));
880 });
881
882 let endpoint = Request::builder()
883 .server(
884 ServerBuilder::default()
885 .flavor_ref("foo")
886 .name("foo")
887 .networks(ServerNetworks::F1(Vec::from([NetworksBuilder::default()
888 .build()
889 .unwrap()])))
890 .build()
891 .unwrap(),
892 )
893 .build()
894 .unwrap();
895 let _: serde_json::Value = endpoint.query(&client).unwrap();
896 mock.assert();
897 }
898
899 #[cfg(feature = "sync")]
900 #[test]
901 fn endpoint_headers() {
902 let server = MockServer::start();
903 let client = FakeOpenStackClient::new(server.base_url());
904 let mock = server.mock(|when, then| {
905 when.method(httpmock::Method::POST)
906 .path("/servers".to_string())
907 .header("foo", "bar")
908 .header("not_foo", "not_bar");
909 then.status(200)
910 .header("content-type", "application/json")
911 .json_body(json!({ "server": {} }));
912 });
913
914 let endpoint = Request::builder()
915 .server(
916 ServerBuilder::default()
917 .flavor_ref("foo")
918 .name("foo")
919 .networks(ServerNetworks::F1(Vec::from([NetworksBuilder::default()
920 .build()
921 .unwrap()])))
922 .build()
923 .unwrap(),
924 )
925 .headers(
926 [(
927 Some(HeaderName::from_static("foo")),
928 HeaderValue::from_static("bar"),
929 )]
930 .into_iter(),
931 )
932 .header(
933 HeaderName::from_static("not_foo"),
934 HeaderValue::from_static("not_bar"),
935 )
936 .build()
937 .unwrap();
938 let _: serde_json::Value = endpoint.query(&client).unwrap();
939 mock.assert();
940 }
941}