@@ -66,6 +66,28 @@ def human_readable(self):
6666 return 'N/A'
6767
6868
69+ class AddressesColumn (cliff_columns .FormattableColumn ):
70+ """Generate a formatted string of a server's addresses."""
71+
72+ def human_readable (self ):
73+ try :
74+ return utils .format_dict_of_list ({
75+ k : [i ['addr' ] for i in v if 'addr' in i ]
76+ for k , v in self ._value .items ()})
77+ except Exception :
78+ return 'N/A'
79+
80+
81+ class HostColumn (cliff_columns .FormattableColumn ):
82+ """Generate a formatted string of a hostname."""
83+
84+ def human_readable (self ):
85+ if self ._value is None :
86+ return ''
87+
88+ return self ._value
89+
90+
6991def _get_ip_address (addresses , address_type , ip_address_family ):
7092 # Old style addresses
7193 if address_type in addresses :
@@ -2288,7 +2310,7 @@ def get_parser(self, prog_name):
22882310 return parser
22892311
22902312 def take_action (self , parsed_args ):
2291- compute_client = self .app .client_manager .compute
2313+ compute_client = self .app .client_manager .sdk_connection . compute
22922314 identity_client = self .app .client_manager .identity
22932315 image_client = self .app .client_manager .image
22942316
@@ -2313,10 +2335,11 @@ def take_action(self, parsed_args):
23132335 # flavor name is given, map it to ID.
23142336 flavor_id = None
23152337 if parsed_args .flavor :
2316- flavor_id = utils .find_resource (
2317- compute_client .flavors ,
2318- parsed_args .flavor ,
2319- ).id
2338+ flavor = compute_client .find_flavor (parsed_args .flavor )
2339+ if flavor is None :
2340+ msg = _ ('Unable to find flavor: %s' ) % parsed_args .flavor
2341+ raise exceptions .CommandError (msg )
2342+ flavor_id = flavor .id
23202343
23212344 # Nova only supports list servers searching by image ID. So if a
23222345 # image name is given, map it to ID.
@@ -2332,19 +2355,21 @@ def take_action(self, parsed_args):
23322355 'ip' : parsed_args .ip ,
23332356 'ip6' : parsed_args .ip6 ,
23342357 'name' : parsed_args .name ,
2335- 'instance_name' : parsed_args .instance_name ,
23362358 'status' : parsed_args .status ,
23372359 'flavor' : flavor_id ,
23382360 'image' : image_id ,
23392361 'host' : parsed_args .host ,
2340- 'tenant_id ' : project_id ,
2341- 'all_tenants ' : parsed_args .all_projects ,
2362+ 'project_id ' : project_id ,
2363+ 'all_projects ' : parsed_args .all_projects ,
23422364 'user_id' : user_id ,
23432365 'deleted' : parsed_args .deleted ,
23442366 'changes-before' : parsed_args .changes_before ,
23452367 'changes-since' : parsed_args .changes_since ,
23462368 }
23472369
2370+ if parsed_args .instance_name is not None :
2371+ search_opts ['instance_name' ] = parsed_args .instance_name
2372+
23482373 if parsed_args .availability_zone :
23492374 search_opts ['availability_zone' ] = parsed_args .availability_zone
23502375
@@ -2376,7 +2401,7 @@ def take_action(self, parsed_args):
23762401 search_opts ['power_state' ] = power_state
23772402
23782403 if parsed_args .tags :
2379- if compute_client . api_version < api_versions . APIVersion ( '2.26' ):
2404+ if not sdk_utils . supports_microversion ( compute_client , '2.26' ):
23802405 msg = _ (
23812406 '--os-compute-api-version 2.26 or greater is required to '
23822407 'support the --tag option'
@@ -2386,7 +2411,7 @@ def take_action(self, parsed_args):
23862411 search_opts ['tags' ] = ',' .join (parsed_args .tags )
23872412
23882413 if parsed_args .not_tags :
2389- if compute_client . api_version < api_versions . APIVersion ( '2.26' ):
2414+ if not sdk_utils . supports_microversion ( compute_client , '2.26' ):
23902415 msg = _ (
23912416 '--os-compute-api-version 2.26 or greater is required to '
23922417 'support the --not-tag option'
@@ -2396,7 +2421,7 @@ def take_action(self, parsed_args):
23962421 search_opts ['not-tags' ] = ',' .join (parsed_args .not_tags )
23972422
23982423 if parsed_args .locked :
2399- if compute_client . api_version < api_versions . APIVersion ( '2.73' ):
2424+ if not sdk_utils . supports_microversion ( compute_client , '2.73' ):
24002425 msg = _ (
24012426 '--os-compute-api-version 2.73 or greater is required to '
24022427 'support the --locked option'
@@ -2405,7 +2430,7 @@ def take_action(self, parsed_args):
24052430
24062431 search_opts ['locked' ] = True
24072432 elif parsed_args .unlocked :
2408- if compute_client . api_version < api_versions . APIVersion ( '2.73' ):
2433+ if not sdk_utils . supports_microversion ( compute_client , '2.73' ):
24092434 msg = _ (
24102435 '--os-compute-api-version 2.73 or greater is required to '
24112436 'support the --unlocked option'
@@ -2414,10 +2439,14 @@ def take_action(self, parsed_args):
24142439
24152440 search_opts ['locked' ] = False
24162441
2442+ if parsed_args .limit is not None :
2443+ search_opts ['limit' ] = parsed_args .limit
2444+ search_opts ['paginated' ] = False
2445+
24172446 LOG .debug ('search options: %s' , search_opts )
24182447
24192448 if search_opts ['changes-before' ]:
2420- if compute_client . api_version < api_versions . APIVersion ( '2.66' ):
2449+ if not sdk_utils . supports_microversion ( compute_client , '2.66' ):
24212450 msg = _ ('--os-compute-api-version 2.66 or later is required' )
24222451 raise exceptions .CommandError (msg )
24232452
@@ -2451,15 +2480,15 @@ def take_action(self, parsed_args):
24512480
24522481 if parsed_args .long :
24532482 columns += (
2454- 'OS-EXT-STS: task_state' ,
2455- 'OS-EXT-STS: power_state' ,
2483+ 'task_state' ,
2484+ 'power_state' ,
24562485 )
24572486 column_headers += (
24582487 'Task State' ,
24592488 'Power State' ,
24602489 )
24612490
2462- columns += ('networks ' ,)
2491+ columns += ('addresses ' ,)
24632492 column_headers += ('Networks' ,)
24642493
24652494 if parsed_args .long :
@@ -2481,7 +2510,7 @@ def take_action(self, parsed_args):
24812510 # microversion 2.47 puts the embedded flavor into the server response
24822511 # body but omits the id, so if not present we just expose the original
24832512 # flavor name in the output
2484- if compute_client . api_version >= api_versions . APIVersion ( '2.47' ):
2513+ if sdk_utils . supports_microversion ( compute_client , '2.47' ):
24852514 columns += ('flavor_name' ,)
24862515 column_headers += ('Flavor' ,)
24872516 else :
@@ -2503,8 +2532,8 @@ def take_action(self, parsed_args):
25032532
25042533 if parsed_args .long :
25052534 columns += (
2506- 'OS-EXT-AZ: availability_zone' ,
2507- 'OS-EXT-SRV-ATTR:host ' ,
2535+ 'availability_zone' ,
2536+ 'hypervisor_hostname ' ,
25082537 'metadata' ,
25092538 )
25102539 column_headers += (
@@ -2513,40 +2542,38 @@ def take_action(self, parsed_args):
25132542 'Properties' ,
25142543 )
25152544
2516- marker_id = None
2517-
25182545 # support for additional columns
25192546 if parsed_args .columns :
25202547 for c in parsed_args .columns :
25212548 if c in ('Project ID' , 'project_id' ):
2522- columns += ('tenant_id ' ,)
2549+ columns += ('project_id ' ,)
25232550 column_headers += ('Project ID' ,)
25242551 if c in ('User ID' , 'user_id' ):
25252552 columns += ('user_id' ,)
25262553 column_headers += ('User ID' ,)
25272554 if c in ('Created At' , 'created_at' ):
2528- columns += ('created ' ,)
2555+ columns += ('created_at ' ,)
25292556 column_headers += ('Created At' ,)
25302557 if c in ('Security Groups' , 'security_groups' ):
25312558 columns += ('security_groups_name' ,)
25322559 column_headers += ('Security Groups' ,)
25332560 if c in ("Task State" , "task_state" ):
2534- columns += ('OS-EXT-STS: task_state' ,)
2561+ columns += ('task_state' ,)
25352562 column_headers += ('Task State' ,)
25362563 if c in ("Power State" , "power_state" ):
2537- columns += ('OS-EXT-STS: power_state' ,)
2564+ columns += ('power_state' ,)
25382565 column_headers += ('Power State' ,)
25392566 if c in ("Image ID" , "image_id" ):
25402567 columns += ('Image ID' ,)
25412568 column_headers += ('Image ID' ,)
25422569 if c in ("Flavor ID" , "flavor_id" ):
2543- columns += ('Flavor ID ' ,)
2570+ columns += ('flavor_id ' ,)
25442571 column_headers += ('Flavor ID' ,)
25452572 if c in ('Availability Zone' , "availability_zone" ):
2546- columns += ('OS-EXT-AZ: availability_zone' ,)
2573+ columns += ('availability_zone' ,)
25472574 column_headers += ('Availability Zone' ,)
25482575 if c in ('Host' , "host" ):
2549- columns += ('OS-EXT-SRV-ATTR:host ' ,)
2576+ columns += ('hypervisor_hostname ' ,)
25502577 column_headers += ('Host' ,)
25512578 if c in ('Properties' , "properties" ):
25522579 columns += ('Metadata' ,)
@@ -2556,24 +2583,18 @@ def take_action(self, parsed_args):
25562583 column_headers = tuple (column_headers )
25572584 columns = tuple (columns )
25582585
2559- if parsed_args .marker :
2586+ if parsed_args .marker is not None :
25602587 # Check if both "--marker" and "--deleted" are used.
25612588 # In that scenario a lookup is not needed as the marker
25622589 # needs to be an ID, because find_resource does not
25632590 # handle deleted resources
25642591 if parsed_args .deleted :
25652592 marker_id = parsed_args .marker
25662593 else :
2567- marker_id = utils .find_resource (
2568- compute_client .servers ,
2569- parsed_args .marker ,
2570- ).id
2594+ marker_id = compute_client .find_server (parsed_args .marker ).id
2595+ search_opts ['marker' ] = marker_id
25712596
2572- data = compute_client .servers .list (
2573- search_opts = search_opts ,
2574- marker = marker_id ,
2575- limit = parsed_args .limit ,
2576- )
2597+ data = list (compute_client .servers (** search_opts ))
25772598
25782599 images = {}
25792600 flavors = {}
@@ -2628,12 +2649,12 @@ def take_action(self, parsed_args):
26282649 # "Flavor Name" is not crucial, so we swallow any
26292650 # exceptions
26302651 try :
2631- flavors [f_id ] = compute_client .flavors . get (f_id )
2652+ flavors [f_id ] = compute_client .find_flavor (f_id )
26322653 except Exception :
26332654 pass
26342655 else :
26352656 try :
2636- flavors_list = compute_client .flavors . list (is_public = None )
2657+ flavors_list = compute_client .flavors (is_public = None )
26372658 for i in flavors_list :
26382659 flavors [i .id ] = i
26392660 except Exception :
@@ -2642,7 +2663,7 @@ def take_action(self, parsed_args):
26422663 # Populate image_name, image_id, flavor_name and flavor_id attributes
26432664 # of server objects so that we can display those columns.
26442665 for s in data :
2645- if compute_client . api_version >= api_versions . APIVersion ( '2.69' ):
2666+ if sdk_utils . supports_microversion ( compute_client , '2.69' ):
26462667 # NOTE(tssurya): From 2.69, we will have the keys 'flavor'
26472668 # and 'image' missing in the server response during
26482669 # infrastructure failure situations.
@@ -2651,7 +2672,7 @@ def take_action(self, parsed_args):
26512672 if not hasattr (s , 'image' ) or not hasattr (s , 'flavor' ):
26522673 continue
26532674
2654- if 'id' in s .image :
2675+ if 'id' in s .image and s . image . id is not None :
26552676 image = images .get (s .image ['id' ])
26562677 if image :
26572678 s .image_name = image .name
@@ -2664,7 +2685,7 @@ def take_action(self, parsed_args):
26642685 s .image_name = IMAGE_STRING_FOR_BFV
26652686 s .image_id = IMAGE_STRING_FOR_BFV
26662687
2667- if compute_client . api_version < api_versions . APIVersion ( '2.47' ):
2688+ if not sdk_utils . supports_microversion ( compute_client , '2.47' ):
26682689 flavor = flavors .get (s .flavor ['id' ])
26692690 if flavor :
26702691 s .flavor_name = flavor .name
@@ -2674,7 +2695,7 @@ def take_action(self, parsed_args):
26742695
26752696 # Add a list with security group name as attribute
26762697 for s in data :
2677- if hasattr (s , 'security_groups' ):
2698+ if hasattr (s , 'security_groups' ) and s . security_groups is not None :
26782699 s .security_groups_name = [x ["name" ] for x in s .security_groups ]
26792700 else :
26802701 s .security_groups_name = []
@@ -2687,10 +2708,10 @@ def take_action(self, parsed_args):
26872708 # it's on, providing useful information to a user in this
26882709 # situation.
26892710 if (
2690- compute_client . api_version >= api_versions . APIVersion ( '2.16' ) and
2711+ sdk_utils . supports_microversion ( compute_client , '2.16' ) and
26912712 parsed_args .long
26922713 ):
2693- if any ([hasattr ( s , ' host_status' ) for s in data ]):
2714+ if any ([s . host_status is not None for s in data ]):
26942715 columns += ('Host Status' ,)
26952716 column_headers += ('Host Status' ,)
26962717
@@ -2700,16 +2721,17 @@ def take_action(self, parsed_args):
27002721 utils .get_item_properties (
27012722 s , columns ,
27022723 mixed_case_fields = (
2703- 'OS-EXT-STS: task_state' ,
2704- 'OS-EXT-STS: power_state' ,
2705- 'OS-EXT-AZ: availability_zone' ,
2706- 'OS-EXT-SRV-ATTR: host' ,
2724+ 'task_state' ,
2725+ 'power_state' ,
2726+ 'availability_zone' ,
2727+ 'host' ,
27072728 ),
27082729 formatters = {
2709- 'OS-EXT-STS: power_state' : PowerStateColumn ,
2710- 'networks ' : format_columns . DictListColumn ,
2730+ 'power_state' : PowerStateColumn ,
2731+ 'addresses ' : AddressesColumn ,
27112732 'metadata' : format_columns .DictColumn ,
27122733 'security_groups_name' : format_columns .ListColumn ,
2734+ 'hypervisor_hostname' : HostColumn ,
27132735 },
27142736 ) for s in data
27152737 ),
0 commit comments