openstack_cli_network/v2/port/
create.rs1use clap::Args;
23use eyre::{OptionExt, 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::network::v2::port::create;
35use openstack_types::network::v2::port::response;
36use serde_json::Value;
37
38#[derive(Args)]
47#[command(about = "Create port")]
48pub struct PortCommand {
49 #[command(flatten)]
51 query: QueryParameters,
52
53 #[command(flatten)]
55 path: PathParameters,
56
57 #[command(flatten)]
59 port: Port,
60}
61
62#[derive(Args)]
64struct QueryParameters {}
65
66#[derive(Args)]
68struct PathParameters {}
69
70#[derive(Clone, Eq, Ord, PartialEq, PartialOrd, ValueEnum)]
71enum BindingVnicType {
72 AcceleratorDirect,
73 AcceleratorDirectPhysical,
74 Baremetal,
75 Direct,
76 DirectPhysical,
77 Macvtap,
78 Normal,
79 RemoteManaged,
80 SmartNic,
81 Vdpa,
82 VirtioForwarder,
83}
84
85#[derive(Clone, Eq, Ord, PartialEq, PartialOrd, ValueEnum)]
86enum NumaAffinityPolicy {
87 Legacy,
88 Preferred,
89 Required,
90 Socket,
91}
92
93#[derive(Args, Clone)]
95struct Port {
96 #[arg(action=clap::ArgAction::Set, help_heading = "Body parameters", long)]
99 admin_state_up: Option<bool>,
100
101 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long, value_name="JSON", value_parser=openstack_cli_core::common::parse_json)]
111 allowed_address_pairs: Option<Vec<Value>>,
112
113 #[arg(help_heading = "Body parameters", long)]
116 binding_host_id: Option<String>,
117
118 #[arg(help_heading = "Body parameters", long, value_name="key=value", value_parser=parse_key_val::<String, Value>)]
135 binding_profile: Option<Vec<(String, Value)>>,
136
137 #[arg(help_heading = "Body parameters", long)]
144 binding_vnic_type: Option<BindingVnicType>,
145
146 #[arg(help_heading = "Body parameters", long)]
149 description: Option<String>,
150
151 #[arg(help_heading = "Body parameters", long)]
154 device_id: Option<String>,
155
156 #[arg(help_heading = "Body parameters", long)]
160 device_owner: Option<String>,
161
162 #[arg(help_heading = "Body parameters", long)]
163 device_profile: Option<String>,
164
165 #[arg(help_heading = "Body parameters", long, action = clap::ArgAction::SetTrue, conflicts_with = "device_profile")]
167 no_device_profile: bool,
168
169 #[arg(help_heading = "Body parameters", long)]
171 dns_domain: Option<String>,
172
173 #[arg(help_heading = "Body parameters", long)]
175 dns_name: Option<String>,
176
177 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long, value_name="JSON", value_parser=openstack_cli_core::common::parse_json)]
182 extra_dhcp_opts: Option<Vec<Value>>,
183
184 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long, value_name="JSON", value_parser=openstack_cli_core::common::parse_json)]
200 fixed_ips: Option<Vec<Value>>,
201
202 #[arg(help_heading = "Body parameters", long, value_name="key=value", value_parser=parse_key_val::<String, Value>)]
212 hints: Option<Vec<(String, Value)>>,
213
214 #[arg(help_heading = "Body parameters", long)]
217 mac_address: Option<String>,
218
219 #[arg(help_heading = "Body parameters", long)]
221 name: Option<String>,
222
223 #[arg(help_heading = "Body parameters", long)]
225 network_id: Option<String>,
226
227 #[arg(help_heading = "Body parameters", long)]
230 numa_affinity_policy: Option<NumaAffinityPolicy>,
231
232 #[arg(action=clap::ArgAction::Set, help_heading = "Body parameters", long)]
237 port_security_enabled: Option<bool>,
238
239 #[arg(action=clap::ArgAction::Set, help_heading = "Body parameters", long)]
242 propagate_uplink_status: Option<bool>,
243
244 #[arg(help_heading = "Body parameters", long)]
246 qos_policy_id: Option<String>,
247
248 #[arg(help_heading = "Body parameters", long, action = clap::ArgAction::SetTrue, conflicts_with = "qos_policy_id")]
250 no_qos_policy_id: bool,
251
252 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long)]
256 security_groups: Option<Vec<String>>,
257
258 #[arg(action=clap::ArgAction::Append, help_heading = "Body parameters", long)]
260 tags: Option<Vec<String>>,
261
262 #[arg(help_heading = "Body parameters", long)]
266 tenant_id: Option<String>,
267}
268
269impl PortCommand {
270 pub async fn take_action<C: CliArgs>(
272 &self,
273 parsed_args: &C,
274 client: &mut AsyncOpenStack,
275 ) -> Result<(), OpenStackCliError> {
276 info!("Create Port");
277
278 let op = OutputProcessor::from_args(parsed_args, Some("network.port"), Some("create"));
279 op.validate_args(parsed_args)?;
280
281 let mut ep_builder = create::Request::builder();
282
283 let args = &self.port;
286 let mut port_builder = create::PortBuilder::default();
287 if let Some(val) = &args.admin_state_up {
288 port_builder.admin_state_up(*val);
289 }
290
291 if let Some(val) = &args.allowed_address_pairs {
292 let allowed_address_pairs_builder: Vec<create::AllowedAddressPairs> = val
293 .iter()
294 .flat_map(|v| serde_json::from_value::<create::AllowedAddressPairs>(v.to_owned()))
295 .collect::<Vec<create::AllowedAddressPairs>>();
296 port_builder.allowed_address_pairs(allowed_address_pairs_builder);
297 }
298
299 if let Some(val) = &args.binding_host_id {
300 port_builder.binding_host_id(val);
301 }
302
303 if let Some(val) = &args.binding_profile {
304 port_builder.binding_profile(val.iter().cloned());
305 }
306
307 if let Some(val) = &args.binding_vnic_type {
308 let tmp = match val {
309 BindingVnicType::AcceleratorDirect => create::BindingVnicType::AcceleratorDirect,
310 BindingVnicType::AcceleratorDirectPhysical => {
311 create::BindingVnicType::AcceleratorDirectPhysical
312 }
313 BindingVnicType::Baremetal => create::BindingVnicType::Baremetal,
314 BindingVnicType::Direct => create::BindingVnicType::Direct,
315 BindingVnicType::DirectPhysical => create::BindingVnicType::DirectPhysical,
316 BindingVnicType::Macvtap => create::BindingVnicType::Macvtap,
317 BindingVnicType::Normal => create::BindingVnicType::Normal,
318 BindingVnicType::RemoteManaged => create::BindingVnicType::RemoteManaged,
319 BindingVnicType::SmartNic => create::BindingVnicType::SmartNic,
320 BindingVnicType::Vdpa => create::BindingVnicType::Vdpa,
321 BindingVnicType::VirtioForwarder => create::BindingVnicType::VirtioForwarder,
322 };
323 port_builder.binding_vnic_type(tmp);
324 }
325
326 if let Some(val) = &args.description {
327 port_builder.description(val);
328 }
329
330 if let Some(val) = &args.device_id {
331 port_builder.device_id(val);
332 }
333
334 if let Some(val) = &args.device_owner {
335 port_builder.device_owner(val);
336 }
337
338 if let Some(val) = &args.device_profile {
339 port_builder.device_profile(Some(val.into()));
340 } else if args.no_device_profile {
341 port_builder.device_profile(None);
342 }
343
344 if let Some(val) = &args.dns_domain {
345 port_builder.dns_domain(val);
346 }
347
348 if let Some(val) = &args.dns_name {
349 port_builder.dns_name(val);
350 }
351
352 if let Some(val) = &args.extra_dhcp_opts {
353 use std::collections::BTreeMap;
354 port_builder.extra_dhcp_opts(
355 val.iter()
356 .map(|v| {
357 v.as_object()
358 .ok_or_eyre("extra_dhcp_opts must be a valid json object")
359 .map(|obj| {
360 obj.into_iter()
361 .map(|(k, v)| (k.into(), v.clone()))
362 .collect::<BTreeMap<_, Value>>()
363 })
364 })
365 .collect::<Result<Vec<_>, _>>()?,
366 );
367 }
368
369 if let Some(val) = &args.fixed_ips {
370 let fixed_ips_builder: Vec<create::FixedIps> = val
371 .iter()
372 .flat_map(|v| serde_json::from_value::<create::FixedIps>(v.to_owned()))
373 .collect::<Vec<create::FixedIps>>();
374 port_builder.fixed_ips(fixed_ips_builder);
375 }
376
377 if let Some(val) = &args.hints {
378 port_builder.hints(val.iter().cloned());
379 }
380
381 if let Some(val) = &args.mac_address {
382 port_builder.mac_address(val);
383 }
384
385 if let Some(val) = &args.name {
386 port_builder.name(val);
387 }
388
389 if let Some(val) = &args.network_id {
390 port_builder.network_id(val);
391 }
392
393 if let Some(val) = &args.numa_affinity_policy {
394 let tmp = match val {
395 NumaAffinityPolicy::Legacy => create::NumaAffinityPolicy::Legacy,
396 NumaAffinityPolicy::Preferred => create::NumaAffinityPolicy::Preferred,
397 NumaAffinityPolicy::Required => create::NumaAffinityPolicy::Required,
398 NumaAffinityPolicy::Socket => create::NumaAffinityPolicy::Socket,
399 };
400 port_builder.numa_affinity_policy(tmp);
401 }
402
403 if let Some(val) = &args.port_security_enabled {
404 port_builder.port_security_enabled(*val);
405 }
406
407 if let Some(val) = &args.propagate_uplink_status {
408 port_builder.propagate_uplink_status(*val);
409 }
410
411 if let Some(val) = &args.qos_policy_id {
412 port_builder.qos_policy_id(Some(val.into()));
413 } else if args.no_qos_policy_id {
414 port_builder.qos_policy_id(None);
415 }
416
417 if let Some(val) = &args.security_groups {
418 port_builder.security_groups(val.iter().map(Into::into).collect::<Vec<_>>());
419 }
420
421 if let Some(val) = &args.tags {
422 port_builder.tags(val.iter().map(Into::into).collect::<Vec<_>>());
423 }
424
425 if let Some(val) = &args.tenant_id {
426 port_builder.tenant_id(val);
427 }
428
429 ep_builder.port(
430 port_builder
431 .build()
432 .wrap_err("error preparing the request data")?,
433 );
434
435 let ep = ep_builder
436 .build()
437 .map_err(|x| OpenStackCliError::EndpointBuild(x.to_string()))?;
438
439 let data: serde_json::Value = ep.query_async(client).await?;
440
441 op.output_single::<response::create::PortResponse>(data.clone())?;
442 op.show_command_hint()?;
444 Ok(())
445 }
446}