From eb4e7eeeb09bdf2e1b80984b378c5a8ea9930f04 Mon Sep 17 00:00:00 2001
From: Li Zhu
Date: Tue, 2 Jan 2024 22:35:15 -0500
Subject: [PATCH] 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
---
.../dcmanagerclient/api/base.py | 9 ++-
.../commands/v1/subcloud_manager.py | 75 ++++++++++++++++---
.../dcmanagerclient/tests/base.py | 65 ++++++++++++++--
.../tests/v1/test_subcloud_manager.py | 24 ++++++
.../dcmanagerclient/utils.py | 10 ++-
5 files changed, 159 insertions(+), 24 deletions(-)
diff --git a/distributedcloud-client/dcmanagerclient/api/base.py b/distributedcloud-client/dcmanagerclient/api/base.py
index 2e0f29e..7d1396a 100644
--- a/distributedcloud-client/dcmanagerclient/api/base.py
+++ b/distributedcloud-client/dcmanagerclient/api/base.py
@@ -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):
diff --git a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py
index 11b19a1..5717ea7 100644
--- a/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py
+++ b/distributedcloud-client/dcmanagerclient/commands/v1/subcloud_manager.py
@@ -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('' 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('' 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)
diff --git a/distributedcloud-client/dcmanagerclient/tests/base.py b/distributedcloud-client/dcmanagerclient/tests/base.py
index db8f56d..c1e27ce 100644
--- a/distributedcloud-client/dcmanagerclient/tests/base.py
+++ b/distributedcloud-client/dcmanagerclient/tests/base.py
@@ -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 = (('',) * len(SUBCLOUD_FIELD_RESULT_LIST),)
EMPTY_SUBCLOUD_FIELD_RESULT_WITH_PEERID_REHOME_DATA = \
(('',) * 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 = \
+ (('',) * 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 = (('',) * 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)
diff --git a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py
index 7703112..bc752c2 100644
--- a/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py
+++ b/distributedcloud-client/dcmanagerclient/tests/v1/test_subcloud_manager.py
@@ -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.\
diff --git a/distributedcloud-client/dcmanagerclient/utils.py b/distributedcloud-client/dcmanagerclient/utils.py
index 9f5c3f6..6312238 100644
--- a/distributedcloud-client/dcmanagerclient/utils.py
+++ b/distributedcloud-client/dcmanagerclient/utils.py
@@ -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):