Skip to content

Commit 42d0d11

Browse files
authored
Merge pull request #417 from Limmen/cli_command
ls command for CSLE logfiles is added to CLI
2 parents cde35a0 + 3d89854 commit 42d0d11

File tree

1 file changed

+124
-2
lines changed
  • simulation-system/libs/csle-cli/src/csle_cli

1 file changed

+124
-2
lines changed

simulation-system/libs/csle-cli/src/csle_cli/cli.py

+124-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import click
1010
import warnings
1111
from typing import Any
12+
import re
1213

1314
warnings.filterwarnings("ignore")
1415
from csle_common.dao.simulation_config.simulation_env_config import SimulationEnvConfig
@@ -3204,15 +3205,16 @@ def ls_shell_complete(ctx, param, incomplete) -> List[str]:
32043205
"| node_exporter | cadvisor | pgadmin | statsmanager | flask | "
32053206
"simulations | emulation_executions | cluster | nginx | postgresql | docker | hostmanagers | "
32063207
"clientmanager | snortmanagers | elkmanager | trafficmanagers | kafkamanager | "
3207-
"ossecmanagers | ryumanager | filebeats | metricbeats | heartbeats")
3208+
"ossecmanagers | ryumanager | filebeats | metricbeats | heartbeats | logfiles | logfile")
32083209
@click.argument('entity', default='all', type=str, shell_complete=ls_shell_complete)
32093210
@click.option('--all', is_flag=True, help='list all')
32103211
@click.option('--running', is_flag=True, help='list running only (default)')
32113212
@click.option('--stopped', is_flag=True, help='list stopped only')
32123213
@click.option('--ip', default="", type=str)
32133214
@click.option('--id', default=None, type=int)
32143215
@click.option('--name', default="", type=str)
3215-
def ls(entity: str, all: bool, running: bool, stopped: bool, ip: str, name: str, id: int) -> None:
3216+
@click.option('--logfile_name', default="", type=str, help='name of the logfile to to retrieve')
3217+
def ls(entity: str, all: bool, running: bool, stopped: bool, ip: str, name: str, id: int, logfile_name: str) -> None:
32163218
"""
32173219
Lists the set of containers, networks, images, or emulations, or all
32183220
@@ -3293,6 +3295,10 @@ def ls(entity: str, all: bool, running: bool, stopped: bool, ip: str, name: str,
32933295
list_heartbeats(ip=ip, emulation=name, ip_first_octet=id)
32943296
elif entity == "packetbeats":
32953297
list_packetbeats(ip=ip, emulation=name, ip_first_octet=id)
3298+
elif entity == "logfiles":
3299+
list_logfiles(ip=ip)
3300+
elif entity == "logfile":
3301+
list_logfile(ip=ip, logfile_name=logfile_name)
32963302
else:
32973303
container = get_running_container(name=entity)
32983304
if container is not None:
@@ -3327,6 +3333,122 @@ def ls(entity: str, all: bool, running: bool, stopped: bool, ip: str, name: str,
33273333
click.secho(f"entity: {entity} is not recognized", fg="red", bold=True)
33283334

33293335

3336+
def list_logfiles(ip: str) -> None:
3337+
"""
3338+
Utility function for listing logfiles
3339+
3340+
:param ip: the ip of the node to list logfiles
3341+
3342+
:return: None
3343+
"""
3344+
import csle_common.constants.constants as constants
3345+
from csle_common.metastore.metastore_facade import MetastoreFacade
3346+
config = MetastoreFacade.get_config(id=1)
3347+
for node in config.cluster_config.cluster_nodes:
3348+
if node.ip == ip or ip == "":
3349+
logfiles_info = ClusterController.get_csle_log_files(
3350+
ip=ip, port=constants.GRPC_SERVERS.CLUSTER_MANAGER_PORT)
3351+
click.secho('+' + '-' * 60 + '+', fg='white')
3352+
click.secho(f'|{"CSLE log files":^20}|{"Directory":^39}|', fg='white')
3353+
click.secho('+' + '=' * 60 + '+', fg='white')
3354+
for key, value in logfiles_info.items():
3355+
click.secho('|', nl=False, fg='white')
3356+
click.secho(f'{key:^20}', nl=False, fg='white')
3357+
click.secho('|', nl=False, fg='white')
3358+
click.secho(f'{value[0]:^39}', nl=False, fg='white')
3359+
click.secho('|', fg='white')
3360+
for dir_index in range(1, len(value)):
3361+
click.secho('|' + ' ' * 20 + '+', fg='white', nl=False)
3362+
click.secho('-' * 39 + '+', fg='white')
3363+
click.secho('|', nl=False, fg='white')
3364+
click.secho(f'{"":^20}', nl=False, fg='white')
3365+
click.secho('|', nl=False, fg='white')
3366+
click.secho(f'{value[dir_index]:^39}', nl=False, fg='white')
3367+
click.secho('|', fg='white')
3368+
click.secho('+' + '=' * 60 + '+', fg='white')
3369+
3370+
3371+
def style_log_line(line: str) -> str:
3372+
"""
3373+
Utility function for changing the color of lines and words in the logfiles when print them.
3374+
3375+
:param line: the line to be printed.
3376+
3377+
:return: the line with style we defined.
3378+
"""
3379+
3380+
date_pattern = r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}"
3381+
log_type_pattern = r"INFO|ERROR|WARNING|DEBUG"
3382+
file_path_pattern = r'(?<=File ")(.*?)(?=")'
3383+
line_number_pattern = r'(?<=line )\d+'
3384+
3385+
date_match = re.search(date_pattern, line)
3386+
if date_match:
3387+
date_str = click.style(date_match.group(), fg='yellow')
3388+
line = line.replace(date_match.group(), date_str)
3389+
3390+
log_type_match = re.search(log_type_pattern, line)
3391+
if log_type_match:
3392+
log_type = log_type_match.group()
3393+
color = 'green' if log_type == 'INFO' else 'red' if log_type == 'ERROR' else 'blue'
3394+
log_type_str = click.style(log_type, fg=color, bold=True)
3395+
line = line.replace(log_type, log_type_str)
3396+
3397+
file_path_match = re.search(file_path_pattern, line)
3398+
if file_path_match:
3399+
file_path_str = click.style(file_path_match.group(), fg='cyan', bold=False)
3400+
line = line.replace(file_path_match.group(), file_path_str)
3401+
3402+
line_number_match = re.search(line_number_pattern, line)
3403+
if line_number_match:
3404+
line_number_str = click.style(line_number_match.group(), fg='magenta', bold=False)
3405+
line = line.replace(line_number_match.group(), line_number_str)
3406+
3407+
return line
3408+
3409+
3410+
def list_logfile(ip: str, logfile_name: str) -> None:
3411+
"""
3412+
Utility function for listing logfiles
3413+
3414+
:param ip: the ip of the node to list logfiles
3415+
:param logfile_name: the file name to retrieve
3416+
3417+
:return: None
3418+
"""
3419+
import csle_common.constants.constants as constants
3420+
from csle_common.metastore.metastore_facade import MetastoreFacade
3421+
config = MetastoreFacade.get_config(id=1)
3422+
for node in config.cluster_config.cluster_nodes:
3423+
if node.ip == ip or ip == "":
3424+
logfile_info = ClusterController.get_log_file(
3425+
ip=ip, port=constants.GRPC_SERVERS.CLUSTER_MANAGER_PORT, log_file_name=logfile_name)
3426+
error_block = False
3427+
for key, value in logfile_info.items():
3428+
click.secho(key)
3429+
for i, line in enumerate(value):
3430+
styled_line = style_log_line(line)
3431+
3432+
if "ERROR" in line and not error_block:
3433+
click.secho("\n" + "=" * 80, fg='red', bold=True)
3434+
error_block = True
3435+
3436+
next_line_starts_log = \
3437+
(i + 1 < len(value) and re.match(
3438+
r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3} - (INFO|ERROR|WARNING|DEBUG)",
3439+
value[i + 1]))
3440+
if error_block and (next_line_starts_log or i == len(value) - 1):
3441+
click.secho(styled_line)
3442+
click.secho("=" * 80 + "\n", fg='red', bold=True)
3443+
error_block = False
3444+
else:
3445+
click.secho(styled_line)
3446+
3447+
if error_block:
3448+
click.secho("=" * 80 + "\n", fg='red', bold=True)
3449+
error_block = False
3450+
3451+
33303452
def list_filebeats(ip: str, emulation: str, ip_first_octet: int) -> None:
33313453
"""
33323454
Utility function for listing filebeats

0 commit comments

Comments
 (0)