Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion doc/Command-Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -14772,9 +14772,11 @@ Resulting archive file is saved as `/var/dump/<DEVICE_HOST_NAME>_YYYYMMDD_HHMMSS

- Example:
```
admin@sonic:~$ show techsupport [--since=<time_specifier>]
admin@sonic:~$ show techsupport [--since=<time_specifier>] [--filename=<file_name>]
```

With the `--filename` option, files will be saved under `/var/dump/<file_name>`. Passing in filenames with path components will be rejected.

If the SONiC system was running for quite some time `show techsupport` will produce a large dump file. To reduce the amount of syslog and core files gathered during system dump use `--since` option:

- Examples:
Expand All @@ -14784,6 +14786,9 @@ If the SONiC system was running for quite some time `show techsupport` will prod
```
admin@sonic:~$ show techsupport --since='hour ago' # Will collect syslog and core files for the last one hour
```
```
admin@sonic:~$ show techsupport --filename=custom_name # saved under /var/dump/custom_name.tar.gz
```

### Debug Dumps

Expand Down
39 changes: 37 additions & 2 deletions scripts/generate_dump
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ TIMEOUT_MIN="5"
SKIP_BCMCMD=0
SAVE_STDERR=true
RETURN_CODE=$EXT_SUCCESS
CUSTOM_FILENAME_SET=false
DEBUG_DUMP=false
ROUTE_TAB_LIMIT_DIRECT_ITERATION=24000
IS_SUPERVISOR=false
Expand Down Expand Up @@ -2903,11 +2904,15 @@ OPTIONS
Redirect any intermediate errors to STDERR
-d
Collect the output of debug dump cli
-f FILENAME
Base name for the output dump file (default: sonic_dump_<hostname>_<timestamp>).
The file will be created as $DUMPDIR/<FILENAME>.tar[.gz].
WARN: if the file already exists, this command will fail and exit.
EOF
}


while getopts ":xnvhzas:t:r:d" opt; do
while getopts ":xnvhzas:t:r:d:f:" opt; do
case $opt in
x)
# enable bash debugging
Expand Down Expand Up @@ -2958,13 +2963,43 @@ while getopts ":xnvhzas:t:r:d" opt; do
d)
DEBUG_DUMP=true
;;
/?)
f)
BASE="${OPTARG}"
if [[ "$BASE" == */* ]]; then
echo "ERROR: Invalid filename ${BASE}, filename must be a plain name with no path component." >&2
exit $EXT_GENERAL
fi
TARDIR=$DUMPDIR/$BASE
TARFILE=$DUMPDIR/$BASE.tar
LOGDIR=$DUMPDIR/$BASE/dump
CUSTOM_FILENAME_SET=true

# avoid dumps with redundant file extensions like "customname.tar.gz.tar.gz"
if [[ "$BASE" == *.tar.gz ]]; then
BASE="${BASE%.tar.gz}".
echo "INFO: Pruning redundant file extension '.tar.gz' from filename; dump will be saved under '${TARFILE}.gz'" >&2
elif [[ "$BASE" == *.tar ]]; then
BASE="${BASE%.tar}"
echo "INFO: Pruning redundant file extension '.tar' from filename; dump will be saved under '${TARFILE}'" >&2
fi
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit $EXT_GENERAL
;;
esac
done

if $CUSTOM_FILENAME_SET; then
if $DO_COMPRESS && [[ -f ${TARFILE}.gz ]]; then
echo "WARNING: ${TARFILE}.gz already exists, aborting without dumping." >&2
exit $EXT_GENERAL
elif ! $DO_COMPRESS && [[ -f $TARFILE ]]; then
echo "WARNING: $TARFILE already exists, aborting without dumping." >&2
exit $EXT_GENERAL
fi
fi

# Check permissions before proceeding further
if [ `whoami` != root ] && ! $NOOP;
then
Expand Down
10 changes: 9 additions & 1 deletion show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1777,14 +1777,17 @@ def users(verbose):

@cli.command()
@click.option('--since', required=False, help="Collect logs and core files since given date")
@click.option('-f', '--filename', required=False,
help="Custom dump filename under /var/dump. WARN: if file already exists, this command will abort.")
@click.option('-g', '--global-timeout', required=False, type=int, help="Global timeout in minutes. WARN: Dump might be incomplete if enforced")
@click.option('-c', '--cmd-timeout', default=5, type=int, help="Individual command timeout in minutes. Default 5 mins")
@click.option('--verbose', is_flag=True, help="Enable verbose output")
@click.option('--allow-process-stop', is_flag=True, help="Dump additional data which may require system interruption")
@click.option('--silent', is_flag=True, help="Run techsupport in silent mode")
@click.option('--debug-dump', is_flag=True, help="Collect Debug Dump Output")
@click.option('--redirect-stderr', '-r', is_flag=True, help="Redirect an intermediate errors to STDERR")
def techsupport(since, global_timeout, cmd_timeout, verbose, allow_process_stop, silent, debug_dump, redirect_stderr):
def techsupport(since, filename, global_timeout, cmd_timeout,
verbose, allow_process_stop, silent, debug_dump, redirect_stderr):
"""Gather information for troubleshooting"""
cmd = ["sudo"]

Expand All @@ -1797,6 +1800,11 @@ def techsupport(since, global_timeout, cmd_timeout, verbose, allow_process_stop,
else:
cmd += ['generate_dump', '-v']

if filename:
if '/' in filename:
raise click.UsageError("-f/--filename requires a file name with no path components")
cmd += ["-f", filename]

if allow_process_stop:
cmd += ["-a"]

Expand Down
18 changes: 17 additions & 1 deletion tests/techsupport_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest
import show.main
from unittest.mock import patch, Mock
from unittest.mock import patch
from click.testing import CliRunner

EXPECTED_BASE_COMMAND = ['sudo']
Expand All @@ -15,10 +15,26 @@
(['--allow-process-stop'], ['generate_dump', '-v', '-a', '-t', '5']),
(['--silent'], ['generate_dump', '-t', '5']),
(['--debug-dump', '--redirect-stderr'], ['generate_dump', '-v', '-d', '-t', '5', '-r']),
(['-f', 'custom-filename'], ['generate_dump', '-v', '-f', 'custom-filename', '-t', '5'])
]
)
def test_techsupport(run_command, cli_arguments, expected):
runner = CliRunner()
result = runner.invoke(show.main.cli.commands['techsupport'], cli_arguments)
run_command.assert_called_with(EXPECTED_BASE_COMMAND + expected, display_cmd=False)


@patch("show.main.run_command")
@pytest.mark.parametrize(
"cli_arguments,expected_msg",
[
(['-f', '/other/dir/custom-filename'], "no path components"),
(['-f', '../relative/filepath'], "no path components"),
]
)
def test_techsupport_filepath(run_command, cli_arguments, expected_msg):
runner = CliRunner()
result = runner.invoke(show.main.cli.commands['techsupport'], cli_arguments)
assert result.exit_code != 0
assert expected_msg in result.output
run_command.assert_not_called()
Loading