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,920 @@
1
+
2
+ from .gpl import STR, LST
3
+
4
+ # from errors import incorrectinput
5
+ incorrectinput = 'INCORRECT SUBNET OR SUBNET MASK DETECTED NULL RETURNED'
6
+
7
+ # ----------------------------------------------------------------------------
8
+ # Module Functions
9
+ # ----------------------------------------------------------------------------
10
+
11
+ def bin_mask(mask):
12
+ mask = int(mask)
13
+ decmask = mask*str(1) + (32-mask)*str(0)
14
+ o1 = str(int(decmask[ 0: 8] , 2))
15
+ o2 = str(int(decmask[ 8: 16], 2))
16
+ o3 = str(int(decmask[16: 24], 2))
17
+ o4 = str(int(decmask[24: 32], 2))
18
+ return o1+'.'+o2+'.'+o3+'.'+o4
19
+ def invalid_subnet(subnet): return f"Not a VALID Subnet {subnet}"
20
+ def to_dec_mask(dotted_mask): return bin2decmask(binsubnet(dotted_mask))
21
+ def bin2dec(binnet): return int(binnet, 2)
22
+ def bin2decmask(binmask): return binmask.count('1')
23
+ def binsubnet(subnet):
24
+ """convert subnet to binary:0's and 1's """
25
+ try:
26
+ if STR.found(subnet, "."): version, split_by, bit_per_oct = 4, ".", 8
27
+ if STR.found(subnet, ":"): version, split_by, bit_per_oct = 6, ":", 16
28
+ s = ''
29
+ octs = subnet.split("/")[0].split(split_by)
30
+ for o in octs:
31
+ if version == 4:
32
+ bo = str(bin(int(o)))[2:]
33
+ elif version == 6:
34
+ bo = str(bin(int(o, bit_per_oct)))[2:]
35
+ lbo = len(bo)
36
+ pzero = '0'*(bit_per_oct - lbo)
37
+ s = s + pzero + bo
38
+ return s
39
+ except:
40
+ pass
41
+
42
+
43
+ def addressing(subnet):
44
+ '''main function proiving ip-subnet object for various functions on it
45
+ --> ipsubnet object
46
+
47
+ :param: subnet: either ipv4 or ipv6 subnet with /mask
48
+ :param type: str
49
+
50
+ :param decmask: decimal mask notation only in case of IPv4 (optional)
51
+ :param type: str
52
+ '''
53
+ v_obj = Validation(subnet)
54
+ if v_obj.validated: return v_obj.ip_obj
55
+
56
+
57
+ def get_summaries(*net_list):
58
+ '''summarize the provided network prefixes
59
+ --> list of sorted summary netwoks
60
+
61
+ :param: *net_list: network prefixes (variable arguments)
62
+ :param type: str/list/tuple/set
63
+
64
+ '''
65
+ if not isinstance(net_list, (list, tuple, set)): return None
66
+ s = Summary(*net_list)
67
+ s.calculate()
68
+ summaries = s.prefixes
69
+ i = 0
70
+ while True:
71
+ i += 1
72
+ if i >= MAX_RECURSION_DEPTH: break
73
+ ss = Summary(*summaries)
74
+ ss.calculate()
75
+ if summaries == ss.prefixes: break
76
+ summaries = ss.prefixes
77
+ return sorted(summaries)
78
+
79
+
80
+ def isSplittedRoute(line):
81
+ """ 1: No single line,
82
+ 0 : Yes splitted line [line1]
83
+ -1: Yes splitted line [line2]
84
+ """
85
+ if found(line, ','):
86
+ return 1 if len(line.split()) > 5 else -1
87
+ else:
88
+ return 0
89
+
90
+ def isSubset(pfx, supernet):
91
+ if not isinstance(pfx, (str, IPv4)):
92
+ raise Exception("INPUTERROR")
93
+ if not isinstance(supernet, (str, IPv4)):
94
+ raise Exception("INPUTERROR")
95
+ if isinstance(supernet, str): supernet = addressing(supernet)
96
+ if isinstance(pfx, str): pfx = addressing(pfx)
97
+ if supernet.mask <= pfx.mask:
98
+ supernet_bin = binsubnet(supernet.subnet)
99
+ pfx_bin = binsubnet(pfx.subnet)
100
+ if supernet_bin[0:supernet.mask] == pfx_bin[0:supernet.mask]:
101
+ return True
102
+ return False
103
+
104
+
105
+ # ----------------------------------------------------------------------------
106
+ # Validation Class - doing subnet validation and version detection
107
+ # ----------------------------------------------------------------------------
108
+ class Validation():
109
+ '''ip-subnet validation class
110
+ :param subnet: ipv4 or ipv6 subnet with "/" mask
111
+ :param type: str
112
+
113
+ '''
114
+
115
+ def __init__(self, subnet):
116
+ '''ip-subnet validation class
117
+ :param subnet: ipv4 or ipv6 subnet with "/" mask
118
+ :param type: str
119
+
120
+ '''
121
+ self.mask = None
122
+ self.subnet = subnet
123
+ self.version = self._version()
124
+ self.validated = False
125
+ self._check_ip_object()
126
+
127
+
128
+ def _version(self):
129
+ if STR.found(self.subnet, ":"):
130
+ return 6
131
+ elif STR.found(self.subnet, "."):
132
+ return 4
133
+ else:
134
+ return 0
135
+
136
+ def _check_ip_object(self):
137
+ object_map = {4: IPv4, 6: IPv6}
138
+ func_map = {4: self.check_v4_input, 6: self.check_v6_input}
139
+ if self.version in object_map:
140
+ self.validated = func_map[self.version]()
141
+ if not self.validated: return None
142
+ self.ip_obj = object_map[self.version](self.subnet)
143
+ self.validated = self.ip_obj + 0 == self.ip_obj.NetworkIP(False)
144
+ if not self.validated: return None
145
+ else:
146
+ raise Exception(invalid_subnet(self.subnet))
147
+
148
+ def check_v4_input(self):
149
+ '''Property to validate provided v4 subnet
150
+ '''
151
+ # ~~~~~~~~~ Mask Check ~~~~~~~~~
152
+ try:
153
+ self.mask = self.subnet.split("/")[1]
154
+ except:
155
+ self.mask = 32
156
+ self.subnet = self.subnet + "/32"
157
+ try:
158
+ self.mask = int(self.mask)
159
+ if not all([self.mask>=0, self.mask<=32]):
160
+ raise Exception(f"Invalid mask length {self.mask}")
161
+ except:
162
+ raise Exception(f"Incorrect Mask {self.mask}")
163
+
164
+ # ~~~~~~~~~ Subnet Check ~~~~~~~~~
165
+ try:
166
+ octs = self.subnet.split("/")[0].split(".")
167
+ if len(octs) != 4:
168
+ raise Exception(f"Invalid Subnet Length {len(octs)}")
169
+ for i in range(4):
170
+ if not all([int(octs[i])>=0, int(octs[i])<=255 ]):
171
+ raise Exception(f"Invalid Subnet Octet {i}")
172
+ return True
173
+ except:
174
+ raise Exception(f"Unidentified Subnet: {self.subnet}")
175
+
176
+ def check_v6_input(self):
177
+ '''Property to validate provided v6 subnet
178
+ '''
179
+ try:
180
+ # ~~~~~~~~~ Mask Check ~~~~~~~~~
181
+ self.mask = self.subnet.split("/")[1]
182
+ except:
183
+ self.mask = 128
184
+ self.subnet = self.subnet + "/128"
185
+ try:
186
+ self.mask = int(self.mask)
187
+ if not all([self.mask>=0, self.mask<=128]):
188
+ raise Exception(f"Invalid mask length {self.mask}")
189
+
190
+ # ~~~~~~~~~ Subnet ~~~~~~~~~
191
+ sip = self.subnet.split("/")[0].split("::")
192
+
193
+ # ~~~~~~~~~ Check Subnet squeezers ~~~~~~~~~
194
+ if len(sip) > 2:
195
+ raise Exception("Invalid Subnet, Squeezers detected > 1")
196
+
197
+ # ~~~~~~~~~ Subnet Length ~~~~~~~~~
198
+ lsip = sip[0].split(":")
199
+ try:
200
+ rsip = sip[1].split(":")
201
+ except:
202
+ rsip = []
203
+ if len(lsip)+len(rsip) > 8:
204
+ raise Exception(f"Invalid Subnet Length {len(lsip)+len(rsip)}")
205
+
206
+ # ~~~~~~~~~ Validate Hextates ~~~~~~~~~
207
+ for hxt in lsip+rsip:
208
+ try:
209
+ if hxt != '' :
210
+ hex(int(hxt, 16))
211
+ except:
212
+ raise Exception(f"Invalid Hextate {hxt}")
213
+
214
+ # ~~~~~~~~~ All Good ~~~~~~~~~
215
+ return True
216
+
217
+ except:
218
+ raise Exception("Unidentified Subnet")
219
+
220
+ # --------------------------------------------------------------------------------------------------
221
+ # Parent IP class defining default methods for v4 and v6 objects
222
+ # --------------------------------------------------------------------------------------------------
223
+
224
+ class IP():
225
+ def __init__(self, subnet):
226
+ self.subnet = subnet
227
+ self.mask = int(self.subnet.split("/")[1])
228
+ self.net = self.subnet.split("/")[0]
229
+ def __hash__(self):
230
+ try:
231
+ return bin2dec(binsubnet(self.NetworkIP()))
232
+ except:
233
+ raise Exception(f"UnhashableInput: {self.subnet}")
234
+ def __str__(self): return self.subnet
235
+ def __repr__(self): return self.subnet
236
+ def __len__(self):
237
+ if self.version == 4:
238
+ return bin2dec(binsubnet(self.broadcast_address())) - bin2dec(binsubnet(self.subnet_zero())) + 1
239
+ if self.version == 6:
240
+ raise Exception("Excessive Integer Value Assignment not possible. "
241
+ "Use IPv6.len() method to get the length of object"
242
+ )
243
+ def __gt__(self, ip): return bin2dec(binsubnet(self.NetworkIP())) - bin2dec(binsubnet(ip.broadcast_address())) > 0
244
+ def __lt__(self, ip): return bin2dec(binsubnet(self.NetworkIP())) - bin2dec(binsubnet(ip.broadcast_address())) < 0
245
+ def __eq__(self, ip): return bin2dec(binsubnet(self.NetworkIP())) - bin2dec(binsubnet(ip.broadcast_address())) == 0
246
+ def __add__(self, n):
247
+ '''add n-ip's to given subnet and return udpated subnet'''
248
+ if isinstance(n, int):
249
+ return self.n_thIP(n, False, "_")
250
+ elif isinstance(n, IPv4):
251
+ summary = get_summaries(self, n)
252
+ if len(summary) == 1:
253
+ return get_summaries(self, n)[0]
254
+ else:
255
+ raise Exception(
256
+ "Inconsistant subnets cannot be added "
257
+ "and >2 instances of IPv4/IPv6 Object add not allowed. please check inputs or "
258
+ "Use 'get_summaries' function instead"
259
+ )
260
+ def __sub__(self, n): return self.n_thIP(-1*n, False, "_")
261
+ def __truediv__(self, n): return self._sub_subnets(n)
262
+ def __iter__(self): return self._subnetips()
263
+ def __getitem__(self, n):
264
+ try:
265
+ return self.n_thIP(n, False)
266
+ except:
267
+ l = []
268
+ for x in self._subnetips(n.start, n.stop):
269
+ l.append(x)
270
+ return tuple(l)
271
+
272
+ # get n-number of subnets of given super-net
273
+ def _sub_subnets(self, n):
274
+ _iplst = []
275
+ for i1, x1 in enumerate(range(self.bit_length)):
276
+ p = 2**x1
277
+ if p >= n: break
278
+ _nsm = self.mask + i1
279
+ _nip = int(binsubnet(self.subnet_zero()), 2)
280
+ _bcip = int(binsubnet(self.broadcast_address()), 2)
281
+ _iis = (_bcip - _nip + 1) // p
282
+ for i2, x2 in enumerate(range(_nip, _bcip, _iis)):
283
+ _iplst.append(self.n_thIP(i2*_iis)+ "/" + str(_nsm))
284
+ return tuple(_iplst)
285
+
286
+ # yields IP Address(es) of the provided subnet
287
+ def _subnetips(self, begin=0, end=0):
288
+ _nip = int(binsubnet(self.subnet_zero()), 2)
289
+ if end == 0:
290
+ _bcip = int(binsubnet(self.broadcast_address()), 2)
291
+ else:
292
+ _bcip = _nip + (end-begin)
293
+ for i2, x2 in enumerate(range(_nip, _bcip)):
294
+ if begin>0: i2 = i2+begin
295
+ yield self.n_thIP(i2)
296
+
297
+
298
+
299
+ # ----------------------------------------------------------------------------
300
+ # IP Subnet (IPv6) class
301
+ # ----------------------------------------------------------------------------
302
+
303
+ class IPv6(IP):
304
+ '''Defines IPv6 object and its various operations'''
305
+
306
+ version = 6
307
+ bit_length = 128
308
+
309
+ # Object Initializer
310
+ def __init__(self, subnet=''):
311
+ super().__init__(subnet)
312
+ self._network_ip()
313
+ self.__actualv6subnet = False # breaked subnet expanded
314
+ self._network_address_bool = False # Subnet zero available/not
315
+
316
+ def len(self): return bin2dec(binsubnet(self.broadcast_address())) - bin2dec(binsubnet(self.subnet_zero())) + 1
317
+
318
+ # ------------------------------------------------------------------------
319
+ # Private Methods
320
+ # ------------------------------------------------------------------------
321
+
322
+ # update Subnet to actual length / expand zeros
323
+ def _to_actualsize(self):
324
+ try:
325
+ if not self.__actualv6subnet:
326
+ p = ''
327
+ sip = self.subnet.split("/")[0].split("::")
328
+ if len(sip) == 2:
329
+ # ~~~~~~ No padding, inserting zeros in middle ~~~~~~~
330
+ for x in range(1, 9):
331
+ p = STR.string_concate(p, self._get_hext(hexTnum=x), conj=':')
332
+ self.subnet = p
333
+ else :
334
+ # ~~~~~~~ pad leading zeros ~~~~~~~
335
+ lsip = sip[0].split(":")
336
+ for x in range(8-len(lsip), 0, -1):
337
+ p = STR.string_concate(p, '0', conj=":")
338
+ if p != '':
339
+ self.subnet = p + ':' + self.subnet
340
+ self.__actualv6subnet = True
341
+ except:
342
+ return False
343
+
344
+ # IP Portion of Input
345
+ def _network_ip(self):
346
+ try:
347
+ self.network = self.subnet.split("/")[0]
348
+ return self.network
349
+ except:
350
+ raise Exception(f"NoValidIPv6SubnetDetected: {self.subnet}")
351
+ return None
352
+
353
+ # Padding subnet with ":0" or ":ffff"
354
+ @staticmethod
355
+ def _pad(padwhat='0', counts=0):
356
+ s = ''
357
+ for x in range(counts):
358
+ s = s + ":" + padwhat
359
+ return s
360
+
361
+ # Return a specific Hextate (hexTnum) from IPV6 address
362
+ def _get_hext(self, hexTnum, s=''):
363
+ if s == '':
364
+ s = self.subnet.split("/")[0]
365
+ try:
366
+ if s != '' and all([hexTnum>0, hexTnum<=8]):
367
+ sip = s.split("/")[0].split("::")
368
+ lsip = sip[0].split(":")
369
+ if hexTnum <= len(lsip):
370
+ return lsip[hexTnum-1]
371
+ else:
372
+ rsip = sip[1].split(":")
373
+ if rsip[0] == '': rsip = []
374
+ if 8-hexTnum < len(rsip):
375
+ return rsip[(9-hexTnum)*-1]
376
+ else:
377
+ return '0'
378
+ else:
379
+ raise Exception(incorrectinput)
380
+ return None
381
+ except:
382
+ raise Exception(incorrectinput)
383
+ return None
384
+
385
+ # Return Number of Network Hextates (hxts) from IPV6 address
386
+ def _get_hextates(self, hxts=1, s=''):
387
+ ox = ''
388
+ for o in range(1, hxts+1):
389
+ ox = STR.string_concate(ox, self._get_hext(o, s=s), conj=':')
390
+ return ox+":"
391
+
392
+ # NETWORK / BC Address Calculation : addtype = 'NET' , 'BC'
393
+ def _endpoint(self, addtype='NET'):
394
+ self._to_actualsize()
395
+ if self.mask != '' and self.mask<128: # Non host-only subnets
396
+ x = 0 if addtype == 'NET' else -1
397
+ padIP = '0' if addtype == 'NET' else 'ffff'
398
+ (asilast, avs) = ([], [])
399
+ fixedOctets = self.mask//16
400
+
401
+ ## get full list of available subnets in selected Hexate.
402
+ while x < 65536:
403
+ asilast.append(x)
404
+ x = x + (2**(16-(self.mask-((fixedOctets)*16))))
405
+
406
+ ## check avlbl subnet and choose less then given one.
407
+ for netx in asilast:
408
+ avs.append(self._get_hextates(fixedOctets)
409
+ + str(hex(netx))[2:])
410
+ if addtype =='BC':
411
+ last_subnet = avs[-1]
412
+ if int(self._get_hext(fixedOctets+1), 16) < netx:
413
+ break
414
+ if addtype =='NET':
415
+ last_subnet = avs[-1]
416
+
417
+ ## Return subnet by padding zeros.
418
+ self.fixedOctets = fixedOctets
419
+ return last_subnet+self._pad(padIP, 7-fixedOctets)
420
+
421
+ else: # host-only subnet
422
+ return self.network
423
+
424
+ def _add_ip_to_network_address(self, num=0, _=''):
425
+ '''-->Adds num of IP to Network IP and return address'''
426
+ self._network_address
427
+ s = self._subnet_zero
428
+ if _ != '':
429
+ s = self.subnet
430
+ _7o = self._get_hextates(7, s)
431
+ _8o = int(self._get_hext(8, s)) + num
432
+ return _7o + str(hex(_8o)[2:])
433
+
434
+ @property
435
+ def _broadcast_address(self): return self._endpoint(addtype='BC')
436
+ @property
437
+ def _network_address(self):
438
+ '''-->Returns only NETWORK ADDRESS for given subnet'''
439
+ if not self._network_address_bool:
440
+ self._subnet_zero = self._endpoint(addtype='NET')
441
+ self._network_address_bool = True
442
+ return self._subnet_zero
443
+ NetworkAddress = _network_address
444
+
445
+
446
+ # ------------------------------------------------------------------------
447
+ # Public Methods
448
+ # ------------------------------------------------------------------------
449
+
450
+ # Return a specific Hextate (hexTnum) from IPV6 address
451
+ def get_hext(self, hexTnum): return self._get_hext(hexTnum)
452
+ getHext = get_hext
453
+
454
+ def subnet_zero(self, withMask=True):
455
+ '''--> Network Address with/without mask for given subnet
456
+ '''
457
+ if withMask :
458
+ return self._network_address + "/" + str(self.mask)
459
+ else:
460
+ return self._network_address
461
+ NetworkIP = subnet_zero
462
+
463
+ def broadcast_address(self, withMask=True):
464
+ '''--> Broadcast Address with/without mask for given subnet
465
+ '''
466
+ if withMask :
467
+ return self._broadcast_address + "/" + str(self.mask)
468
+ else:
469
+ return self._broadcast_address
470
+ BroadcastIP = broadcast_address
471
+
472
+ def n_thIP(self, n=0, withMask=False, _=''):
473
+ '''--> n-th IP with/without mask from given subnet
474
+ '''
475
+ ip = self._add_ip_to_network_address(n, _)
476
+ mask = self.decimalMask
477
+ return ip+"/"+mask if withMask else ip
478
+
479
+ @property
480
+ def decimalMask(self):
481
+ '''--> decimal mask of given subnet'''
482
+ return str(self.mask)
483
+ decmask = decimalMask
484
+
485
+ ## - NA - for IPv6 ##
486
+ @property
487
+ def binmask(self): return None
488
+ @property
489
+ def invmask(self): return None
490
+ def ipdecmask(self, n=0): return self.n_thIP(n, True)
491
+ def ipbinmask(self, n=0): return None
492
+ def ipinvmask(self, n=0): return None
493
+
494
+
495
+ # ----------------------------------------------------------------------------
496
+ # IPv4 Subnet (IPv4) class
497
+ # ----------------------------------------------------------------------------
498
+ class IPv4(IP):
499
+ '''Defines IPv4 object and its various operations
500
+ ::hashable object::
501
+
502
+ '''
503
+
504
+ version = 4
505
+ bit_length = 32
506
+
507
+ # ------------------------------------------------------------------------
508
+ # Private methods / Properties
509
+ # ------------------------------------------------------------------------
510
+
511
+ # binary mask return property
512
+ @property
513
+ def _binmask(self):
514
+ try:
515
+ pone ='1'*self.mask
516
+ pzero = '0'*(32-self.mask)
517
+ return pone+pzero
518
+ except:
519
+ pass
520
+
521
+ # Inverse mask return property
522
+ @property
523
+ def _invmask(self):
524
+ try:
525
+ pone ='0'*self.mask
526
+ pzero = '1'*(32-self.mask)
527
+ return pone+pzero
528
+ except:
529
+ pass
530
+
531
+ @staticmethod
532
+ def _pad_zeros(bins):
533
+ return '0'*(34 - len(str(bins)))+bins[2:]
534
+ @staticmethod
535
+ def _octets_bin2dec(binnet):
536
+ return [bin2dec(binnet[x:x+8]) for x in range(0, 32, 8) ]
537
+ def _bin_and(self, binone, bintwo):
538
+ return self._pad_zeros(bin(int(binone.encode('ascii'), 2) & int(bintwo.encode('ascii'), 2) ))
539
+ def _bin_or(self, binone, bintwo):
540
+ return self._pad_zeros(bin(int(binone.encode('ascii'), 2) | int(bintwo.encode('ascii'), 2) ))
541
+
542
+ # ------------------------------------------------------------------------
543
+ # Available Methods & Public properties of class
544
+ # ------------------------------------------------------------------------
545
+
546
+ def subnet_zero(self, withMask=True):
547
+ '''Network IP Address of subnet from provided IP/Subnet'''
548
+ try:
549
+ s = binsubnet(self.subnet)
550
+ bm = self._binmask
551
+ net = LST.list_to_octet(self._octets_bin2dec(self._bin_and(s, bm )))
552
+ if withMask :
553
+ return net + "/" + str(self.mask)
554
+ else:
555
+ return net
556
+ except:
557
+ pass
558
+ NetworkIP = subnet_zero
559
+
560
+ def broadcast_address(self, withMask=False):
561
+ '''Broadcast IP Address of subnet from provided IP/Subnet'''
562
+ try:
563
+ s = binsubnet(self.subnet)
564
+ im = self._invmask
565
+ bc = LST.list_to_octet(self._octets_bin2dec(self._bin_or(s, im )))
566
+ if withMask :
567
+ return bc + "/" + str(self.mask)
568
+ else:
569
+ return bc
570
+ except:
571
+ pass
572
+ BroadcastIP = broadcast_address
573
+
574
+ def n_thIP(self, n=0, withMask=False, _='', summary_calc=False):
575
+ '''n-th IP Address of subnet from provided IP/Subnet'''
576
+ s = binsubnet(self.subnet)
577
+ if _ == '':
578
+ bm = self._binmask
579
+ addedbin = self._pad_zeros(bin(int(self._bin_and(s, bm), 2)+n))
580
+ else:
581
+ addedbin = self._pad_zeros(bin(int(s.encode('ascii'), 2 )+n))
582
+
583
+ if (any([addedbin > binsubnet(self.broadcast_address()),
584
+ addedbin < binsubnet(self.NetworkIP())]) and
585
+ not summary_calc
586
+ ):
587
+ raise Exception("Address Out of Range")
588
+
589
+ else:
590
+ ip = LST.list_to_octet(self._octets_bin2dec(addedbin))
591
+ if withMask :
592
+ return ip + "/" + str(self.mask)
593
+ else:
594
+ return ip
595
+
596
+ @property
597
+ def decmask(self):
598
+ '''Decimal Mask from provided IP/Subnet - Numeric/Integer'''
599
+ return self.mask
600
+ decimalMask = decmask
601
+
602
+ @property
603
+ def binmask(self):
604
+ '''Binary Mask from provided IP/Subnet'''
605
+ return LST.list_to_octet(self._octets_bin2dec(self._binmask))
606
+
607
+ @property
608
+ def invmask(self):
609
+ '''Inverse Mask from provided IP/Subnet'''
610
+ return LST.list_to_octet(self._octets_bin2dec(self._invmask))
611
+
612
+ def ipdecmask(self, n=0):
613
+ '''IP with Decimal Mask for provided IP/Subnet,
614
+ n ==>
615
+ n-th ip of subnet will appear in output if provided,
616
+ subnet0 ip will appear in output if not provided
617
+ default: n = 0, for Network IP
618
+ '''
619
+ try:
620
+ return self[n] + "/" + str(self.mask)
621
+ except:
622
+ raise Exception(f'Invalid Input : detected')
623
+
624
+ def ipbinmask(self, n=0):
625
+ '''IP with Binary Mask for provided IP/Subnet,
626
+ n ==>
627
+ n-th ip of subnet will appear in output if provided,
628
+ same input subnet/ip will appear in output if not provided
629
+ set - n = 0, for Network IP
630
+ '''
631
+ try:
632
+ return self[n] + " " + self.binmask
633
+ except:
634
+ raise Exception(f'Invalid Input : detected')
635
+
636
+ def ipinvmask(self, n=0):
637
+ '''IP with Inverse Mask for provided IP/Subnet,
638
+ n ==>
639
+ n-th ip of subnet will appear in output if provided,
640
+ same input subnet/ip will appear in output if not provided
641
+ set - n = 0, for Network IP
642
+ '''
643
+ try:
644
+ return self[n] + " " + self.invmask
645
+ except:
646
+ raise Exception(f'Invalid Input : detected')
647
+
648
+
649
+
650
+ # ------------------------------------------------------------------------------
651
+ # Routes Class
652
+ # ------------------------------------------------------------------------------
653
+ class Routes(object):
654
+ ''' Routing Table
655
+ --> Routes object with all routes in dictionary
656
+
657
+ :param hostname: device hostname
658
+ :param type: str
659
+
660
+ :param route_list: output of cisco sh route command in list format
661
+ :param type: list
662
+
663
+ :param route_file: feed text file of sh route output directly instead
664
+ :param type: io/text file
665
+
666
+ Properties
667
+ ----------
668
+ routes: dictionary of route: routename
669
+
670
+ See also
671
+ ---------
672
+ get_prefix_desc: --> Prefix Description / str
673
+ inTable --> checks is provided prefix in routes / bool
674
+ outerPrefix --> outer prefix / str
675
+ '''
676
+ # object initializer
677
+ def __init__(self, hostname, route_list=None, route_file=None):
678
+ if route_file != None: route_list = text_to_List(route_file)
679
+ self.__parse(route_list, hostname)
680
+
681
+ def __getitem__(self, key):
682
+ return self.routes[key]
683
+
684
+ def __iter__(self):
685
+ for k, v in self.routes.items():
686
+ yield (k, v)
687
+
688
+ @property
689
+ def reversed_table(self):
690
+ for k, v in reversed(self.routes.items()):
691
+ yield (k, v)
692
+
693
+ @property
694
+ def routes(self):
695
+ """--> routes with its name"""
696
+ return self._op_items
697
+
698
+ def get_prefix_desc(self, prefix):
699
+ '''Returns prefix description if available or returns for default route
700
+ --> str
701
+
702
+ :param prefix: ip prefix to search in output
703
+ :param type: str
704
+ '''
705
+ pfxlst = []
706
+ if isinstance(prefix, str):
707
+ x = self.__check_in_table(addressing(prefix))[1]
708
+ try:
709
+ pfxlst.append(self[x])
710
+ return pfxlst[0]
711
+ except:
712
+ print("prefixesNotinAnySubnet: Error")
713
+ return None
714
+ elif isinstance(prefix, IPv4):
715
+ x = self.__check_in_table(prefix.subnet)
716
+ pfxlst.append(self[x])
717
+ elif isinstance(prefix, (list, tuple, set)):
718
+ for p in prefix:
719
+ px = self.get_prefix_desc(p)
720
+ if px:
721
+ pfxlst.append(px)
722
+ else:
723
+ raise Exception("INPUTERROR")
724
+ if len(set(pfxlst)) == 1:
725
+ return pfxlst[0]
726
+ else:
727
+ print("prefixesNotinSamesubnet: Error")
728
+
729
+ def inTable(self, prefix):
730
+ '''check if prefix is in routes table, return for Def.Route otherwise
731
+ --> bool
732
+ '''
733
+ return self.__check_in_table(prefix)[0]
734
+
735
+ def outerPrefix(self, prefix):
736
+ '''check and return parent subnet of prefix in routes table, Def.Route else
737
+ --> str
738
+ '''
739
+ return self.__check_in_table(prefix)[1]
740
+
741
+ ######################### LOCAL FUNCTIONS #########################
742
+
743
+ # Helper for inTable and outerPrefix
744
+ def __check_in_table(self, prefix):
745
+ if not isinstance(prefix, (str, IPv4)):
746
+ raise Exception("INPUTERROR")
747
+ for k, v in self.reversed_table:
748
+ if k == '0.0.0.0/0': continue
749
+ if isSubset(prefix, k):
750
+ return (True, k)
751
+ break
752
+ return (False, '0.0.0.0/0')
753
+
754
+ # set routes in dictionary/ parser
755
+ def __parse(self, route_list, hostname):
756
+ headers = (
757
+ "L - local", "C - connected", "S - static", "R - RIP", "M - mobile", "B - BGP",
758
+ "D - EIGRP", "EX - EIGRP external", "O - OSPF", "IA - OSPF inter area",
759
+ "N1 - OSPF NSSA external type 1", "N2 - OSPF NSSA external type 2",
760
+ "E1 - OSPF external type 1", "E2 - OSPF external type 2", "V - VPN",
761
+ "i - IS-IS", "su - IS-IS summary", "L1 - IS-IS level-1", "L2 - IS-IS level-2",
762
+ "ia - IS-IS inter area", "* - candidate default", "U - per-user static route",
763
+ "o - ODR", "P - periodic downloaded static route", "+ - replicated route",
764
+ "Gateway of last resort"
765
+ )
766
+ op_items = OrderedDict()
767
+ for line in route_list:
768
+ if blank_line(line): continue
769
+ if hostname_line(line, hostname): continue
770
+ if find_any(line, headers): continue
771
+ if isSplittedRoute(line) == 0:
772
+ spl = line.strip()
773
+ continue
774
+ if isSplittedRoute(line) == -1:
775
+ line = spl + ' ' + line
776
+ spl = line.split(",")
777
+ if line.find('0.0.0.0 0.0.0.0') > -1:
778
+ op_items['0.0.0.0/0'] = replace_dual_and_split(spl[1])[-1].strip()
779
+ continue
780
+ route = replace_dual_and_split(spl[0])[1]
781
+ try:
782
+ routeMask = binsubnet(replace_dual_and_split(spl[0])[2]).count('1')
783
+ except:
784
+ print(spl)
785
+ routeDesc = replace_dual_and_split(spl[-1])[-1]
786
+ op_items[route + '/' + str(routeMask)] = routeDesc.strip()
787
+ self._op_items = op_items
788
+
789
+
790
+ # ----------------------------------------------------------------------------
791
+ # Prefixes summarization class
792
+ # ----------------------------------------------------------------------------
793
+
794
+ MAX_RECURSION_DEPTH = 100
795
+ class Summary(IPv4):
796
+ '''Defines Summary of prefixes
797
+
798
+ '''
799
+
800
+ def __init__(self, *args):
801
+ self.networks = set()
802
+ for arg in args:
803
+ if isinstance(arg, str):
804
+ if arg.strip():
805
+ arg=IPv4(arg)
806
+ self.networks.add(arg)
807
+ self.summaries = []
808
+ self.networks = sorted(self.networks)
809
+ self._validate_and_update_networks()
810
+
811
+ @property
812
+ def prefixes(self):
813
+ for pfx in self.summaries:
814
+ if isinstance(pfx, str): pfx = IPv4(pfx)
815
+ return set(self.summaries)
816
+
817
+ def _validate_and_update_networks(self):
818
+ for network in self.networks:
819
+ if not Validation(str(network)).validated:
820
+ print(f"InvalidSubnetDetected-Removed: {network}")
821
+ self.networks.remove(network)
822
+
823
+ # kick
824
+ def calculate(self):
825
+ prev_network = None
826
+ for network in self.networks:
827
+ _sumy = self.summary(prev_network, network)
828
+ prev_network = _sumy if _sumy is not None else network
829
+ if _sumy is not None:
830
+ if isinstance(prev_network, str):
831
+ _sumy = IPv4(_sumy)
832
+ prev_network = IPv4(prev_network)
833
+ self.summaries.append(_sumy)
834
+ else:
835
+ self.summaries.append(network)
836
+ continue
837
+
838
+ def summary(self, s1, s2):
839
+ if s2 is None: return s1
840
+ if s1 is None: return s2
841
+ if self._are_equal(s1, s2): return s1
842
+ big_subnet = self._is_any_subset(s1, s2)
843
+ if big_subnet: return big_subnet
844
+ self._sequnce_it(s1, s2)
845
+ self._local_vars()
846
+ if not self._contigious() or not self._immidiate(): return None
847
+ summary_ip = self.first.NetworkIP(False)+"/"+str(self.mask)
848
+ return summary_ip if Validation(summary_ip).validated else None
849
+
850
+ def _sequnce_it(self, s1, s2):
851
+ if int(binsubnet(s1.NetworkIP()), 2 ) > int(binsubnet(s2.NetworkIP()), 2 ):
852
+ (first, second) = (s2, s1)
853
+ else:
854
+ (first, second) = (s1, s2)
855
+ self.first, self.second = first, second
856
+
857
+ def _local_vars(self):
858
+ # ---------- set local vars ------------------
859
+ self.first_len = len(self.first)
860
+ self.second_len = len(self.second)
861
+ self.total = 2*self.first_len if self.first_len >= self.second_len else 2*self.second_len
862
+ self.mask = 32 - len(bin(self.total-1)[2:]) # tantative summary mask
863
+ # --------------------------------------------
864
+
865
+ def _are_equal(self, s1, s2): return s1.mask == s2.mask and s1.NetworkIP() == s2.NetworkIP()
866
+
867
+ def _is_any_subset(self, s1, s2):
868
+ (big_subnet, small_subnet) = (s2, s1) if s1.mask > s2.mask else (s1, s2)
869
+ is_part = False
870
+ for power in range(1, 33):
871
+ no_of_subnets = (2**power)
872
+ try:
873
+ portions = big_subnet/no_of_subnets
874
+ except ValueError:
875
+ break
876
+ if small_subnet.NetworkIP() in portions:
877
+ is_part = True
878
+ break
879
+ return big_subnet if is_part else None
880
+
881
+ def _contigious(self):
882
+ # condition 1 - next ip of subnet 1 should be network ip of subnet 2 / Verfications
883
+ return self.first.n_thIP(self.first_len, summary_calc=True) == self.second.NetworkIP(False)
884
+
885
+ def _immidiate(self):
886
+ # condition 2 - length subnet 1 + lenght subnet 2 == bc ip of subnet 2
887
+ return self.first.n_thIP(self.total-1, summary_calc=True) == self.second.broadcast_address()
888
+
889
+
890
+
891
+ # ----------------------------------------------------------------------------
892
+ # Main Function
893
+ # ----------------------------------------------------------------------------
894
+ if __name__ == '__main__':
895
+ pass
896
+ # END
897
+ # ----------------------------------------------------------------------------
898
+
899
+ # # EXAMPLE on SUMMARIZATION
900
+
901
+ # s1 = IPv4("10.10.0.0/24")
902
+ # s2 = IPv4("10.10.1.0/24")
903
+ # s3 = IPv4("10.10.2.0/24")
904
+ # s4 = IPv4("10.10.3.0/24")
905
+
906
+ # # Method1 -- provide > 2 subnets as below using below func.
907
+ # # Return list of sorted individual/summary networks if non-summarizable
908
+ # print (get_summaries(s1 , s3, s2))
909
+
910
+ # # Method2 -- two subnets can be summarized directly using + operator if can be summarized
911
+ # # raise error if non-summarizable
912
+ # print (s2 + s1)
913
+
914
+ # # Method2 -- two subnets can be summarized using summary method as below.
915
+ # # Return None if non-summarizable
916
+ # s = Summary()
917
+ # print (s.summary(s1, s2))
918
+
919
+
920
+ # ----------------------------------------------------------------------------