illumio-pylo 0.2.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- illumio_pylo/API/APIConnector.py +1308 -0
- illumio_pylo/API/AuditLog.py +42 -0
- illumio_pylo/API/ClusterHealth.py +136 -0
- illumio_pylo/API/CredentialsManager.py +286 -0
- illumio_pylo/API/Explorer.py +1077 -0
- illumio_pylo/API/JsonPayloadTypes.py +240 -0
- illumio_pylo/API/RuleSearchQuery.py +128 -0
- illumio_pylo/API/__init__.py +0 -0
- illumio_pylo/AgentStore.py +139 -0
- illumio_pylo/Exception.py +44 -0
- illumio_pylo/Helpers/__init__.py +3 -0
- illumio_pylo/Helpers/exports.py +508 -0
- illumio_pylo/Helpers/functions.py +166 -0
- illumio_pylo/IPList.py +135 -0
- illumio_pylo/IPMap.py +285 -0
- illumio_pylo/Label.py +25 -0
- illumio_pylo/LabelCommon.py +48 -0
- illumio_pylo/LabelGroup.py +68 -0
- illumio_pylo/LabelStore.py +403 -0
- illumio_pylo/LabeledObject.py +25 -0
- illumio_pylo/Organization.py +258 -0
- illumio_pylo/Query.py +331 -0
- illumio_pylo/ReferenceTracker.py +41 -0
- illumio_pylo/Rule.py +671 -0
- illumio_pylo/Ruleset.py +306 -0
- illumio_pylo/RulesetStore.py +101 -0
- illumio_pylo/SecurityPrincipal.py +62 -0
- illumio_pylo/Service.py +256 -0
- illumio_pylo/SoftwareVersion.py +125 -0
- illumio_pylo/VirtualService.py +17 -0
- illumio_pylo/VirtualServiceStore.py +75 -0
- illumio_pylo/Workload.py +506 -0
- illumio_pylo/WorkloadStore.py +289 -0
- illumio_pylo/__init__.py +82 -0
- illumio_pylo/cli/NativeParsers.py +96 -0
- illumio_pylo/cli/__init__.py +134 -0
- illumio_pylo/cli/__main__.py +10 -0
- illumio_pylo/cli/commands/__init__.py +32 -0
- illumio_pylo/cli/commands/credential_manager.py +168 -0
- illumio_pylo/cli/commands/iplist_import_from_file.py +185 -0
- illumio_pylo/cli/commands/misc.py +7 -0
- illumio_pylo/cli/commands/ruleset_export.py +129 -0
- illumio_pylo/cli/commands/update_pce_objects_cache.py +44 -0
- illumio_pylo/cli/commands/ven_duplicate_remover.py +366 -0
- illumio_pylo/cli/commands/ven_idle_to_visibility.py +287 -0
- illumio_pylo/cli/commands/ven_upgrader.py +226 -0
- illumio_pylo/cli/commands/workload_export.py +251 -0
- illumio_pylo/cli/commands/workload_import.py +423 -0
- illumio_pylo/cli/commands/workload_relabeler.py +510 -0
- illumio_pylo/cli/commands/workload_reset_names_to_null.py +83 -0
- illumio_pylo/cli/commands/workload_used_in_rule_finder.py +80 -0
- illumio_pylo/docs/Doxygen +1757 -0
- illumio_pylo/tmp.py +104 -0
- illumio_pylo/utilities/__init__.py +0 -0
- illumio_pylo/utilities/cli.py +10 -0
- illumio_pylo/utilities/credentials.example.json +20 -0
- illumio_pylo/utilities/explorer_report_exporter.py +86 -0
- illumio_pylo/utilities/health_monitoring.py +102 -0
- illumio_pylo/utilities/iplist_analyzer.py +148 -0
- illumio_pylo/utilities/iplists_stats_duplicates_unused_finder.py +75 -0
- illumio_pylo/utilities/resources/iplists-import-example.csv +3 -0
- illumio_pylo/utilities/resources/iplists-import-example.xlsx +0 -0
- illumio_pylo/utilities/resources/workload-exporter-filter-example.csv +3 -0
- illumio_pylo/utilities/resources/workloads-import-example.csv +2 -0
- illumio_pylo/utilities/resources/workloads-import-example.xlsx +0 -0
- illumio_pylo/utilities/ven_compatibility_report_export.py +240 -0
- illumio_pylo/utilities/ven_idle_to_illumination.py +344 -0
- illumio_pylo/utilities/ven_reassign_pce.py +183 -0
- illumio_pylo-0.2.5.dist-info/LICENSE +176 -0
- illumio_pylo-0.2.5.dist-info/METADATA +197 -0
- illumio_pylo-0.2.5.dist-info/RECORD +73 -0
- illumio_pylo-0.2.5.dist-info/WHEEL +5 -0
- illumio_pylo-0.2.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
from typing import Dict, Any, List, Optional, TypedDict, NotRequired
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
import xlsxwriter
|
|
5
|
+
except ImportError:
|
|
6
|
+
import xlsxwriter
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
import openpyxl
|
|
10
|
+
except ImportError:
|
|
11
|
+
import openpyxl
|
|
12
|
+
|
|
13
|
+
import csv
|
|
14
|
+
import illumio_pylo as pylo
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ExcelHeader(TypedDict):
|
|
19
|
+
name: str
|
|
20
|
+
nice_name: NotRequired[str]
|
|
21
|
+
max_width: NotRequired[int]
|
|
22
|
+
wrap_text: NotRequired[bool]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ArrayToExport:
|
|
26
|
+
def __init__(self, headers):
|
|
27
|
+
self._headers = headers
|
|
28
|
+
self._columns_count = len(headers)
|
|
29
|
+
self._lines = []
|
|
30
|
+
|
|
31
|
+
self._headers_name_to_index = {}
|
|
32
|
+
self._headers_index_to_name = []
|
|
33
|
+
index = 0
|
|
34
|
+
for header_name in headers:
|
|
35
|
+
self._headers_name_to_index[header_name] = index
|
|
36
|
+
self._headers_index_to_name.append(header_name)
|
|
37
|
+
index += 1
|
|
38
|
+
|
|
39
|
+
def columns_count(self):
|
|
40
|
+
return len(self._headers)
|
|
41
|
+
|
|
42
|
+
def lines_count(self):
|
|
43
|
+
return len(self._lines)
|
|
44
|
+
|
|
45
|
+
def add_line_from_object(self, record):
|
|
46
|
+
new_line = []
|
|
47
|
+
for header in self._headers:
|
|
48
|
+
new_line.append(record.get(header))
|
|
49
|
+
|
|
50
|
+
self._lines.append(new_line)
|
|
51
|
+
|
|
52
|
+
def add_line_from_list_of_objects(self, list_of_objects):
|
|
53
|
+
for record in list_of_objects:
|
|
54
|
+
self.add_line_from_object(record)
|
|
55
|
+
|
|
56
|
+
def add_line_from_list(self, line: list):
|
|
57
|
+
if len(line) != self._columns_count:
|
|
58
|
+
raise pylo.PyloEx("line length ({}) does not match the number of columns ({})".format(len(line), self._columns_count))
|
|
59
|
+
self._lines.append(line)
|
|
60
|
+
|
|
61
|
+
def write_to_csv(self, filename, delimiter=',', multivalues_cell_delimiter=' '):
|
|
62
|
+
with open(filename, 'w', newline='') as csv_file:
|
|
63
|
+
filewriter = csv.writer(csv_file, delimiter=delimiter, quotechar='"', quoting=csv.QUOTE_ALL)
|
|
64
|
+
filewriter.writerow(self._headers)
|
|
65
|
+
for line in self._lines:
|
|
66
|
+
new_line = []
|
|
67
|
+
for item in line:
|
|
68
|
+
if type(item) is list:
|
|
69
|
+
new_line.append(pylo.string_list_to_text(item, multivalues_cell_delimiter))
|
|
70
|
+
else:
|
|
71
|
+
new_line.append(item)
|
|
72
|
+
filewriter.writerow(new_line)
|
|
73
|
+
|
|
74
|
+
def write_to_excel(self, filename, worksheet_name='worksheet1', multivalues_cell_delimiter=' '):
|
|
75
|
+
xls_workbook = xlsxwriter.Workbook(filename)
|
|
76
|
+
cell_format = xls_workbook.add_format()
|
|
77
|
+
cell_format.set_text_wrap()
|
|
78
|
+
cell_format.set_valign('vcenter')
|
|
79
|
+
xls_worksheet = xls_workbook.add_worksheet(worksheet_name)
|
|
80
|
+
xls_headers = []
|
|
81
|
+
xls_data = []
|
|
82
|
+
header_index = 0
|
|
83
|
+
for header in self._headers:
|
|
84
|
+
xls_headers.append({'header': header, 'format': cell_format})
|
|
85
|
+
header_index += 1
|
|
86
|
+
|
|
87
|
+
# Building data array
|
|
88
|
+
for line in self._lines:
|
|
89
|
+
new_line = []
|
|
90
|
+
for item in line:
|
|
91
|
+
if type(item) is list:
|
|
92
|
+
new_line.append(pylo.string_list_to_text(item, multivalues_cell_delimiter))
|
|
93
|
+
else:
|
|
94
|
+
new_line.append(item)
|
|
95
|
+
xls_data.append(new_line)
|
|
96
|
+
|
|
97
|
+
xls_table = xls_worksheet.add_table(0, 0, len(self._lines), len(self._headers)-1,
|
|
98
|
+
{'header_row': True, 'data': xls_data, 'columns': xls_headers}
|
|
99
|
+
)
|
|
100
|
+
xls_worksheet.freeze_panes(1, 0)
|
|
101
|
+
xls_workbook.close()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class ArraysToExcel:
|
|
105
|
+
|
|
106
|
+
_sheets: Dict[str, 'ArraysToExcel.Sheet']
|
|
107
|
+
|
|
108
|
+
class Sheet:
|
|
109
|
+
def __init__(self, headers: List[str|ExcelHeader], force_all_wrap_text=True, sheet_color: Optional[str] = None, order_by: Optional[List[str]] = None, multivalues_cell_delimiter=' '):
|
|
110
|
+
self._headers: List[str|ExcelHeader] = headers
|
|
111
|
+
self._columns_count = len(headers)
|
|
112
|
+
self._lines = []
|
|
113
|
+
self._columns_wrap = []
|
|
114
|
+
self._color = sheet_color
|
|
115
|
+
|
|
116
|
+
self._order_by = order_by
|
|
117
|
+
|
|
118
|
+
self._multivalues_cell_delimiter = multivalues_cell_delimiter
|
|
119
|
+
|
|
120
|
+
self._headers_name_to_index = {}
|
|
121
|
+
self._headers_index_to_name = []
|
|
122
|
+
index = 0
|
|
123
|
+
|
|
124
|
+
for header_name in headers:
|
|
125
|
+
self._columns_wrap.append(force_all_wrap_text)
|
|
126
|
+
|
|
127
|
+
if type(header_name) is str:
|
|
128
|
+
self._headers_index_to_name.append(header_name)
|
|
129
|
+
self._headers_name_to_index[header_name] = index
|
|
130
|
+
else:
|
|
131
|
+
header_name['nice_name'] = header_name.get('nice_name', header_name['name'])
|
|
132
|
+
self._headers_index_to_name.append(header_name['name'])
|
|
133
|
+
self._headers_name_to_index[header_name['name']] = index
|
|
134
|
+
|
|
135
|
+
wrap = header_name.get('wrap_text')
|
|
136
|
+
if wrap is not None and not wrap:
|
|
137
|
+
self._columns_wrap[len(self._columns_wrap)-1] = False
|
|
138
|
+
|
|
139
|
+
index += 1
|
|
140
|
+
|
|
141
|
+
def write_to_csv(self, filename: str,
|
|
142
|
+
):
|
|
143
|
+
headers: List[str] = []
|
|
144
|
+
for header in self._headers:
|
|
145
|
+
if type(header) is str:
|
|
146
|
+
headers.append(header)
|
|
147
|
+
continue
|
|
148
|
+
if 'nice_name' in header:
|
|
149
|
+
headers.append(header['nice_name'])
|
|
150
|
+
continue
|
|
151
|
+
headers.append(header['name'])
|
|
152
|
+
|
|
153
|
+
headers_id: List[str] = []
|
|
154
|
+
for header in self._headers:
|
|
155
|
+
if type(header) is str:
|
|
156
|
+
headers_id.append(header)
|
|
157
|
+
continue
|
|
158
|
+
headers_id.append(header['name'])
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
exporter = ArrayToExport(headers)
|
|
162
|
+
|
|
163
|
+
for line in self._lines:
|
|
164
|
+
row = []
|
|
165
|
+
for header in headers_id:
|
|
166
|
+
row.append(line[self._headers_name_to_index[header]])
|
|
167
|
+
exporter.add_line_from_list(row)
|
|
168
|
+
|
|
169
|
+
exporter.write_to_csv(filename)
|
|
170
|
+
|
|
171
|
+
def columns_count(self):
|
|
172
|
+
return len(self._headers)
|
|
173
|
+
|
|
174
|
+
def lines_count(self):
|
|
175
|
+
return len(self._lines)
|
|
176
|
+
|
|
177
|
+
def reorder_lines(self, order_by: List[str]):
|
|
178
|
+
self._lines = sorted(self._lines, key=lambda x: [x[self._headers_name_to_index[header_name]] for header_name in order_by])
|
|
179
|
+
|
|
180
|
+
def add_line_from_object(self, record):
|
|
181
|
+
new_line = []
|
|
182
|
+
for header in self._headers:
|
|
183
|
+
if type(header) is str:
|
|
184
|
+
new_line.append(record.get(header))
|
|
185
|
+
else:
|
|
186
|
+
new_line.append(record.get(header['name']))
|
|
187
|
+
|
|
188
|
+
self._lines.append(new_line)
|
|
189
|
+
|
|
190
|
+
def add_line_from_list_of_objects(self, list_of_objects):
|
|
191
|
+
for record in list_of_objects:
|
|
192
|
+
self.add_line_from_object(record)
|
|
193
|
+
|
|
194
|
+
def add_line_from_list(self, line: list):
|
|
195
|
+
if len(line) != self._columns_count:
|
|
196
|
+
raise pylo.PyloEx("line length ({}) does not match the number of columns ({})".format(len(line), self._columns_count))
|
|
197
|
+
self._lines.append(line)
|
|
198
|
+
|
|
199
|
+
def add_to_document(self, xls_workbook: xlsxwriter.Workbook, sheet_name: str):
|
|
200
|
+
|
|
201
|
+
def find_length(some_text: str) -> int:
|
|
202
|
+
if type(some_text) is bool or some_text is None:
|
|
203
|
+
return 0
|
|
204
|
+
if type(some_text) is int:
|
|
205
|
+
return len(str(some_text))
|
|
206
|
+
|
|
207
|
+
str_length = 0
|
|
208
|
+
split = some_text.split("\n")
|
|
209
|
+
for part in split:
|
|
210
|
+
if len(part) > str_length:
|
|
211
|
+
str_length = len(part)
|
|
212
|
+
|
|
213
|
+
return str_length
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# Data may need to be sorted
|
|
217
|
+
if self._order_by is not None and len(self._order_by) > 0:
|
|
218
|
+
self._lines = sorted(self._lines, key=lambda x: [x[self._headers_name_to_index[header_name]] for header_name in self._order_by])
|
|
219
|
+
# print("********* Sorted by {}".format(self._order_by))
|
|
220
|
+
|
|
221
|
+
xls_worksheet = xls_workbook.add_worksheet(sheet_name)
|
|
222
|
+
if self._color is not None:
|
|
223
|
+
xls_worksheet.tab_color = self._color
|
|
224
|
+
xls_headers = []
|
|
225
|
+
xls_data = []
|
|
226
|
+
|
|
227
|
+
columns_max_width = []
|
|
228
|
+
for header in self._headers:
|
|
229
|
+
columns_max_width.append(0)
|
|
230
|
+
|
|
231
|
+
for line in self._lines:
|
|
232
|
+
new_line = []
|
|
233
|
+
item_index = 0
|
|
234
|
+
for item in line:
|
|
235
|
+
if type(item) is list:
|
|
236
|
+
new_line.append(pylo.string_list_to_text(item, self._multivalues_cell_delimiter))
|
|
237
|
+
else:
|
|
238
|
+
new_line.append(item)
|
|
239
|
+
|
|
240
|
+
length = find_length(new_line[item_index])
|
|
241
|
+
if length > columns_max_width[item_index]:
|
|
242
|
+
columns_max_width[item_index] = length
|
|
243
|
+
item_index += 1
|
|
244
|
+
|
|
245
|
+
xls_data.append(new_line)
|
|
246
|
+
|
|
247
|
+
header_index = 0
|
|
248
|
+
for header in self._headers:
|
|
249
|
+
cell_format = xls_workbook.add_format()
|
|
250
|
+
cell_format.set_text_wrap(self._columns_wrap[header_index])
|
|
251
|
+
cell_format.set_valign('vcenter')
|
|
252
|
+
|
|
253
|
+
header_max_width_setting = None
|
|
254
|
+
|
|
255
|
+
if type(header) is str:
|
|
256
|
+
xls_headers.append({'header': header, 'format': cell_format})
|
|
257
|
+
column_name_length = len(header) + 2 # add 2 for dropdown menus
|
|
258
|
+
else:
|
|
259
|
+
column_name = header.get('nice_name') or header.get('name')
|
|
260
|
+
xls_headers.append({'header': column_name, 'format': cell_format})
|
|
261
|
+
column_name_length = len(column_name) + 2 # add 2 for dropdown menus
|
|
262
|
+
header_max_width_setting = header.get('max_width')
|
|
263
|
+
|
|
264
|
+
# default is to use width=longest string
|
|
265
|
+
column_width = columns_max_width[header_index]+1
|
|
266
|
+
|
|
267
|
+
if column_width < column_name_length:
|
|
268
|
+
column_width = column_name_length
|
|
269
|
+
|
|
270
|
+
if header_max_width_setting is not None:
|
|
271
|
+
if header_max_width_setting is None or header_max_width_setting == 'auto':
|
|
272
|
+
pass
|
|
273
|
+
elif columns_max_width[header_index] > header_max_width_setting:
|
|
274
|
+
column_width = header_max_width_setting
|
|
275
|
+
|
|
276
|
+
#print("column '{}' width={} vs setting={} vs calculated={}".format(header_index, column_width, header_max_width_setting, columns_max_width[header_index]))
|
|
277
|
+
|
|
278
|
+
xls_worksheet.set_column(header_index, header_index, width=column_width*1.1)
|
|
279
|
+
|
|
280
|
+
header_index += 1
|
|
281
|
+
|
|
282
|
+
if len(self._lines) > 0:
|
|
283
|
+
xls_table = xls_worksheet.add_table(0, 0, len(self._lines), len(self._headers)-1,
|
|
284
|
+
{'header_row': True, 'data': xls_data, 'columns': xls_headers}
|
|
285
|
+
)
|
|
286
|
+
else:
|
|
287
|
+
fake_data = []
|
|
288
|
+
for header in self._headers:
|
|
289
|
+
fake_data.append(None)
|
|
290
|
+
|
|
291
|
+
xls_table = xls_worksheet.add_table(0, 0, 1, len(self._headers)-1,
|
|
292
|
+
{'header_row': True, 'data': [fake_data], 'columns': xls_headers}
|
|
293
|
+
)
|
|
294
|
+
xls_worksheet.freeze_panes(1, 0)
|
|
295
|
+
|
|
296
|
+
def __init__(self):
|
|
297
|
+
self._sheets = {}
|
|
298
|
+
|
|
299
|
+
def create_sheet(self, name: str, headers, force_all_wrap_text: bool = True, sheet_color: Optional[str] = None,
|
|
300
|
+
order_by: Optional[List[str]] = None, multivalues_cell_delimiter: str = ' ') -> Sheet:
|
|
301
|
+
if name in self._sheets:
|
|
302
|
+
pylo.PyloEx("A sheet named '{}' already exists".format(name))
|
|
303
|
+
|
|
304
|
+
self._sheets[name] = ArraysToExcel.Sheet(headers, force_all_wrap_text=force_all_wrap_text,
|
|
305
|
+
sheet_color=sheet_color, order_by=order_by,
|
|
306
|
+
multivalues_cell_delimiter=multivalues_cell_delimiter)
|
|
307
|
+
return self._sheets[name]
|
|
308
|
+
|
|
309
|
+
def write_to_excel(self, filename, multivalues_cell_delimiter=' '):
|
|
310
|
+
xls_workbook = xlsxwriter.Workbook(filename)
|
|
311
|
+
|
|
312
|
+
for sheet_name, sheet_object in self._sheets.items():
|
|
313
|
+
sheet_object.add_to_document(xls_workbook, sheet_name)
|
|
314
|
+
|
|
315
|
+
xls_workbook.close()
|
|
316
|
+
|
|
317
|
+
def add_line_from_object(self, record, sheet_name: str):
|
|
318
|
+
self._sheets[sheet_name].add_line_from_object(record)
|
|
319
|
+
|
|
320
|
+
def add_line_from_list_of_objects(self, list_of_objects, sheet_name: str):
|
|
321
|
+
self._sheets[sheet_name].add_line_from_list_of_objects(list_of_objects)
|
|
322
|
+
|
|
323
|
+
def add_line_from_list(self, line: list, sheet_name: str):
|
|
324
|
+
self._sheets[sheet_name].add_line_from_list(line)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class CsvExcelToObject:
|
|
329
|
+
|
|
330
|
+
def __init__(self, filename: str, expected_headers=None, csv_delimiter=',', csv_quotechar='"', strict_headers=False, excel_sheet_name=None):
|
|
331
|
+
|
|
332
|
+
self._detected_headers = []
|
|
333
|
+
self._header_index_to_name = []
|
|
334
|
+
self._raw_lines = []
|
|
335
|
+
self._objects = []
|
|
336
|
+
self._empty_lines_count = 0
|
|
337
|
+
|
|
338
|
+
if strict_headers and expected_headers is None:
|
|
339
|
+
pylo.PyloEx("CSV/Excel file cannot use strict_headers mode without specifying expected_headers")
|
|
340
|
+
|
|
341
|
+
if not os.path.exists(filename):
|
|
342
|
+
raise pylo.PyloEx("File '{}' does not exist".format(filename))
|
|
343
|
+
|
|
344
|
+
optional_headers = []
|
|
345
|
+
mandatory_headers_dict = {}
|
|
346
|
+
for header_infos in expected_headers:
|
|
347
|
+
value = header_infos.get('optional')
|
|
348
|
+
if value is None or value is True:
|
|
349
|
+
optional_headers.append(header_infos)
|
|
350
|
+
else:
|
|
351
|
+
mandatory_headers_dict[header_infos['name']] = header_infos
|
|
352
|
+
|
|
353
|
+
file_base, file_extension = os.path.splitext(filename)
|
|
354
|
+
file_extension = file_extension.lower()
|
|
355
|
+
|
|
356
|
+
if file_extension == '.csv':
|
|
357
|
+
with open(filename) as csv_file:
|
|
358
|
+
csv_reader = csv.reader(csv_file, delimiter=csv_delimiter, quotechar=csv_quotechar)
|
|
359
|
+
row_count = 0
|
|
360
|
+
for row in csv_reader:
|
|
361
|
+
|
|
362
|
+
# this is Headers
|
|
363
|
+
if row_count == 0:
|
|
364
|
+
for item in row:
|
|
365
|
+
if item is None or len(item) < 1:
|
|
366
|
+
raise pylo.PyloEx('CSV headers has blank fields, this is not supported')
|
|
367
|
+
self._detected_headers.append(item.lower())
|
|
368
|
+
if strict_headers and item.lower() not in mandatory_headers_dict:
|
|
369
|
+
raise pylo.PyloEx("CSV/Excel headers have an unexpected header named '{}'".format(item))
|
|
370
|
+
|
|
371
|
+
missing_headers = mandatory_headers_dict.copy()
|
|
372
|
+
for header_name in self._detected_headers:
|
|
373
|
+
self._header_index_to_name.append(header_name)
|
|
374
|
+
if header_name in missing_headers:
|
|
375
|
+
del missing_headers[header_name]
|
|
376
|
+
if len(missing_headers) > 0:
|
|
377
|
+
raise pylo.PyloEx('CSV is missing the following mandatory headers: {}'.format(pylo.string_list_to_text(missing_headers.keys())))
|
|
378
|
+
|
|
379
|
+
# this is DATA
|
|
380
|
+
else:
|
|
381
|
+
if len(self._detected_headers) != len(row):
|
|
382
|
+
raise pylo.PyloEx('CSV line #{} doesnt have the same fields ({}) count than the headers ({}), is it empty?'.format(row_count+1,
|
|
383
|
+
len(self._detected_headers),
|
|
384
|
+
len(row)))
|
|
385
|
+
|
|
386
|
+
self._raw_lines.append(row)
|
|
387
|
+
new_object = {'*line*': row_count+1}
|
|
388
|
+
self._objects.append(new_object)
|
|
389
|
+
row_index = 0
|
|
390
|
+
for item in row:
|
|
391
|
+
new_object[self._detected_headers[row_index]] = item
|
|
392
|
+
row_index += 1
|
|
393
|
+
|
|
394
|
+
# handling missing optional columns
|
|
395
|
+
for opt_header in optional_headers:
|
|
396
|
+
if opt_header['name'] not in new_object:
|
|
397
|
+
if 'default' in opt_header:
|
|
398
|
+
new_object[opt_header['name']] = opt_header['default']
|
|
399
|
+
else:
|
|
400
|
+
new_object[opt_header['name']] = ''
|
|
401
|
+
|
|
402
|
+
row_count += 1
|
|
403
|
+
elif file_extension == '.xlsx':
|
|
404
|
+
#workbook = openpyxl.load_workbook(filename, read_only=True)
|
|
405
|
+
workbook = openpyxl.load_workbook(filename)
|
|
406
|
+
# print("workbook has {} worksheets".format(len(workbook.worksheets)))
|
|
407
|
+
if len(workbook.worksheets) < 1:
|
|
408
|
+
raise pylo.PyloEx("Excel file has no Worksheet")
|
|
409
|
+
|
|
410
|
+
source_worksheet = workbook.worksheets[0]
|
|
411
|
+
|
|
412
|
+
if excel_sheet_name is not None:
|
|
413
|
+
source_worksheet = workbook.get_sheet_by_name(excel_sheet_name)
|
|
414
|
+
if source_worksheet is None:
|
|
415
|
+
raise pylo.PyloEx("Cannot find a Worksheet named '{}' in Excel file '{}'".format(excel_sheet_name, filename))
|
|
416
|
+
|
|
417
|
+
# print("\n\nmax_col {} max_row {}\n\n".format(source_worksheet.max_column,source_worksheet.max_row))
|
|
418
|
+
|
|
419
|
+
for row_count in range(1, source_worksheet.max_row+1):
|
|
420
|
+
# this is Headers
|
|
421
|
+
if row_count == 1:
|
|
422
|
+
for col_index in range(1, source_worksheet.max_column+1):
|
|
423
|
+
item = source_worksheet.cell(row_count, col_index).value
|
|
424
|
+
if item is None or len(item) < 1:
|
|
425
|
+
raise pylo.PyloEx('Excel headers has blank fields, this is not supported')
|
|
426
|
+
self._detected_headers.append(item.lower())
|
|
427
|
+
if strict_headers and item.lower() not in mandatory_headers_dict:
|
|
428
|
+
raise pylo.PyloEx("CSV/Excel headers have an unexpected header named '{}'".format(item))
|
|
429
|
+
|
|
430
|
+
missing_headers = mandatory_headers_dict.copy()
|
|
431
|
+
for header_name in self._detected_headers:
|
|
432
|
+
self._header_index_to_name.append(header_name)
|
|
433
|
+
if header_name in missing_headers:
|
|
434
|
+
del missing_headers[header_name]
|
|
435
|
+
if len(missing_headers) > 0:
|
|
436
|
+
raise pylo.PyloEx('Excel file is missing the following mandatory headers: {}'.format(pylo.string_list_to_text(missing_headers.keys())))
|
|
437
|
+
|
|
438
|
+
# This is DATA
|
|
439
|
+
else:
|
|
440
|
+
new_object = {'*line*': row_count}
|
|
441
|
+
|
|
442
|
+
some_fields_have_data = False
|
|
443
|
+
for col_index in range(1, source_worksheet.max_column+1):
|
|
444
|
+
item = source_worksheet.cell(row_count, col_index).value
|
|
445
|
+
new_object[self._detected_headers[col_index-1]] = item
|
|
446
|
+
if item is not None and (type(item) is int or type(item) is bool or len(item) > 0):
|
|
447
|
+
some_fields_have_data = True
|
|
448
|
+
|
|
449
|
+
if not some_fields_have_data:
|
|
450
|
+
self._empty_lines_count += 1
|
|
451
|
+
continue
|
|
452
|
+
|
|
453
|
+
self._objects.append(new_object)
|
|
454
|
+
|
|
455
|
+
# handling missing optional columns
|
|
456
|
+
for opt_header in optional_headers:
|
|
457
|
+
if opt_header['name'] not in new_object:
|
|
458
|
+
if 'default' in opt_header:
|
|
459
|
+
new_object[opt_header['name']] = opt_header['default']
|
|
460
|
+
else:
|
|
461
|
+
new_object[opt_header['name']] = ''
|
|
462
|
+
|
|
463
|
+
else:
|
|
464
|
+
raise pylo.PyloEx("Unsupported file extension '{}' in filename {}".format(file_extension, filename))
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def count_lines(self):
|
|
468
|
+
return len(self._objects)
|
|
469
|
+
|
|
470
|
+
def count_empty_lines(self):
|
|
471
|
+
return self._empty_lines_count
|
|
472
|
+
|
|
473
|
+
def count_columns(self):
|
|
474
|
+
return len(self._detected_headers)
|
|
475
|
+
|
|
476
|
+
def objects(self):
|
|
477
|
+
return list(self._objects)
|
|
478
|
+
|
|
479
|
+
def save_to_csv(self, filename: str, fields_filter: List[Any]):
|
|
480
|
+
headers = []
|
|
481
|
+
for field in fields_filter:
|
|
482
|
+
headers.append(field['name'])
|
|
483
|
+
|
|
484
|
+
exporter = ArrayToExport(headers)
|
|
485
|
+
|
|
486
|
+
for obj in self._objects:
|
|
487
|
+
row = []
|
|
488
|
+
for header in headers:
|
|
489
|
+
row.append(obj.get(header))
|
|
490
|
+
exporter.add_line_from_list(row)
|
|
491
|
+
|
|
492
|
+
exporter.write_to_csv(filename)
|
|
493
|
+
|
|
494
|
+
def save_to_excel(self, filename: str, fields_filter: list):
|
|
495
|
+
headers = []
|
|
496
|
+
for field in fields_filter:
|
|
497
|
+
headers.append(field['name'])
|
|
498
|
+
|
|
499
|
+
exporter = ArrayToExport(headers)
|
|
500
|
+
|
|
501
|
+
for obj in self._objects:
|
|
502
|
+
row = []
|
|
503
|
+
for header in headers:
|
|
504
|
+
row.append(obj.get(header))
|
|
505
|
+
exporter.add_line_from_list(row)
|
|
506
|
+
|
|
507
|
+
exporter.write_to_excel(filename)
|
|
508
|
+
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def nice_json(json_obj):
|
|
8
|
+
return json.dumps(json_obj, indent=2, sort_keys=True)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def string_list_to_text(string_list, separator=',') -> str:
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
:type string_list: List[str]
|
|
15
|
+
:type separator: str
|
|
16
|
+
"""
|
|
17
|
+
msg = ""
|
|
18
|
+
first = True
|
|
19
|
+
for stringItem in string_list:
|
|
20
|
+
if type(stringItem) is str:
|
|
21
|
+
str_to_print = stringItem
|
|
22
|
+
else:
|
|
23
|
+
str_to_print = stringItem.name
|
|
24
|
+
|
|
25
|
+
if not first:
|
|
26
|
+
msg += separator
|
|
27
|
+
first = False
|
|
28
|
+
msg += str_to_print
|
|
29
|
+
# print("list length {} and msg ='{}'".format(len(string_list), msg))
|
|
30
|
+
return msg
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def obj_with_href_list_to_text(string_list, separator=',') -> str:
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
:type string_list: List[str]
|
|
37
|
+
:type separator: str
|
|
38
|
+
"""
|
|
39
|
+
msg = ""
|
|
40
|
+
first = True
|
|
41
|
+
for stringItem in string_list:
|
|
42
|
+
str_to_print = stringItem.href
|
|
43
|
+
|
|
44
|
+
if not first:
|
|
45
|
+
msg += separator
|
|
46
|
+
first = False
|
|
47
|
+
msg += str_to_print
|
|
48
|
+
# print("list length {} and msg ='{}'".format(len(string_list), msg))
|
|
49
|
+
return msg
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def file_clean(path, no_print=False):
|
|
53
|
+
if not no_print:
|
|
54
|
+
print(" * Cleaning file '{}' from previous runs... ".format(path), end='', flush=True)
|
|
55
|
+
if os.path.exists(path):
|
|
56
|
+
if not os.path.isfile(path):
|
|
57
|
+
raise Exception("Provided path '{}' is not a file!".format(path))
|
|
58
|
+
else:
|
|
59
|
+
os.remove(path)
|
|
60
|
+
print("OK!")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
___ipv4_pattern = re.compile(r"""
|
|
64
|
+
^
|
|
65
|
+
(?:
|
|
66
|
+
# Dotted variants:
|
|
67
|
+
(?:
|
|
68
|
+
# Decimal 1-255 (no leading 0's)
|
|
69
|
+
[3-9]\d?|2(?:5[0-5]|[0-4]?\d)?|1\d{0,2}
|
|
70
|
+
|
|
|
71
|
+
0x0*[0-9a-f]{1,2} # Hexadecimal 0x0 - 0xFF (possible leading 0's)
|
|
72
|
+
|
|
|
73
|
+
0+[1-3]?[0-7]{0,2} # Octal 0 - 0377 (possible leading 0's)
|
|
74
|
+
)
|
|
75
|
+
(?: # Repeat 0-3 times, separated by a dot
|
|
76
|
+
\.
|
|
77
|
+
(?:
|
|
78
|
+
[3-9]\d?|2(?:5[0-5]|[0-4]?\d)?|1\d{0,2}
|
|
79
|
+
|
|
|
80
|
+
0x0*[0-9a-f]{1,2}
|
|
81
|
+
|
|
|
82
|
+
0+[1-3]?[0-7]{0,2}
|
|
83
|
+
)
|
|
84
|
+
){0,3}
|
|
85
|
+
|
|
|
86
|
+
0x0*[0-9a-f]{1,8} # Hexadecimal notation, 0x0 - 0xffffffff
|
|
87
|
+
|
|
|
88
|
+
0+[0-3]?[0-7]{0,10} # Octal notation, 0 - 037777777777
|
|
89
|
+
|
|
|
90
|
+
# Decimal notation, 1-4294967295:
|
|
91
|
+
429496729[0-5]|42949672[0-8]\d|4294967[01]\d\d|429496[0-6]\d{3}|
|
|
92
|
+
42949[0-5]\d{4}|4294[0-8]\d{5}|429[0-3]\d{6}|42[0-8]\d{7}|
|
|
93
|
+
4[01]\d{8}|[1-3]\d{0,9}|[4-9]\d{0,8}
|
|
94
|
+
)
|
|
95
|
+
$
|
|
96
|
+
""", re.VERBOSE | re.IGNORECASE)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def is_valid_ipv4(ip):
|
|
100
|
+
"""Validates IPv4 addresses.
|
|
101
|
+
"""
|
|
102
|
+
return ___ipv4_pattern.match(ip) is not None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
___ipv6_pattern = re.compile(r"""
|
|
106
|
+
^
|
|
107
|
+
\s* # Leading whitespace
|
|
108
|
+
(?!.*::.*::) # Only a single whildcard allowed
|
|
109
|
+
(?:(?!:)|:(?=:)) # Colon iff it would be part of a wildcard
|
|
110
|
+
(?: # Repeat 6 times:
|
|
111
|
+
[0-9a-f]{0,4} # A group of at most four hexadecimal digits
|
|
112
|
+
(?:(?<=::)|(?<!::):) # Colon unless preceeded by wildcard
|
|
113
|
+
){6} #
|
|
114
|
+
(?: # Either
|
|
115
|
+
[0-9a-f]{0,4} # Another group
|
|
116
|
+
(?:(?<=::)|(?<!::):) # Colon unless preceeded by wildcard
|
|
117
|
+
[0-9a-f]{0,4} # Last group
|
|
118
|
+
(?: (?<=::) # Colon iff preceeded by exacly one colon
|
|
119
|
+
| (?<!:) #
|
|
120
|
+
| (?<=:) (?<!::) : #
|
|
121
|
+
) # OR
|
|
122
|
+
| # A v4 address with NO leading zeros
|
|
123
|
+
(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]?\d)
|
|
124
|
+
(?: \.
|
|
125
|
+
(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]?\d)
|
|
126
|
+
){3}
|
|
127
|
+
)
|
|
128
|
+
\s* # Trailing whitespace
|
|
129
|
+
$
|
|
130
|
+
""", re.VERBOSE | re.IGNORECASE | re.DOTALL)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def is_valid_ipv6(ip):
|
|
134
|
+
"""Validates IPv6 addresses.
|
|
135
|
+
"""
|
|
136
|
+
#print("testing ({})".format(ip))
|
|
137
|
+
return ___ipv6_pattern.match(ip) is not None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def hostname_from_fqdn(fqdn: str):
|
|
141
|
+
return fqdn.split('.')[0]
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
__clocks_start = {}
|
|
145
|
+
__clocks_end = {}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def clock_start(name:str = 'default'):
|
|
149
|
+
__clocks_start[name] = time.time()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def clock_stop(name:str = 'default'):
|
|
153
|
+
__clocks_end[name] = time.time()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
import functools
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def clock_elapsed_str(name:str = 'default'):
|
|
160
|
+
t = time.time()-__clocks_start[name]
|
|
161
|
+
return "%d:%02d:%02d.%03d" % \
|
|
162
|
+
functools.reduce(lambda ll,b : divmod(ll[0],b) + ll[1:],
|
|
163
|
+
[(t*1000,),1000,60,60])
|
|
164
|
+
return "{}".format(time.time()-__clocks_start[name])
|
|
165
|
+
|
|
166
|
+
|