Skip to content

Commit 434fcf2

Browse files
committed
show commands for system ready and application extension package app ready support
1 parent 9e2fbf4 commit 434fcf2

File tree

6 files changed

+192
-2
lines changed

6 files changed

+192
-2
lines changed

scripts/sysreadyshow

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
Script to show system ready status.
5+
"""
6+
7+
import os
8+
import sys
9+
import argparse
10+
from tabulate import tabulate
11+
from natsort import natsorted
12+
13+
# mock the redis for unit test purposes #
14+
try:
15+
if os.environ["UTILITIES_UNIT_TESTING"] == "1":
16+
modules_path = os.path.join(os.path.dirname(__file__), "..")
17+
test_path = os.path.join(modules_path, "tests")
18+
sys.path.insert(0, modules_path)
19+
sys.path.insert(0, test_path)
20+
import mock_tables.dbconnector
21+
except KeyError:
22+
pass
23+
24+
from swsscommon.swsscommon import SonicV2Connector
25+
26+
header = ['Service-Name', 'Service-Status', 'App-Ready-Status', 'Down-Reason']
27+
header_detail = ['Service-Name', 'Service-Status', 'App-Ready-Status', 'Down-Reason', 'AppStatus-UpdateTime']
28+
29+
ALL_TABLE_NAME = 'ALL_SERVICE_STATUS'
30+
SERVICE_STATUS = 'service_status'
31+
APP_READY_STATUS = 'app_ready_status'
32+
FAIL_REASON = 'fail_reason'
33+
UPDATE_TIME = 'update_time'
34+
35+
class SysreadyShow(object):
36+
def __init__(self):
37+
self.db = SonicV2Connector(host="127.0.0.1")
38+
self.db.connect(self.db.STATE_DB)
39+
40+
def show(self,type):
41+
TABLE_NAME = ALL_TABLE_NAME
42+
SYSREADY_TABLE = "SYSTEM_READY|SYSTEM_STATE"
43+
44+
keys = self.db.keys(self.db.STATE_DB, TABLE_NAME + '*')
45+
if not keys:
46+
print('No info\n')
47+
return
48+
49+
table = []
50+
for key in natsorted(keys):
51+
key_list = key.split('|')
52+
if len(key_list) != 2: # error data in DB, log it and ignore
53+
print('Warn: Invalid key in table {}: {}'.format(TABLE_NAME, key))
54+
continue
55+
56+
name = key_list[1]
57+
data_dict = self.db.get_all(self.db.STATE_DB, key)
58+
try:
59+
service_status = data_dict[SERVICE_STATUS]
60+
app_ready_status = data_dict[APP_READY_STATUS]
61+
fail_reason = data_dict[FAIL_REASON]
62+
update_time = data_dict[UPDATE_TIME]
63+
except ValueError as e:
64+
print('Error in data_dict')
65+
66+
if type == "alldetail":
67+
table.append((name, data_dict[SERVICE_STATUS], data_dict[APP_READY_STATUS], data_dict[FAIL_REASON],
68+
data_dict[UPDATE_TIME]))
69+
header_info = header_detail
70+
else:
71+
table.append((name, data_dict[SERVICE_STATUS], data_dict[APP_READY_STATUS], data_dict[FAIL_REASON]))
72+
header_info = header
73+
74+
sysready_state = self.db.get(self.db.STATE_DB, SYSREADY_TABLE, "Status")
75+
if sysready_state == "UP":
76+
print("System is ready\n")
77+
else:
78+
print("System is not ready - one or more services are not up\n")
79+
80+
81+
if type == "allbrief":
82+
return
83+
84+
85+
if table:
86+
print(tabulate(table, header_info, tablefmt='simple', stralign='left'))
87+
else:
88+
print('No sysready status data available\n')
89+
90+
91+
92+
def main():
93+
parser = argparse.ArgumentParser(description='Display the System Ready status',
94+
formatter_class=argparse.RawTextHelpFormatter,
95+
epilog="""
96+
Examples:
97+
sysreadyshow --all
98+
sysreadyshow --allbrief
99+
sysreadyshow --alldetail
100+
""")
101+
102+
parser.add_argument('-a', '--all', action='store_true', help='all service status', default=True)
103+
parser.add_argument('-b', '--allbrief', action='store_true', help='all service status brief', default=False)
104+
parser.add_argument('-d', '--alldetail', action='store_true', help='all service status detail', default=False)
105+
args = parser.parse_args()
106+
107+
try:
108+
sysready = SysreadyShow()
109+
if args.alldetail:
110+
sysready.show("alldetail")
111+
elif args.allbrief:
112+
sysready.show("allbrief")
113+
else:
114+
sysready.show("all")
115+
except Exception as e:
116+
print(str(e), file=sys.stderr)
117+
sys.exit(1)
118+
119+
120+
if __name__ == "__main__":
121+
main()

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@
146146
'scripts/null_route_helper',
147147
'scripts/coredump_gen_handler.py',
148148
'scripts/techsupport_cleanup.py',
149-
'scripts/check_db_integrity.py'
149+
'scripts/check_db_integrity.py',
150+
'scripts/sysreadyshow'
150151
],
151152
entry_points={
152153
'console_scripts': [

show/system_health.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import click
55
from tabulate import tabulate
66
import utilities_common.cli as clicommon
7-
7+
import subprocess
88
#
99
# 'system-health' command ("show system-health")
1010
#
@@ -198,3 +198,64 @@ def monitor_list():
198198
entry.append(element[1]['type'])
199199
table.append(entry)
200200
click.echo(tabulate(table, header))
201+
202+
203+
204+
def run_command(command, display_cmd=False, return_cmd=False):
205+
if display_cmd:
206+
click.echo(click.style("Command: ", fg='cyan') + click.style(command, fg='green'))
207+
208+
# No conversion needed for intfutil commands as it already displays
209+
# both SONiC interface name and alias name for all interfaces.
210+
if clicommon.get_interface_naming_mode() == "alias" and not command.startswith("intfutil"):
211+
clicommon.run_command_in_alias_mode(command)
212+
raise sys.exit(0)
213+
214+
proc = subprocess.Popen(command, shell=True, text=True, stdout=subprocess.PIPE)
215+
216+
while True:
217+
if return_cmd:
218+
output = proc.communicate()[0]
219+
return output
220+
output = proc.stdout.readline()
221+
if output == "" and proc.poll() is not None:
222+
break
223+
if output:
224+
click.echo(output.rstrip('\n'))
225+
226+
rc = proc.poll()
227+
if rc != 0:
228+
sys.exit(rc)
229+
230+
231+
@system_health.group('sysready-status',invoke_without_command=True)
232+
#@system_health.command()
233+
@click.pass_context
234+
def sysready_status(ctx):
235+
"""Show system-health system ready status"""
236+
237+
if ctx.invoked_subcommand is None:
238+
try:
239+
cmd = "sudo sysreadyshow --all"
240+
run_command(cmd, display_cmd=False)
241+
242+
except Exception as e:
243+
click.echo("Exception: {}".format(str(e)))
244+
245+
246+
@sysready_status.command('brief')
247+
def sysready_status_brief():
248+
try:
249+
cmd = "sudo sysreadyshow --allbrief"
250+
run_command(cmd, display_cmd=False)
251+
except Exception as e:
252+
click.echo("Exception: {}".format(str(e)))
253+
254+
255+
@sysready_status.command('detail')
256+
def sysready_status_detail():
257+
try:
258+
cmd = "sudo sysreadyshow --alldetail"
259+
run_command(cmd, display_cmd=False)
260+
except Exception as e:
261+
click.echo("Exception: {}".format(str(e)))

sonic_package_manager/manifest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ def unmarshal(self, value):
177177
ManifestField('asic-service', DefaultMarshaller(bool), False),
178178
ManifestField('host-service', DefaultMarshaller(bool), True),
179179
ManifestField('delayed', DefaultMarshaller(bool), False),
180+
ManifestField('check_up_status', DefaultMarshaller(bool), False),
180181
ManifestRoot('warm-shutdown', [
181182
ManifestArray('after', DefaultMarshaller(str)),
182183
ManifestArray('before', DefaultMarshaller(str)),

sonic_package_manager/service_creator/feature.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,5 @@ def get_non_configurable_feature_entries(manifest) -> Dict[str, str]:
143143
'has_per_asic_scope': str(manifest['service']['asic-service']),
144144
'has_global_scope': str(manifest['service']['host-service']),
145145
'has_timer': str(manifest['service']['delayed']),
146+
'check_up_status': str(manifest['service']['check_up_status']),
146147
}

tests/sonic_package_manager/test_service_creator.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ def test_feature_registration(mock_sonic_db, manifest):
215215
'has_per_asic_scope': 'False',
216216
'has_global_scope': 'True',
217217
'has_timer': 'False',
218+
'check_up_status': 'False',
218219
})
219220

220221

@@ -227,6 +228,7 @@ def test_feature_update(mock_sonic_db, manifest):
227228
'has_per_asic_scope': 'False',
228229
'has_global_scope': 'True',
229230
'has_timer': 'False',
231+
'check_up_status': 'False',
230232
}
231233
mock_connector = Mock()
232234
mock_connector.get_entry = Mock(return_value=curr_feature_config)
@@ -249,6 +251,7 @@ def test_feature_update(mock_sonic_db, manifest):
249251
'has_per_asic_scope': 'False',
250252
'has_global_scope': 'True',
251253
'has_timer': 'True',
254+
'check_up_status': 'False',
252255
}),
253256
], any_order=True)
254257

@@ -268,6 +271,7 @@ def test_feature_registration_with_timer(mock_sonic_db, manifest):
268271
'has_per_asic_scope': 'False',
269272
'has_global_scope': 'True',
270273
'has_timer': 'True',
274+
'check_up_status': 'False',
271275
})
272276

273277

@@ -285,4 +289,5 @@ def test_feature_registration_with_non_default_owner(mock_sonic_db, manifest):
285289
'has_per_asic_scope': 'False',
286290
'has_global_scope': 'True',
287291
'has_timer': 'False',
292+
'check_up_status': 'False',
288293
})

0 commit comments

Comments
 (0)