nettoolkit 0.0.3__zip

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.
Files changed (16) hide show
  1. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit/__init__.py +49 -0
  2. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit/__pycache__/__init__.cpython-38.pyc +0 -0
  3. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit/__pycache__/addressing.cpython-38.pyc +0 -0
  4. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit/__pycache__/gpl.cpython-38.pyc +0 -0
  5. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit/__pycache__/hierarchy.cpython-38.pyc +0 -0
  6. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit/__pycache__/hierarchy_rules.cpython-38.pyc +0 -0
  7. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit/__pycache__/jset.cpython-38.pyc +0 -0
  8. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit/addressing.py +920 -0
  9. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit/gpl.py +1207 -0
  10. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit/hierarchy.py +558 -0
  11. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit/hierarchy_rules.py +229 -0
  12. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit/jset.py +109 -0
  13. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit-0.0.3-py3.8.egg-info/PKG-INFO +41 -0
  14. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit-0.0.3-py3.8.egg-info/SOURCES.txt +12 -0
  15. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit-0.0.3-py3.8.egg-info/dependency_links.txt +1 -0
  16. Users/ALI/AppData/Local/Programs/Python/Python38-32/Lib/site-packages/nettoolkit-0.0.3-py3.8.egg-info/top_level.txt +1 -0
@@ -0,0 +1,1207 @@
1
+ # -----------------------------------------------------------------------------
2
+ # IMPORTS
3
+ # -----------------------------------------------------------------------------
4
+ from inspect import signature
5
+
6
+ import pandas as pd
7
+
8
+ from abc import ABC, abstractproperty, abstractclassmethod
9
+ import datetime
10
+ from re import compile
11
+ from collections import OrderedDict
12
+ from os import popen
13
+ import os
14
+ import threading
15
+
16
+ intBeginWith = compile(r'^\D+')
17
+
18
+ # -----------------------------------------------------------------------------
19
+ # Common Classes #
20
+ # -----------------------------------------------------------------------------
21
+
22
+ class Default():
23
+ """Default docString"""
24
+ def __str__(self): return self.__doc__
25
+ def _repr(self):
26
+ fields = signature(self.__init__).parameters
27
+ values = ", ".join(repr(getattr(self, f)) for f in fields)
28
+ return f'{type(self).__name__}({values})'
29
+
30
+
31
+
32
+ class Container(ABC):
33
+ """Support Containers callable objects
34
+ object should contain objVar property
35
+ """
36
+ @abstractproperty
37
+ @property
38
+ def objVar(self): pass
39
+ def __bool__(self): return True if self.objVar else False
40
+ def __len__(self): return len(self.objVar)
41
+ def __dir__(self): return self.objVar # sorted
42
+ def __getitem__(self, i): return self.objVar[i]
43
+ def __setitem__(self, i, v): self.objVar[i] = v
44
+ def __delitem__(self, i): del(self.objVar[i])
45
+ def __contains__(self, i): return i in self.objVar
46
+ def __reversed__(self): return reversed(self.objVar)
47
+ def __missing__(self, i): raise Exception(f'key {i} unavailable') # only for dict subclass
48
+ def __iter__(self):
49
+ if isinstance(self.objVar, (list, tuple, set, str)):
50
+ for line in self.objVar:
51
+ yield line
52
+ elif isinstance(self.objVar, (dict, OrderedDict)):
53
+ for key, value in self.objVar.items():
54
+ yield (key, value)
55
+
56
+ ## TBD / NOT IMPLEMENTED YET ##
57
+ class Numeric():
58
+ """Support Numberic objects"""
59
+ def __add__(self): pass
60
+ def __sub__(self): pass
61
+ def __mul__(self): pass
62
+ def __truediv__(self): pass
63
+ def __floordiv__(self): pass
64
+ def __pow__(self): pass
65
+ def __lshift__(self): pass
66
+ def __rshift__(self): pass
67
+ def __and__(self): pass
68
+ def __xor__(self): pass
69
+ def __or__(self): pass
70
+
71
+ def __iadd__(self): pass
72
+ def __isub__(self): pass
73
+ def __imul__(self): pass
74
+ def __itruediv__(self): pass
75
+ def __ifloordiv__(self): pass
76
+ def __ipow__(self): pass
77
+ def __ilshift__(self): pass
78
+ def __irshift__(self): pass
79
+ def __iand__(self): pass
80
+ def __ixor__(self): pass
81
+ def __ior__(self): pass
82
+
83
+ def __neg__(self): pass
84
+ def __pos__(self): pass
85
+ def __abs__(self): pass
86
+ def __invert__(self): pass
87
+
88
+
89
+ # -----------------------------------------------------------------------------
90
+ # STRING OPERATIONS #
91
+ # -----------------------------------------------------------------------------
92
+ class STR(Container):
93
+
94
+ @staticmethod
95
+ def foundPos(s, sub, pos=0):
96
+ '''Search for substring in string and return index value result
97
+ --> int
98
+
99
+ :param s: main string to be search within
100
+ :type s: str
101
+
102
+ :param sub: substring which is to be search in to main string
103
+ :type sub: str
104
+
105
+ :param pos: position index, search to be start from
106
+ :type pos: int
107
+ '''
108
+ return s.find(sub, pos)
109
+
110
+ @staticmethod
111
+ def found(s, sub, pos=0):
112
+ '''Search for substring in string and return Boolean result
113
+ --> bool
114
+
115
+ :param s: main string to be search within
116
+ :type s: str
117
+
118
+ :param sub: substring which is to be search in to main string
119
+ :type sub: str
120
+
121
+ :param pos: position index, search to be start from
122
+ :type pos: int
123
+ '''
124
+ try:
125
+ return True if s.find(sub, pos) > -1 else False
126
+ except:
127
+ return False
128
+
129
+ @staticmethod
130
+ def find_within(s, prefix, suffix=None, pos=0):
131
+ '''finds characters between prefix and suffix substrings from string,
132
+ --> tuple: (str, int)
133
+
134
+ :param s: main string to be search within
135
+ :type s: str
136
+
137
+ :param prefix: starting substring
138
+ :type prefix: str
139
+
140
+ :param suffix: ending substring
141
+ :type suffix: str
142
+
143
+ :param pos: position index, search to be start from
144
+ :type pos: int
145
+
146
+ -->Tuple
147
+ --------
148
+ index-0 is returned string
149
+ index-1 is position of returned suffix position
150
+ '''
151
+ p = STR.foundPos(s, prefix, pos=pos)+len(prefix)
152
+ if suffix is None:
153
+ ln = len(s)
154
+ else:
155
+ ln = STR.foundPos(s, suffix, pos=p+1)
156
+ if p == -1:
157
+ return None
158
+ if ln == -1:
159
+ ln = len(s)
160
+ return (s[p:ln], ln)
161
+
162
+ @staticmethod
163
+ def string_within(line, prefix, suffix=None, pos=0):
164
+ '''finds characters between prefix and suffix substrings from string,
165
+ --> str
166
+
167
+ :param s: main string to be search within
168
+ :type s: str
169
+
170
+ :param prefix: starting substring
171
+ :type prefix: str
172
+
173
+ :param suffix: ending substring
174
+ :type suffix: str
175
+
176
+ :param pos: position index, search to be start from
177
+ :type pos: int
178
+ '''
179
+ return STR.find_within(line, prefix, suffix, pos)[0]
180
+
181
+ @staticmethod
182
+ def suffix_index_within(line, prefix, suffix=None, pos=0):
183
+ '''finds characters between prefix and suffix substrings from string,
184
+ --> int: index of suffix
185
+
186
+ :param s: main string to be search within
187
+ :type s: str
188
+
189
+ :param prefix: starting substring
190
+ :type prefix: str
191
+
192
+ :param suffix: ending substring
193
+ :type suffix: str
194
+
195
+ :param pos: position index, search to be start from
196
+ :type pos: int
197
+ '''
198
+ return STR.find_within(line, prefix, suffix, pos)[1]
199
+
200
+ @staticmethod
201
+ def find_multi(s, sub, start=0, count=None, index=True, beginwith=False):
202
+ '''search for multiple substrings 'sub' within string 's'
203
+ usage: find_multi(s, sub, [start=n, [count=c], index=True])
204
+ --> list of indexes/bool
205
+
206
+ :param s: main string
207
+ :type s: str
208
+
209
+ :param sub: sub string ( to be search within main string )
210
+ :type sub: (str, tuple, list)
211
+
212
+ :param start: Optional: substring to be start search from index (def:0)
213
+ :type start: int
214
+
215
+ :param count: Optional: count of character from start index (def:end)
216
+ :type count: int
217
+
218
+ :param index: Optional: return index or boolean values(def:True=Index)
219
+ :type index: bool
220
+ '''
221
+ count = len(s) if count is None else count+start
222
+ if isinstance(sub, str):
223
+ i = s.find(sub, start, count)
224
+ if index:
225
+ if beginwith:
226
+ return i if i == 0 else -1
227
+ else:
228
+ return i
229
+ else:
230
+ if beginwith:
231
+ return True if i == 0 else False
232
+ else:
233
+ return False if i == -1 else True
234
+ elif isinstance(sub, (tuple, list)):
235
+ sl = []
236
+ for x in sub:
237
+ sl.append(STR.find_multi(s, x, start, count, index, beginwith))
238
+ return sl
239
+ else:
240
+ return None
241
+
242
+ @staticmethod
243
+ def find_all(s, sub, start=0, count=None, beginwith=False):
244
+ '''search for multiple substrings 'sub' within string 's'
245
+ usage: find_all(s, sub, [start=n, [count=c]])
246
+ --> (all matches) -> bool
247
+
248
+ :param s: main string
249
+ :type s: str
250
+
251
+ :param sub: sub string ( to be search within main string )
252
+ :type sub: (str, tuple, list)
253
+
254
+ :param start: Optional: substring to be start search from index (def:0)
255
+ :type start: int
256
+
257
+ :param count: Optional: count of character from start index (def:end)
258
+ :type count: int
259
+ '''
260
+ sl = STR.find_multi(s, sub, start, count, False, beginwith)
261
+ try:
262
+ return False if False in sl else True
263
+ except:
264
+ return sl
265
+
266
+ @staticmethod
267
+ def find_any(s, sub, start=0, count=None, beginwith=False):
268
+ '''search for multiple substrings 'sub' within string 's'
269
+ usage: find_any(s, sub, [start=n, [count=c]])
270
+ --> (any matches) -> bool
271
+
272
+ :param s: main string
273
+ :type s: str
274
+
275
+ :param sub: sub string ( to be search within main string )
276
+ :type sub: (str, tuple, list)
277
+
278
+ :param start: Optional: substring to be start search from index (def:0)
279
+ :type start: int
280
+
281
+ :param count: Optional: count of character from start index (def:end)
282
+ :type count: int
283
+ '''
284
+ sl = STR.find_multi(s, sub, start, count, False, beginwith)
285
+ try:
286
+ return True if True in sl else False
287
+ except:
288
+ return sl
289
+
290
+ @staticmethod
291
+ def update(s, searchItem='', replaceItem=''):
292
+ '''Updates string for search item with replace item
293
+ --> str
294
+
295
+ :param s: main string
296
+ :type s: str
297
+
298
+ :param searchItem: search string
299
+ :type searchItem: str
300
+
301
+ :param replaceItem: replace string
302
+ :type replaceItem: str '''
303
+ return s.replace(searchItem, replaceItem)
304
+
305
+ @staticmethod
306
+ def replace_dual_and_split(s, duo=' ', strip=None):
307
+ '''Finds subsequent characters in string and replace those with single,
308
+ plus, splits the string using provided character (duo).
309
+ --> list
310
+
311
+ :param s: Source string
312
+ :type s: str
313
+
314
+ :param duo:characters which requires reductions if susequent
315
+ :type duo: str
316
+
317
+ :param strip: values (-1=lstrip ,0=strip ,1=rstrip) - def:None
318
+ :type strip: int
319
+ '''
320
+ return STR.finddualnreplacesingle(s, strip=0).split(duo)
321
+
322
+ @staticmethod
323
+ def finddualnreplacesingle(s, duo=' ', strip=None):
324
+ '''Finds subsequent characters in string and replace those with single.
325
+ --> str
326
+
327
+ :param s: Source string
328
+ :type s: str
329
+
330
+ :param duo: characters which requires reductions if susequent
331
+ :type duo: str
332
+
333
+ :param strip: values (-1=lstrip ,0=strip ,1=rstrip) - def:None
334
+ :type strip: int
335
+ '''
336
+ while s.find(duo+duo) > -1:
337
+ s = s.replace(duo+duo, duo)
338
+ if strip is not None and isinstance(strip, int):
339
+ if strip == -1:
340
+ return s.lstrip()
341
+ elif strip == 0:
342
+ return s.strip()
343
+ elif strip == 1:
344
+ return s.rstrip()
345
+ else:
346
+ print('invalid strip value detected', strip)
347
+ else:
348
+ print('invalid strip value detected', strip)
349
+ return s
350
+
351
+ @staticmethod
352
+ def indention(s):
353
+ '''get string indention value
354
+ --> int
355
+
356
+ :param s: string
357
+ :type s: str
358
+ '''
359
+ return len(s)-len(s.lstrip())
360
+
361
+ @staticmethod
362
+ def is_blank_line(s):
363
+ '''Is provided string/line a blank line
364
+ ---> bool
365
+
366
+ :param s: string
367
+ :type s: str
368
+ '''
369
+ try:
370
+ return True if len(s.strip()) == 0 else False
371
+ except Exception: pass
372
+
373
+ @staticmethod
374
+ def is_hostname_line(s, host):
375
+ '''string/line containing hostname of device
376
+ -->bool
377
+
378
+ :param s: string
379
+ :type s: str
380
+
381
+ :param host: hostname to be find in provided string
382
+ :type host: str
383
+ '''
384
+ return s.find(host) == 0
385
+
386
+ @staticmethod
387
+ def hostname(net_connect):
388
+ '''Hostname from connection'''
389
+ try:
390
+ hns = net_connect.find_prompt()[:-1]
391
+ atPos = STR.foundPos(hns, "@")
392
+ if atPos > -1: hns = hns[atPos+1:]
393
+ return hns
394
+ except:
395
+ pass
396
+
397
+ @staticmethod
398
+ def hostname_from_cli(line, command):
399
+ '''hostname from command line'''
400
+ if not STR.found(line, command): return None
401
+ cmdPos = STR.foundPos(line, command)
402
+ hn = line[:cmdPos].strip()[:-1]
403
+ return hn
404
+
405
+ @staticmethod
406
+ def shrink_if(intName, length=2):
407
+ '''Interface Name shortening for standard 2 Characters '''
408
+ iBW = intBeginWith.match(intName)
409
+ return iBW.group()[:length]+intName[iBW.span()[1]:]
410
+
411
+ @staticmethod
412
+ def if_prefix(intName):
413
+ '''Interface beginning Name'''
414
+ iBW = intBeginWith.match(intName)
415
+ return intName[iBW.start(): iBW.end()]
416
+
417
+ @staticmethod
418
+ def update_str(s, searchItem='', replaceItem=''):
419
+ '''Updates line for search item with replace item'''
420
+ return s.replace(searchItem, replaceItem)
421
+
422
+ @staticmethod
423
+ def get_logfile_name(folder, hn, cmd='', ts='', separator="_@_"):
424
+ '''return log file name for the command on device with/wo provided time_stamp'''
425
+ if ts: ts = separator + ts
426
+ if cmd:
427
+ cmd += ts
428
+ replaceCandidates = ('|', '\\', '/', ':', '*', '?', '"', '<', '>')
429
+ for x in replaceCandidates:
430
+ cmd = STR.update_str(cmd, x, "_")
431
+ cmd = separator + cmd
432
+ return folder+hn+cmd+'.log'
433
+
434
+ @staticmethod
435
+ def string_concate(s, s1, conj=''):
436
+ '''Concatenate strings s and s1 with conjuctor conj
437
+
438
+ :param s: string
439
+ :type s: str
440
+
441
+ :param s1: adder string
442
+ :type s1: string
443
+
444
+ :param conj: conjuctor
445
+ :type conj: string
446
+ '''
447
+ if s == '':
448
+ s = s + s1
449
+ else:
450
+ s = s + conj + s1
451
+ return s
452
+
453
+ @staticmethod
454
+ def right(strg, n):
455
+ '''N-number of characters from right side of string
456
+ --> str
457
+
458
+ :param strg: string
459
+ :type strg: str
460
+
461
+ :param n: number of characters from right
462
+ :type n: int
463
+ '''
464
+ l = len(strg)
465
+ return strg[l-n:l]
466
+
467
+ @staticmethod
468
+ def mid(strg, pos, n=0):
469
+ '''N-number of characters from position in string; default n is till end
470
+ -->str
471
+ :param strg: string
472
+ :type strg: str
473
+
474
+ :param pos: position from where slice to begin
475
+ :type pos: int
476
+
477
+ :param n: number of characters from slice(pos)
478
+ :type n: int
479
+ '''
480
+ l = len(strg)
481
+ if n > 0 :
482
+ return strg[pos-1:pos+n-1]
483
+ else:
484
+ return strg[pos-1:]
485
+
486
+ @staticmethod
487
+ def delete_trailing_remarks(s):
488
+ '''Deletes trailing remarks from Juniper config line/string
489
+ -->str
490
+ :param s: number of characters from right
491
+ :type s: str
492
+ '''
493
+ if s.find("##") > 0:
494
+ s = s[:s.find("##")].rstrip()
495
+ return s.rstrip()
496
+ endingpos = STR.foundPos(s, ";")
497
+ if endingpos < 0: endingpos = STR.foundPos(s, "{")
498
+ if endingpos < 0: endingpos = STR.foundPos(s, "}")
499
+ if endingpos > -1: return s[:endingpos+1]
500
+ return s.rstrip()
501
+
502
+ @staticmethod
503
+ def to_list(s):
504
+ '''Returns list for the provided string - s, split by lines '''
505
+ s = s.split("\n")
506
+ for i, x in enumerate(s):
507
+ s[i] = x + "\n"
508
+ return s
509
+ # return s.split("\n")
510
+
511
+ @staticmethod
512
+ def to_set(s):
513
+ '''return set of values for 'ipList' key from dictionary
514
+ '''
515
+ if isinstance(s, str):
516
+ _s = []
517
+ for _ in s.split('\n'):
518
+ _s.extend(_.split(','))
519
+ return set(LST.remove_empty_members((_s)))
520
+ else:
521
+ return set(s)
522
+
523
+ @staticmethod
524
+ def header_indexes(line):
525
+ exceptional_headers = {'Type', }
526
+ headers = OrderedDict()
527
+ prev_k = None
528
+ for k in STR.replace_dual_and_split(line.rstrip()):
529
+ k = k.strip()
530
+ key = k
531
+ if key in exceptional_headers: key = "__"+key
532
+ headers[key] = [STR.foundPos(line, k), None]
533
+ if prev_k is not None:
534
+ headers[prev_k][1] = STR.foundPos(line, k)
535
+ prev_k = key
536
+ headers[key][1] = 90
537
+ return headers
538
+
539
+ @staticmethod
540
+ def prepend_bgp_as(bgp_as, n):
541
+ s = ''
542
+ for x in range(n): s += str(bgp_as) + " "
543
+ return s[:-1]
544
+
545
+ @staticmethod
546
+ def ending(line, c): return line.strip().endswith(c)
547
+
548
+ @staticmethod
549
+ def starting(line, c): return line.strip().startswith(c)
550
+
551
+
552
+ # -----------------------------------------------------------------------------
553
+ # FILE OPERATIONS/ CONVERSIONS #
554
+ # -----------------------------------------------------------------------------
555
+ class IO():
556
+
557
+ @staticmethod
558
+ def file_list_for_time_stamp(hn, ts, folder, splitter="_@_" ):
559
+ files = set()
560
+ for file in os.listdir(folder):
561
+ if not splitter in file: continue
562
+ if hn in file and ts in file:
563
+ files.add(file)
564
+ return files
565
+
566
+ @staticmethod
567
+ def devices_on_log_files(folder, splitter="_@_"):
568
+ devices = set()
569
+ for file in os.listdir(folder):
570
+ if not splitter in file: continue
571
+ hn = file.split(splitter)
572
+ if hn[0][-4:] == '.log': hn[0] = hn[0][:-4]
573
+ devices.add(hn[0])
574
+ return devices
575
+
576
+ @staticmethod
577
+ def timestamps_for_device(devname, folder, splitter="_@_"):
578
+ stamps = set()
579
+ for file in os.listdir(folder):
580
+ if not splitter in file: continue
581
+ if devname in file:
582
+ stamp = file.split(splitter)
583
+ if stamp[-1][-4:] == '.log': stamp[-1] = stamp[-1][:-4]
584
+ stamps.add(stamp[-1])
585
+ return stamps
586
+
587
+ @staticmethod
588
+ def file_to_str(file):
589
+ '''Returns string output for the provided file
590
+ --> str
591
+
592
+ :param file: text input file name/with path
593
+ :type file: str
594
+ '''
595
+ with open(file, 'r') as f:
596
+ s = f.read()
597
+ return s
598
+
599
+ @staticmethod
600
+ def file_to_list(file):
601
+ '''Returns list for the provided file
602
+ --> list
603
+
604
+ :param file: text input file name/with path
605
+ :type file: str
606
+ '''
607
+ file = file.strip()
608
+ if file is None: return None
609
+ with open(file, 'r') as f:
610
+ lines = f.readlines()
611
+ return lines
612
+
613
+ @staticmethod
614
+ def csv_to_tuple(csv):
615
+ '''Returns tuple from the provided comma separated text values
616
+ --> tuple
617
+
618
+ :param csv: comma separated value
619
+ :type csv: str
620
+ '''
621
+ if csv.find('"') and not csv.find('\"'):
622
+ ln = csv.lstrip().split('"')
623
+ return tuple([x for i, x in enumerate(ln) if i % 2 != 0])
624
+ else:
625
+ return tuple(csv.split(','))
626
+
627
+ @staticmethod
628
+ def to_file(filename, matter):
629
+ '''Creates a file with matter
630
+ --> None
631
+
632
+ :param filename: filename with path to be creaed.
633
+ :type filename: str
634
+
635
+ :param matter: matter to write to new created file.
636
+ :type matter: str, list, tuple
637
+ '''
638
+ with open(filename, 'w') as f:
639
+ if isinstance(matter, str):
640
+ f.write(matter)
641
+ elif isinstance(matter, (list, tuple, set)):
642
+ f.write("\n".join(matter))
643
+
644
+ @staticmethod
645
+ def add_to_file(filename, matter, cr=True):
646
+ '''Writes List/text to output filename.
647
+ --> None
648
+ :param filename: Existing filename with path
649
+ :type filename: str
650
+
651
+ :param matter: matter to write to new created file.
652
+ :type matter: str, tuple, list
653
+
654
+ :param cr: carriage return to add at end of each string/line.(default True)
655
+ :type cr: bool
656
+ '''
657
+ if filename != '':
658
+ if isinstance(matter, str):
659
+ if cr and matter and matter[-1] != "\n": matter += "\n"
660
+ with open(filename, 'a') as f:
661
+ f.write(matter)
662
+ elif isinstance(matter, (list, tuple ,set)):
663
+ for i in matter:
664
+ IO.add_to_file(filename, i)
665
+
666
+ @staticmethod
667
+ def update(file, find_item, replace_item):
668
+ '''
669
+ Find and Replace on provided file and saves file
670
+ ---> None
671
+ :param file: on which find and replace to be apply
672
+ :type str:
673
+
674
+ :param find_item: Search item
675
+ :type str:
676
+
677
+ :param replace_item: Repalce item for the matched find_item
678
+ :type str:
679
+
680
+ '''
681
+ with open(file, 'r') as f:
682
+ filedata = f.read()
683
+ replace_item = str(replace_item)
684
+ if replace_item == 'nan': replace_item = ''
685
+ newdata = filedata.replace(find_item, str(replace_item))
686
+ with open(file, 'w') as f:
687
+ f.write(newdata)
688
+
689
+ @staticmethod
690
+ def jinja_verification(folder):
691
+ s = ''
692
+ for file in os.listdir(folder):
693
+ goahead = {'GOAHEAD FOR': 0, 'GOAHEAD END': 0,}
694
+ repeatfor = {'REPEAT EACH': 0, 'REPEAT STOP': 0,}
695
+ if not file.endswith(".txt"): continue
696
+ with open(folder + "/" + file, 'r') as f:
697
+ rf = f.read()
698
+ for k, v in goahead.items(): goahead[k] = rf.count(k)
699
+ for k, v in repeatfor.items(): repeatfor[k] = rf.count(k)
700
+ bg, eg = goahead['GOAHEAD FOR'], goahead['GOAHEAD END']
701
+ br, er = repeatfor['REPEAT EACH'], repeatfor['REPEAT STOP']
702
+ if bg != eg or br != er: s += f'Descrepencies found in file: <{file}>\n'
703
+ if bg != eg: s += f"\tGOAHEAD conditions : begins {bg} v/s ends {eg}\n"
704
+ if br != er: s += f"\tREPEAT conditions : begins {br} v/s ends {er}\n\n"
705
+ return s
706
+
707
+ # -----------------------------------------------------------------------------
708
+ # LIST MODIFICATIONS #
709
+ # -----------------------------------------------------------------------------
710
+
711
+ class LST():
712
+
713
+ @staticmethod
714
+ def remove_empty_members(lst):
715
+ empty_members = ('', None, 'N/A', 'nil')
716
+ tmp_lst = [m for m in lst if not m in empty_members]
717
+ return tmp_lst
718
+
719
+ @staticmethod
720
+ def convert_vlans_list_to_range_of_vlans_list(vlan_list):
721
+ vlan_list = sorted(vlan_list)
722
+ vlan_list.append(None)
723
+ range_list, previous_vlan = [], 0
724
+ range_begin, range_end = None, None
725
+ for vlan in vlan_list:
726
+ if previous_vlan + 1 == vlan:
727
+ if not range_begin: range_begin = str(previous_vlan) + "-"
728
+ elif range_begin:
729
+ range_end = previous_vlan
730
+ rangeStr = range_begin + str(range_end)
731
+ range_begin = None
732
+ range_list.append(rangeStr)
733
+ elif previous_vlan:
734
+ range_list.append(previous_vlan)
735
+ else:
736
+ pass
737
+ previous_vlan = vlan
738
+ return range_list
739
+
740
+ @staticmethod
741
+ def list_variants(input_list):
742
+ str_list = [str(_)
743
+ for _ in LST.convert_vlans_list_to_range_of_vlans_list(input_list)]
744
+ # str_list = [str(_)
745
+ # for _ in input_list]
746
+ ssv_list = " ".join(str_list)
747
+ csv_list = ",".join(str_list)
748
+ return {
749
+ 'str_list': str_list,
750
+ 'ssv_list': ssv_list,
751
+ 'csv_list': csv_list,
752
+ }
753
+
754
+ @staticmethod
755
+ def list_of_devices(list_of_files):
756
+ devices = set()
757
+ for file in list_of_files:
758
+ if not file.strip(): continue
759
+ f = ".".join(os.path.basename(file).split(".")[:-1])
760
+ hn = f.split("_")[0]
761
+ if not hn in devices: devices.add(hn)
762
+ return devices
763
+
764
+ @staticmethod
765
+ def split(lst, n):
766
+ s = 0
767
+ lst = tuple(lst)
768
+ for _ in range(s, len(lst), n):
769
+ yield lst[_: s+n]
770
+ s += n
771
+
772
+ @staticmethod
773
+ def list_to_octet(lst):
774
+ l = ''
775
+ for x in lst: l = str(x) if l == '' else l +'.'+ str(x)
776
+ return l
777
+
778
+
779
+ # -----------------------------------------------------------------------------
780
+ # DICTIONARY MODIFICATIONS #
781
+ # -----------------------------------------------------------------------------
782
+
783
+ class DIC():
784
+
785
+ # INTERNAL : update dictionary d for provided keyvalue pairs
786
+ # param: d: dest dictionary
787
+ # param: kv: src dictionary with key value pairs
788
+ # --> updated destn dict
789
+ @staticmethod
790
+ def __update_keyValue(d, kv):
791
+ if isinstance(d, dict):
792
+ for k, v in kv.items():
793
+ if isinstance(v, dict):
794
+ for x, y in v.items():
795
+ d = DIC.merge_dict(d, kv)
796
+ else:
797
+ d[k] = v
798
+ return d
799
+
800
+ @staticmethod
801
+ def merge_dict(dx, dy):
802
+ '''Merges two dictionaries for identical keys
803
+ --> dict
804
+ :param dx, dy: Two dictionaries to be merged
805
+ :type dx, dy: dict
806
+ '''
807
+ for k, v in dy.items():
808
+ try:
809
+ dx[k] = DIC.__update_keyValue(dx[k], dy[k])
810
+ except:
811
+ dx[k] = dy[k]
812
+ return dx
813
+
814
+ @staticmethod
815
+ def recursive_dic(dic, indention=0):
816
+ s = ""
817
+ if isinstance(dic, dict):
818
+ for k, v in dic.items():
819
+ s += f"{' '*indention}{k}\n"
820
+ indention += 1
821
+ s += DIC.recursive_dic(v, indention)
822
+ indention -= 1
823
+ elif isinstance(dic, (tuple,list,set)):
824
+ for x in dic:
825
+ if x: s += str(x)+'\n'
826
+ elif isinstance(dic, str):
827
+ if dic: s+= f" {' '*indention}{dic}\n"
828
+ return s
829
+
830
+ # -----------------------------------------------------------------------------
831
+ # DICTIONARY DIFFERECES #
832
+ # -----------------------------------------------------------------------------
833
+
834
+ class DifferenceDict(dict):
835
+
836
+ missing = "- "
837
+ additive = "+ "
838
+
839
+ def __init__(self, d):
840
+ self.d = d
841
+
842
+ def __sub__(self, d): return self.get_change(d, self.missing)
843
+ def __add__(self, d): return self.get_change(d, self.additive)
844
+
845
+ def get_change(self, d, change):
846
+ if isinstance(d, DifferenceDict):
847
+ return dict_differences(self.d, d.d, change)
848
+ elif isinstance(d, dict):
849
+ return dict_differences(self.d, d, change)
850
+
851
+ def _get_differences(subject, change):
852
+ if isinstance(subject, (str, int, float)):
853
+ diff = change + str(subject)
854
+ elif isinstance(subject, set):
855
+ diff = set()
856
+ for item in subject:
857
+ df = _get_differences(item, change)
858
+ diff.add(df)
859
+ elif isinstance(subject, dict):
860
+ diff = dict()
861
+ for key, value in subject.items():
862
+ key = change + str(key)
863
+ if value:
864
+ diff[key] = _get_differences(value, change)
865
+ else:
866
+ diff[key] = ''
867
+ else:
868
+ raise Exception(f"InvalidSubjectTypeError: {type(subject)}:{subject}")
869
+ return diff
870
+
871
+
872
+ def dict_differences(d1, d2, change):
873
+ diff = {}
874
+ if d1 == d2: return None
875
+ if (not (isinstance(d1, (dict, set)) or isinstance(d2, (dict, set))) and
876
+ type(d1) != type(d2)):
877
+ raise Exception(f"TypeMismatch- d1:{type(d1)}d2:{type(d2)} - {d1}{d2}")
878
+ if isinstance(d1, dict):
879
+ for k_d1, v_d1 in d1.items():
880
+ if k_d1 not in d2:
881
+ diff.update( _get_differences({k_d1: v_d1}, change) )
882
+ continue
883
+ if v_d1 == d2[k_d1]: continue
884
+ diff[k_d1] = dict_differences(v_d1, d2[k_d1], change)
885
+ elif isinstance(d1, set):
886
+ diff = _get_differences(d1.difference(d2), change)
887
+ else:
888
+ if d1:
889
+ diff = _get_differences(d1, change)
890
+
891
+ return diff
892
+
893
+ # -----------------------------------------------------------------------------
894
+ # Common Dictionary Methods #
895
+ # -----------------------------------------------------------------------------
896
+ class DictMethods():
897
+ """PAPA FOR DICTIONARY REPR OBJECTS"""
898
+ def __iter__(self):
899
+ for k, v in self.dic.items():
900
+ yield (k, v)
901
+
902
+ def __getitem__(self, item):
903
+ try:
904
+ return self.dic[item]
905
+ except KeyError:
906
+ return None
907
+
908
+ def __get__(self, key, item):
909
+ try:
910
+ return self[key][item]
911
+ except KeyError:
912
+ return None
913
+
914
+ def __setitem__(self, item, value):
915
+ self.dic[item] = value
916
+
917
+ def __delitem__(self, srno):
918
+ try:
919
+ for k in sorted(self.dic.keys()):
920
+ if k <= srno: continue
921
+ self.dic[k-1] = self.dic[k]
922
+ del(self.dic[k])
923
+ except:
924
+ raise KeyError
925
+
926
+ def append(self, item, value):
927
+ try:
928
+ if not self.dic.get(item):
929
+ self.dic[item] = []
930
+ elif isinstance(self.dic[item], (str, int)):
931
+ self.dic[item] = [self.dic[item],]
932
+ self.dic[item].append(value)
933
+ except:
934
+ raise Exception
935
+
936
+
937
+ # -----------------------------------------------------------------------------
938
+ # LOG OPERATIONS #
939
+ # -----------------------------------------------------------------------------
940
+
941
+ class LOG():
942
+
943
+ @staticmethod
944
+ def time_stamp():
945
+ '''current time stamp (for log purpose)
946
+ --> str
947
+ '''
948
+ return str(datetime.datetime.now())[:19]
949
+
950
+ # -----------------------------------------------------------------------------
951
+ # D-B OPERATIONS #
952
+ # -----------------------------------------------------------------------------
953
+
954
+ class DB():
955
+
956
+ @staticmethod
957
+ def read_excel(file, sheet='Sheet1', **kwargs):
958
+ '''
959
+ reads a sheet from an excel
960
+ --->returns dataframe of that sheet data
961
+
962
+ :param file: source excel database file
963
+ :type str:
964
+
965
+ :param sheet: sheet name on source excel which is to be read.
966
+ :type str:
967
+
968
+ :param kwargs: pandas df arguments to read excel
969
+ :type kwargs: mutli
970
+ '''
971
+ return pd.read_excel(file, sheet_name=sheet, **kwargs)
972
+
973
+
974
+
975
+ # ------------------------------------------------------------------------------
976
+ # Excel Data WRITE Class, use with context manager
977
+ # ------------------------------------------------------------------------------
978
+ class XL_WRITE():
979
+ '''EXEL FILE CREATE,
980
+ hostname - excel file name
981
+ **sht_df - sht_name=dataframe
982
+ df - dataframe which data to be copied to Excel.
983
+ sht_name - Sheet Name of Excel where data to be copied
984
+ Excel O/P file will go inside - ./output/ - path
985
+ '''
986
+
987
+ # Object Initializer
988
+ def __init__(self, hostname, folder, index=False, **sht_df):
989
+ i = 0
990
+ self.hostname = hostname
991
+ self.folder = folder
992
+ while True:
993
+ try:
994
+ self.__create_excel(hostname, index, **sht_df)
995
+ break
996
+ except PermissionError:
997
+ i += 1
998
+ hostname = self.hostname+" ("+str(i)+")"
999
+ except Exception as e:
1000
+ print(e)
1001
+ break
1002
+
1003
+ def __repr__(self): return self.op_file
1004
+
1005
+ # write to Excel
1006
+ def __create_excel(self, hostname, index, **sht_df):
1007
+ try:
1008
+ n = 0
1009
+ XL_READ(self.folder + '/'+hostname+'.xlsx', 'tables')
1010
+ while True:
1011
+ n += 1
1012
+ XL_READ(self.folder + '/'+hostname+'-'+str(n)+'.xlsx', 'tables')
1013
+ except:
1014
+ if n == 0:
1015
+ op_file = self.folder + '/'+hostname+'.xlsx'
1016
+ else:
1017
+ op_file = self.folder + '/'+hostname+'-'+str(n)+'.xlsx'
1018
+ self.op_file = op_file
1019
+ with pd.ExcelWriter(op_file) as writer_file:
1020
+ for sht_name, df in sht_df.items():
1021
+ df.to_excel(writer_file, sheet_name=sht_name, index=index)
1022
+
1023
+
1024
+ # ------------------------------------------------------------------------------
1025
+ # Excel Data Read Class
1026
+ # ------------------------------------------------------------------------------
1027
+ class XL_READ:
1028
+ '''EXCEL FILE READING,
1029
+ xl - Excel file to be read
1030
+ shtName - SheetName to be read from given read
1031
+
1032
+ RETURNS
1033
+ -------
1034
+ df - DataFrame object (iterable, lenth, etc. available )
1035
+
1036
+ USAGE EXAMPLE
1037
+ -------------
1038
+ obj = XL_READ('data/cmd_list.xlsx') # get xl df Object
1039
+
1040
+ ### Length of records ###
1041
+ print(len(obj))
1042
+
1043
+ ### Go thru each record ###
1044
+ for header, value in obj:
1045
+ print(header, value)
1046
+
1047
+ ### Get a particular column ###
1048
+ print(obj['command'])
1049
+
1050
+ ### FILTERING RECORDS ###
1051
+ # Option:1
1052
+ flt = {'level':1, 'dev_type':'cisco_ios'} # get arguments in a dict
1053
+ x = obj.filter(**flt) # apply filter dict
1054
+
1055
+ # Option:2
1056
+ x = obj.filter(dev_type='cisco_ios', level=1)# apply filter manually
1057
+
1058
+ # Option:3 - pass external df for filter
1059
+ x = obj.filter(level=1) # created a new DataFrame-x
1060
+ x = obj.filter(df=x, dev_type='cisco_ios')# apply flt manually on new DF-x
1061
+
1062
+ # Option:4 Filter Column & return specific columns only.
1063
+ flt = {'level':1, 'dev_type':'cisco_ios'} # get arguments in a dict
1064
+ col = ['command', 'level']
1065
+ x = obj.column_values(column=col, **flt)
1066
+
1067
+ # Check Output for above options
1068
+ print(x) # filtered output all columns
1069
+ print(x['xl_col']) # filtered output with specific col only
1070
+
1071
+ '''
1072
+
1073
+ # Object Initializer
1074
+ def __init__(self, xl, shtName='Sheet1'):
1075
+ self.df = pd.read_excel(xl, sheet_name=shtName)
1076
+
1077
+ # Object Represantation
1078
+ def __repr__(self):
1079
+ return 'Excel data reprezenting class as DataFrame Object - obj.df'
1080
+
1081
+ # Length of Object
1082
+ def __len__(self):
1083
+ return self.df.last_valid_index()+1
1084
+
1085
+ # Object Iterator
1086
+ def __iter__(self):
1087
+ for header, value in self.df.items():
1088
+ yield (header, value)
1089
+
1090
+ # Get a specific Item/Record from Object
1091
+ def __getitem__(self, key):
1092
+ '''get an item from parameters'''
1093
+ return self.df[key]
1094
+
1095
+
1096
+ # Object Data Filter
1097
+ def filter(self, df=None, **kwarg):
1098
+ '''Filter Records
1099
+ df - external data frame ( default object dataframe )
1100
+ kwarg - filters to be applied on df.
1101
+ '''
1102
+ if df is None:
1103
+ tmpdf = self.df
1104
+ else:
1105
+ tmpdf = df
1106
+ for k, v in kwarg.items():
1107
+ try:
1108
+ tmpdf = tmpdf[tmpdf[k]==v]
1109
+ except:
1110
+ pass
1111
+ return tmpdf
1112
+
1113
+ def column_values(self, column, **kwarg):
1114
+ '''selected column output, after filters applied
1115
+ column - a single column name or , list of column names
1116
+ kwarg - filters to be applied
1117
+ '''
1118
+ return self.filter(**kwarg)[column]
1119
+
1120
+
1121
+
1122
+
1123
+ # -----------------------------------------------------------------------------
1124
+ # IP OPERATIONS #
1125
+ # -----------------------------------------------------------------------------
1126
+
1127
+ class IP():
1128
+
1129
+ @staticmethod
1130
+ def ping_average(ip):
1131
+ lst = popen(f"ping {ip}").read().split("\n")
1132
+ for x in lst:
1133
+ if "Average" in x:
1134
+ avg = x.split()[-1]
1135
+ s = ''
1136
+ for i, n in enumerate(avg):
1137
+ if n.isdigit(): s += n
1138
+ return int(s)
1139
+
1140
+ @staticmethod
1141
+ def bin2dec(binmask):
1142
+ '''convert binary mask to decimal mask
1143
+ ---> decimal mask
1144
+ :param binmask str: binary mask as string
1145
+ '''
1146
+ return 32 - IP.inv2dec(binmask)
1147
+
1148
+ @staticmethod
1149
+ def inv2dec(invmask):
1150
+ '''convert inverse mask to decimal mask
1151
+ ---> decimal mask
1152
+ :param invmask str: inverse mask as string
1153
+ '''
1154
+ m_octs = invmask.split(".")
1155
+ count_of_ones = 0
1156
+ for x in m_octs:
1157
+ x = bin(int(x))
1158
+ count_of_ones += x.count("1")
1159
+ return 32 - count_of_ones
1160
+
1161
+
1162
+ # -----------------------------------------------------------------------------
1163
+ # Execution secquences #
1164
+ # -----------------------------------------------------------------------------
1165
+
1166
+ class Multi_Execution(Default):
1167
+
1168
+ max_connections = 30
1169
+
1170
+ def __str__(self): return self._repr()
1171
+
1172
+ def __init__(self, items=None):
1173
+ self.items = items
1174
+
1175
+ def execute_steps(self, multi_thread=True):
1176
+ self.start(multi_thread)
1177
+
1178
+ def start(self, multi_thread=True):
1179
+ if not self.items: return None
1180
+ if multi_thread:
1181
+ self.execute_mt()
1182
+ else:
1183
+ self.execute_sequencial()
1184
+
1185
+ def end(self): pass
1186
+
1187
+ def get_devices(self):
1188
+ self.devices = LST.list_of_devices(self.files)
1189
+
1190
+ def execute_mt(self):
1191
+ for group, items in enumerate(LST.split(self.items, self.max_connections)):
1192
+ self.execute_threads_max(items)
1193
+
1194
+ def execute_threads_max(self, item_list):
1195
+ ts = []
1196
+ for hn in item_list:
1197
+ t = threading.Thread(target=self.execute, args=(hn,) )
1198
+ t.start()
1199
+ ts.append(t)
1200
+ for t in ts: t.join()
1201
+
1202
+ def execute_sequencial(self):
1203
+ for hn in self.items: self.execute(hn)
1204
+
1205
+ @abstractclassmethod
1206
+ def execute(self, hn): pass
1207
+