@@ -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 :
@@ -2287,7 +2309,7 @@ def get_parser(self, prog_name):
22872309 return parser
22882310
22892311 def take_action (self , parsed_args ):
2290- compute_client = self .app .client_manager .compute
2312+ compute_client = self .app .client_manager .sdk_connection . compute
22912313 identity_client = self .app .client_manager .identity
22922314 image_client = self .app .client_manager .image
22932315
@@ -2312,10 +2334,11 @@ def take_action(self, parsed_args):
23122334 # flavor name is given, map it to ID.
23132335 flavor_id = None
23142336 if parsed_args .flavor :
2315- flavor_id = utils .find_resource (
2316- compute_client .flavors ,
2317- parsed_args .flavor ,
2318- ).id
2337+ flavor = compute_client .find_flavor (parsed_args .flavor )
2338+ if flavor is None :
2339+ msg = _ ('Unable to find flavor: %s' ) % parsed_args .flavor
2340+ raise exceptions .CommandError (msg )
2341+ flavor_id = flavor .id
23192342
23202343 # Nova only supports list servers searching by image ID. So if a
23212344 # image name is given, map it to ID.
@@ -2331,19 +2354,21 @@ def take_action(self, parsed_args):
23312354 'ip' : parsed_args .ip ,
23322355 'ip6' : parsed_args .ip6 ,
23332356 'name' : parsed_args .name ,
2334- 'instance_name' : parsed_args .instance_name ,
23352357 'status' : parsed_args .status ,
23362358 'flavor' : flavor_id ,
23372359 'image' : image_id ,
23382360 'host' : parsed_args .host ,
2339- 'tenant_id ' : project_id ,
2340- 'all_tenants ' : parsed_args .all_projects ,
2361+ 'project_id ' : project_id ,
2362+ 'all_projects ' : parsed_args .all_projects ,
23412363 'user_id' : user_id ,
23422364 'deleted' : parsed_args .deleted ,
23432365 'changes-before' : parsed_args .changes_before ,
23442366 'changes-since' : parsed_args .changes_since ,
23452367 }
23462368
2369+ if parsed_args .instance_name is not None :
2370+ search_opts ['instance_name' ] = parsed_args .instance_name
2371+
23472372 if parsed_args .availability_zone :
23482373 search_opts ['availability_zone' ] = parsed_args .availability_zone
23492374
@@ -2375,7 +2400,7 @@ def take_action(self, parsed_args):
23752400 search_opts ['power_state' ] = power_state
23762401
23772402 if parsed_args .tags :
2378- if compute_client . api_version < api_versions . APIVersion ( '2.26' ):
2403+ if not sdk_utils . supports_microversion ( compute_client , '2.26' ):
23792404 msg = _ (
23802405 '--os-compute-api-version 2.26 or greater is required to '
23812406 'support the --tag option'
@@ -2385,7 +2410,7 @@ def take_action(self, parsed_args):
23852410 search_opts ['tags' ] = ',' .join (parsed_args .tags )
23862411
23872412 if parsed_args .not_tags :
2388- if compute_client . api_version < api_versions . APIVersion ( '2.26' ):
2413+ if not sdk_utils . supports_microversion ( compute_client , '2.26' ):
23892414 msg = _ (
23902415 '--os-compute-api-version 2.26 or greater is required to '
23912416 'support the --not-tag option'
@@ -2395,7 +2420,7 @@ def take_action(self, parsed_args):
23952420 search_opts ['not-tags' ] = ',' .join (parsed_args .not_tags )
23962421
23972422 if parsed_args .locked :
2398- if compute_client . api_version < api_versions . APIVersion ( '2.73' ):
2423+ if not sdk_utils . supports_microversion ( compute_client , '2.73' ):
23992424 msg = _ (
24002425 '--os-compute-api-version 2.73 or greater is required to '
24012426 'support the --locked option'
@@ -2404,7 +2429,7 @@ def take_action(self, parsed_args):
24042429
24052430 search_opts ['locked' ] = True
24062431 elif parsed_args .unlocked :
2407- if compute_client . api_version < api_versions . APIVersion ( '2.73' ):
2432+ if not sdk_utils . supports_microversion ( compute_client , '2.73' ):
24082433 msg = _ (
24092434 '--os-compute-api-version 2.73 or greater is required to '
24102435 'support the --unlocked option'
@@ -2413,10 +2438,14 @@ def take_action(self, parsed_args):
24132438
24142439 search_opts ['locked' ] = False
24152440
2441+ if parsed_args .limit is not None :
2442+ search_opts ['limit' ] = parsed_args .limit
2443+ search_opts ['paginated' ] = False
2444+
24162445 LOG .debug ('search options: %s' , search_opts )
24172446
24182447 if search_opts ['changes-before' ]:
2419- if compute_client . api_version < api_versions . APIVersion ( '2.66' ):
2448+ if not sdk_utils . supports_microversion ( compute_client , '2.66' ):
24202449 msg = _ ('--os-compute-api-version 2.66 or later is required' )
24212450 raise exceptions .CommandError (msg )
24222451
@@ -2450,15 +2479,15 @@ def take_action(self, parsed_args):
24502479
24512480 if parsed_args .long :
24522481 columns += (
2453- 'OS-EXT-STS: task_state' ,
2454- 'OS-EXT-STS: power_state' ,
2482+ 'task_state' ,
2483+ 'power_state' ,
24552484 )
24562485 column_headers += (
24572486 'Task State' ,
24582487 'Power State' ,
24592488 )
24602489
2461- columns += ('networks ' ,)
2490+ columns += ('addresses ' ,)
24622491 column_headers += ('Networks' ,)
24632492
24642493 if parsed_args .long :
@@ -2480,7 +2509,7 @@ def take_action(self, parsed_args):
24802509 # microversion 2.47 puts the embedded flavor into the server response
24812510 # body but omits the id, so if not present we just expose the original
24822511 # flavor name in the output
2483- if compute_client . api_version >= api_versions . APIVersion ( '2.47' ):
2512+ if sdk_utils . supports_microversion ( compute_client , '2.47' ):
24842513 columns += ('flavor_name' ,)
24852514 column_headers += ('Flavor' ,)
24862515 else :
@@ -2502,8 +2531,8 @@ def take_action(self, parsed_args):
25022531
25032532 if parsed_args .long :
25042533 columns += (
2505- 'OS-EXT-AZ: availability_zone' ,
2506- 'OS-EXT-SRV-ATTR:host ' ,
2534+ 'availability_zone' ,
2535+ 'hypervisor_hostname ' ,
25072536 'metadata' ,
25082537 )
25092538 column_headers += (
@@ -2512,40 +2541,38 @@ def take_action(self, parsed_args):
25122541 'Properties' ,
25132542 )
25142543
2515- marker_id = None
2516-
25172544 # support for additional columns
25182545 if parsed_args .columns :
25192546 for c in parsed_args .columns :
25202547 if c in ('Project ID' , 'project_id' ):
2521- columns += ('tenant_id ' ,)
2548+ columns += ('project_id ' ,)
25222549 column_headers += ('Project ID' ,)
25232550 if c in ('User ID' , 'user_id' ):
25242551 columns += ('user_id' ,)
25252552 column_headers += ('User ID' ,)
25262553 if c in ('Created At' , 'created_at' ):
2527- columns += ('created ' ,)
2554+ columns += ('created_at ' ,)
25282555 column_headers += ('Created At' ,)
25292556 if c in ('Security Groups' , 'security_groups' ):
25302557 columns += ('security_groups_name' ,)
25312558 column_headers += ('Security Groups' ,)
25322559 if c in ("Task State" , "task_state" ):
2533- columns += ('OS-EXT-STS: task_state' ,)
2560+ columns += ('task_state' ,)
25342561 column_headers += ('Task State' ,)
25352562 if c in ("Power State" , "power_state" ):
2536- columns += ('OS-EXT-STS: power_state' ,)
2563+ columns += ('power_state' ,)
25372564 column_headers += ('Power State' ,)
25382565 if c in ("Image ID" , "image_id" ):
25392566 columns += ('Image ID' ,)
25402567 column_headers += ('Image ID' ,)
25412568 if c in ("Flavor ID" , "flavor_id" ):
2542- columns += ('Flavor ID ' ,)
2569+ columns += ('flavor_id ' ,)
25432570 column_headers += ('Flavor ID' ,)
25442571 if c in ('Availability Zone' , "availability_zone" ):
2545- columns += ('OS-EXT-AZ: availability_zone' ,)
2572+ columns += ('availability_zone' ,)
25462573 column_headers += ('Availability Zone' ,)
25472574 if c in ('Host' , "host" ):
2548- columns += ('OS-EXT-SRV-ATTR:host ' ,)
2575+ columns += ('hypervisor_hostname ' ,)
25492576 column_headers += ('Host' ,)
25502577 if c in ('Properties' , "properties" ):
25512578 columns += ('Metadata' ,)
@@ -2555,24 +2582,18 @@ def take_action(self, parsed_args):
25552582 column_headers = tuple (column_headers )
25562583 columns = tuple (columns )
25572584
2558- if parsed_args .marker :
2585+ if parsed_args .marker is not None :
25592586 # Check if both "--marker" and "--deleted" are used.
25602587 # In that scenario a lookup is not needed as the marker
25612588 # needs to be an ID, because find_resource does not
25622589 # handle deleted resources
25632590 if parsed_args .deleted :
25642591 marker_id = parsed_args .marker
25652592 else :
2566- marker_id = utils .find_resource (
2567- compute_client .servers ,
2568- parsed_args .marker ,
2569- ).id
2593+ marker_id = compute_client .find_server (parsed_args .marker ).id
2594+ search_opts ['marker' ] = marker_id
25702595
2571- data = compute_client .servers .list (
2572- search_opts = search_opts ,
2573- marker = marker_id ,
2574- limit = parsed_args .limit ,
2575- )
2596+ data = list (compute_client .servers (** search_opts ))
25762597
25772598 images = {}
25782599 flavors = {}
@@ -2627,12 +2648,12 @@ def take_action(self, parsed_args):
26272648 # "Flavor Name" is not crucial, so we swallow any
26282649 # exceptions
26292650 try :
2630- flavors [f_id ] = compute_client .flavors . get (f_id )
2651+ flavors [f_id ] = compute_client .find_flavor (f_id )
26312652 except Exception :
26322653 pass
26332654 else :
26342655 try :
2635- flavors_list = compute_client .flavors . list (is_public = None )
2656+ flavors_list = compute_client .flavors (is_public = None )
26362657 for i in flavors_list :
26372658 flavors [i .id ] = i
26382659 except Exception :
@@ -2641,7 +2662,7 @@ def take_action(self, parsed_args):
26412662 # Populate image_name, image_id, flavor_name and flavor_id attributes
26422663 # of server objects so that we can display those columns.
26432664 for s in data :
2644- if compute_client . api_version >= api_versions . APIVersion ( '2.69' ):
2665+ if sdk_utils . supports_microversion ( compute_client , '2.69' ):
26452666 # NOTE(tssurya): From 2.69, we will have the keys 'flavor'
26462667 # and 'image' missing in the server response during
26472668 # infrastructure failure situations.
@@ -2650,7 +2671,7 @@ def take_action(self, parsed_args):
26502671 if not hasattr (s , 'image' ) or not hasattr (s , 'flavor' ):
26512672 continue
26522673
2653- if 'id' in s .image :
2674+ if 'id' in s .image and s . image . id is not None :
26542675 image = images .get (s .image ['id' ])
26552676 if image :
26562677 s .image_name = image .name
@@ -2663,7 +2684,7 @@ def take_action(self, parsed_args):
26632684 s .image_name = IMAGE_STRING_FOR_BFV
26642685 s .image_id = IMAGE_STRING_FOR_BFV
26652686
2666- if compute_client . api_version < api_versions . APIVersion ( '2.47' ):
2687+ if not sdk_utils . supports_microversion ( compute_client , '2.47' ):
26672688 flavor = flavors .get (s .flavor ['id' ])
26682689 if flavor :
26692690 s .flavor_name = flavor .name
@@ -2673,7 +2694,7 @@ def take_action(self, parsed_args):
26732694
26742695 # Add a list with security group name as attribute
26752696 for s in data :
2676- if hasattr (s , 'security_groups' ):
2697+ if hasattr (s , 'security_groups' ) and s . security_groups is not None :
26772698 s .security_groups_name = [x ["name" ] for x in s .security_groups ]
26782699 else :
26792700 s .security_groups_name = []
@@ -2686,10 +2707,10 @@ def take_action(self, parsed_args):
26862707 # it's on, providing useful information to a user in this
26872708 # situation.
26882709 if (
2689- compute_client . api_version >= api_versions . APIVersion ( '2.16' ) and
2710+ sdk_utils . supports_microversion ( compute_client , '2.16' ) and
26902711 parsed_args .long
26912712 ):
2692- if any ([hasattr ( s , ' host_status' ) for s in data ]):
2713+ if any ([s . host_status is not None for s in data ]):
26932714 columns += ('Host Status' ,)
26942715 column_headers += ('Host Status' ,)
26952716
@@ -2699,16 +2720,17 @@ def take_action(self, parsed_args):
26992720 utils .get_item_properties (
27002721 s , columns ,
27012722 mixed_case_fields = (
2702- 'OS-EXT-STS: task_state' ,
2703- 'OS-EXT-STS: power_state' ,
2704- 'OS-EXT-AZ: availability_zone' ,
2705- 'OS-EXT-SRV-ATTR: host' ,
2723+ 'task_state' ,
2724+ 'power_state' ,
2725+ 'availability_zone' ,
2726+ 'host' ,
27062727 ),
27072728 formatters = {
2708- 'OS-EXT-STS: power_state' : PowerStateColumn ,
2709- 'networks ' : format_columns . DictListColumn ,
2729+ 'power_state' : PowerStateColumn ,
2730+ 'addresses ' : AddressesColumn ,
27102731 'metadata' : format_columns .DictColumn ,
27112732 'security_groups_name' : format_columns .ListColumn ,
2733+ 'hypervisor_hostname' : HostColumn ,
27122734 },
27132735 ) for s in data
27142736 ),
0 commit comments