@@ -15,7 +15,7 @@ This article explains how to convert a Nexus Network Fabric Cable Validation Rep
15
15
16
16
## Prerequisites
17
17
18
- - Requires Modules: ` json pandas as pd `
18
+ - Requires Modules: ` json pandas as pd datetime `
19
19
20
20
## Python Script for Cable Validation JSON to html conversion
21
21
@@ -24,32 +24,48 @@ import json
24
24
import pandas as pd
25
25
from datetime import datetime
26
26
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
+
27
41
now = datetime.now() # current date and time
28
42
date_time = now.strftime("%m-%d-%Y-%H-%M")
29
43
print("date and time:",date_time)
30
44
31
-
32
45
# Get the file name as input from the user
33
46
file_name = input("Please provide post validation json file: ")
34
47
# Load the JSON data from the file
35
48
with open(file_name, 'r') as f:
36
49
data = json.load(f)
37
50
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 = []
40
54
41
55
# Loop through each rack in the racks list
42
56
for rack in data['racks']:
43
- # Loop through each device in the networkDevices list
57
+ # Loop through each device in the networkConfiguration list
44
58
for device in rack['rackInfo']['networkConfiguration']['networkDevices']:
45
59
# Loop through each interface map for the device
46
60
for interface_map in device['fixedInterfaceMaps']:
47
61
# Loop through each validation result for the interface map
48
62
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
50
64
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)
53
69
54
70
# Check if scaleSpecificInterfaceMaps is not None
55
71
if device['scaleSpecificInterfaceMaps'] is not None:
@@ -61,98 +77,71 @@ for rack in data['racks']:
61
77
for validation_result in interfacemaps['validationResult']:
62
78
# Append the data to the list
63
79
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 = """
126
92
<html>
127
93
<head>
128
94
<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;}
137
105
</style>
138
106
</head>
139
107
<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)
154
124
```
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
158
132
```
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