Skip to content

Commit c97f73c

Browse files
committed
Use the compute SDK in server list
Update server list to use the compute component of the OpenStack SDK instead of directly using the nova interface. This change depends on SDK version 0.102.0 for automatic client-side query filters. Change-Id: Ib9985812bfd98320b75f3a82bb594a0daa6e4d93
1 parent 56b0f6d commit c97f73c

4 files changed

Lines changed: 205 additions & 189 deletions

File tree

openstackclient/compute/v2/server.py

Lines changed: 74 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
6991
def _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
),

openstackclient/tests/functional/compute/v2/test_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def test_server_list_with_marker_and_deleted(self):
105105
'server list -f json --deleted --marker ' + name2
106106
))
107107
except exceptions.CommandFailed as e:
108-
self.assertIn('marker [%s] not found (HTTP 400)' % (name2),
108+
self.assertIn('marker [%s] not found' % (name2),
109109
e.stderr.decode('utf-8'))
110110

111111
def test_server_list_with_changes_before(self):

0 commit comments

Comments
 (0)