Skip to content

Commit

Permalink
fixed the output to pdf
Browse files Browse the repository at this point in the history
  • Loading branch information
boazhaim committed Nov 17, 2024
1 parent 95db1bb commit dd43c97
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 75 deletions.
42 changes: 18 additions & 24 deletions plugins/ufm_log_analyzer_plugin/src/loganalyze/log_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,35 +369,29 @@ def create_analyzer(parsed_args, full_extracted_logs_list,
)

used_ufm_version = console_log_analyzer.ufm_versions
text_to_show_in_pdf = f"Used ufm version in console log {used_ufm_version}"
fabric_info = "fabric info:" + os.linesep + str(ibdiagnet_analyzer.get_fabric_size()) \
if ibdiagnet_analyzer else "No Fabric Info found" # pylint: disable=invalid-name
if links_flapping_analyzer:
link_flapping = links_flapping_analyzer.get_link_flapping_last_week() \
if links_flapping_analyzer else "No link flapping info"
text_to_show_in_pdf += os.linesep + str(fabric_info) + os.linesep + \
"Link Flapping:" + os.linesep + str(link_flapping)

critical_events_burst = event_log_analyzer.get_critical_event_bursts()
critical_events_text = "The minute event_type event count" # pylint: disable=invalid-name
for critical_event in critical_events_burst:
timestamp = critical_event['timestamp']
event_type = critical_event['event_type']
event = critical_event['event']
counter = critical_event['count']
event_text = f"{timestamp} {event_type} {event} {counter}"
critical_events_text = critical_events_text + os.linesep + event_text

text_to_show_in_pdf += os.linesep + os.linesep + "More than 5 events burst over a minute:" \
+ os.linesep + critical_events_text
text_to_show_in_pdf = f"Used ufm version in console log {used_ufm_version}{os.linesep}"

pdf = PDFCreator(pdf_path, pdf_header, png_images, text_to_show_in_pdf)
# Adding telemetry stats to the PDF
dataframes_for_pdf = []
fabric_info = ibdiagnet_analyzer.get_fabric_size() if ibdiagnet_analyzer else "No Fabric Info found"
dataframes_for_pdf.append(("Fabric info", fabric_info))
if links_flapping_analyzer:
dataframes_for_pdf.append(("Link Flapping past week", links_flapping_analyzer.get_link_flapping_last_week()))
lists_to_add = []
critical_events_headers = ["timestamp", "event_type", "event", "count"]
lists_to_add.append((event_log_analyzer.get_critical_event_bursts(), "More than 5 events burst over a minute", critical_events_headers))

for cur_telemetry in \
[ibdianget_2_ports_primary_analyzer, ibdianget_2_ports_secondary_analyzer]:
text_to_show_in_pdf += cur_telemetry.text_to_show_in_pdf
dataframes_for_pdf.append((f"{cur_telemetry.telemetry_type} Telemetry iteration time", cur_telemetry.get_last_iterations_time_stats()))
dataframes_for_pdf.append((f"{cur_telemetry.telemetry_type} Telemetry iteration first and last timestamps", cur_telemetry.get_first_last_iteration_timestamp()))
dataframes_for_pdf.append((f"{cur_telemetry.telemetry_type} Telemetry fabric size", cur_telemetry.get_number_of_switches_and_ports()))
lists_to_add.append(([cur_telemetry.get_number_of_core_dumps()], f"{cur_telemetry.telemetry_type} number of core dumps found in the logs",["Amount"]))


# PDF creator gets all the images and to add to the report
pdf = PDFCreator(pdf_path, pdf_header, png_images, text_to_show_in_pdf)
pdf.created_pdf()
pdf.create_pdf(dataframes_for_pdf, lists_to_add)
# Generated a report that can be located in the destination
log.LOGGER.info("Analysis is done, please see the following outputs:")
for image, title in images_and_title_to_present:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ def __init__(self,
else:
self.telemetry_type = "Unknown_telemetry_type"

self._first_timestamp_of_logs = None
self._last_timestamp_of_logs = None

def get_collectx_versions(self):
unique_collectx_versions = self._log_data_sorted[\
self._log_data_sorted['type'] == 'collectx_version']['data'].unique()
Expand All @@ -64,7 +67,7 @@ def get_number_of_switches_and_ports(self):

columns_of_interest = ['data', 'extra2', 'extra4', 'extra135']
column_mapping = {
'data': 'Number of Switches',
'data': '# of Switches',
'extra2': 'CAs',
'extra4': 'Routers',
'extra135': 'Ports'
Expand Down Expand Up @@ -110,6 +113,8 @@ def analyze_iteration_time(self, threshold=0.15):
filtered_data = filtered_data[filtered_data['data'] >= threshold]
filtered_data['timestamp'] = pd.to_datetime(filtered_data['timestamp'], errors='coerce')
filtered_data = filtered_data.dropna(subset=['timestamp'])

filtered_data = filtered_data.sort_values(by='timestamp').reset_index(drop=True)

if not filtered_data['data'].empty:
average = filtered_data['data'].mean()
Expand All @@ -120,9 +125,13 @@ def analyze_iteration_time(self, threshold=0.15):
== max_value, 'timestamp'].iloc[0]
min_timestamp = filtered_data.loc[filtered_data['data'] \
== min_value, 'timestamp'].iloc[0]
first_timestamp = filtered_data['timestamp'].iloc[0]
last_timestamp = filtered_data['timestamp'].iloc[-1]

else:
average = max_value = min_value = 0.0
max_timestamp = min_timestamp = None
first_timestamp = last_timestamp = None

stats = {
'Average': average,
Expand All @@ -135,8 +144,19 @@ def analyze_iteration_time(self, threshold=0.15):
stats_df = pd.DataFrame([stats])
self._iteration_time_data = filtered_data
self._iteration_time_stats = stats_df
self._first_timestamp_of_logs = first_timestamp
self._last_timestamp_of_logs = last_timestamp
return stats_df

def get_first_last_iteration_timestamp(self):
if not self._first_timestamp_of_logs or not self._last_timestamp_of_logs:
self.analyze_iteration_time()
times ={
'first': str(self._first_timestamp_of_logs),
'last': str(self._last_timestamp_of_logs)
}
return pd.DataFrame([times])

def get_last_iterations_time_stats(self):
return self._iteration_time_stats

Expand All @@ -158,27 +178,4 @@ def plot_iteration_time_over_time(self):

def get_number_of_core_dumps(self):
core_dumps = self._log_data_sorted[self._log_data_sorted['type'] == 'timeout_dump_core']
return len(core_dumps)

def full_analysis(self):
txt_for_pdf = os.linesep + os.linesep
txt_for_pdf += f"{self.telemetry_type} info: {os.linesep}"
txt_for_pdf += f"Found the following collectx version(s):{os.linesep}"
for collectx_version in self.get_collectx_versions():
txt_for_pdf += f"{collectx_version}, "
txt_for_pdf += os.linesep
txt_for_pdf += f"Found {self.get_number_of_core_dumps()} core dumps{os.linesep}"
txt_for_pdf += str(self.get_number_of_switches_and_ports())
iteration_stats = self.get_last_iterations_time_stats()
if iteration_stats is None:
self.analyze_iteration_time()
iteration_stats = self.get_last_iterations_time_stats()
txt_for_pdf += f"Iteration time stats:{os.linesep}"
txt_for_pdf += str(iteration_stats)
self.text_to_show_in_pdf = txt_for_pdf
print(f"stats for {self.telemetry_type}:")
print(self.get_last_iterations_time_stats())
print(self.get_number_of_switches_and_ports())
print(f"Collectx versions {self.get_collectx_versions()}")

return super().full_analysis()
return {"Amount":len(core_dumps)}
104 changes: 79 additions & 25 deletions plugins/ufm_log_analyzer_plugin/src/loganalyze/pdf_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

import os
from io import StringIO
from fpdf import FPDF # Import FPDF from fpdf module
from fpdf import FPDF
from tabulate import tabulate # Import FPDF from fpdf module


class PDFCreator(FPDF):
Expand All @@ -36,45 +37,98 @@ def footer(self):
self.set_font("Arial", "I", 8)
self.cell(0, 10, f"Page {self.page_no()}", 0, 0, "C")

def created_pdf(self):
self.set_display_mode("fullpage")
self.add_page()

# Initial coordinates for images
def add_images(self):
"""Adds images to the PDF."""
x_start = 10
y_start = 20
image_width = 180
image_height = 100
spacing = 10

# Add each image
x = x_start
y = y_start
x, y = x_start, y_start
for image_path in self._images_path:
if os.path.exists(image_path):
self.image(
image_path, x=x, y=y, w=image_width, h=image_height, type="PNG"
)

# Update coordinates for the next image
self.image(image_path, x=x, y=y, w=image_width, h=image_height, type="PNG")
y += image_height + spacing

# Check if next image exceeds page height
if y > self.h - image_height - 20:
self.add_page() # Add a new page if needed
self.add_page()
y = y_start

# Add text on a new page
self.add_page() # Add a new page for the text
def add_text(self):
self.set_font("Arial", "", 12)

output = StringIO()
print(self._fabric_stats_list, file=output)
text = (
output.getvalue().strip()
)

text = output.getvalue().strip()
self.multi_cell(0, 10, text)

# Output PDF
def add_list_of_dicts_as_text(self, data_list, title=None, headers=None):
"""Adds a list of dictionaries to the PDF as aligned text."""
if not data_list or not isinstance(data_list, list):
return

# Add title if provided
if title:
self.set_font("Arial", "B", 12)
self.cell(0, 10, title, 0, 1, 'C')
self.ln(5)

# Set font size for the content
self.set_font("Arial", "", 10)

# Prepare data for tabulate
table_data = [[str(item.get(header, '')) for header in headers] for item in data_list]

# Convert the list of dictionaries to a formatted string using `tabulate`
table_str = tabulate(table_data, headers=headers, tablefmt='plain')

# Print the formatted text as plain text
self.multi_cell(0, 10, table_str)
self.ln(10)

def add_dataframe_as_text(self, data_frame, title=None):
"""Adds a DataFrame to the PDF as aligned text without row numbers."""
if data_frame is None or data_frame.empty:
return

# Add title if provided
if title:
self.set_font("Arial", "B", 12)
self.cell(0, 10, title, 0, 1, 'C')
self.ln(5)

# Adjust font size based on the number of columns
num_columns = len(data_frame.columns)
if num_columns > 10:
self.set_font("Arial", "", 8) # Smaller font for many columns
elif num_columns > 5:
self.set_font("Arial", "", 10)
else:
self.set_font("Arial", "", 12)

# Convert DataFrame to a formatted string using `tabulate` without row numbers
table_str = tabulate(data_frame.values, headers=data_frame.columns, tablefmt='plain')

# Print the formatted table as plain text
self.multi_cell(0, 10, table_str)
self.ln(10)

def create_pdf(self, data_frames_with_titles, lists_to_add):
"""Generates the PDF with images, text, and multiple tables."""
self.set_display_mode("fullpage")
self.add_page()

# Add images section
self.add_images()

# Add multiple DataFrames with titles
for title, df in data_frames_with_titles:
self.add_dataframe_as_text(data_frame=df, title=title)

for data_list, title, headers in lists_to_add:
self.add_list_of_dicts_as_text(data_list, title, headers)

# Output the final PDF
# Add text section
self.add_text()

self.output(self._pdf_path)
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ pandas
numpy
matplotlib
IPython
fpdf2
fpdf2
tabulate

0 comments on commit dd43c97

Please sign in to comment.