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/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ from importlib.metadata import PackageNotFoundError, version
2
+
3
+ try:
4
+ __version__ = version("trigger")
5
+ except PackageNotFoundError:
6
+ # Package is not installed (development mode)
7
+ __version__ = "2.0.0.dev0"
@@ -0,0 +1,32 @@
1
+ """
2
+ Trigger's ACL parser.
3
+
4
+ This library contains various modules that allow for parsing, manipulation,
5
+ and management of network access control lists (ACLs). It will parse a complete
6
+ ACL and return an ACL object that can be easily translated to any supported
7
+ vendor syntax.
8
+ """
9
+
10
+ __author__ = "Jathan McCollum"
11
+ __author__ = "Jathan McCollum, Mike Biancaniello, Mike Harding"
12
+ __maintainer__ = "Jathan McCollum"
13
+ __email__ = "jathanism@aol.com"
14
+ __copyright__ = "Copyright 2010-2012, AOL Inc."
15
+ __version__ = (0, 1)
16
+
17
+ import os
18
+
19
+ from trigger.conf import settings
20
+
21
+ __all__ = ["parser", "acl_exists"]
22
+
23
+ # Parser
24
+ from . import parser
25
+ from .parser import *
26
+
27
+ __all__.extend(parser.__all__)
28
+
29
+
30
+ # Functions
31
+ def acl_exists(name):
32
+ return os.access(settings.FIREWALL_DIR + "/acl." + name, os.R_OK)
trigger/acl/autoacl.py ADDED
@@ -0,0 +1,70 @@
1
+ """
2
+ This module controls when ACLs get auto-applied to network devices,
3
+ in addition to what is specified in acls.db.
4
+
5
+ This is primarily used by :class:`~trigger.acl.db.AclsDB` to populate the
6
+ **implicit** ACL-to-device mappings.
7
+
8
+ No changes should be made to this module. You must specify the path to the
9
+ autoacl logic inside of ``settings.py`` as ``AUTOACL_FILE``. This will be
10
+ exported as ``autoacl`` so that the module path for the :func:`autoacl()`
11
+ function will still be :func:`trigger.autoacl.autoacl`.
12
+
13
+ This trickery allows us to keep the business-logic for how ACLs are mapped to
14
+ devices out of the Trigger packaging.
15
+
16
+ If you do not specify a location for ``AUTOACL_FILE`` or the module cannot be
17
+ loaded, then a default :func:`autoacl()` function ill be used.
18
+ """
19
+
20
+ __author__ = "Jathan McCollum, Eileen Tschetter"
21
+ __maintainer__ = "Jathan McCollum"
22
+ __email__ = "jathan.mccollum@teamaol.com"
23
+ __copyright__ = "Copyright 2010-2012, AOL Inc."
24
+
25
+ import warnings
26
+
27
+ from twisted.python import log
28
+
29
+ from trigger.conf import settings
30
+ from trigger.utils.importlib import import_module_from_path
31
+
32
+ __all__ = ("autoacl",)
33
+
34
+ module_path = settings.AUTOACL_FILE
35
+
36
+
37
+ # In either case we're exporting a single name: autoacl().
38
+ try:
39
+ # Placeholder for the custom autoacl module that will provide the autoacl()
40
+ # function. Either of these operations will raise an ImportError if they
41
+ # don't work, so it's safe to have them within the same try statement.
42
+ _autoacl_module = import_module_from_path(module_path, "_autoacl_module")
43
+ log.msg(f"Loading autoacl() from {module_path}")
44
+ from _autoacl_module import autoacl
45
+ except ImportError:
46
+ msg = f"Function autoacl() could not be found in {module_path}, using default!"
47
+ warnings.warn(msg, RuntimeWarning)
48
+
49
+ def autoacl(dev, explicit_acls=None):
50
+ """
51
+ Given a NetDevice object, returns a set of **implicit** (auto) ACLs. We
52
+ require a device object so that we don't have circular dependencies
53
+ between netdevices and autoacl.
54
+
55
+ This function MUST return a ``set()`` of acl names or you will break
56
+ the ACL associations. An empty set is fine, but it must be a set!
57
+
58
+ :param dev: A :class:`~trigger.netdevices.NetDevice` object.
59
+ :param explicit_acls: A set containing names of ACLs. Default: set()
60
+
61
+ >>> dev = nd.find('test1-abc')
62
+ >>> dev.vendor
63
+ <Vendor: Juniper>
64
+ >>> autoacl(dev)
65
+ set(['juniper-router-protect', 'juniper-router.policer'])
66
+
67
+ NOTE: If the default function is returned it does nothing with the
68
+ arguments and always returns an empty set.
69
+ """
70
+ return set()
trigger/acl/db.py ADDED
@@ -0,0 +1,324 @@
1
+ """
2
+ Redis-based replacement of the legacy acls.db file. This is used for
3
+ interfacing with the explicit and automatic ACL-to-device mappings.
4
+
5
+ >>> from trigger.netdevices import NetDevices
6
+ >>> from trigger.acl.db import AclsDB
7
+ >>> nd = NetDevices()
8
+ >>> dev = nd.find('test1-abc')
9
+ >>> a = AclsDB()
10
+ >>> a.get_acl_set(dev)
11
+ set(['juniper-router.policer', 'juniper-router-protect', 'abc123'])
12
+ >>> a.get_acl_set(dev, 'explicit')
13
+ set(['abc123'])
14
+ >>> a.get_acl_set(dev, 'implicit')
15
+ set(['juniper-router.policer', 'juniper-router-protect'])
16
+ >>> a.get_acl_dict(dev)
17
+ {'all': set(['abc123', 'juniper-router-protect', 'juniper-router.policer']),
18
+ 'explicit': set(['abc123']),
19
+ 'implicit': set(['juniper-router-protect', 'juniper-router.policer'])}
20
+ """
21
+
22
+ __author__ = "Jathan McCollum"
23
+ __maintainer__ = "Jathan McCollum"
24
+ __email__ = "jathan@gmail.com"
25
+ __copyright__ = "Copyright 2010-2012, AOL Inc.; 2013 Salesforce.com"
26
+
27
+ from collections import defaultdict
28
+
29
+ import redis
30
+ from twisted.python import log
31
+
32
+ from trigger import exceptions
33
+ from trigger.acl.autoacl import autoacl
34
+ from trigger.conf import settings
35
+
36
+ ACLSDB_BACKUP = "./acls.csv"
37
+ DEBUG = False
38
+
39
+ # The redis instance. It doesn't care if it can't reach Redis until you actually
40
+ # try to talk to Redis.
41
+ r = redis.Redis(
42
+ host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=settings.REDIS_DB
43
+ )
44
+
45
+ # Exports
46
+ __all__ = (
47
+ # functions
48
+ "get_matching_acls",
49
+ "get_all_acls",
50
+ "get_bulk_acls",
51
+ "populate_bulk_acls",
52
+ # classes
53
+ "AclsDB",
54
+ )
55
+
56
+
57
+ # Classes
58
+ class AclsDB:
59
+ """
60
+ Container for ACL operations.
61
+
62
+ add/remove operations are for explicit associations only.
63
+ """
64
+
65
+ def __init__(self):
66
+ self.redis = r
67
+ log.msg("ACLs database client initialized")
68
+
69
+ def add_acl(self, device, acl):
70
+ """
71
+ Add explicit acl to device
72
+
73
+ >>> dev = nd.find('test1-mtc')
74
+ >>> a.add_acl(dev, 'acb123')
75
+ 'added acl abc123 to test1-mtc.net.aol.com'
76
+ """
77
+ try:
78
+ rc = self.redis.sadd(f"acls:explicit:{device.nodeName}", acl)
79
+ except redis.exceptions.ResponseError as err:
80
+ return str(err)
81
+ if rc != 1:
82
+ raise exceptions.ACLSetError(f"{device.nodeName} already has acl {acl}")
83
+ self.redis.save()
84
+
85
+ return f"added acl {acl} to {device}"
86
+
87
+ def remove_acl(self, device, acl):
88
+ """
89
+ Remove explicit acl from device.
90
+
91
+ >>> a.remove_acl(dev, 'acb123')
92
+ 'removed acl abc123 from test1-mtc.net.aol.com'
93
+ """
94
+ try:
95
+ rc = self.redis.srem(f"acls:explicit:{device.nodeName}", acl)
96
+ except redis.exceptions.ResponseError as err:
97
+ return str(err)
98
+ if rc != 1:
99
+ raise exceptions.ACLSetError(f"{device.nodeName} does not have acl {acl}")
100
+ self.redis.save()
101
+
102
+ return f"removed acl {acl} from {device}"
103
+
104
+ def get_acl_dict(self, device):
105
+ """
106
+ Returns a dict of acl mappings for a @device, which is expected to
107
+ be a NetDevice object.
108
+
109
+ >>> a.get_acl_dict(dev)
110
+ {'all': set(['115j', 'protectRE', 'protectRE.policer', 'test-bluej',
111
+ 'testgreenj', 'testops_blockmj']),
112
+ 'explicit': set(['test-bluej', 'testgreenj', 'testops_blockmj']),
113
+ 'implicit': set(['115j', 'protectRE', 'protectRE.policer'])}
114
+ """
115
+ acls = {}
116
+
117
+ # Explicit (we want to make sure the key exists before we try to assign
118
+ # a value)
119
+ expl_key = f"acls:explicit:{device.nodeName}"
120
+ if self.redis.exists(expl_key):
121
+ acls["explicit"] = self.redis.smembers(expl_key) or set()
122
+ else:
123
+ acls["explicit"] = set()
124
+
125
+ # Implicit (automatically-assigned). We're passing the explicit_acls to
126
+ # autoacl so that we can use them logically for auto assignments.
127
+ acls["implicit"] = autoacl(device, explicit_acls=acls["explicit"])
128
+
129
+ # All
130
+ acls["all"] = acls["implicit"] | acls["explicit"]
131
+
132
+ return acls
133
+
134
+ def get_acl_set(self, device, acl_set="all"):
135
+ """
136
+ Return an acl set matching @acl_set for a given device. Match can be
137
+ one of ['all', 'explicit', 'implicit']. Defaults to 'all'.
138
+
139
+ >>> a.get_acl_set(dev)
140
+ set(['testops_blockmj', 'testgreenj', '115j', 'protectRE',
141
+ 'protectRE.policer', 'test-bluej'])
142
+ >>> a.get_acl_set(dev, 'explicit')
143
+ set(['testops_blockmj', 'test-bluej', 'testgreenj'])
144
+ >>> a.get_acl_set(dev, 'implicit')
145
+ set(['protectRE', 'protectRE.policer', '115j'])
146
+ """
147
+ acls_dict = self.get_acl_dict(device)
148
+ # ACL_SETS = ['all', 'explicit', 'implicit', 'bulk']
149
+ ACL_SETS = acls_dict.keys()
150
+ if DEBUG:
151
+ print("fetching"), acl_set, "acls for", device
152
+ if acl_set not in ACL_SETS:
153
+ raise exceptions.InvalidACLSet(f"match statement must be one of {ACL_SETS}")
154
+
155
+ return acls_dict[acl_set]
156
+
157
+
158
+ # Functions
159
+ def populate_explicit_acls(aclsdb_file):
160
+ """
161
+ populate acls:explicit from legacy acls.db file.
162
+
163
+ Format:
164
+
165
+ '{unused},{hostname},{acls}\\n'
166
+
167
+ - @unused is leftover from legacy and is not used
168
+ - @hostname column is the fqdn of the device
169
+ - @acls is a colon-separated list of ACL names
170
+
171
+ Example:
172
+
173
+ xx,test1-abc.net.aol.com,juniper-router.policer:juniper-router-protect:abc123
174
+ xx,test2-abc.net.aol.com,juniper-router.policer:juniper-router-protect:abc123
175
+ """
176
+ import csv
177
+
178
+ for row in csv.reader(open(aclsdb_file)):
179
+ if not row[0].startswith("!"):
180
+ [r.sadd(f"acls:explicit:{row[1]}", acl) for acl in row[2].split(":")]
181
+ r.save()
182
+
183
+
184
+ def backup_explicit_acls():
185
+ """dumps acls:explicit:* to csv"""
186
+ import csv
187
+
188
+ out = csv.writer(open(ACLSDB_BACKUP, "w"))
189
+ for key in r.keys("acls:explicit:*"):
190
+ out.writerow([key.split(":")[-1], ":".join(map(str, r.smembers(key)))])
191
+
192
+
193
+ def populate_implicit_acls(nd=None):
194
+ """populate acls:implicit (autoacls)"""
195
+ nd = nd or get_netdevices()
196
+ for dev in nd.all():
197
+ [r.sadd(f"acls:implicit:{dev.nodeName}", acl) for acl in autoacl(dev)]
198
+ r.save()
199
+
200
+
201
+ def get_netdevices(production_only=True, with_acls=True):
202
+ """Shortcut to import, instantiate, and return a NetDevices instance."""
203
+ from trigger.netdevices import NetDevices
204
+
205
+ return NetDevices(production_only=production_only, with_acls=with_acls)
206
+
207
+
208
+ def get_all_acls(nd=None):
209
+ """
210
+ Returns a dict keyed by acl names whose containing a set of NetDevices
211
+ objects to which each acl is applied.
212
+
213
+ @nd can be your own NetDevices object if one is not supplied already
214
+
215
+ >>> all_acls = get_all_acls()
216
+ >>> all_acls['abc123']
217
+ set([<NetDevice: test1-abc.net.aol.com>, <NetDevice: fw1-xyz.net.aol.com>])
218
+ """
219
+ # nd = nd or settings.get_netdevices()
220
+ nd = nd or get_netdevices()
221
+ all_acls = defaultdict(set)
222
+ for device in nd.all():
223
+ [all_acls[acl].add(device) for acl in device.acls if acl != ""]
224
+
225
+ return all_acls
226
+
227
+
228
+ def get_bulk_acls(nd=None):
229
+ """
230
+ Returns a set of acls with an applied count over
231
+ settings.AUTOLOAD_BULK_THRESH.
232
+ """
233
+ # nd = nd or settings.get_netdevices()
234
+ nd = nd or get_netdevices()
235
+ all_acls = get_all_acls()
236
+ bulk_acls = set(
237
+ [
238
+ acl
239
+ for acl, devs in all_acls.items()
240
+ if len(devs) >= settings.AUTOLOAD_BULK_THRESH
241
+ ]
242
+ )
243
+
244
+ return bulk_acls
245
+
246
+
247
+ def populate_bulk_acls(nd=None):
248
+ """
249
+ Given a NetDevices instance, Adds bulk_acls attribute to NetDevice objects.
250
+ """
251
+ nd = nd or get_netdevices()
252
+ bulk_acls = get_bulk_acls()
253
+ for dev in nd.all():
254
+ dev.bulk_acls = dev.acls.intersection(bulk_acls)
255
+
256
+
257
+ def get_matching_acls(wanted, exact=True, match_acl=True, match_device=False, nd=None):
258
+ """
259
+ Return a sorted list of the names of devices that have at least one
260
+ of the wanted ACLs, and the ACLs that matched on each. Without 'exact',
261
+ match ACL name by startswith.
262
+
263
+ To get a list of devices, matching the ACLs specified:
264
+
265
+ >>> adb.get_matching_acls(['abc123'])
266
+ [('fw1-xyz.net.aol.com', ['abc123']), ('test1-abc.net.aol.com', ['abc123'])]
267
+
268
+ To get a list of ACLS matching the devices specified using an explicit
269
+ match (default) by setting match_device=True:
270
+
271
+ >>> adb.get_matching_acls(['test1-abc'], match_device=True)
272
+ []
273
+ >>> adb.get_matching_acls(['test1-abc.net.aol.com'], match_device=True)
274
+ [('test1-abc.net.aol.com', ['abc123', 'juniper-router-protect',
275
+ 'juniper-router.policer'])]
276
+
277
+ To get a list of ACLS matching the devices specified using a partial
278
+ match. Not how it returns all devices starting with 'test1-mtc':
279
+
280
+ >>> adb.get_matching_acls(['test1-abc'], match_device=True, exact=False)
281
+ [('test1-abc.net.aol.com', ['abc123', 'juniper-router-protect',
282
+ 'juniper-router.policer'])]
283
+ """
284
+ found = []
285
+ wanted_set = set(wanted)
286
+
287
+ def match_exact(x):
288
+ return x & wanted_set
289
+
290
+ def match_begin(x):
291
+ matched = set()
292
+ for a in wanted_set:
293
+ for b in x:
294
+ if b.startswith(a):
295
+ matched.add(b)
296
+ return matched
297
+
298
+ match = exact and match_exact or match_begin
299
+
300
+ # Return all the ACLs if matched by device, or the matched ACLs
301
+ # if matched by ACL.
302
+ # nd = nd or settings.get_netdevices()
303
+ nd = nd or get_netdevices()
304
+ for name, dev in nd.items():
305
+ hit = None
306
+ if match_device:
307
+ if exact and name in wanted:
308
+ hit = dev.acls
309
+ elif not exact:
310
+ for x in wanted:
311
+ if name.startswith(x):
312
+ hit = dev.acls
313
+ break
314
+
315
+ if hit is None and match_acl:
316
+ hit = match(dev.acls)
317
+
318
+ if hit:
319
+ matched = list(hit)
320
+ matched.sort()
321
+ found.append((name, matched))
322
+
323
+ found.sort()
324
+ return found