1use clap::Args;
23use eyre::OptionExt;
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 openstack_sdk::api::QueryAsync;
32use openstack_sdk::api::compute::v2::server::list_detailed;
33use openstack_sdk::api::find_by_name;
34use openstack_sdk::api::identity::v3::project::find as find_project;
35use openstack_sdk::api::identity::v3::user::find as find_user;
36use openstack_sdk::api::{Pagination, paged};
37use openstack_types::compute::v2::server::response;
38use tracing::warn;
39
40#[derive(Args)]
55#[command(about = "List Servers Detailed")]
56pub struct ServersCommand {
57 #[command(flatten)]
59 query: QueryParameters,
60
61 #[command(flatten)]
63 path: PathParameters,
64
65 #[arg(long, default_value_t = 10000)]
67 max_items: usize,
68}
69
70#[derive(Args)]
72struct QueryParameters {
73 #[arg(help_heading = "Query parameters", long)]
74 access_ip_v4: Option<String>,
75
76 #[arg(help_heading = "Query parameters", long)]
77 access_ip_v6: Option<String>,
78
79 #[arg(help_heading = "Query parameters", long)]
80 all_tenants: Option<String>,
81
82 #[arg(help_heading = "Query parameters", long)]
83 auto_disk_config: Option<String>,
84
85 #[arg(help_heading = "Query parameters", long)]
86 availability_zone: Option<String>,
87
88 #[arg(help_heading = "Query parameters", long)]
89 block_device_mapping: Option<String>,
90
91 #[arg(help_heading = "Query parameters", long)]
92 changes_before: Option<String>,
93
94 #[arg(help_heading = "Query parameters", long)]
95 changes_since: Option<String>,
96
97 #[arg(help_heading = "Query parameters", long)]
98 config_drive: Option<String>,
99
100 #[arg(help_heading = "Query parameters", long)]
101 created_at: Option<String>,
102
103 #[arg(help_heading = "Query parameters", long)]
104 deleted: Option<String>,
105
106 #[arg(help_heading = "Query parameters", long)]
107 description: Option<String>,
108
109 #[arg(help_heading = "Query parameters", long)]
110 display_description: Option<String>,
111
112 #[arg(help_heading = "Query parameters", long)]
113 display_name: Option<String>,
114
115 #[arg(help_heading = "Query parameters", long)]
116 flavor: Option<String>,
117
118 #[arg(help_heading = "Query parameters", long)]
119 host: Option<String>,
120
121 #[arg(help_heading = "Query parameters", long)]
122 hostname: Option<String>,
123
124 #[arg(help_heading = "Query parameters", long)]
125 image: Option<String>,
126
127 #[arg(help_heading = "Query parameters", long)]
128 image_ref: Option<String>,
129
130 #[arg(help_heading = "Query parameters", long)]
131 info_cache: Option<String>,
132
133 #[arg(help_heading = "Query parameters", long)]
134 ip: Option<String>,
135
136 #[arg(help_heading = "Query parameters", long)]
137 ip6: Option<String>,
138
139 #[arg(help_heading = "Query parameters", long)]
140 kernel_id: Option<String>,
141
142 #[arg(help_heading = "Query parameters", long)]
143 key_name: Option<String>,
144
145 #[arg(help_heading = "Query parameters", long)]
146 launch_index: Option<String>,
147
148 #[arg(help_heading = "Query parameters", long)]
149 launched_at: Option<String>,
150
151 #[arg(
156 help_heading = "Query parameters",
157 long("page-size"),
158 visible_alias("limit")
159 )]
160 limit: Option<u32>,
161
162 #[arg(help_heading = "Query parameters", long)]
163 locked: Option<String>,
164
165 #[arg(help_heading = "Query parameters", long)]
166 locked_by: Option<String>,
167
168 #[arg(help_heading = "Query parameters", long)]
172 marker: Option<String>,
173
174 #[arg(help_heading = "Query parameters", long)]
175 metadata: Option<String>,
176
177 #[arg(help_heading = "Query parameters", long)]
178 name: Option<String>,
179
180 #[arg(help_heading = "Query parameters", long)]
181 node: Option<String>,
182
183 #[arg(help_heading = "Query parameters", long)]
184 not_tags: Option<String>,
185
186 #[arg(help_heading = "Query parameters", long)]
187 not_tags_any: Option<String>,
188
189 #[arg(help_heading = "Query parameters", long)]
190 pci_devices: Option<String>,
191
192 #[arg(help_heading = "Query parameters", long)]
193 power_state: Option<String>,
194
195 #[arg(help_heading = "Query parameters", long)]
196 progress: Option<String>,
197
198 #[command(flatten)]
200 project: ProjectInput,
201
202 #[arg(help_heading = "Query parameters", long)]
203 ramdisk_id: Option<String>,
204
205 #[arg(help_heading = "Query parameters", long)]
206 reservation_id: Option<String>,
207
208 #[arg(help_heading = "Query parameters", long)]
209 root_device_name: Option<String>,
210
211 #[arg(help_heading = "Query parameters", long)]
212 security_groups: Option<String>,
213
214 #[arg(help_heading = "Query parameters", long)]
215 services: Option<String>,
216
217 #[arg(help_heading = "Query parameters", long)]
218 soft_deleted: Option<String>,
219
220 #[arg(help_heading = "Query parameters", long)]
221 sort_dir: Option<String>,
222
223 #[arg(help_heading = "Query parameters", long, value_parser = ["access_ip_v4","access_ip_v6","auto_disk_config","availability_zone","config_drive","created_at","display_description","display_name","host","hostname","image_ref","instance_type_id","kernel_id","key_name","launch_index","launched_at","locked","locked_by","node","power_state","progress","project_id","ramdisk_id","root_device_name","task_state","terminated_at","updated_at","user_id","uuid","vm_state"])]
224 sort_key: Option<String>,
225
226 #[arg(help_heading = "Query parameters", long)]
227 status: Option<String>,
228
229 #[arg(help_heading = "Query parameters", long)]
230 system_metadata: Option<String>,
231
232 #[arg(help_heading = "Query parameters", long)]
233 tags: Option<String>,
234
235 #[arg(help_heading = "Query parameters", long)]
236 tags_any: Option<String>,
237
238 #[arg(help_heading = "Query parameters", long)]
239 task_state: Option<String>,
240
241 #[arg(help_heading = "Query parameters", long)]
242 tenant_id: Option<String>,
243
244 #[arg(help_heading = "Query parameters", long)]
245 terminated_at: Option<String>,
246
247 #[command(flatten)]
249 user: UserInput,
250
251 #[arg(help_heading = "Query parameters", long)]
252 uuid: Option<String>,
253
254 #[arg(help_heading = "Query parameters", long)]
255 vm_state: Option<String>,
256}
257
258#[derive(Args)]
260#[group(required = false, multiple = false)]
261struct ProjectInput {
262 #[arg(long, help_heading = "Path parameters", value_name = "PROJECT_NAME")]
264 project_name: Option<String>,
265 #[arg(long, help_heading = "Path parameters", value_name = "PROJECT_ID")]
267 project_id: Option<String>,
268 #[arg(long, help_heading = "Path parameters", action = clap::ArgAction::SetTrue)]
270 current_project: bool,
271}
272
273#[derive(Args)]
275#[group(required = false, multiple = false)]
276struct UserInput {
277 #[arg(long, help_heading = "Path parameters", value_name = "USER_NAME")]
279 user_name: Option<String>,
280 #[arg(long, help_heading = "Path parameters", value_name = "USER_ID")]
282 user_id: Option<String>,
283 #[arg(long, help_heading = "Path parameters", action = clap::ArgAction::SetTrue)]
285 current_user: bool,
286}
287
288#[derive(Args)]
290struct PathParameters {}
291
292impl ServersCommand {
293 pub async fn take_action<C: CliArgs>(
295 &self,
296 parsed_args: &C,
297 client: &mut AsyncOpenStack,
298 ) -> Result<(), OpenStackCliError> {
299 info!("List Servers");
300
301 let op = OutputProcessor::from_args(parsed_args, Some("compute.server"), Some("list"));
302 op.validate_args(parsed_args)?;
303
304 let mut ep_builder = list_detailed::Request::builder();
305
306 if let Some(val) = &self.query.access_ip_v4 {
308 ep_builder.access_ip_v4(val);
309 }
310 if let Some(val) = &self.query.access_ip_v6 {
311 ep_builder.access_ip_v6(val);
312 }
313 if let Some(val) = &self.query.all_tenants {
314 ep_builder.all_tenants(val);
315 }
316 if let Some(val) = &self.query.auto_disk_config {
317 ep_builder.auto_disk_config(val);
318 }
319 if let Some(val) = &self.query.availability_zone {
320 ep_builder.availability_zone(val);
321 }
322 if let Some(val) = &self.query.block_device_mapping {
323 ep_builder.block_device_mapping(val);
324 }
325 if let Some(val) = &self.query.changes_before {
326 ep_builder.changes_before(val);
327 }
328 if let Some(val) = &self.query.changes_since {
329 ep_builder.changes_since(val);
330 }
331 if let Some(val) = &self.query.config_drive {
332 ep_builder.config_drive(val);
333 }
334 if let Some(val) = &self.query.created_at {
335 ep_builder.created_at(val);
336 }
337 if let Some(val) = &self.query.deleted {
338 ep_builder.deleted(val);
339 }
340 if let Some(val) = &self.query.description {
341 ep_builder.description(val);
342 }
343 if let Some(val) = &self.query.display_description {
344 ep_builder.display_description(val);
345 }
346 if let Some(val) = &self.query.display_name {
347 ep_builder.display_name(val);
348 }
349 if let Some(val) = &self.query.flavor {
350 ep_builder.flavor(val);
351 }
352 if let Some(val) = &self.query.host {
353 ep_builder.host(val);
354 }
355 if let Some(val) = &self.query.hostname {
356 ep_builder.hostname(val);
357 }
358 if let Some(val) = &self.query.image {
359 ep_builder.image(val);
360 }
361 if let Some(val) = &self.query.image_ref {
362 ep_builder.image_ref(val);
363 }
364 if let Some(val) = &self.query.info_cache {
365 ep_builder.info_cache(val);
366 }
367 if let Some(val) = &self.query.ip {
368 ep_builder.ip(val);
369 }
370 if let Some(val) = &self.query.ip6 {
371 ep_builder.ip6(val);
372 }
373 if let Some(val) = &self.query.kernel_id {
374 ep_builder.kernel_id(val);
375 }
376 if let Some(val) = &self.query.key_name {
377 ep_builder.key_name(val);
378 }
379 if let Some(val) = &self.query.launch_index {
380 ep_builder.launch_index(val);
381 }
382 if let Some(val) = &self.query.launched_at {
383 ep_builder.launched_at(val);
384 }
385 if let Some(val) = &self.query.limit {
386 ep_builder.limit(*val);
387 }
388 if let Some(val) = &self.query.locked {
389 ep_builder.locked(val);
390 }
391 if let Some(val) = &self.query.locked_by {
392 ep_builder.locked_by(val);
393 }
394 if let Some(val) = &self.query.marker {
395 ep_builder.marker(val);
396 }
397 if let Some(val) = &self.query.metadata {
398 ep_builder.metadata(val);
399 }
400 if let Some(val) = &self.query.name {
401 ep_builder.name(val);
402 }
403 if let Some(val) = &self.query.node {
404 ep_builder.node(val);
405 }
406 if let Some(val) = &self.query.not_tags {
407 ep_builder.not_tags(val);
408 }
409 if let Some(val) = &self.query.not_tags_any {
410 ep_builder.not_tags_any(val);
411 }
412 if let Some(val) = &self.query.pci_devices {
413 ep_builder.pci_devices(val);
414 }
415 if let Some(val) = &self.query.power_state {
416 ep_builder.power_state(val);
417 }
418 if let Some(val) = &self.query.progress {
419 ep_builder.progress(val);
420 }
421 if let Some(id) = &self.query.project.project_id {
422 ep_builder.project_id(id);
424 } else if let Some(name) = &self.query.project.project_name {
425 let mut sub_find_builder = find_project::Request::builder();
427 warn!(
428 "Querying project by name (because of `--project-name` parameter passed) may not be definite. This may fail in which case parameter `--project-id` should be used instead."
429 );
430
431 sub_find_builder.id(name);
432 let find_ep = sub_find_builder
433 .build()
434 .map_err(|x| OpenStackCliError::EndpointBuild(x.to_string()))?;
435 let find_data: serde_json::Value = find_by_name(find_ep).query_async(client).await?;
436 match find_data.get("id") {
438 Some(val) => match val.as_str() {
439 Some(id_str) => {
440 ep_builder.project_id(id_str.to_owned());
441 }
442 None => {
443 return Err(OpenStackCliError::ResourceAttributeNotString(
444 serde_json::to_string(&val)?,
445 ));
446 }
447 },
448 None => {
449 return Err(OpenStackCliError::ResourceAttributeMissing(
450 "id".to_string(),
451 ));
452 }
453 };
454 } else if self.query.project.current_project {
455 ep_builder.project_id(
456 client
457 .get_auth_info()
458 .ok_or_eyre("Cannot determine current authentication information")?
459 .token
460 .user
461 .id,
462 );
463 }
464 if let Some(val) = &self.query.ramdisk_id {
465 ep_builder.ramdisk_id(val);
466 }
467 if let Some(val) = &self.query.reservation_id {
468 ep_builder.reservation_id(val);
469 }
470 if let Some(val) = &self.query.root_device_name {
471 ep_builder.root_device_name(val);
472 }
473 if let Some(val) = &self.query.security_groups {
474 ep_builder.security_groups(val);
475 }
476 if let Some(val) = &self.query.services {
477 ep_builder.services(val);
478 }
479 if let Some(val) = &self.query.soft_deleted {
480 ep_builder.soft_deleted(val);
481 }
482 if let Some(val) = &self.query.sort_dir {
483 ep_builder.sort_dir(val);
484 }
485 if let Some(val) = &self.query.sort_key {
486 ep_builder.sort_key(val);
487 }
488 if let Some(val) = &self.query.status {
489 ep_builder.status(val);
490 }
491 if let Some(val) = &self.query.system_metadata {
492 ep_builder.system_metadata(val);
493 }
494 if let Some(val) = &self.query.tags {
495 ep_builder.tags(val);
496 }
497 if let Some(val) = &self.query.tags_any {
498 ep_builder.tags_any(val);
499 }
500 if let Some(val) = &self.query.task_state {
501 ep_builder.task_state(val);
502 }
503 if let Some(val) = &self.query.tenant_id {
504 ep_builder.tenant_id(val);
505 }
506 if let Some(val) = &self.query.terminated_at {
507 ep_builder.terminated_at(val);
508 }
509 if let Some(id) = &self.query.user.user_id {
510 ep_builder.user_id(id);
512 } else if let Some(name) = &self.query.user.user_name {
513 let mut sub_find_builder = find_user::Request::builder();
515 warn!(
516 "Querying user by name (because of `--user-name` parameter passed) may not be definite. This may fail in which case parameter `--user-id` should be used instead."
517 );
518
519 sub_find_builder.id(name);
520 let find_ep = sub_find_builder
521 .build()
522 .map_err(|x| OpenStackCliError::EndpointBuild(x.to_string()))?;
523 let find_data: serde_json::Value = find_by_name(find_ep).query_async(client).await?;
524 match find_data.get("id") {
526 Some(val) => match val.as_str() {
527 Some(id_str) => {
528 ep_builder.user_id(id_str.to_owned());
529 }
530 None => {
531 return Err(OpenStackCliError::ResourceAttributeNotString(
532 serde_json::to_string(&val)?,
533 ));
534 }
535 },
536 None => {
537 return Err(OpenStackCliError::ResourceAttributeMissing(
538 "id".to_string(),
539 ));
540 }
541 };
542 } else if self.query.user.current_user {
543 ep_builder.user_id(
544 client
545 .get_auth_info()
546 .ok_or_eyre("Cannot determine current authentication information")?
547 .token
548 .user
549 .id,
550 );
551 }
552 if let Some(val) = &self.query.uuid {
553 ep_builder.uuid(val);
554 }
555 if let Some(val) = &self.query.vm_state {
556 ep_builder.vm_state(val);
557 }
558
559 let ep = ep_builder
560 .build()
561 .map_err(|x| OpenStackCliError::EndpointBuild(x.to_string()))?;
562
563 let data: Vec<serde_json::Value> = paged(ep, Pagination::Limit(self.max_items))
564 .query_async(client)
565 .await?;
566
567 op.output_list::<response::list_detailed_21::ServerResponse>(data.clone())
568 .or_else(|_| {
569 op.output_list::<response::list_detailed_2100_a::ServerResponse>(data.clone())
570 })
571 .or_else(|_| {
572 op.output_list::<response::list_detailed_2100_b::ServerResponse>(data.clone())
573 })
574 .or_else(|_| {
575 op.output_list::<response::list_detailed_216::ServerResponse>(data.clone())
576 })
577 .or_else(|_| {
578 op.output_list::<response::list_detailed_219::ServerResponse>(data.clone())
579 })
580 .or_else(|_| {
581 op.output_list::<response::list_detailed_226::ServerResponse>(data.clone())
582 })
583 .or_else(|_| op.output_list::<response::list_detailed_23::ServerResponse>(data.clone()))
584 .or_else(|_| {
585 op.output_list::<response::list_detailed_247::ServerResponse>(data.clone())
586 })
587 .or_else(|_| {
588 op.output_list::<response::list_detailed_263::ServerResponse>(data.clone())
589 })
590 .or_else(|_| {
591 op.output_list::<response::list_detailed_269_a::ServerResponse>(data.clone())
592 })
593 .or_else(|_| {
594 op.output_list::<response::list_detailed_269_b::ServerResponse>(data.clone())
595 })
596 .or_else(|_| {
597 op.output_list::<response::list_detailed_273_a::ServerResponse>(data.clone())
598 })
599 .or_else(|_| {
600 op.output_list::<response::list_detailed_273_b::ServerResponse>(data.clone())
601 })
602 .or_else(|_| op.output_list::<response::list_detailed_29::ServerResponse>(data.clone()))
603 .or_else(|_| {
604 op.output_list::<response::list_detailed_290_a::ServerResponse>(data.clone())
605 })
606 .or_else(|_| {
607 op.output_list::<response::list_detailed_290_b::ServerResponse>(data.clone())
608 })
609 .or_else(|_| {
610 op.output_list::<response::list_detailed_296_a::ServerResponse>(data.clone())
611 })
612 .or_else(|_| {
613 op.output_list::<response::list_detailed_296_b::ServerResponse>(data.clone())
614 })
615 .or_else(|_| {
616 op.output_list::<response::list_detailed_298_a::ServerResponse>(data.clone())
617 })
618 .or_else(|_| {
619 op.output_list::<response::list_detailed_298_b::ServerResponse>(data.clone())
620 })?;
621 op.show_command_hint()?;
623 Ok(())
624 }
625}