Skip to content

Commit 18bed46

Browse files
authored
[console][show] Force refresh all lines status during show line (sonic-net#1641)
* [console][show] Force refresh all lines status during show line * Fix UTs Signed-off-by: Jing Kan jika@microsoft.com
1 parent b616cd9 commit 18bed46

File tree

3 files changed

+92
-32
lines changed

3 files changed

+92
-32
lines changed

consutil/lib.py

+37-18
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,17 @@ class ConsolePortProvider(object):
5454
The provider can let user to get console ports information.
5555
"""
5656

57-
def __init__(self, db, configured_only):
57+
def __init__(self, db, configured_only, refresh=False):
5858
self._db = db
59+
self._db_utils = DbUtils(db)
5960
self._configured_only = configured_only
6061
self._ports = []
61-
self._init_all()
62+
self._init_all(refresh)
6263

6364
def get_all(self):
6465
"""Gets all console ports information"""
6566
for port in self._ports:
66-
yield ConsolePortInfo(self._db, port)
67+
yield ConsolePortInfo(self._db_utils, port)
6768

6869
def get(self, target, use_device=False):
6970
"""Gets information of a ports, the target is the line number by default"""
@@ -75,21 +76,30 @@ def get(self, target, use_device=False):
7576
# identify the line number by searching configuration
7677
for port in self._ports:
7778
if search_key in port and port[search_key] == target:
78-
return ConsolePortInfo(self._db, port)
79+
return ConsolePortInfo(self._db_utils, port)
7980

8081
raise LineNotFoundError
8182

82-
def _init_all(self):
83+
def _init_all(self, refresh):
8384
config_db = self._db.cfgdb
8485
state_db = self._db.db
8586

8687
# Querying CONFIG_DB to get configured console ports
8788
keys = config_db.get_keys(CONSOLE_PORT_TABLE)
8889
ports = []
90+
if refresh:
91+
busy_lines = SysInfoProvider.list_active_console_processes()
8992
for k in keys:
9093
port = config_db.get_entry(CONSOLE_PORT_TABLE, k)
9194
port[LINE_KEY] = k
92-
port[CUR_STATE_KEY] = state_db.get_all(state_db.STATE_DB, "{}|{}".format(CONSOLE_PORT_TABLE, k))
95+
if refresh:
96+
if k in busy_lines:
97+
pid, date = busy_lines[k]
98+
port[CUR_STATE_KEY] = self._db_utils.update_state(k, BUSY_FLAG, pid, date)
99+
else:
100+
port[CUR_STATE_KEY] = self._db_utils.update_state(k, IDLE_FLAG)
101+
else:
102+
port[CUR_STATE_KEY] = state_db.get_all(state_db.STATE_DB, "{}|{}".format(CONSOLE_PORT_TABLE, k))
93103
ports.append(port)
94104

95105
# Querying device directory to get all available console ports
@@ -103,8 +113,8 @@ def _init_all(self):
103113
self._ports = ports
104114

105115
class ConsolePortInfo(object):
106-
def __init__(self, db, info):
107-
self._db = db
116+
def __init__(self, db_utils, info):
117+
self._db_utils = db_utils
108118
self._info = info
109119
self._session = None
110120

@@ -224,16 +234,8 @@ def refresh(self):
224234
self._update_state(IDLE_FLAG, "", "")
225235

226236
def _update_state(self, state, pid, date, line_num=None):
227-
state_db = self._db.db
228-
line_key = "{}|{}".format(CONSOLE_PORT_TABLE, self.line_num if line_num is None else line_num)
229-
state_db.set(state_db.STATE_DB, line_key, STATE_KEY, state)
230-
state_db.set(state_db.STATE_DB, line_key, PID_KEY, pid)
231-
state_db.set(state_db.STATE_DB, line_key, START_TIME_KEY, date)
232-
self._info[CUR_STATE_KEY] = {
233-
STATE_KEY: state,
234-
PID_KEY: pid,
235-
START_TIME_KEY: date
236-
}
237+
self._info[CUR_STATE_KEY] = self._db_utils.update_state(
238+
self.line_num if line_num is None else line_num, state, pid, date)
237239

238240
class ConsoleSession(object):
239241
"""
@@ -333,6 +335,23 @@ def run_command(cmd, abort=True):
333335
sys.exit(ERR_CMD)
334336
return output if abort else (output, error)
335337

338+
class DbUtils(object):
339+
def __init__(self, db):
340+
self._db = db
341+
self._config_db = db.cfgdb
342+
self._state_db = db.db
343+
344+
def update_state(self, line_num, state, pid="", date=""):
345+
key = "{}|{}".format(CONSOLE_PORT_TABLE, line_num)
346+
self._state_db.set(self._state_db.STATE_DB, key, STATE_KEY, state)
347+
self._state_db.set(self._state_db.STATE_DB, key, PID_KEY, pid)
348+
self._state_db.set(self._state_db.STATE_DB, key, START_TIME_KEY, date)
349+
return {
350+
STATE_KEY: state,
351+
PID_KEY: pid,
352+
START_TIME_KEY: date
353+
}
354+
336355
class InvalidConfigurationError(Exception):
337356
def __init__(self, config_key, message):
338357
self.config_key = config_key

consutil/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def consutil(db):
3434
@click.option('--brief', '-b', metavar='<brief_mode>', required=False, is_flag=True)
3535
def show(db, brief):
3636
"""Show all ports and their info include available ttyUSB devices unless specified brief mode"""
37-
port_provider = ConsolePortProvider(db, brief)
37+
port_provider = ConsolePortProvider(db, brief, refresh=True)
3838
ports = list(port_provider.get_all())
3939

4040
# sort ports for table rendering

tests/console_test.py

+54-13
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ def test_console_port_provider_get_line_by_device_not_found(self):
303303
def test_console_port_info_refresh_without_session(self):
304304
db = Db()
305305

306-
port = ConsolePortInfo(db, { "LINE" : "1" })
306+
port = ConsolePortInfo(DbUtils(db), { "LINE" : "1" })
307307
port.refresh()
308308
assert port.busy
309309
assert port.session_pid == "223"
@@ -313,15 +313,15 @@ def test_console_port_info_refresh_without_session(self):
313313
def test_console_port_info_refresh_without_session_idle(self):
314314
db = Db()
315315

316-
port = ConsolePortInfo(db, { "LINE" : "1" })
316+
port = ConsolePortInfo(DbUtils(db), { "LINE" : "1" })
317317
port.refresh()
318318
assert port.busy == False
319319

320320
@mock.patch('consutil.lib.SysInfoProvider.get_active_console_process_info', mock.MagicMock(return_value=("1", "223", "2020/11/2")))
321321
def test_console_port_info_refresh_with_session(self):
322322
db = Db()
323323

324-
port = ConsolePortInfo(db, { "LINE" : "1" })
324+
port = ConsolePortInfo(DbUtils(db), { "LINE" : "1" })
325325
port._session = ConsoleSession(port, mock.MagicMock(pid="223"))
326326
print(port)
327327

@@ -334,7 +334,7 @@ def test_console_port_info_refresh_with_session(self):
334334
def test_console_port_info_refresh_with_session_line_mismatch(self):
335335
db = Db()
336336

337-
port = ConsolePortInfo(db, { "LINE" : "1" })
337+
port = ConsolePortInfo(DbUtils(db), { "LINE" : "1" })
338338
port._session = ConsoleSession(port, mock.MagicMock(pid="223"))
339339
print(port)
340340

@@ -347,7 +347,7 @@ def test_console_port_info_refresh_with_session_line_mismatch(self):
347347
def test_console_port_info_refresh_with_session_process_ended(self):
348348
db = Db()
349349

350-
port = ConsolePortInfo(db, { "LINE" : "1" })
350+
port = ConsolePortInfo(DbUtils(db), { "LINE" : "1" })
351351
port._session = ConsoleSession(port, mock.MagicMock(pid="223"))
352352
print(port)
353353

@@ -356,23 +356,23 @@ def test_console_port_info_refresh_with_session_process_ended(self):
356356

357357
def test_console_port_info_connect_state_busy(self):
358358
db = Db()
359-
port = ConsolePortInfo(db, { "LINE" : "1", "CUR_STATE" : { "state" : "busy" } })
359+
port = ConsolePortInfo(DbUtils(db), { "LINE" : "1", "CUR_STATE" : { "state" : "busy" } })
360360

361361
port.refresh = mock.MagicMock(return_value=None)
362362
with pytest.raises(LineBusyError):
363363
port.connect()
364364

365365
def test_console_port_info_connect_invalid_config(self):
366366
db = Db()
367-
port = ConsolePortInfo(db, { "LINE" : "1", "CUR_STATE" : { "state" : "idle" } })
367+
port = ConsolePortInfo(DbUtils(db), { "LINE" : "1", "CUR_STATE" : { "state" : "idle" } })
368368

369369
port.refresh = mock.MagicMock(return_value=None)
370370
with pytest.raises(InvalidConfigurationError):
371371
port.connect()
372372

373373
def test_console_port_info_connect_device_busy(self):
374374
db = Db()
375-
port = ConsolePortInfo(db, { "LINE" : "1", "baud_rate" : "9600", "CUR_STATE" : { "state" : "idle" } })
375+
port = ConsolePortInfo(DbUtils(db), { "LINE" : "1", "baud_rate" : "9600", "CUR_STATE" : { "state" : "idle" } })
376376

377377
port.refresh = mock.MagicMock(return_value=None)
378378
mock_proc = mock.MagicMock(spec=subprocess.Popen)
@@ -384,7 +384,7 @@ def test_console_port_info_connect_device_busy(self):
384384

385385
def test_console_port_info_connect_connection_fail(self):
386386
db = Db()
387-
port = ConsolePortInfo(db, { "LINE" : "1", "baud_rate" : "9600", "CUR_STATE" : { "state" : "idle" } })
387+
port = ConsolePortInfo(DbUtils(db), { "LINE" : "1", "baud_rate" : "9600", "CUR_STATE" : { "state" : "idle" } })
388388

389389
port.refresh = mock.MagicMock(return_value=None)
390390
mock_proc = mock.MagicMock(spec=subprocess.Popen)
@@ -396,7 +396,7 @@ def test_console_port_info_connect_connection_fail(self):
396396

397397
def test_console_port_info_connect_success(self):
398398
db = Db()
399-
port = ConsolePortInfo(db, { "LINE" : "1", "baud_rate" : "9600", "CUR_STATE" : { "state" : "idle" } })
399+
port = ConsolePortInfo(DbUtils(db), { "LINE" : "1", "baud_rate" : "9600", "CUR_STATE" : { "state" : "idle" } })
400400

401401
port.refresh = mock.MagicMock(return_value=None)
402402
mock_proc = mock.MagicMock(spec=subprocess.Popen, pid="223")
@@ -409,22 +409,22 @@ def test_console_port_info_connect_success(self):
409409

410410
def test_console_port_info_clear_session_line_not_busy(self):
411411
db = Db()
412-
port = ConsolePortInfo(db, { "LINE" : "1", "baud_rate" : "9600", "CUR_STATE" : { "state" : "idle" } })
412+
port = ConsolePortInfo(DbUtils(db), { "LINE" : "1", "baud_rate" : "9600", "CUR_STATE" : { "state" : "idle" } })
413413

414414
port.refresh = mock.MagicMock(return_value=None)
415415
assert not port.clear_session()
416416

417417
@mock.patch('consutil.lib.SysInfoProvider.run_command', mock.MagicMock(return_value=None))
418418
def test_console_port_info_clear_session_with_state_db(self):
419419
db = Db()
420-
port = ConsolePortInfo(db, { "LINE" : "1", "baud_rate" : "9600", "CUR_STATE" : { "state" : "busy", "pid" : "223" } })
420+
port = ConsolePortInfo(DbUtils(db), { "LINE" : "1", "baud_rate" : "9600", "CUR_STATE" : { "state" : "busy", "pid" : "223" } })
421421

422422
port.refresh = mock.MagicMock(return_value=None)
423423
assert port.clear_session()
424424

425425
def test_console_port_info_clear_session_with_existing_session(self):
426426
db = Db()
427-
port = ConsolePortInfo(db, { "LINE" : "1", "baud_rate" : "9600", "CUR_STATE" : { "state" : "busy" } })
427+
port = ConsolePortInfo(DbUtils(db), { "LINE" : "1", "baud_rate" : "9600", "CUR_STATE" : { "state" : "busy" } })
428428
port._session = ConsoleSession(port, None)
429429
port._session.close = mock.MagicMock(return_value=None)
430430
port.refresh = mock.MagicMock(return_value=None)
@@ -538,6 +538,7 @@ def setup_class(cls):
538538
3 9600 Enabled - -
539539
"""
540540
@mock.patch('consutil.lib.SysInfoProvider.init_device_prefix', mock.MagicMock(return_value=None))
541+
@mock.patch('consutil.lib.SysInfoProvider.list_active_console_processes', mock.MagicMock(return_value={ "2" : ("223", "Wed Mar 6 08:31:35 2019")}))
541542
def test_show(self):
542543
runner = CliRunner()
543544
db = Db()
@@ -556,6 +557,46 @@ def test_show(self):
556557
assert result.exit_code == 0
557558
assert result.output == TestConsutilShow.expect_show_output
558559

560+
@mock.patch('consutil.lib.SysInfoProvider.init_device_prefix', mock.MagicMock(return_value=None))
561+
@mock.patch('consutil.lib.SysInfoProvider.list_active_console_processes', mock.MagicMock(return_value={ "2" : ("223", "Wed Mar 6 08:31:35 2019")}))
562+
def test_show_stale_idle_to_busy(self):
563+
runner = CliRunner()
564+
db = Db()
565+
db.cfgdb.set_entry("CONSOLE_PORT", 1, { "remote_device" : "switch1", "baud_rate" : "9600" })
566+
db.cfgdb.set_entry("CONSOLE_PORT", 2, { "remote_device" : "switch2", "baud_rate" : "9600" })
567+
db.cfgdb.set_entry("CONSOLE_PORT", 3, { "baud_rate" : "9600", "flow_control" : "1" })
568+
569+
# use '--brief' option to avoid access system
570+
result = runner.invoke(consutil.consutil.commands["show"], ['--brief'], obj=db)
571+
print(result.exit_code)
572+
print(sys.stderr, result.output)
573+
assert result.exit_code == 0
574+
assert result.output == TestConsutilShow.expect_show_output
575+
576+
@mock.patch('consutil.lib.SysInfoProvider.init_device_prefix', mock.MagicMock(return_value=None))
577+
@mock.patch('consutil.lib.SysInfoProvider.list_active_console_processes', mock.MagicMock(return_value={ "2" : ("223", "Wed Mar 6 08:31:35 2019")}))
578+
def test_show_stale_busy_to_idle(self):
579+
runner = CliRunner()
580+
db = Db()
581+
db.cfgdb.set_entry("CONSOLE_PORT", 1, { "remote_device" : "switch1", "baud_rate" : "9600" })
582+
db.cfgdb.set_entry("CONSOLE_PORT", 2, { "remote_device" : "switch2", "baud_rate" : "9600" })
583+
db.cfgdb.set_entry("CONSOLE_PORT", 3, { "baud_rate" : "9600", "flow_control" : "1" })
584+
585+
db.db.set(db.db.STATE_DB, "CONSOLE_PORT|1", "state", "busy")
586+
db.db.set(db.db.STATE_DB, "CONSOLE_PORT|1", "pid", "222")
587+
db.db.set(db.db.STATE_DB, "CONSOLE_PORT|1", "start_time", "Wed Mar 6 08:31:35 2019")
588+
589+
db.db.set(db.db.STATE_DB, "CONSOLE_PORT|2", "state", "busy")
590+
db.db.set(db.db.STATE_DB, "CONSOLE_PORT|2", "pid", "223")
591+
db.db.set(db.db.STATE_DB, "CONSOLE_PORT|2", "start_time", "Wed Mar 6 08:31:35 2019")
592+
593+
# use '--brief' option to avoid access system
594+
result = runner.invoke(consutil.consutil.commands["show"], ['--brief'], obj=db)
595+
print(result.exit_code)
596+
print(sys.stderr, result.output)
597+
assert result.exit_code == 0
598+
assert result.output == TestConsutilShow.expect_show_output
599+
559600
class TestConsutilConnect(object):
560601
@classmethod
561602
def setup_class(cls):

0 commit comments

Comments
 (0)