Skip to content

Commit 299cfa4

Browse files
authored
[action] [PR:4266] [console] add config console escape (#320)
#### What I did Add escape char config to console feature. In CONFIG_DB, an optional new entry under CONSOLE_SWITCH, "console_mgmt", is added, "default_escape_char" as the key and we expect a lower case letter as value. Similarly, an optional new entry under CONSOLE_PORT, and then each line, "escape_char" as the key and we also expect a lower case letter as value. Commands are: 1. config console default_escape (upper and lower case letters, or clear), e.g. config console default_escape k, config console default_escape B, config console default_escape clear. 2. config console escape [LINE_NUMBER] (upper and lower case letters, or clear), e.g. config console escape 6 C, config console escape 5 clear 3. show line --show-escape, e.g. show line --show-escape, show line -s -b Connect command work as usual, except that it will add --escape <escape_char> in the underlying picocom command if it is configured. Additionally, 1. some style change to old code to pass pre-commit check 2. unit tests for new functions 3. enhancement to old unit tests and its bug fix #### How I did it Implement them #### How to verify it Run the commands in dut. Signed-off-by: Sonic Build Admin <sonicbld@microsoft.com>
1 parent e487dad commit 299cfa4

5 files changed

Lines changed: 854 additions & 111 deletions

File tree

config/console.py

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import click
2+
import string
23
import utilities_common.cli as clicommon
34
from .validated_config_db_connector import ValidatedConfigDBConnector
45
from jsonpatch import JsonPatchConflict
6+
7+
58
#
69
# 'console' group ('config console ...')
710
#
@@ -10,6 +13,7 @@ def console():
1013
"""Console-related configuration tasks"""
1114
pass
1215

16+
1317
#
1418
# 'console enable' group ('config console enable')
1519
#
@@ -30,6 +34,7 @@ def enable_console_switch(db):
3034
ctx = click.get_current_context()
3135
ctx.fail("Invalid ConfigDB. Error: {}".format(e))
3236

37+
3338
#
3439
# 'console disable' group ('config console disable')
3540
#
@@ -50,6 +55,39 @@ def disable_console_switch(db):
5055
ctx = click.get_current_context()
5156
ctx.fail("Invalid ConfigDB. Error: {}".format(e))
5257

58+
59+
#
60+
# 'console default_escape' group ('config console default_escape A|B|...')
61+
#
62+
@console.command('default_escape')
63+
@clicommon.pass_db
64+
@click.argument('escape', metavar='<escape_char|clear>', required=True,
65+
type=click.Choice(list(string.ascii_letters) + ["clear"], case_sensitive=True))
66+
def set_console_default_escape_char(db, escape):
67+
"""Set console escape character or clear the existing one"""
68+
config_db = ValidatedConfigDBConnector(db.cfgdb)
69+
70+
table = "CONSOLE_SWITCH"
71+
dataKey1 = 'console_mgmt'
72+
dataKey2 = 'default_escape_char'
73+
74+
existing_entry = config_db.get_entry(table, dataKey1) or {}
75+
if escape == "clear":
76+
# Remove the default_escape_char field while preserving other keys (e.g., 'enabled')
77+
if dataKey2 in existing_entry:
78+
del existing_entry[dataKey2]
79+
data = existing_entry
80+
else:
81+
existing_entry[dataKey2] = escape.lower()
82+
data = existing_entry
83+
84+
try:
85+
config_db.set_entry(table, dataKey1, data)
86+
except ValueError as e:
87+
ctx = click.get_current_context()
88+
ctx.fail("Invalid ConfigDB. Error: {}".format(e))
89+
90+
5391
#
5492
# 'console add' group ('config console add ...')
5593
#
@@ -59,14 +97,17 @@ def disable_console_switch(db):
5997
@click.option('--baud', '-b', metavar='<baud>', required=True, type=click.INT)
6098
@click.option('--flowcontrol', '-f', metavar='<flow_control>', required=False, is_flag=True)
6199
@click.option('--devicename', '-d', metavar='<device_name>', required=False)
62-
def add_console_setting(db, linenum, baud, flowcontrol, devicename):
100+
@click.option('--escape', '-e', metavar='<escape_char>', required=False,
101+
type=click.Choice(list(string.ascii_letters), case_sensitive=True))
102+
def add_console_setting(db, linenum, baud, flowcontrol, devicename, escape):
63103
"""Add Console-realted configuration tasks"""
64104
config_db = ValidatedConfigDBConnector(db.cfgdb)
65105

66106
table = "CONSOLE_PORT"
67107
dataKey1 = 'baud_rate'
68108
dataKey2 = 'flow_control'
69109
dataKey3 = 'remote_device'
110+
dataKey4 = 'escape_char'
70111

71112
ctx = click.get_current_context()
72113
data = config_db.get_entry(table, linenum)
@@ -81,6 +122,9 @@ def add_console_setting(db, linenum, baud, flowcontrol, devicename):
81122
ctx.fail("Given device name {} has been used. Please enter a valid device name or remove the existing one !!".format(devicename))
82123
console_entry[dataKey3] = devicename
83124

125+
if escape:
126+
console_entry[dataKey4] = escape.lower()
127+
84128
try:
85129
config_db.set_entry(table, linenum, console_entry)
86130
except ValueError as e:
@@ -109,14 +153,15 @@ def remove_console_setting(db, linenum):
109153
else:
110154
ctx.fail("Trying to delete console port setting, which is not present.")
111155

156+
112157
#
113158
# 'console remote_device' group ('config console remote_device ...')
114159
#
115160
@console.command('remote_device')
116161
@clicommon.pass_db
117162
@click.argument('linenum', metavar='<line_number>', required=True, type=click.IntRange(0, 65535))
118163
@click.argument('devicename', metavar='<device_name>', required=False)
119-
def upate_console_remote_device_name(db, linenum, devicename):
164+
def update_console_remote_device_name(db, linenum, devicename):
120165
"""Update remote device name for a console line"""
121166
config_db = ValidatedConfigDBConnector(db.cfgdb)
122167
ctx = click.get_current_context()
@@ -131,11 +176,12 @@ def upate_console_remote_device_name(db, linenum, devicename):
131176
return
132177
elif not devicename:
133178
# remove configuration key from console setting if user not give a remote device name
134-
data.pop(dataKey, None)
135-
try:
136-
config_db.mod_entry(table, linenum, data)
137-
except ValueError as e:
138-
ctx.fail("Invalid ConfigDB. Error: {}".format(e))
179+
if dataKey in data:
180+
del data[dataKey]
181+
try:
182+
config_db.set_entry(table, linenum, data)
183+
except ValueError as e:
184+
ctx.fail("Invalid ConfigDB. Error: {}".format(e))
139185
elif isExistingSameDevice(config_db, devicename, table):
140186
ctx.fail("Given device name {} has been used. Please enter a valid device name or remove the existing one !!".format(devicename))
141187
else:
@@ -147,6 +193,7 @@ def upate_console_remote_device_name(db, linenum, devicename):
147193
else:
148194
ctx.fail("Trying to update console port setting, which is not present.")
149195

196+
150197
#
151198
# 'console baud' group ('config console baud ...')
152199
#
@@ -177,6 +224,7 @@ def update_console_baud(db, linenum, baud):
177224
else:
178225
ctx.fail("Trying to update console port setting, which is not present.")
179226

227+
180228
#
181229
# 'console flow_control' group ('config console flow_control ...')
182230
#
@@ -208,6 +256,39 @@ def update_console_flow_control(db, mode, linenum):
208256
else:
209257
ctx.fail("Trying to update console port setting, which is not present.")
210258

259+
260+
#
261+
# 'console escape' group ('config console escape ...')
262+
#
263+
@console.command('escape')
264+
@clicommon.pass_db
265+
@click.argument('linenum', metavar='<line_number>', required=True, type=click.IntRange(0, 65535))
266+
@click.argument('escape', metavar='<escape_char|clear>', required=True,
267+
type=click.Choice(list(string.ascii_letters) + ["clear"], case_sensitive=True))
268+
def update_console_escape_char(db, linenum, escape):
269+
"""Update escape character for a console line"""
270+
config_db = ValidatedConfigDBConnector(db.cfgdb)
271+
ctx = click.get_current_context()
272+
273+
table = "CONSOLE_PORT"
274+
dataKey = 'escape_char'
275+
276+
data = config_db.get_entry(table, linenum)
277+
if data:
278+
if escape == "clear":
279+
if dataKey in data:
280+
del data[dataKey]
281+
else:
282+
data[dataKey] = escape.lower()
283+
284+
try:
285+
config_db.set_entry(table, linenum, data)
286+
except ValueError as e:
287+
ctx.fail("Invalid ConfigDB. Error: {}".format(e))
288+
else:
289+
ctx.fail("Trying to update console port setting, which is not present.")
290+
291+
211292
def isExistingSameDevice(config_db, deviceName, table):
212293
"""Check if the given device name is conflict with existing device"""
213294
settings = config_db.get_table(table)

consutil/lib.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
FLOW_KEY = "flow_control"
3333
FEATURE_KEY = "console_mgmt"
3434
FEATURE_ENABLED_KEY = "enabled"
35+
DEFAULT_FEATURE_ESCAPE_KEY = "default_escape_char"
36+
FEATURE_ESCAPE_KEY = "escape_char"
3537

3638
# STATE_DB Keys
3739
STATE_KEY = "state"
@@ -65,7 +67,7 @@ def __init__(self, db, configured_only, refresh=False):
6567
def get_all(self):
6668
"""Gets all console ports information"""
6769
for port in self._ports:
68-
yield ConsolePortInfo(self._db_utils, port)
70+
yield ConsolePortInfo(self._db_utils, port, self._default_escape_char)
6971

7072
def get(self, target, use_device=False):
7173
"""Gets information of a ports, the target is the line number by default"""
@@ -77,14 +79,29 @@ def get(self, target, use_device=False):
7779
# identify the line number by searching configuration
7880
for port in self._ports:
7981
if search_key in port and port[search_key] == target:
80-
return ConsolePortInfo(self._db_utils, port)
82+
return ConsolePortInfo(self._db_utils, port, self._default_escape_char)
8183

8284
raise LineNotFoundError
8385

8486
def _init_all(self, refresh):
8587
config_db = self._db.cfgdb
8688
state_db = self._db.db
8789

90+
# Querying CONFIG_DB to get console management feature state
91+
feature_state = config_db.get_entry(CONSOLE_SWITCH_TABLE, FEATURE_KEY)
92+
# Default to no escape character when console management feature is disabled or missing.
93+
self._default_escape_char = None
94+
if feature_state and feature_state.get(FEATURE_ENABLED_KEY, "no") == "yes":
95+
self._default_escape_char = feature_state.get(
96+
DEFAULT_FEATURE_ESCAPE_KEY,
97+
feature_state.get(DEFAULT_FEATURE_ESCAPE_KEY, None),
98+
)
99+
if self._default_escape_char is not None and not self._default_escape_char.islower():
100+
raise InvalidConfigurationError(
101+
DEFAULT_FEATURE_ESCAPE_KEY,
102+
"default console escape character is not valid",
103+
)
104+
88105
# Querying CONFIG_DB to get configured console ports
89106
keys = config_db.get_keys(CONSOLE_PORT_TABLE)
90107
ports = []
@@ -114,11 +131,12 @@ def _init_all(self, refresh):
114131
self._ports = ports
115132

116133
class ConsolePortInfo(object):
117-
def __init__(self, db_utils, info):
134+
def __init__(self, db_utils, info, default_escape_char=None):
118135
self._db_utils = db_utils
119136
self._info = info
120137
self._session = None
121-
138+
self._default_escape_char = default_escape_char
139+
122140
def __str__(self):
123141
return "({}, {}, {})".format(self.line_num, self.baud, self.remote_device)
124142

@@ -137,7 +155,19 @@ def flow_control(self):
137155
@property
138156
def remote_device(self):
139157
return self._info[DEVICE_KEY] if DEVICE_KEY in self._info else None
140-
158+
159+
@property
160+
def default_escape_char(self):
161+
return self._default_escape_char
162+
163+
@property
164+
def line_escape_char(self):
165+
return self._info.get(FEATURE_ESCAPE_KEY, None)
166+
167+
@property
168+
def escape_char(self):
169+
return self._info.get(FEATURE_ESCAPE_KEY, self._default_escape_char)
170+
141171
@property
142172
def busy(self):
143173
return STATE_KEY in self.cur_state and self.cur_state[STATE_KEY] == BUSY_FLAG
@@ -170,7 +200,9 @@ def connect(self):
170200

171201
# build and start picocom command
172202
flow_cmd = "h" if self.flow_control else "n"
173-
cmd = "picocom -b {} -f {} {}{}".format(self.baud, flow_cmd, SysInfoProvider.DEVICE_PREFIX, self.line_num)
203+
escape_cmd = "-e {}".format(self.escape_char) if self.escape_char else ""
204+
cmd = "picocom {} -b {} -f {} {}{}".format(escape_cmd, self.baud, flow_cmd,
205+
SysInfoProvider.DEVICE_PREFIX, self.line_num)
174206

175207
# start connection
176208
try:
@@ -207,7 +239,7 @@ def clear_session(self):
207239
finally:
208240
self.refresh()
209241
self._session = None
210-
242+
211243
return True
212244

213245
def refresh(self):

0 commit comments

Comments
 (0)