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