Skip to content

Commit dcd6743

Browse files
authored
Add show reboot-cause history cli (sonic-net#1210)
What I did To make the reboot cause history information available for telemetry service, during processing the reboot-cause on boot, the information is saved in state-db. The cli command show reboot-cause is changed to support the new file format and file name. New command show reboot-cause history is added to display the reboot-cause information which is stored in state-db. How I did it Add the 'show reboot-cause history' command to read up to the10 previous reboot-cause information from state-db and display them. Read the last reboot-cause from the new file "previous-reboot-cause.json" and process the data to display for show reboot-cause New command output (if the output of a command-line utility has changed) admin@sonic:~$ show reboot-cause history name cause time user comment ------------------- ----------- ---------------------------- ------ --------- 2020_10_09_04_53_58 warm-reboot Fri Oct 9 04:51:47 UTC 2020 admin 2020_10_09_02_33_06 reboot Fri Oct 9 02:29:44 UTC 2020 admin 2020_10_09_02_00_53 fast-reboot Fri Oct 9 01:58:04 UTC 2020 admin 2020_10_09_01_56_59 reboot Fri Oct 9 01:53:49 UTC 2020 admin
1 parent 6fabbed commit dcd6743

File tree

5 files changed

+175
-21
lines changed

5 files changed

+175
-21
lines changed

doc/Command-Reference.md

+20
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,26 @@ This command displays the cause of the previous reboot
552552
User issued reboot command [User: admin, Time: Mon Mar 25 01:02:03 UTC 2019]
553553
```
554554

555+
**show reboot-cause history**
556+
557+
This command displays the history of the previous reboots up to 10 entry
558+
559+
- Usage:
560+
```
561+
show reboot-cause history
562+
```
563+
564+
- Example:
565+
```
566+
admin@sonic:~$ show reboot-cause history
567+
Name Cause Time User Comment
568+
------------------- ----------- ---------------------------- ------ ---------
569+
2020_10_09_02_33_06 reboot Fri Oct 9 02:29:44 UTC 2020 admin
570+
2020_10_09_01_56_59 reboot Fri Oct 9 01:53:49 UTC 2020 admin
571+
2020_10_09_02_00_53 fast-reboot Fri Oct 9 01:58:04 UTC 2020 admin
572+
2020_10_09_04_53_58 warm-reboot Fri Oct 9 04:51:47 UTC 2020 admin
573+
```
574+
555575
**show uptime**
556576

557577
This command displays the current system uptime

show/main.py

+2-21
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from . import kube
2525
from . import mlnx
2626
from . import muxcable
27+
from . import reboot_cause
2728
from . import vlan
2829
from . import system_health
2930

@@ -134,6 +135,7 @@ def cli(ctx):
134135
cli.add_command(interfaces.interfaces)
135136
cli.add_command(kube.kubernetes)
136137
cli.add_command(muxcable.muxcable)
138+
cli.add_command(reboot_cause.reboot_cause)
137139
cli.add_command(vlan.vlan)
138140
cli.add_command(system_health.system_health)
139141

@@ -1812,27 +1814,6 @@ def mmu():
18121814
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, text=True)
18131815
click.echo(proc.stdout.read())
18141816

1815-
1816-
#
1817-
# 'reboot-cause' command ("show reboot-cause")
1818-
#
1819-
@cli.command('reboot-cause')
1820-
def reboot_cause():
1821-
"""Show cause of most recent reboot"""
1822-
PREVIOUS_REBOOT_CAUSE_FILE = "/host/reboot-cause/previous-reboot-cause.txt"
1823-
1824-
# At boot time, PREVIOUS_REBOOT_CAUSE_FILE is generated based on
1825-
# the contents of the 'reboot cause' file as it was left when the device
1826-
# went down for reboot. This file should always be created at boot,
1827-
# but check first just in case it's not present.
1828-
if not os.path.isfile(PREVIOUS_REBOOT_CAUSE_FILE):
1829-
click.echo("Unable to determine cause of previous reboot\n")
1830-
else:
1831-
cmd = "cat {}".format(PREVIOUS_REBOOT_CAUSE_FILE)
1832-
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, text=True)
1833-
click.echo(proc.stdout.read())
1834-
1835-
18361817
#
18371818
# 'line' command ("show line")
18381819
#

show/reboot_cause.py

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import json
2+
import os
3+
import sys
4+
5+
import click
6+
from tabulate import tabulate
7+
from swsssdk import SonicV2Connector
8+
import utilities_common.cli as clicommon
9+
10+
11+
PREVIOUS_REBOOT_CAUSE_FILE = "/host/reboot-cause/previous-reboot-cause.json"
12+
USER_ISSUED_REBOOT_CAUSE_REGEX ="User issued \'{}\' command [User: {}, Time: {}]"
13+
14+
def read_reboot_cause_file():
15+
result = ""
16+
if os.path.exists(PREVIOUS_REBOOT_CAUSE_FILE):
17+
with open(PREVIOUS_REBOOT_CAUSE_FILE) as f:
18+
result = json.load(f)
19+
return result
20+
21+
#
22+
# 'reboot-cause' group ("show reboot-cause")
23+
#
24+
@click.group(cls=clicommon.AliasedGroup, invoke_without_command=True)
25+
@click.pass_context
26+
def reboot_cause(ctx):
27+
"""Show cause of most recent reboot"""
28+
if ctx.invoked_subcommand is None:
29+
reboot_cause = ""
30+
# Read the previous reboot cause
31+
data = read_reboot_cause_file()
32+
if data['user'] == "N/A":
33+
reboot_cause = "{}".format(data['cause'])
34+
else:
35+
reboot_cause = USER_ISSUED_REBOOT_CAUSE_REGEX.format(data['cause'], data['user'], data['time'])
36+
37+
click.echo(reboot_cause)
38+
39+
# 'history' subcommand ("show reboot-cause history")
40+
@reboot_cause.command()
41+
def history():
42+
"""Show history of reboot-cause"""
43+
REBOOT_CAUSE_TABLE_NAME = "REBOOT_CAUSE"
44+
TABLE_NAME_SEPARATOR = '|'
45+
db = SonicV2Connector(host='127.0.0.1')
46+
db.connect(db.STATE_DB, False) # Make one attempt only
47+
prefix = REBOOT_CAUSE_TABLE_NAME + TABLE_NAME_SEPARATOR
48+
_hash = '{}{}'.format(prefix, '*')
49+
table_keys = db.keys(db.STATE_DB, _hash)
50+
if table_keys is not None:
51+
table_keys.sort(reverse=True)
52+
53+
table = []
54+
for tk in table_keys:
55+
entry = db.get_all(db.STATE_DB, tk)
56+
r = []
57+
r.append(tk.replace(prefix,""))
58+
r.append(entry['cause'] if 'cause' in entry else "")
59+
r.append(entry['time'] if 'time' in entry else "")
60+
r.append(entry['user'] if 'user' in entry else "")
61+
r.append(entry['comment'] if 'comment' in entry else "")
62+
table.append(r)
63+
64+
header = ['Name', 'Cause', 'Time', 'User', 'Comment']
65+
click.echo(tabulate(table, header, numalign="left"))
66+
else:
67+
click.echo("Reboot-cause history is not yet available in StateDB")
68+
sys.exit(1)

tests/mock_tables/state_db.json

+12
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,18 @@
273273
"30" : "200.200.200.5@Vlan1000",
274274
"31" : "200.200.200.5@Vlan1000"
275275
},
276+
"REBOOT_CAUSE|2020_10_09_04_53_58": {
277+
"cause": "warm-reboot",
278+
"time": "Fri Oct 9 04:51:47 UTC 2020",
279+
"user": "admin",
280+
"comment": "N/A"
281+
},
282+
"REBOOT_CAUSE|2020_10_09_02_33_06": {
283+
"cause": "reboot",
284+
"time": "Fri Oct 9 02:29:44 UTC 2020",
285+
"user": "admin",
286+
"comment": "N/A"
287+
},
276288
"CHASSIS_TABLE|CHASSIS 1": {
277289
"module_num": "5"
278290
},

tests/reboot_cause_test.py

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import os
2+
import sys
3+
import textwrap
4+
import mock
5+
from click.testing import CliRunner
6+
from .mock_tables import dbconnector
7+
8+
test_path = os.path.dirname(os.path.abspath(__file__))
9+
modules_path = os.path.dirname(test_path)
10+
sys.path.insert(0, modules_path)
11+
12+
13+
import show.main as show
14+
15+
"""
16+
Note: The following 'show reboot-cause' commands simply call other SONiC
17+
CLI utilities, so the unit tests for the other utilities are expected
18+
to cover testing their functionality:
19+
20+
show reboot-cause
21+
show reboot-cause history
22+
"""
23+
24+
class TestShowRebootCause(object):
25+
@classmethod
26+
def setup_class(cls):
27+
print("SETUP")
28+
os.environ["UTILITIES_UNIT_TESTING"] = "1"
29+
30+
# Test 'show reboot-cause' without previous-reboot-cause.json
31+
def test_reboot_cause_no_history_file(self):
32+
expected_output = ""
33+
runner = CliRunner()
34+
result = runner.invoke(show.cli.commands["reboot-cause"], [])
35+
assert result.output == expected_output
36+
37+
# Test 'show reboot-cause' with user issued reboot
38+
def test_reboot_cause_user(self):
39+
expected_output = "User issued 'reboot' command [User: admin, Time: Thu Oct 22 03:11:08 UTC 2020]\n"
40+
41+
with mock.patch("show.reboot_cause.read_reboot_cause_file", return_value={"comment": "", "gen_time": "2020_10_22_03_14_07", "cause": "reboot", "user": "admin", "time": "Thu Oct 22 03:11:08 UTC 2020"}):
42+
runner = CliRunner()
43+
result = runner.invoke(show.cli.commands["reboot-cause"], [])
44+
assert result.output == expected_output
45+
46+
47+
# Test 'show reboot-cause' with non-user issue reboot (hardware reboot-cause or unknown reboot-cause)
48+
def test_reboot_cause_non_user(self):
49+
expected_output = "Watchdog\n"
50+
51+
with mock.patch("show.reboot_cause.read_reboot_cause_file", return_value={"comment": "N/A", "gen_time": "2020_10_22_03_15_08", "cause": "Watchdog", "user": "N/A", "time": "N/A"}):
52+
runner = CliRunner()
53+
result = runner.invoke(show.cli.commands["reboot-cause"], [])
54+
assert result.output == expected_output
55+
56+
# Test 'show reboot-cause history'
57+
def test_reboot_cause_history(self):
58+
expected_output = """\
59+
Name Cause Time User Comment
60+
------------------- ----------- ---------------------------- ------ ---------
61+
2020_10_09_04_53_58 warm-reboot Fri Oct 9 04:51:47 UTC 2020 admin N/A
62+
2020_10_09_02_33_06 reboot Fri Oct 9 02:29:44 UTC 2020 admin N/A
63+
"""
64+
runner = CliRunner()
65+
result = runner.invoke(show.cli.commands["reboot-cause"].commands["history"], [])
66+
print(result.output)
67+
assert result.output == expected_output
68+
69+
@classmethod
70+
def teardown_class(cls):
71+
print("TEARDOWN")
72+
os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1])
73+
os.environ["UTILITIES_UNIT_TESTING"] = "0"

0 commit comments

Comments
 (0)