Add rendering collect bundle files to report tool

This update causes report to automatically generate an index.html file
in the report_analysis folder.
Point browser to index.html to render the report_analysis graphically.
Both Correlated Results and Plugin Results have its sub-items.
Clicking items will show corresponding content in the content area.

Test Plan:
PASS: Verify the current report tool functions is not affected
PASS: Verify the index.html is generated in the collect bundle folder
PASS: Verify System Info can be read correctly
PASS: Verify users can add/remove items in left panel of System Info
PASS: Verify Correlated Results and Plugin Results are shown
PASS: Verify the subitems of the Results are clickable and shown
PASS: Verify the menus are expandable/collapsible
PASS: Verify vertical scrollbar is applied when overflow occurs
PASS: Verify system info for a selectable list of hosts are displayed
PASS: Verify handling of long hostnames
PASS: Verify handling of long list of hosts
PASS: Verify controller-0 is printed when loading
PASS: Verify correlated summary is printed when loading
PASS: Verify correlated summary is printed when 'Correlated Results'
      menu title is selected
PASS: Verify correlated results file contents are displayed by clicking
      on the 'Correlated Results' groups
PASS: Verify plugin results summary is printed when 'Plugin Results'
      menu title is selected
PASS: Verify selecting additional hosts adds to that list while
      deselecting them removes
PASS: Verify plugin results file contents can be displayed by clicking
      on the 'Plugin Results group.
PASS: Verify page is refreshed when System Information is clicked
PASS: Verify index.html is created for all report bundle pointer
      options ; -b , -d , -f
PASS: Verify index.html is created and included in the report analysis
      when report is run as part of collect
PASS: Verify the generated html and css content has no error in console

Story: 2010533
Task: 49191
Change-Id: I3303a9f0f138777f16be0ea34b1f633bdd394ee0
Signed-off-by: Lance Xu <lance.xu@windriver.com>
This commit is contained in:
Lance Xu 2023-12-05 11:38:42 -05:00
parent 944f847b43
commit 260d56ad9a
1 changed files with 126 additions and 16 deletions

View File

@ -6,17 +6,19 @@
#
########################################################################
#
# This file contains the Render function
# The Rendering tool visualizes the collect bundle and generates index.html file
# This file contains the Render function.
# The Rendering tool visualizes the collect bundle and generates
# an index.html file
#
########################################################################
from datetime import datetime
from pathlib import Path
import os
def extract_section(log_contents, start_phrase):
"""extract the correlated or plugin content of the summary
"""Extract the correlated or plugin content of the summary
Parameters:
log_contents (string): content of the log
@ -32,7 +34,7 @@ def extract_section(log_contents, start_phrase):
def remove_timestamp(text):
"""remove timestamp of summary message
"""Remove timestamp of summary message
Parameters:
text (string): the summary message
@ -51,7 +53,7 @@ def remove_timestamp(text):
def remove_emptyinfo(text):
""" remove 'INFO' text of summary message
"""Remove 'INFO' text of summary message
Parameters:
text (string): the summary message
@ -66,7 +68,7 @@ def remove_emptyinfo(text):
def process_section(section, title):
"""return text with timestamp and INFO: removed
"""Return text with timestamp and INFO: removed
Parameters:
section (string): the message of the correlated/plugins section
@ -79,7 +81,7 @@ def process_section(section, title):
def classify_node(data):
"""classify node type in system_info summary
"""Classify node type in system_info summary
Parameters:
data (string): the summary of system_info
@ -92,7 +94,7 @@ def classify_node(data):
def controller_sort(x):
"""sort the controller, place the controller-0 first
"""Sort the controller, place the controller-0 first
Parameters:
x (list): list of controller info
@ -101,7 +103,7 @@ def controller_sort(x):
def html_css():
"""static css code of the rendering tool
"""Static css code of the rendering tool
iframe, textarea: the content panel showing information
#show-worker: the show more worker button
@ -193,7 +195,7 @@ def html_css():
margin-top: 10px;
}}
.content-item, .content-itemtwo {{
.content-item, .content-itemtwo, .content-itemthree {{
display: none;
}}
@ -218,6 +220,31 @@ def html_css():
color: #2F4F4F;
}}
.caret {{
cursor: pointer;
user-select: none;
color: #00857e;
font-weight: bold;
}}
.caret::before {{
content: '+';
color: #2F4F4F;
margin-right: 6px;
}}
.caret-down::before {{
color: #2F4F4F;
content: '-';
}}
.text-color {{
color: #00ada4;
}}
.nested {{ display: none; }}
.active {{ display: block; }}
</style>
</head>
"""
@ -225,7 +252,7 @@ def html_css():
def html_script():
"""static script code
"""Static script code
Functions:
toggleContent: show content in System Info section
@ -234,6 +261,7 @@ def html_script():
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 = """
<script>
@ -323,6 +351,20 @@ def html_script():
}}
}}
function showContentThree(event, contentId) {{
event.preventDefault();
const contentItems = document.querySelectorAll('.content-itemthree');
contentItems.forEach(item => {{
item.style.display = 'none';
}});
const selectedContent = document.getElementById(contentId);
if (selectedContent) {{
selectedContent.style.display = 'block';
}}
}}
function toggleContent(option, menuItem) {{
const contentDiv = document.getElementById(option);
const icon = menuItem.querySelector('.icon');
@ -395,6 +437,28 @@ def html_script():
}}
}}
function toggleTree() {{
var toggler = document.getElementsByClassName('caret');
for (var i = 0; i < toggler.length; i++) {{
var nested = toggler[i].parentElement.querySelector('.nested');
var isEmpty = nested.querySelectorAll('li').length === 0;
if (!isEmpty) {{
toggler[i].addEventListener('click', function() {{
this.parentElement.querySelector('.nested').classList.toggle('active');
this.classList.toggle('caret-down');
this.parentElement.classList.toggle('text-color');
}});
}} else {{
toggler[i].style.color = '#808080';
}}
}}
}}
// Call the function when the page loads to initialize the tree behavior
toggleTree();
</script>
</html>
"""
@ -402,7 +466,7 @@ def html_script():
def html_info(sys_section):
"""system info part generation
"""System info part generation
reads from plugin/system_info and show by different types
order: controller, storage(if there exists), worker(if there exists)
@ -505,7 +569,7 @@ def html_info(sys_section):
def html_result(log_contents, output_dir):
"""result part generation in the menu-content style
"""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
@ -575,13 +639,58 @@ def html_result(log_contents, output_dir):
html_content_two += f'<div class="content-itemtwo" id="content-item-plugin_results"><h2>Plugin Results</h2><textarea>{plugin_section}</textarea></div>'
html_content_two += """
</div>
</div>
</body>
</div><br>
"""
return html_content_two
def html_collect(output_dir):
os.chdir('../../')
current_directory = Path('.')
tree_html = ""
content_html = "<div class='content'>"
target_dir = current_directory.resolve()
newtree_html, newcontent_html = generate_directory_tree(current_directory, target_dir, True)
tree_html += newtree_html
content_html += newcontent_html
content_html += "</div>"
html_content_three = """<div class="container-menu"><div class="menu">
""" + tree_html + "</div>" + content_html + "</div></body>"
return html_content_three
def generate_directory_tree(directory_path, target_dir, is_top_level=False):
directory_name = directory_path.name
tree_html = ""
content_html = ""
approved_list = ['.log', '.conf', '.info', '.json', '.alarm', '.pid', '.list', '.lock', '.txt']
if not is_top_level:
tree_html = f'<li><span class="caret">{directory_name}</span><ul class="nested">'
for item in directory_path.iterdir():
try:
if item.is_dir() and item.name != "report_analysis":
nested_tree_html, nested_content_html = generate_directory_tree(item, target_dir)
tree_html += nested_tree_html
content_html += nested_content_html
elif item.is_file():
if os.path.getsize(item) == 0:
tree_html += f'<li><a style="color: #808080">{item}</a></li>'
else:
if item.name.endswith(tuple(approved_list)):
tree_html += f'<li><a href="#" class="toggle-sign" onclick="showContentThree(event, \'{item.name}\')">{item.name}</a></li>'
content_html += f'<div class="content-itemthree" id="{item.name}"><h2>{item.name}</h2><iframe src="{target_dir}/{item}"></iframe></div>'
else:
if not item.name.endswith(".tgz"):
tree_html += f'<li><a href="../{item}" target="_blank">{item}</a></li>'
except PermissionError as e:
continue
if not is_top_level:
tree_html += '</ul></li>'
return tree_html, content_html
# main
def main(input_dir, output_dir):
reportlog_path = os.path.join(output_dir, 'report.log')
@ -596,8 +705,9 @@ def main(input_dir, output_dir):
html_file = os.path.abspath(os.path.join(output_dir, 'index.html'))
sys_section = sysinfo_contents.strip().split("\n\n")
html_content = html_css() + html_info(sys_section) + html_result(log_contents, output_dir) + html_script()
html_content = html_css() + html_info(sys_section) + html_result(log_contents, output_dir)
html_content = html_content.format()
html_content += html_collect(output_dir) + html_script()
# write the HTML content to file
with open(html_file, "w") as file: