######################################################################## # # Copyright (c) 2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # ######################################################################## # # This file contains the Render function. # The Rendering tool visualizes the collect bundle and generates # an index.html file # ######################################################################## from datetime import datetime import os from pathlib import Path import re def exclude_path(): """Generate a set for files to be excluded """ exclude_file = Path(__file__).parent / 'render.exclude' exclude_paths = set() if exclude_file.exists(): with exclude_file.open('r') as file: exclude_paths = set(line.strip() for line in file) return exclude_paths def can_open_file(file_path): """Test if the file can be opened or not empty Parameters: file_path(Path): path of the file """ try: with open(file_path, 'r'): return os.path.getsize(file_path) != 0 except IOError: return False def extract_section(log_contents, start_phrase): """Extract the correlated or plugin content of the summary Parameters: log_contents (string): content of the log start_phrase (string): the name of the section extracted """ start = log_contents.find(start_phrase) if start == -1: return "" end = log_contents.find("\n\n", start) if end == -1: end = len(log_contents) return log_contents[start:end].strip() def remove_timestamp(text): """Remove timestamp of summary message Parameters: text (string): the summary message """ lines = text.split('\n') temp = [] for line in lines: split_string = line.split(' ', 1) # check if the first part is time format, then remove if it is if split_string[0] and datetime.fromisoformat(split_string[0]): temp.append(split_string[1]) else: temp.append(line) final_text = '\n'.join(temp) return final_text def remove_emptyinfo(text): """Remove 'INFO' text of summary message Parameters: text (string): the summary message """ lines = text.split('\n') temp = [] for line in lines: if line.strip() != 'INFO:': temp.append(line) final_text = '\n'.join(temp) return final_text def process_section(section, title): """Return text with timestamp and INFO: removed Parameters: section (string): the message of the correlated/plugins section title (string): correlated/plugin results """ section = section[len(title):] section = remove_timestamp(section) section = remove_emptyinfo(section) return section def classify_node(data): """Classify node type in system_info summary Parameters: data (string): the summary of system_info """ node_type = '' for item in data: if 'Node Type' in item: node_type = item.split(':')[-1].strip().lower() return node_type def controller_sort(x): """Sort the controller, place the controller-0 first Parameters: x (list): list of controller info """ return x[0] != 'controller-0' def html_css(): """Static css code of the rendering tool iframe, textarea: the content panel showing information #show-worker: the show more worker button .container-menu: the overall layout of the page .menu: the sidebar menu of the page #correlated-results-toggle, #plugin-results-toggle: +/- button for results menu """ html_content_css = """ Report Analysis """ return html_content_css def html_script(): """Static script code Functions: toggleContent: show content in System Info section toggleSub: show/hide submenus in correlated/plugin results toggleMenu: show the correlated/plugin summary showContentStorage: display content of selected storage item showContentWorker: display content of selected worker item showContentTwo: display content of result section toggleTree: show the collect bundle """ html_content_script = """ """ return html_content_script def html_info(sys_section): """System info part generation reads from plugin/system_info and show by different types order: controller, storage(if there exists), worker(if there exists) Parameters: sys_section (string): the summary of system_info """ controller_section = [] storage_section = [] worker_section = [] for i in sys_section: section_lines = i.strip().split("\n") section_type = classify_node(section_lines) if "controller" == section_type: controller_section.append(section_lines) if "storage" == section_type: storage_section.append(section_lines) if "worker" == section_type: worker_section.append(section_lines) controller_section = sorted(controller_section, key=controller_sort) controller_zero = controller_section.pop(0) sections = { "controller": controller_section, "storage": storage_section, "worker": worker_section } html_content_one = "" html_content_one += """
""" # controller-0 html_content_one += """
""" for i in controller_zero: html_content_one += f'{i}'.replace(' ', ' ') html_content_one += "
" html_content_one += "
" for section_type, section_list in sections.items(): for i, section in enumerate(section_list): if section_type == "controller": div_id = f"{section_type}-{i + 1}" else: div_id = f"{section_type}-{i}" html_content_one += f'" html_content_one += "

""" return html_content_one def html_result(log_contents, output_dir): """Result part generation in the menu-content style generates correlated results, plugin results, and the items under them subitems for plugins and correlated results under separate menus Parameters: log_contents (string): content of the summary output_dir (string): the location of output """ # Extract sections from the log plugin_section = extract_section(log_contents, 'Plugin Results:') correlated_section = extract_section(log_contents, 'Correlated Results:') # Process the extracted sections plugin_section = process_section(plugin_section, 'Plugin Results:') correlated_section = process_section(correlated_section, 'Correlated Results:') # HTML part correlated_directory = os.path.join(os.getcwd(), output_dir) os.chdir(correlated_directory) correlated_items = [] for file in os.listdir(correlated_directory): if os.path.isfile(file) and '.' not in file: correlated_items.append({'name': file, 'id': f'content-item-{file}'}) plugin_directory = os.path.join(correlated_directory, 'plugins') os.chdir(plugin_directory) plugin_items = [] for file in os.listdir(plugin_directory): if os.path.isfile(file) and file != "system_info": plugin_items.append({'name': file, 'id': f'content-item-{file}'}) html_content_two = "" html_content_two += """
" html_content_two += """
""" for item in correlated_items: html_content_two += f'

{item["name"].capitalize()}

' for item in plugin_items: html_content_two += f'

{item["name"].capitalize()}

' html_content_two += f'

Correlated Results

' html_content_two += f'

Plugin Results

' html_content_two += """
""" return html_content_two def generate_collect(): os.chdir('../../') current_directory = Path('.') finalstr = """
  • + Collect Bundle
  • " return finalstr def html_collect(): """Collect bundle code generation Calls a helper function to to generate the collect bundle """ current_directory = Path('.') tree_html = "" content_html = "
    " target_dir = current_directory.resolve() newtree_html, newcontent_html = generate_directory_tree(current_directory, exclude_path(), target_dir, 0) tree_html += newtree_html content_html += newcontent_html content_html += "
    " html_content_three = """
    " + content_html + "
    " return html_content_three def generate_directory_tree(directory_path, exclude_path, target_dir, is_top_level): """Helper function for Collect bundle generation Parameters: directory_path(Path): the path of the directory in each call target_dir(string): the path of the file/folder is_top_level(bool): if the level is the top level of the collect bundle """ directory_name = directory_path.name tree_html = "" content_html = "" approved_list = ['.log', '.conf', '.info', '.json', '.alarm', '.pid', '.list', '.lock', '.txt'] if is_top_level == 1: temp_name = re.sub(r'[^a-zA-Z0-9]', '', directory_name) tree_html = f'