trigger 2.0.0__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.
Files changed (61) hide show
  1. trigger/__init__.py +7 -0
  2. trigger/acl/__init__.py +32 -0
  3. trigger/acl/autoacl.py +70 -0
  4. trigger/acl/db.py +324 -0
  5. trigger/acl/dicts.py +357 -0
  6. trigger/acl/grammar.py +112 -0
  7. trigger/acl/ios.py +222 -0
  8. trigger/acl/junos.py +422 -0
  9. trigger/acl/models.py +118 -0
  10. trigger/acl/parser.py +168 -0
  11. trigger/acl/queue.py +296 -0
  12. trigger/acl/support.py +1431 -0
  13. trigger/acl/tools.py +746 -0
  14. trigger/bin/__init__.py +0 -0
  15. trigger/bin/acl.py +233 -0
  16. trigger/bin/acl_script.py +574 -0
  17. trigger/bin/aclconv.py +82 -0
  18. trigger/bin/check_access.py +93 -0
  19. trigger/bin/check_syntax.py +66 -0
  20. trigger/bin/fe.py +197 -0
  21. trigger/bin/find_access.py +191 -0
  22. trigger/bin/gnng.py +434 -0
  23. trigger/bin/gong.py +86 -0
  24. trigger/bin/load_acl.py +841 -0
  25. trigger/bin/load_config.py +18 -0
  26. trigger/bin/netdev.py +317 -0
  27. trigger/bin/optimizer.py +638 -0
  28. trigger/bin/run_cmds.py +18 -0
  29. trigger/changemgmt/__init__.py +352 -0
  30. trigger/changemgmt/bounce.py +57 -0
  31. trigger/cmds.py +1217 -0
  32. trigger/conf/__init__.py +94 -0
  33. trigger/conf/global_settings.py +674 -0
  34. trigger/contrib/__init__.py +7 -0
  35. trigger/exceptions.py +307 -0
  36. trigger/gorc.py +172 -0
  37. trigger/netdevices/__init__.py +1288 -0
  38. trigger/netdevices/loader.py +174 -0
  39. trigger/netscreen.py +1030 -0
  40. trigger/packages/__init__.py +6 -0
  41. trigger/packages/peewee.py +8084 -0
  42. trigger/rancid.py +463 -0
  43. trigger/tacacsrc.py +584 -0
  44. trigger/twister.py +2203 -0
  45. trigger/twister2.py +745 -0
  46. trigger/utils/__init__.py +88 -0
  47. trigger/utils/cli.py +349 -0
  48. trigger/utils/importlib.py +77 -0
  49. trigger/utils/network.py +157 -0
  50. trigger/utils/rcs.py +178 -0
  51. trigger/utils/templates.py +81 -0
  52. trigger/utils/url.py +78 -0
  53. trigger/utils/xmltodict.py +298 -0
  54. trigger-2.0.0.dist-info/METADATA +146 -0
  55. trigger-2.0.0.dist-info/RECORD +61 -0
  56. trigger-2.0.0.dist-info/WHEEL +5 -0
  57. trigger-2.0.0.dist-info/entry_points.txt +15 -0
  58. trigger-2.0.0.dist-info/licenses/AUTHORS.md +20 -0
  59. trigger-2.0.0.dist-info/licenses/LICENSE.md +28 -0
  60. trigger-2.0.0.dist-info/top_level.txt +2 -0
  61. twisted/plugins/trigger_xmlrpc.py +124 -0
trigger/acl/tools.py ADDED
@@ -0,0 +1,746 @@
1
+ """
2
+ Various tools for use in scripts or other modules. Heavy lifting from tools
3
+ that have matured over time have been moved into this module.
4
+ """
5
+
6
+ __author__ = "Jathan McCollum, Eileen Tschetter"
7
+ __maintainer__ = "Jathan McCollum"
8
+ __email__ = "jathan.mccollum@teamaol.com"
9
+ __copyright__ = "Copyright 2010-2011, AOL Inc."
10
+
11
+ import datetime
12
+ import os
13
+ import re
14
+ import tempfile
15
+ from collections import defaultdict
16
+
17
+ import IPy
18
+
19
+ from trigger.acl.parser import *
20
+ from trigger.conf import settings
21
+
22
+ # Defaults
23
+ DEBUG = False
24
+ DATE_FORMAT = "%Y-%m-%d"
25
+ DEFAULT_EXPIRE = 6 * 30 # 6 months
26
+
27
+
28
+ # Exports
29
+ __all__ = (
30
+ "create_trigger_term",
31
+ "create_access",
32
+ "check_access",
33
+ "ACLScript",
34
+ "process_bulk_loads",
35
+ "get_bulk_acls",
36
+ "get_comment_matches",
37
+ "write_tmpacl",
38
+ "diff_files",
39
+ "worklog",
40
+ "insert_term_into_acl",
41
+ "create_new_acl",
42
+ )
43
+
44
+
45
+ # Functions
46
+ def create_trigger_term(
47
+ source_ips=[],
48
+ dest_ips=[],
49
+ source_ports=[],
50
+ dest_ports=[],
51
+ protocols=[],
52
+ action=["accept"],
53
+ name="generated_term",
54
+ ):
55
+ """Constructs & returns a Term object from constituent parts."""
56
+ term = Term()
57
+ term.action = action
58
+ term.name = name
59
+ for key, data in {
60
+ "source-address": source_ips,
61
+ "destination-address": dest_ips,
62
+ "source-port": source_ports,
63
+ "destination-port": dest_ports,
64
+ "protocol": protocols,
65
+ }.items():
66
+ for n in data:
67
+ if key in term.match:
68
+ term.match[key].append(n)
69
+ else:
70
+ term.match[key] = [n]
71
+ return term
72
+
73
+
74
+ def check_access(terms_to_check, new_term, quiet=True, format="junos", acl_name=None):
75
+ """
76
+ Determine whether access is permitted by a given ACL (list of terms).
77
+
78
+ Tests a new term against a list of terms. Return True if access in new term
79
+ is permitted, or False if not.
80
+
81
+ Optionally displays the terms that apply and what edits are needed.
82
+
83
+ :param terms_to_check:
84
+ A list of Term objects to check
85
+
86
+ :param new_term:
87
+ The Term object used for the access test
88
+
89
+ :param quiet:
90
+ Toggle whether output is displayed
91
+
92
+ :param format:
93
+ The ACL format to use for output display
94
+
95
+ :param acl_name:
96
+ The ACL name to use for output display
97
+ """
98
+ permitted = None
99
+ matches = {
100
+ "source-address": new_term.match.get("source-address", []),
101
+ "destination-address": new_term.match.get("destination-address", []),
102
+ "protocol": new_term.match.get("protocol", []),
103
+ "destination-port": new_term.match.get("destination-port", []),
104
+ "source-port": new_term.match.get("source-port", []),
105
+ }
106
+
107
+ def _permitted_in_term(term, comment=" check_access: PERMITTED HERE"):
108
+ """
109
+ A little closure to re-use internally that returns a Boolean based
110
+ on the given Term object's action.
111
+ """
112
+ action = term.action[0]
113
+ if action == "accept":
114
+ is_permitted = True
115
+ if not quiet:
116
+ term.comments.append(Comment(comment))
117
+
118
+ elif action in ("discard", "reject"):
119
+ is_permitted = False
120
+ if not quiet:
121
+ print("\n".join(new_term.output(format, acl_name=acl_name)))
122
+ else:
123
+ is_permitted = None
124
+
125
+ return is_permitted
126
+
127
+ for t in terms_to_check:
128
+ hit = True
129
+ complicated = False
130
+
131
+ for comment in t.comments:
132
+ if "trigger: make discard" in comment:
133
+ t.setaction("discard") # .action[0] = 'discard'
134
+ t.makediscard = True # set 'make discard' flag
135
+
136
+ for k, v in t.match.items():
137
+ if k not in matches or not matches[k]:
138
+ complicated = True
139
+
140
+ else:
141
+ for test in matches[k]:
142
+ if test not in v:
143
+ hit = False
144
+ break
145
+
146
+ if hit and not t.inactive:
147
+ # Simple access check. Elegant!
148
+ if not complicated and permitted is None:
149
+ permitted = _permitted_in_term(t)
150
+
151
+ # Complicated checks should set hit=False unless you want
152
+ # them to display and potentially confuse end-users
153
+ # TODO (jathan): Factor this into a "better way"
154
+ else:
155
+ # Does the term have 'port' defined?
156
+ if "port" in t.match:
157
+ port_match = t.match.get("port")
158
+ match_fields = (matches["destination-port"], matches["source-port"])
159
+
160
+ # Iterate the fields, and then the ports for each field. If
161
+ # one of the port numbers is within port_match, check if
162
+ # the action permits/denies and set the permitted flag.
163
+ for field in match_fields:
164
+ for portnum in field:
165
+ if portnum in port_match:
166
+ permitted = _permitted_in_term(t)
167
+ else:
168
+ hit = False
169
+
170
+ # Other complicated checks would go here...
171
+
172
+ # If a complicated check happened and was not a hit, skip to the
173
+ # next term
174
+ if complicated and not hit:
175
+ continue
176
+
177
+ if not quiet:
178
+ print("\n".join(t.output(format, acl_name=acl_name)))
179
+
180
+ return permitted
181
+
182
+
183
+ def create_access(terms_to_check, new_term):
184
+ """
185
+ Breaks a new_term up into separate constituent parts so that they can be
186
+ compared in a check_access test.
187
+
188
+ Returns a list of terms that should be inserted.
189
+ """
190
+ protos = new_term.match.get("protocol", ["any"])
191
+ sources = new_term.match.get("source-address", ["any"])
192
+ dests = new_term.match.get("destination-address", ["any"])
193
+ sourceports = new_term.match.get("source-port", ["any"])
194
+ destports = new_term.match.get("destination-port", ["any"])
195
+
196
+ ret = []
197
+ for proto in protos:
198
+ for source in sources:
199
+ for sourceport in sourceports:
200
+ for dest in dests:
201
+ for destport in destports:
202
+ t = Term()
203
+ if str(proto) != "any":
204
+ t.match["protocol"] = [proto]
205
+ if str(source) != "any":
206
+ t.match["source-address"] = [source]
207
+ if str(dest) != "any":
208
+ t.match["destination-address"] = [dest]
209
+ if str(sourceport) != "any":
210
+ t.match["source-port"] = [sourceport]
211
+ if str(destport) != "any":
212
+ t.match["destination-port"] = [destport]
213
+ if not check_access(terms_to_check, t):
214
+ ret.append(t)
215
+
216
+ return ret
217
+
218
+
219
+ # note, following code is -not currently used-
220
+ def insert_term_into_acl(new_term, aclobj, debug=False):
221
+ """
222
+ Return a new ACL object with the new_term added in the proper place based
223
+ on the aclobj. Intended to recursively append to an interim ACL object
224
+ based on a list of Term objects.
225
+
226
+ It's safe to assume that this function is incomplete pending better
227
+ documentation and examples.
228
+
229
+ :param new_term:
230
+ The Term object to use for comparison against aclobj
231
+
232
+ :param aclobj:
233
+ The original ACL object to use for creation of new_acl
234
+
235
+ Example::
236
+
237
+ import copy
238
+ # terms_to_be_added is a list of Term objects that is to be added in
239
+ # the "right place" into new_acl based on the contents of aclobj
240
+ original_acl = parse(open('acl.original'))
241
+ new_acl = copy.deepcopy(original_acl) # Dupe the original
242
+ for term in terms_to_be_added:
243
+ new_acl = generate_new_acl(term, new_acl)
244
+ """
245
+ new_acl = ACL() # ACL comes from trigger.acl.parser
246
+ new_acl.policers = aclobj.policers
247
+ new_acl.format = aclobj.format
248
+ new_acl.name = aclobj.name
249
+ already_added = False
250
+
251
+ for c in aclobj.comments:
252
+ new_acl.comments.append(c)
253
+
254
+ # The following logic is almost identical to that of check_access() except
255
+ # that it tracks already_added and knows how to handle insertion of terms
256
+ # before or after Terms with an action of 'discard' or 'reject'.
257
+ for t in aclobj.terms:
258
+ hit = True
259
+ complicated = False
260
+ permitted = None
261
+ for k, v in t.match.items():
262
+ if debug:
263
+ print("generate_new_acl(): k,v==", k, "and", v)
264
+ if k == "protocol" and k not in new_term.match:
265
+ continue
266
+ if k not in new_term.match:
267
+ complicated = True
268
+ continue
269
+ else:
270
+ for test in new_term.match[k]:
271
+ if test not in v:
272
+ hit = False
273
+ break
274
+
275
+ if not hit and k in (
276
+ "source-port",
277
+ "destination-port",
278
+ "source-address",
279
+ "destination-address",
280
+ ):
281
+ # Here is where it gets odd: If we have multiple IPs in this
282
+ # new term, and one of them matches in a deny, we must set hit
283
+ # to True.
284
+ if t.action[0] in ("discard", "reject"):
285
+ for test in new_term.match[k]:
286
+ if test in v:
287
+ hit = True
288
+
289
+ # Check whether access in new_term is permitted (a la check_access(),
290
+ # track whether it's already been added into new_acl, and then add it
291
+ # in the "right place".
292
+ if hit and not t.inactive and not already_added:
293
+ if not complicated and permitted is None:
294
+ for comment in t.comments:
295
+ if (
296
+ "trigger: make discard" in comment
297
+ and new_term.action[0] == "accept"
298
+ ):
299
+ new_acl.terms.append(new_term)
300
+ already_added = True
301
+ permitted = True
302
+ if t.action[0] in ("discard", "reject") and new_term.action[0] in (
303
+ "discard",
304
+ "reject",
305
+ ):
306
+ permitted = False
307
+ elif t.action[0] in ("discard", "reject"):
308
+ permitted = False
309
+ new_acl.terms.append(new_term)
310
+ already_added = True
311
+ elif t.action[0] == "accept" and new_term.action[0] in (
312
+ "discard",
313
+ "reject",
314
+ ):
315
+ permitted = False
316
+ new_acl.terms.append(new_term)
317
+ already_added = True
318
+ elif t.action[0] == "accept" and new_term.action[0] == "accept":
319
+ permitted = True
320
+ if debug:
321
+ print("PERMITTED?", permitted)
322
+
323
+ # Original term is always appended as we move on
324
+ new_acl.terms.append(t)
325
+
326
+ return new_acl
327
+
328
+
329
+ def create_new_acl(old_file, terms_to_be_added):
330
+ """Given a list of Term objects call insert_term_into_acl() to determine
331
+ what needs to be added in based on the contents of old_file. Returns a new
332
+ ACL object."""
333
+ aclobj = parse(open(old_file)) # Start with the original ACL contents
334
+ new_acl = None
335
+ for new_term in terms_to_be_added:
336
+ new_acl = insert_term_into_acl(new_term, aclobj)
337
+
338
+ return new_acl
339
+
340
+
341
+ def get_bulk_acls():
342
+ """
343
+ Returns a dict of acls with an applied count over settings.AUTOLOAD_BULK_THRESH
344
+ """
345
+ from trigger.netdevices import NetDevices
346
+
347
+ nd = NetDevices()
348
+ all_acls = defaultdict(int)
349
+ for dev in nd.all():
350
+ for acl in dev.acls:
351
+ all_acls[acl] += 1
352
+
353
+ bulk_acls = {}
354
+ for acl, count in all_acls.items():
355
+ if count >= settings.AUTOLOAD_BULK_THRESH and acl != "":
356
+ bulk_acls[acl] = count
357
+
358
+ return bulk_acls
359
+
360
+
361
+ def process_bulk_loads(work, max_hits=settings.BULK_MAX_HITS_DEFAULT, force_bulk=False):
362
+ """
363
+ Formerly "process --ones".
364
+
365
+ Processes work dict and determines tuple of (prefix, site) for each device. Stores
366
+ tuple as a dict key in prefix_hits. If prefix_hits[(prefix, site)] is greater than max_hits,
367
+ remove all further matching devices from work dict.
368
+
369
+ By default if a device has no acls flagged as bulk_acls, it is not removed from the work dict.
370
+
371
+ Example:
372
+ * Device 'foo1-xyz.example.com' returns ('foo', 'xyz') as tuple.
373
+ * This is stored as prefix_hits[('foo', 'xyz')] = 1
374
+ * All further devices matching that tuple increment the hits for that tuple
375
+ * Any devices matching hit counter exceeds max_hits is removed from work dict
376
+
377
+ You may override max_hits to increase the num. of devices on which to load a bulk acl.
378
+ You may pass force_bulk=True to treat all loads as bulk loads.
379
+ """
380
+
381
+ prefix_pat = re.compile(r"^([a-z]+)\d{0,2}-([a-z0-9]+)")
382
+ prefix_hits = defaultdict(int)
383
+ import trigger.acl.db as adb
384
+
385
+ adb.get_bulk_acls()
386
+ adb.get_netdevices()
387
+
388
+ if DEBUG:
389
+ print("DEVLIST:", sorted(work))
390
+
391
+ # Sort devices numerically
392
+ for dev in sorted(work):
393
+ if DEBUG:
394
+ print("Doing", dev)
395
+
396
+ # testacls = dev.bulk_acls
397
+ # if force_bulk:
398
+ # testacls = dev.acls
399
+ testacls = dev.acls if force_bulk else dev.bulk_acls
400
+
401
+ for acl in (
402
+ testacls
403
+ ): # only look at each acl once, but look at all acls if bulk load forced
404
+ if acl in work[dev]:
405
+ # if acl in work[router]:
406
+ if DEBUG:
407
+ (
408
+ print("Determining threshold for acl "),
409
+ acl,
410
+ " on device ",
411
+ dev,
412
+ "\n",
413
+ )
414
+ if acl in settings.BULK_MAX_HITS:
415
+ max_hits = settings.BULK_MAX_HITS[acl]
416
+
417
+ try:
418
+ prefix_site = prefix_pat.findall(dev.nodeName)[0]
419
+ except IndexError:
420
+ continue
421
+
422
+ # Mark a hit for this tuple, and dump remaining matches
423
+ prefix_hits[prefix_site] += 1
424
+
425
+ if DEBUG:
426
+ print(prefix_site), prefix_hits[prefix_site]
427
+ if prefix_hits[prefix_site] > max_hits:
428
+ msg = (
429
+ "Removing %s on %s from job queue: threshold of %d exceeded for "
430
+ "'%s' devices in '%s'"
431
+ % (acl, dev, max_hits, prefix_site[0], prefix_site[1])
432
+ )
433
+ print(msg)
434
+ if "log" in globals():
435
+ log.msg(msg)
436
+
437
+ # Remove that acl from being loaded, but still load on that device
438
+ work[dev].remove(acl)
439
+ # work[router].remove(acl)
440
+
441
+ # done with all the devices
442
+ return work
443
+
444
+
445
+ def get_comment_matches(aclobj, requests):
446
+ """Given an ACL object and a list of ticket numbers return a list of matching comments."""
447
+ matches = set()
448
+ for t in aclobj.terms:
449
+ for req in requests:
450
+ for c in t.comments:
451
+ if req in c:
452
+ matches.add(t)
453
+ # [matches.add(t) for c in t.comments if req in c]
454
+
455
+ return matches
456
+
457
+
458
+ def update_expirations(matches, numdays=DEFAULT_EXPIRE):
459
+ """Update expiration dates on matching terms. This modifies mutable objects, so use cautiously."""
460
+ print("matching terms:", [term.name for term in matches])
461
+ for term in matches:
462
+ date = None
463
+ for comment in term.comments:
464
+ try:
465
+ date = re.search(r"(\d{4}\-\d\d\-\d\d)", comment.data).group()
466
+ except AttributeError:
467
+ # print 'No date match in term: %s, comment: %s' % (term.name, comment)
468
+ continue
469
+
470
+ try:
471
+ dstamp = datetime.datetime.strptime(date, DATE_FORMAT)
472
+ except ValueError as err:
473
+ print("BAD DATE FOR THIS COMMENT:")
474
+ print("comment:", comment.data)
475
+ print("bad date:", date)
476
+ print(err)
477
+ print("Fix the date and start the job again!")
478
+ import sys
479
+
480
+ sys.exit()
481
+
482
+ new_date = dstamp + datetime.timedelta(days=numdays)
483
+ # print 'Before:\n' + comment.data + '\n'
484
+ print(f"Updated date for term: {term.name}")
485
+ comment.data = comment.data.replace(
486
+ date, datetime.datetime.strftime(new_date, DATE_FORMAT)
487
+ )
488
+ # print 'After:\n' + comment.data
489
+
490
+
491
+ def write_tmpacl(acl, process_name="_tmpacl"):
492
+ """Write a temporary file to disk from an Trigger acl.ACL object & return the filename"""
493
+ tmpfile = tempfile.mktemp() + process_name
494
+ f = open(tmpfile, "w")
495
+ for x in acl.output(acl.format, replace=True):
496
+ f.write(x)
497
+ f.write("\n")
498
+ f.close()
499
+
500
+ return tmpfile
501
+
502
+
503
+ def diff_files(old, new):
504
+ """Return a unified diff between two files"""
505
+ return os.popen(f"diff -Naur {old} {new}").read()
506
+
507
+
508
+ def worklog(title, diff, log_string="updated by express-gen"):
509
+ """Save a diff to the ACL worklog"""
510
+ from time import localtime, strftime
511
+
512
+ from trigger.utils.rcs import RCS
513
+
514
+ date = strftime("%Y%m%d", localtime())
515
+ file = os.path.join(settings.FIREWALL_DIR, "workdocs", "workdoc." + date)
516
+ rcs = RCS(file)
517
+
518
+ if not os.path.isfile(file):
519
+ print(f"Creating new worklog {file}")
520
+ f = open(file, "w")
521
+ f.write("# vi:noai:\n\n")
522
+ f.close()
523
+ rcs.checkin(".")
524
+
525
+ print(f"inserting the diff into the worklog {file}")
526
+ rcs.lock_loop()
527
+ fd = open(file, "a")
528
+ fd.write(f'"{title}"\n')
529
+ fd.write(diff)
530
+ fd.close()
531
+
532
+ print(f"inserting {title} into the load queue")
533
+ rcs.checkin(log_string)
534
+
535
+ # Use acl to insert into queue, should be replaced with API call
536
+ os.spawnlp(os.P_WAIT, "acl", "acl", "-i", title)
537
+
538
+
539
+ # Classes
540
+ class ACLScript:
541
+ """
542
+ Interface to generating or modifying access-lists. Intended for use in
543
+ creating command-line utilities using the ACL API.
544
+ """
545
+
546
+ def __init__(
547
+ self,
548
+ acl=None,
549
+ mode="insert",
550
+ cmd="acl_script",
551
+ show_mods=True,
552
+ no_worklog=False,
553
+ no_changes=False,
554
+ ):
555
+ self.source_ips = []
556
+ self.dest_ips = []
557
+ self.protocol = []
558
+ self.source_ports = []
559
+ self.dest_ports = []
560
+ self.modify_terms = []
561
+ self.bcomments = []
562
+ self.tempfiles = []
563
+ self.acl = acl
564
+ self.cmd = cmd
565
+ self.mode = mode
566
+ self.show_mods = show_mods
567
+ self.no_worklog = no_worklog
568
+ self.no_changes = no_changes
569
+
570
+ def cleanup(self):
571
+ for file in self.tempfiles:
572
+ os.remove(file)
573
+
574
+ def genargs(self, interactive=False):
575
+ if not self.acl:
576
+ raise "need acl defined"
577
+
578
+ argz = []
579
+ argz.append(f"-a {self.acl}")
580
+
581
+ if self.show_mods:
582
+ argz.append("--show-mods")
583
+
584
+ if self.no_worklog:
585
+ argz.append("--no-worklog")
586
+
587
+ if self.no_changes:
588
+ argz.append("--no-changes")
589
+
590
+ if not interactive:
591
+ argz.append("--no-input")
592
+
593
+ if self.mode == "insert":
594
+ argz.append("--insert-defined")
595
+
596
+ elif self.mode == "replace":
597
+ argz.append("--replace-defined")
598
+
599
+ else:
600
+ raise "invalid mode"
601
+
602
+ for k, v in {
603
+ "--source-address-from-file": self.source_ips,
604
+ "--destination-address-from-file": self.dest_ips,
605
+ }.items():
606
+ if len(v) == 0:
607
+ continue
608
+ tmpf = tempfile.mktemp() + "_genacl"
609
+ self.tempfiles.append(tmpf)
610
+ try:
611
+ f = open(tmpf, "w")
612
+ except:
613
+ print("UNABLE TO OPEN TMPFILE")
614
+ raise "YIKES!"
615
+ for x in v:
616
+ f.write(f"{x.strNormal()}\n")
617
+ f.close()
618
+
619
+ argz.append(f"{k} {tmpf}")
620
+
621
+ for k, v in {"-p": self.source_ports, "-P": self.dest_ports}.items():
622
+ if not len(v):
623
+ continue
624
+
625
+ for x in v:
626
+ argz.append("%s %d" % (k, x))
627
+
628
+ if len(self.modify_terms) and len(self.bcomments):
629
+ print("Can only define either modify_terms or between comments")
630
+ raise "Can only define either modify_terms or between comments"
631
+
632
+ if self.modify_terms:
633
+ for x in self.modify_terms:
634
+ argz.append(f"-t {x}")
635
+ else:
636
+ for x in self.bcomments:
637
+ b, e = x
638
+ argz.append(f'-c "{b}" "{e}"')
639
+
640
+ for proto in self.protocol:
641
+ argz.append(f"--protocol {proto}")
642
+
643
+ return argz
644
+
645
+ def parselog(self, log):
646
+ return log
647
+
648
+ def run(self, interactive=False):
649
+ args = self.genargs(interactive=interactive)
650
+ log = []
651
+ # print self.cmd + ' ' + ' '.join(args)
652
+ if interactive:
653
+ os.system(self.cmd + " " + " ".join(args))
654
+ else:
655
+ f = os.popen(self.cmd + " " + " ".join(args))
656
+ line = f.readline()
657
+ while line:
658
+ line = line.rstrip()
659
+ log.append(line)
660
+ line = f.readline()
661
+ return log
662
+
663
+ def errors_from_log(self, log):
664
+ errors = ""
665
+ for l in log:
666
+ if "%%ERROR%%" in l:
667
+ l = l.spit("%%ERROR%%")[1]
668
+ errors += l[1:] + "\n"
669
+ return errors
670
+
671
+ def diff_from_log(self, log):
672
+ diff = ""
673
+ for l in log:
674
+ if "%%DIFF%%" in l:
675
+ l = l.split("%%DIFF%%")[1]
676
+ diff += l[1:] + "\n"
677
+ return diff
678
+
679
+ def set_acl(self, acl):
680
+ self.acl = acl
681
+
682
+ def _add_addr(self, to, src):
683
+ if isinstance(src, list):
684
+ for x in src:
685
+ if IPy.IP(x) not in to:
686
+ to.append(IPy.IP(x))
687
+ else:
688
+ if IPy.IP(src) not in to:
689
+ to.append(IPy.IP(src))
690
+
691
+ def _add_port(self, to, src):
692
+ if isinstance(src, list):
693
+ for x in src:
694
+ if x not in to:
695
+ to.append(int(x))
696
+ else:
697
+ if int(src) not in to:
698
+ to.append(int(src))
699
+
700
+ def add_protocol(self, src):
701
+ to = self.protocol
702
+ if isinstance(src, list):
703
+ for x in src:
704
+ if x not in to:
705
+ to.append(x)
706
+ else:
707
+ if src not in to:
708
+ to.append(src)
709
+
710
+ def add_src_host(self, data):
711
+ self._add_addr(self.source_ips, data)
712
+
713
+ def add_dst_host(self, data):
714
+ self._add_addr(self.dest_ips, data)
715
+
716
+ def add_src_port(self, data):
717
+ self._add_port(self.source_ports, data)
718
+
719
+ def add_dst_port(self, data):
720
+ self._add_port(self.dest_ports, data)
721
+
722
+ def add_modify_between_comments(self, begin, end):
723
+ del self.modify_terms
724
+ self.modify_terms = []
725
+ self.bcomments.append((begin, end))
726
+
727
+ def add_modify_term(self, term):
728
+ del self.bcomments
729
+ self.bcomments = []
730
+ if term not in self.modify_terms:
731
+ self.modify_terms.append(term)
732
+
733
+ def get_protocols(self):
734
+ return self.protocol
735
+
736
+ def get_src_hosts(self):
737
+ return self.source_ips
738
+
739
+ def get_dst_hosts(self):
740
+ return self.dest_ips
741
+
742
+ def get_src_ports(self):
743
+ return self.source_ports
744
+
745
+ def get_dst_ports(self):
746
+ return self.dest_ports