openstack_cli_compute/v2/server/create_21.rs
1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12//
13// SPDX-License-Identifier: Apache-2.0
14//
15// WARNING: This file is automatically generated from OpenAPI schema using
16// `openstack-codegenerator`.
17
18//! Create Server command [microversion = 2.1]
19//!
20//! Wraps invoking of the `v2.1/servers` with `POST` method
21
22use clap::Args;
23use eyre::WrapErr;
24use tracing::info;
25
26use openstack_cli_core::cli::CliArgs;
27use openstack_cli_core::error::OpenStackCliError;
28use openstack_cli_core::output::OutputProcessor;
29use openstack_sdk::AsyncOpenStack;
30
31use clap::ValueEnum;
32use openstack_cli_core::common::parse_key_val;
33use openstack_sdk::api::QueryAsync;
34use openstack_sdk::api::compute::v2::server::create_21;
35use openstack_types::compute::v2::server::response;
36use serde_json::Value;
37
38/// Creates a server.
39///
40/// The progress of this operation depends on the location of the requested
41/// image, network I/O, host load, selected flavor, and other factors.
42///
43/// To check the progress of the request, make a `GET /servers/{id}` request.
44/// This call returns a progress attribute, which is a percentage value from 0
45/// to 100.
46///
47/// The `Location` header returns the full URL to the newly created server and
48/// is available as a `self` and `bookmark` link in the server representation.
49///
50/// When you create a server, the response shows only the server ID, its links,
51/// and the admin password. You can get additional attributes through
52/// subsequent `GET` requests on the server.
53///
54/// Include the `block_device_mapping_v2` parameter in the create request body
55/// to boot a server from a volume.
56///
57/// Include the `key_name` parameter in the create request body to add a
58/// keypair to the server when you create it. To create a keypair, make a
59/// [create keypair](https://docs.openstack.org/api-ref/compute/#create-or-import-keypair)
60/// request.
61///
62/// **Preconditions**
63///
64/// **Asynchronous postconditions**
65///
66/// **Troubleshooting**
67///
68/// Normal response codes: 202
69///
70/// Error response codes: badRequest(400), unauthorized(401), forbidden(403),
71/// itemNotFound(404), conflict(409)
72#[derive(Args)]
73#[command(about = "Create Server (microversion = 2.1)")]
74pub struct ServerCommand {
75 /// Request Query parameters
76 #[command(flatten)]
77 query: QueryParameters,
78
79 /// Path parameters
80 #[command(flatten)]
81 path: PathParameters,
82
83 /// The dictionary of data to send to the scheduler. Alternatively, you can
84 /// specify `OS-SCH-HNT:scheduler_hints` as the key in the request body.
85 ///
86 /// Note
87 ///
88 /// This is a top-level key in the request body, not part of the server
89 /// portion of the request body.
90 ///
91 /// There are a few caveats with scheduler hints:
92 ///
93 /// - The request validation schema is per hint. For example, some require
94 /// a single string value, and some accept a list of values.
95 /// - Hints are only used based on the cloud scheduler configuration, which
96 /// varies per deployment.
97 /// - Hints are pluggable per deployment, meaning that a cloud can have
98 /// custom hints which may not be available in another cloud.
99 ///
100 /// For these reasons, it is important to consult each cloud’s user
101 /// documentation to know what is available for scheduler hints.
102 #[command(flatten)]
103 os_scheduler_hints: Option<OsSchedulerHints>,
104
105 /// A `server` object.
106 #[command(flatten)]
107 server: Server,
108}
109
110/// Query parameters
111#[derive(Args)]
112struct QueryParameters {}
113
114/// Path parameters
115#[derive(Args)]
116struct PathParameters {}
117/// OsSchedulerHints Body data
118#[derive(Args, Clone)]
119struct OsSchedulerHints {
120 /// Schedule the server on a host in the network specified with this
121 /// parameter and a cidr (`os:scheduler_hints.cidr`). It is available when
122 /// `SimpleCIDRAffinityFilter` is available on cloud side.
123 #[arg(help_heading = "Body parameters", long)]
124 build_near_host_ip: Option<String>,
125
126 /// Schedule the server on a host in the network specified with an IP
127 /// address (`os:scheduler_hints:build_near_host_ip`) and this parameter.
128 /// If `os:scheduler_hints:build_near_host_ip` is specified and this
129 /// parameter is omitted, `/24` is used. It is available when
130 /// `SimpleCIDRAffinityFilter` is available on cloud side.
131 #[arg(help_heading = "Body parameters", long)]
132 cidr: Option<String>,
133
134 /// A list of cell routes or a cell route (string). Schedule the server in
135 /// a cell that is not specified. It is available when
136 /// `DifferentCellFilter` is available on cloud side that is cell v1
137 /// environment.
138 ///
139 /// Parameter is an array, may be provided multiple times.
140 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long)]
141 different_cell: Option<Vec<String>>,
142
143 /// A list of server UUIDs or a server UUID. Schedule the server on a
144 /// different host from a set of servers. It is available when
145 /// `DifferentHostFilter` is available on cloud side.
146 ///
147 /// Parameter is an array, may be provided multiple times.
148 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long)]
149 different_host: Option<Vec<String>>,
150
151 /// The server group UUID. Schedule the server according to a policy of the
152 /// server group (`anti-affinity`, `affinity`, `soft-anti-affinity` or
153 /// `soft-affinity`). It is available when `ServerGroupAffinityFilter`,
154 /// `ServerGroupAntiAffinityFilter`, `ServerGroupSoftAntiAffinityWeigher`,
155 /// `ServerGroupSoftAffinityWeigher` are available on cloud side.
156 ///
157 /// Parameter is an array, may be provided multiple times.
158 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long)]
159 group: Option<Vec<String>>,
160
161 /// Schedule the server by using a custom filter in JSON format. For
162 /// example:
163 ///
164 /// ```text
165 /// "query": "[\">=\",\"$free_ram_mb\",1024]"
166 /// ```
167 ///
168 /// It is available when `JsonFilter` is available on cloud side.
169 #[arg(help_heading = "Body parameters", long, value_name="JSON", value_parser=openstack_cli_core::common::parse_json)]
170 query: Option<Value>,
171
172 /// A list of server UUIDs or a server UUID. Schedule the server on the
173 /// same host as another server in a set of servers. It is available when
174 /// `SameHostFilter` is available on cloud side.
175 ///
176 /// Parameter is an array, may be provided multiple times.
177 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long)]
178 same_host: Option<Vec<String>>,
179
180 /// A target cell name. Schedule the server in a host in the cell
181 /// specified. It is available when `TargetCellFilter` is available on
182 /// cloud side that is cell v1 environment.
183 #[arg(help_heading = "Body parameters", long)]
184 target_cell: Option<String>,
185}
186
187#[derive(Clone, Eq, Ord, PartialEq, PartialOrd, ValueEnum)]
188enum OsDcfDiskConfig {
189 Auto,
190 Manual,
191}
192
193/// Server Body data
194#[derive(Args, Clone)]
195struct Server {
196 /// IPv4 address that should be used to access this server.
197 #[arg(help_heading = "Body parameters", long)]
198 access_ipv4: Option<String>,
199
200 /// IPv6 address that should be used to access this server.
201 #[arg(help_heading = "Body parameters", long)]
202 access_ipv6: Option<String>,
203
204 /// The administrative password of the server. If you omit this parameter,
205 /// the operation generates a new password.
206 #[arg(help_heading = "Body parameters", long)]
207 admin_pass: Option<String>,
208
209 /// A target cell name. Schedule the server in a host in the cell
210 /// specified. It is available when `TargetCellFilter` is available on
211 /// cloud side that is cell v1 environment.
212 #[arg(help_heading = "Body parameters", long)]
213 availability_zone: Option<String>,
214
215 /// Parameter is an array, may be provided multiple times.
216 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long, value_name="JSON", value_parser=openstack_cli_core::common::parse_json)]
217 block_device_mapping: Option<Vec<Value>>,
218
219 /// Enables fine grained control of the block device mapping for an
220 /// instance. This is typically used for booting servers from volumes. An
221 /// example format would look as follows:
222 ///
223 /// > ```text
224 /// > "block_device_mapping_v2": [{
225 /// > "boot_index": "0",
226 /// > "uuid": "ac408821-c95a-448f-9292-73986c790911",
227 /// > "source_type": "image",
228 /// > "volume_size": "25",
229 /// > "destination_type": "volume",
230 /// > "delete_on_termination": true,
231 /// > "tag": "disk1",
232 /// > "disk_bus": "scsi"}]
233 /// > ```
234 ///
235 /// In microversion 2.32, `tag` is an optional string attribute that can be
236 /// used to assign a tag to the block device. This tag is then exposed to
237 /// the guest in the metadata API and the config drive and is associated to
238 /// hardware metadata for that block device, such as bus (ex: SCSI), bus
239 /// address (ex: 1:0:2:0), and serial.
240 ///
241 /// A bug has caused the `tag` attribute to no longer be accepted starting
242 /// with version 2.33. It has been restored in version 2.42.
243 ///
244 /// Parameter is an array, may be provided multiple times.
245 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long, value_name="JSON", value_parser=openstack_cli_core::common::parse_json)]
246 block_device_mapping_v2: Option<Vec<Value>>,
247
248 /// Indicates whether a config drive enables metadata injection. The
249 /// config_drive setting provides information about a drive that the
250 /// instance can mount at boot time. The instance reads files from the
251 /// drive to get information that is normally available through the
252 /// metadata service. This metadata is different from the user data. Not
253 /// all cloud providers enable the `config_drive`. Read more in the
254 /// [OpenStack End User Guide](https://docs.openstack.org/nova/latest/user/config-drive.html).
255 #[arg(action=clap::ArgAction::Set, help_heading = "Body parameters", long)]
256 config_drive: Option<bool>,
257
258 /// The flavor reference, as an ID (including a UUID) or full URL, for the
259 /// flavor for your server instance.
260 #[arg(help_heading = "Body parameters", long)]
261 flavor_ref: String,
262
263 /// The UUID of the image to use for your server instance. This is not
264 /// required in case of boot from volume. In all other cases it is required
265 /// and must be a valid UUID otherwise API will return 400.
266 #[arg(help_heading = "Body parameters", long)]
267 image_ref: Option<String>,
268
269 /// A target cell name. Schedule the server in a host in the cell
270 /// specified. It is available when `TargetCellFilter` is available on
271 /// cloud side that is cell v1 environment.
272 #[arg(help_heading = "Body parameters", long)]
273 key_name: Option<String>,
274
275 #[arg(help_heading = "Body parameters", long)]
276 max_count: Option<i32>,
277
278 /// Metadata key and value pairs. The maximum size of the metadata key and
279 /// value is 255 bytes each.
280 #[arg(help_heading = "Body parameters", long, value_name="key=value", value_parser=parse_key_val::<String, String>)]
281 metadata: Option<Vec<(String, String)>>,
282
283 #[arg(help_heading = "Body parameters", long)]
284 min_count: Option<i32>,
285
286 /// A target cell name. Schedule the server in a host in the cell
287 /// specified. It is available when `TargetCellFilter` is available on
288 /// cloud side that is cell v1 environment.
289 #[arg(help_heading = "Body parameters", long)]
290 name: String,
291
292 /// A list of `network` object. Required parameter when there are multiple
293 /// networks defined for the tenant. When you do not specify the networks
294 /// parameter, the server attaches to the only network created for the
295 /// current tenant. Optionally, you can create one or more NICs on the
296 /// server. To provision the server instance with a NIC for a network,
297 /// specify the UUID of the network in the `uuid` attribute in a `networks`
298 /// object. To provision the server instance with a NIC for an already
299 /// existing port, specify the port-id in the `port` attribute in a
300 /// `networks` object.
301 ///
302 /// If multiple networks are defined, the order in which they appear in the
303 /// guest operating system will not necessarily reflect the order in which
304 /// they are given in the server boot request. Guests should therefore not
305 /// depend on device order to deduce any information about their network
306 /// devices. Instead, device role tags should be used: introduced in 2.32,
307 /// broken in 2.37, and re-introduced and fixed in 2.42, the `tag` is an
308 /// optional, string attribute that can be used to assign a tag to a
309 /// virtual network interface. This tag is then exposed to the guest in the
310 /// metadata API and the config drive and is associated to hardware
311 /// metadata for that network interface, such as bus (ex: PCI), bus address
312 /// (ex: 0000:00:02.0), and MAC address.
313 ///
314 /// A bug has caused the `tag` attribute to no longer be accepted starting
315 /// with version 2.37. Therefore, network interfaces could only be tagged
316 /// in versions 2.32 to 2.36 inclusively. Version 2.42 has restored the
317 /// `tag` attribute.
318 ///
319 /// Starting with microversion 2.37, this field is required and the special
320 /// string values *auto* and *none* can be specified for networks. *auto*
321 /// tells the Compute service to use a network that is available to the
322 /// project, if one exists. If one does not exist, the Compute service will
323 /// attempt to automatically allocate a network for the project (if
324 /// possible). *none* tells the Compute service to not allocate a network
325 /// for the instance. The *auto* and *none* values cannot be used with any
326 /// other network values, including other network uuids, ports, fixed IPs
327 /// or device tags. These are requested as strings for the networks value,
328 /// not in a list. See the associated example.
329 ///
330 /// Parameter is an array, may be provided multiple times.
331 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long, value_name="JSON", value_parser=openstack_cli_core::common::parse_json)]
332 networks: Option<Vec<Value>>,
333
334 /// Controls how the API partitions the disk when you create, rebuild, or
335 /// resize servers. A server inherits the `OS-DCF:diskConfig` value from
336 /// the image from which it was created, and an image inherits the
337 /// `OS-DCF:diskConfig` value from the server from which it was created. To
338 /// override the inherited setting, you can include this attribute in the
339 /// request body of a server create, rebuild, or resize request. If the
340 /// `OS-DCF:diskConfig` value for an image is `MANUAL`, you cannot create a
341 /// server from that image and set its `OS-DCF:diskConfig` value to `AUTO`.
342 /// A valid value is:
343 ///
344 /// - `AUTO`. The API builds the server with a single partition the size of
345 /// the target flavor disk. The API automatically adjusts the file system
346 /// to fit the entire partition.
347 /// - `MANUAL`. The API builds the server by using whatever partition
348 /// scheme and file system is in the source image. If the target flavor
349 /// disk is larger, the API does not partition the remaining disk space.
350 #[arg(help_heading = "Body parameters", long)]
351 os_dcf_disk_config: Option<OsDcfDiskConfig>,
352
353 /// The file path and contents, text only, to inject into the server at
354 /// launch. The maximum size of the file path data is 255 bytes. The
355 /// maximum limit is the number of allowed bytes in the decoded, rather
356 /// than encoded, data.
357 ///
358 /// **Available until version 2.56**
359 ///
360 /// Parameter is an array, may be provided multiple times.
361 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long, value_name="JSON", value_parser=openstack_cli_core::common::parse_json)]
362 personality: Option<Vec<Value>>,
363
364 /// Indicates whether a config drive enables metadata injection. The
365 /// config_drive setting provides information about a drive that the
366 /// instance can mount at boot time. The instance reads files from the
367 /// drive to get information that is normally available through the
368 /// metadata service. This metadata is different from the user data. Not
369 /// all cloud providers enable the `config_drive`. Read more in the
370 /// [OpenStack End User Guide](https://docs.openstack.org/nova/latest/user/config-drive.html).
371 #[arg(action=clap::ArgAction::Set, help_heading = "Body parameters", long)]
372 return_reservation_id: Option<bool>,
373
374 /// One or more security groups. Specify the name of the security group in
375 /// the `name` attribute. If you omit this attribute, the API creates the
376 /// server in the `default` security group. Requested security groups are
377 /// not applied to pre-existing ports.
378 ///
379 /// Parameter is an array, may be provided multiple times.
380 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long)]
381 security_groups: Option<Vec<String>>,
382
383 /// Configuration information or scripts to use upon launch. Must be Base64
384 /// encoded. Restricted to 65535 bytes.
385 ///
386 /// Note
387 ///
388 /// The `null` value allowed in Nova legacy v2 API, but due to the strict
389 /// input validation, it isn’t allowed in Nova v2.1 API.
390 #[arg(help_heading = "Body parameters", long)]
391 user_data: Option<String>,
392}
393
394impl ServerCommand {
395 /// Perform command action
396 pub async fn take_action<C: CliArgs>(
397 &self,
398 parsed_args: &C,
399 client: &mut AsyncOpenStack,
400 ) -> Result<(), OpenStackCliError> {
401 info!("Create Server");
402
403 let op = OutputProcessor::from_args(parsed_args, Some("compute.server"), Some("create"));
404 op.validate_args(parsed_args)?;
405
406 let mut ep_builder = create_21::Request::builder();
407 ep_builder.header(
408 http::header::HeaderName::from_static("openstack-api-version"),
409 http::header::HeaderValue::from_static("compute 2.1"),
410 );
411
412 // Set body parameters
413 // Set Request.os_scheduler_hints data
414 if let Some(args) = &self.os_scheduler_hints {
415 let mut os_scheduler_hints_builder = create_21::OsSchedulerHintsBuilder::default();
416 if let Some(val) = &args.build_near_host_ip {
417 os_scheduler_hints_builder.build_near_host_ip(val);
418 }
419
420 if let Some(val) = &args.cidr {
421 os_scheduler_hints_builder.cidr(val);
422 }
423
424 if let Some(val) = &args.different_cell {
425 os_scheduler_hints_builder
426 .different_cell(val.iter().map(Into::into).collect::<Vec<_>>());
427 }
428
429 if let Some(val) = &args.different_host {
430 os_scheduler_hints_builder
431 .different_host(val.iter().map(Into::into).collect::<Vec<_>>());
432 }
433
434 if let Some(val) = &args.group {
435 os_scheduler_hints_builder.group(val.iter().map(Into::into).collect::<Vec<_>>());
436 }
437
438 if let Some(val) = &args.query {
439 os_scheduler_hints_builder.query(val.clone());
440 }
441
442 if let Some(val) = &args.same_host {
443 os_scheduler_hints_builder
444 .same_host(val.iter().map(Into::into).collect::<Vec<_>>());
445 }
446
447 if let Some(val) = &args.target_cell {
448 os_scheduler_hints_builder.target_cell(val);
449 }
450
451 ep_builder.os_scheduler_hints(
452 os_scheduler_hints_builder
453 .build()
454 .wrap_err("error preparing the request data")?,
455 );
456 }
457
458 // Set Request.server data
459 let args = &self.server;
460 let mut server_builder = create_21::ServerBuilder::default();
461 if let Some(val) = &args.os_dcf_disk_config {
462 let tmp = match val {
463 OsDcfDiskConfig::Auto => create_21::OsDcfDiskConfig::Auto,
464 OsDcfDiskConfig::Manual => create_21::OsDcfDiskConfig::Manual,
465 };
466 server_builder.os_dcf_disk_config(tmp);
467 }
468
469 if let Some(val) = &args.access_ipv4 {
470 server_builder.access_ipv4(val);
471 }
472
473 if let Some(val) = &args.access_ipv6 {
474 server_builder.access_ipv6(val);
475 }
476
477 if let Some(val) = &args.admin_pass {
478 server_builder.admin_pass(val);
479 }
480
481 if let Some(val) = &args.availability_zone {
482 server_builder.availability_zone(val);
483 }
484
485 if let Some(val) = &args.block_device_mapping {
486 let block_device_mapping_builder: Vec<create_21::BlockDeviceMapping> = val
487 .iter()
488 .flat_map(|v| serde_json::from_value::<create_21::BlockDeviceMapping>(v.to_owned()))
489 .collect::<Vec<create_21::BlockDeviceMapping>>();
490 server_builder.block_device_mapping(block_device_mapping_builder);
491 }
492
493 if let Some(val) = &args.block_device_mapping_v2 {
494 let block_device_mapping_v2_builder: Vec<create_21::BlockDeviceMappingV2> = val
495 .iter()
496 .flat_map(|v| {
497 serde_json::from_value::<create_21::BlockDeviceMappingV2>(v.to_owned())
498 })
499 .collect::<Vec<create_21::BlockDeviceMappingV2>>();
500 server_builder.block_device_mapping_v2(block_device_mapping_v2_builder);
501 }
502
503 if let Some(val) = &args.config_drive {
504 server_builder.config_drive(*val);
505 }
506
507 server_builder.flavor_ref(&args.flavor_ref);
508
509 if let Some(val) = &args.image_ref {
510 server_builder.image_ref(val);
511 }
512
513 if let Some(val) = &args.key_name {
514 server_builder.key_name(val);
515 }
516
517 if let Some(val) = &args.max_count {
518 server_builder.max_count(*val);
519 }
520
521 if let Some(val) = &args.metadata {
522 server_builder.metadata(val.iter().cloned());
523 }
524
525 if let Some(val) = &args.min_count {
526 server_builder.min_count(*val);
527 }
528
529 server_builder.name(&args.name);
530
531 if let Some(val) = &args.networks {
532 let networks_builder: Vec<create_21::Networks> = val
533 .iter()
534 .flat_map(|v| serde_json::from_value::<create_21::Networks>(v.to_owned()))
535 .collect::<Vec<create_21::Networks>>();
536 server_builder.networks(networks_builder);
537 }
538
539 if let Some(val) = &args.personality {
540 let personality_builder: Vec<create_21::Personality> = val
541 .iter()
542 .flat_map(|v| serde_json::from_value::<create_21::Personality>(v.to_owned()))
543 .collect::<Vec<create_21::Personality>>();
544 server_builder.personality(personality_builder);
545 }
546
547 if let Some(val) = &args.return_reservation_id {
548 server_builder.return_reservation_id(*val);
549 }
550
551 if let Some(val) = &args.security_groups {
552 let security_groups_builder: Vec<create_21::SecurityGroups> = val
553 .iter()
554 .flat_map(|v| create_21::SecurityGroupsBuilder::default().name(v).build())
555 .collect();
556 server_builder.security_groups(security_groups_builder);
557 }
558
559 if let Some(val) = &args.user_data {
560 server_builder.user_data(val);
561 }
562
563 ep_builder.server(
564 server_builder
565 .build()
566 .wrap_err("error preparing the request data")?,
567 );
568
569 let ep = ep_builder
570 .build()
571 .map_err(|x| OpenStackCliError::EndpointBuild(x.to_string()))?;
572
573 let data: serde_json::Value = ep.query_async(client).await?;
574
575 op.output_single::<response::create_a::ServerResponse>(data.clone())
576 .or_else(|_| op.output_single::<response::create_b::ServerResponse>(data.clone()))?;
577 // Show command specific hints
578 op.show_command_hint()?;
579 Ok(())
580 }
581}