Skip to content

[consutil, config]: add supporting command for console monitor utility#4208

Merged
lizhijianrd merged 6 commits into
sonic-net:masterfrom
wiperi:cliffchen/submit/console_monitor_commands
May 11, 2026
Merged

[consutil, config]: add supporting command for console monitor utility#4208
lizhijianrd merged 6 commits into
sonic-net:masterfrom
wiperi:cliffchen/submit/console_monitor_commands

Conversation

@wiperi
Copy link
Copy Markdown
Contributor

@wiperi wiperi commented Jan 19, 2026

What I did

Add new modification and new commands to support console monitor feature. HLD

How I did it

  1. Modify show line commands. Display 2 new column, Oper State and State Duration.
  2. Add new commands config console heartbeat {enable/disable}. Provide configuration ability of console monitor dte (heartbeat sending) service.

How to verify it

Manually tested on physical testbed.

Previous command output (if the output of a command-line utility has changed)

  Line    Baud    Flow Control    PID    Start Time      Device
------  ------  --------------  -----  ------------  ----------  
     1    9600        Disabled      -             -   Terminal1             
     2    9600        Disabled      -             -   Terminal2

New command output (if the output of a command-line utility has changed)

  Line    Baud    Flow Control    PID    Start Time      Device    Oper State    State Duration
------  ------  --------------  -----  ------------  ----------  ------------  ----------------
     1    9600        Disabled      -             -   Terminal1             Up       3d16h24m34s
     2    9600        Disabled      -             -   Terminal2        Unknown           1h5m17s

Copilot AI review requested due to automatic review settings January 19, 2026 05:04
@mssonicbld
Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for the console monitor utility feature by extending the show line command to display operational state and state duration information, and adding a new config console heartbeat command to control the heartbeat feature on controlled devices.

Changes:

  • Modified show line command to display two new columns: "Oper State" and "State Duration"
  • Added config console heartbeat {enable/disable} command for configuring the console monitor DTE service
  • Updated picocom connection to use PTY symlink suffix for device paths

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
consutil/main.py Updated table headers and body to include oper_state and state_duration columns in show command output
consutil/lib.py Added new properties (oper_state, last_state_change, state_duration) to ConsolePortInfo class, implemented duration formatting logic, updated picocom command with PTY_SYMLINK_SUFFIX, and modified update_state to preserve oper_state and last_state_change
config/console.py Added new heartbeat command to enable/disable console heartbeat on controlled devices

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread consutil/lib.py
Comment on lines +164 to +195
def state_duration(self):
"""Calculate and format the duration since last state change.
Format: XdXhXmXs (only shows non-zero parts)
"""
if not self.last_state_change:
return None
try:
ts = int(self.last_state_change)
now = int(time.time())
diff = now - ts
if diff < 0:
return None

# Calculate time components
days, remainder = divmod(diff, 24 * 3600)
hours, remainder = divmod(remainder, 3600)
minutes, seconds = divmod(remainder, 60)

# Build formatted string, only include non-zero parts
parts = []
if days > 0:
parts.append(f"{days}d")
if hours > 0:
parts.append(f"{hours}h")
if minutes > 0:
parts.append(f"{minutes}m")
if seconds > 0 or not parts: # Always show seconds if nothing else
parts.append(f"{seconds}s")

return "".join(parts)
except (ValueError, OSError):
return None
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The state_duration property includes logic for calculating and formatting time durations but lacks test coverage. Consider adding unit tests to verify the calculation handles edge cases correctly (e.g., duration of 0 seconds, very long durations, invalid timestamps, negative time differences).

Copilot uses AI. Check for mistakes.
Comment thread consutil/lib.py Outdated
# build and start picocom command
flow_cmd = "h" if self.flow_control else "n"
cmd = "picocom -b {} -f {} {}{}".format(self.baud, flow_cmd, SysInfoProvider.DEVICE_PREFIX, self.line_num)
cmd = "picocom -b {} -f {} {}{}{}".format(self.baud, flow_cmd, SysInfoProvider.DEVICE_PREFIX, self.line_num, PTY_SYMLINK_SUFFIX)
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding PTY_SYMLINK_SUFFIX to the picocom command changes the device path from "/dev/ttyUSB" to "/dev/ttyUSB-PTS". This may cause the process detection regex in _parse_processes_info to fail to match these processes, since the regex expects the device path to match the pattern DEVICE_PREFIX + digits. Verify that the regex pattern correctly handles the new device path format, or update it to accommodate the "-PTS" suffix.

Suggested change
cmd = "picocom -b {} -f {} {}{}{}".format(self.baud, flow_cmd, SysInfoProvider.DEVICE_PREFIX, self.line_num, PTY_SYMLINK_SUFFIX)
cmd = "picocom -b {} -f {} {}{}".format(self.baud, flow_cmd, SysInfoProvider.DEVICE_PREFIX, self.line_num)

Copilot uses AI. Check for mistakes.
Comment thread consutil/lib.py Outdated
Comment on lines +346 to +402
self._state_db.set(self._state_db.STATE_DB, key, STATE_KEY, state)
self._state_db.set(self._state_db.STATE_DB, key, PID_KEY, pid)
self._state_db.set(self._state_db.STATE_DB, key, START_TIME_KEY, date)

# Read existing oper_state and last_state_change from STATE_DB
existing_data = self._state_db.get_all(self._state_db.STATE_DB, key)
oper_state = existing_data.get(OPER_STATE_KEY, "") if existing_data else ""
last_state_change = existing_data.get(LAST_STATE_CHANGE_KEY, "") if existing_data else ""

Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for reading existing oper_state and last_state_change happens after the state update operations (lines 394-396). This means the get_all call will retrieve the already-updated values rather than the existing values before the update. The get_all should be called before any set operations to properly preserve the existing oper_state and last_state_change values.

Suggested change
self._state_db.set(self._state_db.STATE_DB, key, STATE_KEY, state)
self._state_db.set(self._state_db.STATE_DB, key, PID_KEY, pid)
self._state_db.set(self._state_db.STATE_DB, key, START_TIME_KEY, date)
# Read existing oper_state and last_state_change from STATE_DB
existing_data = self._state_db.get_all(self._state_db.STATE_DB, key)
oper_state = existing_data.get(OPER_STATE_KEY, "") if existing_data else ""
last_state_change = existing_data.get(LAST_STATE_CHANGE_KEY, "") if existing_data else ""
# Read existing oper_state and last_state_change from STATE_DB
existing_data = self._state_db.get_all(self._state_db.STATE_DB, key)
oper_state = existing_data.get(OPER_STATE_KEY, "") if existing_data else ""
last_state_change = existing_data.get(LAST_STATE_CHANGE_KEY, "") if existing_data else ""
# Update state-related fields in STATE_DB
self._state_db.set(self._state_db.STATE_DB, key, STATE_KEY, state)
self._state_db.set(self._state_db.STATE_DB, key, PID_KEY, pid)
self._state_db.set(self._state_db.STATE_DB, key, START_TIME_KEY, date)

Copilot uses AI. Check for mistakes.
Comment thread config/console.py
Comment on lines +56 to +72
@console.command('heartbeat')
@clicommon.pass_db
@click.argument('mode', metavar='<mode>', required=True, type=click.Choice(["enable", "disable"]))
def update_console_heartbeat(db, mode):
"""Enable/Disable console heartbeat on controlled device (DTE side)"""
config_db = ValidatedConfigDBConnector(db.cfgdb)

table = "CONSOLE_SWITCH"
dataKey1 = 'controlled_device'
dataKey2 = 'enabled'

data = { dataKey2 : "yes" if mode == "enable" else "no" }
try:
config_db.mod_entry(table, dataKey1, data)
except ValueError as e:
ctx = click.get_current_context()
ctx.fail("Invalid ConfigDB. Error: {}".format(e))
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new console heartbeat command lacks test coverage. The test file console_test.py includes tests for other console commands (enable, disable, add, del) but no tests for the heartbeat command. Add tests to verify the heartbeat enable/disable functionality works correctly, including validation error cases.

Copilot uses AI. Check for mistakes.
- modify consutil, supporting oper state display

Signed-off-by: cliffchen <t-cliffchen+github@microsoft.com>
@wiperi wiperi force-pushed the cliffchen/submit/console_monitor_commands branch from 3994817 to 80383da Compare January 19, 2026 05:40
@mssonicbld
Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@mssonicbld
Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

- add tests for state duration of show line commands
-  add tests for config console heartbeat commands
- update TestConsutilShow to match new output format

Signed-off-by: cliffchen <t-cliffchen+github@microsoft.com>
@wiperi wiperi force-pushed the cliffchen/submit/console_monitor_commands branch from 37f28c9 to 9669792 Compare January 19, 2026 09:27
@mssonicbld
Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

Signed-off-by: cliffchen <t-cliffchen+github@microsoft.com>
@mssonicbld
Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@mssonicbld
Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

…eric suffix

Signed-off-by: cliffchen <t-cliffchen+github@microsoft.com>
@wiperi wiperi force-pushed the cliffchen/submit/console_monitor_commands branch from 1ddbc55 to 3e024c0 Compare January 27, 2026 20:24
@mssonicbld
Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@mssonicbld
Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

Comment thread consutil/main.py Outdated
@mssonicbld
Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

Signed-off-by: Zhijian Li <zhijianli@microsoft.com>
@lizhijianrd lizhijianrd force-pushed the cliffchen/submit/console_monitor_commands branch from 0982842 to 21c2039 Compare May 9, 2026 15:18
@mssonicbld
Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@lizhijianrd lizhijianrd merged commit c37b846 into sonic-net:master May 11, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants