Skip to content

Commit 3994fcd

Browse files
authored
Fix sonic-kdump-config for running commands with pipe (#4045)
* Fix sonic-kdump-config for running commands with pipe The exising code does not work because the commands with pipe are only able to run in shell * Avoid using use_shell=True in sonic-kdump-config * Fix unit test for the usage of getstatusoutput_noshell_pipe
1 parent 7cc435b commit 3994fcd

File tree

2 files changed

+49
-55
lines changed

2 files changed

+49
-55
lines changed

scripts/sonic-kdump-config

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import subprocess
2828
from swsscommon.swsscommon import ConfigDBConnector
2929
from sonic_installer.bootloader import get_bootloader
3030
from sonic_installer.common import IMAGE_PREFIX
31+
from sonic_py_common.general import getstatusoutput_noshell_pipe
3132

3233
aboot_cfg_template ="/host/image-%s/kernel-cmdline"
3334
grub_cfg = "/host/grub/grub.cfg"
@@ -371,8 +372,12 @@ def get_kdump_ssh_path():
371372
#
372373
# @return The integer value X from USE_KDUMP=X in /etc/default/kdump-tools
373374
def read_use_kdump():
374-
(rc, lines, err_str) = run_command("grep 'USE_KDUMP=.*' %s | cut -d = -f 2" % kdump_cfg, use_shell=False);
375-
if rc == 0 and type(lines) == list and len(lines) >= 1:
375+
cmd0 = ['grep', 'USE_KDUMP=.*', kdump_cfg]
376+
cmd1 = ['cut', '-d', '=', '-f', '2' ]
377+
378+
exitcodes, output = getstatusoutput_noshell_pipe(cmd0, cmd1)
379+
lines = output.splitlines()
380+
if all(code == 0 for code in exitcodes) and type(lines) == list and len(lines) >= 1:
376381
try:
377382
return int(lines[0])
378383
except Exception as e:
@@ -405,8 +410,12 @@ def write_use_kdump(use_kdump):
405410
#
406411
# @return The integer value X from KDUMP_NUM_DUMPS=X in /etc/default/kdump-tools
407412
def read_num_dumps():
408-
(rc, lines, err_str) = run_command("grep '#*KDUMP_NUM_DUMPS=.*' %s | cut -d = -f 2" % kdump_cfg, use_shell=False);
409-
if rc == 0 and type(lines) == list and len(lines) >= 1:
413+
cmd0 = ['grep', '#*KDUMP_NUM_DUMPS=.*', kdump_cfg]
414+
cmd1 = ['cut', '-d', '=', '-f', '2' ]
415+
416+
exitcodes, output = getstatusoutput_noshell_pipe(cmd0, cmd1)
417+
lines = output.splitlines()
418+
if all(code == 0 for code in exitcodes) and type(lines) == list and len(lines) >= 1:
410419
try:
411420
return int(lines[0])
412421
except Exception as e:
@@ -448,8 +457,13 @@ def write_kdump_remote():
448457
print("SSH and SSH_KEY commented out for local configuration.")
449458

450459
def read_ssh_string():
451-
(rc, lines, err_str) = run_command("grep '#*SSH=.*' %s | cut -d '=' -f 2 | tr -d '\"'" % kdump_cfg, use_shell=False)
452-
if rc == 0 and type(lines) == list and len(lines) >= 1:
460+
cmd0 = ['grep', '#*SSH=.*', kdump_cfg]
461+
cmd1 = ['cut', '-d', '=', '-f', '2' ]
462+
cmd2 = ['tr', '-d', '\"']
463+
464+
exitcodes, output = getstatusoutput_noshell_pipe(cmd0, cmd1, cmd2)
465+
lines = output.splitlines()
466+
if all(code == 0 for code in exitcodes) and type(lines) == list and len(lines) >= 1:
453467
try:
454468
return lines[0].strip() # Return the SSH string (e.g., user@ip_address)
455469
except Exception as e:
@@ -479,8 +493,13 @@ def write_ssh_string(ssh_string):
479493
sys.exit(1)
480494

481495
def read_ssh_path():
482-
(rc, lines, err_str) = run_command("grep '#*SSH_KEY=.*' %s | cut -d '=' -f 2 | tr -d '\"'" % kdump_cfg, use_shell=False)
483-
if rc == 0 and type(lines) == list and len(lines) >= 1:
496+
cmd0 = ['grep', '#*SSH_KEY=.*', kdump_cfg]
497+
cmd1 = ['cut', '-d', '=', '-f', '2' ]
498+
cmd2 = ['tr', '-d', '\"']
499+
500+
exitcodes, output = getstatusoutput_noshell_pipe(cmd0, cmd1, cmd2)
501+
lines = output.splitlines()
502+
if all(code == 0 for code in exitcodes) and type(lines) == list and len(lines) >= 1:
484503
try:
485504
ssh_path = lines[0].strip()
486505
if not ssh_path.startswith('/'): # Validate that the path starts with a slash

tests/sonic_kdump_config_test.py

Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -54,90 +54,65 @@ class TestSonicKdumpConfig(unittest.TestCase):
5454
def setup_class(cls):
5555
print("SETUP")
5656

57-
@patch("sonic_kdump_config.run_command")
57+
@patch('sonic_kdump_config.getstatusoutput_noshell_pipe')
5858
def test_read_num_kdumps(self, mock_run_cmd):
5959
"""Tests the function `read_num_kdumps(...)` in script `sonic-kdump-config`.
6060
"""
61-
mock_run_cmd.return_value = (0, ["0"], None)
61+
mock_run_cmd.return_value = ([0, 0], "0")
6262
num_dumps = sonic_kdump_config.read_num_dumps()
6363
assert num_dumps == 0
6464
logger.info("Value of 'num_dumps' is: '{}'.".format(num_dumps))
6565
logger.info("Expected value of 'num_dumps' is: '0'.")
6666

67-
mock_run_cmd.return_value = (0, ["NotInteger"], None)
68-
with self.assertRaises(SystemExit) as sys_exit:
69-
num_dumps = sonic_kdump_config.read_num_dumps()
70-
self.assertEqual(sys_exit.exception.code, 1)
71-
72-
mock_run_cmd.return_value = (0, (), None)
73-
with self.assertRaises(SystemExit) as sys_exit:
74-
num_dumps = sonic_kdump_config.read_num_dumps()
75-
self.assertEqual(sys_exit.exception.code, 1)
76-
77-
mock_run_cmd.return_value = (0, [], None)
67+
mock_run_cmd.return_value = ([0, 0], "NotInteger")
7868
with self.assertRaises(SystemExit) as sys_exit:
7969
num_dumps = sonic_kdump_config.read_num_dumps()
8070
self.assertEqual(sys_exit.exception.code, 1)
8171

82-
mock_run_cmd.return_value = (1, [], None)
72+
mock_run_cmd.return_value = ([0, 0], "")
8373
with self.assertRaises(SystemExit) as sys_exit:
8474
num_dumps = sonic_kdump_config.read_num_dumps()
8575
self.assertEqual(sys_exit.exception.code, 1)
8676

87-
mock_run_cmd.return_value = (1, ["3"], None)
77+
mock_run_cmd.return_value = ([1, 0], "3")
8878
with self.assertRaises(SystemExit) as sys_exit:
8979
num_dumps = sonic_kdump_config.read_num_dumps()
9080
self.assertEqual(sys_exit.exception.code, 1)
9181

92-
mock_run_cmd.return_value = (1, (), None)
82+
mock_run_cmd.return_value = ([0, 1], "3")
9383
with self.assertRaises(SystemExit) as sys_exit:
9484
num_dumps = sonic_kdump_config.read_num_dumps()
9585
self.assertEqual(sys_exit.exception.code, 1)
9686

97-
mock_run_cmd.return_value = (1, ["NotInteger"], None)
87+
mock_run_cmd.return_value = ([0, 1], "NotInteger")
9888
with self.assertRaises(SystemExit) as sys_exit:
9989
num_dumps = sonic_kdump_config.read_num_dumps()
10090
self.assertEqual(sys_exit.exception.code, 1)
10191

102-
@patch("sonic_kdump_config.run_command")
92+
@patch('sonic_kdump_config.getstatusoutput_noshell_pipe')
10393
def test_read_use_kdump(self, mock_run_cmd):
10494
"""Tests the function `read_use_kdump(...)` in script `sonic-kdump-config`.
10595
"""
106-
mock_run_cmd.return_value = (0, ["0"], None)
96+
mock_run_cmd.return_value = ([0, 0], "0")
10797
is_kdump_enabled = sonic_kdump_config.read_use_kdump()
10898
assert is_kdump_enabled == 0
10999

110-
mock_run_cmd.return_value = (0, (), None)
100+
mock_run_cmd.return_value = ([0, 0], "NotInteger")
111101
with self.assertRaises(SystemExit) as sys_exit:
112102
is_kdump_enabled = sonic_kdump_config.read_use_kdump()
113103
self.assertEqual(sys_exit.exception.code, 1)
114104

115-
mock_run_cmd.return_value = (0, [], None)
105+
mock_run_cmd.return_value = ([0, 0], "")
116106
with self.assertRaises(SystemExit) as sys_exit:
117107
is_kdump_enabled = sonic_kdump_config.read_use_kdump()
118108
self.assertEqual(sys_exit.exception.code, 1)
119109

120-
mock_run_cmd.return_value = (0, ["NotInteger"], None)
110+
mock_run_cmd.return_value = ([1, 0], "3")
121111
with self.assertRaises(SystemExit) as sys_exit:
122112
is_kdump_enabled = sonic_kdump_config.read_use_kdump()
123113
self.assertEqual(sys_exit.exception.code, 1)
124114

125-
mock_run_cmd.return_value = (1, ["0"], None)
126-
with self.assertRaises(SystemExit) as sys_exit:
127-
is_kdump_enabled = sonic_kdump_config.read_use_kdump()
128-
self.assertEqual(sys_exit.exception.code, 1)
129-
130-
mock_run_cmd.return_value = (1, ["NotInteger"], None)
131-
with self.assertRaises(SystemExit) as sys_exit:
132-
is_kdump_enabled = sonic_kdump_config.read_use_kdump()
133-
self.assertEqual(sys_exit.exception.code, 1)
134-
135-
mock_run_cmd.return_value = (1, (), None)
136-
with self.assertRaises(SystemExit) as sys_exit:
137-
is_kdump_enabled = sonic_kdump_config.read_use_kdump()
138-
self.assertEqual(sys_exit.exception.code, 1)
139-
140-
mock_run_cmd.return_value = (1, [], None)
115+
mock_run_cmd.return_value = ([0, 1], "3")
141116
with self.assertRaises(SystemExit) as sys_exit:
142117
is_kdump_enabled = sonic_kdump_config.read_use_kdump()
143118
self.assertEqual(sys_exit.exception.code, 1)
@@ -467,28 +442,28 @@ def test_cmd_kdump_remote(self, mock_run_command, mock_read_remote):
467442
sonic_kdump_config.cmd_kdump_remote(verbose=True)
468443
mock_print.assert_called_with("SSH and SSH_KEY commented out for local configuration.")
469444

470-
@patch("sonic_kdump_config.run_command")
445+
@patch('sonic_kdump_config.getstatusoutput_noshell_pipe')
471446
def test_read_ssh_string(self, mock_run_cmd):
472447
"""Tests the function `read_ssh_string(...)` in script `sonic-kdump-config`."""
473448

474449
# Test case for successful read
475-
mock_run_cmd.return_value = (0, ['user@ip_address'], None) # Simulate successful command execution
450+
mock_run_cmd.return_value = ([0, 0, 0], 'user@ip_address') # Simulate successful command execution
476451
ssh_string = sonic_kdump_config.read_ssh_string()
477452
self.assertEqual(ssh_string, 'user@ip_address')
478453

479454
# Test case for non-integer output
480-
mock_run_cmd.return_value = (0, ['NotAString'], None) # Simulate command execution returning a non-string
455+
mock_run_cmd.return_value = ([0, 0, 0], 'NotAString') # Simulate command execution returning a non-string
481456
ssh_string = sonic_kdump_config.read_ssh_string()
482457
self.assertEqual(ssh_string, 'NotAString')
483458

484459
# Test case for empty output
485-
mock_run_cmd.return_value = (0, [], None) # Simulate command execution with empty output
460+
mock_run_cmd.return_value = ([0, 0, 0], '') # Simulate command execution with empty output
486461
with self.assertRaises(SystemExit) as sys_exit:
487462
sonic_kdump_config.read_ssh_string()
488463
self.assertEqual(sys_exit.exception.code, 1)
489464

490465
# Test case for command failure
491-
mock_run_cmd.return_value = (1, [], None) # Simulate command failure
466+
mock_run_cmd.return_value = ([0, 0, 1], '') # Simulate command failure
492467
with self.assertRaises(SystemExit) as sys_exit:
493468
sonic_kdump_config.read_ssh_string()
494469
self.assertEqual(sys_exit.exception.code, 1)
@@ -530,23 +505,23 @@ def test_write_ssh_string(self, mock_read_ssh_string, mock_run_cmd):
530505
sonic_kdump_config.write_ssh_string('user@ip_address')
531506
self.assertEqual(sys_exit.exception.code, 1)
532507

533-
@patch("sonic_kdump_config.run_command")
508+
@patch('sonic_kdump_config.getstatusoutput_noshell_pipe')
534509
def test_read_ssh_path(self, mock_run_cmd):
535510
"""Tests the function `read_ssh_path(...)` in script `sonic-kdump-config`."""
536511

537512
# Test successful case with valid SSH path
538-
mock_run_cmd.return_value = (0, ['/path/to/keys'], None)
513+
mock_run_cmd.return_value = ([0, 0, 0], '/path/to/keys')
539514
ssh_path = sonic_kdump_config.read_ssh_path()
540515
self.assertEqual(ssh_path, '/path/to/keys')
541516

542517
# Test case where SSH path is invalid
543-
mock_run_cmd.return_value = (0, ['NotAPath'], None)
518+
mock_run_cmd.return_value = ([0, 0, 0], 'NotAPath')
544519
with self.assertRaises(SystemExit) as sys_exit:
545520
ssh_path = sonic_kdump_config.read_ssh_path()
546521
self.assertEqual(sys_exit.exception.code, 1)
547522

548523
# Test case where grep fails (no SSH path found)
549-
mock_run_cmd.return_value = (1, [], None)
524+
mock_run_cmd.return_value = ([0, 0, 1], '')
550525
with self.assertRaises(SystemExit) as sys_exit:
551526
ssh_path = sonic_kdump_config.read_ssh_path()
552527
self.assertEqual(sys_exit.exception.code, 1)

0 commit comments

Comments
 (0)