Skip to content

Commit 694ed4b

Browse files
authored
Update howto-convert-cable-validation-html.md
add sample cable validation report
1 parent 6ec62d0 commit 694ed4b

File tree

1 file changed

+84
-95
lines changed

1 file changed

+84
-95
lines changed

articles/operator-nexus/howto-convert-cable-validation-html.md

Lines changed: 84 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ This article explains how to convert a Nexus Network Fabric Cable Validation Rep
1515

1616
## Prerequisites
1717

18-
- Requires Modules: `json pandas as pd`
18+
- Requires Modules: `json pandas as pd datetime`
1919

2020
## Python Script for Cable Validation JSON to html conversion
2121

@@ -24,32 +24,48 @@ import json
2424
import pandas as pd
2525
from datetime import datetime
2626
27+
def color_status(val):
28+
"""
29+
Takes a scalar and returns a string with
30+
the css property `'color: green'` for compliant,
31+
`'color: red'` for noncompliant and black for others
32+
"""
33+
if val == 'Compliant':
34+
color = 'green'
35+
elif val == 'NonCompliant':
36+
color = 'red'
37+
else:
38+
color = 'black'
39+
return 'color: %s' % color
40+
2741
now = datetime.now() # current date and time
2842
date_time = now.strftime("%m-%d-%Y-%H-%M")
2943
print("date and time:",date_time)
3044
31-
3245
# Get the file name as input from the user
3346
file_name = input("Please provide post validation json file: ")
3447
# Load the JSON data from the file
3548
with open(file_name, 'r') as f:
3649
data = json.load(f)
3750
38-
# Prepare a list to store the data
39-
data_list = []
51+
# Prepare two lists to store the data
52+
cable_validation_data = []
53+
cable_specification_validation_data = []
4054
4155
# Loop through each rack in the racks list
4256
for rack in data['racks']:
43-
# Loop through each device in the networkDevices list
57+
# Loop through each device in the networkConfiguration list
4458
for device in rack['rackInfo']['networkConfiguration']['networkDevices']:
4559
# Loop through each interface map for the device
4660
for interface_map in device['fixedInterfaceMaps']:
4761
# Loop through each validation result for the interface map
4862
for validation_result in interface_map['validationResult']:
49-
# Append the data to the list
63+
# Append the data to the list based on validation type
5064
temp_item = [device['name'], interface_map['name'], validation_result['status'], interface_map['destinationHostname'], interface_map['destinationPort'],validation_result['validationDetails']['deviceConfiguration'], validation_result['validationDetails']['error'] , validation_result['validationDetails']['reason'],'FixedInterface']
51-
# print(temp_item)
52-
data_list.append(temp_item)
65+
if validation_result['validationType'] == 'CableValidation':
66+
cable_validation_data.append(temp_item)
67+
elif validation_result['validationType'] == 'CableSpecificationValidation':
68+
cable_specification_validation_data.append(temp_item)
5369
5470
# Check if scaleSpecificInterfaceMaps is not None
5571
if device['scaleSpecificInterfaceMaps'] is not None:
@@ -61,98 +77,71 @@ for rack in data['racks']:
6177
for validation_result in interfacemaps['validationResult']:
6278
# Append the data to the list
6379
temp_item = [device['name'], interfacemaps['name'], validation_result['status'], interfacemaps['destinationHostname'], interfacemaps['destinationPort'], validation_result['validationDetails']['deviceConfiguration'], validation_result['validationDetails']['error'] , validation_result['validationDetails']['reason'], 'ScaleSpecificInterface']
64-
# print(temp_item)
65-
data_list.append(temp_item)
66-
67-
# # Check if scaleSpecificInterfaceMaps is not None
68-
# if device['topologySpecificInterfaceMaps'] is not None:
69-
# # Loop through each scaleSpecificInterface_Map for the interface map
70-
# for topo_map in device['topologySpecificInterfaceMaps']:
71-
# # Loop through each interface map for the device.
72-
# for interfacemaps in topo_map['InterfaceMaps']:
73-
# # Loop through each validation result for the scaleSpecificInterface_Map
74-
# for validation_result in interfacemaps['validationResult']:
75-
# # Append the data to the list
76-
# data_list.append([device['name'], interfacemaps['name'], validation_result['status'], interface_map['destinationHostname'], interface_map['destinationPort'], validation_result['validationDetails']['deviceConfiguration'], validation_result['validationDetails']['error'] , validation_result['validationDetails']['reason'], 'TopologySpecificInterface'])
77-
78-
# Convert the list to a DataFrame
79-
df = pd.DataFrame(data_list, columns=['Device Name', 'Interface Map Name', 'Validation Result Status', 'Destination Hostname ', 'Destination Port ','Device Configuration', 'Error', 'Reason','Map Type'])
80-
81-
# Function to apply color based on validation result
82-
def color_status(val):
83-
color = 'red' if val == 'NonCompliant' else 'green' if val == 'Compliant' else 'black'
84-
return 'color: %s' % color
85-
86-
# Apply the color to the DataFrame
87-
styled_df = df.style.applymap(color_status, subset=['Validation Result Status'])
88-
89-
# Set CSS properties for th elements in dataframe
90-
th_props = [
91-
('font-size', '18px'),
92-
('text-align', 'center'),
93-
('font-weight', 'bold'),
94-
('color', '#6d6d6d'),
95-
('background-color', '#f7f7f9')
96-
]
97-
98-
# Set CSS properties for td elements in dataframe
99-
td_props = [
100-
('font-size', '16px')
101-
]
102-
103-
# Set table styles
104-
styles = [
105-
dict(selector="th", props=th_props),
106-
dict(selector="td", props=td_props)
107-
]
108-
109-
# Filter the DataFrame based on the 'Validation Result Status' column
110-
df_compliant = df[df['Validation Result Status'] == 'Compliant']
111-
df_noncompliant = df[df['Validation Result Status'] == 'NonCompliant']
112-
df_unknown = df[df['Validation Result Status'] == 'Unknown']
113-
114-
# Apply the color to the DataFrames
115-
styled_df_compliant = df_compliant.style.applymap(color_status, subset=['Validation Result Status'])
116-
styled_df_noncompliant = df_noncompliant.style.applymap(color_status, subset=['Validation Result Status'])
117-
styled_df_unknown = df_unknown.style.applymap(color_status, subset=['Validation Result Status'])
118-
119-
# Generate the DataFrames' HTML strings
120-
df_html_compliant = styled_df_compliant.set_table_styles(styles).to_html()
121-
df_html_noncompliant = styled_df_noncompliant.set_table_styles(styles).to_html()
122-
df_html_unknown = styled_df_unknown.set_table_styles(styles).to_html()
123-
124-
# Combine the HTML strings
125-
html = f"""
80+
if validation_result['validationType'] == 'CableValidation':
81+
cable_validation_data.append(temp_item)
82+
elif validation_result['validationType'] == 'CableSpecificationValidation':
83+
cable_specification_validation_data.append(temp_item)
84+
85+
# Convert the lists to DataFrames
86+
cable_validation_df = pd.DataFrame(cable_validation_data, columns=['Device Name', 'Interface Map Name', 'Status', 'Destination Hostname', 'Destination Port', 'Device Configuration', 'Error', 'Reason', 'Interface Type'])
87+
cable_specification_validation_df = pd.DataFrame(cable_specification_validation_data, columns=['Device Name', 'Interface Map Name', 'Status', 'Destination Hostname', 'Destination Port', 'Device Configuration', 'Error', 'Reason', 'Interface Type'])
88+
89+
# Group the DataFrames by 'Status' and append each group's HTML representation to a string
90+
#html_string = '<html><head><style>table {border-collapse: collapse;} th, td {border: 1px solid black; padding: 5px;}</style></head><body>'
91+
html_string = """
12692
<html>
12793
<head>
12894
<style>
129-
body {{
130-
background-color: #f0f0f5;
131-
}}
132-
.box {{
133-
border: 1px solid black;
134-
margin: 10px;
135-
padding: 10px;
136-
}}
95+
body {
96+
font-family: Arial, sans-serif;
97+
max-width: 960px; /* Set the maximum width of the page */
98+
margin: 0 auto; /* Center the page */
99+
}
100+
h2 {color: #2A2A2A;}
101+
table {border-collapse: collapse; width: 100%;}
102+
th, td {border: 1px solid #ddd; padding: 8px;}
103+
tr:nth-child(even) {background-color: #f2f2f2;}
104+
th {padding-top: 12px; padding-bottom: 12px; text-align: left; background-color: #4CAF50; color: white;}
137105
</style>
138106
</head>
139107
<body>
140-
<div class="box">
141-
<h2>Compliant</h2>
142-
{df_html_compliant}
143-
</div>
144-
<div class="box">
145-
<h2>NonCompliant</h2>
146-
{df_html_noncompliant}
147-
</div>
148-
<div class="box">
149-
<h2>Unknown</h2>
150-
{df_html_unknown}
151-
</div>
152-
</body>
153-
</html>
108+
"""
109+
for status, group_df in cable_validation_df.groupby('Status'):
110+
styled_group_df = group_df.style.applymap(color_status, subset=['Status']).set_table_attributes('class="dataframe"')
111+
html_string += f'<h2>Cable Validation - {status}</h2>'
112+
html_string += styled_group_df.to_html()
113+
114+
for status, group_df in cable_specification_validation_df.groupby('Status'):
115+
styled_group_df = group_df.style.applymap(color_status, subset=['Status']).set_table_attributes('class="dataframe"')
116+
html_string += f'<h2>Cable Specification Validation - {status}</h2>'
117+
html_string += styled_group_df.to_html()
118+
119+
html_string += '</body></html>'
120+
121+
# Write the string to an HTML file
122+
with open('CableValidationAndSpecification-{filename}.html'.format(filename = date_time), 'w') as f:
123+
f.write(html_string)
154124
```
155-
# Save the HTML string to a file
156-
with open("reports/report-{filename}.html".format(filename = date_time), 'w') as f:
157-
f.write(html)
125+
126+
## Usage
127+
To run the conversion tool execute the following:
128+
```
129+
python cable-html.py
130+
..
131+
Please provide post validation json file: <CABLE_VALIDATION_FILENAME>.json
158132
```
133+
134+
The report output has filename `CableValidationAndSpecification-<DATE>.html`.
135+
136+
## Cable Validation Report HTML results
137+
138+
The report will be sparated into 6 sections:
139+
- Cable Validation - Compliant
140+
- Cable Validation - NonCompliant
141+
- Cable Validation - Unknown
142+
- Cable Specification Validation - Compliant
143+
- Cable Specification Validation - NonCompliant
144+
- Cable Specification Validation - Unknown
145+
146+
Sample report:
147+
:::image type="content" source="media\cable-validation-html.png" alt-text="Screenshot that shows the sample cable validation report." lightbox="media\cable-validation-html.png":::

0 commit comments

Comments
 (0)