Skip to content

Commit 494dd62

Browse files
authored
show commands for SYSTEM READY (sonic-net#1851)
What I did show command support for System Ready Feature Implementation . Also sonic-package-manager support for app readiness. "show system-health sysready-status" "show system-health sysready-status detail" "show system-health sysready-status brief" How I did it Introduce "show system-health sysready-status" click CLI with options. How to verify it Check the click CLIs introduced.
1 parent 4fc09b1 commit 494dd62

File tree

8 files changed

+224
-1
lines changed

8 files changed

+224
-1
lines changed

scripts/sysreadyshow

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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 #lgtm [py/unused-import]
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+
SERVICE_STATUS_TABLE = 'ALL_SERVICE_STATUS'
30+
SYSREADY_TABLE = "SYSTEM_READY|SYSTEM_STATE"
31+
SERVICE_STATUS = 'service_status'
32+
APP_READY_STATUS = 'app_ready_status'
33+
FAIL_REASON = 'fail_reason'
34+
UPDATE_TIME = 'update_time'
35+
36+
class SysreadyShow(object):
37+
def __init__(self):
38+
self.db = SonicV2Connector(host="127.0.0.1")
39+
self.db.connect(self.db.STATE_DB)
40+
41+
def show(self, detailed_info):
42+
keys = self.db.keys(self.db.STATE_DB, SERVICE_STATUS_TABLE + '*')
43+
if not keys:
44+
print('No system ready status data available - system-health service might be down\n')
45+
return
46+
47+
sysready_state = self.db.get(self.db.STATE_DB, SYSREADY_TABLE, "Status")
48+
if sysready_state == "UP":
49+
print("System is ready\n")
50+
else:
51+
print("System is not ready - one or more services are not up\n")
52+
53+
#When brief option is specified, return here.
54+
if detailed_info == False:
55+
return
56+
57+
if detailed_info is None:
58+
header_info = header
59+
else:
60+
header_info = header_detail
61+
62+
table = []
63+
for key in natsorted(keys):
64+
key_list = key.split('|')
65+
if len(key_list) != 2: # error data in DB, log it and ignore
66+
print('Warn: Invalid key in table {}: {}'.format(SERVICE_STATUS_TABLE, key))
67+
continue
68+
69+
name = key_list[1]
70+
data_dict = self.db.get_all(self.db.STATE_DB, key)
71+
try:
72+
service_status = data_dict[SERVICE_STATUS]
73+
app_ready_status = data_dict[APP_READY_STATUS]
74+
fail_reason = data_dict[FAIL_REASON]
75+
update_time = data_dict[UPDATE_TIME]
76+
except ValueError as e:
77+
print('Error in data_dict')
78+
79+
if detailed_info is None:
80+
table.append((name, service_status, app_ready_status, fail_reason))
81+
else:
82+
table.append((name, service_status, app_ready_status, fail_reason, update_time))
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+
def main():
92+
parser = argparse.ArgumentParser(description='Display the System Ready status',
93+
formatter_class=argparse.RawTextHelpFormatter,
94+
epilog="""
95+
Examples:
96+
sysreadyshow
97+
sysreadyshow --brief
98+
sysreadyshow --detail
99+
""")
100+
101+
parser.add_argument('-b', '--brief', action='store_true', help='brief system ready status', default=False)
102+
parser.add_argument('-d', '--detail', action='store_true', help='detailed system ready status', default=False)
103+
args = parser.parse_args()
104+
105+
try:
106+
sysready = SysreadyShow()
107+
if args.detail:
108+
detailed_info = True
109+
elif args.brief:
110+
detailed_info = False
111+
else:
112+
detailed_info = None
113+
sysready.show(detailed_info)
114+
except Exception as e:
115+
print(str(e), file=sys.stderr)
116+
sys.exit(1)
117+
118+
119+
if __name__ == "__main__":
120+
main()

setup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@
150150
'scripts/memory_threshold_check_handler.py',
151151
'scripts/techsupport_cleanup.py',
152152
'scripts/storm_control.py',
153-
'scripts/check_db_integrity.py'
153+
'scripts/check_db_integrity.py',
154+
'scripts/sysreadyshow'
154155
],
155156
entry_points={
156157
'console_scripts': [

show/system_health.py

+31
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,34 @@ def monitor_list():
198198
entry.append(element[1]['type'])
199199
table.append(entry)
200200
click.echo(tabulate(table, header))
201+
202+
203+
@system_health.group('sysready-status',invoke_without_command=True)
204+
@click.pass_context
205+
def sysready_status(ctx):
206+
"""Show system-health system ready status"""
207+
208+
if ctx.invoked_subcommand is None:
209+
try:
210+
cmd = "sysreadyshow"
211+
clicommon.run_command(cmd, display_cmd=False)
212+
except Exception as e:
213+
click.echo("Exception: {}".format(str(e)))
214+
215+
216+
@sysready_status.command('brief')
217+
def sysready_status_brief():
218+
try:
219+
cmd = "sysreadyshow --brief"
220+
clicommon.run_command(cmd, display_cmd=False)
221+
except Exception as e:
222+
click.echo("Exception: {}".format(str(e)))
223+
224+
225+
@sysready_status.command('detail')
226+
def sysready_status_detail():
227+
try:
228+
cmd = "sysreadyshow --detail"
229+
clicommon.run_command(cmd, display_cmd=False)
230+
except Exception as e:
231+
click.echo("Exception: {}".format(str(e)))

sonic_package_manager/manifest.py

+1
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

+1
Original file line numberDiff line numberDiff line change
@@ -202,4 +202,5 @@ def get_non_configurable_feature_entries(manifest) -> Dict[str, str]:
202202
'has_per_asic_scope': str(manifest['service']['asic-service']),
203203
'has_global_scope': str(manifest['service']['host-service']),
204204
'has_timer': str(manifest['service']['delayed']),
205+
'check_up_status': str(manifest['service']['check_up_status']),
205206
}

tests/mock_tables/state_db.json

+27
Original file line numberDiff line numberDiff line change
@@ -837,5 +837,32 @@
837837
},
838838
"LINK_TRAINING|Ethernet112": {
839839
"status": "off"
840+
},
841+
"ALL_SERVICE_STATUS|mgmt-framework": {
842+
"app_ready_status": "OK",
843+
"fail_reason": "-",
844+
"service_status": "OK",
845+
"update_time": "-"
846+
},
847+
"ALL_SERVICE_STATUS|swss": {
848+
"app_ready_status": "OK",
849+
"fail_reason": "-",
850+
"service_status": "OK",
851+
"update_time": "-"
852+
},
853+
"ALL_SERVICE_STATUS|bgp": {
854+
"app_ready_status": "Down",
855+
"fail_reason": "Inactive",
856+
"service_status": "Down",
857+
"update_time": "-"
858+
},
859+
"ALL_SERVICE_STATUS|pmon": {
860+
"app_ready_status": "OK",
861+
"fail_reason": "-",
862+
"service_status": "OK",
863+
"update_time": "-"
864+
},
865+
"SYSTEM_READY|SYSTEM_STATE": {
866+
"Status":"DOWN"
840867
}
841868
}

tests/sonic_package_manager/test_service_creator.py

+5
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ def test_feature_registration(mock_sonic_db, manifest):
216216
'has_per_asic_scope': 'False',
217217
'has_global_scope': 'True',
218218
'has_timer': 'False',
219+
'check_up_status': 'False',
219220
})
220221

221222

@@ -228,6 +229,7 @@ def test_feature_update(mock_sonic_db, manifest):
228229
'has_per_asic_scope': 'False',
229230
'has_global_scope': 'True',
230231
'has_timer': 'False',
232+
'check_up_status': 'False',
231233
}
232234
mock_connector = Mock()
233235
mock_connector.get_entry = Mock(return_value=curr_feature_config)
@@ -250,6 +252,7 @@ def test_feature_update(mock_sonic_db, manifest):
250252
'has_per_asic_scope': 'False',
251253
'has_global_scope': 'True',
252254
'has_timer': 'True',
255+
'check_up_status': 'False',
253256
}),
254257
], any_order=True)
255258

@@ -270,6 +273,7 @@ def test_feature_registration_with_timer(mock_sonic_db, manifest):
270273
'has_per_asic_scope': 'False',
271274
'has_global_scope': 'True',
272275
'has_timer': 'True',
276+
'check_up_status': 'False',
273277
})
274278

275279

@@ -288,6 +292,7 @@ def test_feature_registration_with_non_default_owner(mock_sonic_db, manifest):
288292
'has_per_asic_scope': 'False',
289293
'has_global_scope': 'True',
290294
'has_timer': 'False',
295+
'check_up_status': 'False',
291296
})
292297

293298

tests/system_health_test.py

+37
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,43 @@ def test_health_detail(self):
306306
"""
307307
assert result.output == expected
308308

309+
def test_health_systemready(self):
310+
runner = CliRunner()
311+
result = runner.invoke(show.cli.commands["system-health"].commands["sysready-status"])
312+
click.echo(result.output)
313+
print("myresult:{}".format(result.output))
314+
expected = """\
315+
System is not ready - one or more services are not up
316+
317+
Service-Name Service-Status App-Ready-Status Down-Reason
318+
-------------- ---------------- ------------------ -------------
319+
bgp Down Down Inactive
320+
mgmt-framework OK OK -
321+
pmon OK OK -
322+
swss OK OK -
323+
"""
324+
assert result.output == expected
325+
result = runner.invoke(show.cli.commands["system-health"].commands["sysready-status"],["brief"])
326+
click.echo(result.output)
327+
print("myresult:{}".format(result.output))
328+
expected = """\
329+
System is not ready - one or more services are not up
330+
"""
331+
assert result.output == expected
332+
result = runner.invoke(show.cli.commands["system-health"].commands["sysready-status"],["detail"])
333+
click.echo(result.output)
334+
print("myresult:{}".format(result.output))
335+
expected = """\
336+
System is not ready - one or more services are not up
337+
338+
Service-Name Service-Status App-Ready-Status Down-Reason AppStatus-UpdateTime
339+
-------------- ---------------- ------------------ ------------- ----------------------
340+
bgp Down Down Inactive -
341+
mgmt-framework OK OK - -
342+
pmon OK OK - -
343+
swss OK OK - -
344+
"""
345+
309346
@classmethod
310347
def teardown_class(cls):
311348
print("TEARDOWN")

0 commit comments

Comments
 (0)