Display prestage status and versions in subcloud list

The prestage release can be either the previous or current release for
a subcloud. Checking the prestage release individually on thousands of
subclouds is impractical. Therefore, a new column called "prestage
versions" is added to the output of the "dcmanager subcloud list'
command. Besides, decoupling prestage status from deploy status.

This commit also introduces the following changes:
- 'backup datetime' column removal and addition of 'prestage status'
  column to 'dcmanager subcloud list' output.
- 'dcmanager subcloud show' and similar commands now display 'prestage
  status' and 'prestage versions' columns in their output.
- New '-d/--detail' option is added to 'dcmanager subcloud list' to
  display all columns of the subclouds.
- '-c/--column' option of 'dcmanager subcloud list' can be used to
  specify the column(s) of the subclouds. eg. -c column1 -c column2 ...

Test plan:
PASS: Verify the correct "prestage status" and "prestage versions"
      output of the "dcmanager subcloud list" command.
PASS: Verify the correct output of the new arguments -d/--detail and
      -c/--column.

Depends-On: https://review.opendev.org/c/starlingx/distcloud/+/904541

Story: 2010611
Task: 49369

Change-Id: I8d127220fcd705dca542afeca2ac8f19f20172a1
Signed-off-by: lzhu1 <li.zhu@windriver.com>
This commit is contained in:
Li Zhu 2024-01-02 22:35:15 -05:00
parent c9e03caa9d
commit eb4e7eeeb0
5 changed files with 159 additions and 24 deletions

View File

@ -1,5 +1,5 @@
# Copyright (c) 2016 Ericsson AB
# Copyright (c) 2017-2023 Wind River Systems, Inc.
# Copyright (c) 2017-2024 Wind River Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -53,6 +53,8 @@ class Subcloud(Resource):
'backup-status': 'backup_status',
'backup-datetime': 'backup_datetime',
'prestage-software-version': 'prestage_software_version',
'prestage-status': 'prestage_status',
'prestage-versions': 'prestage_versions',
'region-name': 'region_name'
}
@ -64,7 +66,8 @@ class Subcloud(Resource):
group_id, sync_status="unknown", endpoint_sync_status=None,
backup_status=None, backup_datetime=None,
error_description=None, prestage_software_version=None,
peer_group_id=None, rehome_data=None, region_name=None):
peer_group_id=None, rehome_data=None, region_name=None,
prestage_status=None, prestage_versions=None):
if endpoint_sync_status is None:
endpoint_sync_status = {}
self.manager = manager
@ -95,6 +98,8 @@ class Subcloud(Resource):
self.backup_datetime = backup_datetime
self.prestage_software_version = prestage_software_version
self.region_name = region_name
self.prestage_status = prestage_status
self.prestage_versions = prestage_versions
@classmethod
def from_payload(cls, manager, payload):

View File

@ -39,7 +39,7 @@ def format(subcloud=None):
'deploy status',
'sync',
'backup status',
'backup datetime'
'prestage status'
)
if subcloud:
@ -51,7 +51,7 @@ def format(subcloud=None):
subcloud.deploy_status,
subcloud.sync_status,
subcloud.backup_status,
subcloud.backup_datetime,
subcloud.prestage_status
)
else:
@ -60,7 +60,7 @@ def format(subcloud=None):
return columns, data
def detail_format(subcloud=None):
def basic_detail_format(subcloud=None):
columns = (
'id',
'name',
@ -80,7 +80,9 @@ def detail_format(subcloud=None):
'created_at',
'updated_at',
'backup_status',
'backup_datetime'
'backup_datetime',
'prestage_status',
'prestage_versions'
)
if subcloud:
@ -103,9 +105,21 @@ def detail_format(subcloud=None):
subcloud.created_at,
subcloud.updated_at,
subcloud.backup_status,
subcloud.backup_datetime
subcloud.backup_datetime,
subcloud.prestage_status,
subcloud.prestage_versions
)
else:
data = (tuple('<none>' for _ in range(len(columns))),)
return columns, data
def detail_format(subcloud=None):
columns, data = basic_detail_format(subcloud)
if subcloud:
for _listitem, sync_status in enumerate(subcloud.endpoint_sync_status):
added_field = (sync_status['endpoint_type'] +
"_sync_status",)
@ -117,10 +131,6 @@ def detail_format(subcloud=None):
columns += ('oam_floating_ip',)
data += (subcloud.oam_floating_ip,)
if subcloud.prestage_software_version:
columns += ('prestage_software_version',)
data += (subcloud.prestage_software_version,)
if subcloud.deploy_config_sync_status != "unknown":
columns += ('deploy_config_sync_status',)
data += (subcloud.deploy_config_sync_status,)
@ -128,6 +138,33 @@ def detail_format(subcloud=None):
if subcloud.region_name is not None:
columns += ('region_name',)
data += (subcloud.region_name,)
return columns, data
def detail_prestage_format(subcloud=None):
columns, data = detail_format(subcloud)
if subcloud and subcloud.prestage_software_version:
columns += ('prestage_software_version',)
data += (subcloud.prestage_software_version,)
return columns, data
def detail_list_format(subcloud=None):
columns, data = basic_detail_format(subcloud)
# Find the index of 'deploy_status' in the tuple
deploy_status_index = columns.index('deploy_status')
# Insert "sync" field after 'deploy_status'
columns = columns[:deploy_status_index + 1] + ("sync",) + \
columns[deploy_status_index + 1:]
if subcloud:
data = data[:deploy_status_index + 1] + (subcloud.sync_status,) + \
data[deploy_status_index + 1:]
else:
data = (tuple('<none>' for _ in range(len(columns))),)
@ -306,8 +343,18 @@ class AddSubcloud(base.DCManagerShowOne):
class ListSubcloud(base.DCManagerLister):
"""List subclouds."""
def __init__(self, app, app_args):
super(ListSubcloud, self).__init__(app, app_args)
# Set a flag to indicate displaying a basic column list or
# a list with customized or all columns
self.show_basic_list = True
def _validate_parsed_args(self, parsed_args):
self.show_basic_list = \
False if parsed_args.columns or parsed_args.detail else True
def _get_format_function(self):
return format
return format if self.show_basic_list else detail_list_format
def get_parser(self, prog_name):
parser = super(ListSubcloud, self).get_parser(prog_name)
@ -317,6 +364,12 @@ class ListSubcloud(base.DCManagerLister):
action='store_true',
help='List all subclouds include "secondary" state subclouds'
)
parser.add_argument(
'-d', '--detail',
required=False,
action='store_true',
help="List all columns of the subclouds"
)
return parser
def _get_resources(self, parsed_args):
@ -894,7 +947,7 @@ class PrestageSubcloud(base.DCManagerShowOne):
"""Prestage a subcloud."""
def _get_format_function(self):
return detail_format
return detail_prestage_format
def get_parser(self, prog_name):
parser = super(PrestageSubcloud, self).get_parser(prog_name)

View File

@ -1,6 +1,6 @@
# Copyright 2013 - Mirantis, Inc.
# Copyright 2016 - Ericsson AB.
# Copyright (c) 2017-2021 Wind River Systems, Inc.
# Copyright (c) 2017-2024 Wind River Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -56,6 +56,9 @@ SUBCLOUD_PEERGROUP_ID = None
SUBCLOUD_REHOME_DATA = None
BACKUP_STATUS = 'None'
BACKUP_DATETIME = 'None'
PRESTAGE_STATUS = 'None'
PRESTAGE_VERSIONS = None
SYNC = None
# Useful for subcloud name configuration
NAME_SC2 = "subcloud2"
@ -84,7 +87,9 @@ SUBCLOUD_RESOURCE = api_base.Subcloud(
updated_at=TIME_NOW,
group_id=DEFAULT_SUBCLOUD_GROUP_ID,
backup_status=BACKUP_STATUS,
backup_datetime=BACKUP_DATETIME)
backup_datetime=BACKUP_DATETIME,
prestage_status=PRESTAGE_STATUS,
prestage_versions=PRESTAGE_VERSIONS)
# Subcloud CLI resource object with peerid rehome data
SUBCLOUD_RESOURCE_WITH_PEERID = api_base.Subcloud(
@ -107,7 +112,35 @@ SUBCLOUD_RESOURCE_WITH_PEERID = api_base.Subcloud(
created_at=TIME_NOW,
updated_at=TIME_NOW,
backup_status=BACKUP_STATUS,
backup_datetime=BACKUP_DATETIME)
backup_datetime=BACKUP_DATETIME,
prestage_status=PRESTAGE_STATUS,
prestage_versions=PRESTAGE_VERSIONS)
# Subcloud CLI resource object with all list fields
SUBCLOUD_RESOURCE_WITH_ALL_LIST_FIELDS = api_base.Subcloud(
mock,
subcloud_id=ID,
name=NAME,
description=DESCRIPTION,
location=LOCATION,
software_version=SOFTWARE_VERSION,
management_state=MANAGEMENT_STATE,
availability_status=AVAILABILITY_STATUS,
deploy_status=DEPLOY_STATUS,
sync_status=SYNC,
management_subnet=MANAGEMENT_SUBNET,
management_start_ip=MANAGEMENT_START_IP,
management_end_ip=MANAGEMENT_END_IP,
management_gateway_ip=MANAGEMENT_GATEWAY_IP,
systemcontroller_gateway_ip=SYSTEMCONTROLLER_GATEWAY_IP,
group_id=DEFAULT_SUBCLOUD_GROUP_ID,
peer_group_id=SUBCLOUD_PEERGROUP_ID,
created_at=TIME_NOW,
updated_at=TIME_NOW,
backup_status=BACKUP_STATUS,
backup_datetime=BACKUP_DATETIME,
prestage_status=PRESTAGE_STATUS,
prestage_versions=PRESTAGE_VERSIONS)
# Subcloud result values returned from various API calls (e.g. subcloud show)
SUBCLOUD_FIELD_RESULT_LIST = (
@ -151,13 +184,26 @@ SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID = (
TIME_NOW,
TIME_NOW,
BACKUP_STATUS,
BACKUP_DATETIME
BACKUP_DATETIME,
PRESTAGE_STATUS,
PRESTAGE_VERSIONS
)
EMPTY_SUBCLOUD_FIELD_RESULT = (('<none>',) * len(SUBCLOUD_FIELD_RESULT_LIST),)
EMPTY_SUBCLOUD_FIELD_RESULT_WITH_PEERID_REHOME_DATA = \
(('<none>',) * len(SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID),)
# Create subcloud all fields based on SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID
# and add an additional "sync" field
DEPLOY_STATUS_IDX = SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID.index(DEPLOY_STATUS)
SUBCLOUD_ALL_FIELDS_RESULT_LIST = \
SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID[:DEPLOY_STATUS_IDX + 1] + \
(SYNC,) + \
SUBCLOUD_FIELD_RESULT_LIST_WITH_PEERID[DEPLOY_STATUS_IDX + 1:]
EMPTY_SUBCLOUD_ALL_FIELDS_RESULT = \
(('<none>',) * len(SUBCLOUD_ALL_FIELDS_RESULT_LIST),)
# Subcloud result values returned from subcloud list command
SUBCLOUD_LIST_RESULT = (
ID,
@ -167,7 +213,7 @@ SUBCLOUD_LIST_RESULT = (
DEPLOY_STATUS,
SYNC_STATUS,
BACKUP_STATUS,
BACKUP_DATETIME
PRESTAGE_STATUS
)
EMPTY_SUBCLOUD_LIST_RESULT = (('<none>',) * len(SUBCLOUD_LIST_RESULT),)
@ -187,7 +233,9 @@ FAKE_BOOTSTRAP_VALUES = {
'backup_status': BACKUP_STATUS,
'backup_datetime': BACKUP_DATETIME,
'backup_status': BACKUP_STATUS,
'backup_datetime': BACKUP_DATETIME
'backup_datetime': BACKUP_DATETIME,
'prestage_status': PRESTAGE_STATUS,
'prestage_versions': PRESTAGE_VERSIONS
}
FAKE_INSTALL_VALUES = {
@ -268,12 +316,13 @@ class BaseCommandTest(testtools.TestCase):
super(BaseCommandTest, self).setUp()
self.app = mock.Mock()
self.client = self.app.client_manager.subcloud_manager
self.parsed_args = None
def call(self, command, app_args=None, prog_name=''):
if app_args is None:
app_args = []
cmd = command(self.app, app_args)
parsed_args = cmd.get_parser(prog_name).parse_args(app_args)
self.parsed_args = cmd.get_parser(prog_name).parse_args(app_args)
return cmd.take_action(parsed_args)
return cmd.take_action(self.parsed_args)

View File

@ -40,6 +40,30 @@ class TestCLISubcloudManagerV1(base.BaseCommandTest):
self.assertEqual(base.EMPTY_SUBCLOUD_LIST_RESULT,
actual_call[1])
def test_list_subclouds_with_all_fields(self):
self.client.subcloud_manager.list_subclouds.return_value = \
[base.SUBCLOUD_RESOURCE_WITH_ALL_LIST_FIELDS]
actual_call = self.call(subcloud_cmd.ListSubcloud, app_args=['-d'])
self.assertEqual([base.SUBCLOUD_ALL_FIELDS_RESULT_LIST],
actual_call[1])
def test_list_subclouds_with_all_empty_fields(self):
self.client.subcloud_manager.list_subclouds.return_value = []
actual_call = self.call(subcloud_cmd.ListSubcloud,
app_args=['--detail'])
self.assertEqual(base.EMPTY_SUBCLOUD_ALL_FIELDS_RESULT,
actual_call[1])
def test_list_subclouds_with_specified_columns(self):
self.client.subcloud_manager.list_subclouds.return_value = \
[base.SUBCLOUD_RESOURCE_WITH_ALL_LIST_FIELDS]
self.call(subcloud_cmd.ListSubcloud,
app_args=['-c', 'name',
'-c', 'prestage_status',
'-c', 'prestage_versions'])
self.assertEqual(self.parsed_args.columns,
['name', 'prestage_status', 'prestage_versions'])
def test_delete_subcloud_with_subcloud_id(self):
self.call(subcloud_cmd.DeleteSubcloud, app_args=[base.ID])
self.client.subcloud_manager.delete_subcloud.\

View File

@ -1,7 +1,7 @@
# Copyright 2016 - Ericsson AB
# Copyright 2015 - Huawei Technologies Co. Ltd
# Copyright 2015 - StackStorm, Inc.
# Copyright (c) 2017-2023 Wind River Systems, Inc.
# Copyright (c) 2017-2024 Wind River Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -124,7 +124,9 @@ def subcloud_detail_format(subcloud=None):
'created_at',
'updated_at',
'backup_status',
'backup_datetime'
'backup_datetime',
'prestage_status',
'prestage_versions'
)
if subcloud:
@ -147,7 +149,9 @@ def subcloud_detail_format(subcloud=None):
subcloud.created_at,
subcloud.updated_at,
subcloud.backup_status,
subcloud.backup_datetime
subcloud.backup_datetime,
subcloud.prestage_status,
subcloud.prestage_versions
)
for _listitem, sync_status in enumerate(subcloud.endpoint_sync_status):