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/parser.py ADDED
@@ -0,0 +1,168 @@
1
+ """
2
+ Parse and manipulate network access control lists.
3
+
4
+ This library doesn't completely follow the border of the valid/invalid ACL
5
+ set, which is determined by multiple vendors and not completely documented
6
+ by any of them. We could asymptotically approach that with an enormous
7
+ amount of testing, although it would require a 'flavor' flag (vendor,
8
+ router model, software version) for full support. The realistic goal
9
+ is to catch all the errors that we see in practice, and to accept all
10
+ the ACLs that we use in practice, rather than to try to reject *every*
11
+ invalid ACL and accept *every* valid ACL.
12
+
13
+ >>> from trigger.acl import parse
14
+ >>> aclobj = parse("access-list 123 permit tcp any host 10.20.30.40 eq 80")
15
+ >>> aclobj.terms
16
+ [<Term: None>]
17
+ """
18
+
19
+ """
20
+ 7/15/2014
21
+ This file was split into smaller modules: dicts, grammar, junos, ios, and support.
22
+ These modules are then included back into parser.py.
23
+ This makes the code more readable.
24
+ """
25
+
26
+ __author__ = "Jathan McCollum, Mike Biancaniello, Michael Harding, Michael Shields"
27
+ __maintainer__ = "Jathan McCollum"
28
+ __email__ = "jathanism@aol.com"
29
+ __copyright__ = "Copyright 2006-2013, AOL Inc.; 2013 Saleforce.com"
30
+
31
+ from simpleparse.common import comments, strings # noqa: F401
32
+ from simpleparse.dispatchprocessor import DispatchProcessor, dispatch, dispatchList
33
+ from simpleparse.parser import Parser
34
+
35
+ from trigger import exceptions
36
+
37
+ from .ios import *
38
+ from .junos import *
39
+ from .support import *
40
+
41
+ # Exports
42
+ __all__ = (
43
+ # Constants,
44
+ "ports",
45
+ # Functions
46
+ "check_range",
47
+ "default_processor",
48
+ "do_port_lookup",
49
+ "do_protocol_lookup",
50
+ "literals",
51
+ "make_nondefault_processor",
52
+ "parse",
53
+ "strip_comments",
54
+ "S",
55
+ # Classes
56
+ "ACL",
57
+ "ACLParser",
58
+ "ACLProcessor",
59
+ "Comment",
60
+ "Matches",
61
+ "Policer",
62
+ "PolicerGroup",
63
+ "Protocol",
64
+ "RangeList",
65
+ "Remark",
66
+ "Term",
67
+ "TermList",
68
+ "TIP",
69
+ )
70
+
71
+ # Temporary resting place for comments, so the rest of the parser can
72
+ # ignore them. Yes, this makes the library not thread-safe.
73
+ Comments = []
74
+
75
+ #
76
+ # Parsing infrastructure
77
+ #
78
+
79
+
80
+ class ACLProcessor(DispatchProcessor):
81
+ pass
82
+
83
+
84
+ def default_processor(self, tag_info, buffer):
85
+ tag, start, stop, subtags = tag_info
86
+ if not subtags:
87
+ return buffer[start:stop]
88
+ elif len(subtags) == 1:
89
+ return dispatch(self, subtags[0], buffer)
90
+ else:
91
+ return dispatchList(self, subtags, buffer)
92
+
93
+
94
+ def make_nondefault_processor(action):
95
+ if callable(action):
96
+
97
+ def processor(self, tag_info, buffer):
98
+ tag, start, stop, subtags = tag_info
99
+ if tag in subtagged:
100
+ results = [
101
+ getattr(self, subtag[0])(subtag, buffer) for subtag in subtags
102
+ ]
103
+ return action(strip_comments(results))
104
+ else:
105
+ return action(buffer[start:stop])
106
+
107
+ else:
108
+
109
+ def processor(self, tag_info, buffer):
110
+ tag, start, stop, subtags = tag_info
111
+ return action
112
+
113
+ return processor
114
+
115
+
116
+ grammar = []
117
+ for production, rule in rules.items():
118
+ if isinstance(rule, tuple):
119
+ assert len(rule) == 2
120
+ setattr(ACLProcessor, production, make_nondefault_processor(rule[1]))
121
+ grammar.append(f"{production} := {rule[0]}")
122
+ else:
123
+ setattr(ACLProcessor, production, default_processor)
124
+ grammar.append(f"{production} := {rule}")
125
+
126
+ grammar = "\n".join(grammar)
127
+
128
+
129
+ class ACLParser(Parser):
130
+ def buildProcessor(self):
131
+ return ACLProcessor()
132
+
133
+
134
+ ###parser = ACLParser(grammar)
135
+
136
+
137
+ def parse(input_data):
138
+ """
139
+ Parse a complete ACL and return an ACL object. This should be the only
140
+ external interface to the parser.
141
+
142
+ >>> from trigger.acl import parse
143
+ >>> aclobj = parse("access-list 123 permit tcp any host 10.20.30.40 eq 80")
144
+ >>> aclobj.terms
145
+ [<Term: None>]
146
+
147
+ :param input_data:
148
+ An ACL policy as a string or file-like object.
149
+ """
150
+ parser = ACLParser(grammar)
151
+
152
+ try:
153
+ data = input_data.read()
154
+ except AttributeError:
155
+ data = input_data
156
+
157
+ ## parse the acl
158
+ success, children, nextchar = parser.parse(data)
159
+
160
+ if success and nextchar == len(data):
161
+ assert len(children) == 1
162
+ return children[0]
163
+ else:
164
+ line = data[:nextchar].count("\n") + 1
165
+ column = len(data[data[nextchar].rfind("\n") : nextchar]) + 1
166
+ raise exceptions.ParseError(
167
+ "Could not match syntax. Please report as a bug.", line, column
168
+ )
trigger/acl/queue.py ADDED
@@ -0,0 +1,296 @@
1
+ """
2
+ Database interface for automated ACL queue. Used primarily by ``load_acl`` and
3
+ ``acl``` commands for manipulating the work queue.
4
+
5
+ >>> from trigger.acl.queue import Queue
6
+ >>> q = Queue()
7
+ >>> q.list()
8
+ (('dc1-abc.net.aol.com', 'datacenter-protect'), ('dc2-abc.net.aol.com',
9
+ 'datacenter-protect'))
10
+ """
11
+
12
+ __author__ = "Jathan McCollum"
13
+ __maintainer__ = "Jathan McCollum"
14
+ __email__ = "jmccollum@salesforce.com"
15
+ __version__ = "2.0.1"
16
+
17
+
18
+ import datetime
19
+
20
+ from trigger import exceptions
21
+ from trigger.conf import settings
22
+ from trigger.netdevices import NetDevices
23
+ from trigger.utils import get_user
24
+
25
+ from . import models
26
+
27
+ # Globals
28
+ QUEUE_NAMES = ("integrated", "manual")
29
+
30
+
31
+ # Exports
32
+ __all__ = ("Queue",)
33
+
34
+
35
+ # Classes
36
+ class Queue:
37
+ """
38
+ Interacts with firewalls database to insert/remove items into the queue.
39
+
40
+ :param verbose:
41
+ Toggle verbosity
42
+
43
+ :type verbose:
44
+ Boolean
45
+ """
46
+
47
+ def __init__(self, verbose=True):
48
+ self.nd = NetDevices()
49
+ self.verbose = verbose
50
+ self.login = get_user()
51
+
52
+ def vprint(self, msg):
53
+ """
54
+ Print something if ``verbose`` instance variable is set.
55
+
56
+ :param msg:
57
+ The string to print
58
+ """
59
+ if self.verbose:
60
+ print(msg)
61
+
62
+ def get_model(self, queue):
63
+ """
64
+ Given a queue name, return its DB model.
65
+
66
+ :param queue:
67
+ Name of the queue whose object you want
68
+ """
69
+ return models.MODEL_MAP.get(queue, None)
70
+
71
+ def create_task(self, queue, *args, **kwargs):
72
+ """
73
+ Create a task in the specified queue.
74
+
75
+ :param queue:
76
+ Name of the queue whose object you want
77
+ """
78
+ model = self.get_model(queue)
79
+ model.create(*args, **kwargs)
80
+
81
+ def _normalize(self, arg, prefix=""):
82
+ """
83
+ Remove ``prefix`` from ``arg``, and set "escalation" bit.
84
+
85
+ :param arg:
86
+ Arg (typically an ACL filename) to trim
87
+
88
+ :param prefix:
89
+ Prefix to trim from arg
90
+ """
91
+ if arg.startswith(prefix):
92
+ arg = arg[len(prefix) :]
93
+ escalation = False
94
+ if arg.upper().endswith(" ESCALATION"):
95
+ escalation = True
96
+ arg = arg[:-11]
97
+ return (escalation, arg)
98
+
99
+ def insert(self, acl, routers, escalation=False):
100
+ """
101
+ Insert an ACL and associated devices into the ACL load queue.
102
+
103
+ Attempts to insert into integrated queue. If ACL test fails, then
104
+ item is inserted into manual queue.
105
+
106
+ :param acl:
107
+ ACL name
108
+
109
+ :param routers:
110
+ List of device names
111
+
112
+ :param escalation:
113
+ Whether this is an escalated task
114
+ """
115
+ if not acl:
116
+ raise exceptions.ACLQueueError(
117
+ "You must specify an ACL to insert into the queue"
118
+ )
119
+ if not routers:
120
+ routers = []
121
+
122
+ escalation, acl = self._normalize(acl)
123
+ if routers:
124
+ for router in routers:
125
+ try:
126
+ dev = self.nd.find(router)
127
+ except KeyError:
128
+ msg = f"Could not find device {router}"
129
+ raise exceptions.TriggerError(msg)
130
+
131
+ if acl not in dev.acls:
132
+ msg = f"Could not find {acl} in ACL list for {router}"
133
+ raise exceptions.TriggerError(msg)
134
+
135
+ self.create_task(
136
+ queue="integrated", acl=acl, router=router, escalation=escalation
137
+ )
138
+
139
+ self.vprint(
140
+ "ACL {} injected into integrated load queue for {}".format(
141
+ acl, ", ".join(dev[: dev.find(".")] for dev in routers)
142
+ )
143
+ )
144
+
145
+ else:
146
+ self.create_task(queue="manual", q_name=acl, login=self.login)
147
+ self.vprint(f'"{acl}" injected into manual load queue')
148
+
149
+ def delete(self, acl, routers=None, escalation=False):
150
+ """
151
+ Delete an ACL from the firewall database queue.
152
+
153
+ Attempts to delete from integrated queue. If ACL test fails
154
+ or if routers are not specified, the item is deleted from manual queue.
155
+
156
+ :param acl:
157
+ ACL name
158
+
159
+ :param routers:
160
+ List of device names. If this is ommitted, the manual queue is used.
161
+
162
+ :param escalation:
163
+ Whether this is an escalated task
164
+ """
165
+ if not acl:
166
+ raise exceptions.ACLQueueError(
167
+ "You must specify an ACL to delete from the queue"
168
+ )
169
+
170
+ escalation, acl = self._normalize(acl)
171
+ m = self.get_model("integrated")
172
+
173
+ if routers is not None:
174
+ devs = routers
175
+ else:
176
+ self.vprint("Fetching routers from database")
177
+ result = (
178
+ m.select(m.router)
179
+ .distinct()
180
+ .where(m.acl == acl, m.loaded >> None)
181
+ .order_by(m.router)
182
+ )
183
+ rows = result.tuples()
184
+ devs = [row[0] for row in rows]
185
+
186
+ if devs:
187
+ for dev in devs:
188
+ m.delete().where(
189
+ m.acl == acl, m.router == dev, m.loaded >> None
190
+ ).execute()
191
+
192
+ self.vprint(
193
+ "ACL {} cleared from integrated load queue for {}".format(
194
+ acl, ", ".join(dev[: dev.find(".")] for dev in devs)
195
+ )
196
+ )
197
+ return True
198
+
199
+ else:
200
+ m = self.get_model("manual")
201
+ if m.delete().where(m.q_name == acl, m.done == False).execute(): # noqa: E712
202
+ self.vprint(f"{acl!r} cleared from manual load queue")
203
+ return True
204
+
205
+ self.vprint(f"{acl!r} not found in any queues")
206
+ return False
207
+
208
+ def complete(self, device, acls):
209
+ """
210
+ Mark a device and its ACLs as complete using current timestamp.
211
+
212
+ (Integrated queue only.)
213
+
214
+ :param device:
215
+ Device names
216
+
217
+ :param acls:
218
+ List of ACL names
219
+ """
220
+ m = self.get_model("integrated")
221
+ for acl in acls:
222
+ now = datetime.datetime.now()
223
+ m.update(loaded=now).where(
224
+ m.acl == acl, m.router == device, m.loaded >> None
225
+ ).execute()
226
+
227
+ self.vprint(f"Marked the following ACLs as complete for {device}:")
228
+ self.vprint(", ".join(acls))
229
+
230
+ def remove(self, acl, routers, escalation=False):
231
+ """
232
+ Integrated queue only.
233
+
234
+ Mark an ACL and associated devices as "removed" (loaded=0). Intended
235
+ for use when performing manual actions on the load queue when
236
+ troubleshooting or addressing errors with automated loads. This leaves
237
+ the items in the database but removes them from the active queue.
238
+
239
+ :param acl:
240
+ ACL name
241
+
242
+ :param routers:
243
+ List of device names
244
+
245
+ :param escalation:
246
+ Whether this is an escalated task
247
+ """
248
+ if not acl:
249
+ raise exceptions.ACLQueueError(
250
+ "You must specify an ACL to remove from the queue"
251
+ )
252
+
253
+ m = self.get_model("integrated")
254
+ loaded = 0
255
+ if settings.DATABASE_ENGINE == "postgresql":
256
+ loaded = "-infinity" # See: http://bit.ly/15f0J3z
257
+ for router in routers:
258
+ m.update(loaded=loaded).where(
259
+ m.acl == acl, m.router == router, m.loaded >> None
260
+ ).execute()
261
+
262
+ self.vprint(f"Marked the following devices as removed for ACL {acl}: ")
263
+ self.vprint(", ".join(routers))
264
+
265
+ def list(self, queue="integrated", escalation=False, q_names=QUEUE_NAMES):
266
+ """
267
+ List items in the specified queue, defauls to integrated queue.
268
+
269
+ :param queue:
270
+ Name of the queue to list
271
+
272
+ :param escalation:
273
+ Whether this is an escalated task
274
+
275
+ :param q_names:
276
+ (Optional) List of valid queue names
277
+ """
278
+ if queue not in q_names:
279
+ self.vprint(f"Queue must be one of {q_names}, not: {queue}")
280
+ return False
281
+
282
+ m = self.get_model(queue)
283
+
284
+ if queue == "integrated":
285
+ result = (
286
+ m.select(m.router, m.acl)
287
+ .distinct()
288
+ .where(m.loaded >> None, m.escalation == escalation)
289
+ )
290
+ elif queue == "manual":
291
+ result = m.select(m.q_name, m.login, m.q_ts, m.done).where(m.done == False) # noqa: E712
292
+ else:
293
+ raise RuntimeError("This should never happen!!")
294
+
295
+ all_data = list(result.tuples())
296
+ return all_data