[consutil, config]: add supporting command for console monitor utility#4208
Conversation
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
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 linecommand 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.
| 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 |
There was a problem hiding this comment.
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).
| # 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) |
There was a problem hiding this comment.
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.
| 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) |
| 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 "" | ||
|
|
There was a problem hiding this comment.
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.
| 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) | |
| @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)) |
There was a problem hiding this comment.
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.
- modify consutil, supporting oper state display Signed-off-by: cliffchen <t-cliffchen+github@microsoft.com>
3994817 to
80383da
Compare
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
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>
37f28c9 to
9669792
Compare
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Signed-off-by: cliffchen <t-cliffchen+github@microsoft.com>
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
…eric suffix Signed-off-by: cliffchen <t-cliffchen+github@microsoft.com>
1ddbc55 to
3e024c0
Compare
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Signed-off-by: Zhijian Li <zhijianli@microsoft.com>
0982842 to
21c2039
Compare
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
What I did
Add new modification and new commands to support console monitor feature. HLD
How I did it
show linecommands. Display 2 new column,Oper StateandState Duration.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)
New command output (if the output of a command-line utility has changed)